#!/opt/homebrew/bin/python3.11 """ Comprehensive Age & Gender Updater. Scans all persons in DB, finds a representative frame, and updates demographics using InsightFace. """ import os import cv2 import psycopg2 import insightface # Configuration DB_CONFIG = {"host": "localhost", "user": "accusys", "dbname": "momentry"} BASE_VIDEO_DIR = "output" def get_face_app(): print("Loading InsightFace model (buffalo_l)...") app = insightface.app.FaceAnalysis( name="buffalo_l", providers=["CPUExecutionProvider"] ) app.prepare(ctx_id=0, det_size=(640, 640)) return app def get_video_path(video_uuid): """Locate video file.""" path = f"{BASE_VIDEO_DIR}/{video_uuid}/{video_uuid}.mp4" if os.path.exists(path): return path return None def update_db(conn, person_id, age, gender): """Update demographics in DB.""" cur = conn.cursor() cur.execute( """ UPDATE person_identities SET age = %s, gender = %s WHERE person_id = %s """, (age, gender, person_id), ) conn.commit() def process_person(app, conn, person_id, video_uuid, timestamp): """Extract frame and analyze face.""" video_path = get_video_path(video_uuid) if not video_path: return # OpenCV seek (approximate) cap = cv2.VideoCapture(video_path) if not cap.isOpened(): return # Try seeking by msec cap.set(cv2.CAP_PROP_POS_MSEC, timestamp * 1000) ret, frame = cap.read() # If seeking failed or frame is bad, try frame number estimation (assuming 30fps as fallback, though inaccurate) # But for this script, we just try a few times around the timestamp attempts = 0 while not ret and attempts < 3: ret, frame = cap.read() attempts += 1 cap.release() if not ret or frame is None: print(f" - Failed to get frame for {person_id}") return # Analyze faces = app.get(frame) if faces: # Take the first (usually largest/clearest) face face = faces[0] age = int(face.age) if hasattr(face, "age") else None gender_val = face.gender if hasattr(face, "gender") else None # gender is often 0 or 1 in insightface, map it gender = "female" if gender_val == 0 else ("male" if gender_val == 1 else None) if age and gender: print(f" -> Detected: Age {age}, Gender {gender}") update_db(conn, person_id, age, gender) else: print(" -> Face found but attributes missing.") else: print(" -> No face detected in frame.") def main(): print("=== Starting Full Demographics Scan ===") conn = psycopg2.connect(**DB_CONFIG) cur = conn.cursor() # Get all persons missing age/gender # We group by person_id and video_uuid to handle multiple videos if necessary cur.execute(""" SELECT person_id, video_uuid, MIN(first_appearance_time) as min_time FROM person_identities WHERE age IS NULL OR gender IS NULL GROUP BY person_id, video_uuid """) rows = cur.fetchall() if not rows: print("All persons already have demographics data!") return print(f"Found {len(rows)} persons to process.") app = get_face_app() for i, (person_id, video_uuid, min_time) in enumerate(rows): print( f"[{i + 1}/{len(rows)}] Processing: {person_id} (Video: {video_uuid}, Time: {min_time:.1f}s)" ) process_person(app, conn, person_id, video_uuid, min_time) print("=== Done ===") conn.close() if __name__ == "__main__": main()