- Remove session-ses_2f27.md (161KB raw session log) - Remove 49 ROOT_* duplicate files across REFERENCE/ - Remove 14 duplicate files between REFERENCE/ root and history/ - Remove asr_legacy.rs (dead code, replaced by asr.rs) - Remove src/core/worker/ (duplicate JobWorker) - Remove src/core/layers/ (empty directory) - Remove 4 .bak files in src/ - Remove 7 dead private methods in worker/processor.rs - Remove backup directory from git tracking
103 lines
3.0 KiB
Python
103 lines
3.0 KiB
Python
#!/opt/homebrew/bin/python3.11
|
|
"""
|
|
Backfill missing Age & Gender for persons.
|
|
"""
|
|
|
|
import os
|
|
import cv2
|
|
import psycopg2
|
|
import insightface
|
|
|
|
DB_CONFIG = {"host": "localhost", "user": "accusys", "dbname": "momentry"}
|
|
BASE_VIDEO_DIR = "output"
|
|
|
|
|
|
def main():
|
|
print("=== Starting Missing Demographics Backfill ===")
|
|
|
|
conn = psycopg2.connect(**DB_CONFIG)
|
|
cur = conn.cursor()
|
|
|
|
# Load Model
|
|
print("Loading InsightFace model...")
|
|
try:
|
|
app = insightface.app.FaceAnalysis(
|
|
name="buffalo_l", providers=["CPUExecutionProvider"]
|
|
)
|
|
app.prepare(ctx_id=0, det_size=(320, 320))
|
|
print("Model loaded.")
|
|
except Exception as e:
|
|
print(f"Error loading model: {e}")
|
|
return
|
|
|
|
# Query persons missing data
|
|
# Join with appearances to find a valid timestamp
|
|
cur.execute("""
|
|
SELECT DISTINCT ON (pi.person_id) pi.person_id, pa.video_uuid, pa.start_time
|
|
FROM person_identities pi
|
|
JOIN person_appearances pa ON pi.person_id = pa.person_id
|
|
WHERE pi.age IS NULL OR pi.gender IS NULL
|
|
ORDER BY pi.person_id, pa.start_time
|
|
""")
|
|
rows = cur.fetchall()
|
|
|
|
print(f"Found {len(rows)} entries to process.")
|
|
|
|
for i, (person_id, video_uuid, start_time) in enumerate(rows):
|
|
# Skip if time is null
|
|
if start_time is None:
|
|
continue
|
|
|
|
print(f"[{i + 1}/{len(rows)}] Processing: {person_id} @ {start_time:.1f}s")
|
|
|
|
video_path = f"{BASE_VIDEO_DIR}/{video_uuid}/{video_uuid}.mp4"
|
|
if not os.path.exists(video_path):
|
|
print(f" -> Video not found at {video_path}")
|
|
continue
|
|
|
|
cap = cv2.VideoCapture(video_path)
|
|
if not cap.isOpened():
|
|
print(" -> Could not open video.")
|
|
continue
|
|
|
|
# Seek
|
|
cap.set(cv2.CAP_PROP_POS_MSEC, start_time * 1000)
|
|
ret, frame = cap.read()
|
|
cap.release()
|
|
|
|
if not ret or frame is None:
|
|
print(" -> Failed to read frame.")
|
|
continue
|
|
|
|
faces = app.get(frame)
|
|
if faces:
|
|
face = faces[0]
|
|
age = int(face.age) if hasattr(face, "age") else None
|
|
gender_val = face.gender if hasattr(face, "gender") else None
|
|
gender = (
|
|
"female" if gender_val == 0 else ("male" if gender_val == 1 else None)
|
|
)
|
|
|
|
if age is not None and gender is not None:
|
|
cur.execute(
|
|
"""
|
|
UPDATE person_identities
|
|
SET age = %s, gender = %s
|
|
WHERE person_id = %s
|
|
""",
|
|
(age, gender, person_id),
|
|
)
|
|
conn.commit()
|
|
print(f" -> Updated: Age {age}, Gender {gender}")
|
|
else:
|
|
print(f" -> Detection incomplete (Age:{age}, Gender:{gender})")
|
|
else:
|
|
print(" -> No face found in frame.")
|
|
|
|
print("=== Done ===")
|
|
conn.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|