feat: OCR independent chunks + TMDb seed with file_uuid

- Rule 1 now creates OCR-only chunks instead of merging into ASRX
- generate_seed_embeddings.py supports --file-uuid parameter
- get_seeds() filters by file_uuid
- identity_matcher.py uses file_uuid for seed matching
- Push QDRANT_API_KEY to Python subprocesses
- Face clustering uses frame+bbox matching instead of face_id
- Portal uses JWT authentication
- FilesView filter logic fixed
This commit is contained in:
Accusys
2026-07-06 08:56:56 +08:00
parent cb604b74ec
commit 799ede5a0e
10 changed files with 147 additions and 38 deletions

View File

@@ -104,9 +104,14 @@ def main():
print(f"[FACE_CLUSTER] Loading embeddings from Qdrant for {UUID}...")
try:
import requests
qdrant_url = "http://localhost:6333"
qdrant_url = os.environ.get("QDRANT_URL", "http://localhost:6333")
qdrant_api_key = os.environ.get("QDRANT_API_KEY", "")
collection = "_faces"
headers = {}
if qdrant_api_key:
headers["api-key"] = qdrant_api_key
# Query all embeddings for this file_uuid
response = requests.post(
f"{qdrant_url}/collections/{collection}/points/scroll",
@@ -118,7 +123,8 @@ def main():
},
"limit": 10000,
"with_vector": True
}
},
headers=headers
)
if response.status_code == 200:
@@ -140,22 +146,57 @@ def main():
print(f"[FACE_CLUSTER] Failed to load embeddings from Qdrant: {e}")
embedding_map = {}
# Use embeddings from Qdrant or face.json
# Use embeddings from Qdrant - match by frame + bbox
embeddings = []
face_refs = []
print(f"🔍 Collecting face embeddings for {UUID}...")
# Build a lookup: (frame, bbox_center) -> embedding
# Use frame number and approximate bbox center for matching
qdrant_by_frame = {}
for point in points:
payload = point.get("payload", {})
frame = payload.get("frame")
bbox = payload.get("bbox", {})
vector = point.get("vector")
if frame is not None and vector:
# Use frame + bbox center as key
cx = bbox.get("x", 0) + bbox.get("width", 0) // 2
cy = bbox.get("y", 0) + bbox.get("height", 0) // 2
key = (frame, cx, cy)
if key not in qdrant_by_frame:
qdrant_by_frame[key] = vector
print(f"[FACE_CLUSTER] Built Qdrant lookup with {len(qdrant_by_frame)} entries")
for frame_idx, frame_obj in enumerate(frames_list):
frame_num = frame_obj.get("frame", frame_idx)
faces = frame_obj.get("faces", [])
if not faces:
continue
for face_idx, face in enumerate(faces):
face_id = face.get("face_id")
if face_id and face_id in embedding_map:
embeddings.append(embedding_map[face_id])
face_refs.append({"frame_idx": frame_idx, "face_idx": face_idx, "face_id": face_id})
x = face.get("x", 0)
y = face.get("y", 0)
w = face.get("width", 0)
h = face.get("height", 0)
cx = x + w // 2
cy = y + h // 2
# Try exact match first
key = (frame_num, cx, cy)
if key in qdrant_by_frame:
embeddings.append(qdrant_by_frame[key])
face_refs.append({"frame_idx": frame_idx, "face_idx": face_idx})
continue
# Try approximate match (within 50 pixels)
for (qf, qx, qy), vec in qdrant_by_frame.items():
if qf == frame_num and abs(qx - cx) < 50 and abs(qy - cy) < 50:
embeddings.append(vec)
face_refs.append({"frame_idx": frame_idx, "face_idx": face_idx})
break
if not embeddings:
print("❌ No embeddings found in Qdrant.")