fix: face traces split at scene cuts — even same person, different cut

This commit is contained in:
Accusys
2026-05-14 00:21:17 +08:00
parent 9007e46b9f
commit 74f00d3baa
2 changed files with 40 additions and 5 deletions

View File

@@ -28,7 +28,7 @@ Output:
import json
import argparse
import numpy as np
from typing import Dict, List
from typing import Dict, List, Set
from collections import defaultdict
@@ -112,6 +112,9 @@ def match_faces(
distance_threshold: float = 100.0,
use_embedding: bool = True,
frame_gap: int = 1,
cut_boundaries: Set[int] = None,
prev_frame: int = None,
curr_frame: int = None,
) -> Dict[int, int]:
"""
Match current frame faces to previous frame faces
@@ -124,6 +127,9 @@ def match_faces(
distance_threshold: Maximum bbox center distance for matching
use_embedding: Whether to use embedding similarity
frame_gap: Number of frames between current and previous (1=adjacent)
cut_boundaries: Set of frame numbers where scene cuts occur
prev_frame: Previous frame number (for cut detection)
curr_frame: Current frame number (for cut detection)
Returns:
Dict mapping current_face_index -> previous_face_index (or -1 if new)
@@ -131,6 +137,12 @@ def match_faces(
if not previous_faces:
return {i: -1 for i in range(len(current_faces))}
# If a scene cut exists between prev and current frame, force all new traces
if cut_boundaries and prev_frame is not None and curr_frame is not None:
for cf in cut_boundaries:
if prev_frame < cf <= curr_frame:
return {i: -1 for i in range(len(current_faces))}
matches = {}
used_prev = set()
@@ -193,8 +205,6 @@ def match_faces(
if prev_at_edge and not curr_at_edge and similarity < 0.8:
continue
if iou > iou_threshold and similarity > similarity_threshold:
if iou > iou_threshold and similarity > similarity_threshold:
score = iou + similarity
elif iou > 0.5 and similarity > 0.65:
@@ -226,6 +236,7 @@ def track_faces(
similarity_threshold: float = 0.7,
distance_threshold: float = 100.0,
use_embedding: bool = True,
cut_boundaries: Set[int] = None,
) -> Dict:
"""
Track faces across all frames
@@ -254,6 +265,7 @@ def track_faces(
prev_faces = []
prev_trace_ids = []
prev_frame_num = None
prev_face_frame = None # last frame number that had actual faces
print(f"\nTracking faces across {len(sorted_frames)} frames...")
print(f"Parameters: iou={iou_threshold}, similarity={similarity_threshold}, distance={distance_threshold}")
@@ -279,6 +291,9 @@ def track_faces(
distance_threshold,
use_embedding,
frame_gap,
cut_boundaries,
prev_face_frame,
frame_num,
)
trace_ids = []
@@ -307,6 +322,7 @@ def track_faces(
prev_faces = faces
prev_trace_ids = trace_ids
prev_face_frame = frame_num
if frame_num % 100 == 0:
print(f" Frame {frame_num}: {len(faces)} faces, {len(set(trace_ids))} active traces")
@@ -455,9 +471,18 @@ def main():
parser.add_argument("--similarity-threshold", type=float, default=0.7, help="Embedding similarity threshold")
parser.add_argument("--distance-threshold", type=float, default=100.0, help="Distance threshold")
parser.add_argument("--no-embedding", action="store_true", help="Disable embedding matching")
parser.add_argument("--cuts-json", help="Path to cut.json for scene-cut-aware tracking")
parser.add_argument("--analyze-only", action="store_true", help="Only analyze, don't output")
args = parser.parse_args()
# Load cut boundaries if provided
cut_boundaries = None
if args.cuts_json:
with open(args.cuts_json) as f:
cuts = json.load(f)
cut_boundaries = {s["start_frame"] for s in cuts.get("scenes", []) if s["start_frame"] > 0}
print(f" Cut boundaries loaded: {len(cut_boundaries)} cuts")
print("=" * 60)
print("Face Tracker")
print("=" * 60)
@@ -474,6 +499,7 @@ def main():
similarity_threshold=args.similarity_threshold,
distance_threshold=args.distance_threshold,
use_embedding=not args.no_embedding,
cut_boundaries=cut_boundaries,
)
analyze_traces(face_data)