From 37604524be788b62e2602fdd124f8aff0529be9c Mon Sep 17 00:00:00 2001 From: xubin Date: Sat, 22 Jul 2023 17:36:05 +0800 Subject: [PATCH 1/5] fix bug of maximum 80 categories --- cocoviewer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocoviewer.py b/cocoviewer.py index 470a4be..3695484 100644 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -137,7 +137,7 @@ def prepare_colors(n_objects: int, shuffle: bool = True) -> list: def get_categories(instances: dict) -> dict: """Extracts categories from annotations file and prepares color for each one.""" # Parse categories - colors = prepare_colors(n_objects=80, shuffle=True) + colors = prepare_colors(n_objects=len(instances["categories"]), shuffle=True) categories = list( zip( [[category["id"], category["name"]] for category in instances["categories"]], From 45958bb20a2a6ea884f582955335d000f24c75ab Mon Sep 17 00:00:00 2001 From: xubin Date: Sat, 22 Jul 2023 18:18:49 +0800 Subject: [PATCH 2/5] add imagelist panel --- .gitignore | 3 ++- cocoviewer.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 08bd8aa..77b3f90 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,5 @@ coverage.xml dmypy.json # IDEA config files -.idea/ \ No newline at end of file +.idea/ +.vscode/ diff --git a/cocoviewer.py b/cocoviewer.py index 3695484..27fb8c3 100644 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -84,6 +84,9 @@ def next_image(self): def previous_image(self): """Loads the previous image in a list.""" self.current_image = self.images.prev() + + def select_image(self, ind): + self.current_image = self.images.set(ind) def parse_coco(annotations_file: str) -> tuple: @@ -180,7 +183,9 @@ 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) + text_size = draw.textbbox((0, 0), text, font=font) + tw = text_size[2] - text_size[0] + th = text_size[3] - text_size[1] tx0 = b[0] ty0 = b[1] - th @@ -212,7 +217,10 @@ def draw_masks(draw, objects, obj_categories, ignore, alpha): if isinstance(m, list): for m_ in m: if m_: - draw.polygon(m_, outline=fill, fill=fill) + try: + draw.polygon(m_, outline=fill, fill=fill) + except: + print('WARNING draw_masks: invalid polygon', m_) # RLE mask for collection of objects (iscrowd=1) elif isinstance(m, dict) and objects[i]["iscrowd"]: mask = rle_to_mask(m["counts"][:-1], m["size"][0], m["size"][1]) @@ -267,6 +275,12 @@ def prev(self): self.n -= 1 current_image = self.image_list[self.n] return current_image + + def set(self,ind): + assert ind>=0 and ind < self.max + self.n = ind + current_image = self.image_list[self.n] + return current_image class ImagePanel(ttk.Frame): @@ -487,6 +501,20 @@ def __init__(self, parent): self.object_box.pack(side=tk.TOP, fill=tk.Y, expand=True) self.add(self.object_subpanel) +class ImagelistPanel(ttk.PanedWindow): + def __init__(self, parent): + super().__init__(parent) + # Image list subpanel + self.pack(side=tk.LEFT, fill=tk.Y) + self.imglist_subpanel = ttk.Frame() + ttk.Label(self.imglist_subpanel, text="image list", borderwidth=2, background="gray50").pack(side=tk.TOP, fill=tk.X) + # image list controller + scrollbar = tk.Scrollbar(self.imglist_subpanel) + scrollbar.pack(side=tk.RIGHT, fill=tk.Y) + self.imglist_box = tk.Listbox(self.imglist_subpanel, selectmode=tk.EXTENDED, exportselection=0,yscrollcommand=scrollbar.set) + scrollbar.config(command=self.imglist_box.yview) + self.imglist_box.pack(side=tk.TOP, fill=tk.Y, expand=True) + self.add(self.imglist_subpanel) class SlidersBar(ttk.Frame): def __init__(self, parent): @@ -504,16 +532,18 @@ def __init__(self, parent): # Mask transparency controller self.mask_slider = tk.Scale(self, label="mask", from_=0, to=255, tickinterval=50, orient=tk.HORIZONTAL) self.mask_slider.pack(side=tk.LEFT, fill=tk.X, expand=True) + class Controller: - def __init__(self, data, root, image_panel, statusbar, menu, objects_panel, sliders): + def __init__(self, data, root, image_panel, statusbar, menu, objects_panel, sliders, imglist_panel): self.data = data # data layer self.root = root # root window self.image_panel = image_panel # image panel self.statusbar = statusbar # statusbar on the bottom self.menu = menu # main menu on the top self.objects_panel = objects_panel + self.imglist_panel = imglist_panel self.sliders = sliders # StatusBar Vars @@ -564,9 +594,10 @@ def __init__(self, data, root, image_panel, statusbar, menu, objects_panel, slid self.selected_objs = None self.category_box_content = tk.StringVar() self.object_box_content = tk.StringVar() + self.imglist_box_content = tk.StringVar() self.objects_panel.category_box.configure(listvariable=self.category_box_content) self.objects_panel.object_box.configure(listvariable=self.object_box_content) - + self.imglist_panel.imglist_box.configure(listvariable=self.imglist_box_content) # Sliders Setup self.bbox_thickness = tk.IntVar() self.bbox_thickness.set(3) @@ -587,6 +618,8 @@ def __init__(self, data, root, image_panel, statusbar, menu, objects_panel, slid self.current_img_categories = None self.update_img() + self.update_imglist_box() + def set_locals(self): self.bboxes_on_local = self.bboxes_on_global.get() self.labels_on_local = self.labels_on_global.get() @@ -825,6 +858,21 @@ def select_object(self, event): self.selected_cats = selected_cats self.update_img() + def update_imglist_box(self): + imglist = [f'{i:04d} {name}' for i,name in self.data.images.image_list] + self.imglist_box_content.set(imglist) + max_len = max(len(item) for item in imglist) + self.imglist_panel.imglist_box.config(width = max_len) + self.imglist_panel.imglist_box.select_set(0,tk.END) + + def select_img(self, event): + ind = self.imglist_panel.imglist_box.curselection()[0] + self.data.select_image(ind) + self.set_locals() + self.selected_cats = None + self.selected_objs = None + self.update_img(local=False) + def update_sliders_state(self): self.bbox_slider_status_update() self.label_slider_status_update() @@ -864,6 +912,7 @@ def bind_events(self): # Objects Panel self.objects_panel.category_box.bind("<>", self.select_category) self.objects_panel.object_box.bind("<>", self.select_object) + self.imglist_panel.imglist_box.bind("<>", self.select_img) self.image_panel.bind("", lambda e: self.image_panel.focus_set()) @@ -888,9 +937,10 @@ def main(): statusbar = StatusBar(root) sliders = SlidersBar(root) objects_panel = ObjectsPanel(root) + imglist_panel = ImagelistPanel(root) menu = Menu(root) image_panel = ImagePanel(root) - Controller(data, root, image_panel, statusbar, menu, objects_panel, sliders) + Controller(data, root, image_panel, statusbar, menu, objects_panel, sliders, imglist_panel) root.mainloop() From 37d090f799591eae7f7f5c6c8eb80b2ed24d16a3 Mon Sep 17 00:00:00 2001 From: xubin Date: Mon, 24 Jul 2023 15:30:15 +0800 Subject: [PATCH 3/5] align keyboard respond with imagelist --- cocoviewer.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) mode change 100644 => 100755 cocoviewer.py diff --git a/cocoviewer.py b/cocoviewer.py old mode 100644 new mode 100755 index 27fb8c3..9266680 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -511,7 +511,7 @@ def __init__(self, parent): # image list controller scrollbar = tk.Scrollbar(self.imglist_subpanel) scrollbar.pack(side=tk.RIGHT, fill=tk.Y) - self.imglist_box = tk.Listbox(self.imglist_subpanel, selectmode=tk.EXTENDED, exportselection=0,yscrollcommand=scrollbar.set) + self.imglist_box = tk.Listbox(self.imglist_subpanel, selectmode=tk.SINGLE, exportselection=0,yscrollcommand=scrollbar.set) scrollbar.config(command=self.imglist_box.yview) self.imglist_box.pack(side=tk.TOP, fill=tk.Y, expand=True) self.add(self.imglist_subpanel) @@ -721,18 +721,20 @@ def exit(self, event=None): self.root.quit() def next_img(self, event=None): - self.data.next_image() - self.set_locals() - self.selected_cats = None - self.selected_objs = None - self.update_img(local=False) + cur_id = self.imglist_panel.imglist_box.index('active') + cur_id = min(self.imglist_panel.imglist_box.size() - 1,cur_id+1) + self.imglist_panel.imglist_box.selection_clear(0, tk.END) + self.imglist_panel.imglist_box.activate(cur_id) + self.imglist_panel.imglist_box.selection_set(cur_id) + self.select_img(None) def prev_img(self, event=None): - self.data.previous_image() - self.set_locals() - self.selected_cats = None - self.selected_objs = None - self.update_img(local=False) + cur_id = self.imglist_panel.imglist_box.index('active') + cur_id = max(0,cur_id-1) + self.imglist_panel.imglist_box.selection_clear(0, tk.END) + self.imglist_panel.imglist_box.activate(cur_id) + self.imglist_panel.imglist_box.selection_set(cur_id) + self.select_img(None) def save_image(self, event=None): """Saves composed image as png file.""" From 305a8e3fae41aac3f05eff166e50600bd1198460 Mon Sep 17 00:00:00 2001 From: xubin Date: Fri, 28 Jul 2023 11:48:00 +0800 Subject: [PATCH 4/5] add icon --- coco_viewer.png | Bin 0 -> 28244 bytes cocoviewer.py | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 coco_viewer.png diff --git a/coco_viewer.png b/coco_viewer.png new file mode 100644 index 0000000000000000000000000000000000000000..2736f2ee3aacb166e70b905b83181243306b231e GIT binary patch literal 28244 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 DzK$&C literal 0 HcmV?d00001 diff --git a/cocoviewer.py b/cocoviewer.py index 9266680..11aea40 100755 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -927,7 +927,9 @@ def main(): args = parser.parse_args() root = tk.Tk() root.title("COCO Viewer") - + # Set the window icon + icon_path = os.path.dirname(os.readlink(__file__))+'/coco_viewer.png' + root.iconphoto(True, tk.PhotoImage(file=icon_path)) 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!") From 134088f91cd590d2337e562f92a0f6fee4a4971f Mon Sep 17 00:00:00 2001 From: binxu <422844229@qq.com> Date: Sat, 7 Oct 2023 20:39:48 +0800 Subject: [PATCH 5/5] Update cocoviewer.py --- cocoviewer.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/cocoviewer.py b/cocoviewer.py index 11aea40..a904250 100755 --- a/cocoviewer.py +++ b/cocoviewer.py @@ -20,16 +20,12 @@ logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") parser = argparse.ArgumentParser(description="View images with bboxes from the COCO dataset") -parser.add_argument("-i", "--images", default="", type=str, metavar="PATH", help="path to images folder") parser.add_argument( - "-a", - "--annotations", - default="", + "annotations", type=str, - metavar="PATH", help="path to annotations json file", ) - +parser.add_argument("imagedir", default="./",nargs='?', type=str, help="path to images folder") class Data: """Handles data related stuff.""" @@ -928,16 +924,10 @@ def main(): root = tk.Tk() root.title("COCO Viewer") # Set the window icon - icon_path = os.path.dirname(os.readlink(__file__))+'/coco_viewer.png' + icon_path = os.path.dirname(os.path.realpath(__file__))+'/coco_viewer.png' root.iconphoto(True, tk.PhotoImage(file=icon_path)) - 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 - data = Data(args.images, args.annotations) + data = Data(args.imagedir, args.annotations) statusbar = StatusBar(root) sliders = SlidersBar(root) objects_panel = ObjectsPanel(root)