def _fit_2d_gaussian(subimage, implementation='agpy'): """ Fit 2D Gaussian to a 5x5 pixel area. This is a function internal to pflib that abstracts away the specific methods used to fit. See fit_psf for the description of the 2D Gaussian model, parameters, and return values. Arguments: subimage: 5x5 Numpy array of pixels to fit. algorithm: The name of the fitter to use. Currently, only agpy is supported. Returns: (h_0, w_0, H, A, sigma_h, sigma_w, theta, fit_img) """ assert subimage.shape[0] == 5 and subimage.shape[1] == 5 if implementation != 'agpy': raise NotImplementedError("Currently, only agpy is supported.") #agpy.gaussfit parameters -- #(H(eight), A(mplitude), h_0, w_0, sigma_h, sigma_w, theta) #initial fitting parameters based on emperical eyeball shufti ((H, A, h_0, w_0, sigma_h, sigma_w, theta), fit_img) = \ agpy.gaussfit(subimage, params=(np.mean(subimage), np.amax(subimage), 2.5, 2.5, 1, 1, 0), return_all=False, limitedmin=[True] * 7, limitedmax=[False, False, True, True, True, True, True], minpars=np.array([0.00, (np.amax(subimage) - np.mean(subimage)) / 3.0, 2.00, 2.00, 0.75, 0.75, 0.00]), maxpars=np.array([0.00, 0.00, 3.00, 3.00, 2.00, 2.00, 360.00]), returnfitimage=True) return (h_0, w_0, H, A, sigma_h, sigma_w, theta, fit_img)
def fit_peaks(images, image_tuples=None, correlation_matrix=None, median_diameter=5, r_2_threshold=0.5, c_std=4, consolidation_radius_sq=4**2, child_number=0, child_priority_increment=0, silent=False): """ The core function of the algorithm. In each image, finds all peaks and evaluates their resemblance to a Gaussian PSF model. """ #renice os.nice(child_priority_increment) #generate empty image metadata tuples if empty if image_tuples is None: if not silent: logging.info('pflib.fit_peaks child number ' + str(child_number) + ': no image_tuples metadata passed, using empty ' + 'tuples') image_tuples = [() for t in images] elif len(image_tuples) != len(images): if not silent: logging.info('pflib.fit_peaks child number ' + str(child_number) + ': number of image metadata tuples does not match number of ' + 'images, using empty tuples') image_tuples = [() for t in images] #perform squareroot just this once for the entire function call consolidation_box = np.ceil(math.sqrt(consolidation_radius_sq)) #copy images to prevent tampering with originals by any function called images = [np.copy(image).astype(np.int64) for image in images] images_s3 = images if not silent: logging.info("pflib.fit_peaks child number " + str(child_number) + ": copied images") sys.stdout.flush() #TODO:the following line does not check that images remain within bounds as #spalttiefpass and gaussian may increase positive values #subtract median filter images_s3 = \ [np.subtract(image, np.minimum(spf.median_filter(image, median_diameter), image)) for image in images_s3] if not silent: logging.info("pflib.fit_peaks child number " + str(child_number) + ": applied median filter") sys.stdout.flush() #perform correlation if correlation_matrix is not None: images_s3 = [np.maximum(scipy.signal.correlate(image, correlation_matrix, mode='same'), np.zeros_like(image)).astype(np.int64) for image in images] #if not silent: # logging.info("pflib.fit_peaks child number " + str(child_number) + # ": applied correlation filter") #sys.stdout.flush() consolidated_peak_stack = [[] for image in images] for i, image in enumerate(images_s3): start_time = time.clock() #number of fit attempts fit_counter = 0 #no dictionary comprehensions in python 2.6 :( #pixel_bins = {(h,w): [] for h in range(image.shape[0]) # for w in range(image.shape[1])} #pixel_bins = dict([((h,w), []) for h in range(image.shape[0]) # for w in range(image.shape[1])]) pixel_bins = [[[] for w in range(image.shape[1])] for h in range(image.shape[0])] #threshold beneath which a pixel needs not to be checked candidate_threshold = np.mean(image) + c_std * np.std(image) for h, w in itertools.product(range(image.shape[0]), range(image.shape[1])): #keep track of progress within each image peak_time = time.clock() if (not 1 < h < image.shape[0] - 2 or not 1 < w < image.shape[1] - 2): continue if image[h, w] < candidate_threshold: continue fit_counter = fit_counter + 1 subimage = images[i][h - 2:h + 3, w - 2:w + 3] #agpy.gaussfit parameters -- #(height, amplitude, x, y, width_x, width_y, rota) ((fit, fit_err), fit_image) = \ agpy.gaussfit(subimage, params=(np.mean(subimage), np.amax(subimage), 2.5, 2.5, 1, 1, 0), return_all=True, limitedmin = [True] * 7, limitedmax = [False, False, True, True, True, True, True], minpars=np.array([0.00, (np.amax(subimage) - np.mean(subimage)) / 3.0, 2.00, 2.00, 0.75, 0.75, 0.00]), maxpars=np.array([0.00, 0.00, 3.00, 3.00, 2.00, 2.00, 360.00]), returnfitimage=True) #rmse rmse = math.sqrt( sum([(subimage[x,y] - fit_image[x,y])**2 for x,y in itertools.product(range(5),range(5))]) / 9.0) #coefficient of determination r**2 r_2 = (1.0 - sum(np.reshape((subimage - fit_image)**2, -1)) / sum((np.reshape(subimage, -1) - np.mean(subimage))**2)) if r_2 < r_2_threshold: continue #Illumina S/N = #([peak pixel] - [mean of 16 outer edge pixels]) / # (std[16 outer pixels]) op = [subimage[x, y] for x in range(subimage.shape[0]) for y in range(subimage.shape[1]) if x == 0 or x == subimage.shape[0] - 1 or y == 0 or y == subimage.shape[1] - 1] s_n = (np.amax(subimage) - np.mean(op)) / np.std(op) #peak fitting result tuple #(0 1 2-- 3------ 4------- 5-------- 6--- 7--, 8--) #(h, w, fit, fit_err, subimage, fit_image, rmse, r_2, s_n) #agpy.gaussfit parameters -- # 0----- 1-------- 2 3 4------ 5------ 6--- #(height, amplitude, x, y, width_x, width_y, rota) peak = (fit[2] + h - 2, fit[3] + w - 2, fit, fit_err, subimage, fit_image, rmse, r_2, s_n) #for all fits, consolidate them into the best one within a radius; #check peaks in the consolidated peak stack #default is that this is a new peak if not (0 <= peak[0] <= image.shape[0] and 0 <= peak[1] <= image.shape[1]): continue (pixel_bins[int(np.around(peak[0]))] [int(np.around(peak[1]))].append(peak)) if (not silent and (h + w) % int(image.shape[0] * image.shape[1] / 1000) == 0): logging.info("pflib.fitpeaks child number " + str(child_number) + ": done fitting at h,w " + str((h,w)) + " in " + str(time.clock() - peak_time) + " sec; layer " + str(i) + ' of ' + str(len(images_s3)) + "; cumulative time " + str(time.clock() - start_time) + "; fit_counter " + str(fit_counter)) sys.stdout.flush() consolidation_time = time.clock() for h, w in itertools.product(range(image.shape[0]), range(image.shape[1])): if pixel_bins[h][w]: pixel_bins[h][w] = max(pixel_bins[h][w], key=lambda x: x[7]) else: pixel_bins[h][w] = None for h, w in itertools.product(range(image.shape[0]), range(image.shape[1])): if not pixel_bins[h][w]: continue local_max = True for j, q in itertools.product( range(int(max(h - consolidation_box - 2, 0)), int(min(h + consolidation_box + 3, image.shape[0]))), range(int(max(w - consolidation_box - 2, 0)), int(min(w + consolidation_box + 3, image.shape[1])))): if not pixel_bins[j][q] or (j == h and q == w): continue if ((pixel_bins[j][q][0] - pixel_bins[h][w][0])**2 + (pixel_bins[j][q][1] - pixel_bins[h][w][1])**2 < consolidation_radius_sq): if pixel_bins[j][q][7] < pixel_bins[h][w][7]: pixel_bins[j][q] = None else: local_max = False if not local_max: pixel_bins[h][w] = None consolidated_peak_stack[i] = \ (image_tuples[i], [pixel_bins[h][w] for h, w in itertools.product(range(image.shape[0]), range(image.shape[1])) if pixel_bins[h][w]]) if not silent: logging.info("pflib.fit_peaks child number " + str(child_number) + ": consolidation accomplished in " + str(time.clock() - consolidation_time) + " sec; " + str(len(consolidated_peak_stack[i][1])) + " peaks remaining of " + str(fit_counter) + " fit_counter") if not silent: logging.info("pflib.fit_peaks child number " + str(child_number) + ": image " + str(i + 1) + " of " + str(len(images_s3)) + " processed in " + str(time.clock() - start_time) + " sec;" + " fit_counter " + str(fit_counter)) return consolidated_peak_stack