diff --git a/src/core/processor/tkg.rs b/src/core/processor/tkg.rs index 61dbc5a..a7f6059 100644 --- a/src/core/processor/tkg.rs +++ b/src/core/processor/tkg.rs @@ -613,34 +613,6 @@ struct AsrxSpeakerStat { count: i64, } -// ── Face detection trace ────────────────────────────────────────── - -#[derive(Debug, sqlx::FromRow)] -struct FaceTraceRow { - trace_id: i64, - frame_count: i64, - start_f: i64, - end_f: i64, - avg_x: Option, - avg_y: Option, - avg_w: Option, - avg_h: Option, -} - -#[derive(Debug, sqlx::FromRow)] -struct FaceDetectionRow { - trace_id: i64, - frame_number: i64, - #[allow(dead_code)] - x: Option, - #[allow(dead_code)] - y: Option, - #[allow(dead_code)] - width: Option, - #[allow(dead_code)] - height: Option, -} - // ── Public API ──────────────────────────────────────────────────── #[derive(Debug, Serialize)] @@ -1193,135 +1165,6 @@ async fn build_face_track_nodes_from_qdrant( Ok(count) } -async fn build_face_track_nodes_from_face_json( - pool: &PgPool, - file_uuid: &str, - pose_data: &[FacePose], -) -> Result { - let face_json_path = - Path::new(&*crate::core::config::OUTPUT_DIR).join(format!("{}.face.json", file_uuid)); - - if !face_json_path.exists() { - tracing::info!("[TKG-Phase2.5] No face.json for {}", file_uuid); - return Ok(0); - } - - let content = std::fs::read_to_string(&face_json_path) - .with_context(|| format!("Failed to read {:?}", face_json_path))?; - let face_result: crate::core::processor::face::FaceResult = serde_json::from_str(&content) - .with_context(|| format!("Failed to parse {:?}", face_json_path))?; - - // Extract faces with embeddings - let faces_with_embeddings: Vec<(u64, f64, crate::core::processor::face::Face)> = face_result - .frames - .iter() - .flat_map(|frame| { - frame.faces.iter().filter_map(|face| { - if let Some(ref embedding) = face.embedding { - Some((frame.frame, frame.timestamp, face.clone())) - } else { - None - } - }) - }) - .collect(); - - if faces_with_embeddings.is_empty() { - tracing::info!( - "[TKG-Phase2.5] No embeddings in face.json for {}", - file_uuid - ); - return Ok(0); - } - - tracing::info!( - "[TKG-Phase2.5] Found {} faces with embeddings in face.json", - faces_with_embeddings.len() - ); - - // Simple clustering: assign trace_id based on consecutive similar embeddings - let nodes_table = t("tkg_nodes"); - let mut trace_id = 1_i64; - let mut traces: Vec<(i64, Vec<(u64, f64, f64, f64, f64, f64)>)> = vec![]; - - for (frame, timestamp, face) in &faces_with_embeddings { - // For simplicity, assign all faces to one trace (proper clustering requires DBSCAN) - if traces.is_empty() { - traces.push((trace_id, vec![])); - } - traces[0].1.push(( - *frame, - *timestamp, - face.x as f64, - face.y as f64, - face.width as f64, - face.height as f64, - )); - } - - let mut count = 0; - for (tid, frames) in &traces { - let external_id = format!("face_track_{}", tid); - let label = format!("Face Trace {}", tid); - - let frame_count = frames.len() as i64; - let start_f = frames - .iter() - .map(|(f, _, _, _, _, _)| *f as i64) - .min() - .unwrap_or(0); - let end_f = frames - .iter() - .map(|(f, _, _, _, _, _)| *f as i64) - .max() - .unwrap_or(0); - let avg_x = frames.iter().map(|(_, _, x, _, _, _)| *x).sum::() / frame_count as f64; - let avg_y = frames.iter().map(|(_, _, _, y, _, _)| *y).sum::() / frame_count as f64; - let avg_w = frames.iter().map(|(_, _, _, _, w, _)| *w).sum::() / frame_count as f64; - let avg_h = frames.iter().map(|(_, _, _, _, _, h)| *h).sum::() / frame_count as f64; - - let props = serde_json::json!({ - "trace_id": tid, - "frame_count": frame_count, - "start_frame": start_f, - "end_frame": end_f, - "avg_bbox": { - "x": avg_x.round() as i64, - "y": avg_y.round() as i64, - "width": avg_w.round() as i64, - "height": avg_h.round() as i64, - }, - }); - - sqlx::query(&format!( - r#" - INSERT INTO {} (node_type, external_id, file_uuid, label, properties) - VALUES ($1, $2, $3, $4, $5::jsonb) - ON CONFLICT (file_uuid, node_type, external_id) - DO UPDATE SET - properties = COALESCE(EXCLUDED.properties, tkg_nodes.properties), - label = COALESCE(NULLIF(EXCLUDED.label, ''), tkg_nodes.label) - "#, - nodes_table - )) - .bind("face_track") - .bind(&external_id) - .bind(file_uuid) - .bind(&label) - .bind(serde_json::to_string(&props)?) - .execute(pool) - .await?; - - count += 1; - } - - tracing::info!( - "[TKG-Phase2.5] Built {} face_track nodes from face.json", - count - ); - Ok(count) -} - async fn build_yolo_object_nodes( pool: &PgPool, file_uuid: &str,