A bunch of changes

This commit is contained in:
Ignacio Serantes
2026-03-23 21:53:19 +01:00
parent a402828d1a
commit 547bfbf760
9 changed files with 544 additions and 150 deletions

View File

@@ -29,6 +29,7 @@ from PySide6.QtCore import (
from constants import (
APP_CONFIG, DEFAULT_FACE_BOX_COLOR, DEFAULT_PET_BOX_COLOR, DEFAULT_VIEWER_SHORTCUTS,
DEFAULT_BODY_BOX_COLOR,
DEFAULT_OBJECT_BOX_COLOR, DEFAULT_LANDMARK_BOX_COLOR, FORCE_X11, ICON_THEME_VIEWER,
ICON_THEME_VIEWER_FALLBACK, KSCREEN_DOCTOR_MARGIN, KWINOUTPUTCONFIG_PATH,
VIEWER_AUTO_RESIZE_WINDOW_DEFAULT, VIEWER_FORM_MARGIN, VIEWER_LABEL,
@@ -47,6 +48,9 @@ class FaceNameDialog(QDialog):
if region_type == "Pet":
self.setWindowTitle(UITexts.ADD_PET_TITLE)
layout_label = UITexts.ADD_PET_LABEL
elif region_type == "Body":
self.setWindowTitle(UITexts.ADD_BODY_TITLE)
layout_label = UITexts.ADD_BODY_LABEL
elif region_type == "Object":
self.setWindowTitle(UITexts.ADD_OBJECT_TITLE)
layout_label = UITexts.ADD_OBJECT_LABEL
@@ -441,6 +445,8 @@ class FaceCanvas(QLabel):
face_color = QColor(face_color_str)
pet_color_str = APP_CONFIG.get("pet_box_color", DEFAULT_PET_BOX_COLOR)
pet_color = QColor(pet_color_str)
body_color_str = APP_CONFIG.get("body_box_color", DEFAULT_BODY_BOX_COLOR)
body_color = QColor(body_color_str)
object_color_str = APP_CONFIG.get("object_box_color", DEFAULT_OBJECT_BOX_COLOR)
object_color = QColor(object_color_str)
landmark_color_str = APP_CONFIG.get("landmark_box_color",
@@ -452,11 +458,14 @@ class FaceCanvas(QLabel):
rect = self.map_from_source(face)
is_pet = face.get('type') == 'Pet'
is_body = face.get('type') == 'Body'
is_object = face.get('type') == 'Object'
is_landmark = face.get('type') == 'Landmark'
if is_pet:
color = pet_color
elif is_body:
color = body_color
elif is_object:
color = object_color
elif is_landmark:
@@ -677,8 +686,10 @@ class FaceCanvas(QLabel):
elif event.button() == Qt.LeftButton:
self.dragging = True
self.drag_start_pos = event.globalPosition().toPoint()
self.drag_start_scroll_x = self.viewer.scroll_area.horizontalScrollBar().value()
self.drag_start_scroll_y = self.viewer.scroll_area.verticalScrollBar().value()
self.drag_start_scroll_x = \
self.viewer.scroll_area.horizontalScrollBar().value()
self.drag_start_scroll_y = \
self.viewer.scroll_area.verticalScrollBar().value()
self.setCursor(Qt.ClosedHandCursor)
event.accept()
else:
@@ -863,12 +874,15 @@ class FaceCanvas(QLabel):
menu = QMenu(self)
action_face = menu.addAction(UITexts.TYPE_FACE)
action_pet = menu.addAction(UITexts.TYPE_PET)
action_body = menu.addAction(UITexts.TYPE_BODY)
action_object = menu.addAction(UITexts.TYPE_OBJECT)
action_landmark = menu.addAction(UITexts.TYPE_LANDMARK)
# Show menu at mouse release position
res = menu.exec(event.globalPosition().toPoint())
if res == action_pet:
region_type = "Pet"
elif res == action_body:
region_type = "Body"
elif res == action_object:
region_type = "Object"
elif res == action_landmark:
@@ -885,6 +899,8 @@ class FaceCanvas(QLabel):
if self.viewer.main_win:
if region_type == "Pet":
history_list = self.viewer.main_win.pet_names_history
elif region_type == "Body":
history_list = self.viewer.main_win.body_names_history
elif region_type == "Object":
history_list = self.viewer.main_win.object_names_history
elif region_type == "Landmark":
@@ -903,6 +919,8 @@ class FaceCanvas(QLabel):
if self.viewer.main_win:
if region_type == "Pet":
self.viewer.main_win.pet_names_history = updated_history
elif region_type == "Body":
self.viewer.main_win.body_names_history = updated_history
elif region_type == "Object":
self.viewer.main_win.object_names_history = updated_history
elif region_type == "Landmark":
@@ -1192,6 +1210,7 @@ class ImageViewer(QWidget):
"flip_vertical": self.toggle_flip_vertical,
"detect_faces": self.run_face_detection,
"detect_pets": self.run_pet_detection,
"detect_bodies": self.run_body_detection,
"fast_tag": self.show_fast_tag_menu,
"rotate_right": lambda: self.apply_rotation(90, True),
"rotate_left": lambda: self.apply_rotation(-90, True),
@@ -1217,7 +1236,7 @@ class ImageViewer(QWidget):
Optimized to update the existing list if possible, rather than
rebuilding it entirely.
"""
if not self.filmstrip.isVisible():
if self.filmstrip.isHidden():
return
# --- OPTIMIZATION ---
@@ -1887,9 +1906,14 @@ class ImageViewer(QWidget):
zoom = int(self.controller.zoom_factor * 100)
self.sb_info_label.setText(f"{w} x {h} px | {zoom}%")
# Use tags from controller's internal state
# Use tags from metadata if provided (priority to avoid race conditions),
# otherwise fallback to controller's internal state.
tags_source = self.controller._current_tags
if metadata and 'tags' in metadata:
tags_source = metadata['tags']
display_tags = [t.strip().split('/')[-1]
for t in self.controller._current_tags if t.strip()]
for t in tags_source if t.strip()]
self.sb_tags_label.setText(", ".join(display_tags))
@Slot(str, dict)
@@ -2080,8 +2104,8 @@ class ImageViewer(QWidget):
return False
menu = QMenu(self)
action_del = menu.addAction(UITexts.DELETE_FACE)
action_ren = menu.addAction(UITexts.RENAME_FACE_TITLE)
action_del = menu.addAction(UITexts.DELETE_AREA_TITLE)
action_ren = menu.addAction(UITexts.RENAME_AREA_TITLE)
res = menu.exec(event.globalPos())
if res == action_del:
@@ -2107,6 +2131,8 @@ class ImageViewer(QWidget):
if self.main_win:
if region_type == "Pet":
history_list = self.main_win.pet_names_history
elif region_type == "Body":
history_list = self.main_win.body_names_history
elif region_type == "Object":
history_list = self.main_win.object_names_history
elif region_type == "Landmark":
@@ -2135,6 +2161,8 @@ class ImageViewer(QWidget):
if self.main_win:
if region_type == "Pet":
self.main_win.pet_names_history = updated_history
elif region_type == "Body":
self.main_win.body_names_history = updated_history
elif region_type == "Object":
self.main_win.object_names_history = updated_history
elif region_type == "Landmark":
@@ -2185,6 +2213,9 @@ class ImageViewer(QWidget):
{"text": UITexts.DETECT_PETS, "action": "detect_pets",
"icon": "edit-image-face-recognize"},
"separator",
{"text": UITexts.DETECT_BODIES, "action": "detect_bodies",
"icon": "edit-image-face-recognize"},
"separator",
{"text": UITexts.VIEWER_MENU_ROTATE, "icon": "transform-rotate",
"submenu": [
{"text": UITexts.VIEWER_MENU_ROTATE_LEFT,
@@ -2491,6 +2522,61 @@ class ImageViewer(QWidget):
if added_count > 0:
self.controller.save_faces()
def run_body_detection(self):
"""Runs body detection on the current image."""
QApplication.setOverrideCursor(Qt.WaitCursor)
try:
new_bodies = self.controller.detect_bodies()
finally:
QApplication.restoreOverrideCursor()
if not new_bodies:
return
IOU_THRESHOLD = 0.7
added_count = 0
for new_body in new_bodies:
is_duplicate = False
for existing_face in self.controller.faces:
iou = self._calculate_iou(new_body, existing_face)
if iou > IOU_THRESHOLD:
is_duplicate = True
break
if is_duplicate:
continue
if not self.controller.show_faces:
self.toggle_faces()
self.controller.faces.append(new_body)
self.canvas.update()
w = self.canvas.width()
h = self.canvas.height()
self.scroll_area.ensureVisible(int(new_body.get('x', 0) * w),
int(new_body.get('y', 0) * h), 50, 50)
QApplication.processEvents()
# For bodies, we typically don't ask for a name immediately unless desired
# Or we can treat it like pets/faces and ask. Let's ask.
history = self.main_win.body_names_history if self.main_win else []
full_tag, updated_history, ok = FaceNameDialog.get_name(
self, history, main_win=self.main_win, region_type="Body")
if ok and full_tag:
new_body['name'] = full_tag
self.controller.toggle_tag(full_tag, True)
if self.main_win:
self.main_win.body_names_history = updated_history
added_count += 1
else:
self.controller.faces.pop()
self.canvas.update()
if added_count > 0:
self.controller.save_faces()
def toggle_filmstrip(self):
"""Shows or hides the filmstrip widget."""
visible = not self.filmstrip.isVisible()