Example #1
0
class ImageSession:
    def __init__(self, master, filename, hdu=0):
        # filename should be a string!
        self.filename = filename
        self.hdu = hdu
        
        # The master window?
        self.master = master
        
        if not os.path.exists(self.filename):
            tkMessageBox.showerror("File not found", "File {0} doesn't exist!".format(file))
            return
        
        self.openFile()
        self.createWindow()
    
    def openFile(self):
        # Try to open the file and read in the data from HDU
        try:
            hdulist = pf.open(self.filename)
            try:
                if hdulist[self.hdu].header['XTENSION'] == 'BINTABLE':
                    tkMessageBox.showerror("HDU Error", "The HDU {0} is not an ImageHDU!".format(self.hdu))
                    raise util.NotAnImageHDUError("The HDU {0} is not an ImageHDU!".format(self.hdu))
                # HDU is a table -- throw an error!
            except KeyError:
                # HDU is an image, carry on!
                pass
            
            self.rawData = hdulist[self.hdu].data
            self.rawData.shape
        except IOError:
            tkMessageBox.showerror("FITS file error ", "File {0} does not appear to be a valid FITS file!".format(self.filename))
            raise util.ImageSessionError("File {0} does not appear to be a valid FITS file!".format(self.filename))
        except AttributeError:
            tkMessageBox.showerror("FITS file error ", "File {0} does not appear to have data in HDU 0.".format(self.filename))
            raise util.ImageSessionError("File {0} does not appear to have data in HDU 0.".format(self.filename))
    
    def createWindow(self):
        # Create the actual window to draw the image and scale controls
        self.ImageWindow = Tk.Toplevel(self.master)
        self.ImageWindow.title(self.filename)
        #self.ImageWindow.protocol("WM_DELETE_WINDOW", self.closeImage)
        
        # TODO: THIS SHOULD DETECT SCREEN RESOLUTION
        screenSize = (1050/1.5, 1680/1.5)
        if self.rawData.shape[0] > screenSize[0] and self.rawData.shape[1] > screenSize[1]:
            factor1 = screenSize[0] / self.rawData.shape[0]
            factor2 = screenSize[1] / self.rawData.shape[1]
            self.zoomFactor = min([factor1, factor2])
        elif self.rawData.shape[1] > screenSize[0]:
            self.zoomFactor = screenSize[0]/ self.rawData.shape[1]
        elif self.rawData.shape[0] > screenSize[1]:
            self.zoomFactor = screenSize[1] / self.rawData.shape[0]
        else:
            self.zoomFactor = 1.
        
        # Create the image tools
        scaleTypeLabel = Tk.Label(self.ImageWindow, text="Scaling: ")
        scaleTypeLabel.grid(row=0, column=0)
        self.scalingName = Tk.StringVar()
        self.scalingName.set("arcsinh")
        self.scalingOption = Tk.OptionMenu(self.ImageWindow, self.scalingName, "arcsinh","linear","sqrt", command=self.setRescaler)
        self.scalingOption.grid(row=0, column=1)
        
        rescaleLabel = Tk.Label(self.ImageWindow, text="Rescale: ")
        rescaleLabel.grid(row=1, column=0)
        self.scaleValue = Tk.Scale(self.ImageWindow, from_=-4, to=6, resolution=0.05, orient=Tk.HORIZONTAL, showvalue=1, length=300)
        self.scaleValue.set(1.0)
        self.scaleValue.grid(row=1, column=1)
        
        minLabel = Tk.Label(self.ImageWindow, text="Min Pixel Value: ")
        minLabel.grid(row=2, column=0)
        self.minValue = Tk.Scale(self.ImageWindow, resolution=0.05, orient=Tk.HORIZONTAL, showvalue=1, length=300)
        self.minValue.set(1.0)
        self.minValue.grid(row=2, column=1)
        
        maxLabel = Tk.Label(self.ImageWindow, text="Max Pixel Value: ")
        maxLabel.grid(row=3, column=0)
        self.maxValue = Tk.Scale(self.ImageWindow, resolution=0.05, orient=Tk.HORIZONTAL, showvalue=1, length=300)
        self.maxValue.set(1.0)
        self.maxValue.grid(row=3, column=1)
        
        # Set the min/max slider boundaries
        self.minValue.config(from_=self.rawData.min(), to=self.rawData.max())
        self.minValue.set(self.rawData.min())
        self.maxValue.config(from_=self.rawData.min(), to=self.rawData.max())
        self.maxValue.set(self.rawData.max())
        
        # Set the default rescaler
        self.rescaler = util.arcsinhStretch
        
        self.scaleImage()
        self.drawImage()
        
        # Bind the sliders to the scaleImage() method
        self.scaleValue.bind("<ButtonRelease-1>", self.redraw)
        self.minValue.bind("<ButtonRelease-1>", self.redraw)
        self.maxValue.bind("<ButtonRelease-1>", self.redraw)
        self.scalingOption.bind("<ButtonRelease-1>", self.redraw)
        
    def setRescaler(self, event):
        self.rescaler = scalingTypes[self.scalingName.get()]
    
    def redraw(self, event=None):
        """ This rescales and redraws the image using the current values for scaling """
        self.scaleImage()
        self.drawImage()
    
    def updateThumbnail(self, event):
        """ """
        self.lastMousePosition = (event.x, event.y)
        self.pilThumbnail = self.pilImage.transform(self.pilImage.size, Image.EXTENT, (event.x-25,event.y-25,event.x+25,event.y+25))
        self.thumbnailImage = ImageTk.PhotoImage(self.pilThumbnail.resize((200,200)))
        self.thumbnailImageLabel.configure(image=self.thumbnailImage)
        
    def scaleImage(self):
        """ This method re-scales the image data """
        self.scaledData = 255.0*self.rescaler(self.rawData, beta=10.**self.scaleValue.get(), min=self.minValue.get(), max=self.maxValue.get(), clip=True)
    
    def plotContour(self, event):
        """ If the 'c' key is pressed, generate a contour plot of whatever is in the thumbnail zoom box """
        self.contourPlot = Tk.Toplevel(self.master)
        self.contourPlot.title("Contour plot for: {0}".format(self.filename))
        
        rawShape = self.rawData.shape
        
        lastx, lasty = self.lastMousePosition
        lastx = round(lastx/self.zoomFactor)
        lasty = round(lasty/self.zoomFactor)
        boxHalfSize = 25/self.zoomFactor
        x1,y1,x2,y2 = [x for x in map(round, (lastx-boxHalfSize,lasty-boxHalfSize,lastx+boxHalfSize,lasty+boxHalfSize))]
        
        if x1 < 0:
            x1 = 0
            x2 = boxHalfSize*2
        if x2 > rawShape[1]:
            x2 = rawShape[1]
            x1 = x2 - boxHalfSize*2
        if y1 < 0:
            y1 = 0
            y2 = boxHalfSize*2
        if y2 > rawShape[0]:
            y2 = rawShape[0]
            y1 = y2 - boxHalfSize*2
        
        thumbData = self.rawData[y1:y2, x1:x2]
        shp = thumbData.shape
        x,y = np.meshgrid(range(shp[0]), range(shp[1]))

        self.fig = Figure(figsize=(5,5))
        ax = self.fig.add_subplot(111)
        ax.contour(x, y, thumbData)
        ax.set_ylim(ax.get_ylim()[::-1])
        ax.get_xaxis().set_ticks([])
        ax.get_yaxis().set_ticks([])
        self.canvas = FigureCanvasTkAgg(self.fig, master=self.contourPlot)
        self.canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
        
    def drawImage(self):
        """ This method will draw the image into a PhotoImage object in the new image window """
        
        self.pilImage = Image.fromarray(self.scaledData.astype(np.uint8))
        newSize = (int(self.pilImage.size[0]*self.zoomFactor), int(self.pilImage.size[1]*self.zoomFactor))
        self.pilImage = self.pilImage.resize(newSize)
        self.tkImage = ImageTk.PhotoImage(self.pilImage)
        self.canvas = Tk.Canvas(self.ImageWindow, width=newSize[0], height=newSize[1], bd=0)
        self.canvas.create_image(0, 0, image=self.tkImage, anchor="nw")
        self.canvas.grid(row=0, column=2, rowspan=5, sticky="nswe")
        self.canvas.bind("<Motion>", self.updateThumbnail)
        self.canvas.bind("c", self.plotContour)
        self.canvas.focus_set()
        
        """ # Code for source detection:
        numSigma = 2.
        labels, num = snd.label(self.rawData > (numSigma*np.std(self.rawData)), np.ones((3,3)))
        coords = snd.center_of_mass(self.rawData, labels, range(1,num+1))
        rad = 5.
        for coord in coords:
            y,x = coord
            x = x*self.zoomFactor
            y = y*self.zoomFactor
            circ1 = self.canvas.create_oval(x-rad,y-rad,x+rad,y+rad, outline='red')
        """
        
        self.pilThumbnail = self.pilImage.transform(self.pilImage.size, Image.EXTENT, (0,0,50,50))
        self.pilThumbnail = self.pilThumbnail.resize((200,200))
        self.thumbnailImage = ImageTk.PhotoImage(self.pilThumbnail)
        self.thumbnailImageLabel = Tk.Label(self.ImageWindow, image=self.thumbnailImage, command=None)
        self.thumbnailImageLabel.grid(row=4, column=0, columnspan=2, rowspan=5)
Example #2
0
class supreme(Tk):
    def __init__(self, *args, **kwargs):
        Tk.__init__(self, *args, **kwargs)

        self.title("Floor Plan Annotation Tool")
        self.geometry("1200x1000")
        self.main_menu = Menu(self)
        self.config(menu=self.main_menu)
        self.fileMenu = Menu(self.main_menu, tearoff=False)
        self.main_menu.add_command(label="Open Image",
                                   command=self.open_Folder)
        self.folderName = None
        self.baseFolder = os.getcwd()
        self.folderContent = []

        self.csv_file_name = ''
        self.xml_file_name = ''

        self.f = Figure(figsize=(5, 5), dpi=100)
        self.ax = self.f.add_axes([0, 0, 1, 1])
        self.df = None
        self.s = StringVar()
        self.tl_list = []
        self.br_list = []
        self.t = StringVar()
        #plt.ion()
        self.canvas = Canvas(width=300, height=400)
        self.canvas.pack(pady=100)
        self.img = ImageTk.PhotoImage(Image.open("ann_tool.gif"))
        self.canvas.create_image(0, 0, image=self.img, anchor=NW)

        self.predicted_img = None

    def show_pred_image(self, filename):
        os.chdir(self.baseFolder)
        self.predicted_img, json_result = process_img.predict(
            self.folderName, filename)

        self.create_csvFile(json_result, filename)

        def animate(i):
            self.df = pd.read_csv(self.csv_file_name)
            lt = []
            bt = []
            wl = []
            hl = []
            obj = []
            for j in range(self.df.shape[0]):
                lt.append(self.df.loc[j]['xleft'])
                bt.append(self.df.loc[j]['yleft'])
                wl.append(self.df.loc[j]['width'])
                hl.append(self.df.loc[j]['height'])
                obj.append(self.df.loc[j]['label'])

            self.ax.clear()
            self.ax.imshow(self.predicted_img)
            #print('inside')
            self.ax.axis('off')
            for left, bottom, w, h, ob in zip(lt, bt, wl, hl, obj):
                p = patches.Rectangle((left, bottom),
                                      w,
                                      h,
                                      linewidth=1,
                                      edgecolor='r',
                                      facecolor='none')
                self.ax.add_patch(p)
                self.ax.text(left + w,
                             bottom,
                             ob,
                             horizontalalignment='right',
                             verticalalignment='bottom')

            self.ax.plot()

        if self.canvas != None:
            self.canvas.destroy()

        self.canvas = FigureCanvasTkAgg(self.f, self)
        self.canvas.draw()
        self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=True)

        toolbar = NavigationToolbar2TkAgg(self.canvas, self)
        toolbar.update()
        self.canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=True)

        destr_button = Button(self,
                              text="Discard",
                              fg='red',
                              command=self.discardFile)
        destr_button.pack(side=LEFT, padx=10, pady=5)

        conf_button = Button(self,
                             text="Confirm",
                             fg='green',
                             command=self.confirmFile)
        conf_button.pack(side=LEFT, padx=10, pady=5)

        def onclick(event):
            xcord = event.xdata
            ycord = event.ydata
            for j in range(self.df.shape[0]):
                if (between(xcord, self.df.loc[j]['xleft'],
                            self.df.loc[j]['xleft'] + self.df.loc[j]['width'])
                        and between(
                            ycord, self.df.loc[j]['yleft'],
                            self.df.loc[j]['yleft'] +
                            self.df.loc[j]['height'])):

                    top = Toplevel()
                    top.geometry('200x120+' +
                                 str(np.random.randint(low=100, high=200)) +
                                 '+' +
                                 str(np.random.randint(low=100, high=200)))
                    top.title('Edit Object')

                    button1 = Button(top,
                                     text="Delete",
                                     bg='red',
                                     command=partial(deleteObj, j))
                    button1.pack()

                    entry = Entry(top, textvariable=self.s)
                    self.s.set(root.df.loc[j]['label'])
                    entry.pack()
                    button2 = Button(top,
                                     text="Save",
                                     bg='green',
                                     command=partial(saveObj, j))
                    button2.pack()
                else:
                    if event.dblclick:
                        toggle_selector.RS = RectangleSelector(
                            self.ax,
                            line_select_callback,
                            drawtype='box',
                            useblit=True,
                            button=[1],
                            minspanx=5,
                            minspany=5,
                            spancoords='pixels',
                            interactive=True)

        def between(x, l, r):
            if x >= l and x <= r:
                return True
            else:
                return False

        def deleteObj(obj_id):
            print(obj_id)
            self.df.drop([obj_id], axis=0, inplace=True)
            df2 = self.df
            df2.to_csv(self.csv_file_name, index=False)

        def saveObj(obj_id):
            obj_name = self.s.get()
            self.df['label'][obj_id] = obj_name
            df2 = self.df
            df2.to_csv(self.csv_file_name, index=False)

        def saveNewObj(tl, br):
            df2 = pd.DataFrame([[
                tl[0], tl[1], br[0] - tl[0], br[1] - tl[1],
                self.t.get(), 0.5
            ]],
                               columns=[
                                   'xleft', 'yleft', 'width', 'height',
                                   'label', 'confidence'
                               ])
            result = self.df.append(df2, ignore_index=True)
            result.to_csv(self.csv_file_name, index=False)

            self.tl_list = []
            self.br_list = []
            self.t.set('')
            #print('saveNewObj')
            toggle_selector.RS.set_active(False)

        def toggle_selector(event):
            toggle_selector.RS.set_active(True)

        def line_select_callback(clk, rls):

            #print('line line_select_callback')
            self.tl_list.append(int(clk.xdata))
            self.tl_list.append(int(clk.ydata))
            self.br_list.append(int(rls.xdata))
            self.br_list.append(int(rls.ydata))

            top = Toplevel()
            top.geometry('200x120+' +
                         str(np.random.randint(low=100, high=200)) + '+' +
                         str(np.random.randint(low=100, high=200)))
            top.title('Add Object')
            lb = Label(top, text='Enter Object Name')
            lb.pack()
            entry = Entry(top, textvariable=self.t)
            entry.pack()
            button2 = Button(top,
                             text="Save",
                             bg='green',
                             command=partial(saveNewObj, self.tl_list,
                                             self.br_list))
            button2.pack()

        toggle_selector.RS = RectangleSelector(self.ax,
                                               line_select_callback,
                                               drawtype='box',
                                               useblit=True,
                                               button=[1],
                                               minspanx=5,
                                               minspany=5,
                                               spancoords='pixels',
                                               interactive=True)
        bbox = plt.connect('key_press_event', toggle_selector)

        cid = self.f.canvas.mpl_connect('button_press_event', onclick)

        ani = animation.FuncAnimation(self.f, animate, interval=1000)
        #print('after')
        plt.show()
        #plt.close()

    def create_csvFile(self, result, filename):
        self.csv_file_name = 'Z' + filename.split('.')[0] + '.csv'
        #currFolder = os.getcwd()
        os.chdir(self.folderName)

        with open(self.csv_file_name, mode='w') as csv_file:
            fieldnames = [
                'xleft', 'yleft', 'width', 'height', 'label', 'confidence'
            ]
            writer = csv.DictWriter(csv_file, fieldnames=fieldnames)

            writer.writeheader()
            for i, eachline in enumerate(result):
                writer.writerow({
                    'xleft':
                    eachline['topleft']['x'],
                    'yleft':
                    eachline['topleft']['y'],
                    'width':
                    eachline['bottomright']['x'] - eachline['topleft']['x'],
                    'height':
                    eachline['bottomright']['y'] - eachline['topleft']['y'],
                    'label':
                    eachline['label'],
                    'confidence':
                    eachline['confidence']
                })

    def create_xmlFile(self, savedir):
        def write_xml(objects, tl, br, savedir):
            height, width, depth = self.predicted_img.shape

            annotation = ET.Element('annotation')
            # ET.SubElement(annotation, 'folder').text = folder
            ET.SubElement(annotation, 'filename').text = self.folderContent[0]
            ET.SubElement(annotation, 'segmented').text = '0'
            size = ET.SubElement(annotation, 'size')
            ET.SubElement(size, 'width').text = str(width)
            ET.SubElement(size, 'height').text = str(height)
            ET.SubElement(size, 'depth').text = str(depth)
            for obj, topl, botr in zip(objects, tl, br):
                ob = ET.SubElement(annotation, 'object')
                ET.SubElement(ob, 'name').text = obj
                ET.SubElement(ob, 'pose').text = 'Unspecified'
                ET.SubElement(ob, 'truncated').text = '0'
                ET.SubElement(ob, 'difficult').text = '0'
                bbox = ET.SubElement(ob, 'bndbox')
                ET.SubElement(bbox, 'xmin').text = str(topl[0])
                ET.SubElement(bbox, 'ymin').text = str(topl[1])
                ET.SubElement(bbox, 'xmax').text = str(botr[0])
                ET.SubElement(bbox, 'ymax').text = str(botr[1])

            xml_str = ET.tostring(annotation)
            root = etree.fromstring(xml_str)
            xml_str = etree.tostring(root, pretty_print=True)
            save_path = os.path.join(
                savedir, self.folderContent[0].replace('jpg', 'xml'))
            with open(save_path, 'wb') as temp_xml:
                temp_xml.write(xml_str)

        objects = []
        tl = []
        br = []
        for j in range(self.df.shape[0]):
            tl.append((self.df.loc[j]['xleft'], self.df.loc[j]['yleft']))
            br.append((self.df.loc[j]['xleft'] + self.df.loc[j]['width'],
                       self.df.loc[j]['yleft'] + self.df.loc[j]['height']))
            objects.append(self.df.loc[j]['label'])

        write_xml(objects, tl, br, savedir)

    def open_Folder(self):

        options = {}
        options['initialdir'] = self.baseFolder
        options['title'] = 'choose a folder'
        options['mustexist'] = False
        self.folderName = filedialog.askdirectory(**options)

        if self.folderName is not None:
            print(self.folderName)
            self.folderContent = os.listdir(self.folderName)
            self.folderContent = [
                files for files in self.folderContent if 'jpg' in files
            ]  #check it
            self.folderContent.sort()
            #print(self.folderContent)

            if 'discard' in self.folderContent:
                self.folderContent.remove('discard')
            if 'confirm' in self.folderContent:
                self.folderContent.remove('confirm')

            #os.chdir(self.folderName)
            #print(self.folderContent)
        else:
            print('folder not found')
            exit()

        self.show_pred_image(self.folderContent[0])

    def discardFile(self):
        save_discard = 'discard'
        #currFolder = os.getcwd()
        #os.chdir(self.folderName)
        print(os.getcwd())
        if os.path.exists(os.path.join(self.folderName,
                                       save_discard)) == False:
            os.mkdir(save_discard)

        os.rename(self.folderContent[0],
                  os.path.join(save_discard, self.folderContent[0]))

        self.folderContent.pop(0)
        os.remove(self.csv_file_name)  #check it

        if len(self.folderContent) != 0:
            self.show_pred_image(self.folderContent[0])
        else:
            messagebox.showerror('Message', 'All the Images are Processed')

    def confirmFile(self):
        save_confirm = 'confirm'
        #currFolder = os.getcwd()
        #os.chdir(self.folderName)
        print(os.getcwd())
        if os.path.exists(os.path.join(self.folderName,
                                       save_confirm)) == False:
            os.mkdir(save_confirm)

        self.create_xmlFile(save_confirm)
        os.rename(self.folderContent[0],
                  os.path.join(save_confirm, self.folderContent[0]))
        #os.chdir(self.baseFolder)
        self.folderContent.pop(0)
        os.remove(self.csv_file_name)  #check it

        if len(self.folderContent) != 0:
            self.show_pred_image(self.folderContent[0])
        else:
            messagebox.showerror('Message', 'All the Images are Processed')