feat: wire TKG builder into worker pipeline + face-face edges
- Auto-run tkg_builder.py after face trace store + Qdrant sync + trace chunks - Add face-face CO_OCCURS_WITH edges (two traces in same frame) - docs: TKG integration report for M4
This commit is contained in:
@@ -365,6 +365,73 @@ def build_speaker_face_edges(cur, schema, file_uuid):
|
||||
return edge_count
|
||||
|
||||
|
||||
def build_face_face_edges(cur, schema, file_uuid):
|
||||
"""Build CO_OCCURS_WITH edges: face_trace ↔ face_trace in same frame"""
|
||||
print("[TKG] Building face-face co-occurrence edges...")
|
||||
|
||||
cur.execute(
|
||||
f"""
|
||||
SELECT a.trace_id AS tid_a, b.trace_id AS tid_b,
|
||||
a.frame_number, a.timestamp_secs,
|
||||
a.x AS ax, a.y AS ay, a.width AS aw, a.height AS ah,
|
||||
b.x AS bx, b.y AS by, b.width AS bw, b.height AS bh
|
||||
FROM {schema}.face_detections a
|
||||
JOIN {schema}.face_detections b
|
||||
ON a.file_uuid = b.file_uuid
|
||||
AND a.frame_number = b.frame_number
|
||||
AND a.trace_id < b.trace_id
|
||||
WHERE a.file_uuid = %s
|
||||
AND a.trace_id IS NOT NULL
|
||||
AND b.trace_id IS NOT NULL
|
||||
ORDER BY a.frame_number
|
||||
""",
|
||||
(file_uuid,),
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
if not rows:
|
||||
print("[TKG] No face-face co-occurrences found")
|
||||
return 0
|
||||
|
||||
# Deduplicate by pair (group all frames where same two traces co-occur)
|
||||
pair_first = {}
|
||||
pair_frames = {}
|
||||
for tid_a, tid_b, frame, ts, ax, ay, aw, ah, bx, by, bw, bh in rows:
|
||||
key = (min(tid_a, tid_b), max(tid_a, tid_b))
|
||||
if key not in pair_first:
|
||||
pair_first[key] = frame
|
||||
pair_frames.setdefault(key, []).append(frame)
|
||||
|
||||
edge_count = 0
|
||||
for (tid_a, tid_b), frames in pair_frames.items():
|
||||
cur.execute(
|
||||
f"SELECT id FROM {schema}.tkg_nodes WHERE file_uuid=%s AND node_type='face_trace' AND external_id=%s",
|
||||
(file_uuid, f"trace_{tid_a}"),
|
||||
)
|
||||
n_a = cur.fetchone()
|
||||
cur.execute(
|
||||
f"SELECT id FROM {schema}.tkg_nodes WHERE file_uuid=%s AND node_type='face_trace' AND external_id=%s",
|
||||
(file_uuid, f"trace_{tid_b}"),
|
||||
)
|
||||
n_b = cur.fetchone()
|
||||
if not n_a or not n_b:
|
||||
continue
|
||||
|
||||
distance_px = ((frames[0] - frames[0]) ** 2) ** 0.5 # placeholder
|
||||
ensure_edge(
|
||||
cur, schema, file_uuid,
|
||||
"CO_OCCURS_WITH",
|
||||
n_a[0], n_b[0],
|
||||
{
|
||||
"first_frame": int(frames[0]),
|
||||
"frame_count": len(frames),
|
||||
},
|
||||
)
|
||||
edge_count += 1
|
||||
|
||||
print(f"[TKG] {edge_count} face-face co-occurrence edges created")
|
||||
return edge_count
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Build Temporal Knowledge Graph")
|
||||
parser.add_argument("--file-uuid", required=True)
|
||||
@@ -382,17 +449,19 @@ def main():
|
||||
|
||||
e1 = build_co_occurrence_edges(cur, args.schema, args.file_uuid)
|
||||
e2 = build_speaker_face_edges(cur, args.schema, args.file_uuid)
|
||||
e3 = build_face_face_edges(cur, args.schema, args.file_uuid)
|
||||
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
print(f"\n[TKG] Complete: {n1+n2+n3} nodes, {e1+e2} edges")
|
||||
print(f"\n[TKG] Complete: {n1+n2+n3} nodes, {e1+e2+e3} edges")
|
||||
print(f" Face traces: {n1}")
|
||||
print(f" Objects: {n2}")
|
||||
print(f" Speakers: {n3}")
|
||||
print(f" Co-occur: {e1}")
|
||||
print(f" Speaker-face:{e2}")
|
||||
print(f" Face-face: {e3}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user