next-frame-predict / anomaly.py
dvdface's picture
Simplify project structure: rename dirs and files
8b78124 verified
Raw History Blame Contribute Delete
3.27 kB
from __future__ import annotations
import argparse
import csv
import json
from pathlib import Path
from typing import Any, Dict, List, Optional
import numpy as np
from infer.io import load_array, load_frames_from_dir
from infer.predictor import Predictor
from infer.anomaly import compute_anomaly_from_4frames
def _parse_args() -> argparse.Namespace:
ap = argparse.ArgumentParser(description="Compute per-window anomaly scores (ahat_error and 1-SSIM vectors).")
ap.add_argument("--model_dir", type=str, default="savedmodel")
ap.add_argument("--frames_dir", type=str, default=None)
ap.add_argument("--array", type=str, default=None)
ap.add_argument("--out_json", type=str, default="anomaly.json")
ap.add_argument("--out_csv", type=str, default="anomaly.csv")
ap.add_argument("--start", type=int, default=0, help="Start frame index (inclusive) on the full sequence")
ap.add_argument("--end", type=int, default=-1, help="End frame index (exclusive); -1 means to the end")
return ap.parse_args()
def main() -> None:
args = _parse_args()
if (args.frames_dir is None) == (args.array is None):
raise SystemExit("Provide exactly one of --frames_dir or --array")
if args.frames_dir is not None:
frames = load_frames_from_dir(args.frames_dir) # [T,H,W,C] 0..255
else:
frames = load_array(args.array)
if frames.ndim == 5:
if frames.shape[0] != 1:
raise SystemExit("Batch arrays are not supported in CLI; please provide a single sequence.")
frames = frames[0]
if frames.ndim != 4 or frames.shape[0] < 4:
raise SystemExit(f"Expected frames with shape [T,H,W,C] and T>=4, got {frames.shape}")
T = frames.shape[0]
start = max(0, int(args.start))
end = T if int(args.end) == -1 else min(T, int(args.end))
if end - start < 4:
raise SystemExit("Range too small; need at least 4 frames.")
pred = Predictor(args.model_dir)
if pred.sequence_len is not None and pred.sequence_len != 4:
raise SystemExit(f"This tool assumes model sequence_len=4, got {pred.sequence_len}")
rows: List[Dict[str, Any]] = []
for t in range(start + 3, end):
win = frames[t - 3 : t + 1] # [4,H,W,C] where win[3] is GT frame4
res = compute_anomaly_from_4frames(pred, win)
rows.append(
{
"t": int(t),
"ahat_error": res.ahat_error.tolist(),
"dissim_1mssim": res.dissim_1mssim.tolist(),
}
)
out_json = Path(args.out_json)
out_json.parent.mkdir(parents=True, exist_ok=True)
out_json.write_text(json.dumps({"items": rows}, ensure_ascii=False, indent=2), encoding="utf-8")
out_csv = Path(args.out_csv)
out_csv.parent.mkdir(parents=True, exist_ok=True)
with out_csv.open("w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow(["t", "ahat_error_1", "ahat_error_2", "ahat_error_3", "ahat_error_4", "dissim_1", "dissim_2", "dissim_3", "dissim_4"])
for r in rows:
a = r["ahat_error"]
d = r["dissim_1mssim"]
w.writerow([r["t"], *a, *d])
print(f"Wrote: {out_json}")
print(f"Wrote: {out_csv}")
if __name__ == "__main__":
main()