fix: improve TUI with View buttons and smooth out blur interpolation

This commit is contained in:
fiatcode 2026-02-27 23:18:15 +07:00
parent aa4bb03098
commit 599fa858f6
2 changed files with 63 additions and 13 deletions

View file

@ -337,6 +337,16 @@ class FaceSelectionScreen(Screen):
.face-row {
height: 3;
margin-bottom: 0;
align: left middle;
}
.face-checkbox {
width: 80%;
}
.view-btn {
min-width: 8;
margin-left: 2;
}
#blur-method-label {
@ -391,13 +401,21 @@ class FaceSelectionScreen(Screen):
sample_info = (
f" [dim]{self.face_samples[cluster.id]}[/dim]"
)
with Horizontal(classes="face-row"):
yield Checkbox(
f"Person {cluster.id + 1} "
f"({len(cluster.faces)} detections)"
f"{sample_info}",
value=True,
id=f"cluster-{cluster.id}",
classes="face-row",
classes="face-checkbox",
)
if cluster.id in self.face_samples:
yield Button(
"View",
id=f"view-{cluster.id}",
variant="default",
classes="view-btn",
)
yield Label("Blur method:", id="blur-method-label")
yield Select(
@ -424,6 +442,25 @@ class FaceSelectionScreen(Screen):
self.app.pop_screen()
elif event.button.id == "blur-btn":
self._start_encoding()
elif event.button.id and event.button.id.startswith("view-"):
cluster_id = int(event.button.id.split("-")[1])
if cluster_id in self.face_samples:
self._open_image(self.face_samples[cluster_id])
def _open_image(self, path: Path) -> None:
"""Open image in default viewer."""
import subprocess
import sys
try:
if sys.platform == "darwin":
subprocess.run(["open", str(path)])
elif sys.platform == "win32":
os.startfile(str(path))
else:
subprocess.run(["xdg-open", str(path)])
except Exception:
pass # Ignore if viewer fails to launch
def _start_encoding(self) -> None:
# Collect selected cluster IDs
@ -438,7 +475,9 @@ class FaceSelectionScreen(Screen):
if cluster.id == -1:
selected_ids.add(-1)
blur_method = self.query_one("#blur-method", Select).value
blur_method = str(self.query_one("#blur-method", Select).value)
if blur_method == "Select.NoSelection":
blur_method = "gaussian"
# Generate output path
stem = self.video_path.stem

View file

@ -145,13 +145,24 @@ def get_bboxes_for_frame(
all_clusters = set(prev_by_cluster.keys()) | set(next_by_cluster.keys())
for cid in all_clusters:
if cid in prev_by_cluster and cid in next_by_cluster:
# Face present in both: standard linear interpolation
bbox = interpolate_bboxes(prev_by_cluster[cid], next_by_cluster[cid], t)
result.append((cid, bbox))
elif cid in prev_by_cluster:
# Face leaving frame — use prev bbox (fade out handled by caller if desired)
result.append((cid, prev_by_cluster[cid]))
# Face leaving: shrink to center of previous bbox
x1, y1, x2, y2 = prev_by_cluster[cid]
cx, cy = (x1 + x2) // 2, (y1 + y2) // 2
center_bbox = (cx, cy, cx, cy)
# Interpolate from full bbox (t=0) to center point (t=1)
bbox = interpolate_bboxes(prev_by_cluster[cid], center_bbox, t)
result.append((cid, bbox))
else:
# Face entering frame — use next bbox
result.append((cid, next_by_cluster[cid]))
# Face entering: grow from center of next bbox
x1, y1, x2, y2 = next_by_cluster[cid]
cx, cy = (x1 + x2) // 2, (y1 + y2) // 2
center_bbox = (cx, cy, cx, cy)
# Interpolate from center point (t=0) to full bbox (t=1)
bbox = interpolate_bboxes(center_bbox, next_by_cluster[cid], t)
result.append((cid, bbox))
return result