From 25cad34b8d0192246e372692ded70f7a8e409a62 Mon Sep 17 00:00:00 2001 From: Jorge Bruned Date: Sun, 21 Apr 2024 22:20:36 +0200 Subject: [PATCH 1/7] Replace PIL's deprecated textsize Use textbbox instead of the deprecated textsize method --- cocoviewer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cocoviewer.py b/cocoviewer.py index 470a4be..b5c06e8 100644 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -180,9 +180,8 @@ def draw_bboxes(draw, objects, labels, obj_categories, ignore, width, label_size # TODO: Implement notification message as popup window font = ImageFont.load_default() - tw, th = draw.textsize(text, font) - tx0 = b[0] - ty0 = b[1] - th + left, top, right, bottom = draw.textbbox((0, 0), text, font=font) + tw, th = right - left, bottom - top # TODO: Looks weird! We need image dims to make it right tx0 = max(b[0], max(b[0], tx0)) if tx0 < 0 else tx0 From 4c6271c804a6f0144040c655b06e8548c3bd0102 Mon Sep 17 00:00:00 2001 From: Jorge Bruned Date: Mon, 22 Apr 2024 00:07:27 +0200 Subject: [PATCH 2/7] Add space below the text baseline Take into account font descent when computing the size of the text bbox --- cocoviewer.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cocoviewer.py b/cocoviewer.py index b5c06e8..0614baf 100644 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -182,6 +182,9 @@ def draw_bboxes(draw, objects, labels, obj_categories, ignore, width, label_size left, top, right, bottom = draw.textbbox((0, 0), text, font=font) tw, th = right - left, bottom - top + # Add space below the baseline + _, descent = font.getmetrics() + th += descent # TODO: Looks weird! We need image dims to make it right tx0 = max(b[0], max(b[0], tx0)) if tx0 < 0 else tx0 From 8afc69931ec033ab28f893b69bc5191ddc1c9fd5 Mon Sep 17 00:00:00 2001 From: Jorge Bruned Date: Mon, 22 Apr 2024 00:13:58 +0200 Subject: [PATCH 3/7] Use annotations' image_root as fallback --- cocoviewer.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/cocoviewer.py b/cocoviewer.py index 0614baf..a5d152a 100644 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -873,18 +873,31 @@ def print_info(message: str): logging.info(message) +def exit_with_error(message: str, root=None): + root.geometry("300x150") # app size when no data is provided + messagebox.showwarning("Warning!", message) + print_info("Exiting because of error: " + message) + root.destroy() + exit(1) + + def main(): print_info("Starting...") args = parser.parse_args() root = tk.Tk() root.title("COCO Viewer") - if not args.images or not args.annotations: - root.geometry("300x150") # app size when no data is provided - messagebox.showwarning("Warning!", "Nothing to show.\nPlease specify a path to the COCO dataset!") - print_info("Exiting...") - root.destroy() - return + if not args.annotations: + exit_with_error("No annotations file provided!", root) + elif not os.path.exists(args.annotations) or not os.path.isfile(args.annotations): + exit_with_error("Invalid annotations file path!", root) + elif not args.images: + try: + # Use image root from the annotations file if not provided via command line + args.images = json.loads(open(args.annotations).read())["image_root"] + except KeyError: + exit_with_error("No images folder provided neither via the annotations file " + "nor as a command line argument!", root) data = Data(args.images, args.annotations) statusbar = StatusBar(root) From 15eaea9683bec1edbe2d162215c5e93909c34c58 Mon Sep 17 00:00:00 2001 From: Jorge Bruned Date: Mon, 22 Apr 2024 00:21:39 +0200 Subject: [PATCH 4/7] Fix font size calculation Bring back accidentally deleted lines --- cocoviewer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cocoviewer.py b/cocoviewer.py index a5d152a..136269a 100644 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -181,10 +181,10 @@ def draw_bboxes(draw, objects, labels, obj_categories, ignore, width, label_size font = ImageFont.load_default() left, top, right, bottom = draw.textbbox((0, 0), text, font=font) - tw, th = right - left, bottom - top - # Add space below the baseline _, descent = font.getmetrics() - th += descent + tw, th = right - left, bottom - top + descent + tx0 = b[0] + ty0 = b[1] - th # TODO: Looks weird! We need image dims to make it right tx0 = max(b[0], max(b[0], tx0)) if tx0 < 0 else tx0 From 621cc89cc164f7b74c6961f8c3016cfb624bc546 Mon Sep 17 00:00:00 2001 From: Jorge Bruned Date: Mon, 22 Apr 2024 00:28:27 +0200 Subject: [PATCH 5/7] Simplify font bbox size calculation --- cocoviewer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cocoviewer.py b/cocoviewer.py index 136269a..74afef6 100644 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -180,9 +180,9 @@ def draw_bboxes(draw, objects, labels, obj_categories, ignore, width, label_size # TODO: Implement notification message as popup window font = ImageFont.load_default() - left, top, right, bottom = draw.textbbox((0, 0), text, font=font) + _, _, tw, th = draw.textbbox((0, 0), text, font=font) _, descent = font.getmetrics() - tw, th = right - left, bottom - top + descent + th += descent tx0 = b[0] ty0 = b[1] - th From 703f4cd9cf2362d99b43eed606f5d486d42fcc4c Mon Sep 17 00:00:00 2001 From: Jorge Bruned Date: Mon, 22 Apr 2024 13:34:57 +0200 Subject: [PATCH 6/7] Allow to zoom image in/out --- README.md | 2 ++ cocoviewer.py | 26 +++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 508be8f..348f7de 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Simple COCO Objects Viewer in Tkinter. Allows quick viewing on local machine. | M, Ctrl + M | Toggle **M**asks | | Ctrl + S | Save Current Image | | Ctrl + Q, Ctrl + W | Exit Viewer | +| Ctrl + + | Zoom In | +| Ctrl + - | Zoom Out | ## Requirements `python3` `PIL` diff --git a/cocoviewer.py b/cocoviewer.py index 74afef6..ffa16f4 100644 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -460,6 +460,11 @@ def view_menu(self): menu.colormenu.add_radiobutton(label="Objects", value=True) menu.add_cascade(label="Coloring", menu=menu.colormenu) + + menu.add_separator() + menu.add_command(label="Zoom In", accelerator="Ctrl++") + menu.add_command(label="Zoom Out", accelerator="Ctrl+-") + return menu @@ -584,11 +589,14 @@ def __init__(self, data, root, image_panel, statusbar, menu, objects_panel, slid self.bind_events() # Compose the very first image + self.zoom_factor = 1.0 self.current_composed_image = None self.current_img_obj_categories = None self.current_img_categories = None self.update_img() + ZOOM_STEP = 1.1 + def set_locals(self): self.bboxes_on_local = self.bboxes_on_global.get() self.labels_on_local = self.labels_on_global.get() @@ -664,9 +672,13 @@ def update_img(self, local=True, width=None, alpha=None, label_size=None): label_size=label_size, ) - # Prepare PIL image for Tkinter + # Scale PIL image according to zoom_factor and prepare for Tkinter img = self.current_composed_image w, h = img.size + if self.zoom_factor != 1.0: + w = int(w * self.zoom_factor) + h = int(h * self.zoom_factor) + img = img.resize((w, h), Image.LANCZOS) img = ImageTk.PhotoImage(img) # Set image as current @@ -841,6 +853,14 @@ def label_slider_status_update(self): def masks_slider_status_update(self): self.sliders.mask_slider.configure(state=tk.NORMAL if self.masks_on_local else tk.DISABLED) + def zoom_in(self, event): + self.zoom_factor *= self.ZOOM_STEP + self.update_img(local=False) + + def zoom_out(self, event): + self.zoom_factor /= self.ZOOM_STEP + self.update_img(local=False) + def bind_events(self): """Binds events.""" # Navigation @@ -868,6 +888,10 @@ def bind_events(self): self.objects_panel.object_box.bind("<>", self.select_object) self.image_panel.bind("", lambda e: self.image_panel.focus_set()) + # Zoom + self.root.bind("", self.zoom_in) + self.root.bind("", self.zoom_out) + def print_info(message: str): logging.info(message) From 37580c3b74977fea9385c3a08a1b76ca0f6f1c46 Mon Sep 17 00:00:00 2001 From: Jorge Bruned Date: Mon, 22 Apr 2024 13:45:34 +0200 Subject: [PATCH 7/7] Bind zoom listeners to menu items --- cocoviewer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cocoviewer.py b/cocoviewer.py index ffa16f4..83b6efc 100644 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -550,6 +550,8 @@ def __init__(self, data, root, image_panel, statusbar, menu, objects_panel, slid self.menu.view.entryconfigure("BBoxes", variable=self.bboxes_on_global, command=self.menu_view_bboxes) self.menu.view.entryconfigure("Labels", variable=self.labels_on_global, command=self.menu_view_labels) self.menu.view.entryconfigure("Masks", variable=self.masks_on_global, command=self.menu_view_masks) + self.menu.view.entryconfigure("Zoom In", command=self.zoom_in) + self.menu.view.entryconfigure("Zoom Out", command=self.zoom_out) self.menu.view.colormenu.entryconfigure( "Categories", variable=self.coloring_on_global, @@ -853,11 +855,11 @@ def label_slider_status_update(self): def masks_slider_status_update(self): self.sliders.mask_slider.configure(state=tk.NORMAL if self.masks_on_local else tk.DISABLED) - def zoom_in(self, event): + def zoom_in(self, event=None): self.zoom_factor *= self.ZOOM_STEP self.update_img(local=False) - def zoom_out(self, event): + def zoom_out(self, event=None): self.zoom_factor /= self.ZOOM_STEP self.update_img(local=False)