def init_gui(self, cim): """Make a new tkinter window""" root = Tk() root.grid_rowconfigure(0, weight=1) root.columnconfigure(0, weight=1) root.title(cim) root.resizable(False, True) frame_main = Frame(root) frame_main.grid(sticky='news') # Create a frame for the canvas with non-zero row&column weights frame_canvas = Frame(frame_main) frame_canvas.grid(row=0, column=0) frame_canvas.grid_rowconfigure(0, weight=1) frame_canvas.grid_columnconfigure(0, weight=1) # Set grid_propagate to False to allow 5-by-5 buttons resizing later frame_canvas.grid_propagate(False) # Add a canvas in that frame canvas = Canvas(frame_canvas) canvas.grid(row=0, column=0) canvas.pack(fill="both", expand=True) # Link a scrollbar to the canvas vsb = Scrollbar(frame_canvas, orient="vertical", command=canvas.yview) vsb.grid(row=0, column=1, sticky='ns') canvas.configure(yscrollcommand=vsb.set) # Create a frame to contain the scrollable content frame_progress = Frame(canvas) canvas.create_window((0, 0), window=frame_progress, anchor='nw') self.canvas = canvas self.frame_main = frame_main for process in self.process_list: self.init_new_progressbar(frame_progress, p_length=300, p_process=process) frame_progress.update_idletasks() p = self.process_list[0] frame_width = p['task_label'].winfo_width( ) + p['progressbar'].winfo_width() + p['status_label'].winfo_width() rows = 20 if len(self.process_list) > 20 else len(self.process_list) frame_height = (p['progressbar'].winfo_height() * rows) frame_canvas.config(width=frame_width + vsb.winfo_width(), height=frame_height) canvas.config(width=frame_width + vsb.winfo_width(), height=frame_height) canvas.config(scrollregion=canvas.bbox("all")) canvas.update() self.frame_main.bind("<Configure>", self.configure) return root
class ToolBox(ttk.Labelframe): def __init__(self, *args, **kwargs): ttk.Labelframe.__init__(self, *args, text = "Tools") self.config(height = 200, width = 200) self.bind("<MouseWheel>", self._on_mouse_wheel) self.eyedropper_symbol = load_tk_image_from_bytes_array(eyedropper_symbol_gray_bytes) self.pencil_symbol = load_tk_image_from_bytes_array(pencil_symbol_gray_bytes) self.updownleftright = load_tk_image_from_bytes_array(updownleftright_symbol_gray_bytes) self.updownarrow_symbol = load_tk_image_from_bytes_array(updownarrow_symbol_gray_bytes) self.leftrightarrow_symbol = load_tk_image_from_bytes_array(leftrightarrow_symbol_gray_bytes) self.bucket_symbol = load_tk_image_from_bytes_array(bucket_symbol_gray_bytes) self.eraser_symbol = load_tk_image_from_bytes_array(eraser_symbol_gray_bytes) self.line_symbol = load_tk_image_from_bytes_array(line_symbol_gray_bytes) self.rectangle_symbol = load_tk_image_from_bytes_array(rectangle_symbol_bytes) self.filled_rectangle_symbol = load_tk_image_from_bytes_array(filled_rectangle_bytes) self.select_box_symbol = load_tk_image_from_bytes_array(select_box_symbol_bytes) self.ellipse_symbol = load_tk_image_from_bytes_array(ellipse_symbol_bytes) self.filled_ellipse_symbol = load_tk_image_from_bytes_array(filled_ellipse_symbol_bytes) self.move_selection_symbol = load_tk_image_from_bytes_array(move_selection_symbol_bytes) self.paste_symbol = load_tk_image_from_bytes_array(paste_symbol_bytes) self.tool_symbol_map = { TOOLCONST.DRAW : self.pencil_symbol, TOOLCONST.LINE : self.line_symbol, TOOLCONST.SELECT_BOX : self.select_box_symbol, TOOLCONST.MOVE_SELECTION : self.move_selection_symbol, TOOLCONST.RECTANGLE : self.rectangle_symbol, TOOLCONST.FILLED_RECTANGLE: self.filled_rectangle_symbol, TOOLCONST.ELLIPSE : self.ellipse_symbol, TOOLCONST.FILLED_ELLIPSE : self.filled_ellipse_symbol, TOOLCONST.BUCKET : self.bucket_symbol, TOOLCONST.DRAW_ROW : self.leftrightarrow_symbol, TOOLCONST.DRAW_COLUMN : self.updownarrow_symbol, # TOOLCONST.EYEDROPPER : self.eyedropper_symbol, TOOLCONST.ERASE : self.eraser_symbol, TOOLCONST.PASTE : self.paste_symbol, } self.thumbnails = [] self.tiles = [] self.canvas_height = 200 self.canvas = ResizableCanvas(self, relief="sunken") self.canvas.config(width = 200, height = 200, highlightthickness=0) self.scrollbar = Scrollbar(self) self.scrollbar.config(command=self.on_scroll_bar) self.canvas.config(yscrollcommand=self.scrollbar.set) self.scrollbar.pack(side="right", fill="y") self.canvas.pack(side = "right", expand=True, fill="both") self.canvas_frame = Frame(self.canvas, border = 0, highlightthickness = 0) self.canvas_frame.bind("<MouseWheel>", self._on_mouse_wheel) self.canvas_frame.config(width= 200, height = self.canvas_height) self.canvas.create_window(0,0, window=self.canvas_frame, anchor='nw') self.canvas.bind("<MouseWheel>", self._on_mouse_wheel) self.canvas.config(scrollregion=(0,0, 200, self.canvas_height)) self.canvas.bind("<Enter>", self.mouse_enter) self.canvas.bind("<Leave>", self.mouse_leave) self.canvas.bind("<Motion>", self.on_mouse_move) self.canvas.bind("<Button-1>", self.on_click) self.canvas.bind("<Configure>", self.on_configure) if kwargs.get("controller"): self.controller = kwargs.pop("controller") else: self.controller = ToolController self.canvas.after_idle(self.refresh) def on_configure(self, event = None): self.refresh() self.canvas.config( width=self.winfo_width(), height=self.winfo_height(), highlightthickness=0) def mouse_enter(self, event): pass def mouse_leave(self, event): pass def on_click(self, event): y = int(event.y + (float(self.canvas.yview()[0]) * self.canvas_height)) x = event.x for t in self.tiles: if t.is_in_range(x,y): ToolController.set_tool(t.tool) self.refresh() else: t.deactivate() def on_mouse_move(self, event): y = int(event.y + (float(self.canvas.yview()[0]) * self.canvas_height)) x = event.x for t in self.tiles: if t.is_in_range(x,y): if t.active: continue else: t.activate() else: t.deactivate() def refresh(self, event = None): self.canvas.delete("all") self.thumbnails = [] self.tiles = [] self.canvas.delete("all") BUTTONWIDTH = 25.0 padding = 10.0 x_spacing = BUTTONWIDTH + padding y_spacing = BUTTONWIDTH + padding y_space_offset = padding #Set the width _framewidth = self.canvas.winfo_width() - self.scrollbar.winfo_width() self.canvas_frame.config(width=_framewidth) #Get integer number of tiles fittable in the window _maxperrow = _framewidth // x_spacing #If there's not enough room to build anything if not _maxperrow: return empty_space = _framewidth - (_maxperrow * x_spacing) space_offset = empty_space / (_maxperrow + 1) _y = 0 _x = 0 for t in self.tool_symbol_map: base_y = _y * y_spacing + padding + (_y + 1) * (y_space_offset) base_x = _x * x_spacing + padding + (_x + 1) * (space_offset) tt = ToolTile(self, t, self.tool_symbol_map[t]) self.tiles.append(tt) tt.set_dimensions(base_x, base_y, x_spacing, y_spacing) self.place_tile(tt) _x += 1 if _x == _maxperrow: _x = 0 _y += 1 _y += 1 _canvasheight = _y * y_spacing + padding + (_y + 1) * (y_space_offset) if _canvasheight < self.winfo_height(): _canvasheight = self.winfo_height() self.canvas_frame.config(height = _canvasheight,width= _framewidth) self.canvas.config(scrollregion=(0,0,_framewidth, _canvasheight)) tool = TOOLS[ToolController.tool]["text"] self.configure(text = f"Tools - {tool}") def place_tile(self, tile): tn = tile.thumbnail self.thumbnails.append(tn) tile.references.append(self.canvas.create_image(tile.x + 4, tile.y + 4, anchor = "nw", image = tn)) if tile.active: self.activate_tile() if tile.tool == ToolController.tool: self.select_tile(tile) def select_tile(self, tile): self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height, outline="#000000", width = 2), def activate_tile(self, tile): tile.active_references.extend([ self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height, outline="#000000", width = 1), ]) def deactivate_tile(self, tile): for r in tile.active_references: self.canvas.delete(r) def _on_mouse_wheel(self, event): if platform.system() == 'Windows': self.canvas.yview_scroll(-1 * int(event.delta / 120), 'units') elif platform.system() == 'Darwin': self.canvas.yview_scroll(-1 * int(event.delta), 'units') else: if event.num == 4: self.canvas.yview_scroll(-1, 'units') elif event.num == 5: self.canvas.yview_scroll(1, 'units') def on_scroll_bar(self, move_type, move_units, __ = None): if move_type == "moveto": self.canvas.yview("moveto", move_units)
class StartPage(Frame): def __init__(self, controller, *args, **kwargs): ttk.Frame.__init__(self, *args, **kwargs) self.controller = controller self.repos = [] self.thumbnails = [] self.tiles = [] self.apppage = None canvas_container = Frame(self) canvas_container.pack(side="top", fill="both", expand=True) self.canvas_height = 720 self.canvas_width = 1080 self.canvas = ResizableCanvas(canvas_container, relief="sunken", background="#333333") self.canvas.config(width=self.canvas_width, height=self.canvas_height, highlightthickness=0) self.scrollbar = Scrollbar(canvas_container) self.scrollbar.config(command=self.on_scroll_bar) self.canvas.config(yscrollcommand=self.scrollbar.set) self.scrollbar.pack(side="right", fill="y") self.canvas.pack(side="right", expand=True, fill="both") self.canvas_frame = Frame(self.canvas, border=0, highlightthickness=0) self.canvas_frame.bind("<MouseWheel>", self._on_mouse_wheel) self.canvas_frame.config(width=self.canvas_width, height=self.canvas_height) self.canvas.create_window(0, 0, window=self.canvas_frame, anchor='nw') self.canvas.bind("<MouseWheel>", self._on_mouse_wheel) self.canvas.config(scrollregion=(0, 0, self.canvas_height, self.canvas_height)) # self.canvas.bind("<Enter>", self.mouse_enter) # self.canvas.bind("<Leave>", self.mouse_leave) self.canvas.bind("<Motion>", self.mouse_move) self.canvas.bind("<Button-1>", self.on_click) self.canvas.bind("<Configure>", self.on_configure) self.bind("<MouseWheel>", self._on_mouse_wheel) self.refresh() def on_configure(self, event=None): self.refresh() self.canvas.config(width=self.winfo_width(), height=self.winfo_height(), highlightthickness=0) # def mouse_enter(self, event): pass # def mouse_leave(self, event): pass def load_repo(self, repo): waiting_frame = Frame(self, background="#333333") waiting_frame.place(relwidth=1, relheight=1) print(f"Loading repo {repo.name}") # waiting_text = Label(waiting_frame, # text = "Please wait while the repo loads...", # font = font.Font(size = 46, weight = "bold"), # background = "#333333", # foreground = "#CCCCCC", # ) # waiting_text.place( # relwidth = 1, # relheight = 1 # ) waiting_text = Text( waiting_frame, wrap="word", background="#333333", foreground="#CCCCCC", borderwidth=0, highlightthickness=0, font=font.Font(size=46, weight="bold"), ) waiting_text.tag_configure("center", justify='center') waiting_text.insert("end", "Please wait while the repo loads") waiting_text.tag_add("center", "1.0", "end") waiting_text.configure(state="disable") waiting_text.place(relwidth=1, relheight=1) def load_and_close_wait(): repo.load_repo() self.apppage = AppPage(self.controller, repo, self) self.apppage.place(relwidth=1, relheight=1) waiting_frame.destroy() self.apppage.refresh() self.controller.threader.new_session() self.controller.threader.add(load_and_close_wait) def on_click(self, event): y = int(event.y + (float(self.canvas.yview()[0]) * self.canvas_height)) x = event.x for t in self.tiles: if t.is_in_range(x, y): self.load_repo(t.repo) else: t.deactivate() def mouse_move(self, event): y = int(event.y + (float(self.canvas.yview()[0]) * self.canvas_height)) x = event.x for t in self.tiles: if t.is_in_range(x, y): if t.active: continue else: t.activate() else: t.deactivate() def refresh(self, event=None): self.canvas.delete("all") self.thumbnails = [] self.tiles = [] self.canvas.delete("all") i = 0 y_offset = 10 y_padding = 50 x_padding = 50 tile_height = 200 tile_width = 200 framewidth = self.canvas.winfo_width() - self.scrollbar.winfo_width() self.canvas_frame.config(width=framewidth) # Get integer number of tiles fittable in the window maxperrow = framewidth // (tile_width + x_padding) #If there's not enough room to build anything stop if not maxperrow: return maxwidth = (maxperrow) * tile_width + maxperrow * x_padding filler_space = framewidth - maxwidth filler_space = filler_space / (maxperrow + 1) _x, _y = 0, 0 #vars to track where in grid we are placing for r in self.controller.repos: place_x = _x * tile_width + (_x + 1) * x_padding + ( _x + 1) * filler_space place_y = _y * tile_height + y_offset + (_y + 1) * y_padding rt = Tile(self, r, load_image_object_from_bytes_array(r.image)) self.tiles.append(rt) rt.set_dimensions(place_x, place_y, tile_width, tile_height) self.place_tile(rt) _x += 1 if _x == maxperrow: _x, _y = 0, _y + 1 _y += 1 height = _y * tile_height + y_offset + (_y + 1) * y_padding frameheight = self.canvas_frame.winfo_height() height = height if height > frameheight else frameheight self.canvas_height = height self.canvas_frame.config(width=200, height=self.winfo_height()) self.canvas.config(scrollregion=(0, 0, 200, self.canvas_height)) def place_tile(self, tile): tile.references.append( self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width, tile.y + tile.height - 1, fill="#7F7F7F")) if tile.thumbnail: maxwidth = tile.width - 8 maxheight = tile.width - 8 art_image = tile.thumbnail wpercent = (maxwidth / float(art_image.size[0])) hsize = int((float(art_image.size[1]) * float(wpercent))) new_image = art_image.resize((maxwidth, hsize), Image.ANTIALIAS) if new_image.size[1] > maxheight: hpercent = (maxheight / float(art_image.size[1])) wsize = int((float(art_image.size[0]) * float(hpercent))) new_image = art_image.resize((wsize, maxheight), Image.ANTIALIAS) thumbnail = ImageTk.PhotoImage(new_image) self.thumbnails.append(thumbnail) tile.references.append( self.canvas.create_image(tile.x + 4, tile.y + 4, anchor="nw", image=thumbnail)) tile.references.extend([ self.canvas.create_text(tile.x + 8, tile.y + 8, anchor="nw", text=tile.repo_name, font="bold", fill="#CCCCCC") ]) if tile.active: self.activate_tile() def activate_tile(self, tile): tile.active_references.extend([ self.canvas.create_rectangle(tile.x, tile.y, tile.x + tile.width - 1, tile.y + tile.height - 1, outline="#000000", width=4), ]) def deactivate_tile(self, tile): for r in tile.active_references: self.canvas.delete(r) def _on_mouse_wheel(self, event): if platform.system() == 'Windows': self.canvas.yview_scroll(-1 * int(event.delta / 120), 'units') elif platform.system() == 'Darwin': self.canvas.yview_scroll(-1 * int(event.delta), 'units') else: if event.num == 4: self.canvas.yview_scroll(-1, 'units') elif event.num == 5: self.canvas.yview_scroll(1, 'units') def on_scroll_bar(self, move_type, move_units, __=None): if move_type == "moveto": self.canvas.yview("moveto", move_units) def exit(self, event=None): if self.apppage: try: self.apppage.exit() except: pass
class vis_tool: def __init__(self): self.args = parse_args() cfg = mmcv.Config.fromfile(self.args.config) self.window = Tk() self.menubar = Menu(self.window) self.info = StringVar() self.info_label = Label( self.window, bg='yellow', width=4, textvariable=self.info) self.listBox_img = Listbox( self.window, width=50, height=25, font=('Times New Roman', 10)) self.listBox_obj = Listbox( self.window, width=50, height=12, font=('Times New Roman', 10)) self.scrollbar_img = Scrollbar( self.window, width=15, orient='vertical') self.scrollbar_obj = Scrollbar( self.window, width=15, orient='vertical') self.listBox_img_info = StringVar() self.listBox_img_label = Label( self.window, font=('Arial', 11), bg='yellow', width=4, height=1, textvariable=self.listBox_img_info) self.listBox_obj_info = StringVar() self.listBox_obj_label1 = Label( self.window, font=('Arial', 11), bg='yellow', width=4, height=1, textvariable=self.listBox_obj_info) self.listBox_obj_label2 = Label( self.window, font=('Arial', 11), bg='yellow', width=4, height=1, text='Object Class : Score (IoU)') if cfg.dataset_type == 'VOCDataset': self.data_info = VOC_dataset(cfg, self.args) elif cfg.dataset_type == 'CocoDataset': self.data_info = COCO_dataset(cfg, self.args) self.info.set('DATASET: {}'.format(self.data_info.dataset)) # load image and show it on the window self.img = self.data_info.get_img_by_index(0) self.photo = ImageTk.PhotoImage(self.img) self.label_img = Label(self.window, image=self.photo) self.show_det_txt = IntVar(value=1) self.checkbn_det_txt = Checkbutton( self.window, text='Text', font=('Arial', 10, 'bold'), variable=self.show_det_txt, command=self.change_img, fg='#0000FF') self.show_dets = IntVar(value=1) self.checkbn_det = Checkbutton( self.window, text='Detections', font=('Arial', 10, 'bold'), variable=self.show_dets, command=self.change_img, fg='#0000FF') self.show_gt_txt = IntVar(value=1) self.checkbn_gt_txt = Checkbutton( self.window, text='Text', font=('Arial', 10, 'bold'), variable=self.show_gt_txt, command=self.change_img, fg='#FF8C00') self.show_gts = IntVar(value=1) self.checkbn_gt = Checkbutton( self.window, text='Groundtruth', font=('Arial', 10, 'bold'), variable=self.show_gts, command=self.change_img, fg='#FF8C00') self.combo_label = Label( self.window, bg='yellow', width=10, height=1, text='Show Category', font=('Arial', 11)) self.combo_category = ttk.Combobox( self.window, font=('Arial', 11), values=self.data_info.aug_category.combo_list) self.combo_category.current(0) self.th_label = Label( self.window, font=('Arial', 11), bg='yellow', width=10, height=1, text='Score Threshold') self.threshold = np.float32(0.5) self.th_entry = Entry( self.window, font=('Arial', 11), width=10, textvariable=StringVar(self.window, value=str(self.threshold))) self.th_button = Button( self.window, text='Enter', height=1, command=self.change_threshold) self.iou_th_label = Label( self.window, font=('Arial', 11), bg='yellow', width=10, height=1, text='IoU Threshold') self.iou_threshold = np.float32(0.5) self.iou_th_entry = Entry( self.window, font=('Arial', 11), width=10, textvariable=StringVar(self.window, value=str(self.iou_threshold))) self.iou_th_button = Button( self.window, text='Enter', height=1, command=self.change_iou_threshold) self.find_label = Label( self.window, font=('Arial', 11), bg='yellow', width=10, height=1, text='find') self.find_name = '' self.find_entry = Entry( self.window, font=('Arial', 11), width=10, textvariable=StringVar(self.window, value=str(self.find_name))) self.find_button = Button( self.window, text='Enter', height=1, command=self.findname) self.listBox_img_idx = 0 # ====== ohter attribute ====== self.img_name = '' self.show_img = None self.output = self.args.output if not os.path.isdir(self.output): os.makedirs(self.output) self.img_list = self.data_info.img_list # flag for find/threshold button switch focused element self.button_clicked = False def change_threshold(self, event=None): try: self.threshold = np.float32(self.th_entry.get()) self.change_img() # after changing threshold, focus on listBox for easy control if self.window.focus_get() == self.listBox_obj: self.listBox_obj.focus() else: self.listBox_img.focus() self.button_clicked = True except ValueError: self.window.title('Please enter a number as score threshold.') def change_iou_threshold(self, event=None): try: self.iou_threshold = np.float32(self.iou_th_entry.get()) self.change_img() # after changing threshold, focus on listBox for easy control if self.window.focus_get() == self.listBox_obj: self.listBox_obj.focus() else: self.listBox_img.focus() self.button_clicked = True except ValueError: self.window.title("Please enter a number as IoU threshold.") # draw groundtruth def draw_gt_boxes(self, img, objs): for obj in objs: cls_name = obj[0] # according combobox to decide whether to plot this category if self.combo_category.get() == 'All': show_category = self.data_info.aug_category.category else: show_category = [self.combo_category.get()] if cls_name not in show_category: continue box = obj[1:] xmin = max(box[0], 0) ymin = max(box[1], 0) xmax = min(box[0] + box[2], self.img_width) ymax = min(box[1] + box[3], self.img_height) font = cv2.FONT_HERSHEY_SIMPLEX if self.show_gt_txt.get(): if ymax + 30 >= self.img_height: cv2.rectangle(img, (xmin, ymin), (xmin + len(cls_name) * 10, int(ymin - 20)), (255, 140, 0), cv2.FILLED) cv2.putText(img, cls_name, (xmin, int(ymin - 5)), font, 0.5, (255, 255, 255), 1) else: cv2.rectangle(img, (xmin, ymax), (xmin + len(cls_name) * 10, int(ymax + 20)), (255, 140, 0), cv2.FILLED) cv2.putText(img, cls_name, (xmin, int(ymax + 15)), font, 0.5, (255, 255, 255), 1) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), self.args.gt_box_color, 1) return img def get_iou(self, det): iou = np.zeros_like(det) GT = self.data_info.get_singleImg_gt(self.img_name) for idx, cls_objs in enumerate(det): category = self.data_info.aug_category.category[idx] BBGT = [] for t in GT: if not t[0] == category: continue BBGT.append([t[1], t[2], t[1] + t[3], t[2] + t[4]]) BBGT = np.asarray(BBGT) d = [0] * len(BBGT) # for check 1 GT map to several det confidence = cls_objs[:, 4] BB = cls_objs[:, 0:4] # bounding box # sort by confidence sorted_ind = np.argsort(-confidence) sorted_scores = np.sort(-confidence) BB = BB[sorted_ind, :] # for returning original order ind_table = {i: sorted_ind[i] for i in range(len(sorted_ind))} iou[idx] = np.zeros(len(BB)) if len(BBGT) > 0: for i in range(len(BB)): bb = BB[i, :] # compute overlaps # intersection ixmin = np.maximum(BBGT[:, 0], bb[0]) iymin = np.maximum(BBGT[:, 1], bb[1]) ixmax = np.minimum(BBGT[:, 2], bb[2]) iymax = np.minimum(BBGT[:, 3], bb[3]) iw = np.maximum(ixmax - ixmin + 1., 0.) ih = np.maximum(iymax - iymin + 1., 0.) inters = iw * ih # union uni = ((bb[2] - bb[0] + 1.) * (bb[3] - bb[1] + 1.) + (BBGT[:, 2] - BBGT[:, 0] + 1.) * (BBGT[:, 3] - BBGT[:, 1] + 1.) - inters) overlaps = inters / uni ovmax = np.max(overlaps) # max overlaps with all gt jmax = np.argmax(overlaps) if ovmax > self.iou_threshold: if not d[jmax]: d[jmax] = 1 else: # multiple bounding boxes map to one gt ovmax = -ovmax iou[idx][ind_table[i]] = ovmax # return to unsorted order return iou def draw_all_det_boxes(self, img, single_detection): if self.data_info.has_anno: self.iou = self.get_iou(single_detection) for idx, cls_objs in enumerate(single_detection): category = self.data_info.aug_category.category[idx] if self.combo_category.get() == 'All': show_category = self.data_info.aug_category.category else: show_category = [self.combo_category.get()] if category not in show_category: continue for obj_idx, obj in enumerate(cls_objs): [score, box] = [round(obj[4], 2), obj[:4]] if score >= self.threshold: box = list(map(int, list(map(round, box)))) xmin = max(box[0], 0) ymin = max(box[1], 0) xmax = min(box[2], self.img_width) ymax = min(box[3], self.img_height) if not self.data_info.has_anno or \ self.iou[idx][obj_idx] >= self.iou_threshold: color = self.args.det_box_color else: color = (255, 0, 0) if self.show_det_txt.get(): font = cv2.FONT_HERSHEY_SIMPLEX text = category + ' : ' + str(score) if ymax + 30 >= self.img_height: cv2.rectangle( img, (xmin, ymin), (xmin + len(text) * 9, int(ymin - 20)), (0, 0, 255), cv2.FILLED) cv2.putText(img, text, (xmin, int(ymin - 5)), font, 0.5, (255, 255, 255), 1) else: cv2.rectangle( img, (xmin, ymax), (xmin + len(text) * 9, int(ymax + 20)), (0, 0, 255), cv2.FILLED) cv2.putText(img, text, (xmin, int(ymax + 15)), font, 0.5, (255, 255, 255), 1) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), color, 2) return img def draw_all_det_boxes_masks(self, img, single_detection): img = np.require(img, requirements=['W']) boxes, masks = single_detection # draw segmentation masks # reference mmdetection/mmdet/models/detectors/base.py if self.combo_category.get() != 'All': show_idx = self.data_info.aug_category.category.index( self.combo_category.get()) masks = np.asarray([masks[show_idx]]) boxes = np.asarray([boxes[show_idx]]) category = self.data_info.aug_category.category[show_idx] segms = list(itertools.chain(*masks)) bboxes = np.vstack(boxes) inds = np.where(np.round(bboxes[:, -1], 2) >= self.threshold)[0] self.color_list = [] for i in inds: color_mask = np.random.randint(0, 256, (1, 3), dtype=np.uint8) mask = maskUtils.decode(segms[i]).astype(np.bool) img[mask] = img[mask] * 0.5 + color_mask * 0.5 self.color_list.append('#%02x%02x%02x' % tuple(color_mask[0])) if self.data_info.has_anno: boxes2, _ = single_detection self.iou = self.get_iou(boxes2) if self.combo_category.get() != 'All': iou = np.asarray([self.iou[show_idx]]) else: iou = self.iou # draw bounding box for idx, cls_objs in enumerate(boxes): if self.combo_category.get() == 'All': category = self.data_info.aug_category.category[idx] for obj_idx, obj in enumerate(cls_objs): [score, box] = [round(obj[4], 2), obj[:4]] if score >= self.threshold: box = list(map(int, list(map(round, box)))) xmin = max(box[0], 0) ymin = max(box[1], 0) xmax = min(box[2], self.img_width) ymax = min(box[3], self.img_height) if not self.data_info.has_anno or \ iou[idx][obj_idx] >= self.iou_threshold: color = self.args.det_box_color else: color = (255, 0, 0) if self.show_det_txt.get(): font = cv2.FONT_HERSHEY_SIMPLEX text = category + ' : ' + str(score) if ymax + 30 >= self.img_height: cv2.rectangle( img, (xmin, ymin), (xmin + len(text) * 9, int(ymin - 20)), (0, 0, 255), cv2.FILLED) cv2.putText(img, text, (xmin, int(ymin - 5)), font, 0.5, (255, 255, 255), 1) else: cv2.rectangle( img, (xmin, ymax), (xmin + len(text) * 9, int(ymax + 20)), (0, 0, 255), cv2.FILLED) cv2.putText(img, text, (xmin, int(ymax + 15)), font, 0.5, (255, 255, 255), 1) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), color, 2) return img def change_img(self, event=None): if len(self.listBox_img.curselection()) != 0: self.listBox_img_idx = self.listBox_img.curselection()[0] self.listBox_img_info.set('Image {:6} / {:6}'.format( self.listBox_img_idx + 1, self.listBox_img.size())) name = self.listBox_img.get(self.listBox_img_idx) self.window.title('DATASET : ' + self.data_info.dataset + ' ' + name) img = self.data_info.get_img_by_name(name) self.img_width, self.img_height = img.width, img.height img = np.asarray(img) self.img_name = name self.img = img if self.data_info.has_anno and self.show_gts.get(): objs = self.data_info.get_singleImg_gt(name) img = self.draw_gt_boxes(img, objs) if self.data_info.results is not None and self.show_dets.get(): if self.data_info.mask is False: dets = self.data_info.get_singleImg_dets(name) img = self.draw_all_det_boxes(img, dets) else: dets = self.data_info.get_singleImg_dets(name).transpose( (1, 0)) img = self.draw_all_det_boxes_masks(img, dets) self.clear_add_listBox_obj() self.show_img = img img = Image.fromarray(img) img = self.scale_img(img) self.photo = ImageTk.PhotoImage(img) self.label_img.config(image=self.photo) self.window.update_idletasks() if self.img_name in os.listdir(self.output): self.listBox_img_label.config(bg='#CCFF99') else: self.listBox_img_label.config(bg='yellow') def draw_one_det_boxes(self, img, single_detection, selected_idx=-1): idx_counter = 0 for idx, cls_objs in enumerate(single_detection): category = self.data_info.aug_category.category[idx] if self.combo_category.get() == 'All': show_category = self.data_info.aug_category.category else: show_category = [self.combo_category.get()] if category not in show_category: continue for obj_idx, obj in enumerate(cls_objs): [score, box] = [round(obj[4], 2), obj[:4]] if score >= self.threshold: if idx_counter == selected_idx: box = list(map(int, list(map(round, box)))) xmin = max(box[0], 0) ymin = max(box[1], 0) xmax = min(box[2], self.img_width) ymax = min(box[3], self.img_height) if not self.data_info.has_anno or \ self.iou[idx][obj_idx] >= self.iou_threshold: color = self.args.det_box_color else: color = (255, 0, 0) if self.show_det_txt.get(): font = cv2.FONT_HERSHEY_SIMPLEX text = category + ' : ' + str(score) if ymax + 30 >= self.img_height: cv2.rectangle( img, (xmin, ymin), (xmin + len(text) * 9, int(ymin - 20)), (0, 0, 255), cv2.FILLED) cv2.putText(img, text, (xmin, int(ymin - 5)), font, 0.5, (255, 255, 255), 1) else: cv2.rectangle( img, (xmin, ymax), (xmin + len(text) * 9, int(ymax + 20)), (0, 0, 255), cv2.FILLED) cv2.putText(img, text, (xmin, int(ymax + 15)), font, 0.5, (255, 255, 255), 1) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), color, 2) return img else: idx_counter += 1 def draw_one_det_boxes_masks(self, img, single_detection, selected_idx=-1): img = np.require(img, requirements=['W']) boxes, masks = single_detection # draw segmentation masks # reference mmdetection/mmdet/models/detectors/base.py if self.combo_category.get() != 'All': show_idx = self.data_info.aug_category.category.index( self.combo_category.get()) category = self.data_info.aug_category.category[ show_idx] # fixed category masks = np.asarray([masks[show_idx]]) boxes = np.asarray([boxes[show_idx]]) segms = list(itertools.chain(*masks)) bboxes = np.vstack(boxes) inds = np.where(np.round(bboxes[:, -1], 2) >= self.threshold)[0] self.color_list = [] for inds_idx, i in enumerate(inds): if inds_idx == selected_idx: color_mask = np.random.randint(0, 256, (1, 3), dtype=np.uint8) mask = maskUtils.decode(segms[i]).astype(np.bool) img[mask] = img[mask] * 0.5 + color_mask * 0.5 self.color_list.append('#%02x%02x%02x' % tuple(color_mask[0])) if self.data_info.has_anno: if self.combo_category.get() != 'All': iou = np.asarray([self.iou[show_idx]]) else: iou = self.iou # draw bounding box idx_counter = 0 for idx, cls_objs in enumerate(boxes): if self.combo_category.get() == 'All': category = self.data_info.aug_category.category[idx] for obj_idx, obj in enumerate(cls_objs): [score, box] = [round(obj[4], 2), obj[:4]] if score >= self.threshold: if idx_counter == selected_idx: box = list(map(int, list(map(round, box)))) xmin = max(box[0], 0) ymin = max(box[1], 0) xmax = min(box[2], self.img_width) ymax = min(box[3], self.img_height) if not self.data_info.has_anno or \ iou[idx][obj_idx] >= self.iou_threshold: color = self.args.det_box_color else: color = (255, 0, 0) if self.show_det_txt.get(): font = cv2.FONT_HERSHEY_SIMPLEX text = category + ' : ' + str(score) if ymax + 30 >= self.img_height: cv2.rectangle( img, (xmin, ymin), (xmin + len(text) * 9, int(ymin - 20)), (0, 0, 255), cv2.FILLED) cv2.putText(img, text, (xmin, int(ymin - 5)), font, 0.5, (255, 255, 255), 1) else: cv2.rectangle( img, (xmin, ymax), (xmin + len(text) * 9, int(ymax + 20)), (0, 0, 255), cv2.FILLED) cv2.putText(img, text, (xmin, int(ymax + 15)), font, 0.5, (255, 255, 255), 1) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), color, 2) return img else: idx_counter += 1 # plot only one object def change_obj(self, event=None): if len(self.listBox_obj.curselection()) == 0: self.listBox_img.focus() return else: listBox_obj_idx = self.listBox_obj.curselection()[0] self.listBox_obj_info.set('Detected Object : {:4} / {:4}'.format( listBox_obj_idx + 1, self.listBox_obj.size())) name = self.listBox_img.get(self.listBox_img_idx) img = self.data_info.get_img_by_name(name) self.img_width, self.img_height = img.width, img.height img = np.asarray(img) self.img_name = name self.img = img if self.data_info.has_anno and self.show_gts.get(): objs = self.data_info.get_singleImg_gt(name) img = self.draw_gt_boxes(img, objs) if self.data_info.results is not None and self.show_dets.get(): if self.data_info.mask is False: dets = self.data_info.get_singleImg_dets(name) img = self.draw_one_det_boxes(img, dets, listBox_obj_idx) else: dets = self.data_info.get_singleImg_dets(name).transpose( (1, 0)) img = self.draw_one_det_boxes_masks(img, dets, listBox_obj_idx) self.show_img = img img = Image.fromarray(img) img = self.scale_img(img) self.photo = ImageTk.PhotoImage(img) self.label_img.config(image=self.photo) self.window.update_idletasks() if self.img_name in os.listdir(self.output): self.listBox_img_label.config(bg='#CCFF99') else: self.listBox_img_label.config(bg='yellow') # ============================================ def scale_img(self, img): [s_w, s_h] = [1, 1] # if window size is (1920, 1080), # the default max image size is (1440, 810) (fix_width, fix_height) = (1440, 810) # change image size according to window size if self.window.winfo_width() != 1: fix_width = ( self.window.winfo_width() - self.listBox_img.winfo_width() - self.scrollbar_img.winfo_width() - 5) # fix_height = int(fix_width * 9 / 16) fix_height = 750 # handle image size is too big if img.width > fix_width: s_w = fix_width / img.width if img.height > fix_height: s_h = fix_height / img.height scale = min(s_w, s_h) img = img.resize((int(img.width * scale), int(img.height * scale)), Image.ANTIALIAS) return img def clear_add_listBox_obj(self): self.listBox_obj.delete(0, 'end') if self.data_info.mask is False: single_detection = self.data_info.get_singleImg_dets( self.img_list[self.listBox_img_idx]) else: single_detection, single_mask = self.data_info.get_singleImg_dets( self.img_list[self.listBox_img_idx]).transpose((1, 0)) if self.combo_category.get() == 'All': show_category = self.data_info.aug_category.category else: show_category = [self.combo_category.get()] num = 0 for idx, cls_objs in enumerate(single_detection): category = self.data_info.aug_category.category[idx] if category not in show_category: continue for obj_idx, obj in enumerate(cls_objs): score = np.round(obj[4], 2) if score >= self.threshold: if not self.data_info.has_anno: self.listBox_obj.insert('end', category + " : " + str(score)) elif self.iou[idx][obj_idx] > self.iou_threshold: s = "{:15} : {:5.3} ( {:<6.3})".format( category, score, abs(round(self.iou[idx][obj_idx], 2))) self.listBox_obj.insert('end', s) self.listBox_obj.itemconfig(num, fg="green") else: s = "{:15} : {:5.3} ( {:<6.3})".format( category, score, abs(round(self.iou[idx][obj_idx], 2))) self.listBox_obj.insert('end', s) self.listBox_obj.itemconfig(num, fg="red") num += 1 self.listBox_obj_info.set('Detected Object : {:3}'.format(num)) def change_threshold_button(self, v): self.threshold += v if self.threshold <= 0: self.threshold = 0 elif self.threshold >= 1: self.threshold = 1 self.th_entry.delete(0, END) self.th_entry.insert(0, str(round(self.threshold, 2))) self.change_threshold() def change_iou_threshold_button(self, v): self.iou_threshold += v if self.iou_threshold <= 0: self.iou_threshold = 0 elif self.iou_threshold >= 1: self.iou_threshold = 1 self.iou_th_entry.delete(0, END) self.iou_th_entry.insert(0, str(round(self.iou_threshold, 2))) self.change_iou_threshold() def save_img(self): print('Save image to ' + os.path.join(self.output, self.img_name)) cv2.imwrite( os.path.join(self.output, self.img_name), cv2.cvtColor(self.show_img, cv2.COLOR_BGR2RGB)) self.listBox_img_label.config(bg='#CCFF99') def eventhandler(self, event): entry_list = [self.find_entry, self.th_entry, self.iou_th_entry] if self.window.focus_get() not in entry_list: if platform.system() == 'Windows': state_1key = 8 state_2key = 12 else: # 'Linux' state_1key = 16 state_2key = 20 if event.state == state_2key and event.keysym == 'Left': self.change_iou_threshold_button(-0.1) elif event.state == state_2key and event.keysym == 'Right': self.change_iou_threshold_button(0.1) elif event.state == state_1key and event.keysym == 'Left': self.change_threshold_button(-0.1) elif event.state == state_1key and event.keysym == 'Right': self.change_threshold_button(0.1) elif event.keysym == 'q': self.window.quit() elif event.keysym == 's': self.save_img() if self.button_clicked: self.button_clicked = False else: if event.keysym in ['KP_Enter', 'Return']: self.listBox_obj.focus() self.listBox_obj.select_set(0) elif event.keysym == 'Escape': self.change_img() self.listBox_img.focus() def combobox_change(self, event=None): self.listBox_img.focus() self.change_img() def clear_add_listBox_img(self): self.listBox_img.delete(0, 'end') # delete listBox_img 0 ~ end items # add image name to listBox_img for item in self.img_list: self.listBox_img.insert('end', item) self.listBox_img.select_set(0) self.listBox_img.focus() self.change_img() def findname(self, event=None): self.find_name = self.find_entry.get() new_list = [] if self.find_name == '': new_list = self.data_info.img_list else: for img_name in self.data_info.img_list: if self.find_name[0] == '!': if self.find_name[1:] not in img_name: new_list.append(img_name) else: if self.find_name in img_name: new_list.append(img_name) if len(new_list) != 0: self.img_list = new_list self.clear_add_listBox_img() self.clear_add_listBox_obj() self.button_clicked = True else: self.window.title("Can't find any image about '{}'".format( self.find_name)) def run(self): self.window.title('DATASET : ' + self.data_info.dataset) self.window.geometry('1280x800+350+100') # self.menubar.add_command(label='QUIT', command=self.window.quit) self.window.config(menu=self.menubar) # display the menu self.scrollbar_img.config(command=self.listBox_img.yview) self.listBox_img.config(yscrollcommand=self.scrollbar_img.set) self.scrollbar_obj.config(command=self.listBox_obj.yview) self.listBox_obj.config(yscrollcommand=self.scrollbar_obj.set) layer1 = 0 layer2 = 50 # ======================= layer 1 ========================= # combobox self.combo_label.grid( row=layer1 + 30, column=0, sticky=W + E + N + S, padx=3, pady=3, columnspan=6) self.combo_category.grid( row=layer1 + 30, column=6, sticky=W + E + N + S, padx=3, pady=3, columnspan=6) # show det self.checkbn_det.grid( row=layer1 + 40, column=0, sticky=N + S, padx=3, pady=3, columnspan=4) # show det text self.checkbn_det_txt.grid( row=layer1 + 40, column=4, sticky=N + S, padx=3, pady=3, columnspan=2) if self.data_info.has_anno != False: # show gt self.checkbn_gt.grid( row=layer1 + 40, column=6, sticky=N + S, padx=3, pady=3, columnspan=4) # show gt text self.checkbn_gt_txt.grid( row=layer1 + 40, column=10, sticky=N + S, padx=3, pady=3, columnspan=2) # ======================= layer 2 ========================= self.listBox_img_label.grid( row=layer2 + 0, column=0, sticky=N + S + E + W, columnspan=12) # find name self.find_label.grid( row=layer2 + 20, column=0, sticky=E + W, columnspan=4) self.find_entry.grid( row=layer2 + 20, column=4, sticky=E + W, columnspan=4) self.find_button.grid( row=layer2 + 20, column=8, sticky=E + W, pady=3, columnspan=4) self.scrollbar_img.grid(row=layer2 + 30, column=11, sticky=N + S + W) self.label_img.grid( row=layer1 + 30, column=12, sticky=N + E, padx=3, pady=3, rowspan=110) self.listBox_img.grid( row=layer2 + 30, column=0, sticky=N + S + E + W, pady=3, columnspan=11) if self.data_info.det_file != '': self.th_label.grid( row=layer2 + 40, column=0, sticky=E + W, columnspan=6) self.th_entry.grid( row=layer2 + 40, column=6, sticky=E + W, columnspan=3) self.th_button.grid( row=layer2 + 40, column=9, sticky=E + W, columnspan=3) if self.data_info.has_anno != False: self.iou_th_label.grid( row=layer2 + 50, column=0, sticky=E + W, columnspan=6) self.iou_th_entry.grid( row=layer2 + 50, column=6, sticky=E + W, columnspan=3) self.iou_th_button.grid( row=layer2 + 50, column=9, sticky=E + W, columnspan=3) self.listBox_obj_label1.grid( row=layer2 + 60, column=0, sticky=E + W, pady=3, columnspan=12) if self.data_info.has_anno != False: self.listBox_obj_label2.grid( row=layer2 + 70, column=0, sticky=E + W, pady=2, columnspan=12) self.scrollbar_obj.grid( row=layer2 + 80, column=11, sticky=N + S + W, pady=3) self.listBox_obj.grid( row=layer2 + 80, column=0, sticky=N + S + E + W, pady=3, columnspan=11) self.clear_add_listBox_img() self.listBox_img.bind('<<ListboxSelect>>', self.change_img) self.listBox_img.bind_all('<KeyRelease>', self.eventhandler) self.listBox_obj.bind('<<ListboxSelect>>', self.change_obj) self.th_entry.bind('<Return>', self.change_threshold) self.th_entry.bind('<KP_Enter>', self.change_threshold) self.iou_th_entry.bind('<Return>', self.change_iou_threshold) self.iou_th_entry.bind('<KP_Enter>', self.change_iou_threshold) self.find_entry.bind('<Return>', self.findname) self.find_entry.bind('<KP_Enter>', self.findname) self.combo_category.bind('<<ComboboxSelected>>', self.combobox_change) self.window.mainloop()