{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os, glob, json, cv2, numpy as np\nfrom PIL import Image\nfrom tqdm import tqdm\n\n# auto-find the labeled sample folder (handles /competitions/ path)\nMETA_PATH  = glob.glob(\"/kaggle/input/**/train_sample_videos/metadata.json\", recursive=True)[0]\nSAMPLE_DIR = os.path.dirname(META_PATH)\nOUT_DIR    = \"/kaggle/working/faces\"\nN_FRAMES   = 32\nMARGIN     = 0.30          # winner's 30% crop margin\nSIZE       = 380           # EfficientNet-B4 native resolution\n\nprint(\"using:\", SAMPLE_DIR)\nos.makedirs(f\"{OUT_DIR}/REAL\", exist_ok=True)\nos.makedirs(f\"{OUT_DIR}/FAKE\", exist_ok=True)\n\nface_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + \"haarcascade_frontalface_default.xml\")\n\nwith open(META_PATH) as f:\n    meta = json.load(f)\nprint(f\"videos in metadata: {len(meta)}\")\n\ndef extract_faces(video_path, label, vid_id):\n    cap = cv2.VideoCapture(video_path)\n    total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n    if total <= 0:\n        cap.release(); return 0\n    saved = 0\n    for i, idx in enumerate(np.linspace(0, total - 1, N_FRAMES).astype(int)):\n        cap.set(cv2.CAP_PROP_POS_FRAMES, int(idx))\n        ok, frame = cap.read()\n        if not ok:\n            continue\n        rgb  = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)\n        faces = face_cascade.detectMultiScale(gray, 1.1, 5, minSize=(60, 60))\n        if len(faces) == 0:\n            continue\n        x, y, w, h = max(faces, key=lambda b: b[2] * b[3])\n        mx, my = int(w * MARGIN), int(h * MARGIN)\n        H, W = rgb.shape[:2]\n        x1, y1 = max(0, x - mx), max(0, y - my)\n        x2, y2 = min(W, x + w + mx), min(H, y + h + my)\n        crop = rgb[y1:y2, x1:x2]\n        if crop.size == 0:\n            continue\n        crop = cv2.resize(crop, (SIZE, SIZE))\n        Image.fromarray(crop).save(f\"{OUT_DIR}/{label}/{vid_id}_{i}.jpg\", quality=90)\n        saved += 1\n    cap.release()\n    return saved\n\nreal_count = fake_count = 0\nfor vid, info in tqdm(meta.items()):\n    path = os.path.join(SAMPLE_DIR, vid)\n    if not os.path.exists(path):\n        continue\n    label = info[\"label\"]            # 'REAL' or 'FAKE'\n    n = extract_faces(path, label, vid.replace(\".mp4\", \"\"))\n    if label == \"REAL\": real_count += n\n    else:               fake_count += n\n\nprint(f\"\\n✓ done. REAL faces: {real_count}  |  FAKE faces: {fake_count}\")\nprint(f\"saved under: {OUT_DIR}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-30T12:20:19.167626Z","iopub.execute_input":"2026-06-30T12:20:19.168365Z","iopub.status.idle":"2026-06-30T12:20:21.410605Z","shell.execute_reply.started":"2026-06-30T12:20:19.168330Z","shell.execute_reply":"2026-06-30T12:20:21.409844Z"}},"outputs":[],"execution_count":null}]}