import cv2 import yaml from pathlib import Path from tqdm import tqdm def load_config(config_path: Path) -> dict: with open(config_path, "r") as f: return yaml.safe_load(f) def extract_frames(config: dict, project_root: Path) -> None: video_dir = project_root / config["data"]["video_dir"] output_dir = project_root / config["data"]["extracted_frames_dir"] interval = config["data"]["frame_interval_seconds"] output_dir.mkdir(parents=True, exist_ok=True) video_files = sorted(video_dir.glob("*.mp4")) if not video_files: print(f"No .mp4 files found in {video_dir}") return for video_path in tqdm(video_files, desc="Processing videos"): cap = cv2.VideoCapture(str(video_path)) if not cap.isOpened(): print(f"Warning: could not open {video_path.name}, skipping.") continue fps = cap.get(cv2.CAP_PROP_FPS) total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) frame_skip = int(fps * interval) if frame_skip <= 0: print(f"Warning: invalid FPS ({fps}) for {video_path.name}, skipping.") cap.release() continue saved_count = 0 for frame_idx in tqdm( range(0, total_frames, frame_skip), desc=f" {video_path.name}", leave=False, ): cap.set(cv2.CAP_PROP_POS_FRAMES, frame_idx) ret, frame = cap.read() if not ret: break out_name = f"{video_path.stem}_frame_{saved_count:04d}.jpg" cv2.imwrite(str(output_dir / out_name), frame) saved_count += 1 cap.release() print(f" -> {video_path.name}: extracted {saved_count} frames") print("Frame extraction complete.") if __name__ == "__main__": project_root = Path(__file__).resolve().parents[2] config_path = project_root / "config.yaml" config = load_config(config_path) extract_frames(config, project_root)