Add more preview

This commit is contained in:
Xin Wang
2026-08-28 00:46:05 +08:00
parent b5ce64702a
commit 9bd204b468
3 changed files with 200 additions and 7 deletions
+95 -1
View File
@@ -23,7 +23,7 @@ from extract_turn_local_features import (
FrozenQwenLocalEncoder, ego4d_clip_boxes, extract_local,
validate_source_timeline,
model_config_digest,
box_coordinate_offset, frame_crops, parse_args, preview_boxes,
box_coordinate_offset, frame_crops, parse_args, preview_boxes, preview_ticks, preview_time_bounds,
TESTED_TRANSFORMERS_VERSIONS, validate_local_transformers_version,
main as extract_main,
)
@@ -160,6 +160,100 @@ class LocalTests(unittest.TestCase):
self.assertEqual(audit[0]["boxes"][0]["box"], [100, 80, 160, 140])
self.assertTrue((folder / audit[0]["image"]).is_file())
def test_preview_can_save_more_than_four_images_in_a_later_range(self):
clip = SimpleNamespace(path=Path("source.mp4"), width=640, height=360,
crop=None, dense_start=0.)
media = SimpleNamespace(clips=[clip],
_source_frame_for_time=lambda t: (0, round(t * 2), t),
frame_at_or_before=lambda t: Image.new("RGB", (640, 360)))
with tempfile.TemporaryDirectory() as directory:
args = SimpleNamespace(box_coordinate_space="camera", crop_expansion=1., video_fps=2.,
output_root=Path(directory), preview_count=8,
preview_start=2., preview_end=6., preview_with_boxes=False)
preview_boxes(media, [{}], args, "clip", 10.)
folder = Path(directory) / "clip_preview"
audit = json.loads((folder / "boxes.json").read_text())
self.assertEqual([row["time"] for row in audit], [2., 2.5, 3., 3.5, 4., 4.5, 5., 5.5])
self.assertEqual(audit[0]["image"], "frame_000004.jpg")
self.assertEqual(len(list(folder.glob("*.jpg"))), 8)
report = json.loads((folder / "preview.json").read_text())
self.assertEqual(report["saved_count"], 8)
self.assertEqual(report["saved_with_boxes"], 0)
def test_boxed_preview_filters_valid_corrected_crops_without_decoding(self):
# Encoded boxes are corrected by the crop origin, as in feature extraction.
clip = SimpleNamespace(crop=(204, 0, 640, 360), width=844, height=360)
media = SimpleNamespace(clips=[clip], _source_frame_for_time=lambda t: (0, round(t * 2), t))
valid = {"id": "p", "xyxy": [304, 80, 364, 140]}
invalid = {"id": "p", "xyxy": [4, 80, 64, 140]} # entirely inside removed banner
annotations = [{1: [valid], 3: [invalid], 5: [valid], 7: [valid]}]
args = SimpleNamespace(box_coordinate_space="encoded", crop_expansion=1., video_fps=2.,
preview_count=2, preview_start=0., preview_end=None, preview_with_boxes=True)
selected, grid_count, eligible_count = preview_ticks(media, annotations, args, 4.)
self.assertEqual(selected, [1, 7]) # uniform among valid frames, not first N
self.assertEqual((grid_count, eligible_count), (8, 3))
# No RGB reader is needed by the scan, and annotations are not changed.
self.assertEqual(len(annotations[0]), 4)
def test_empty_boxed_preview_writes_an_explicit_empty_report(self):
clip = SimpleNamespace(crop=None, width=640, height=360)
media = SimpleNamespace(clips=[clip], _source_frame_for_time=lambda t: (0, round(t * 2), t))
with tempfile.TemporaryDirectory() as directory:
args = SimpleNamespace(box_coordinate_space="camera", crop_expansion=1., video_fps=2.,
output_root=Path(directory), preview_count=40,
preview_start=0., preview_end=None, preview_with_boxes=True)
preview_boxes(media, [{}], args, "empty_clip", 4.)
folder = Path(directory) / "empty_clip_preview"
self.assertEqual(json.loads((folder / "boxes.json").read_text()), [])
report = json.loads((folder / "preview.json").read_text())
self.assertEqual(report["eligible_frame_count"], 0)
self.assertEqual(report["saved_count"], 0)
self.assertEqual(list(folder.glob("*.jpg")), [])
def test_preview_range_clips_to_available_end_and_excludes_endpoint(self):
args = SimpleNamespace(video_fps=2., preview_start=.1, preview_end=100., preview_count=100)
self.assertEqual(preview_time_bounds(args, 1.), (.1, 1.))
self.assertEqual(preview_ticks(None, [], args, 1.), ([1], 1, 1))
args.preview_start = 2.
with self.assertRaisesRegex(ValueError, "outside the available prefix"):
preview_time_bounds(args, 1.)
def test_preview_cli_validation(self):
import contextlib
import io
required = ["--data-root", "/raw", "--feature-root", "/global", "--target-root", "/targets",
"--output-root", "/preview", "--samples", "4"]
valid = parse_args(required + ["--preview-only", "--preview-count", "40", "--preview-start", "30",
"--preview-end", "120", "--preview-with-boxes"])
self.assertEqual((valid.preview_count, valid.preview_start, valid.preview_end), (40, 30., 120.))
for extra in (["--preview-only", "--preview-count", "0"],
["--preview-only", "--preview-start", "-1"],
["--preview-only", "--preview-end", "nan"],
["--preview-only", "--preview-start", "10", "--preview-end", "10"],
["--preview-only", "--max-steps", "20", "--preview-end", "60"],
["--preview-count", "40"], ["--preview-with-boxes"]):
with self.subTest(extra=extra), contextlib.redirect_stderr(io.StringIO()), self.assertRaises(SystemExit):
parse_args(required + extra)
def test_preview_end_limits_media_reader_without_loading_encoder(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
reference, _, labels = fixture()
torch.save(reference, root / "Session_4.pt")
np.save(root / "Session_4.npy", labels)
args = ["--data-root", str(root), "--feature-root", str(root), "--target-root", str(root),
"--output-root", str(root / "preview"), "--samples", "4",
"--preview-only", "--preview-end", "2", "--preview-count", "20"]
media = SimpleNamespace(clips=[], metadata=lambda: {})
with patch("extract_turn_local_features.build_media_and_boxes", return_value=(media, [])) as build, \
patch("extract_turn_local_features.preview_boxes") as preview, \
patch("extract_turn_local_features.FrozenQwenLocalEncoder") as encoder:
extract_main(args)
self.assertEqual(build.call_args.args[2], 2.)
preview.assert_called_once()
encoder.assert_not_called()
def test_sidecar_alignment_contract_and_modalities(self):
reference, side, _ = fixture()
with tempfile.TemporaryDirectory() as directory: