import cv2 import os import numpy as np import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) from utils.lbp_features import FACE_SIZE face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + 'haarcascade_frontalface_default.xml' ) def crop_wajah(gray): """Crop wajah persis seperti notebook ablasi jalur 2.""" wajah = face_cascade.detectMultiScale(gray, 1.1, 5, minSize=(50, 50)) if len(wajah) == 0: return None, None (x, y, w, h) = max(wajah, key=lambda r: r[2] * r[3]) sisi = max(w, h) cx, cy = x + w // 2, y + h // 2 half = sisi // 2 y1 = max(0, cy - half) y2 = min(gray.shape[0], cy + half) x1 = max(0, cx - half) x2 = min(gray.shape[1], cx + half) face_crop_gray = cv2.resize(gray[y1:y2, x1:x2], FACE_SIZE) face_crop_color = None return face_crop_gray, (x1, y1, x2, y2) def extract_frames(video_path, output_dir, target_frames=200): """ Ekstraksi frame sesuai ablasi jalur 2: 1. Baca SEMUA frame 2. Crop SEMUA frame (Haar Cascade) 3. Sampling merata (np.linspace) dari yang berhasil crop """ if not os.path.exists(video_path): raise Exception(f"Video tidak ditemukan: {video_path}") if not os.path.exists(output_dir): os.makedirs(output_dir) else: for f in os.listdir(output_dir): if (f.startswith("frame_") or f.startswith("raw_frame_")) and f.endswith(".jpg"): os.remove(os.path.join(output_dir, f)) cap = cv2.VideoCapture(video_path) if not cap.isOpened(): raise Exception("Gagal membuka file video.") total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) fps = cap.get(cv2.CAP_PROP_FPS) if total_frames <= 0: raise Exception("Video tidak valid (0 frame).") semua_crop = [] skipped_no_face = 0 frame_idx = 0 while True: ret, frame = cap.read() if not ret: break gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) face_gray, bbox = crop_wajah(gray) if face_gray is None: skipped_no_face += 1 frame_idx += 1 continue (x1, y1, x2, y2) = bbox face_color = frame[y1:y2, x1:x2] semua_crop.append({ 'frame_idx': frame_idx, 'face_gray': face_gray, 'face_color': face_color, }) frame_idx += 1 cap.release() total_crop = len(semua_crop) if total_crop == 0: raise Exception( "Tidak ada frame wajah yang berhasil diekstrak. " "Pastikan wajah terlihat jelas di video." ) if total_crop <= target_frames: hasil = semua_crop else: indices = np.linspace(0, total_crop - 1, target_frames, dtype=int) hasil = [semua_crop[i] for i in indices] saved_count = 0 for cand in hasil: filename = f"frame_{saved_count:03d}.jpg" filename_raw = f"raw_frame_{saved_count:03d}.jpg" cv2.imwrite( os.path.join(output_dir, filename), cand['face_gray'], [int(cv2.IMWRITE_JPEG_QUALITY), 95] ) cv2.imwrite(os.path.join(output_dir, filename_raw), cand['face_color']) saved_count += 1 interval = total_crop / saved_count if saved_count > 0 else 0 return { "total_video_frames": total_frames, "video_fps": round(fps, 1), "total_crop_detected": total_crop, "total_extracted": saved_count, "target_frames": target_frames, "skipped_no_face": skipped_no_face, "sampling_interval": round(interval, 1), "output_dir": output_dir }