fix: trace debug mode — show all traces in frame range with interpolation
Debug overlay now lists every trace visible in the current frame range,
including interpolated frames (continuous from first to last detection).
Format per trace line:
Trace {id}: start_frame={n} Identity={name}
This commit is contained in:
@@ -274,7 +274,7 @@ async fn trace_video(
|
|||||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||||
let (video_path, fps, _width, _height) = row.ok_or(StatusCode::NOT_FOUND)?;
|
let (video_path, fps, _width, _height) = row.ok_or(StatusCode::NOT_FOUND)?;
|
||||||
|
|
||||||
// Get all detections for this trace_id
|
// Query face detections to find frame range for target trace
|
||||||
let face_table = schema::table_name("face_detections");
|
let face_table = schema::table_name("face_detections");
|
||||||
let rows: Vec<(i32, i32, i32, i32, i32)> = sqlx::query_as(&format!(
|
let rows: Vec<(i32, i32, i32, i32, i32)> = sqlx::query_as(&format!(
|
||||||
"SELECT frame_number, x, y, width, height FROM {} WHERE file_uuid = $1 AND trace_id = $2 ORDER BY frame_number",
|
"SELECT frame_number, x, y, width, height FROM {} WHERE file_uuid = $1 AND trace_id = $2 ORDER BY frame_number",
|
||||||
@@ -319,19 +319,36 @@ async fn trace_video(
|
|||||||
.unwrap());
|
.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// === DEBUG MODE: text overlay without bounding boxes ===
|
// === DEBUG MODE: text overlay, list all traces in frame range ===
|
||||||
// Query identity info for this trace
|
let start_fn = (start_sec * fps) as i32;
|
||||||
|
let end_fn = (start_sec + duration) as i32;
|
||||||
|
|
||||||
|
// Query all traces with identity names in the visible frame range
|
||||||
let identities_table = schema::table_name("identities");
|
let identities_table = schema::table_name("identities");
|
||||||
let identity_name: String = sqlx::query_scalar(&format!(
|
let all_rows: Vec<(i32, i32, Option<String>)> = sqlx::query_as(&format!(
|
||||||
"SELECT COALESCE(i.name, 'unknown') FROM {} fd LEFT JOIN {} i ON i.id = fd.identity_id WHERE fd.file_uuid = $1 AND fd.trace_id = $2 LIMIT 1",
|
"SELECT fd.trace_id, fd.frame_number, i.name \
|
||||||
|
FROM {} fd \
|
||||||
|
LEFT JOIN {} i ON fd.identity_id = i.id \
|
||||||
|
WHERE fd.file_uuid = $1 AND fd.frame_number BETWEEN $2 AND $3 AND fd.trace_id IS NOT NULL \
|
||||||
|
ORDER BY fd.trace_id, fd.frame_number",
|
||||||
face_table, identities_table
|
face_table, identities_table
|
||||||
))
|
))
|
||||||
.bind(&file_uuid).bind(trace_id)
|
.bind(&file_uuid).bind(start_fn).bind(end_fn)
|
||||||
.fetch_optional(state.db.pool()).await
|
.fetch_all(state.db.pool()).await
|
||||||
.unwrap_or(None)
|
.unwrap_or_default();
|
||||||
.unwrap_or_else(|| "unknown".to_string());
|
|
||||||
|
|
||||||
// Query cut_id for the first frame
|
// Group frames by trace_id, compute start_frame per trace
|
||||||
|
use std::collections::HashMap;
|
||||||
|
let mut trace_frames: HashMap<i32, Vec<i32>> = HashMap::new();
|
||||||
|
let mut trace_identity: HashMap<i32, String> = HashMap::new();
|
||||||
|
for (tid, fn_, name_opt) in &all_rows {
|
||||||
|
trace_frames.entry(*tid).or_default().push(*fn_);
|
||||||
|
if let Some(name) = name_opt {
|
||||||
|
trace_identity.entry(*tid).or_insert_with(|| name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query cut_id for this segment
|
||||||
let cut_table = schema::table_name("cut");
|
let cut_table = schema::table_name("cut");
|
||||||
let cut_id: i32 = sqlx::query_scalar(
|
let cut_id: i32 = sqlx::query_scalar(
|
||||||
&format!("SELECT scene_number FROM {} WHERE file_uuid = $1 AND start_frame <= $2 AND end_frame >= $2 LIMIT 1", cut_table)
|
&format!("SELECT scene_number FROM {} WHERE file_uuid = $1 AND start_frame <= $2 AND end_frame >= $2 LIMIT 1", cut_table)
|
||||||
@@ -341,28 +358,47 @@ async fn trace_video(
|
|||||||
.unwrap_or(None)
|
.unwrap_or(None)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
// Sort traces for consistent ordering
|
||||||
|
let mut sorted_traces: Vec<(i32, &Vec<i32>)> = trace_frames.iter().map(|(k, v)| (*k, v)).collect();
|
||||||
|
sorted_traces.sort_by_key(|(tid, _)| *tid);
|
||||||
|
|
||||||
let frame_offset = first_frame as i64 - (padding * fps) as i64;
|
let frame_offset = first_frame as i64 - (padding * fps) as i64;
|
||||||
let trace_start = rows[0].0;
|
|
||||||
let fps_str = &fps.to_string();
|
let fps_str = &fps.to_string();
|
||||||
|
|
||||||
// Static trace info (shown throughout)
|
// Build drawtext entries
|
||||||
let info_block = format!(
|
let mut parts: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
// Static header
|
||||||
|
parts.push(format!(
|
||||||
"drawtext=text='File UUID: {}':fontsize=14:fontcolor=white:box=1:boxcolor=black@0.6:x=10:y=12", file_uuid
|
"drawtext=text='File UUID: {}':fontsize=14:fontcolor=white:box=1:boxcolor=black@0.6:x=10:y=12", file_uuid
|
||||||
);
|
));
|
||||||
let trace_block = format!(
|
parts.push(format!(
|
||||||
"drawtext=text='Trace {}: start_frame={} Identity: {}':fontsize=14:fontcolor=white:box=1:boxcolor=black@0.6:x=10:y=34", trace_id, trace_start, identity_name
|
"drawtext=text='Cut: {}':fontsize=14:fontcolor=white:box=1:boxcolor=black@0.6:x=10:y=34", cut_id
|
||||||
);
|
));
|
||||||
let cut_block = format!(
|
parts.push(format!(
|
||||||
"drawtext=text='Cut: {}':fontsize=14:fontcolor=white:box=1:boxcolor=black@0.6:x=10:y=56", cut_id
|
"drawtext=text='Frame: %{{eif:n+{}:d}} Time: %{{eif:(n+{})*100/{}:d}}s':fontsize=14:fontcolor=white:box=1:boxcolor=black@0.6:x=10:y=56",
|
||||||
);
|
|
||||||
|
|
||||||
// Per-frame info (frame number, time)
|
|
||||||
let frame_info = format!(
|
|
||||||
"drawtext=text='Frame: %{{eif:n+{}:d}} Time: %{{eif:(n+{})*100/{}:d}}s':fontsize=14:fontcolor=white:box=1:boxcolor=black@0.6:x=10:y=78",
|
|
||||||
frame_offset, frame_offset, fps_str
|
frame_offset, frame_offset, fps_str
|
||||||
);
|
));
|
||||||
|
|
||||||
let filter_text = format!("{}, {}, {}, {}", info_block, trace_block, cut_block, frame_info);
|
// Per-trace entries: show trace_id, start_frame, identity name
|
||||||
|
// Position starts at y=78, increments by 22 per trace
|
||||||
|
let mut y_pos = 78;
|
||||||
|
for (tid, frames) in &sorted_traces {
|
||||||
|
let start = frames.iter().min().unwrap_or(&first_frame);
|
||||||
|
let identity = trace_identity.get(tid).map(|s| s.as_str()).unwrap_or("unknown");
|
||||||
|
let label = format!("Trace {}: start={} {}", tid, start, identity);
|
||||||
|
|
||||||
|
// Continuous range (interpolated): visible from first to last frame
|
||||||
|
let enable = format!("between(n,{},{})", frames[0] as i64 - frame_offset, frames[frames.len() - 1] as i64 - frame_offset);
|
||||||
|
|
||||||
|
parts.push(format!(
|
||||||
|
"drawtext=text='{}':fontsize=14:fontcolor=white:box=1:boxcolor=black@0.6:x=10:y={}:enable='{}'",
|
||||||
|
label, y_pos, enable
|
||||||
|
));
|
||||||
|
y_pos += 22;
|
||||||
|
}
|
||||||
|
|
||||||
|
let filter_text = parts.join(",");
|
||||||
let filter_file = std::env::temp_dir().join(format!("vf_{}.txt", uuid::Uuid::new_v4()));
|
let filter_file = std::env::temp_dir().join(format!("vf_{}.txt", uuid::Uuid::new_v4()));
|
||||||
let _ = std::fs::write(&filter_file, &filter_text);
|
let _ = std::fs::write(&filter_file, &filter_text);
|
||||||
let filter_path = filter_file.to_str().unwrap_or("");
|
let filter_path = filter_file.to_str().unwrap_or("");
|
||||||
|
|||||||
Reference in New Issue
Block a user