From e877ac1c24fc6c3852753b462045e036a565a159 Mon Sep 17 00:00:00 2001 From: Ian List Date: Fri, 11 Apr 2025 15:42:10 +0200 Subject: [PATCH 1/4] Switch from OptionMenu to Combobox for label type selection Deselect all annotations before saving --- labeler/views/gui.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/labeler/views/gui.py b/labeler/views/gui.py index 88a2ec2..66a3735 100644 --- a/labeler/views/gui.py +++ b/labeler/views/gui.py @@ -192,9 +192,11 @@ def __init__(self, *args, **kwargs): self.type_variable = tk.StringVar(self.top_frame, self.type_options[0]) # Listener for label type self.type_variable.trace_add("write", lambda *args: self.save_type()) - self.label_type = ttk.OptionMenu( - self.top_frame, self.type_variable, self.type_variable.get(), *self.type_options - ) + # self.label_type = ttk.OptionMenu( + # self.top_frame, self.type_variable, self.type_variable.get(), *self.type_options + # ) + self.label_type = ttk.Combobox(self.top_frame, textvariable=self.type_variable) + self.label_type["values"] = self.type_options self.progress_bar = ttk.Progressbar(self.top_frame, orient="horizontal", length=100, mode="determinate") # Canvas @@ -566,6 +568,7 @@ def saver(self, event: tk.Event | None = None): """ Save the annotations for the image """ + self.deselect_all() logger.info(f"Saving annotations for {self.image_name}") self.save_image_button.configure(state="disabled") saving_path = self.img_cnv.save_json() # type: ignore[union-attr] From 56cd516bdddd2cc03f1c530dcd7eb5411ad32512 Mon Sep 17 00:00:00 2001 From: Ian List Date: Mon, 14 Apr 2025 15:43:36 +0200 Subject: [PATCH 2/4] Remove commented-out OptionMenu and update test_saver Added monkeypatch to test_saver to mock deselect_all method. --- labeler/views/gui.py | 3 --- tests/common/test_gui.py | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/labeler/views/gui.py b/labeler/views/gui.py index 66a3735..0268f2b 100644 --- a/labeler/views/gui.py +++ b/labeler/views/gui.py @@ -192,9 +192,6 @@ def __init__(self, *args, **kwargs): self.type_variable = tk.StringVar(self.top_frame, self.type_options[0]) # Listener for label type self.type_variable.trace_add("write", lambda *args: self.save_type()) - # self.label_type = ttk.OptionMenu( - # self.top_frame, self.type_variable, self.type_variable.get(), *self.type_options - # ) self.label_type = ttk.Combobox(self.top_frame, textvariable=self.type_variable) self.label_type["values"] = self.type_options self.progress_bar = ttk.Progressbar(self.top_frame, orient="horizontal", length=100, mode="determinate") diff --git a/tests/common/test_gui.py b/tests/common/test_gui.py index aac4b94..50c6ba3 100644 --- a/tests/common/test_gui.py +++ b/tests/common/test_gui.py @@ -338,11 +338,11 @@ def test_delete_selected(gui_app): gui_app.save_image_button.configure.assert_called_once_with(state="normal") -def test_saver(gui_app): +def test_saver(gui_app, monkeypatch): gui_app.img_cnv = Mock() gui_app.img_cnv.save_json.return_value = "test_path" gui_app.save_image_button = Mock() - + monkeypatch.setattr(gui_app, "deselect_all", lambda: None) with patch("labeler.views.gui.logger") as mock_logger: gui_app.saver() From e1037c51a18405f9aee75f87a6a88b2cf0898341 Mon Sep 17 00:00:00 2001 From: felix Date: Thu, 17 Apr 2025 15:00:21 +0200 Subject: [PATCH 3/4] disallow zoom while drawing & multi selct boxes for type annotations --- labeler/automation/auto_labeler.py | 3 +++ labeler/views/canvas.py | 6 ++++++ labeler/views/gui.py | 17 ++++++++++------- pyproject.toml | 3 +++ setup.py | 2 +- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/labeler/automation/auto_labeler.py b/labeler/automation/auto_labeler.py index 9db3f53..c65046c 100644 --- a/labeler/automation/auto_labeler.py +++ b/labeler/automation/auto_labeler.py @@ -7,9 +7,12 @@ import cv2 import numpy as np +import onnxruntime as ort from onnxtr.io import DocumentFile from onnxtr.models import from_hub, ocr_predictor +ort.set_default_logger_severity(os.getenv("ORT_LOG_SEVERITY_LEVEL", 4)) + from ..logger import logger __all__ = ["AutoLabeler"] diff --git a/labeler/views/canvas.py b/labeler/views/canvas.py index 8706e6d..5ffccfd 100644 --- a/labeler/views/canvas.py +++ b/labeler/views/canvas.py @@ -57,6 +57,12 @@ def __init__(self, root: Any, canvas: Any, image_path: str): def zoom(self, event: Event | None = None): """Handle zooming in and out.""" + # NOTE: The zoom functionality is disabled while drawing a polygon + # because it can cause scale issues with the polygon points. + if self.drawing_polygon: + logger.info("Cannot zoom while drawing a polygon") + return + zoom_step = 0.1 # Zoom step size max_zoom = min(float(os.environ.get("DOCTR_LABELER_MAX_ZOOM", 1.5)), 2.0) # Maximum zoom capped at 2.0 min_zoom = max(float(os.environ.get("DOCTR_LABELER_MIN_ZOOM", 0.5)), 0.1) # Minimum zoom capped at 0.1 diff --git a/labeler/views/gui.py b/labeler/views/gui.py index 0268f2b..cb3767f 100644 --- a/labeler/views/gui.py +++ b/labeler/views/gui.py @@ -192,8 +192,9 @@ def __init__(self, *args, **kwargs): self.type_variable = tk.StringVar(self.top_frame, self.type_options[0]) # Listener for label type self.type_variable.trace_add("write", lambda *args: self.save_type()) - self.label_type = ttk.Combobox(self.top_frame, textvariable=self.type_variable) - self.label_type["values"] = self.type_options + self.label_type = ttk.Combobox( + self.top_frame, textvariable=self.type_variable, values=self.type_options, state="readonly" + ) self.progress_bar = ttk.Progressbar(self.top_frame, orient="horizontal", length=100, mode="determinate") # Canvas @@ -350,13 +351,15 @@ def save_type(self, *args): if not self.img_cnv or not hasattr(self, "last_selected_polygon"): return - last_selected_polygon = next( - (poly for poly in self.img_cnv.polygons if poly.polygon_id == self.last_selected_polygon), None - ) - if last_selected_polygon and last_selected_polygon.select_poly: + selected_polys = [poly for poly in self.img_cnv.polygons if poly.select_poly] + if selected_polys: with self.img_cnv.polygons_mutex: new_type = self.type_variable.get().strip() - last_selected_polygon.poly_type = new_type + # skip if it's the default type + if new_type == self.type_options[0]: + return + for poly in selected_polys: + poly.poly_type = new_type self.img_cnv.current_saved = False self.save_image_button.configure(state="normal") diff --git a/pyproject.toml b/pyproject.toml index fce25cb..55b8dbb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -105,6 +105,7 @@ module = [ "darkdetect.*", "tqdm.*", "huggingface_hub.*", + "onnxruntime.*", "onnxtr.*", ] ignore_missing_imports = true @@ -131,6 +132,8 @@ known-third-party = [ "tkinter", "sv_ttk", "darkdetect", + "onnxruntime", + "onnxtr", ] [tool.ruff.lint.per-file-ignores] diff --git a/setup.py b/setup.py index 71d48e8..824bff5 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup PKG_NAME = "doctr-labeler" -VERSION = os.getenv("BUILD_VERSION", "0.1.5a0") +VERSION = os.getenv("BUILD_VERSION", "0.1.6a0") if __name__ == "__main__": From 619aa8135ed46deaf2472bd28e627ce91318425f Mon Sep 17 00:00:00 2001 From: Ian List Date: Tue, 22 Apr 2025 11:19:44 +0200 Subject: [PATCH 4/4] ``` Set label_type widget state to readonly ``` --- labeler/views/gui.py | 2 +- tests/common/test_gui.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/labeler/views/gui.py b/labeler/views/gui.py index cb3767f..d40ff99 100644 --- a/labeler/views/gui.py +++ b/labeler/views/gui.py @@ -311,7 +311,7 @@ def show_buttons(self): self.draw_poly_button.configure(state="normal") self.make_tight_button.configure(state="normal") self.label_text.configure(state="normal") - self.label_type.configure(state="normal") + self.label_type.configure(state="readonly") def select_all(self, event: tk.Event | None = None): """ diff --git a/tests/common/test_gui.py b/tests/common/test_gui.py index 50c6ba3..79fb8b7 100644 --- a/tests/common/test_gui.py +++ b/tests/common/test_gui.py @@ -45,7 +45,7 @@ def test_show_buttons(gui_app): assert str(gui_app.draw_poly_button["state"]) == "normal" assert str(gui_app.make_tight_button["state"]) == "normal" assert str(gui_app.label_text["state"]) == "normal" - assert str(gui_app.label_type["state"]) == "normal" + assert str(gui_app.label_type["state"]) == "readonly" def test_toggle_keep_drawing(gui_app): @@ -151,10 +151,10 @@ def test_show_buttons_enables_buttons(gui_app): gui_app.draw_poly_button, gui_app.make_tight_button, gui_app.label_text, - gui_app.label_type, ] for button in buttons: assert str(button["state"]) == "normal" + assert str(gui_app.label_type["state"]) == "readonly" def test_select_all(gui_app):