tianma Cursor commited on
Commit ·
223873d
1
Parent(s): 4c4a6cb
Add TAIC/C-TAIC codec test scripts and README testing section.
Browse files- README.md +51 -11
- checkpoints/README.md +10 -2
- configs/eval/ctaic_s1.yaml +3 -1
- configs/eval/ctaic_s2.yaml +2 -0
- configs/eval/ctaic_s3.yaml +2 -0
- configs/eval/taic_detection.yaml +4 -1
- configs/eval/taic_instance.yaml +4 -1
- configs/eval/taic_panoptic.yaml +4 -1
- configs/eval/taic_pose.yaml +4 -1
- configs/eval/taic_semantic.yaml +4 -1
- flexicm/utils/codec_test.py +228 -0
- scripts/eval_ctaic.py +146 -31
- scripts/eval_taic.py +124 -33
README.md
CHANGED
|
@@ -122,7 +122,9 @@ FlexICM/
|
|
| 122 |
├── scripts/
|
| 123 |
│ ├── download_base_codecs.sh
|
| 124 |
│ ├── train_taic.py
|
| 125 |
-
│
|
|
|
|
|
|
|
| 126 |
├── flexicm/
|
| 127 |
│ ├── models/ # TAIC / C-TAIC / SFMA / TaskConnector / Conditional
|
| 128 |
│ ├── layers/ # RSTB / WindowAttention (same lineage as AdaptiveICMH)
|
|
@@ -133,8 +135,7 @@ FlexICM/
|
|
| 133 |
# see checkpoints/README.md
|
| 134 |
```
|
| 135 |
|
| 136 |
-
Eval
|
| 137 |
-
Eval entry points: `scripts/eval_taic.py`, `scripts/eval_ctaic.py` (currently only check that real checkpoints replaced `PLACEHOLDER` files).
|
| 138 |
|
| 139 |
---
|
| 140 |
|
|
@@ -292,6 +293,53 @@ stage1_checkpoint: # Stage-1 result loaded in Stage 2
|
|
| 292 |
|
| 293 |
---
|
| 294 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
## Code Map to the Paper
|
| 296 |
|
| 297 |
| Paper component | Code location |
|
|
@@ -306,11 +354,3 @@ stage1_checkpoint: # Stage-1 result loaded in Stage 2
|
|
| 306 |
| Five teachers | `flexicm/tasks/__init__.py` |
|
| 307 |
|
| 308 |
|
| 309 |
-
---
|
| 310 |
-
|
| 311 |
-
## Citation
|
| 312 |
-
|
| 313 |
-
If you use this code or the paper, please cite FlexICM and acknowledge the base works:
|
| 314 |
-
|
| 315 |
-
- TIC: Lu et al., Transformer-based Image Compression
|
| 316 |
-
- TransTIC / AdaptiveICMH: task-adaptive SFMA tuning
|
|
|
|
| 122 |
├── scripts/
|
| 123 |
│ ├── download_base_codecs.sh
|
| 124 |
│ ├── train_taic.py
|
| 125 |
+
│ ├── train_ctaic.py
|
| 126 |
+
│ ├── eval_taic.py # codec test (bpp / feature D)
|
| 127 |
+
│ └── eval_ctaic.py # codec test for C-TAIC
|
| 128 |
├── flexicm/
|
| 129 |
│ ├── models/ # TAIC / C-TAIC / SFMA / TaskConnector / Conditional
|
| 130 |
│ ├── layers/ # RSTB / WindowAttention (same lineage as AdaptiveICMH)
|
|
|
|
| 135 |
# see checkpoints/README.md
|
| 136 |
```
|
| 137 |
|
| 138 |
+
Eval / codec-test configs: `configs/eval/`.
|
|
|
|
| 139 |
|
| 140 |
---
|
| 141 |
|
|
|
|
| 293 |
|
| 294 |
---
|
| 295 |
|
| 296 |
+
## Codec Test
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
For C-TAIC, reported `bpp` is **extension-layer only** (base-layer rate is excluded), matching the paper.
|
| 300 |
+
|
| 301 |
+
### Prepare checkpoints
|
| 302 |
+
|
| 303 |
+
1. Train models (or copy trained weights) into the `checkpoints/` tree — Download checkpoints.
|
| 304 |
+
2. Edit `dataset_path` / `gpu_id` in `configs/eval/*.yaml`
|
| 305 |
+
|
| 306 |
+
### Test TAIC (five tasks)
|
| 307 |
+
|
| 308 |
+
```bash
|
| 309 |
+
python scripts/eval_taic.py -c configs/eval/taic_detection.yaml
|
| 310 |
+
python scripts/eval_taic.py -c configs/eval/taic_semantic.yaml
|
| 311 |
+
python scripts/eval_taic.py -c configs/eval/taic_instance.yaml
|
| 312 |
+
python scripts/eval_taic.py -c configs/eval/taic_panoptic.yaml
|
| 313 |
+
python scripts/eval_taic.py -c configs/eval/taic_pose.yaml
|
| 314 |
+
|
| 315 |
+
# optional: also measure actual entropy-coded bitstream bpp
|
| 316 |
+
python scripts/eval_taic.py -c configs/eval/taic_detection.yaml --actual-bpp
|
| 317 |
+
|
| 318 |
+
# optional: smoke test on a few batches
|
| 319 |
+
python scripts/eval_taic.py -c configs/eval/taic_detection.yaml --max-batches 10
|
| 320 |
+
```
|
| 321 |
+
|
| 322 |
+
Results JSON is written under `logs/eval_taic/<task>/<quality>/`.
|
| 323 |
+
|
| 324 |
+
### Test C-TAIC (three scenarios)
|
| 325 |
+
|
| 326 |
+
```bash
|
| 327 |
+
python scripts/eval_ctaic.py -c configs/eval/ctaic_s1.yaml
|
| 328 |
+
python scripts/eval_ctaic.py -c configs/eval/ctaic_s2.yaml
|
| 329 |
+
python scripts/eval_ctaic.py -c configs/eval/ctaic_s3.yaml
|
| 330 |
+
|
| 331 |
+
# disable base-layer conditioning (TAIC-mode / graceful degradation)
|
| 332 |
+
python scripts/eval_ctaic.py -c configs/eval/ctaic_s1.yaml --no-condition
|
| 333 |
+
|
| 334 |
+
python scripts/eval_ctaic.py -c configs/eval/ctaic_s1.yaml --actual-bpp
|
| 335 |
+
```
|
| 336 |
+
|
| 337 |
+
Results JSON is written under `logs/eval_ctaic/<scenario>/<quality>/`.
|
| 338 |
+
|
| 339 |
+
> Task rate–accuracy curves (mAP / mIoU / PQ / OKS vs bpp) are **not** included yet.
|
| 340 |
+
|
| 341 |
+
---
|
| 342 |
+
|
| 343 |
## Code Map to the Paper
|
| 344 |
|
| 345 |
| Paper component | Code location |
|
|
|
|
| 354 |
| Five teachers | `flexicm/tasks/__init__.py` |
|
| 355 |
|
| 356 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
checkpoints/README.md
CHANGED
|
@@ -60,5 +60,13 @@ cp logs/ctaic_s1_stage2/1/checkpoint_best_loss.pth.tar \
|
|
| 60 |
## Eval configs
|
| 61 |
|
| 62 |
See `configs/eval/` — they point to these placeholder paths.
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
## Eval configs
|
| 61 |
|
| 62 |
See `configs/eval/` — they point to these placeholder paths.
|
| 63 |
+
|
| 64 |
+
Codec test (bpp / feature distortion):
|
| 65 |
+
|
| 66 |
+
```bash
|
| 67 |
+
python scripts/eval_taic.py -c configs/eval/taic_detection.yaml
|
| 68 |
+
python scripts/eval_ctaic.py -c configs/eval/ctaic_s1.yaml
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
Scripts refuse to run if a `PLACEHOLDER` file is still present or if the `.pth.tar` is missing.
|
| 72 |
+
Task rate–accuracy metrics are not implemented in these scripts yet.
|
configs/eval/ctaic_s1.yaml
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
-
#
|
| 2 |
scenario: "s1"
|
| 3 |
dataset_path: "/data/coco2017"
|
|
|
|
| 4 |
quality_level: 1
|
| 5 |
lmbda: 0.0035
|
| 6 |
checkpoint: "./checkpoints/ctaic/s1_det_instance/stage2/1/checkpoint_best_loss.pth.tar"
|
|
@@ -10,3 +11,4 @@ gpu_id: 0
|
|
| 10 |
cuda: true
|
| 11 |
test_batch_size: 1
|
| 12 |
num_workers: 4
|
|
|
|
|
|
| 1 |
+
# Codec test config for C-TAIC scenario s1 (det -> instance)
|
| 2 |
scenario: "s1"
|
| 3 |
dataset_path: "/data/coco2017"
|
| 4 |
+
split: "val2017"
|
| 5 |
quality_level: 1
|
| 6 |
lmbda: 0.0035
|
| 7 |
checkpoint: "./checkpoints/ctaic/s1_det_instance/stage2/1/checkpoint_best_loss.pth.tar"
|
|
|
|
| 11 |
cuda: true
|
| 12 |
test_batch_size: 1
|
| 13 |
num_workers: 4
|
| 14 |
+
pretrained_backbone: true
|
configs/eval/ctaic_s2.yaml
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
scenario: "s2"
|
| 2 |
dataset_path: "/data/coco2017"
|
|
|
|
| 3 |
quality_level: 1
|
| 4 |
lmbda: 0.0035
|
| 5 |
checkpoint: "./checkpoints/ctaic/s2_sem_panoptic/stage2/1/checkpoint_best_loss.pth.tar"
|
|
@@ -9,3 +10,4 @@ gpu_id: 0
|
|
| 9 |
cuda: true
|
| 10 |
test_batch_size: 1
|
| 11 |
num_workers: 4
|
|
|
|
|
|
| 1 |
scenario: "s2"
|
| 2 |
dataset_path: "/data/coco2017"
|
| 3 |
+
split: "val2017"
|
| 4 |
quality_level: 1
|
| 5 |
lmbda: 0.0035
|
| 6 |
checkpoint: "./checkpoints/ctaic/s2_sem_panoptic/stage2/1/checkpoint_best_loss.pth.tar"
|
|
|
|
| 10 |
cuda: true
|
| 11 |
test_batch_size: 1
|
| 12 |
num_workers: 4
|
| 13 |
+
pretrained_backbone: true
|
configs/eval/ctaic_s3.yaml
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
scenario: "s3"
|
| 2 |
dataset_path: "/data/coco2017"
|
|
|
|
| 3 |
quality_level: 1
|
| 4 |
lmbda: 0.0035
|
| 5 |
checkpoint: "./checkpoints/ctaic/s3_det_pose/stage2/1/checkpoint_best_loss.pth.tar"
|
|
@@ -9,3 +10,4 @@ gpu_id: 0
|
|
| 9 |
cuda: true
|
| 10 |
test_batch_size: 1
|
| 11 |
num_workers: 4
|
|
|
|
|
|
| 1 |
scenario: "s3"
|
| 2 |
dataset_path: "/data/coco2017"
|
| 3 |
+
split: "val2017"
|
| 4 |
quality_level: 1
|
| 5 |
lmbda: 0.0035
|
| 6 |
checkpoint: "./checkpoints/ctaic/s3_det_pose/stage2/1/checkpoint_best_loss.pth.tar"
|
|
|
|
| 10 |
cuda: true
|
| 11 |
test_batch_size: 1
|
| 12 |
num_workers: 4
|
| 13 |
+
pretrained_backbone: true
|
configs/eval/taic_detection.yaml
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
-
#
|
|
|
|
| 2 |
task: "detection"
|
| 3 |
dataset_path: "/data/coco2017"
|
|
|
|
| 4 |
quality_level: 1
|
| 5 |
lmbda: 0.0035
|
| 6 |
checkpoint: "./checkpoints/taic/detection/1/checkpoint_best_loss.pth.tar"
|
|
@@ -9,3 +11,4 @@ gpu_id: 0
|
|
| 9 |
cuda: true
|
| 10 |
test_batch_size: 1
|
| 11 |
num_workers: 4
|
|
|
|
|
|
| 1 |
+
# Codec test config for TAIC / detection
|
| 2 |
+
# Place real weights at checkpoint path (replace PLACEHOLDER).
|
| 3 |
task: "detection"
|
| 4 |
dataset_path: "/data/coco2017"
|
| 5 |
+
split: "val2017"
|
| 6 |
quality_level: 1
|
| 7 |
lmbda: 0.0035
|
| 8 |
checkpoint: "./checkpoints/taic/detection/1/checkpoint_best_loss.pth.tar"
|
|
|
|
| 11 |
cuda: true
|
| 12 |
test_batch_size: 1
|
| 13 |
num_workers: 4
|
| 14 |
+
pretrained_backbone: true
|
configs/eval/taic_instance.yaml
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
-
#
|
|
|
|
| 2 |
task: "instance"
|
| 3 |
dataset_path: "/data/coco2017"
|
|
|
|
| 4 |
quality_level: 1
|
| 5 |
lmbda: 0.0035
|
| 6 |
checkpoint: "./checkpoints/taic/instance/1/checkpoint_best_loss.pth.tar"
|
|
@@ -9,3 +11,4 @@ gpu_id: 0
|
|
| 9 |
cuda: true
|
| 10 |
test_batch_size: 1
|
| 11 |
num_workers: 4
|
|
|
|
|
|
| 1 |
+
# Codec test config for TAIC / instance
|
| 2 |
+
# Place real weights at checkpoint path (replace PLACEHOLDER).
|
| 3 |
task: "instance"
|
| 4 |
dataset_path: "/data/coco2017"
|
| 5 |
+
split: "val2017"
|
| 6 |
quality_level: 1
|
| 7 |
lmbda: 0.0035
|
| 8 |
checkpoint: "./checkpoints/taic/instance/1/checkpoint_best_loss.pth.tar"
|
|
|
|
| 11 |
cuda: true
|
| 12 |
test_batch_size: 1
|
| 13 |
num_workers: 4
|
| 14 |
+
pretrained_backbone: true
|
configs/eval/taic_panoptic.yaml
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
-
#
|
|
|
|
| 2 |
task: "panoptic"
|
| 3 |
dataset_path: "/data/coco2017"
|
|
|
|
| 4 |
quality_level: 1
|
| 5 |
lmbda: 0.0035
|
| 6 |
checkpoint: "./checkpoints/taic/panoptic/1/checkpoint_best_loss.pth.tar"
|
|
@@ -9,3 +11,4 @@ gpu_id: 0
|
|
| 9 |
cuda: true
|
| 10 |
test_batch_size: 1
|
| 11 |
num_workers: 4
|
|
|
|
|
|
| 1 |
+
# Codec test config for TAIC / panoptic
|
| 2 |
+
# Place real weights at checkpoint path (replace PLACEHOLDER).
|
| 3 |
task: "panoptic"
|
| 4 |
dataset_path: "/data/coco2017"
|
| 5 |
+
split: "val2017"
|
| 6 |
quality_level: 1
|
| 7 |
lmbda: 0.0035
|
| 8 |
checkpoint: "./checkpoints/taic/panoptic/1/checkpoint_best_loss.pth.tar"
|
|
|
|
| 11 |
cuda: true
|
| 12 |
test_batch_size: 1
|
| 13 |
num_workers: 4
|
| 14 |
+
pretrained_backbone: true
|
configs/eval/taic_pose.yaml
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
-
#
|
|
|
|
| 2 |
task: "pose"
|
| 3 |
dataset_path: "/data/coco2017"
|
|
|
|
| 4 |
quality_level: 1
|
| 5 |
lmbda: 0.0035
|
| 6 |
checkpoint: "./checkpoints/taic/pose/1/checkpoint_best_loss.pth.tar"
|
|
@@ -9,3 +11,4 @@ gpu_id: 0
|
|
| 9 |
cuda: true
|
| 10 |
test_batch_size: 1
|
| 11 |
num_workers: 4
|
|
|
|
|
|
| 1 |
+
# Codec test config for TAIC / pose
|
| 2 |
+
# Place real weights at checkpoint path (replace PLACEHOLDER).
|
| 3 |
task: "pose"
|
| 4 |
dataset_path: "/data/coco2017"
|
| 5 |
+
split: "val2017"
|
| 6 |
quality_level: 1
|
| 7 |
lmbda: 0.0035
|
| 8 |
checkpoint: "./checkpoints/taic/pose/1/checkpoint_best_loss.pth.tar"
|
|
|
|
| 11 |
cuda: true
|
| 12 |
test_batch_size: 1
|
| 13 |
num_workers: 4
|
| 14 |
+
pretrained_backbone: true
|
configs/eval/taic_semantic.yaml
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
-
#
|
|
|
|
| 2 |
task: "semantic"
|
| 3 |
dataset_path: "/data/coco2017"
|
|
|
|
| 4 |
quality_level: 1
|
| 5 |
lmbda: 0.0035
|
| 6 |
checkpoint: "./checkpoints/taic/semantic/1/checkpoint_best_loss.pth.tar"
|
|
@@ -9,3 +11,4 @@ gpu_id: 0
|
|
| 9 |
cuda: true
|
| 10 |
test_batch_size: 1
|
| 11 |
num_workers: 4
|
|
|
|
|
|
| 1 |
+
# Codec test config for TAIC / semantic
|
| 2 |
+
# Place real weights at checkpoint path (replace PLACEHOLDER).
|
| 3 |
task: "semantic"
|
| 4 |
dataset_path: "/data/coco2017"
|
| 5 |
+
split: "val2017"
|
| 6 |
quality_level: 1
|
| 7 |
lmbda: 0.0035
|
| 8 |
checkpoint: "./checkpoints/taic/semantic/1/checkpoint_best_loss.pth.tar"
|
|
|
|
| 11 |
cuda: true
|
| 12 |
test_batch_size: 1
|
| 13 |
num_workers: 4
|
| 14 |
+
pretrained_backbone: true
|
flexicm/utils/codec_test.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Shared helpers for codec test / eval scripts."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
from typing import Dict, Optional, Tuple
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
|
| 10 |
+
from flexicm.utils.alignment import Alignment
|
| 11 |
+
from flexicm.utils.train_utils import AverageMeter
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def resolve_ckpt(path: str, repo_root: str, label: str = "checkpoint") -> str:
|
| 15 |
+
if not path:
|
| 16 |
+
raise FileNotFoundError(f"{label}: path is empty")
|
| 17 |
+
if not os.path.isabs(path):
|
| 18 |
+
path = os.path.join(repo_root, path)
|
| 19 |
+
placeholder = os.path.join(os.path.dirname(path), "PLACEHOLDER")
|
| 20 |
+
if os.path.isfile(placeholder) and not os.path.isfile(path):
|
| 21 |
+
raise FileNotFoundError(
|
| 22 |
+
f"{label}: not ready (PLACEHOLDER still present):\n {placeholder}\n"
|
| 23 |
+
f"Expected real weights at:\n {path}\n"
|
| 24 |
+
"See checkpoints/README.md"
|
| 25 |
+
)
|
| 26 |
+
if not os.path.isfile(path):
|
| 27 |
+
raise FileNotFoundError(f"{label}: missing file: {path}")
|
| 28 |
+
if os.path.basename(path) == "PLACEHOLDER" or path.endswith(".txt"):
|
| 29 |
+
raise FileNotFoundError(f"{label}: refusing placeholder file: {path}")
|
| 30 |
+
return path
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def crop_feature_to_image(h: torch.Tensor, image_hw: Tuple[int, int]) -> torch.Tensor:
|
| 34 |
+
"""Crop decoded feature h (H/4, W/4 of padded input) to original image size / 4."""
|
| 35 |
+
H, W = image_hw
|
| 36 |
+
return h[..., : H // 4, : W // 4]
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def pad_for_codec(images: torch.Tensor, divisor: int = 256, device=None):
|
| 40 |
+
align = Alignment(divisor=divisor, mode="pad", padding_mode="constant")
|
| 41 |
+
if device is not None:
|
| 42 |
+
align = align.to(device)
|
| 43 |
+
return align.align(images), align
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def likelihood_bpp(likelihoods: Dict[str, torch.Tensor], num_pixels: int) -> torch.Tensor:
|
| 47 |
+
import math
|
| 48 |
+
|
| 49 |
+
return sum(
|
| 50 |
+
(torch.log(lik).sum() / (-math.log(2) * num_pixels))
|
| 51 |
+
for lik in likelihoods.values()
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def actual_bitstream_bpp(strings, num_pixels: int) -> float:
|
| 56 |
+
"""Estimate bpp from CompressAI byte strings: [[y_bytes...], [z_bytes...]]."""
|
| 57 |
+
total_bits = 0
|
| 58 |
+
for group in strings:
|
| 59 |
+
for s in group:
|
| 60 |
+
if isinstance(s, (bytes, bytearray)):
|
| 61 |
+
total_bits += len(s) * 8
|
| 62 |
+
elif torch.is_tensor(s):
|
| 63 |
+
total_bits += int(s.numel() * s.element_size() * 8)
|
| 64 |
+
else:
|
| 65 |
+
total_bits += len(s) * 8
|
| 66 |
+
return total_bits / float(num_pixels)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
@torch.no_grad()
|
| 70 |
+
def test_taic_loader(
|
| 71 |
+
model,
|
| 72 |
+
teacher,
|
| 73 |
+
loader,
|
| 74 |
+
criterion,
|
| 75 |
+
device,
|
| 76 |
+
align_divisor: int = 256,
|
| 77 |
+
run_actual_bpp: bool = False,
|
| 78 |
+
max_batches: Optional[int] = None,
|
| 79 |
+
log_every: int = 50,
|
| 80 |
+
):
|
| 81 |
+
"""Run codec test: likelihood bpp + feature distortion (+ optional real bpp)."""
|
| 82 |
+
model.eval()
|
| 83 |
+
teacher.eval()
|
| 84 |
+
meters = {k: AverageMeter() for k in ("loss", "bpp", "distortion", "actual_bpp")}
|
| 85 |
+
|
| 86 |
+
if run_actual_bpp:
|
| 87 |
+
model.update(force=True)
|
| 88 |
+
|
| 89 |
+
for i, images in enumerate(loader):
|
| 90 |
+
if max_batches is not None and i >= max_batches:
|
| 91 |
+
break
|
| 92 |
+
images = images.to(device)
|
| 93 |
+
N, _, H, W = images.shape
|
| 94 |
+
num_pixels = N * H * W
|
| 95 |
+
|
| 96 |
+
x, _ = pad_for_codec(images, divisor=align_divisor, device=device)
|
| 97 |
+
out = model(x)
|
| 98 |
+
h = crop_feature_to_image(out["h"], (H, W))
|
| 99 |
+
out["h"] = h
|
| 100 |
+
|
| 101 |
+
gt = teacher.gt_features(images)
|
| 102 |
+
pred = teacher.pred_features(h)
|
| 103 |
+
stats = criterion(out, pred, gt, num_pixels=num_pixels)
|
| 104 |
+
|
| 105 |
+
meters["loss"].update(stats["loss"].item(), n=N)
|
| 106 |
+
meters["bpp"].update(stats["bpp"].item(), n=N)
|
| 107 |
+
meters["distortion"].update(stats["distortion"].item(), n=N)
|
| 108 |
+
|
| 109 |
+
if run_actual_bpp:
|
| 110 |
+
try:
|
| 111 |
+
enc = model.compress(x)
|
| 112 |
+
dec = model.decompress(
|
| 113 |
+
enc["strings"], enc["shape"], x_size=(x.shape[2], x.shape[3])
|
| 114 |
+
)
|
| 115 |
+
abpp = actual_bitstream_bpp(enc["strings"], num_pixels)
|
| 116 |
+
meters["actual_bpp"].update(abpp, n=N)
|
| 117 |
+
# sanity: decoded h spatial size
|
| 118 |
+
_ = dec["h"]
|
| 119 |
+
except Exception as e:
|
| 120 |
+
if i == 0:
|
| 121 |
+
print(f"[warn] actual bpp / compress-decompress failed: {e}")
|
| 122 |
+
|
| 123 |
+
if i % log_every == 0:
|
| 124 |
+
msg = (
|
| 125 |
+
f"[{i}/{len(loader)}] bpp={meters['bpp'].avg:.4f} "
|
| 126 |
+
f"D={meters['distortion'].avg:.6f} loss={meters['loss'].avg:.4f}"
|
| 127 |
+
)
|
| 128 |
+
if run_actual_bpp and meters["actual_bpp"].count > 0:
|
| 129 |
+
msg += f" actual_bpp={meters['actual_bpp'].avg:.4f}"
|
| 130 |
+
print(msg)
|
| 131 |
+
|
| 132 |
+
result = {
|
| 133 |
+
"bpp": meters["bpp"].avg,
|
| 134 |
+
"distortion": meters["distortion"].avg,
|
| 135 |
+
"loss": meters["loss"].avg,
|
| 136 |
+
"num_batches": meters["bpp"].count,
|
| 137 |
+
}
|
| 138 |
+
if run_actual_bpp and meters["actual_bpp"].count > 0:
|
| 139 |
+
result["actual_bpp"] = meters["actual_bpp"].avg
|
| 140 |
+
return result
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
@torch.no_grad()
|
| 144 |
+
def test_ctaic_loader(
|
| 145 |
+
ext_model,
|
| 146 |
+
base_model,
|
| 147 |
+
teacher,
|
| 148 |
+
loader,
|
| 149 |
+
criterion,
|
| 150 |
+
device,
|
| 151 |
+
use_condition: bool = True,
|
| 152 |
+
align_divisor: int = 256,
|
| 153 |
+
run_actual_bpp: bool = False,
|
| 154 |
+
max_batches: Optional[int] = None,
|
| 155 |
+
log_every: int = 50,
|
| 156 |
+
):
|
| 157 |
+
"""Codec test for C-TAIC; bpp is extension-layer only (paper Sec.IV.E.2)."""
|
| 158 |
+
ext_model.eval()
|
| 159 |
+
base_model.eval()
|
| 160 |
+
teacher.eval()
|
| 161 |
+
meters = {k: AverageMeter() for k in ("loss", "bpp", "distortion", "actual_bpp")}
|
| 162 |
+
|
| 163 |
+
if run_actual_bpp:
|
| 164 |
+
ext_model.update(force=True)
|
| 165 |
+
|
| 166 |
+
for i, images in enumerate(loader):
|
| 167 |
+
if max_batches is not None and i >= max_batches:
|
| 168 |
+
break
|
| 169 |
+
images = images.to(device)
|
| 170 |
+
N, _, H, W = images.shape
|
| 171 |
+
num_pixels = N * H * W
|
| 172 |
+
|
| 173 |
+
x, _ = pad_for_codec(images, divisor=align_divisor, device=device)
|
| 174 |
+
y_b = None
|
| 175 |
+
if use_condition:
|
| 176 |
+
base_out = base_model(x)
|
| 177 |
+
y_b = base_out["y_hat"]
|
| 178 |
+
|
| 179 |
+
out = ext_model(x, y_b_hat=y_b, use_condition=use_condition and y_b is not None)
|
| 180 |
+
h = crop_feature_to_image(out["h"], (H, W))
|
| 181 |
+
out["h"] = h
|
| 182 |
+
|
| 183 |
+
gt = teacher.gt_features(images)
|
| 184 |
+
pred = teacher.pred_features(h)
|
| 185 |
+
stats = criterion(out, pred, gt, num_pixels=num_pixels)
|
| 186 |
+
|
| 187 |
+
meters["loss"].update(stats["loss"].item(), n=N)
|
| 188 |
+
meters["bpp"].update(stats["bpp"].item(), n=N)
|
| 189 |
+
meters["distortion"].update(stats["distortion"].item(), n=N)
|
| 190 |
+
|
| 191 |
+
if run_actual_bpp:
|
| 192 |
+
try:
|
| 193 |
+
enc = ext_model.compress(
|
| 194 |
+
x, y_b_hat=y_b, use_condition=use_condition and y_b is not None
|
| 195 |
+
)
|
| 196 |
+
dec = ext_model.decompress(
|
| 197 |
+
enc["strings"],
|
| 198 |
+
enc["shape"],
|
| 199 |
+
x_size=(x.shape[2], x.shape[3]),
|
| 200 |
+
y_b_hat=y_b,
|
| 201 |
+
use_condition=use_condition and y_b is not None,
|
| 202 |
+
)
|
| 203 |
+
abpp = actual_bitstream_bpp(enc["strings"], num_pixels)
|
| 204 |
+
meters["actual_bpp"].update(abpp, n=N)
|
| 205 |
+
_ = dec["h"]
|
| 206 |
+
except Exception as e:
|
| 207 |
+
if i == 0:
|
| 208 |
+
print(f"[warn] actual bpp / compress-decompress failed: {e}")
|
| 209 |
+
|
| 210 |
+
if i % log_every == 0:
|
| 211 |
+
msg = (
|
| 212 |
+
f"[{i}/{len(loader)}] bpp={meters['bpp'].avg:.4f} "
|
| 213 |
+
f"D={meters['distortion'].avg:.6f} loss={meters['loss'].avg:.4f}"
|
| 214 |
+
)
|
| 215 |
+
if run_actual_bpp and meters["actual_bpp"].count > 0:
|
| 216 |
+
msg += f" actual_bpp={meters['actual_bpp'].avg:.4f}"
|
| 217 |
+
print(msg)
|
| 218 |
+
|
| 219 |
+
result = {
|
| 220 |
+
"bpp": meters["bpp"].avg,
|
| 221 |
+
"distortion": meters["distortion"].avg,
|
| 222 |
+
"loss": meters["loss"].avg,
|
| 223 |
+
"num_batches": meters["bpp"].count,
|
| 224 |
+
"use_condition": use_condition,
|
| 225 |
+
}
|
| 226 |
+
if run_actual_bpp and meters["actual_bpp"].count > 0:
|
| 227 |
+
result["actual_bpp"] = meters["actual_bpp"].avg
|
| 228 |
+
return result
|
scripts/eval_ctaic.py
CHANGED
|
@@ -1,52 +1,167 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
"""
|
| 7 |
|
| 8 |
from __future__ import annotations
|
| 9 |
|
| 10 |
import argparse
|
|
|
|
| 11 |
import os
|
| 12 |
import sys
|
|
|
|
| 13 |
|
| 14 |
-
|
| 15 |
-
|
| 16 |
|
| 17 |
-
|
|
|
|
|
|
|
| 18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
placeholder = os.path.join(os.path.dirname(path), "PLACEHOLDER")
|
| 26 |
-
if os.path.isfile(placeholder) and not os.path.isfile(path):
|
| 27 |
-
raise FileNotFoundError(
|
| 28 |
-
f"{label}: checkpoint not ready (PLACEHOLDER still present):\n {placeholder}\n"
|
| 29 |
-
f"Expected real weights at:\n {path}\n"
|
| 30 |
-
"See checkpoints/README.md"
|
| 31 |
-
)
|
| 32 |
-
if not os.path.isfile(path):
|
| 33 |
-
raise FileNotFoundError(f"{label}: missing checkpoint: {path}")
|
| 34 |
-
return path
|
| 35 |
|
| 36 |
|
| 37 |
-
def
|
| 38 |
-
parser = argparse.ArgumentParser("
|
| 39 |
parser.add_argument("-c", "--config", required=True, help="configs/eval/ctaic_*.yaml")
|
| 40 |
-
|
| 41 |
-
cfg_path =
|
| 42 |
cfg = load_yaml_config(cfg_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
return 0
|
| 51 |
|
| 52 |
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
+
"""Codec test for C-TAIC (extension layer).
|
| 3 |
|
| 4 |
+
Reports extension-layer likelihood bpp and feature distortion D (paper: bpp excludes base layer).
|
| 5 |
+
Optional actual bitstream bpp via compress/decompress.
|
| 6 |
+
Does NOT compute task metrics (mAP / mIoU / PQ / OKS) — those come later.
|
| 7 |
+
|
| 8 |
+
Example:
|
| 9 |
+
python scripts/eval_ctaic.py -c configs/eval/ctaic_s1.yaml
|
| 10 |
+
python scripts/eval_ctaic.py -c configs/eval/ctaic_s1.yaml --no-condition # TAIC-mode ablation
|
| 11 |
+
python scripts/eval_ctaic.py -c configs/eval/ctaic_s1.yaml --actual-bpp
|
| 12 |
"""
|
| 13 |
|
| 14 |
from __future__ import annotations
|
| 15 |
|
| 16 |
import argparse
|
| 17 |
+
import json
|
| 18 |
import os
|
| 19 |
import sys
|
| 20 |
+
from datetime import datetime
|
| 21 |
|
| 22 |
+
import torch
|
| 23 |
+
from torch.utils.data import DataLoader
|
| 24 |
|
| 25 |
+
REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
| 26 |
+
if REPO_ROOT not in sys.path:
|
| 27 |
+
sys.path.insert(0, REPO_ROOT)
|
| 28 |
|
| 29 |
+
from flexicm.data import COCOImageDataset, COCOWholeBodyImageDataset, ImageFolderDataset, build_test_transform
|
| 30 |
+
from flexicm.models import CTAIC, TAIC
|
| 31 |
+
from flexicm.tasks import TASK_META, build_teacher
|
| 32 |
+
from flexicm.tasks.losses import TAICCriterion
|
| 33 |
+
from flexicm.utils.codec_test import resolve_ckpt, test_ctaic_loader
|
| 34 |
+
from flexicm.utils.train_utils import load_checkpoint_dict, load_yaml_config, set_seed
|
| 35 |
|
| 36 |
+
SCENARIOS = {
|
| 37 |
+
"s1": {"base": "detection", "ext": "instance"},
|
| 38 |
+
"s2": {"base": "semantic", "ext": "panoptic"},
|
| 39 |
+
"s3": {"base": "detection", "ext": "pose"},
|
| 40 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
|
| 43 |
+
def parse_args(argv):
|
| 44 |
+
parser = argparse.ArgumentParser("Codec test: FlexICM C-TAIC")
|
| 45 |
parser.add_argument("-c", "--config", required=True, help="configs/eval/ctaic_*.yaml")
|
| 46 |
+
given, remaining = parser.parse_known_args(argv)
|
| 47 |
+
cfg_path = given.config if os.path.isabs(given.config) else os.path.join(REPO_ROOT, given.config)
|
| 48 |
cfg = load_yaml_config(cfg_path)
|
| 49 |
+
parser.set_defaults(**cfg)
|
| 50 |
+
parser.add_argument("--actual-bpp", action="store_true")
|
| 51 |
+
parser.add_argument("--no-condition", action="store_true", help="Disable base-layer conditioning (TAIC mode)")
|
| 52 |
+
parser.add_argument("--max-batches", type=int, default=None)
|
| 53 |
+
parser.add_argument("--split", type=str, default=None)
|
| 54 |
+
args = parser.parse_args(remaining)
|
| 55 |
+
args.config = cfg_path
|
| 56 |
+
if "--actual-bpp" in argv:
|
| 57 |
+
args.actual_bpp = True
|
| 58 |
+
if "--no-condition" in argv:
|
| 59 |
+
args.no_condition = True
|
| 60 |
+
return args
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def build_loader(args, ext_task, device):
|
| 64 |
+
split = args.split or getattr(args, "split", None) or "val2017"
|
| 65 |
+
tf = build_test_transform()
|
| 66 |
+
root = args.dataset_path
|
| 67 |
+
split_dir = os.path.join(root, split)
|
| 68 |
+
if os.path.isdir(split_dir):
|
| 69 |
+
if ext_task == "pose":
|
| 70 |
+
dataset = COCOWholeBodyImageDataset(root, split, tf)
|
| 71 |
+
else:
|
| 72 |
+
dataset = COCOImageDataset(root, split, tf)
|
| 73 |
+
else:
|
| 74 |
+
dataset = ImageFolderDataset(root, tf)
|
| 75 |
+
return DataLoader(
|
| 76 |
+
dataset,
|
| 77 |
+
batch_size=getattr(args, "test_batch_size", 1),
|
| 78 |
+
shuffle=False,
|
| 79 |
+
num_workers=getattr(args, "num_workers", 4),
|
| 80 |
+
pin_memory=(device == "cuda"),
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def main(argv):
|
| 85 |
+
args = parse_args(argv)
|
| 86 |
+
set_seed(getattr(args, "seed", 42))
|
| 87 |
+
|
| 88 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = str(getattr(args, "gpu_id", 0))
|
| 89 |
+
device = "cuda" if getattr(args, "cuda", True) and torch.cuda.is_available() else "cpu"
|
| 90 |
+
|
| 91 |
+
scenario = args.scenario
|
| 92 |
+
base_task = SCENARIOS[scenario]["base"]
|
| 93 |
+
ext_task = SCENARIOS[scenario]["ext"]
|
| 94 |
+
base_meta = TASK_META[base_task]
|
| 95 |
+
ext_meta = TASK_META[ext_task]
|
| 96 |
+
out_channels = getattr(args, "out_channels", ext_meta["out_channels"])
|
| 97 |
+
align_mode = getattr(args, "align_mode", ext_meta["align_mode"])
|
| 98 |
+
lmbda = getattr(args, "lmbda", 0.0035)
|
| 99 |
+
use_condition = not bool(getattr(args, "no_condition", False))
|
| 100 |
+
|
| 101 |
+
ext_ckpt = resolve_ckpt(args.checkpoint, REPO_ROOT, label="C-TAIC checkpoint")
|
| 102 |
+
base_ckpt = resolve_ckpt(args.base_taic_checkpoint, REPO_ROOT, label="base TAIC checkpoint")
|
| 103 |
+
|
| 104 |
+
print(f"Loading base TAIC ({base_task}): {base_ckpt}")
|
| 105 |
+
base = TAIC(N=128, M=192, out_channels=base_meta["out_channels"]).to(device)
|
| 106 |
+
state, _ = load_checkpoint_dict(base_ckpt, map_location=device)
|
| 107 |
+
base.load_state_dict(state, strict=False)
|
| 108 |
+
base.eval()
|
| 109 |
+
for p in base.parameters():
|
| 110 |
+
p.requires_grad = False
|
| 111 |
+
|
| 112 |
+
print(f"Loading C-TAIC extension ({ext_task}): {ext_ckpt}")
|
| 113 |
+
net = CTAIC(N=128, M=192, out_channels=out_channels).to(device)
|
| 114 |
+
state, _ = load_checkpoint_dict(ext_ckpt, map_location=device)
|
| 115 |
+
net.load_state_dict(state, strict=False)
|
| 116 |
+
net.eval()
|
| 117 |
+
|
| 118 |
+
teacher = build_teacher(ext_task, pretrained_backbone=getattr(args, "pretrained_backbone", True))
|
| 119 |
+
teacher = teacher.to(device).eval()
|
| 120 |
+
criterion = TAICCriterion(lmbda=lmbda, align_mode=align_mode)
|
| 121 |
+
|
| 122 |
+
loader = build_loader(args, ext_task, device)
|
| 123 |
+
print(
|
| 124 |
+
f"Test set size: {len(loader.dataset)} device={device} "
|
| 125 |
+
f"scenario={scenario} use_condition={use_condition}"
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
result = test_ctaic_loader(
|
| 129 |
+
net,
|
| 130 |
+
base,
|
| 131 |
+
teacher,
|
| 132 |
+
loader,
|
| 133 |
+
criterion,
|
| 134 |
+
device,
|
| 135 |
+
use_condition=use_condition,
|
| 136 |
+
align_divisor=256,
|
| 137 |
+
run_actual_bpp=bool(getattr(args, "actual_bpp", False)),
|
| 138 |
+
max_batches=args.max_batches,
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
print("==== C-TAIC codec test summary ====")
|
| 142 |
+
for k, v in result.items():
|
| 143 |
+
if isinstance(v, float):
|
| 144 |
+
print(f" {k}: {v:.6f}")
|
| 145 |
+
else:
|
| 146 |
+
print(f" {k}: {v}")
|
| 147 |
|
| 148 |
+
out_dir = getattr(args, "result_dir", None) or os.path.join(
|
| 149 |
+
REPO_ROOT, "logs", "eval_ctaic", scenario, str(getattr(args, "quality_level", 1))
|
| 150 |
+
)
|
| 151 |
+
os.makedirs(out_dir, exist_ok=True)
|
| 152 |
+
out_json = os.path.join(out_dir, f"codec_test_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json")
|
| 153 |
+
payload = {
|
| 154 |
+
"scenario": scenario,
|
| 155 |
+
"base_task": base_task,
|
| 156 |
+
"ext_task": ext_task,
|
| 157 |
+
"checkpoint": ext_ckpt,
|
| 158 |
+
"base_taic_checkpoint": base_ckpt,
|
| 159 |
+
"config": args.config,
|
| 160 |
+
"result": result,
|
| 161 |
+
}
|
| 162 |
+
with open(out_json, "w") as f:
|
| 163 |
+
json.dump(payload, f, indent=2)
|
| 164 |
+
print(f"Wrote {out_json}")
|
| 165 |
return 0
|
| 166 |
|
| 167 |
|
scripts/eval_taic.py
CHANGED
|
@@ -1,51 +1,142 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
"""
|
| 7 |
|
| 8 |
from __future__ import annotations
|
| 9 |
|
| 10 |
import argparse
|
|
|
|
| 11 |
import os
|
| 12 |
import sys
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
| 15 |
-
sys.path
|
| 16 |
-
|
| 17 |
-
from flexicm.utils.train_utils import load_yaml_config
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
def resolve_ckpt(path: str) -> str:
|
| 21 |
-
if not path:
|
| 22 |
-
raise FileNotFoundError("checkpoint path is empty")
|
| 23 |
-
if not os.path.isabs(path):
|
| 24 |
-
path = os.path.join(REPO_ROOT, path)
|
| 25 |
-
placeholder = os.path.join(os.path.dirname(path), "PLACEHOLDER")
|
| 26 |
-
if os.path.isfile(placeholder) and not os.path.isfile(path):
|
| 27 |
-
raise FileNotFoundError(
|
| 28 |
-
f"Checkpoint not ready (PLACEHOLDER still present):\n {placeholder}\n"
|
| 29 |
-
f"Expected real weights at:\n {path}\n"
|
| 30 |
-
"See checkpoints/README.md"
|
| 31 |
-
)
|
| 32 |
-
if not os.path.isfile(path):
|
| 33 |
-
raise FileNotFoundError(f"Missing checkpoint: {path}")
|
| 34 |
-
if os.path.basename(path) == "PLACEHOLDER" or path.endswith(".txt"):
|
| 35 |
-
raise FileNotFoundError(f"Refusing to load placeholder file: {path}")
|
| 36 |
-
return path
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
|
|
|
| 41 |
parser.add_argument("-c", "--config", required=True, help="configs/eval/taic_*.yaml")
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
return 0
|
| 50 |
|
| 51 |
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
+
"""Codec test for TAIC (base layer).
|
| 3 |
|
| 4 |
+
Reports likelihood bpp, feature-alignment distortion D, and optional actual bitstream bpp.
|
| 5 |
+
Does NOT compute task metrics (mAP / mIoU / PQ / OKS) — those come later.
|
| 6 |
+
|
| 7 |
+
Example:
|
| 8 |
+
python scripts/eval_taic.py -c configs/eval/taic_detection.yaml
|
| 9 |
+
python scripts/eval_taic.py -c configs/eval/taic_detection.yaml --actual-bpp
|
| 10 |
"""
|
| 11 |
|
| 12 |
from __future__ import annotations
|
| 13 |
|
| 14 |
import argparse
|
| 15 |
+
import json
|
| 16 |
import os
|
| 17 |
import sys
|
| 18 |
+
from datetime import datetime
|
| 19 |
+
|
| 20 |
+
import torch
|
| 21 |
+
from torch.utils.data import DataLoader
|
| 22 |
|
| 23 |
REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
| 24 |
+
if REPO_ROOT not in sys.path:
|
| 25 |
+
sys.path.insert(0, REPO_ROOT)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
+
from flexicm.data import (
|
| 28 |
+
COCOImageDataset,
|
| 29 |
+
COCOWholeBodyImageDataset,
|
| 30 |
+
ImageFolderDataset,
|
| 31 |
+
build_test_transform,
|
| 32 |
+
)
|
| 33 |
+
from flexicm.models import TAIC
|
| 34 |
+
from flexicm.tasks import TASK_META, build_teacher
|
| 35 |
+
from flexicm.tasks.losses import TAICCriterion
|
| 36 |
+
from flexicm.utils.codec_test import resolve_ckpt, test_taic_loader
|
| 37 |
+
from flexicm.utils.train_utils import load_checkpoint_dict, load_yaml_config, set_seed
|
| 38 |
|
| 39 |
+
|
| 40 |
+
def parse_args(argv):
|
| 41 |
+
parser = argparse.ArgumentParser("Codec test: FlexICM TAIC")
|
| 42 |
parser.add_argument("-c", "--config", required=True, help="configs/eval/taic_*.yaml")
|
| 43 |
+
given, remaining = parser.parse_known_args(argv)
|
| 44 |
+
cfg_path = given.config if os.path.isabs(given.config) else os.path.join(REPO_ROOT, given.config)
|
| 45 |
+
cfg = load_yaml_config(cfg_path)
|
| 46 |
+
parser.set_defaults(**cfg)
|
| 47 |
+
parser.add_argument("--actual-bpp", action="store_true", help="Also run compress/decompress bpp")
|
| 48 |
+
parser.add_argument("--max-batches", type=int, default=None, help="Limit batches for a smoke test")
|
| 49 |
+
parser.add_argument("--split", type=str, default=None, help="Override image split folder, e.g. val2017")
|
| 50 |
+
args = parser.parse_args(remaining)
|
| 51 |
+
args.config = cfg_path
|
| 52 |
+
if given.__dict__.get("actual_bpp") or "--actual-bpp" in argv:
|
| 53 |
+
args.actual_bpp = True
|
| 54 |
+
return args
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def build_loader(args, device):
|
| 58 |
+
split = args.split or getattr(args, "split", None) or "val2017"
|
| 59 |
+
tf = build_test_transform()
|
| 60 |
+
root = args.dataset_path
|
| 61 |
+
split_dir = os.path.join(root, split)
|
| 62 |
+
if os.path.isdir(split_dir):
|
| 63 |
+
if args.task == "pose":
|
| 64 |
+
dataset = COCOWholeBodyImageDataset(root, split, tf)
|
| 65 |
+
else:
|
| 66 |
+
dataset = COCOImageDataset(root, split, tf)
|
| 67 |
+
else:
|
| 68 |
+
dataset = ImageFolderDataset(root, tf)
|
| 69 |
+
|
| 70 |
+
return DataLoader(
|
| 71 |
+
dataset,
|
| 72 |
+
batch_size=getattr(args, "test_batch_size", 1),
|
| 73 |
+
shuffle=False,
|
| 74 |
+
num_workers=getattr(args, "num_workers", 4),
|
| 75 |
+
pin_memory=(device == "cuda"),
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def main(argv):
|
| 80 |
+
args = parse_args(argv)
|
| 81 |
+
set_seed(getattr(args, "seed", 42))
|
| 82 |
+
|
| 83 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = str(getattr(args, "gpu_id", 0))
|
| 84 |
+
device = "cuda" if getattr(args, "cuda", True) and torch.cuda.is_available() else "cpu"
|
| 85 |
+
|
| 86 |
+
task = args.task
|
| 87 |
+
meta = TASK_META[task]
|
| 88 |
+
out_channels = getattr(args, "out_channels", meta["out_channels"])
|
| 89 |
+
align_mode = getattr(args, "align_mode", meta["align_mode"])
|
| 90 |
+
lmbda = getattr(args, "lmbda", 0.0035)
|
| 91 |
+
|
| 92 |
+
ckpt = resolve_ckpt(args.checkpoint, REPO_ROOT, label="TAIC checkpoint")
|
| 93 |
+
print(f"Loading TAIC checkpoint: {ckpt}")
|
| 94 |
+
|
| 95 |
+
net = TAIC(N=128, M=192, out_channels=out_channels).to(device)
|
| 96 |
+
state, _ = load_checkpoint_dict(ckpt, map_location=device)
|
| 97 |
+
missing = net.load_state_dict(state, strict=False)
|
| 98 |
+
print(f"load_state_dict: missing={len(missing.missing_keys)} unexpected={len(missing.unexpected_keys)}")
|
| 99 |
+
net.eval()
|
| 100 |
+
|
| 101 |
+
teacher = build_teacher(task, pretrained_backbone=getattr(args, "pretrained_backbone", True))
|
| 102 |
+
teacher = teacher.to(device).eval()
|
| 103 |
+
criterion = TAICCriterion(lmbda=lmbda, align_mode=align_mode)
|
| 104 |
+
|
| 105 |
+
loader = build_loader(args, device)
|
| 106 |
+
print(f"Test set size: {len(loader.dataset)} device={device} task={task}")
|
| 107 |
+
|
| 108 |
+
result = test_taic_loader(
|
| 109 |
+
net,
|
| 110 |
+
teacher,
|
| 111 |
+
loader,
|
| 112 |
+
criterion,
|
| 113 |
+
device,
|
| 114 |
+
align_divisor=256,
|
| 115 |
+
run_actual_bpp=bool(getattr(args, "actual_bpp", False)),
|
| 116 |
+
max_batches=args.max_batches,
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
print("==== TAIC codec test summary ====")
|
| 120 |
+
for k, v in result.items():
|
| 121 |
+
if isinstance(v, float):
|
| 122 |
+
print(f" {k}: {v:.6f}")
|
| 123 |
+
else:
|
| 124 |
+
print(f" {k}: {v}")
|
| 125 |
|
| 126 |
+
out_dir = getattr(args, "result_dir", None) or os.path.join(
|
| 127 |
+
REPO_ROOT, "logs", "eval_taic", task, str(getattr(args, "quality_level", 1))
|
| 128 |
+
)
|
| 129 |
+
os.makedirs(out_dir, exist_ok=True)
|
| 130 |
+
out_json = os.path.join(out_dir, f"codec_test_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json")
|
| 131 |
+
payload = {
|
| 132 |
+
"task": task,
|
| 133 |
+
"checkpoint": ckpt,
|
| 134 |
+
"config": args.config,
|
| 135 |
+
"result": result,
|
| 136 |
+
}
|
| 137 |
+
with open(out_json, "w") as f:
|
| 138 |
+
json.dump(payload, f, indent=2)
|
| 139 |
+
print(f"Wrote {out_json}")
|
| 140 |
return 0
|
| 141 |
|
| 142 |
|