FlexICM / scripts /eval_taic.py
tianma
Support optional fixed eval_size and update detection configs.
3482727
Raw History Blame
8.9 kB
#!/usr/bin/env python3
"""Test / eval for TAIC: codec stats + optional full task-network metrics.
Codec-only (default):
bpp, feature distortion D, loss; optional --actual-bpp
With task metrics (--with-metrics):
also load official task-network config/checkpoint, run truncated task net from h,
report mAP-bbox / mAP-mask / mIoU / PQ / mAP-OKS (task-dependent).
Examples:
python scripts/eval_taic.py -c configs/eval/taic_detection.yaml
python scripts/eval_taic.py -c configs/eval/taic_detection.yaml --with-metrics
python scripts/eval_taic.py -c configs/eval/taic_detection.yaml --with-metrics --max-batches 50
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from datetime import datetime
import torch
from torch.utils.data import DataLoader
REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
if REPO_ROOT not in sys.path:
sys.path.insert(0, REPO_ROOT)
from flexicm.data import (
COCOImageDataset,
COCOWholeBodyImageDataset,
ImageFolderDataset,
build_test_transform,
)
from flexicm.data.coco_eval import COCOEvalDataset, TASK_ANN_FILES, coco_eval_collate
from flexicm.models import TAIC
from flexicm.tasks import TASK_META, build_teacher
from flexicm.tasks.losses import TAICCriterion
from flexicm.tasks.metric_eval import run_task_metric_eval
from flexicm.tasks.metric_runners import (
DEFAULT_TASK_NET_CKPTS,
DEFAULT_TASK_NET_CONFIGS,
build_metric_runner,
)
from flexicm.utils.codec_test import resolve_ckpt, test_taic_loader
from flexicm.utils.train_utils import load_checkpoint_dict, load_yaml_config, set_seed
def parse_args(argv):
parser = argparse.ArgumentParser("Test FlexICM TAIC (codec + optional task metrics)")
parser.add_argument("-c", "--config", required=True, help="configs/eval/taic_*.yaml")
given, remaining = parser.parse_known_args(argv)
cfg_path = given.config if os.path.isabs(given.config) else os.path.join(REPO_ROOT, given.config)
cfg = load_yaml_config(cfg_path)
# -c already consumed by the first parse; keep it as a default for the second pass
parser.set_defaults(config=cfg_path, **cfg)
for action in parser._actions:
if "--config" in action.option_strings:
action.required = False
break
parser.add_argument("--actual-bpp", action="store_true")
parser.add_argument("--with-metrics", action="store_true", help="Run full task-network metrics")
parser.add_argument("--max-batches", type=int, default=None)
parser.add_argument("--split", type=str, default=None)
parser.add_argument(
"--eval-size",
type=int,
default=cfg.get("eval_size", None),
help="If set (e.g. 256), resize eval images to eval_size×eval_size",
)
args = parser.parse_args(remaining)
args.config = cfg_path
if "--actual-bpp" in argv:
args.actual_bpp = True
if "--with-metrics" in argv:
args.with_metrics = True
return args
def build_codec_loader(args, device):
split = args.split or getattr(args, "split", None) or "val2017"
eval_size = getattr(args, "eval_size", None)
tf = build_test_transform(eval_size=eval_size)
root = args.dataset_path
split_dir = os.path.join(root, split)
if os.path.isdir(split_dir):
if args.task == "pose":
dataset = COCOWholeBodyImageDataset(root, split, tf)
else:
dataset = COCOImageDataset(root, split, tf)
else:
dataset = ImageFolderDataset(root, tf)
return DataLoader(
dataset,
batch_size=getattr(args, "test_batch_size", 1),
shuffle=False,
num_workers=getattr(args, "num_workers", 4),
pin_memory=(device == "cuda"),
)
def build_metric_loader(args, device):
split = args.split or getattr(args, "split", None) or "val2017"
ann_rel = getattr(args, "ann_file", None) or TASK_ANN_FILES.get(args.task)
if ann_rel is None:
raise ValueError(f"No ann_file for task={args.task}")
ann_file = ann_rel if os.path.isabs(ann_rel) else os.path.join(args.dataset_path, ann_rel)
eval_size = getattr(args, "eval_size", None)
dataset = COCOEvalDataset(
args.dataset_path,
ann_file=ann_file,
image_prefix=split,
transform=build_test_transform(eval_size=eval_size),
)
loader = DataLoader(
dataset,
batch_size=1,
shuffle=False,
num_workers=getattr(args, "num_workers", 4),
pin_memory=(device == "cuda"),
collate_fn=coco_eval_collate,
)
return loader, ann_file
def main(argv):
args = parse_args(argv)
set_seed(getattr(args, "seed", 42))
os.environ["CUDA_VISIBLE_DEVICES"] = str(getattr(args, "gpu_id", 0))
device = "cuda" if getattr(args, "cuda", True) and torch.cuda.is_available() else "cpu"
task = args.task
meta = TASK_META[task]
out_channels = getattr(args, "out_channels", meta["out_channels"])
align_mode = getattr(args, "align_mode", meta["align_mode"])
lmbda = getattr(args, "lmbda", 0.0035)
ckpt = resolve_ckpt(args.checkpoint, REPO_ROOT, label="TAIC checkpoint")
print(f"Loading TAIC checkpoint: {ckpt}")
net = TAIC(N=128, M=192, out_channels=out_channels).to(device)
state, _ = load_checkpoint_dict(ckpt, map_location=device)
missing = net.load_state_dict(state, strict=False)
print(f"load_state_dict: missing={len(missing.missing_keys)} unexpected={len(missing.unexpected_keys)}")
net.eval()
# ---- codec test ----
teacher = build_teacher(
task,
pretrained_backbone=getattr(args, "pretrained_backbone", True),
use_official_teacher=getattr(args, "use_official_teacher", True),
task_config=getattr(args, "task_config", None),
task_checkpoint=getattr(args, "task_checkpoint", None),
device=device,
)
teacher = teacher.to(device).eval()
criterion = TAICCriterion(lmbda=lmbda, align_mode=align_mode)
codec_loader = build_codec_loader(args, device)
eval_size = getattr(args, "eval_size", None)
size_msg = f"{eval_size}x{eval_size}" if eval_size else "original"
print(f"[codec] test set size: {len(codec_loader.dataset)} device={device} task={task} input={size_msg}")
codec_result = test_taic_loader(
net,
teacher,
codec_loader,
criterion,
device,
align_divisor=256,
run_actual_bpp=bool(getattr(args, "actual_bpp", False)),
max_batches=args.max_batches,
)
print("==== TAIC codec test summary ====")
for k, v in codec_result.items():
print(f" {k}: {v:.6f}" if isinstance(v, float) else f" {k}: {v}")
payload = {
"task": task,
"checkpoint": ckpt,
"config": args.config,
"codec_result": codec_result,
}
# ---- optional task metrics ----
if getattr(args, "with_metrics", False):
task_cfg = getattr(args, "task_config", None) or DEFAULT_TASK_NET_CONFIGS[task]
task_ckpt = getattr(args, "task_checkpoint", None) or DEFAULT_TASK_NET_CKPTS[task]
if not os.path.isabs(task_cfg):
task_cfg = os.path.join(REPO_ROOT, task_cfg)
task_ckpt = resolve_ckpt(task_ckpt, REPO_ROOT, label=f"{task} task-network checkpoint")
print(f"[metric] loading task network:\n config={task_cfg}\n ckpt={task_ckpt}")
runner = build_metric_runner(task, device=device)
runner.load(task_cfg, task_ckpt)
metric_loader, ann_file = build_metric_loader(args, device)
print(f"[metric] COCO eval images: {len(metric_loader.dataset)} ann={ann_file}")
metrics = run_task_metric_eval(
net,
runner,
metric_loader,
device,
ann_file=ann_file,
use_condition=False,
base_codec=None,
max_batches=args.max_batches,
finalize_kwargs={
"gt_folder": getattr(args, "panoptic_gt_folder", None),
"pred_folder": getattr(args, "panoptic_pred_folder", None),
"num_classes": getattr(args, "num_classes", 133),
},
)
print("==== TAIC task metric summary ====")
for k, v in metrics.items():
print(f" {k}: {v}")
payload["task_config"] = task_cfg
payload["task_checkpoint"] = task_ckpt
payload["task_metrics"] = metrics
out_dir = getattr(args, "result_dir", None) or os.path.join(
REPO_ROOT, "logs", "eval_taic", task, str(getattr(args, "quality_level", 1))
)
os.makedirs(out_dir, exist_ok=True)
out_json = os.path.join(out_dir, f"eval_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json")
with open(out_json, "w") as f:
json.dump(payload, f, indent=2, default=str)
print(f"Wrote {out_json}")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))