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 CHANGED
@@ -122,7 +122,9 @@ FlexICM/
122
  ├── scripts/
123
  │ ├── download_base_codecs.sh
124
  │ ├── train_taic.py
125
- │ └── train_ctaic.py
 
 
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 configs (stub until full metrics are implemented): `configs/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
- Eval scripts will refuse to run if a `PLACEHOLDER` file is still present
64
- or if the `.pth.tar` is missing.
 
 
 
 
 
 
 
 
 
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
- # Eval config (stub) — s1: detection (base) + instance (extension)
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
- # Eval config (stub) — replace PLACEHOLDER weights under checkpoints/taic/detection/
 
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
- # Eval config (stub) — replace PLACEHOLDER weights under checkpoints/taic/instance/
 
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
- # Eval config (stub) — replace PLACEHOLDER weights under checkpoints/taic/panoptic/
 
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
- # Eval config (stub) — replace PLACEHOLDER weights under checkpoints/taic/pose/
 
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
- # Eval config (stub) — replace PLACEHOLDER weights under checkpoints/taic/semantic/
 
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
- """Eval / test entry for C-TAIC (placeholder).
3
 
4
- Full multi-task rate-accuracy evaluation will be added later.
5
- This script only validates that required checkpoints exist and are not PLACEHOLDERs.
 
 
 
 
 
 
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.insert(0, REPO_ROOT)
16
 
17
- from flexicm.utils.train_utils import load_yaml_config
 
 
18
 
 
 
 
 
 
 
19
 
20
- def resolve_ckpt(path: str, label: str) -> str:
21
- if not path:
22
- raise FileNotFoundError(f"{label}: 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"{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 main(argv):
38
- parser = argparse.ArgumentParser("Eval FlexICM C-TAIC (stub)")
39
  parser.add_argument("-c", "--config", required=True, help="configs/eval/ctaic_*.yaml")
40
- args = parser.parse_args(argv)
41
- cfg_path = args.config if os.path.isabs(args.config) else os.path.join(REPO_ROOT, args.config)
42
  cfg = load_yaml_config(cfg_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- ext = resolve_ckpt(cfg["checkpoint"], "extension C-TAIC")
45
- base = resolve_ckpt(cfg["base_taic_checkpoint"], "base TAIC")
46
- print(f"[eval_ctaic stub] extension ckpt ok: {ext}")
47
- print(f"[eval_ctaic stub] base ckpt ok: {base}")
48
- print(f"[eval_ctaic stub] scenario={cfg.get('scenario')} quality={cfg.get('quality_level')}")
49
- print("[eval_ctaic stub] Full metric evaluation is not implemented yet.")
 
 
 
 
 
 
 
 
 
 
 
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
- """Eval / test entry for TAIC (placeholder).
3
 
4
- Full rate-accuracy evaluation (mAP / mIoU / PQ / OKS) will be added later.
5
- This script only validates that the requested checkpoint exists and is not a PLACEHOLDER.
 
 
 
 
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.insert(0, REPO_ROOT)
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
- def main(argv):
40
- parser = argparse.ArgumentParser("Eval FlexICM TAIC (stub)")
 
41
  parser.add_argument("-c", "--config", required=True, help="configs/eval/taic_*.yaml")
42
- args = parser.parse_args(argv)
43
- cfg = load_yaml_config(args.config if os.path.isabs(args.config) else os.path.join(REPO_ROOT, args.config))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
- ckpt = resolve_ckpt(cfg["checkpoint"])
46
- print(f"[eval_taic stub] checkpoint ok: {ckpt}")
47
- print(f"[eval_taic stub] task={cfg.get('task')} quality={cfg.get('quality_level')}")
48
- print("[eval_taic stub] Full metric evaluation is not implemented yet.")
 
 
 
 
 
 
 
 
 
 
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