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)