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 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])
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()