def it_returns_3_for_rho_one_half(): im = imops.gauss2_rho_form(10, 1.0, 1.0, 8, 8, rho=0.5, const=0, mea=17) ratio = imops.distribution_aspect_ratio(im) assert (ratio - 3.0)**2 < 0.001**2
def it_returns_2_for_x_ellipse(): im = imops.gauss2_rho_form(10, 1.0, 1.5, 8, 8, rho=0.0, const=0, mea=17) ratio = imops.distribution_aspect_ratio(im) assert (ratio - 2.25)**2 < 0.01**2
def it_returns_1_for_circluar(): im = imops.gauss2_rho_form(10, 1.0, 1.0, 8, 8, rho=0.0, const=0, mea=17) ratio = imops.distribution_aspect_ratio(im) assert (ratio - 1.0)**2 < 0.001**2
def _psf_accumulate(im, locs, mea, keep_dist=8, threshold_abs=None, return_reasons=True): """ Given a single im, typically a regional sub-image, accumulate PSF evidence from each locs that meets a set of criteria. Any one image may not produce enough (or any) candidate spots and it is therefore expected that this function is called over a large number of fields to get sufficient samples. Arguments: im: Expected to be a single field, channel, cycle (BG already removed). locs: array (n, 2) in coordinates of im. Expected to be well-separated mea: The peak_measure (must be odd) threshold_abs: The average pixel brightness to accept the peak keep_dist: Pixels distance to determine crowding Returns: psf: ndarray (mea, mea) image reason_counts: An array of masks of why peaks were accepted/rejected See PSFEstimateMaskFields for the columns """ from scipy.spatial.distance import cdist # Defer slow import n_locs = len(locs) dist = cdist(locs, locs, metric="euclidean") dist[dist == 0.0] = np.nan if not np.all(np.isnan(dist)): closest_dist = np.nanmin(dist, axis=1) else: closest_dist = np.zeros(n_locs) # Aligned peaks will accumulate into this psf matrix dim = (mea, mea) dim2 = (mea + 2, mea + 2) psf = np.zeros(dim) n_reason_mask_fields = len(PSFEstimateMaskFields) reason_masks = np.zeros((n_locs, n_reason_mask_fields)) for i, (loc, closest_neighbor_dist) in enumerate(zip(locs, closest_dist)): reason_masks[i, PSFEstimateMaskFields.considered] = 1 # EXTRACT a peak with extra pixels around the edges (dim2 not dim) peak_im = imops.crop(im, off=YX(loc), dim=HW(dim2), center=True) if peak_im.shape != dim2: # Skip near edges reason_masks[i, PSFEstimateMaskFields.skipped_near_edges] = 1 continue if closest_neighbor_dist < keep_dist: reason_masks[i, PSFEstimateMaskFields.skipped_too_crowded] = 1 continue if np.any(np.isnan(peak_im)): reason_masks[i, PSFEstimateMaskFields.skipped_has_nan] = 1 continue # Sub-pixel align the peak to the center assert not np.any(np.isnan(peak_im)) centered_peak_im = sub_pixel_center(peak_im.astype(np.float64)) # Removing ckipping as the noise should cancel out # centered_peak_im = np.clip(centered_peak_im, a_min=0.0, a_max=None) peak_max = np.max(centered_peak_im) if peak_max == 0.0: reason_masks[i, PSFEstimateMaskFields.skipped_empty] = 1 continue if threshold_abs is not None and peak_max < threshold_abs: # Reject spots that are not active reason_masks[i, PSFEstimateMaskFields.skipped_too_dark] = 1 continue r = imops.distribution_aspect_ratio(centered_peak_im) if r > 2.0: reason_masks[i, PSFEstimateMaskFields.skipped_too_oval] = 1 continue # TRIM off the extra now centered_peak_im = centered_peak_im[1:-1, 1:-1] psf += centered_peak_im / np.sum(centered_peak_im) reason_masks[i, PSFEstimateMaskFields.accepted] = 1 n_accepted = np.sum(reason_masks[:, PSFEstimateMaskFields.accepted]) if n_accepted > 0: psf /= np.sum(psf) if return_reasons: return psf, reason_masks return psf