File size: 7,331 Bytes
9a069e7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | #!/usr/bin/env python3
"""Unit and CPU smoke checks for fixed-frame SegmentQA preprocessing."""
from __future__ import annotations
import json
import os
import shutil
import subprocess
import sys
from pathlib import Path
import av
PROJECT_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(PROJECT_ROOT / "scripts"))
import preprocess_segmentqa_fixed_frames as prep # noqa: E402
PYTHON = "/cpfs01/projects-SSD/cfff-906dc71fafda_SSD/gbw_21307130160/conda-envs/medrl/bin/python"
DATA_ROOT = "/cpfs01/projects-SSD/cfff-906dc71fafda_SSD/gbw_21307130160/dataset/challenge"
OUT = PROJECT_ROOT / "tmp/segmentqa_fixed80_preprocess_smoke_20260826"
def test_env() -> dict[str, str]:
env = dict(os.environ)
env["PYTHONPATH"] = str(PROJECT_ROOT / "ms-swift")
return env
def video_stats(path: Path) -> dict:
with av.open(str(path)) as container:
stream = container.streams.video[0]
pts = []
dims = set()
for frame in container.decode(stream):
pts.append(float(frame.pts * frame.time_base))
dims.add((frame.width, frame.height))
return {"count": len(pts), "pts": pts, "dims": dims}
def test_time_and_sampling_helpers() -> None:
assert prep.parse_time("01:02:03") == 3723
assert prep.parse_time(12.5) == 12.5
targets = prep.target_times(10, 18, 4)
assert targets == [11, 13, 15, 17]
assert prep.metadata_from_pts([10.1234, 10.5678], 420, 224) == {
"fps": 1000,
"frames_indices": [10123, 10568],
"total_num_frames": 10569,
"width": 420,
"height": 224,
"duration": 10.569,
"video_backend": "precomputed_absolute_pts",
}
def test_template_metadata_override_path() -> None:
from swift.template.templates.qwen import _extract_precomputed_absolute_video_metadata as extract_metadata
metadata = prep.metadata_from_pts([1.0, 2.0], 420, 224)
video_inputs = {"video": "clip.mp4", "nframes": 80, "enable_thinking": False, "video_metadata": metadata}
extracted = extract_metadata(video_inputs, 0)
assert extracted == metadata
assert video_inputs == {"video": "clip.mp4", "nframes": 80, "enable_thinking": False}
legacy_inputs = {"video": "clip.mp4", "fps": 1.0, "min_frames": 16, "max_frames": 64}
assert extract_metadata(legacy_inputs, 0) is None
assert legacy_inputs == {"video": "clip.mp4", "fps": 1.0, "min_frames": 16, "max_frames": 64}
def validate_qwen_fetch_video_path(record: dict) -> None:
from qwen_vl_utils import fetch_video
from swift.template.templates.qwen import _extract_precomputed_absolute_video_metadata
from transformers.video_utils import VideoMetadata
video_inputs = {"video": record["videos"][0], **record["chat_template_kwargs"]}
metadata = _extract_precomputed_absolute_video_metadata(video_inputs, 0)
assert metadata is not None
assert "do_sample_frames" not in metadata
VideoMetadata(**metadata)
(video, fetched_metadata), sample_fps = fetch_video(
video_inputs, return_video_sample_fps=True, return_video_metadata=True, image_patch_size=16)
assert video.shape[0] == 80
assert video.shape[1] == 3
assert video.shape[-2] % 32 == 0
assert video.shape[-1] % 32 == 0
assert fetched_metadata["total_num_frames"] >= 80
assert sample_fps > 0
assert video_inputs == {"video": record["videos"][0], "nframes": 80, "enable_thinking": False}
def assert_author_direct_segment_prompt(prompt: str) -> None:
assert "timestamp in the source video" in prompt
assert "<1234.5 seconds> is 00:20:34" in prompt
assert "START OF THE VIDEO, not from the start of this" in prompt
assert (
"The foreign object classes are exactly: Sponge, Clip, Specimen Bag, Silicone Loop, External Drain, "
"Needle, Gallstone, Specimen, Mesh, Absorbable Hemostatic Agent."
) in prompt
assert "Reply with the answer and nothing else -- no reasoning, no preamble, no" in prompt
assert "A single short line." in prompt
def test_cpu_smoke_preprocess_train() -> None:
if OUT.exists():
shutil.rmtree(OUT)
cmd = [
PYTHON,
"scripts/preprocess_segmentqa_fixed_frames.py",
"--data-root",
DATA_ROOT,
"--output-dir",
str(OUT),
"--splits",
"train",
"--frames",
"80",
"--width",
"420",
"--height",
"224",
"--workers",
"1",
"--limit",
"2",
]
result = subprocess.run(cmd, cwd=PROJECT_ROOT, check=True, text=True, capture_output=True, env=test_env())
summary = json.loads(result.stdout.strip().splitlines()[-1])
assert summary["segments"] == 2
assert summary["success"] == 2
assert summary["failed"] == 0
manifest = [json.loads(line) for line in (OUT / "segment_manifest.jsonl").read_text().splitlines()]
assert {item["dataset"] for item in manifest} == set(prep.DATASET_NAMES)
for item in manifest:
stats = video_stats(OUT / item["relpath"])
assert stats["count"] == 80
assert stats["dims"] == {(420, 224)}
assert item["verified_frame_count"] == 80
assert item["width"] == 420
assert item["height"] == 224
assert len(item["selected_source_pts"]) == 80
assert all(item["start_seconds"] <= pts < item["end_seconds"] for pts in item["selected_source_pts"])
assert item["video_metadata"]["fps"] == 1000
assert item["video_metadata"]["frames_indices"] == [round(pts * 1000) for pts in item["selected_source_pts"]]
assert item["video_metadata"]["total_num_frames"] == max(item["video_metadata"]["frames_indices"]) + 1
assert item["video_metadata"]["video_backend"] == "precomputed_absolute_pts"
assert "do_sample_frames" not in item["video_metadata"]
train_rows = [json.loads(line) for line in (OUT / "train.jsonl").read_text().splitlines()]
assert len(train_rows) >= 2
assert all(row["messages"][0]["role"] == "system" for row in train_rows)
assert_author_direct_segment_prompt(train_rows[0]["messages"][0]["content"])
assert all(row["messages"][1]["content"].startswith("<video>\n") for row in train_rows)
assert all(row["messages"][2]["role"] == "assistant" and row["messages"][2]["content"] for row in train_rows)
assert all(row["chat_template_kwargs"]["nframes"] == 80 for row in train_rows)
assert all(row["chat_template_kwargs"]["enable_thinking"] is False for row in train_rows)
validate_qwen_fetch_video_path(train_rows[0])
assert not (OUT / "test_requests.jsonl").exists()
report = json.loads((OUT / "validation_report.json").read_text())
assert report["status"] == "pass"
assert report["success"] == report["segments"] == 2
before = {p: p.stat().st_mtime for p in OUT.glob("videos/train/*/*.mp4")}
resume = subprocess.run(cmd + ["--resume"], cwd=PROJECT_ROOT, check=True, text=True, capture_output=True, env=test_env())
resume_summary = json.loads(resume.stdout.strip().splitlines()[-1])
assert resume_summary["success"] == 2
assert before == {p: p.stat().st_mtime for p in OUT.glob("videos/train/*/*.mp4")}
if __name__ == "__main__":
test_time_and_sampling_helpers()
test_template_metadata_override_path()
test_cpu_smoke_preprocess_train()
|