Spaces:
Sleeping
Sleeping
| """ | |
| preprocess_celebdf.py β Extract face crops from Celeb-DF v2 for training | |
| Reads from: | |
| Celeb-DF/Celeb-real/ β label 0 (REAL) | |
| Celeb-DF/YouTube-real/ β label 0 (REAL) | |
| Celeb-DF/Celeb-synthesis/ β label 1 (FAKE) | |
| Outputs to: | |
| data/train/real/ and data/train/fake/ | |
| data/val/real/ and data/val/fake/ | |
| Usage: | |
| python preprocess_celebdf.py --dataset_dir ../Celeb-DF --out_dir ../data | |
| --frames_per_video 15 --val_split 0.15 | |
| """ | |
| import argparse | |
| import os | |
| import random | |
| import sys | |
| from pathlib import Path | |
| import cv2 | |
| # ββ Face detector (OpenCV, no extra deps) ββββββββββββββββββββββββββββ | |
| CASCADE = cv2.data.haarcascades + "haarcascade_frontalface_default.xml" | |
| face_det = cv2.CascadeClassifier(CASCADE) | |
| def extract_faces(video_path: str, n_frames: int = 15, size: int = 224): | |
| """Sample n_frames evenly, detect face, return list of BGR crops.""" | |
| cap = cv2.VideoCapture(video_path) | |
| total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) | |
| if total <= 0: | |
| cap.release() | |
| return [] | |
| indices = sorted(random.sample(range(total), min(n_frames, total))) | |
| crops = [] | |
| for idx in indices: | |
| cap.set(cv2.CAP_PROP_POS_FRAMES, idx) | |
| ret, frame = cap.read() | |
| if not ret: | |
| continue | |
| gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) | |
| faces = face_det.detectMultiScale(gray, 1.1, 4, minSize=(48, 48)) | |
| h, w = frame.shape[:2] | |
| if len(faces) == 0: | |
| # centre crop fallback | |
| s = min(h, w) | |
| y0, x0 = (h - s) // 2, (w - s) // 2 | |
| crop = frame[y0:y0+s, x0:x0+s] | |
| else: | |
| fx, fy, fw, fh = max(faces, key=lambda r: r[2]*r[3]) | |
| pad = int(max(fw, fh) * 0.2) | |
| x1, y1 = max(0, fx-pad), max(0, fy-pad) | |
| x2, y2 = min(w, fx+fw+pad), min(h, fy+fh+pad) | |
| crop = frame[y1:y2, x1:x2] | |
| if crop.size == 0: | |
| continue | |
| crops.append(cv2.resize(crop, (size, size))) | |
| cap.release() | |
| return crops | |
| def save_crops(crops, out_dir: Path, stem: str): | |
| saved = 0 | |
| for i, crop in enumerate(crops): | |
| p = out_dir / f"{stem}_{i:03d}.jpg" | |
| cv2.imwrite(str(p), crop, [cv2.IMWRITE_JPEG_QUALITY, 90]) | |
| saved += 1 | |
| return saved | |
| def collect_videos(folders): | |
| videos = [] | |
| for folder in folders: | |
| p = Path(folder) | |
| if p.exists(): | |
| videos += list(p.glob("*.mp4")) + list(p.glob("*.avi")) | |
| return videos | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--dataset_dir", default="../datasets/video", | |
| help="Path to video dataset root (contains real/celeb_real, real/youtube_real, fake/celeb_synthesis)") | |
| parser.add_argument("--out_dir", default="../datasets/video_crops", | |
| help="Output directory for face crops") | |
| parser.add_argument("--frames_per_video", type=int, default=15, | |
| help="Face crops to extract per video") | |
| parser.add_argument("--val_split", type=float, default=0.15, | |
| help="Fraction of videos held out for validation") | |
| parser.add_argument("--seed", type=int, default=42) | |
| args = parser.parse_args() | |
| random.seed(args.seed) | |
| ds = Path(args.dataset_dir) | |
| # ββ Source folders ββββββββββββββββββββββββββββββββββββββββββββββ | |
| real_folders = [ds / "real" / "celeb_real", ds / "real" / "youtube_real"] | |
| fake_folders = [ds / "fake" / "celeb_synthesis"] | |
| real_videos = collect_videos(real_folders) | |
| fake_videos = collect_videos(fake_folders) | |
| print(f"Found {len(real_videos)} REAL videos | {len(fake_videos)} FAKE videos") | |
| def split(vids): | |
| random.shuffle(vids) | |
| n_val = max(1, int(len(vids) * args.val_split)) | |
| return vids[n_val:], vids[:n_val] # train, val | |
| real_train, real_val = split(real_videos) | |
| fake_train, fake_val = split(fake_videos) | |
| out = Path(args.out_dir) | |
| splits = { | |
| ("train", "real"): real_train, | |
| ("train", "fake"): fake_train, | |
| ("val", "real"): real_val, | |
| ("val", "fake"): fake_val, | |
| } | |
| # ββ Extract βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| total_saved = 0 | |
| for (split_name, label), vids in splits.items(): | |
| out_dir = out / split_name / label | |
| out_dir.mkdir(parents=True, exist_ok=True) | |
| print(f"\n[{split_name}/{label}] Processing {len(vids)} videos β {out_dir}") | |
| for i, vpath in enumerate(vids): | |
| crops = extract_faces(str(vpath), n_frames=args.frames_per_video) | |
| stem = vpath.stem | |
| saved = save_crops(crops, out_dir, stem) | |
| total_saved += saved | |
| print(f" [{i+1:04d}/{len(vids)}] {vpath.name} β {saved} crops", end="\r") | |
| print() # newline after carriage-returns | |
| print(f"\nβ Done! Total face crops saved: {total_saved}") | |
| print(f" Output: {out.resolve()}") | |
| # Summary | |
| for (s, l), _ in splits.items(): | |
| d = out / s / l | |
| n = len(list(d.glob("*.jpg"))) | |
| print(f" {s}/{l}: {n} images") | |
| if __name__ == "__main__": | |
| main() | |