fix: face group name read consistency, sync_file_status fix, cleanup ghost records, identity_agent replaced with face_dedup
- get_face_groups_handler: COALESCE(tp.name, tn.label) for name consistency - sync_file_status: compare JSON vs pre_chunks (not chunk table) - face consistency: compare frames.len() not total_faces - cleanup 2 ghost records with NULL file_name/file_path - replace identity_agent with face_dedup in pipeline stages - remove identity_agent_api.rs and all references - update required_processors to match actual processors - update AGENTS.md with team responsibilities - add Studio pipeline changes documentation
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
#!/opt/homebrew/bin/python3.11
|
||||
"""
|
||||
Compare Apple Vision pose vs MediaPipe pose
|
||||
|
||||
Finds:
|
||||
- Intersection: Poses detected by both
|
||||
- Apple Vision only: Poses only in Apple Vision
|
||||
- MediaPipe only: Poses only in MediaPipe
|
||||
|
||||
Usage:
|
||||
python3 scripts/compare_pose_detections.py --file-uuid <uuid>
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def load_apple_vision_poses(file_uuid, output_dir):
|
||||
"""Load Apple Vision pose data from pose.json"""
|
||||
pose_path = Path(output_dir) / f"{file_uuid}.pose.json"
|
||||
|
||||
if not pose_path.exists():
|
||||
return {}
|
||||
|
||||
with open(pose_path) as f:
|
||||
data = json.load(f)
|
||||
|
||||
poses = {}
|
||||
for frame in data.get('frames', []):
|
||||
frame_num = frame.get('frame', frame.get('frame_number', 0))
|
||||
for i, person in enumerate(frame.get('persons', [])):
|
||||
pose_key = f"frame_{frame_num}_person_{i}"
|
||||
poses[pose_key] = {
|
||||
'frame': frame_num,
|
||||
'person_idx': i,
|
||||
'keypoints': person.get('keypoints', []),
|
||||
'source': 'apple_vision'
|
||||
}
|
||||
|
||||
return poses
|
||||
|
||||
|
||||
def load_mediapipe_poses(file_uuid, output_dir):
|
||||
"""Load MediaPipe pose data from pose.mediapipe.json"""
|
||||
pose_path = Path(output_dir) / f"{file_uuid}.pose.mediapipe.json"
|
||||
|
||||
if not pose_path.exists():
|
||||
return {}
|
||||
|
||||
with open(pose_path) as f:
|
||||
data = json.load(f)
|
||||
|
||||
poses = {}
|
||||
for frame in data.get('frames', []):
|
||||
frame_num = frame.get('frame', 0)
|
||||
for i, person in enumerate(frame.get('persons', [])):
|
||||
pose_key = f"frame_{frame_num}_person_{i}"
|
||||
poses[pose_key] = {
|
||||
'frame': frame_num,
|
||||
'person_idx': i,
|
||||
'keypoints': person.get('keypoints', []),
|
||||
'source': 'mediapipe'
|
||||
}
|
||||
|
||||
return poses
|
||||
|
||||
|
||||
def compare_poses(file_uuid, output_dir):
|
||||
"""Compare Apple Vision vs MediaPipe poses."""
|
||||
|
||||
print(f"[compare] Loading pose data for {file_uuid}...")
|
||||
|
||||
av_poses = load_apple_vision_poses(file_uuid, output_dir)
|
||||
mp_poses = load_mediapipe_poses(file_uuid, output_dir)
|
||||
|
||||
print(f"[compare] Apple Vision poses: {len(av_poses)}")
|
||||
print(f"[compare] MediaPipe poses: {len(mp_poses)}")
|
||||
|
||||
# Find intersection and differences
|
||||
av_keys = set(av_poses.keys())
|
||||
mp_keys = set(mp_poses.keys())
|
||||
|
||||
intersection = av_keys & mp_keys
|
||||
av_only = av_keys - mp_keys
|
||||
mp_only = mp_keys - av_keys
|
||||
|
||||
print(f"\n[compare] === COMPARISON ===")
|
||||
print(f"[compare] Intersection (both detected): {len(intersection)}")
|
||||
print(f"[compare] Apple Vision only: {len(av_only)}")
|
||||
print(f"[compare] MediaPipe only: {len(mp_only)}")
|
||||
|
||||
# Analyze intersection - check alignment
|
||||
intersection_aligned = 0
|
||||
for key in intersection:
|
||||
av_pose = av_poses[key]
|
||||
mp_pose = mp_poses[key]
|
||||
|
||||
# Check if both have face keypoints
|
||||
av_face_kps = [kp for kp in av_pose.get('keypoints', []) if kp.get('name') in ['nose', 'left_eye', 'right_eye']]
|
||||
mp_face_kps = [kp for kp in mp_pose.get('keypoints', []) if kp.get('name') in ['nose', 'left_eye', 'right_eye']]
|
||||
|
||||
if av_face_kps and mp_face_kps:
|
||||
intersection_aligned += 1
|
||||
|
||||
print(f"\n[compare] Intersection with face keypoints: {intersection_aligned}")
|
||||
|
||||
# Frame coverage
|
||||
av_frames = set(av_poses[k]['frame'] for k in av_keys)
|
||||
mp_frames = set(mp_poses[k]['frame'] for k in mp_keys)
|
||||
|
||||
print(f"\n[compare] === FRAME COVERAGE ===")
|
||||
print(f"[compare] Apple Vision frames: {len(av_frames)}")
|
||||
print(f"[compare] MediaPipe frames: {len(mp_frames)}")
|
||||
print(f"[compare] Overlapping frames: {len(av_frames & mp_frames)}")
|
||||
|
||||
# Save results
|
||||
output_path = Path(output_dir) / f"{file_uuid}.pose_comparison.json"
|
||||
with open(output_path, 'w') as f:
|
||||
json.dump({
|
||||
'apple_vision_count': len(av_poses),
|
||||
'mediapipe_count': len(mp_poses),
|
||||
'intersection_count': len(intersection),
|
||||
'apple_vision_only_count': len(av_only),
|
||||
'mediapipe_only_count': len(mp_only),
|
||||
'intersection_aligned_count': intersection_aligned,
|
||||
'apple_vision_frames': len(av_frames),
|
||||
'mediapipe_frames': len(mp_frames),
|
||||
'overlapping_frames': len(av_frames & mp_frames),
|
||||
'intersection_keys': sorted(list(intersection))[:100], # Sample
|
||||
'apple_vision_only_keys': sorted(list(av_only))[:100],
|
||||
'mediapipe_only_keys': sorted(list(mp_only))[:100],
|
||||
}, f, indent=2)
|
||||
|
||||
print(f"\n[compare] Results saved to: {output_path}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Compare pose detections")
|
||||
parser.add_argument("--file-uuid", "-u", required=True, help="File UUID")
|
||||
parser.add_argument("--output-dir", "-o", default="/Users/accusys/momentry/output", help="Output directory")
|
||||
args = parser.parse_args()
|
||||
|
||||
compare_poses(args.file_uuid, args.output_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user