VLAlert / training /pretrain_v2 /prepare_dad_frames.py
AsianPlayer's picture
Add VLAlert code
1e05592 verified
Raw
History Blame Contribute Delete
3.98 kB
#!/usr/bin/env python3
"""
Extract frames from DAD (Dashcam Accident Dataset) MP4 files.
=============================================================
DAD has no timing annotations — just positive/negative video splits.
positive/: 455 crash videos (no exact accident timestamp)
negative/: 829 safe driving (no annotations needed)
Frames are extracted at 20 fps and saved as:
data/pretrain_v2/dad_frames/{positive,negative}/{vid_id}/000.jpg ...
Each video also gets a minimal annotation.json:
{ "split": "positive"|"negative",
"n_frames": int,
"accident": bool,
"fps": 20 }
Usage:
python training/pretrain_v2/prepare_dad_frames.py [--workers 4]
"""
import argparse
import json
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
import cv2
DAD_VIDEOS_DIR = "PROJECT_ROOT/DAD/videos/training"
OUTPUT_BASE = "PROJECT_ROOT/data/pretrain_v2/dad_frames"
DAD_TEST_VIDEOS_DIR = "PROJECT_ROOT/DAD/videos/testing"
OUTPUT_BASE_TEST = "PROJECT_ROOT/data/pretrain_v2/dad_frames_test"
TARGET_FPS = 20.0
MAX_FRAMES = 400 # cap at 400 frames (~20s at 20fps)
def extract_video(args):
"""Extract one video. Returns (video_id, n_frames, error|None)."""
vid_path, out_dir, split = args
vid_id = vid_path.stem
out_dir.mkdir(parents=True, exist_ok=True)
ann_path = out_dir / "annotation.json"
if ann_path.exists():
# Already extracted — skip
existing = json.loads(ann_path.read_text())
return vid_id, existing.get("n_frames", 0), None
cap = cv2.VideoCapture(str(vid_path))
if not cap.isOpened():
return vid_id, 0, f"Cannot open {vid_path}"
orig_fps = cap.get(cv2.CAP_PROP_FPS) or TARGET_FPS
interval = max(1, int(round(orig_fps / TARGET_FPS)))
raw_idx = 0
saved = 0
while saved < MAX_FRAMES:
ret, frame = cap.read()
if not ret:
break
if raw_idx % interval == 0:
cv2.imwrite(str(out_dir / f"{saved:03d}.jpg"), frame)
saved += 1
raw_idx += 1
cap.release()
if saved == 0:
return vid_id, 0, f"No frames extracted from {vid_path}"
ann_path.write_text(json.dumps({
"split": split,
"accident": (split == "positive"),
"n_frames": saved,
"fps": TARGET_FPS,
}, indent=2), encoding="utf-8")
return vid_id, saved, None
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--workers", type=int, default=4)
parser.add_argument("--split", choices=["training", "testing"], default="training",
help="DAD split to extract: training or testing")
args = parser.parse_args()
if args.split == "testing":
dad_root = Path(DAD_TEST_VIDEOS_DIR)
output_base = Path(OUTPUT_BASE_TEST)
else:
dad_root = Path(DAD_VIDEOS_DIR)
output_base = Path(OUTPUT_BASE)
tasks = []
for split in ("positive", "negative"):
vid_dir = dad_root / split
if not vid_dir.exists():
print(f"Skip {vid_dir} — not found")
continue
vids = sorted(vid_dir.glob("*.mp4"))
print(f" {split}: {len(vids)} videos")
for v in vids:
out_dir = output_base / split / v.stem
tasks.append((v, out_dir, split))
print(f"\nExtracting {len(tasks)} videos with {args.workers} workers ...")
ok = 0
fail = 0
with ThreadPoolExecutor(max_workers=args.workers) as ex:
futs = {ex.submit(extract_video, t): t for t in tasks}
from tqdm import tqdm
for fut in tqdm(as_completed(futs), total=len(futs)):
vid_id, n, err = fut.result()
if err:
print(f" FAIL {vid_id}: {err}")
fail += 1
else:
ok += 1
print(f"\nDone: {ok} extracted, {fail} failed")
print(f"Frames saved to: {output_base}")
if __name__ == "__main__":
main()