def paintImageOne(self, cut, xy=(0, 0), dxy_pix=(0, 0), window=0):
        """paints an image in the self.one_axis axis, plotting a box of size 2*window+1 around that point
        
        keywords
        cut) the 2d numpy matrix with the image data
        dxy_pix) the center of the box to be drawn given as an (x,y) tuple
        window)the size of the box, where the height is 2*window+1
        
        """
        (xc, yc) = xy
        (dx, dy) = dxy_pix

        pixsize = self.imgCollection.get_pixel_size()

        dx = dx * pixsize
        dy = dy * pixsize
        #the size of the cutout box in microns
        boxsize_um = (2 * window + 1) * pixsize

        #if there is no image yet, create one and a box
        if self.oneImage == None:
            self.oneImage = self.paintImageCenter(cut,
                                                  self.one_axis,
                                                  xc=xc,
                                                  yc=yc,
                                                  scale=pixsize)
            self.oneBox = CenterRectangle((xc + dx, yc + dy),
                                          width=50,
                                          height=50,
                                          edgecolor='r',
                                          linewidth=1.5,
                                          fill=False)
            self.one_axis.add_patch(self.oneBox)
            self.one_axis_center = Line2D([xc], [yc],
                                          marker='+',
                                          markersize=7,
                                          markeredgewidth=1.5,
                                          markeredgecolor='r')
            self.one_axis.add_line(self.one_axis_center)
            self.one_axis.set_title('Point 1')
            self.one_axis.set_ylabel('Microns')
            self.one_axis.autoscale(False)
            self.oneImage.set_clim(0, self.maxvalue)
        #if there is an image update it and the self.oneBox
        else:
            self.updateImageCenter(cut,
                                   self.oneImage,
                                   self.one_axis,
                                   xc=xc,
                                   yc=yc,
                                   scale=pixsize)
            self.oneBox.set_center((dx + xc, dy + yc))
            self.oneBox.set_height(boxsize_um)
            self.oneBox.set_width(boxsize_um)
            self.one_axis_center.set_xdata([xc])
            self.one_axis_center.set_ydata([yc])
Exemple #2
0
 def paintImageOne(self,cut,xy=(0,0),dxy_pix=(0,0),window=0):
     """paints an image in the self.one_axis axis, plotting a box of size 2*window+1 around that point
     
     keywords
     cut) the 2d numpy matrix with the image data
     dxy_pix) the center of the box to be drawn given as an (x,y) tuple
     window)the size of the box, where the height is 2*window+1
     
     """ 
     (xc,yc)=xy  
     (dx,dy)=dxy_pix
     dx=dx*self.orig_um_per_pix;
     dy=dy*self.orig_um_per_pix;
     #the size of the cutout box in microns
     boxsize_um=(2*window+1)*self.orig_um_per_pix;
     
     #if there is no image yet, create one and a box
     if self.oneImage==None:
         self.oneImage=self.paintImageCenter(cut, self.one_axis,xc=xc,yc=yc,scale=self.orig_um_per_pix)
         self.oneBox=CenterRectangle((xc+dx,yc+dy),width=50,height=50,edgecolor='r',linewidth=1.5,fill=False)
         self.one_axis.add_patch(self.oneBox)
         self.one_axis_center=Line2D([xc],[yc],marker='+',markersize=7,markeredgewidth=1.5,markeredgecolor='r')
         self.one_axis.add_line(self.one_axis_center) 
         self.one_axis.set_title('Point 1')
         self.one_axis.set_ylabel('Microns')
         self.one_axis.autoscale(False)
         self.oneImage.set_clim(0,self.maxvalue)     
     #if there is an image update it and the self.oneBox
     else:
         self.updateImageCenter(cut, self.oneImage, self.one_axis,xc=xc,yc=yc,scale=self.orig_um_per_pix)
         self.oneBox.set_center((dx+xc,dy+yc))
         self.oneBox.set_height(boxsize_um)
         self.oneBox.set_width(boxsize_um)
         self.one_axis_center.set_xdata([xc])
         self.one_axis_center.set_ydata([yc])
Exemple #3
0
class MosaicImage():
    """A class for storing the a large mosaic imagein a matplotlib axis. Also contains functions for finding corresponding points
    in the larger mosaic image, and plotting informative graphs about that process in different axis"""
    def __init__(self,axis,one_axis,two_axis,corr_axis,imagefile,imagematrix,extent=None,flipVert=False,fullRes=False):
        """initialization function which will plot the imagematrix passed in and set the bounds according the bounds specified by extent
        
        keywords)
        axis)the matplotlib axis to plot the image into
        one_axis) the matplotlib axis to plot the cutout of the fixed point when using the corresponding point functionality
        two_axis) the matplotlib axis to plot the cutout of the point that should be moved when using the corresponding point functionality
        corr_axis) the matplotlib axis to plot out the matrix of correlation values found when using the corresponding point functionality
        imagefile) a string with the path of the file which contains the full resolution image that should be used when calculating the corresponding point funcationality
         currently the reading of the image is using PIL so the path specified must be an image which is PIL readable
        imagematrix) a numpy 2d matrix containing a low resolution varsion of the full resolution image, for the purposes of faster plotting/memory management
        extent) a list [minx,maxx,miny,maxy] of the corners of the image.  This will specify the scale of the image, and allow the corresponding point functionality
        to specify how much the movable point should be shifted in the units given by this extent.  If omitted the units will be in pixels and extent will default to
        [0,width,height,0].
       
        """
        #define the attributes of this class
        self.axis=axis
        self.one_axis=one_axis
        self.two_axis=two_axis
        self.corr_axis=corr_axis
        self.imagefile=imagefile
        self.flipVert=flipVert
        self.imagematrix=imagematrix
        
        #read in the full resolution height/width using PIL
        image = Image.open(imagefile)
        (self.originalwidth,self.originalheight)=image.size
        
        #if extent was not specified default to units of pixels with 0,0 in the upper left
        if extent==None:
            if flipVert:
                self.extent=[0,self.originalwidth,0,self.originalheight]
            else:
                self.extent=[0,self.originalwidth,self.originalheight,0]
        else:
            self.extent=extent
 
        #calculate the width of the image (calling it _um assuming its in units of microns)
        #from now on I will assume the units are in microns, though if they were in some other unit it would just carry through
        width_um=abs(self.extent[1]-self.extent[0])
        #height_um=self.extent[2]-self.extent[3]
        
        #calculate the pixels/micron of full resolution picture
        self.orig_um_per_pix=width_um/self.originalwidth    
        #calculate the pixels/micron of the downsampled matrix 
        (matrix_height,matrix_width)=imagematrix.shape
        self.matrix_scale=matrix_width/width_um
        
        #plot the image using paintImage
        self.paintImage()
        
        #initialize the images for the various subplots as None
        self.oneImage=None
        self.twoImage=None
        self.corrImage=None
        self.set_maxval(self.imagematrix.max(axis=None))
        self.fullRes=fullRes
        self.axis.set_title('Mosaic Image')
        
     
    def set_extent(self,extent):
        self.extent=extent
        width_um=abs(self.extent[1]-self.extent[0])
        #height_um=self.extent[2]-self.extent[3]
        
        #calculate the pixels/micron of full resolution picture
        self.orig_um_per_pix=width_um/self.originalwidth    
        #calculate the pixels/micron of the downsampled matrix 
        (matrix_height,matrix_width)=self.imagematrix.shape
        self.matrix_scale=matrix_width/width_um  
        self.Image.set_extent(self.extent)
        self.axis.set_xlim(self.extent[0],self.extent[1])
        self.axis.set_ylim(self.extent[2],self.extent[3])
        self.axis.set_xlabel('X Position (microns)')
        self.axis.set_ylabel('Y Position (microns)')
                  
    def paintImage(self):
        """plots self.imagematrix in self.axis using self.extent to define the boundaries"""
        self.Image=self.axis.imshow(self.imagematrix,cmap='gray',extent=self.extent)
        (minval,maxval)=self.Image.get_clim()
        self.maxvalue=maxval
        #self.axis.canvas.get_toolbar().slider.SetSelection(minval,self.maxvalue)
        self.axis.autoscale(False)
        self.axis.set_xlabel('X Position (pixels)')
        self.axis.set_ylabel('Y Position (pixels)')
        self.Image.set_clim(0,25000)
    
    def set_maxval(self,maxvalue):
        """set the maximum value in the image colormap"""
        self.maxvalue=maxvalue;
        self.repaint()
        
    def repaint(self):
        """sets the new clim for the Image using self.maxvalue as the new maximum value"""
        (minval,maxval)=self.Image.get_clim()
        self.Image.set_clim(minval,self.maxvalue)
        if self.oneImage!=None:
            self.oneImage.set_clim(minval,self.maxvalue)
        if self.twoImage!=None:
            self.twoImage.set_clim(minval,self.maxvalue)
    
    def paintImageCenter(self,cut,theaxis,xc=0,yc=0,skip=1,cmap='gray',scale=1):
        """paints an image and redefines the coordinates such that 0,0 is at the center
        
        keywords
        cut)the 2d numpy matrix with the image data
        the axis)the matplotlib axis to plot it in
        skip)the factor to rescale the axis by so that 1 entry in the cut, is equal to skip units on the axis (default=1)
        cmap)the colormap designation to use for the plot (default 'gray')
        
        """
        theaxis.cla()
        (h,w)=cut.shape
        dh=skip*1.0*(h-1)/2       
        dw=skip*1.0*(w-1)/2
        dh=dh*scale;
        dw=dw*scale;
        
        if self.extent[0]<self.extent[1]:
            left=xc-dw
            right=xc+dw
        else:
            left=xc+dw
            right=xc-dw
        if self.extent[2]<self.extent[3]:
            top=yc-dh
            bot=yc+dh
        else:
            top=yc+dh
            bot=yc-dh
            
        ext=[left,right,top,bot]

        image=theaxis.imshow(cut,cmap=cmap,extent=ext)
        
        if self.extent[0]<self.extent[1]:
            theaxis.set_xlim(xc-dw,xc+dw)
        else:
            theaxis.set_xlim(xc+dw,xc-dw)
        if self.extent[2]<self.extent[3]:
            theaxis.set_ylim(yc-dh,yc+dh)
        else:
            theaxis.set_ylim(yc+dh,yc-dh)
            
        theaxis.hold(True)

        return image 
    
    def updateImageCenter(self,cut,theimage,theaxis,xc=0,yc=0,skip=1,scale=1):
        """updates an image with a new image
        
        keywords
        cut) the 2d numpy matrix with the image data 
        theimage) the image to update
        theaxis) the axis that the image is in
        skip)the factor to rescale the axis by so that 1 entry in the cut, is equal to skip units on the axis (default=1)

        """
        (h,w)=cut.shape
        dh=skip*1.0*(h-1)/2       
        dw=skip*1.0*(w-1)/2
        dh=dh*scale;
        dw=dw*scale;
        theimage.set_array(cut)
        if self.extent[0]<self.extent[1]:
            left=xc-dw
            right=xc+dw
            theaxis.set_xlim(xc-dw,xc+dw)
        else:
            left=xc+dw
            right=xc-dw
            theaxis.set_xlim(xc+dw,xc-dw)
        if self.extent[2]<self.extent[3]:
            top=yc-dh
            bot=yc+dh
            theaxis.set_ylim(yc-dh,yc+dh)
        else:
            top=yc+dh
            bot=yc-dh
            theaxis.set_ylim(yc+dh,yc-dh)
        ext=[left,right,top,bot]
        theimage.set_extent(ext)
         
         
    def paintImageOne(self,cut,xy=(0,0),dxy_pix=(0,0),window=0):
        """paints an image in the self.one_axis axis, plotting a box of size 2*window+1 around that point
        
        keywords
        cut) the 2d numpy matrix with the image data
        dxy_pix) the center of the box to be drawn given as an (x,y) tuple
        window)the size of the box, where the height is 2*window+1
        
        """ 
        (xc,yc)=xy  
        (dx,dy)=dxy_pix
        dx=dx*self.orig_um_per_pix;
        dy=dy*self.orig_um_per_pix;
        #the size of the cutout box in microns
        boxsize_um=(2*window+1)*self.orig_um_per_pix;
        
        #if there is no image yet, create one and a box
        if self.oneImage==None:
            self.oneImage=self.paintImageCenter(cut, self.one_axis,xc=xc,yc=yc,scale=self.orig_um_per_pix)
            self.oneBox=CenterRectangle((xc+dx,yc+dy),width=50,height=50,edgecolor='r',linewidth=1.5,fill=False)
            self.one_axis.add_patch(self.oneBox)
            self.one_axis_center=Line2D([xc],[yc],marker='+',markersize=7,markeredgewidth=1.5,markeredgecolor='r')
            self.one_axis.add_line(self.one_axis_center) 
            self.one_axis.set_title('Point 1')
            self.one_axis.set_ylabel('Microns')
            self.one_axis.autoscale(False)
            self.oneImage.set_clim(0,self.maxvalue)     
        #if there is an image update it and the self.oneBox
        else:
            self.updateImageCenter(cut, self.oneImage, self.one_axis,xc=xc,yc=yc,scale=self.orig_um_per_pix)
            self.oneBox.set_center((dx+xc,dy+yc))
            self.oneBox.set_height(boxsize_um)
            self.oneBox.set_width(boxsize_um)
            self.one_axis_center.set_xdata([xc])
            self.one_axis_center.set_ydata([yc])
    
        
    def paintImageTwo(self,cut,xy=(0,0)):
        """paints an image in the self.two_axis, with 0,0 at the center cut=the 2d numpy"""
        #create or update appropriately
        (xc,yc)=xy
        if self.twoImage==None:
            self.twoImage=self.paintImageCenter(cut, self.two_axis,xc=xc,yc=yc,scale=self.orig_um_per_pix)
            self.two_axis_center=Line2D([xc],[yc],marker='+',markersize=7,markeredgewidth=1.5,markeredgecolor='r')
            self.two_axis.add_line(self.two_axis_center) 
            self.two_axis.set_title('Point 2')
            self.two_axis.set_ylabel('Pixels from point 2')
            self.two_axis.autoscale(False)
            self.twoImage.set_clim(0,self.maxvalue)
 
        else:
            self.updateImageCenter(cut, self.twoImage, self.two_axis,xc=xc,yc=yc,scale=self.orig_um_per_pix)
            self.two_axis_center.set_xdata([xc])
            self.two_axis_center.set_ydata([yc])
    
    def paintCorrImage(self,corrmat,dxy_pix,skip):
        """paints an image in the self.corr_axis, with 0,0 at the center and rescaled by skip, plotting a point at dxy_pix
        
        keywords)
        corrmat) the 2d numpy matrix with the image data
        dxy_pix) the offset in pixels from the center of the image to plot the point
        skip) the factor to rescale the axis by, so that when corrmat was produced by mycorrelate2d with a certain skip value, 
        the axis will be in units of pixels
        
        """
        #unpack the values
        (dx,dy)=dxy_pix
        #update or create new
        if self.corrImage==None:
            self.corrImage=self.paintImageCenter(corrmat, self.corr_axis,skip=skip,cmap='jet')             
            self.maxcorrPoint,=self.corr_axis.plot(dx,dy,'ro')

            self.colorbar=self.corr_axis.figure.colorbar(self.corrImage,shrink=.9)
            self.corr_axis.set_title('Cross Correlation')
            self.corr_axis.set_ylabel('Pixels shifted')
          
        else:
            self.updateImageCenter(corrmat, self.corrImage, self.corr_axis,skip=skip)
            self.maxcorrPoint.set_data(dx,dy)   
        #hard code the correlation maximum at .5
        self.corrImage.set_clim(0,.5)

    def convert_pos_to_orig_ind(self,x,y):
        """converts a position in original units (usually microns) to indices in the original full resolution image
        
        keywords)
        x)x position in microns
        y)y position in microns
        
        returns) (x_pix,y_pix) the indices in pixels of that location
        
        """
        print "(x,y) before"
        print (x,y)
        #calculate distance from left hand edge
        
        x=abs(x-self.extent[0])
        print self.flipVert
        print self.fullRes
        if self.flipVert and not self.fullRes:
            y=abs(self.extent[2]-y)
            print "alternative distance"
        else:
            print "regular distance"
            y=abs(self.extent[3]-y)
        #calculate distance from top of image
        print "x,y relative"
        print (x,y)
        x_pix=int(round(x/self.orig_um_per_pix))
        y_pix=int(round(y/self.orig_um_per_pix))
        print (x_pix,y_pix)
        return (x_pix,y_pix)
    
    def cutout_window(self,x,y,window):
        """returns a cutout of the original image at a certain location and size
        
        keywords)
        x)x position in microns
        y)y position in microns
        window) size of the patch to cutout, will cutout +/- window in both vertical and horizontal dimensions
        note.. behavior not well specified at edges, may crash
        
        function uses PIL to read in image and crop it appropriately
        returns) cut: a 2d numpy matrix containing the removed patch
        
        """
        print (x,y)
        (xpx,ypx)=self.convert_pos_to_orig_ind(x,y)
        print "centered at %d %d"%(x,y)
        if self.fullRes==True:
            cut=self.imagematrix[ypx-window:ypx+window,xpx-window:xpx+window]
            return cut
        else:
            image=Image.open(self.imagefile)
            if image.mode != 'P':
                image=image.convert('P')
            #print "image mode is %s"%image.mode
            image=image.crop([xpx-window,ypx-window,xpx+window,ypx+window])
            #enh = ImageEnhance.Contrast(image)
            #image=enh.enhance(1.5)   
            (width,height)=image.size
            #print "about to get data"
            thedata=image.getdata()
            #print "this is the data"
            #print thedata
            cut=np.reshape(np.array(thedata,np.dtype('uint8')),(height,width))
            if self.flipVert:
                cut=np.flipud(cut)
            return cut                      
    
    def cross_correlate_two_to_one(self,xy1,xy2,window=100,delta=75,skip=3):
        """take two points in the image, and calculate the 2d cross correlation function of the image around those two points
        
        keywords)
        xy1) a (x,y) tuple specifying point 1, the point that should be fixed
        xy2) a (x,y) tuple specifiying point 2, the point that should be moved
        window) the size of the patch to cutout (+/- window around the points) for calculating the correlation (default = 100 pixels)
        delta) the size of the maximal shift +/- delta from no shift to calculate
        skip) the number of integer pixels to skip over when calculating the correlation
        
        returns (one_cut,two_cut,corrmat)
        one_cut) the patch cutout around point 1
        two_cut) the patch cutout around point 2
        corrmat) the matrix of correlation values measured with 0,0 being a shift of -delta,-delta
        
        """
        (x1,y1)=xy1
        (x2,y2)=xy2
        one_cut=self.cutout_window(x1,y1,window+delta)
        two_cut=self.cutout_window(x2,y2,window)
        #return (target_cut,source_cut,mycorrelate2d(target_cut,source_cut,mode='valid'))
        return (one_cut,two_cut,mycorrelate2d(one_cut,two_cut,skip))
       
    def align_by_correlation(self,xy1,xy2,window=100,delta=75,skip=3):
        """take two points in the image, and calculate the 2d cross correlation function of the image around those two points
        plots the results in the appropriate axis, and returns the shift which aligns the two points given in microns
        
        keywords)
        xy1) a (x,y) tuple specifying point 1, the point that should be fixed
        xy2) a (x,y) tuple specifiying point 2, the point that should be moved
        window) the size of the patch to cutout (+/- window around the points) for calculating the correlation (default = 100 pixels)
        delta) the size of the maximal shift +/- delta from no shift to calculate
        skip) the number of integer pixels to skip over when calculating the correlation
        
        returns) (maxC,dxy_um)
        maxC)the maximal correlation measured
        dxy_um) the (x,y) tuple which contains the shift in microns necessary to align point xy2 with point xy1
        
        """
        #calculate the cutout patches and the correlation matrix
        (one_cut,two_cut,corrmat)=self.cross_correlate_two_to_one(xy1,xy2,window,delta,skip)
        #find the peak of the matrix
        maxind=corrmat.argmax()
        #determine the indices of that peak
        (max_i,max_j)=np.unravel_index(maxind,corrmat.shape)
        #calculate the shift for that index in pixels
        dy_pix=max_i*skip-delta
        dx_pix=max_j*skip-delta
        #convert those indices into microns
        dy_um=dy_pix*self.orig_um_per_pix
        dx_um=dx_pix*self.orig_um_per_pix
        #pack up the shifts into tuples
        dxy_pix=(dx_pix,dy_pix)
        dxy_um=(dx_um,dy_um)
        #calculate what the maximal correlation was
        corrval=corrmat.max()
        
        print "(correlation,(dx,dy))="
        print (corrval,dxy_pix)
        #paint the patch around the first point in its axis, with a box of size of the two_cut centered around where we found it
        self.paintImageOne(one_cut,xy=xy1,dxy_pix=dxy_pix, window=window)
        #paint the patch around the second point in its axis
        self.paintImageTwo(two_cut,xy=xy2)
        #paint the correlation matrix in its axis
        self.paintCorrImage(corrmat, dxy_pix,skip)
        return (corrmat.max(),dxy_um)
    
    def paintPointsOneTwo(self,xy1,xy2,window):
        (x1,y1)=xy1
        (x2,y2)=xy2
        one_cut=self.cutout_window(x1,y1,window)
        two_cut=self.cutout_window(x2,y2,window)
        self.paintImageOne(one_cut,xy1)
        #paint the patch around the second point in its axis
        self.paintImageTwo(two_cut,xy2)
        
    def make_preview_stack(self,xpos,ypos,width,height,directory):
        print "make a preview stack"
   
        hw_pix=int(round(width*.5/self.orig_um_per_pix))
        hh_pix=int(round(height*.5/self.orig_um_per_pix))
        queue = Queue.Queue()
          
        #spawn a pool of threads, and pass them queue instance 
        for i in range(4):
            t = ImageCutThread(queue)
            t.setDaemon(True)
            t.start()
              
        for i in range(len(self.mosaicArray.xpos)):
            (cx_pix,cy_pix)=self.convert_pos_to_ind(xpos[i],ypos[i])
            rect=[cx_pix-hw_pix,cy_pix-hh_pix,cx_pix+hw_pix,cy_pix+hh_pix]
            queue.put((self.imagefile,rect,i))
        queue.join()
class MosaicImage():
    """A class for storing the a large mosaic image in a matplotlib axis. Also contains functions for finding corresponding points
    in the larger mosaic image, and plotting informative graphs about that process in different axis"""
    def __init__(self,axis,one_axis,two_axis,corr_axis,imagefile,imagematrix,proj_folder=None,extent=None,px_Um=False,flipVert=False):
        """initialization function which will plot the imagematrix passed in and set the bounds according the bounds specified by extent
        
        keywords)
        axis)the matplotlib axis to plot the image into
        one_axis) the matplotlib axis to plot the cutout of the fixed point when using the corresponding point functionality
        two_axis) the matplotlib axis to plot the cutout of the point that should be moved when using the corresponding point functionality
        corr_axis) the matplotlib axis to plot out the matrix of correlation values found when using the corresponding point functionality
        imagefile) a string with the path of the file which contains the full resolution image that should be used when calculating the corresponding point funcationality
         currently the reading of the image is using PIL so the path specified must be an image which is PIL readable
        imagematrix) a numpy 2d matrix containing a low resolution version of the full resolution image, for the purposes of faster plotting/memory management
        extent) a list [minx,maxx,miny,maxy] of the corners of the image.  This will specify the scale of the image, and allow the corresponding point functionality
        to specify how much the movable point should be shifted in the units given by this extent.  If omitted the units will be in pixels and extent will default to
        [0,width,height,0].
       
        """
        #define the attributes of this class
        self.axis=axis
        self.one_axis=one_axis
        self.two_axis=two_axis
        self.corr_axis=corr_axis
        self.imagefile=imagefile
        self.imagefiles = [imagefile] #need to somehow load more images into this, assume they get added
        self.imageExtents = {imagefile:MetadataHandler.LoadMetadata(imagefile)[0]}
        self.flipVert=flipVert
        self.imagematrix=imagematrix
        self.PxSizeUm = px_Um
        
        if proj_folder:
            self.proj_folder=proj_folder
        
        #read in the full resolution height/width using PIL
        image = Image.open(imagefile) 
        
        (self.originalwidth,self.originalheight)=image.size
        
        #if extent was not specified default to units of pixels with 0,0 in the upper left
        if extent==None:
            if flipVert:
                self.extent=[0,self.originalwidth,0,self.originalheight]
            else:
                self.extent=[0,self.originalwidth,self.originalheight,0]
        else:
            self.extent=extent
 
        #calculate the width of the image (calling it _um assuming its in units of microns)
        #from now on I will assume the units are in microns, though if they were in some other unit it would just carry through
        width_um=self.extent[1]-self.extent[0]
        width_um *= self.PxSizeUm 
        
        
        #calculate the pixels/micron of full resolution picture
        self.orig_um_per_pix=width_um/self.originalwidth
        
        #calculate the pixels/micron of the downsampled matrix 
        (matrix_height,matrix_width)=imagematrix.shape
        self.matrix_scale=matrix_width/width_um

        print "px/Um downsample",self.matrix_scale        
        
        
        #plot the image using paintImage
        self.paintImage()
        
        #initialize the images for the various subplots as None
        self.oneImage=None
        self.twoImage=None
        self.corrImage=None
        self.set_maxval(self.imagematrix.max(axis=None))
        
        self.axis.set_title('Mosaic Image')

    def pad(self,mosaic,tile,mosaic_extent,tile_extent,scaling):
        #takes mosaic and pads based on current extent(in Um) and new extent,
        #returns new mosaic and new extent

        #checking to see whether tile or mosaic has the maximum/min for extent
        
        if tile_extent[0] <= mosaic_extent[0]:
            minx = tile_extent[0]
        else:
            minx = mosaic_extent[0]
            
        if tile_extent[1] >= mosaic_extent[1]:
            maxx = tile_extent[1]
        else:
            maxx = mosaic_extent[1]
            
        if tile_extent[2] <= mosaic_extent[2]:
            miny = tile_extent[2]
        else:
            miny = mosaic_extent[2]
            
        if tile_extent[3] >= mosaic_extent[3]:
            maxy = tile_extent[3]
        else:
            maxy = mosaic_extent[3]
        
        extent = (minx,maxx,miny,maxy)

        #can I just convert back to pixels here? also are pxUm conversions
        #intoducing error?
        px_Um = self.PxSizeUm
        
        px1,px2 = [i/px_Um for i in mosaic_extent],[i/px_Um for i in tile_extent]

        #Padding
        size_int = (abs(int((maxx/px_Um-minx/px_Um)*scaling)),abs(int((maxy/px_Um-miny/px_Um)*scaling)))
        
        im = Image.new('L',size_int)
        im.paste(mosaic,(int(abs((px1[1]-maxx/px_Um)*scaling)),int(abs((px1[3]-maxy/px_Um)*scaling))))
        im.paste(tile,(int(abs((px2[1]-maxx/px_Um)*scaling)),int(abs((px2[3]-maxy/px_Um)*scaling))))

        im.save(os.path.join(self.proj_folder,'mosaic.tif'))

        return im,extent
        
    def extendMosaicTiff(self, mosaic, image_file_name, low_res_image_array, old_extent,scaling):
        #add new image to image files
        self.imagefiles.append(image_file_name)
                
        #grab image metadata
        tile_extent = MetadataHandler.LoadMetadata(image_file_name)[0]        

        #add image extent to imageExtents dict
        self.imageExtents[image_file_name] = tile_extent
        
        #pad low res
        self.imagematrix,self.extent=self.pad(mosaic,low_res_image_array,old_extent,tile_extent,scaling)
        
        self.imagematrix = np.reshape(self.imagematrix.getdata(),(self.imagematrix.size[1],self.imagematrix.size[0]))

        return self.extent
        

    def findTileExtent(self,tile):
        if len(self.imagefiles) == 1:
            return self.extent
        else:
            return self.imageExtents[tile]        
            
    
    def findHighResImageFile(self,x,y):
        #assume x and y are stage coordinates (in microns)
        for img in self.imagefiles: #eventually turn this into a dict instead of parsing each time
            #grab metadata
            (tile_extent,zpos) = MetadataHandler.LoadMetadata(img)
            
            if x >= tile_extent[0] and x <= tile_extent[1]:
                if y >= tile_extent[2] and y <= tile_extent[3]:   
                    return img,zpos,tile_extent
        print "no high res tile found"        
        return False #did not find HightResImageFile containing x,y coords

     
    def set_extent(self,extent):
        self.extent=extent
        width_um=self.extent[1]-self.extent[0]
        #height_um=self.extent[2]-self.extent[3]
        
        #calculate the pixels/micron of full resolution picture
##        self.orig_um_per_pix=width_um/self.originalwidth
        
        #calculate the pixels/micron of the downsampled matrix 
        (matrix_height,matrix_width)=self.imagematrix.shape
        self.matrix_scale=matrix_width/width_um
        self.Image.set_extent((self.extent[1],self.extent[0],self.extent[2],self.extent[3]))
        self.axis.set_xlim(self.extent[1],self.extent[0]) #changed from 0,1 to 1,0 on 7_22_13 to try to flip x-axis without reversing images
        if self.flipVert:
            self.axis.set_ylim(self.extent[3],self.extent[2])
        else:
            self.axis.set_ylim(self.extent[2],self.extent[3])
        self.axis.set_xlabel('X Position (microns)')
        self.axis.set_ylabel('Y Position (microns)')
        

    def paintImage(self):
        """plots self.imagematrix in self.axis using self.extent to define the boundaries"""
        self.Image=self.axis.imshow(self.imagematrix,cmap='gray',extent=self.extent)
        (minval,maxval)=self.Image.get_clim()
        self.maxvalue=maxval
        #self.axis.canvas.get_toolbar().slider.SetSelection(minval,self.maxvalue)
        self.axis.autoscale(False)
        self.axis.set_xlabel('X Position (pixels)')
        self.axis.set_ylabel('Y Position (pixels)')
        self.Image.set_clim(0,25000)
    
    def set_maxval(self,maxvalue):
        """set the maximum value in the image colormap"""
        self.maxvalue=maxvalue;
        self.repaint()
        
    def repaint(self):
        """sets the new clim for the Image using self.maxvalue as the new maximum value"""
        (minval,maxval)=self.Image.get_clim()
        self.Image.set_clim(minval,self.maxvalue)
        if self.oneImage!=None:
            self.oneImage.set_clim(minval,self.maxvalue)
        if self.twoImage!=None:
            self.twoImage.set_clim(minval,self.maxvalue)
    
    def paintImageCenter(self,cut,theaxis,xc=0,yc=0,skip=1,cmap='gray',scale=1):
        """paints an image and redefines the coordinates such that 0,0 is at the center
        
        keywords
        cut)the 2d numpy matrix with the image data
        the axis)the matplotlib axis to plot it in
        skip)the factor to rescale the axis by so that 1 entry in the cut, is equal to skip units on the axis (default=1)
        cmap)the colormap designation to use for the plot (default 'gray')
        
        """
        theaxis.cla()
        (h,w)=cut.shape
        dh=skip*1.0*(h-1)/2       
        dw=skip*1.0*(w-1)/2
        dh=dh*scale;
        dw=dw*scale;
        if self.flipVert:
            ext=[xc-dw,xc+dw,yc+dh,yc-dh] #would have to flip it here too
        else:
            ext=[xc+dw,xc-dw,yc-dh,yc+dh] #flipped x here
        image=theaxis.imshow(cut,cmap=cmap,extent=ext)
        theaxis.set_xlim(xc+dw,xc-dw)  #flipped x here too
        if self.flipVert:
            theaxis.set_ylim(yc-dh,yc+dh)
        else:
            theaxis.set_ylim(yc-dh,yc+dh) #changed this to have cutouts work but it changed the cross correllation, made it negative somewhere 
        theaxis.hold(True)
        return image 
    
    def updateImageCenter(self,cut,theimage,theaxis,xc=0,yc=0,skip=1,scale=1):
        """updates an image with a new image
        
        keywords
        cut) the 2d numpy matrix with the image data 
        theimage) the image to update
        theaxis) the axis that the image is in
        skip)the factor to rescale the axis by so that 1 entry in the cut, is equal to skip units on the axis (default=1)

        """
        (h,w)=cut.shape
        dh=skip*1.0*(h-1)/2       
        dw=skip*1.0*(w-1)/2
        dh=dh*scale;
        dw=dw*scale;
        theimage.set_array(cut)
        if self.flipVert:
            ext=[xc-dw,xc+dw,yc-dh,yc+dh]
        else:
            ext=[xc+dw,xc-dw,yc-dh,yc+dh]
        theimage.set_extent(ext)
        theaxis.set_xlim(xc+dw,xc-dw) #changed this for x switching cutouts?
        if self.flipVert:
            theaxis.set_ylim(yc-dh,yc+dh)
        else:
            theaxis.set_ylim(yc-dh,yc+dh) #changed this to have cutouts work 
         
    def paintImageOne(self,cut,xy=(0,0),dxy_pix=(0,0),window=0):
        """paints an image in the self.one_axis axis, plotting a box of size 2*window+1 around that point
        
        keywords
        cut) the 2d numpy matrix with the image data
        dxy_pix) the center of the box to be drawn given as an (x,y) tuple
        window)the size of the box, where the height is 2*window+1
        
        """
        (xc,yc)=xy  
        (dx,dy)=dxy_pix
        dx=dx*self.orig_um_per_pix;
        dy=dy*self.orig_um_per_pix;
        #the size of the cutout box in microns
        boxsize_um=(2*window+1)*self.orig_um_per_pix;
        
        #if there is no image yet, create one and a box
        if self.oneImage==None:
            self.oneImage=self.paintImageCenter(cut, self.one_axis,xc=xc,yc=yc,scale=self.orig_um_per_pix)
            self.oneBox=CenterRectangle((xc+dx,yc+dy),width=50,height=50,edgecolor='r',linewidth=1.5,fill=False)
            self.one_axis.add_patch(self.oneBox)
            self.one_axis_center=Line2D([xc],[yc],marker='+',markersize=7,markeredgewidth=1.5,markeredgecolor='r')
            self.one_axis.add_line(self.one_axis_center) 
            self.one_axis.set_title('Point 1')
            self.one_axis.set_ylabel('Microns')
            self.one_axis.autoscale(False)
            self.oneImage.set_clim(0,self.maxvalue)     
        #if there is an image update it and the self.oneBox
        else:
            self.updateImageCenter(cut, self.oneImage, self.one_axis,xc=xc,yc=yc,scale=self.orig_um_per_pix)
            self.oneBox.set_center((dx+xc,dy+yc))
            self.oneBox.set_height(boxsize_um)
            self.oneBox.set_width(boxsize_um)
            self.one_axis_center.set_xdata([xc])
            self.one_axis_center.set_ydata([yc])
    
        
    def paintImageTwo(self,cut,xy=(0,0)):
        """paints an image in the self.two_axis, with 0,0 at the center cut=the 2d numpy"""
        #create or update appropriately
        (xc,yc)=xy
        if self.twoImage==None:
            self.twoImage=self.paintImageCenter(cut, self.two_axis,xc=xc,yc=yc,scale=self.orig_um_per_pix)
            self.two_axis_center=Line2D([xc],[yc],marker='+',markersize=7,markeredgewidth=1.5,markeredgecolor='r')
            self.two_axis.add_line(self.two_axis_center) 
            self.two_axis.set_title('Point 2')
            self.two_axis.set_ylabel('Pixels from point 2')
            self.two_axis.autoscale(False)
            self.twoImage.set_clim(0,self.maxvalue)
 
        else:
            self.updateImageCenter(cut, self.twoImage, self.two_axis,xc=xc,yc=yc,scale=self.orig_um_per_pix)
            self.two_axis_center.set_xdata([xc])
            self.two_axis_center.set_ydata([yc])
    
    def paintCorrImage(self,corrmat,dxy_pix,skip):
        """paints an image in the self.corr_axis, with 0,0 at the center and rescaled by skip, plotting a point at dxy_pix
        
        keywords)
        corrmat) the 2d numpy matrix with the image data
        dxy_pix) the offset in pixels from the center of the image to plot the point
        skip) the factor to rescale the axis by, so that when corrmat was produced by mycorrelate2d with a certain skip value, 
        the axis will be in units of pixels
        
        """
        #unpack the values
        (dx,dy)=dxy_pix
        #update or create new
        if self.corrImage==None:
            self.corrImage=self.paintImageCenter(corrmat, self.corr_axis,skip=skip,cmap='jet')             
            self.maxcorrPoint,=self.corr_axis.plot(-dx,-dy,'ro')

            self.colorbar=self.corr_axis.figure.colorbar(self.corrImage,shrink=.9)
            self.corr_axis.set_title('Cross Correlation')
            self.corr_axis.set_ylabel('Pixels shifted')
          
        else:
            self.updateImageCenter(corrmat, self.corrImage, self.corr_axis,skip=skip)
            self.maxcorrPoint.set_data(-dx,-dy)   
        #hard code the correlation maximum at .5
        self.corrImage.set_clim(0,.5)

    def convert_pos_to_orig_ind(self,x,y,tile=None):
        """converts a positionin original units (usually microns) to indices in the original full resolution image
        
        keywords)
        x)x position in microns
        y)y position in microns
        
        returns) (x_pix,y_pix) the indices in pixels of that location
        
        """
        extent = self.findTileExtent(tile) if tile else self.extent
        x=x-extent[1] #CHANGED THIS TO FLIP X IN CUTOUTS
        y=abs(extent[3]-extent[2])-abs(extent[2]-y)
        x_pix=abs(int(x/self.PxSizeUm)) #should just be converting to pixels here. Works but may be slightly off.
        y_pix=abs(int(y/self.PxSizeUm))
        return (x_pix,y_pix)
    
    
    def cutout_window(self,x,y,window):
        """returns a cutout of the original image at a certain location and size
        
        keywords)
        x)x position in microns
        y)y position in microns
        window) size of the patch to cutout, will cutout +/- window in both vertical and horizontal dimensions
        note.. behavior not well specified at edges, may crash
        
        function uses PIL to read in image and crop it appropriately
        returns) cut: a 2d numpy matrix containing the removed patch
        
        """
        try:
            tile = self.findHighResImageFile(x,y)[0]
        except TypeError:
            print "Failed to find high res tile, make sure the tile exists and/or you have selected two points"
            return False 
        print x,y,"xy CUTOUT POS"
        (xpx,ypx)=self.convert_pos_to_orig_ind(x,y,tile)
        image = Image.open(tile)
        image=image.crop([xpx-window,ypx-window,xpx+window,ypx+window])
        (width,height)=image.size
        

        #WHAT MODE SHOULD THIS BE?
        cut=np.reshape(np.array(image.getdata(),np.dtype('uint16')),(height,width))
        return cut



    def cross_correlate_two_to_one(self,xy1,xy2,window=100,delta=75,skip=3):
        """take two points in the image, and calculate the 2d cross correlation function of the image around those two points
        
        keywords)
        xy1) a (x,y) tuple specifying point 1, the point that should be fixed
        xy2) a (x,y) tuple specifiying point 2, the point that should be moved
        window) the size of the patch to cutout (+/- window around the points) for calculating the correlation (default = 100 pixels)
        delta) the size of the maximal shift +/- delta from no shift to calculate
        skip) the number of integer pixels to skip over when calculating the correlation
        
        returns (one_cut,two_cut,corrmat)
        one_cut) the patch cutout around point 1
        two_cut) the patch cutout around point 2
        corrmat) the matrix of correlation values measured with 0,0 being a shift of -delta,-delta
        
        """

        (x1,y1)=xy1
        (x2,y2)=xy2
        one_cut=self.cutout_window(x1,y1,window+delta)
        two_cut=self.cutout_window(x2,y2,window)
        return (one_cut,two_cut,mycorrelate2d(one_cut,two_cut,skip))
       
    def align_by_correlation(self,xy1,xy2,window=100,delta=75,skip=3):
        """take two points in the image, and calculate the 2d cross correlation function of the image around those two points
        plots the results in the appropriate axis, and returns the shift which aligns the two points given in microns
        
        keywords)
        xy1) a (x,y) tuple specifying point 1, the point that should be fixed
        xy2) a (x,y) tuple specifiying point 2, the point that should be moved
        window) the size of the patch to cutout (+/- window around the points) for calculating the correlation (default = 100 pixels)
        delta) the size of the maximal shift +/- delta from no shift to calculate
        skip) the number of integer pixels to skip over when calculating the correlation
        
        returns) (maxC,dxy_um)
        maxC)the maximal correlation measured
        dxy_um) the (x,y) tuple which contains the shift in microns necessary to align point xy2 with point xy1
        
        """
        #calculate the cutout patches and the correlation matrix
        (one_cut,two_cut,corrmat)=self.cross_correlate_two_to_one(xy1,xy2,window,delta,skip)
        #find the peak of the matrix
        maxind=corrmat.argmax()
        #determine the indices of that peak
        (max_i,max_j)=np.unravel_index(maxind,corrmat.shape)
        
        #calculate the shift for that index in pixels
        dy_pix=max_i*skip-delta
        dx_pix=max_j*skip-delta

        #convert those indices into microns
        dy_um=dy_pix*self.orig_um_per_pix
        dx_um=dx_pix*self.orig_um_per_pix

        #pack up the shifts into tuples
        dxy_pix=(dx_pix,dy_pix)
        dxy_um=(dx_um,dy_um)
        #calculate what the maximal correlation was
        corrval=corrmat.max()
        
        print "(correlation,(dx,dy))="
        print (corrval,dxy_pix)
        #paint the patch around the first point in its axis, with a box of size of the two_cut centered around where we found it
        self.paintImageOne(one_cut,xy=xy1,dxy_pix=dxy_pix, window=window)
        #paint the patch around the second point in its axis
        self.paintImageTwo(two_cut,xy=xy2)
        #paint the correlation matrix in its axis
        self.paintCorrImage(corrmat, dxy_pix,skip)
        return (corrmat.max(),dxy_um)
    
    def paintPointsOneTwo(self,xy1,xy2,window):
        (x1,y1)=xy1
        (x2,y2)=xy2
        one_cut=self.cutout_window(x1,y1,window)
        two_cut=self.cutout_window(x2,y2,window)
        self.paintImageOne(one_cut,xy1)
        #paint the patch around the second point in its axis
        self.paintImageTwo(two_cut,xy2)
        
    def make_preview_stack(self,xpos,ypos,width,height,directory):
        print "make a preview stack"
   
        hw_pix=int(round(width*.5/self.orig_um_per_pix))
        hh_pix=int(round(height*.5/self.orig_um_per_pix))
        queue = Queue.Queue()
          
        #spawn a pool of threads, and pass them queue instance 
        for i in range(4):
            t = ImageCutThread(queue)
            t.setDaemon(True)
            t.start()
              
        for i in range(len(self.mosaicArray.xpos)):
            (cx_pix,cy_pix)=self.convert_pos_to_ind(xpos[i],ypos[i])
            rect=[cx_pix-hw_pix,cy_pix-hh_pix,cx_pix+hw_pix,cy_pix+hh_pix]
            queue.put((self.imagefile,rect,i))
        queue.join()
class MosaicImage():
    """A class for storing the a large mosaic imagein a matplotlib axis. Also contains functions for finding corresponding points
    in the larger mosaic image, and plotting informative graphs about that process in different axis"""
    def __init__(self,axis,one_axis,two_axis,corr_axis,imgSrc,rootPath):
        """initialization function which will plot the imagematrix passed in and set the bounds according the bounds specified by extent
        
        keywords)
        axis)the matplotlib axis to plot the image into
        one_axis) the matplotlib axis to plot the cutout of the fixed point when using the corresponding point functionality
        two_axis) the matplotlib axis to plot the cutout of the point that should be moved when using the corresponding point functionality
        corr_axis) the matplotlib axis to plot out the matrix of correlation values found when using the corresponding point functionality
        imagefile) a string with the path of the file which contains the full resolution image that should be used when calculating the corresponding point funcationality
         currently the reading of the image is using PIL so the path specified must be an image which is PIL readable
        imagematrix) a numpy 2d matrix containing a low resolution varsion of the full resolution image, for the purposes of faster plotting/memory management
        extent) a list [minx,maxx,miny,maxy] of the corners of the image.  This will specify the scale of the image, and allow the corresponding point functionality
        to specify how much the movable point should be shifted in the units given by this extent.  If omitted the units will be in pixels and extent will default to
        [0,width,height,0].
       
        """
        #define the attributes of this class
        self.axis=axis
        self.one_axis=one_axis
        self.two_axis=two_axis
        self.corr_axis=corr_axis
        
        
        #initialize the images for the various subplots as None
        self.oneImage=None
        self.twoImage=None
        self.corrImage=None

        self.imgCollection=ImageCollection(rootpath=rootPath,imageSource=imgSrc,axis=self.axis)
        
        (x,y)=imgSrc.get_xy()
        bbox=imgSrc.calc_bbox(x,y)
        self.imgCollection.set_view_home(bbox)
        self.imgCollection.loadImageCollection()
        
        
        self.maxvalue=255
        imgSrc.set_channel('Violet')
        imgSrc.set_exposure(250)
        
        self.axis.set_title('Mosaic Image')
        
                  
    # def paintImage(self):
        # """plots self.imagematrix in self.axis using self.extent to define the boundaries"""
        # self.Image=self.axis.imshow(self.imagematrix,cmap='gray',extent=self.extent)
        # (minval,maxval)=self.Image.get_clim()
        # self.maxvalue=maxval
        # #self.axis.canvas.get_toolbar().slider.SetSelection(minval,self.maxvalue)
        # self.axis.autoscale(False)
        # self.axis.set_xlabel('X Position (pixels)')
        # self.axis.set_ylabel('Y Position (pixels)')
        # self.Image.set_clim(0,25000)
    
    def set_maxval(self,maxvalue):
        """set the maximum value in the image colormap"""
        self.maxvalue=maxvalue;
        self.repaint()
    
    def set_view_home(self):
        self.imgCollection.set_view_home()
        
    def repaint(self):
        """sets the new clim for the Image using self.maxvalue as the new maximum value"""
        #(minval,maxval)=self.Image.get_clim()
        self.imgCollection.update_clim(max=self.maxvalue)
        
        if self.oneImage!=None:
            self.oneImage.set_clim(0,self.maxvalue)
        if self.twoImage!=None:
            self.twoImage.set_clim(0,self.maxvalue)
    
    def paintImageCenter(self,cut,theaxis,xc=0,yc=0,skip=1,cmap='gray',scale=1):
        """paints an image and redefines the coordinates such that 0,0 is at the center
        
        keywords
        cut)the 2d numpy matrix with the image data
        the axis)the matplotlib axis to plot it in
        skip)the factor to rescale the axis by so that 1 entry in the cut, is equal to skip units on the axis (default=1)
        cmap)the colormap designation to use for the plot (default 'gray')
        
        """
        theaxis.cla()
        (h,w)=cut.shape
        dh=skip*1.0*(h-1)/2       
        dw=skip*1.0*(w-1)/2
        dh=dh*scale;
        dw=dw*scale;
        
        left=xc-dw
        right=xc+dw
        top=yc-dh
        bot=yc+dh
            
        ext=[left,right,bot,top]

        image=theaxis.imshow(cut,cmap=cmap,extent=ext)
        
        theaxis.set_xlim(left=xc-dw,right=xc+dw)
        theaxis.set_ylim(bottom=yc+dh,top=yc-dh)
            
        theaxis.hold(True)

        return image 
    
    def updateImageCenter(self,cut,theimage,theaxis,xc=0,yc=0,skip=1,scale=1):
        """updates an image with a new image
        
        keywords
        cut) the 2d numpy matrix with the image data 
        theimage) the image to update
        theaxis) the axis that the image is in
        skip)the factor to rescale the axis by so that 1 entry in the cut, is equal to skip units on the axis (default=1)

        """
        (h,w)=cut.shape[0:2]
        dh=skip*1.0*(h-1)/2       
        dw=skip*1.0*(w-1)/2
        dh=dh*scale;
        dw=dw*scale;
        theimage.set_array(cut)
      
        left=xc-dw
        right=xc+dw
        theaxis.set_xlim(left=xc-dw,right=xc+dw)

        top=yc-dh
        bot=yc+dh
        theaxis.set_ylim(top=yc-dh,bottom=yc+dh)

        ext=[left,right,bot,top]
        theimage.set_extent(ext)
         
         
    def paintImageOne(self,cut,xy=(0,0),dxy_pix=(0,0),window=0):
        """paints an image in the self.one_axis axis, plotting a box of size 2*window+1 around that point
        
        keywords
        cut) the 2d numpy matrix with the image data
        dxy_pix) the center of the box to be drawn given as an (x,y) tuple
        window)the size of the box, where the height is 2*window+1
        
        """ 
        (xc,yc)=xy  
        (dx,dy)=dxy_pix
        
        pixsize=self.imgCollection.get_pixel_size()
        
        dx=dx*pixsize;
        dy=dy*pixsize;
        #the size of the cutout box in microns
        boxsize_um=(2*window+1)*pixsize;
        
        #if there is no image yet, create one and a box
        if self.oneImage==None:
            self.oneImage=self.paintImageCenter(cut, self.one_axis,xc=xc,yc=yc,scale=pixsize)
            self.oneBox=CenterRectangle((xc+dx,yc+dy),width=50,height=50,edgecolor='r',linewidth=1.5,fill=False)
            self.one_axis.add_patch(self.oneBox)
            self.one_axis_center=Line2D([xc],[yc],marker='+',markersize=7,markeredgewidth=1.5,markeredgecolor='r')
            self.one_axis.add_line(self.one_axis_center) 
            self.one_axis.set_title('Point 1')
            self.one_axis.set_ylabel('Microns')
            self.one_axis.autoscale(False)
            self.oneImage.set_clim(0,self.maxvalue)     
        #if there is an image update it and the self.oneBox
        else:
            self.updateImageCenter(cut, self.oneImage, self.one_axis,xc=xc,yc=yc,scale=pixsize)
            self.oneBox.set_center((dx+xc,dy+yc))
            self.oneBox.set_height(boxsize_um)
            self.oneBox.set_width(boxsize_um)
            self.one_axis_center.set_xdata([xc])
            self.one_axis_center.set_ydata([yc])
    
        
    def paintImageTwo(self,cut,xy=(0,0),xyp=None,pointcolor='r'):
        """paints an image in the self.two_axis, with 0,0 at the center cut=the 2d numpy"""
        #create or update appropriately
        pixsize=self.imgCollection.get_pixel_size()
       
        
        (xc,yc)=xy
        if xyp is not None:
            (xp,yp)=xyp
        else:
            (xp,yp)=xy
            
        if self.twoImage==None:
            self.twoImage=self.paintImageCenter(cut, self.two_axis,xc=xc,yc=yc,scale=pixsize)
            self.two_axis_center=Line2D([xp],[yp],marker='+',markersize=7,markeredgewidth=1.5,markeredgecolor=pointcolor)
            self.two_axis.add_line(self.two_axis_center) 
            self.two_axis.set_title('Point 2')
            self.two_axis.set_ylabel('Pixels from point 2')
            self.two_axis.autoscale(False)
            self.twoImage.set_clim(0,self.maxvalue)
 
        else:
            self.updateImageCenter(cut, self.twoImage, self.two_axis,xc=xc,yc=yc,scale=pixsize)
            self.two_axis_center.set_xdata([xp])
            self.two_axis_center.set_ydata([yp])
    
    def paintCorrImage(self,corrmat,dxy_pix,skip):
        """paints an image in the self.corr_axis, with 0,0 at the center and rescaled by skip, plotting a point at dxy_pix
        
        keywords)
        corrmat) the 2d numpy matrix with the image data
        dxy_pix) the offset in pixels from the center of the image to plot the point
        skip) the factor to rescale the axis by, so that when corrmat was produced by mycorrelate2d with a certain skip value, 
        the axis will be in units of pixels
        
        """
        #unpack the values
        (dx,dy)=dxy_pix
        #update or create new
        if self.corrImage==None:
            self.corrImage=self.paintImageCenter(corrmat, self.corr_axis,skip=skip,cmap='jet')             
            self.maxcorrPoint,=self.corr_axis.plot(dx,dy,'ro')

            self.colorbar=self.corr_axis.figure.colorbar(self.corrImage,shrink=.9)
            self.corr_axis.set_title('Cross Correlation')
            self.corr_axis.set_ylabel('Pixels shifted')
          
        else:
            self.updateImageCenter(corrmat, self.corrImage, self.corr_axis,skip=skip)
            self.maxcorrPoint.set_data(dx,dy)   
        #hard code the correlation maximum at .5
        self.corrImage.set_clim(0,.5)
    
    def cutout_window(self,x,y,window):
        """returns a cutout of the original image at a certain location and size
        
        keywords)
        x)x position in microns
        y)y position in microns
        window) size of the patch to cutout, will cutout +/- window in both vertical and horizontal dimensions
        note.. behavior not well specified at edges, may crash
        
        function uses PIL to read in image and crop it appropriately
        returns) cut: a 2d numpy matrix containing the removed patch
        
        """
        box=Rectangle(x-window,x+window,y-window,y+window)
        return self.imgCollection.get_cutout(box)
        
    def cross_correlate_two_to_one(self,xy1,xy2,window=60,delta=40,skip=3):
        """take two points in the image, and calculate the 2d cross correlation function of the image around those two points
        
        keywords)
        xy1) a (x,y) tuple specifying point 1, the point that should be fixed
        xy2) a (x,y) tuple specifiying point 2, the point that should be moved
        window) the size of the patch to cutout (+/- window around the points) for calculating the correlation (default = 100 um)
        delta) the size of the maximal shift +/- delta from no shift to calculate
        skip) the number of integer pixels to skip over when calculating the correlation
        
        returns (one_cut,two_cut,corrmat)
        one_cut) the patch cutout around point 1
        two_cut) the patch cutout around point 2
        corrmat) the matrix of correlation values measured with 0,0 being a shift of -delta,-delta
        
        """
        (x1,y1)=xy1
        (x2,y2)=xy2
        one_cut=self.cutout_window(x1,y1,window+delta)
        two_cut=self.cutout_window(x2,y2,window)
        #return (target_cut,source_cut,mycorrelate2d(target_cut,source_cut,mode='valid'))
        return (one_cut,two_cut,mycorrelate2d(one_cut,two_cut,skip))
       
    def align_by_correlation(self,xy1,xy2,window=60,delta=40,skip=3):
        """take two points in the image, and calculate the 2d cross correlation function of the image around those two points
        plots the results in the appropriate axis, and returns the shift which aligns the two points given in microns
        
        keywords)
        xy1) a (x,y) tuple specifying point 1, the point that should be fixed
        xy2) a (x,y) tuple specifiying point 2, the point that should be moved
        window) the size of the patch to cutout (+/- window around the points) for calculating the correlation (default = 100 pixels)
        delta) the size of the maximal shift +/- delta from no shift to calculate
        skip) the number of integer pixels to skip over when calculating the correlation
        
        returns) (maxC,dxy_um)
        maxC)the maximal correlation measured
        dxy_um) the (x,y) tuple which contains the shift in microns necessary to align point xy2 with point xy1
        
        """
        pixsize=self.imgCollection.get_pixel_size()
        #calculate the cutout patches and the correlation matrix
        (one_cut,two_cut,corrmat)=self.cross_correlate_two_to_one(xy1,xy2,window,delta,skip)
        #find the peak of the matrix
        maxind=corrmat.argmax()
        (h,w)=corrmat.shape
        #determine the indices of that peak
        (max_i,max_j)=np.unravel_index(maxind,corrmat.shape)
        
        
        #calculate the shift for that index in pixels
        dy_pix=int((max_i-(h/2))*skip)
        dx_pix=int((max_j-(w/2))*skip)
        #convert those indices into microns
        
        
        dy_um=dy_pix*pixsize
        dx_um=dx_pix*pixsize
        #pack up the shifts into tuples
        dxy_pix=(dx_pix,dy_pix)
        dxy_um=(dx_um,dy_um)
        #calculate what the maximal correlation was
        corrval=corrmat.max()
        
        print "(correlation,(dx,dy))="
        print (corrval,dxy_pix)
        #paint the patch around the first point in its axis, with a box of size of the two_cut centered around where we found it
        self.paintImageOne(one_cut,xy=xy1,dxy_pix=dxy_pix, window=window)
        #paint the patch around the second point in its axis
        self.paintImageTwo(two_cut,xy=xy2)
        #paint the correlation matrix in its axis
        self.paintCorrImage(corrmat, dxy_pix,skip)
        return (corrmat.max(),dxy_um)
        
    def explore_match(self,img1, kp1,img2,kp2, status = None, H = None):
        h1, w1 = img1.shape[:2]
        h2, w2 = img2.shape[:2]
        vis = np.zeros((max(h1, h2), w1+w2), np.uint8)
        vis[:h1, :w1] = img1
        vis[:h2, w1:w1+w2] = img2
        vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)

        if H is not None:
            corners = np.float32([[0, 0], [w1, 0], [w1, h1], [0, h1]])
            corners = np.int32( cv2.perspectiveTransform(corners.reshape(1, -1, 2), H).reshape(-1, 2) + (w1, 0) )
            cv2.polylines(vis, [corners], True, (255, 255, 255))

        if status is None:
            status = np.ones(len(kp1), np.bool_)
        p1 = np.int32([kpp.pt for kpp in kp1])
        p2 = np.int32([kpp.pt for kpp in kp2]) + (w1, 0)

        green = (0, 255, 0)
        red = (0, 0, 255)
        white = (255, 255, 255)
        kp_color = (51, 103, 236)
        for (x1, y1), (x2, y2), inlier in zip(p1, p2, status):
            if inlier:
                col = green
                cv2.circle(vis, (x1, y1), 2, col, -1)
                cv2.circle(vis, (x2, y2), 2, col, -1)
            else:
                col = red
                r = 2
                thickness = 3
                cv2.line(vis, (x1-r, y1-r), (x1+r, y1+r), col, thickness)
                cv2.line(vis, (x1-r, y1+r), (x1+r, y1-r), col, thickness)
                cv2.line(vis, (x2-r, y2-r), (x2+r, y2+r), col, thickness)
                cv2.line(vis, (x2-r, y2+r), (x2+r, y2-r), col, thickness)
        vis0 = vis.copy()
        for (x1, y1), (x2, y2), inlier in zip(p1, p2, status):
            if inlier:
                cv2.line(vis, (x1, y1), (x2, y2), green)
            else:
                cv2.line(vis, (x1, y1), (x2, y2), red)
        return vis
        
            
    def align_by_sift(self,xy1,xy2,window=70):
        """take two points in the image, and calculate SIFT features image around those two points
        cutting out size window

        keywords)
        xy1) a (x,y) tuple specifying point 1, the point that should be fixed
        xy2) a (x,y) tuple specifiying point 2, the point that should be moved
        window) the size of the patch to cutout (+/- window around the points) for calculating the correlation (default = 70 um)


        returns) (maxC,dxy_um)
        maxC)the maximal correlation measured
        dxy_um) the (x,y) tuple which contains the shift in microns necessary to align point xy2 with point xy1

        """
        print "starting align by sift"
        pixsize=self.imgCollection.get_pixel_size()
        #cutout the images around the two points
        (x1,y1)=xy1
        (x2,y2)=xy2
        one_cut=self.cutout_window(x1,y1,window)
        two_cut=self.cutout_window(x2,y2,window)
        
        #one_cuta=np.minimum(one_cut*256.0/self.maxvalue,255.0).astype(np.uint8)
        #two_cuta=np.minimum(two_cut*256.0/self.maxvalue,255.0).astype(np.uint8)
        one_cuta=cv2.equalizeHist(one_cut)
        two_cuta=cv2.equalizeHist(two_cut)
        
        
        sift = cv2.SIFT(nfeatures=500,contrastThreshold=.2)
        kp1, des1 = sift.detectAndCompute(one_cuta,None)
        kp2, des2 = sift.detectAndCompute(two_cuta,None)
        print "features1:%d"%len(kp1)
        print "features2:%d"%len(kp2)
        

        
        img_one = cv2.drawKeypoints(one_cuta,kp1)
        img_two = cv2.drawKeypoints(two_cuta,kp2)
        
  
        #image2=self.two_axis.imshow(img_two)
        
        # FLANN parameters
        FLANN_INDEX_KDTREE = 0
        index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
        search_params = dict(checks=50)   # or pass empty dictionary

        flann = cv2.FlannBasedMatcher(index_params,search_params)

        matches = flann.knnMatch(des1,des2,k=2)

        # Need to draw only good matches, so create a mask
        matchesMask = np.zeros(len(matches))
        
        kp1matchIdx=[]
        kp2matchIdx=[]
        # ratio test as per Lowe's paper
        for i,(m,n) in enumerate(matches):
            if m.distance < 0.9*n.distance:
                kp1matchIdx.append(m.queryIdx)
                kp2matchIdx.append(m.trainIdx)
               

      
        p1 = np.array([kp1[i].pt for i in kp1matchIdx])
        p2 = np.array([kp2[i].pt for i in kp2matchIdx]) 
       # p1c = [pt-np.array[window,window] for pt in p1]
       # p2c = [pt-np.array[window,window] for pt in p2]
        kp1m = [kp1[i] for i in kp1matchIdx]
        kp2m = [kp2[i] for i in kp2matchIdx]
        #print "kp1matchshape"
        #print matchesMask
        #print len(kp1match)
        #print len(kp2match)
        
        #draw_params = dict(matchColor = (0,255,0),
        #                   singlePointColor = (255,0,0),
        #                   matchesMask = matchesMask,
        #                   flags = 0)

        #img3 = cv2.drawMatches(one_cut,kp1,two_cut,kp2,matches,None,**draw_params)
        
        
        transModel=ransac.RigidModel()
        bestModel,bestInlierIdx=ransac.ransac(p1,p2,transModel,2,300,20.0,3,debug=True,return_all=True)
        
        if bestModel is not None:
            
            the_center = np.array([[one_cut.shape[0]/2,one_cut.shape[1]/2]])
            trans_center=transModel.transform_points(the_center,bestModel)
            offset=the_center-trans_center
            xc=x2+offset[0,0]*pixsize
            yc=y2-offset[0,1]*pixsize
          
            #newcenter=Line2D([trans_center[0,0]+one_cut.shape[1]],[trans_center[0,1]],marker='+',markersize=7,markeredgewidth=1.5,markeredgecolor='r')
            #oldcenter=Line2D([the_center[0,0]],[the_center[0,1]],marker='+',markersize=7,markeredgewidth=1.5,markeredgecolor='r')
            
            
            dx_um=-bestModel.t[0]*pixsize
            dy_um=-bestModel.t[1]*pixsize
           
                
      
            print "matches:%d"%len(kp1matchIdx)
            print "inliers:%d"%len(bestInlierIdx)
            print ('translation',bestModel.t)
            print ('rotation',bestModel.R)
            
            mask = np.zeros(len(p1), np.bool_)
            mask[bestInlierIdx]=1
            
                 
            #img3 = self.explore_match(one_cuta,kp1m,two_cuta,kp2m,mask)
            #self.corr_axis.cla()
            #self.corr_axis.imshow(img3)
            #self.corr_axis.add_line(newcenter)
            #self.corr_axis.add_line(oldcenter)
            #self.repaint()

         
            #self.paintImageOne(img_one,xy=xy1)
            #paint the patch around the second point in its axis
            #self.paintImageTwo(img_two,xy=xy2)
            #paint the correlation matrix in its axis
            #self.paintCorrImage(corrmat, dxy_pix,skip)
            
          
          
            print (dx_um,dy_um)

            
            self.paintImageOne(img_one,xy=xy1)
            self.paintImageTwo(img_two,xy=xy2,xyp=(x2-dx_um,y2-dy_um))
            
            return ((dx_um,dy_um),len(bestInlierIdx))
        else:
            self.paintImageOne(img_one,xy=xy1)
            self.paintImageTwo(img_two,xy=xy2)
            
            return ((0.0,0.0),0)
        
    def paintPointsOneTwo(self,xy1,xy2,window):
        (x1,y1)=xy1
        (x2,y2)=xy2
        print "getting p1 window at ",x1,y1
        print "getting p2 window at ",x2,y2
        
        one_cut=self.cutout_window(x1,y1,window)
        two_cut=self.cutout_window(x2,y2,window)
        self.paintImageOne(one_cut,xy1)
        #paint the patch around the second point in its axis
        self.paintImageTwo(two_cut,xy2)
        
    def make_preview_stack(self,xpos,ypos,width,height,directory):
        print "make a preview stack"
   
        hw_pix=int(round(width*.5/self.orig_um_per_pix))
        hh_pix=int(round(height*.5/self.orig_um_per_pix))
        queue = Queue.Queue()
          
        #spawn a pool of threads, and pass them queue instance 
        for i in range(4):
            t = ImageCutThread(queue)
            t.setDaemon(True)
            t.start()
              
        for i in range(len(self.mosaicArray.xpos)):
            (cx_pix,cy_pix)=self.convert_pos_to_ind(xpos[i],ypos[i])
            rect=[cx_pix-hw_pix,cy_pix-hh_pix,cx_pix+hw_pix,cy_pix+hh_pix]
            queue.put((self.imagefile,rect,i))
        queue.join()
class MosaicImage():
    """A class for storing the a large mosaic imagein a matplotlib axis. Also contains functions for finding corresponding points
    in the larger mosaic image, and plotting informative graphs about that process in different axis"""
    def __init__(self,
                 axis,
                 one_axis,
                 two_axis,
                 corr_axis,
                 imgSrc,
                 rootPath,
                 figure=None,
                 load_callback=None):
        """initialization function which will plot the imagematrix passed in and set the bounds according the bounds specified by extent
        
        keywords)
        axis)the matplotlib axis to plot the image into
        one_axis) the matplotlib axis to plot the cutout of the fixed point when using the corresponding point functionality
        two_axis) the matplotlib axis to plot the cutout of the point that should be moved when using the corresponding point functionality
        corr_axis) the matplotlib axis to plot out the matrix of correlation values found when using the corresponding point functionality
        imagefile) a string with the path of the file which contains the full resolution image that should be used when calculating the corresponding point funcationality
         currently the reading of the image is using PIL so the path specified must be an image which is PIL readable
        imagematrix) a numpy 2d matrix containing a low resolution varsion of the full resolution image, for the purposes of faster plotting/memory management
        extent) a list [minx,maxx,miny,maxy] of the corners of the image.  This will specify the scale of the image, and allow the corresponding point functionality
        to specify how much the movable point should be shifted in the units given by this extent.  If omitted the units will be in pixels and extent will default to
        [0,width,height,0].
       
        """
        #define the attributes of this class
        self.axis = axis
        self.one_axis = one_axis
        self.two_axis = two_axis
        self.corr_axis = corr_axis

        #initialize the images for the various subplots as None
        self.oneImage = None
        self.twoImage = None
        self.corrImage = None
        self.imgSrc = imgSrc
        self.imgCollection = ImageCollection(rootpath=rootPath,
                                             imageSource=imgSrc,
                                             axis=self.axis)

        (x, y) = imgSrc.get_xy()
        bbox = imgSrc.calc_bbox(x, y)
        self.imgCollection.set_view_home()
        self.imgCollection.load_image_collection(load_callback=load_callback)

        self.maxvalue = 512
        self.currentPosLine2D = Line2D([x], [y],
                                       marker='o',
                                       markersize=7,
                                       markeredgewidth=1.5,
                                       markeredgecolor='r',
                                       zorder=100)
        self.axis.add_line(self.currentPosLine2D)
        self.axis.set_title('Mosaic Image')
        self.fig = figure
        self.update_pos_cursor()

    # def paintImage(self):
    # """plots self.imagematrix in self.axis using self.extent to define the boundaries"""
    # self.Image=self.axis.imshow(self.imagematrix,cmap='gray',extent=self.extent)
    # (minval,maxval)=self.Image.get_clim()
    # self.maxvalue=maxval
    # #self.axis.canvas.get_toolbar().slider.SetSelection(minval,self.maxvalue)
    # self.axis.autoscale(False)
    # self.axis.set_xlabel('X Position (pixels)')
    # self.axis.set_ylabel('Y Position (pixels)')
    # self.Image.set_clim(0,25000)

    def update_pos_cursor(self):
        x, y = self.imgSrc.get_xy()
        self.currentPosLine2D.set_xdata([x])
        self.currentPosLine2D.set_ydata([y])
        self.axis.draw_artist(self.currentPosLine2D)
        self.fig.canvas.draw()
        #self.cursor_timer = threading.Timer(1, self.update_pos_cursor)
        #self.cursor_timer.start()

    def set_maxval(self, maxvalue):
        """set the maximum value in the image colormap"""
        self.maxvalue = maxvalue
        self.repaint()

    def set_view_home(self):
        self.imgCollection.set_view_home()

    def crop_to_images(self, evt):
        self.imgCollection.crop_to_images(evt)

    def repaint(self):
        """sets the new clim for the Image using self.maxvalue as the new maximum value"""
        #(minval,maxval)=self.Image.get_clim()
        self.imgCollection.update_clim(max=self.maxvalue)

        if self.oneImage != None:
            self.oneImage.set_clim(0, self.maxvalue)
        if self.twoImage != None:
            self.twoImage.set_clim(0, self.maxvalue)

    def paintImageCenter(self,
                         cut,
                         theaxis,
                         xc=0,
                         yc=0,
                         skip=1,
                         cmap='gray',
                         scale=1,
                         interpolation='nearest'):
        """paints an image and redefines the coordinates such that 0,0 is at the center
        
        keywords
        cut)the 2d numpy matrix with the image data
        the axis)the matplotlib axis to plot it in
        skip)the factor to rescale the axis by so that 1 entry in the cut, is equal to skip units on the axis (default=1)
        cmap)the colormap designation to use for the plot (default 'gray')
        
        """
        theaxis.cla()
        (h, w) = cut.shape
        dh = skip * 1.0 * (h - 1) / 2
        dw = skip * 1.0 * (w - 1) / 2
        dh = dh * scale
        dw = dw * scale

        left = xc - dw
        right = xc + dw
        top = yc - dh
        bot = yc + dh

        ext = [left, right, bot, top]

        image = theaxis.imshow(cut,
                               cmap=cmap,
                               extent=ext,
                               interpolation=interpolation)

        theaxis.set_xlim(left=xc - dw, right=xc + dw)
        theaxis.set_ylim(bottom=yc + dh, top=yc - dh)

        theaxis.hold(True)

        return image

    def updateImageCenter(self,
                          cut,
                          theimage,
                          theaxis,
                          xc=0,
                          yc=0,
                          skip=1,
                          scale=1):
        """updates an image with a new image
        
        keywords
        cut) the 2d numpy matrix with the image data 
        theimage) the image to update
        theaxis) the axis that the image is in
        skip)the factor to rescale the axis by so that 1 entry in the cut, is equal to skip units on the axis (default=1)

        """
        (h, w) = cut.shape[0:2]
        dh = skip * 1.0 * (h - 1) / 2
        dw = skip * 1.0 * (w - 1) / 2
        dh = dh * scale
        dw = dw * scale
        theimage.set_array(cut)

        left = xc - dw
        right = xc + dw
        theaxis.set_xlim(left=xc - dw, right=xc + dw)

        top = yc - dh
        bot = yc + dh
        theaxis.set_ylim(top=yc - dh, bottom=yc + dh)

        ext = [left, right, bot, top]
        theimage.set_extent(ext)

    def paintImageOne(self, cut, xy=(0, 0), dxy_pix=(0, 0), window=0):
        """paints an image in the self.one_axis axis, plotting a box of size 2*window+1 around that point
        
        keywords
        cut) the 2d numpy matrix with the image data
        dxy_pix) the center of the box to be drawn given as an (x,y) tuple
        window)the size of the box, where the height is 2*window+1
        
        """
        (xc, yc) = xy
        (dx, dy) = dxy_pix

        pixsize = self.imgCollection.get_pixel_size()

        dx = dx * pixsize
        dy = dy * pixsize
        #the size of the cutout box in microns
        boxsize_um = (2 * window + 1) * pixsize

        #if there is no image yet, create one and a box
        if self.oneImage == None:
            self.oneImage = self.paintImageCenter(cut,
                                                  self.one_axis,
                                                  xc=xc,
                                                  yc=yc,
                                                  scale=pixsize)
            self.oneBox = CenterRectangle((xc + dx, yc + dy),
                                          width=50,
                                          height=50,
                                          edgecolor='r',
                                          linewidth=1.5,
                                          fill=False)
            self.one_axis.add_patch(self.oneBox)
            self.one_axis_center = Line2D([xc], [yc],
                                          marker='+',
                                          markersize=7,
                                          markeredgewidth=1.5,
                                          markeredgecolor='r')
            self.one_axis.add_line(self.one_axis_center)
            self.one_axis.set_title('Point 1')
            self.one_axis.set_ylabel('Microns')
            self.one_axis.autoscale(False)
            self.oneImage.set_clim(0, self.maxvalue)
        #if there is an image update it and the self.oneBox
        else:
            self.updateImageCenter(cut,
                                   self.oneImage,
                                   self.one_axis,
                                   xc=xc,
                                   yc=yc,
                                   scale=pixsize)
            self.oneBox.set_center((dx + xc, dy + yc))
            self.oneBox.set_height(boxsize_um)
            self.oneBox.set_width(boxsize_um)
            self.one_axis_center.set_xdata([xc])
            self.one_axis_center.set_ydata([yc])

    def paintImageTwo(self, cut, xy=(0, 0), xyp=None, pointcolor='r'):
        """paints an image in the self.two_axis, with 0,0 at the center cut=the 2d numpy"""
        #create or update appropriately
        pixsize = self.imgCollection.get_pixel_size()

        (xc, yc) = xy
        if xyp is not None:
            (xp, yp) = xyp
        else:
            (xp, yp) = xy

        if self.twoImage == None:
            self.twoImage = self.paintImageCenter(cut,
                                                  self.two_axis,
                                                  xc=xc,
                                                  yc=yc,
                                                  scale=pixsize)
            self.two_axis_center = Line2D([xp], [yp],
                                          marker='+',
                                          markersize=7,
                                          markeredgewidth=1.5,
                                          markeredgecolor=pointcolor)
            self.two_axis.add_line(self.two_axis_center)
            self.two_axis.set_title('Point 2')
            self.two_axis.set_ylabel('Pixels from point 2')
            self.two_axis.autoscale(False)
            self.twoImage.set_clim(0, self.maxvalue)

        else:
            self.updateImageCenter(cut,
                                   self.twoImage,
                                   self.two_axis,
                                   xc=xc,
                                   yc=yc,
                                   scale=pixsize)
            self.two_axis_center.set_xdata([xp])
            self.two_axis_center.set_ydata([yp])

    def paintCorrImage(self, corrmat, dxy_pix, skip=1):
        """paints an image in the self.corr_axis, with 0,0 at the center and rescaled by skip, plotting a point at dxy_pix
        
        keywords)
        corrmat) the 2d numpy matrix with the image data
        dxy_pix) the offset in pixels from the center of the image to plot the point
        skip) the factor to rescale the axis by, so that when corrmat was produced by mycorrelate2d with a certain skip value, 
        the axis will be in units of pixels
        
        """
        #unpack the values
        (dx, dy) = dxy_pix
        #update or create new
        if self.corrImage == None:
            self.corrImage = self.paintImageCenter(corrmat,
                                                   self.corr_axis,
                                                   skip=skip,
                                                   cmap='jet')
            self.maxcorrPoint, = self.corr_axis.plot(dx, dy, 'ro')

            self.colorbar = self.corr_axis.figure.colorbar(
                self.corrImage, shrink=.9, ticks=[0.2, 0.4, 0.6, 0.8, 1.0])
            self.corrImage.set_clim(vmin=0.0, vmax=1.0)
            self.corr_axis.set_title('Cross Correlation')
            self.corr_axis.set_ylabel('Pixels shifted')

        else:
            self.updateImageCenter(corrmat,
                                   self.corrImage,
                                   self.corr_axis,
                                   skip=skip)
            self.maxcorrPoint.set_data(dx, dy)
        #hard code the correlation maximum at .5
        #self.corrImage.set_clim(0,.5)

    def cutout_window(self, x, y, window):
        """returns a cutout of the original image at a certain location and size
        
        keywords)
        x)x position in microns
        y)y position in microns
        window) size of the patch to cutout (microns), will cutout +/- window in both vertical and horizontal dimensions
        note.. behavior not well specified at edges, may crash
        
        function uses PIL to read in image and crop it appropriately
        returns) cut: a 2d numpy matrix containing the removed patch
        
        """
        box = Rectangle(x - window, x + window, y - window, y + window)
        return self.imgCollection.get_cutout(box)

    def cross_correlate_two_to_one(self,
                                   xy1,
                                   xy2,
                                   window=60,
                                   delta=40,
                                   skip=3):
        """take two points in the image, and calculate the 2d cross correlation function of the image around those two points
        
        keywords)
        xy1) a (x,y) tuple specifying point 1, the point that should be fixed
        xy2) a (x,y) tuple specifiying point 2, the point that should be moved
        window) the size of the patch to cutout (+/- window around the points) for calculating the correlation (default = 100 um)
        delta) the size of the maximal shift +/- delta from no shift to calculate
        skip) the number of integer pixels to skip over when calculating the correlation
        
        returns (one_cut,two_cut,corrmat)
        one_cut) the patch cutout around point 1
        two_cut) the patch cutout around point 2
        corrmat) the matrix of correlation values measured with 0,0 being a shift of -delta,-delta
        
        """
        (x1, y1) = xy1
        (x2, y2) = xy2
        one_cut = self.cutout_window(x1, y1, window + delta)
        two_cut = self.cutout_window(x2, y2, window)
        #return (target_cut,source_cut,mycorrelate2d(target_cut,source_cut,mode='valid'))
        return (one_cut, two_cut, mycorrelate2d(one_cut, two_cut, skip))

    def _cross_correlation_shift(self, fixed_cutout, to_shift_cutout):
        '''
        :param one_cut: cutout around point 1
        :param two_cut: cutout around point 2
        :return: corrmatt, corval, dx_pix, dy_pix
        '''
        src_image = np.array(fixed_cutout, dtype=np.complex128, copy=False)
        target_image = np.array(to_shift_cutout,
                                dtype=np.complex128,
                                copy=False)
        f1 = np.std(fixed_cutout)
        f2 = np.std(to_shift_cutout)
        normfactor = f1 * f2 * fixed_cutout.size
        src_freq = np.fft.fftn(src_image)
        target_freq = np.fft.fftn(target_image)
        shape = src_freq.shape
        image_product = src_freq * target_freq.conj()
        corrmat = np.fft.ifftn(image_product)
        corrmat = np.fft.fftshift(corrmat.real / normfactor)
        #find the peak of the matrix
        maxind = corrmat.argmax()
        (h, w) = corrmat.shape
        #determine the indices of that peak
        (max_i, max_j) = np.unravel_index(maxind, corrmat.shape)

        #calculate the shift for that index in pixels
        dy_pix = int((max_i - (h / 2)))
        dx_pix = int((max_j - (w / 2)))

        #calculate what the maximal correlation was
        corrval = corrmat.max()

        return corrmat, corrval, dx_pix, dy_pix

    def _get_faster_pixel_dimension(self, current_dimension):
        '''
        Uses a list of pre-calculated dimensions to cut the image size down
        to one that is faster for np.fft.fftn(). Dimensions are all integers of the form
        k*2^n for small k.
        :param current_dimension:
        :return: new dimension
        '''
        better_dimensions = [
            80, 84, 88, 92, 96, 104, 110, 112, 120, 128, 130, 132, 136, 140,
            152, 156, 160, 168, 176, 184, 192, 208, 220, 224, 240, 256, 260,
            264, 272, 280, 304, 312, 320, 336, 352, 368, 384, 416, 440, 448,
            480, 512, 520, 528, 544, 560, 608, 624, 640, 672, 704, 736, 768,
            832, 880, 896, 960, 1024, 1040, 1056, 1088, 1120, 1216, 1248, 1280,
            1344, 1408, 1472, 1536, 1664, 1760, 1792, 1920, 2048
        ]

        pos = bisect_right(better_dimensions, current_dimension) - 1
        print 'pos', pos
        return better_dimensions[pos]

    def get_central_region(self, cutout, dim):
        '''

        :param cutout: a 2d numpy array, could be non square
        :param dim: an integer dimensional
        :return: cutout_central, the central dim x dim region of cutout
        '''
        cut_height = cutout.shape[0] - dim
        cut_width = cutout.shape[1] - dim
        top_pix = np.int(np.floor(cut_height / 2.0))
        left_pix = np.int(np.floor(cut_width / 2.0))
        cutout_central = cutout[top_pix:top_pix + dim, left_pix:left_pix + dim]
        return cutout_central

    def fix_cutout_size(self, cutout1, cutout2):
        '''

        :param cutout1,2: two 2d numpy array representing a windowed cutouts around a point of interest,
        should be in the range of 100-2048 pixels in height/width
        :return: cutout1_fix,cutout2_fix: the a 2d numpy arrays that are square, and have been cropped to be of a size
        that will be relatively fast to calculate a 2d FFT of.
        '''
        min_dim = min(cutout1.shape[0], cutout1.shape[1], cutout2.shape[0],
                      cutout2.shape[1])
        new_dim = self._get_faster_pixel_dimension(min_dim)

        cutout1_fix = self.get_central_region(cutout1, new_dim)
        cutout2_fix = self.get_central_region(cutout2, new_dim)

        return (cutout1_fix, cutout2_fix)

    def align_by_correlation(self, xy1, xy2, CorrSettings=CorrSettings()):
        """take two points in the image, and calculate the 2d cross correlation function of the image around those two points
        plots the results in the appropriate axis, and returns the shift which aligns the two points given in microns
        
        keywords)
        xy1) a (x,y) tuple specifying point 1, the point that should be fixed
        xy2) a (x,y) tuple specifiying point 2, the point that should be moved
        window) the size of the patch to cutout (+/- window around the points) for calculating the correlation (default = 100 pixels)
        delta) the size of the maximal shift +/- delta from no shift to calculate
        skip) the number of integer pixels to skip over when calculating the correlation
        
        returns) (maxC,dxy_um)
        maxC)the maximal correlation measured
        dxy_um) the (x,y) tuple which contains the shift in microns necessary to align point xy2 with point xy1
        
        """
        start_time = time.time()

        window = CorrSettings.window
        delta = CorrSettings.delta
        skip = CorrSettings.skip

        pixsize = self.imgCollection.get_pixel_size()
        #calculate the cutout patches and the correlation matrix
        #(one_cut,two_cut,corrmat)=self.cross_correlate_two_to_one(xy1,xy2,window,delta,skip)
        (x1, y1) = xy1
        (x2, y2) = xy2
        one_cut = self.cutout_window(x1, y1, window)
        two_cut = self.cutout_window(x2, y2, window)

        print("---cutout a . %s seconds  ---" % (time.time() - start_time))
        print 'one_shape,two_shape ', one_cut.shape, two_cut.shape
        one_cut, two_cut = self.fix_cutout_size(one_cut, two_cut)

        one_cut = one_cut - np.mean(one_cut)
        two_cut = two_cut - np.mean(two_cut)
        print 'new dimensions ', one_cut.shape, two_cut.shape
        print("---cutout ended. %s seconds  ---" % (time.time() - start_time))

        corrmat, corrval, dx_pix, dy_pix = self._cross_correlation_shift(
            one_cut, two_cut)

        #convert dy_pix and dx_pix into microns
        dy_um = dy_pix * pixsize
        dx_um = dx_pix * pixsize
        #pack up the shifts into tuples
        dxy_pix = (dx_pix, dy_pix)
        dxy_um = (dx_um, dy_um)

        print("---correlation ended. %s seconds  ---" %
              (time.time() - start_time))
        print "(correlation,(dx,dy))=  ",
        print(corrval, dxy_pix)

        #paint the patch around the first point in its axis, with a box of size of the two_cut centered around where we found it
        self.paintImageOne(one_cut, xy=xy1, dxy_pix=dxy_pix)
        #paint the patch around the second point in its axis
        self.paintImageTwo(two_cut,
                           xy=xy2,
                           xyp=(xy2[0] - dx_um, xy2[1] - dy_um))
        #paint the correlation matrix in its axis
        self.paintCorrImage(corrmat, dxy_pix)

        print("---painting ended %s seconds ---" % (time.time() - start_time))
        return (corrval, dxy_um)

    def explore_match(self, img1, kp1, img2, kp2, status=None, H=None):
        h1, w1 = img1.shape[:2]
        h2, w2 = img2.shape[:2]
        vis = np.zeros((max(h1, h2), w1 + w2), np.uint8)
        vis[:h1, :w1] = img1
        vis[:h2, w1:w1 + w2] = img2
        vis = cv2.cvtColor(vis, cv2.COLOR_GRAY2BGR)

        if H is not None:
            corners = np.float32([[0, 0], [w1, 0], [w1, h1], [0, h1]])
            corners = np.int32(
                cv2.perspectiveTransform(corners.reshape(1, -1, 2), H).reshape(
                    -1, 2) + (w1, 0))
            cv2.polylines(vis, [corners], True, (255, 255, 255))

        if status is None:
            status = np.ones(len(kp1), np.bool_)
        p1 = np.int32([kpp.pt for kpp in kp1])
        p2 = np.int32([kpp.pt for kpp in kp2]) + (w1, 0)

        green = (0, 255, 0)
        red = (0, 0, 255)
        white = (255, 255, 255)
        kp_color = (51, 103, 236)
        for (x1, y1), (x2, y2), inlier in zip(p1, p2, status):
            if inlier:
                col = green
                cv2.circle(vis, (x1, y1), 2, col, -1)
                cv2.circle(vis, (x2, y2), 2, col, -1)
            else:
                col = red
                r = 2
                thickness = 3
                cv2.line(vis, (x1 - r, y1 - r), (x1 + r, y1 + r), col,
                         thickness)
                cv2.line(vis, (x1 - r, y1 + r), (x1 + r, y1 - r), col,
                         thickness)
                cv2.line(vis, (x2 - r, y2 - r), (x2 + r, y2 + r), col,
                         thickness)
                cv2.line(vis, (x2 - r, y2 + r), (x2 + r, y2 - r), col,
                         thickness)
        vis0 = vis.copy()
        for (x1, y1), (x2, y2), inlier in zip(p1, p2, status):
            if inlier:
                cv2.line(vis, (x1, y1), (x2, y2), green)
            else:
                cv2.line(vis, (x1, y1), (x2, y2), red)
        return vis

    def align_by_sift(self, xy1, xy2, window=70, SiftSettings=SiftSettings()):
        """take two points in the image, and calculate SIFT features image around those two points
        cutting out size window

        keywords)
        xy1) a (x,y) tuple specifying point 1, the point that should be fixed
        xy2) a (x,y) tuple specifiying point 2, the point that should be moved
        window) the size of the patch to cutout (+/- window around the points) for calculating the correlation (default = 70 um)


        returns) (maxC,dxy_um)
        maxC)the maximal correlation measured
        dxy_um) the (x,y) tuple which contains the shift in microns necessary to align point xy2 with point xy1

        """
        print "starting align by sift"
        pixsize = self.imgCollection.get_pixel_size()
        #cutout the images around the two points
        (x1, y1) = xy1
        (x2, y2) = xy2
        one_cut = self.cutout_window(x1, y1, window)
        two_cut = self.cutout_window(x2, y2, window)

        #one_cuta=np.minimum(one_cut*256.0/self.maxvalue,255.0).astype(np.uint8)
        #two_cuta=np.minimum(two_cut*256.0/self.maxvalue,255.0).astype(np.uint8)
        one_cuta = np.copy(one_cut)
        two_cuta = np.copy(two_cut)

        one_cuta = cv2.equalizeHist(one_cuta)
        two_cuta = cv2.equalizeHist(two_cuta)

        sift = cv2.SIFT(nfeatures=SiftSettings.numFeatures,
                        contrastThreshold=SiftSettings.contrastThreshold)
        kp1, des1 = sift.detectAndCompute(one_cuta, None)
        kp2, des2 = sift.detectAndCompute(two_cuta, None)
        print "features1:%d" % len(kp1)
        print "features2:%d" % len(kp2)

        #img_one = cv2.drawKeypoints(one_cut,kp1)
        #img_two = cv2.drawKeypoints(two_cut,kp2)

        #image2=self.two_axis.imshow(img_two)

        # FLANN parameters
        FLANN_INDEX_KDTREE = 0
        index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
        search_params = dict(checks=50)  # or pass empty dictionary

        flann = cv2.FlannBasedMatcher(index_params, search_params)

        matches = flann.knnMatch(des1, des2, k=2)

        # Need to draw only good matches, so create a mask
        matchesMask = np.zeros(len(matches))

        kp1matchIdx = []
        kp2matchIdx = []
        # ratio test as per Lowe's paper
        for i, (m, n) in enumerate(matches):
            if m.distance < 0.9 * n.distance:
                kp1matchIdx.append(m.queryIdx)
                kp2matchIdx.append(m.trainIdx)

        p1 = np.array([kp1[i].pt for i in kp1matchIdx])
        p2 = np.array([kp2[i].pt for i in kp2matchIdx])
        # p1c = [pt-np.array[window,window] for pt in p1]
        # p2c = [pt-np.array[window,window] for pt in p2]
        kp1m = [kp1[i] for i in kp1matchIdx]
        kp2m = [kp2[i] for i in kp2matchIdx]
        #print "kp1matchshape"
        #print matchesMask
        #print len(kp1match)
        #print len(kp2match)

        #draw_params = dict(matchColor = (0,255,0),
        #                   singlePointColor = (255,0,0),
        #                   matchesMask = matchesMask,
        #                   flags = 0)

        #img3 = cv2.drawMatches(one_cut,kp1,two_cut,kp2,matches,None,**draw_params)

        transModel = ransac.RigidModel()
        bestModel, bestInlierIdx = ransac.ransac(p1,
                                                 p2,
                                                 transModel,
                                                 2,
                                                 300,
                                                 20.0,
                                                 3,
                                                 debug=True,
                                                 return_all=True)

        if bestModel is not None:

            the_center = np.array(
                [[one_cut.shape[0] / 2, one_cut.shape[1] / 2]])
            trans_center = transModel.transform_points(the_center, bestModel)
            offset = the_center - trans_center
            xc = x2 + offset[0, 0] * pixsize
            yc = y2 - offset[0, 1] * pixsize

            #newcenter=Line2D([trans_center[0,0]+one_cut.shape[1]],[trans_center[0,1]],marker='+',markersize=7,markeredgewidth=1.5,markeredgecolor='r')
            #oldcenter=Line2D([the_center[0,0]],[the_center[0,1]],marker='+',markersize=7,markeredgewidth=1.5,markeredgecolor='r')

            dx_um = -bestModel.t[0] * pixsize
            dy_um = -bestModel.t[1] * pixsize

            print "matches:%d" % len(kp1matchIdx)
            print "inliers:%d" % len(bestInlierIdx)
            print('translation', bestModel.t)
            print('rotation', bestModel.R)

            mask = np.zeros(len(p1), np.bool_)
            mask[bestInlierIdx] = 1

            #img3 = self.explore_match(one_cuta,kp1m,two_cuta,kp2m,mask)
            #self.corr_axis.cla()
            #self.corr_axis.imshow(img3)
            #self.corr_axis.add_line(newcenter)
            #self.corr_axis.add_line(oldcenter)
            #self.repaint()

            #self.paintImageOne(img_one,xy=xy1)
            #paint the patch around the second point in its axis
            #self.paintImageTwo(img_two,xy=xy2)
            #paint the correlation matrix in its axis
            #self.paintCorrImage(corrmat, dxy_pix,skip)

            print(dx_um, dy_um)

            self.paintImageOne(one_cut, xy=xy1)
            self.paintImageTwo(two_cut, xy=xy2, xyp=(x2 - dx_um, y2 - dy_um))

            return ((dx_um, dy_um), len(bestInlierIdx))
        else:
            print "no model found"
            self.paintImageOne(one_cut, xy=xy1)
            self.paintImageTwo(two_cut, xy=xy2)

            return ((0.0, 0.0), 0)

    def paintPointsOneTwo(self, xy1, xy2, window=None):
        (x1, y1) = xy1
        (x2, y2) = xy2
        print "getting p1 window at ", x1, y1
        print "getting p2 window at ", x2, y2
        fw, fh = self.imgCollection.get_image_size_um()
        if window is None:
            min_dim = min(fw, fh)
            window = min_dim * .8 / 2

        one_cut = self.cutout_window(x1, y1, window)
        two_cut = self.cutout_window(x2, y2, window)
        self.paintImageOne(one_cut, xy1)
        #paint the patch around the second point in its axis
        self.paintImageTwo(two_cut, xy2)

    def make_preview_stack(self, xpos, ypos, width, height, directory):
        print "make a preview stack"

        hw_pix = int(round(width * .5 / self.orig_um_per_pix))
        hh_pix = int(round(height * .5 / self.orig_um_per_pix))
        queue = Queue.Queue()

        #spawn a pool of threads, and pass them queue instance
        for i in range(4):
            t = ImageCutThread(queue)
            t.setDaemon(True)
            t.start()

        for i in range(len(self.mosaicArray.xpos)):
            (cx_pix, cy_pix) = self.convert_pos_to_ind(xpos[i], ypos[i])
            rect = [
                cx_pix - hw_pix, cy_pix - hh_pix, cx_pix + hw_pix,
                cy_pix + hh_pix
            ]
            queue.put((self.imagefile, rect, i))
        queue.join()