def _hat_masks(hat_rad, brim_rad): """" Returns hat and brim boolean masks. brim_rad is from the center, _not_ in addition to hat_rad """ mea = 2 * brim_rad + 1 hat = imops.generate_circle_mask(hat_rad, mea) brim = imops.generate_circle_mask(brim_rad, mea) brim = brim & ~hat return hat, brim
def render(self, im, fl_i, ch_i, cy_i, aln_offset): super().render(im, fl_i, ch_i, cy_i, aln_offset) blob = imops.generate_circle_mask(self.size, size=self.size * 3) imops.accum_inplace( im, self.amp * blob, XY(0.25 * im.shape[0], 0.25 * im.shape[0]), center=True )
def _align(cy_ims): """ Align a stack of cy_ims by generating simplified fiducial for each cycle (assumes camera does not move between channels) Returns: aln_offsets: list of YX tuples max_score: list of max_score """ kern = _kernel() fiducial_ims = [] for im in cy_ims: med = float(np.nanmedian(im)) im = np.nan_to_num(im, nan=med) fiducial_ims += [imops.convolve(im, kern)] fiducial_ims = np.array(fiducial_ims) - np.median(fiducial_ims) noise_floor = -np.min(fiducial_ims) fiducial_ims = np.where(fiducial_ims < noise_floor, 0, 1).astype(np.uint8) kern = imops.generate_circle_mask(3).astype(np.uint8) fiducial_cy_ims = np.array([ cv2.dilate(im, kern, iterations=1) for im in fiducial_ims ]).astype(float) aln_offsets, aln_scores = imops.align(fiducial_cy_ims) return aln_offsets, aln_scores
def _regional_bg_fg_stats(im, mask_radius=2, divs=5, return_ims=False): """ Using an approximate peak kernel, separate FG and BG regionally and return the statistics. Arguments: im: a single frame mask_radius: Radius in pixels of extra space added around FG candidates divs: Regional divisions (both horiz and vert) return_ims If True, also return the fg_im, bg_im fg_im will have np.nan in all background spaces bg_im will have np.nan in the foreground spaces Returns: array(divs, divs, 4) with the 4 being: (bg_mean, bg_std, fg_mean, fg_std) Optionally returns fg_im, bg_im """ circle = imops.generate_circle_mask(mask_radius).astype(np.uint8) kern = _kernel() cim = imops.convolve(np.nan_to_num(im, nan=np.nanmedian(im)), kern) # cim can end up with artifacts around the nans to the nan_mask # is dilated and splated as zeros back over the im nan_mask = cv2.dilate(np.isnan(im).astype(np.uint8), circle, iterations=1) # The negative side of the convoluted image has no signal # so the std of the symetric distribution (reflecting the # negative side around zero) is a good estimator of noise. if (cim < 0).sum() == 0: # Handle the empty case to avoid warning thresh = 1e10 else: thresh = np.nanstd(np.concatenate((cim[cim < 0], -cim[cim < 0]))) thresh = np.nan_to_num( thresh, nan=1e10) # For nan thresh just make them very large cim = np.nan_to_num(cim) fg_mask = np.where(cim > thresh, 1, 0) fg_im = np.where(fg_mask & ~nan_mask, im, np.nan) fg_mask = cv2.dilate(fg_mask.astype(np.uint8), circle, iterations=1) bg_im = np.where(fg_mask | nan_mask, np.nan, im) def nanstats(dat): if np.all(np.isnan(dat)): return np.nan, np.nan return np.nanmean(dat), np.nanstd(dat) reg_bg_means, reg_bg_stds = imops.region_map(bg_im, nanstats, divs=divs) reg_fg_means, reg_fg_stds = imops.region_map(fg_im, nanstats, divs=divs) stats = np.stack((reg_bg_means, reg_bg_stds, reg_fg_means, reg_fg_stds), axis=2) if return_ims: return stats, fg_im, bg_im else: return stats
def circle_locs(im, locs, inner_radius=3, outer_radius=4, fill_mode="nan"): """ Returns a copy of im with circles placed around the locs. Arguments im: The background image locs: Nx2 matrix of peak locations circle_radius: Radius of circle to draw fill_mode: "nan": Use im and overlay with circles of NaNs "index": zero for all background and the loc index otherwise (This causes the loss of the 0-th peak) style_mode: "donut" Draw a 1 pixel donut "solid": Draw a filled circle This can then be visualized like: circle_im = circle_locs(im, locs, fill_mode="nan") z.im(circle_im, _nan_color="red") """ mea = (outer_radius + 1) * 2 + 1 hat = imops.generate_circle_mask(inner_radius, mea) brim = imops.generate_circle_mask(outer_radius, mea) brim = brim & ~hat if fill_mode == "nan": circle_im = np.zeros_like(im) for loc in locs: imops.set_with_mask_in_place(circle_im, brim, 1, loc=loc, center=True) return np.where(circle_im == 1, np.nan, im) if fill_mode == "index": circle_im = np.zeros_like(im) for loc_i, loc in enumerate(locs): imops.set_with_mask_in_place(circle_im, brim, loc_i, loc=loc, center=True) return circle_im
def it_generates_a_circle_mask_with_even_radius(): circle = imops.generate_circle_mask(2) T = True F = False expected = np.array([ [F, T, T, T, F], [T, T, T, T, T], [T, T, T, T, T], [T, T, T, T, T], [F, T, T, T, F], ]) assert all(circle.flatten() == expected.flatten())
def it_generates_a_circle_mask_with_even_radius_embedded_in_larger_dim(): circle = imops.generate_circle_mask(2, 7) T = True F = False expected = np.array([ [F, F, F, F, F, F, F], [F, F, T, T, T, F, F], [F, T, T, T, T, T, F], [F, T, T, T, T, T, F], [F, T, T, T, T, T, F], [F, F, T, T, T, F, F], [F, F, F, F, F, F, F], ]) assert all(circle.flatten() == expected.flatten())
def circle_locs( im, locs, inner_radius=3, outer_radius=4, fill_mode="nan", vals=None, keep_mask=None, peak_iz=None, ): """ Returns a copy of im with circles placed around the locs. Arguments im: The background image locs: Nx2 matrix of peak locations circle_radius: Radius of circle to draw fill_mode: "nan": Use im and overlay with circles of NaNs "index": zero for all background and the loc index otherwise (This causes the loss of the 0-th peak) "one": zero on background one on foreground "vals": zero on background val[loc_i[ for foreground "peak_iz": peak_iz style_mode: "donut" Draw a 1 pixel donut "solid": Draw a filled circle Notes: This can then be visualized like: circle_im = circle_locs(im, locs, fill_mode="nan") z.im(circle_im, _nan_color="red") """ n_locs = len(locs) if keep_mask is None: keep_mask = np.ones((n_locs, ), dtype=bool) mea = (outer_radius + 1) * 2 + 1 hat = imops.generate_circle_mask(inner_radius, mea) brim = imops.generate_circle_mask(outer_radius, mea) brim = brim & ~hat if fill_mode == "nan": circle_im = np.zeros_like(im) for loc_i, (loc, keep) in enumerate(zip(locs, keep_mask)): if keep: imops.set_with_mask_in_place(circle_im, brim, 1, loc=loc, center=True) return np.where(circle_im == 1, np.nan, im) if fill_mode == "index": circle_im = np.zeros_like(im) for loc_i, (loc, keep) in enumerate(zip(locs, keep_mask)): if keep: imops.set_with_mask_in_place(circle_im, brim, loc_i, loc=loc, center=True) return circle_im if fill_mode == "peak_iz": circle_im = np.zeros_like(im) for peak_i, loc, keep in zip(peak_iz, locs, keep_mask): if keep: imops.set_with_mask_in_place(circle_im, brim, peak_i, loc=loc, center=True) return circle_im if fill_mode == "one": circle_im = np.zeros_like(im) for loc_i, (loc, keep) in enumerate(zip(locs, keep_mask)): if keep: imops.set_with_mask_in_place(circle_im, brim, 1, loc=loc, center=True) return circle_im if fill_mode == "vals": check.array_t(vals, shape=(locs.shape[0], )) circle_im = np.zeros_like(im) for loc_i, (loc, val, keep) in enumerate(zip(locs, vals, keep_mask)): if keep: imops.set_with_mask_in_place(circle_im, brim, val, loc=loc, center=True) return circle_im