Пример #1
0
        def get_tools():
            scroll_y_listbox = Frame(window)
            scrollbar = Scrollbar(scroll_y_listbox, orient=VERTICAL)
            listbox = Listbox(scroll_y_listbox,
                              yscrollcommand=scrollbar.set,
                              borderwidth=0)

            def open_selected_tool(e=None):
                tool_file = listbox.get(ACTIVE)
                for x in self.opened_tools:
                    if tool_file in x:
                        raise InterfaceError(
                            f"Only One worksheet per workbook can be opened at a time, please close {x}"
                        )
                window_date = self.deploy_window("Select Date...",
                                                 300,
                                                 280,
                                                 resizable=False)

                ltitle = Label(window_date,
                               text="Select the day you are capturing for:",
                               pady=10)
                ltitle.pack()

                cal = Calendar(window_date)
                cal.pack()

                f = Frame(window_date, height=10)
                f.pack()

                def forward_with_date():
                    window_wait = self.deploy_window("Please Wait...",
                                                     500,
                                                     250,
                                                     resizable=False)
                    lbl = Label(
                        window_wait,
                        text=
                        "Loading Data... Don't close the program, this may take a minute."
                    )
                    lbl.pack()

                    open_tool = Tool(tool_file)
                    date = cal.selection_get()

                    if tool_file in self.opened_tools:
                        InterfaceError(
                            f"'{tool_file}' has already been added to the capture area."
                        )
                    else:
                        canvas = Canvas(self.root.notebook)
                        frame = Frame(canvas)
                        scroll_y = Scrollbar(canvas,
                                             orient="vertical",
                                             command=canvas.yview)
                        scroll_x = Scrollbar(canvas,
                                             orient="horizontal",
                                             command=canvas.xview)

                        def gen_table(sheet=None):
                            rf = open_tool.wb["DATASET"]
                            _sheet = sheet
                            _inc = False

                            if _sheet is not None and _sheet['A2'].value == rf[
                                    'A2'].value:
                                data = _sheet
                                print("STANDARD IMPORT")
                            elif _sheet is not None and _sheet[
                                    'A2'].value != rf['A2'].value:
                                ws = open_tool.wb.get_sheet_by_name(
                                    f"CAP={date}")
                                open_tool.wb.remove_sheet(ws)
                                open_tool.save()
                                open_tool.wb.create_sheet(f"CAP={date}")
                                open_tool.save()
                                data = rf
                                _sheet = None
                                _inc = True
                                print("INCORRECt IMPORT")
                            else:
                                data = rf

                            row = []
                            for xc, x in enumerate(data.rows):
                                col = []
                                for yc, y in enumerate(x):
                                    rown = xc + 1
                                    letter = excel_alpha()
                                    if _sheet is None:
                                        open_tool.wb[f"CAP={date}"][
                                            f"{letter[yc]}{rown}"] = y.value
                                    if rown == 1 or letter[yc] == "A":
                                        val = {
                                            "type": "label",
                                            "value": y.value
                                        }
                                    else:
                                        val = {
                                            "type": "input",
                                            "value": y.value
                                        }
                                    col.append(val)
                                row.append(col)
                            open_tool.save()
                            # if _inc:
                            # messagebox.showerror("Bad CAPTURE WORKSHEET", f"Worksheet CAP={date} was broken, it has been replaced.")
                            Table(frame, row, open_tool.file_name,
                                  f"CAP={date}")

                        try:
                            sheet = open_tool.wb.get_sheet_by_name(
                                f"CAP={date}")
                            gen_table(sheet)
                        except KeyError:
                            open_tool.wb.create_sheet(f"CAP={date}")
                            open_tool.save()
                            gen_table()

                        canvas.create_window(0, 0, anchor='nw', window=frame)
                        canvas.update_idletasks()

                        canvas.configure(scrollregion=canvas.bbox('all'),
                                         yscrollcommand=scroll_y.set,
                                         xscrollcommand=scroll_x.set)

                        canvas.pack(fill='both', expand=True, side='left')
                        scroll_y.pack(fill='y', side='right')
                        scroll_x.pack(fill='x', side='bottom')

                        self.root.notebook.add(
                            canvas, text=f"{open_tool.name} [{date}]")
                        self.root.notebook.pack(expand=1, fill=BOTH)
                        window_wait.destroy()
                        window_date.destroy()

                button = Button(window_date,
                                text="Process...",
                                command=forward_with_date)
                button.pack()

            scrollbar.config(command=listbox.yview)

            listbox.pack(side=LEFT, fill=BOTH, expand=1)
            listbox.bind("<Double-1>", open_selected_tool)
            listbox.bind("<Return>", open_selected_tool)
            listbox.focus()

            scrollbar.pack(side=RIGHT, fill=Y)

            scroll_y_listbox.pack(fill=BOTH, expand=1)

            options = Frame(window, bg="grey", pady=7, padx=7)
            select = Button(options,
                            text="Open...",
                            pady=5,
                            padx=25,
                            command=open_selected_tool)
            select.pack(side=LEFT)

            close = Button(options,
                           text="Close",
                           pady=5,
                           padx=25,
                           command=lambda: window.destroy())
            close.pack(side=RIGHT)

            options.pack(fill=X)

            for file in listdir("."):
                if file.endswith(".xlsx"):
                    tool = Tool(file)
                    if tool.manifest:
                        listbox.insert(END, tool.file_name)
            wait.pack_forget()
Пример #2
0
class AddMovieGUI:
    def __init__(self, master, queue, end_app):
        """ initializes the GUI and binds the logic to the GUI elements

        Parameters
        ----------
        master : the root Tk instance
        queue : queue
            The queue that contain tasks for the GUI to update.
        end_app : function
            call them to end the main thread calliing periodicCall
        """
        self.queue = queue
        self.master = master
        self.end_app = end_app
        master.wm_title("IMDB & Amazon Scraper")
        master.protocol("WM_DELETE_WINDOW", self._quit)

        # Set up the GUI
        WINDOW_WIDTH = 60
        master.bind('<Control-q>', self._quit)

        # Search TextBox
        self.searchTextBox = Text(master, height=1, width=WINDOW_WIDTH // 2)
        self.searchTextBox.bind('<Return>', self.searchPressedEvent)
        self.searchTextBox.bind("<Control-Key-a>", self.select_all)
        self.searchTextBox.pack(pady=5, padx=WINDOW_WIDTH)
        self.searchTextBox.focus_set()
        self.searchTextBox.bind("<Tab>", self.focus_next_list_window)

        # Movie Results List
        self.movieListBox = Listbox(master, width=WINDOW_WIDTH)
        self.movieListBox.pack(pady=5, padx=WINDOW_WIDTH)
        self.movieListBox.bind("<Return>", self.submitPressedEvent)
        self.movieListBox.bind("<Double-Button-1>", self.submitPressedEvent)
        self.movieListBox.bind("<Tab>", self.focus_next_window)
        self.movieListBox.bind('<<ListboxSelect>>', self.movieListChanged)

        # Movie Poster Image
        self.imgPanel = Label(master)
        clearImg(self)
        self.imgPanel.pack()

        # Status TextBox
        self.statusTextBox = Entry(master, width=WINDOW_WIDTH)
        self.statusTextBox.pack(pady=5, padx=WINDOW_WIDTH)
        self.insertStatusText("")

        # Center the window in the middle of the screen
        center(master)

    def processIncoming(self):
        """ Handle all messages currently in the queue, if any.

        """
        while self.queue.qsize():
            try:
                msg = self.queue.get(0)
                # after querying movie and getting results. Update GUI
                if msg == "movie searched":
                    self.searchPressed2()
                else:
                    raise Exception('unknown message in queue!')
            except queue.Empty:
                # just on general principles, although we don't
                # expect this branch to be taken in this case
                pass

    def select_all(self, event):
        """ Select all text in SearchTextBox. Bound to Ctrl-a

        """
        event.widget.tag_add(SEL, "1.0", END)
        event.widget.mark_set(INSERT, "1.0")
        event.widget.see(INSERT)
        return 'break'

    def searchPressedEvent(self, event):
        """ Function called after user searched a movie name.
        Initiate search to bing and update movieDict and movie poster

        """
        # Disable searchTextBox so users can't input another query
        # while a query is already running
        self.searchTextBox.config(state=DISABLED)
        query = self.searchTextBox.get("1.0", END)
        self.insertStatusText("Searching movie: " + query)
        clearApp(self)
        # Start thread to search movie name.
        Thread(target=self.searchPressed, args=(query, )).start()
        return "break"

    def searchPressed(self, query):
        """ Queries movie name and stores returned imdb ID's in self.movieList

        Parameters
        ----------
        query : the query string

        """
        # Get list of imdb IDs for a input query string
        imdbIDs = imdbBingSearch(query)
        self.movieList = []
        # Extract movie info for each imdbID and store in movieList dict.
        for imdbID in imdbIDs:
            imdbDict = bsIMDB(imdbID)
            if imdbDict:
                self.movieList.append(imdbDict)
        # Put message into queue to update GUI with new movie list.
        msg = "movie searched"
        self.queue.put(msg)

    def searchPressed2(self):
        """ Update GUI after getting the movieList.
        Put movie string (title, year, type) into movieListBox
        Change focus to movieListBox
        Update movie image
        re-enable searchTextBox

        """
        # Insert movies found from bing into movieListBox
        for movie in self.movieList:
            self.movieListBox.insert(END,
                                     "{title} {year} {type}".format(**movie))
        self.insertStatusText("")
        # Update movieListBox focus and movie image
        self.movieListBox.select_set(0)
        self.movieListBox.focus()
        self.movieListChanged(None)
        self.searchTextBox.config(state=NORMAL)

    def submitPressedEvent(self, event):
        """ User chooses a movie from movieListBox.

        """
        if self.movieListBox.size() == 0:
            return

        # Get selected movie in movieListBox
        selection = self.movieListBox.curselection()[0]
        self.movieDict = self.movieList[selection]

        # Form another query. Not used here. But this is used for further
        # web scraping on other sites (not shown).
        self.movieDict['queryStr'] = "{title} {year}".format(**self.movieDict)

        # Check if selected movie is already in DB. if not, then insert it
        # Else, open ReplaceMoviePopUp and ask user to replace it or not
        if (alreadyExist(self.movieDict)):
            self.insertStatusText("Movie already in database")
            self.moviePopUp = ReplaceMoviePopUp(self, self.master)
        else:
            insert2DB(self.movieDict)
        return "break"

    def insertStatusText(self, text):
        """ Insert text in the bottom status bar to inform user.

        """
        self.statusTextBox.config(state=NORMAL)
        self.statusTextBox.delete(0, END)
        self.statusTextBox.insert(END, text)
        self.statusTextBox.config(state=DISABLED)

    def _quit(self, event=None):
        self.end_app()
        self.master.destroy()
        return "break"

    def focus_next_window(self, event):
        event.widget.tk_focusNext().focus()
        return "break"

    def focus_next_list_window(self, event):
        event.widget.tk_focusNext().focus()
        event.widget.tk_focusNext().select_set(0)
        return "break"

    def movieListChanged(self, event):
        """ Update image when movieList selection changes.
        In the original version, this instigates more events downstream.

        """
        if self.movieListBox.size() == 0:
            return

        # store the current selection in curMovie for subsequent processing
        self.curMovie = self.movieListBox.curselection()[0]
        movieDict = self.movieList[self.curMovie]

        # Take image URL and store image in imgData. Then set it in imgPanel
        gotImage = False
        if 'imageURL' in movieDict and movieDict['imageURL'] != "":
            url = "http://ia.media-imdb.com/images/M/{0}._V1_"\
                "SY150_CR3,0,101,150_AL_.jpg".format(movieDict['imageURL'])
            resp = getContent(url)
            if resp:
                imgData = BytesIO(resp.read())
                pil_image = Image.open(imgData)
                img2 = ImageTk.PhotoImage(image=pil_image)
                self.imgPanel.config(image=img2)
                self.imgPanel.image = img2
                gotImage = True

        # If we couldn't load movie image, set default image instead
        if not gotImage:
            clearImg(self)
        return "break"
Пример #3
0
        def get_users():
            scroll_y_listbox = Frame(window)
            scrollbar = Scrollbar(scroll_y_listbox, orient=VERTICAL)
            listbox = Listbox(scroll_y_listbox,
                              yscrollcommand=scrollbar.set,
                              borderwidth=0)

            scrollbar.config(command=listbox.yview)

            def process_login(e=None):
                username = listbox.get(ACTIVE)
                password = simpledialog.askstring(
                    title="User Password",
                    prompt=f"Please enter password for {listbox.get(ACTIVE)}",
                    show="*")
                login = self.user_management.login(username, password)
                if login:
                    messagebox.showinfo(
                        title="Success",
                        message=f"{login['username']} is now logged in")
                    window.destroy()
                else:
                    messagebox.showerror(
                        title="Failed",
                        message=f"Failed to login, wrong password.")

            def process_delete(e=None):
                confirm = messagebox.askyesnocancel(
                    title=f"Delete {listbox.get(ACTIVE)}",
                    message=
                    f"Are you sure you want to remove account {listbox.get(ACTIVE)}"
                )
                if confirm:
                    self.user_management.delete(listbox.get(ACTIVE))
                    window.destroy()

            listbox.pack(side=LEFT, fill=BOTH, expand=1)
            listbox.bind("<Double-1>", process_login)
            listbox.bind("<Return>", process_login)
            listbox.focus()

            scrollbar.pack(side=RIGHT, fill=Y)

            scroll_y_listbox.pack(fill=BOTH, expand=1)

            options = Frame(window, bg="grey", pady=7, padx=7)
            select = Button(options,
                            text="Login...",
                            pady=5,
                            padx=25,
                            command=process_login)
            select.grid(row=0, column=0, pady=2)

            delete = Button(options,
                            text="Delete",
                            pady=5,
                            padx=25,
                            bg="red",
                            fg="white",
                            command=process_delete)
            delete.grid(row=0, column=1, padx=5)

            close = Button(options,
                           text="Close",
                           pady=5,
                           padx=25,
                           command=lambda: window.destroy())
            close.grid(row=0, column=2, pady=2)

            options.pack(fill=X)

            for users in self.user_management.get_all_accounts():
                listbox.insert(END, users[0])
            wait.pack_forget()
Пример #4
0
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()