def _im(off, bg_std=14.0): bg_mean = 145 mea = 23 psf = imops.gauss2_rho_form( amp=1.0, std_x=1.5, std_y=1.5, pos_x=mea // 2, pos_y=mea // 2, rho=0.0, const=0.0, mea=mea, ) with synth.Synth(overwrite=True, dim=(mea, mea)) as s: peaks = ( synth.PeaksModelGaussianCircular(n_peaks=1) .amps_constant(val=1_000) .widths_uniform(1.5) ) peaks.locs = [(mea // 2 + off[0], mea // 2 + off[1])] synth.CameraModel(bias=bg_mean, std=bg_std) im = s.render_chcy()[0, 0] - bg_mean return im, psf
def it_finds_sub_pixel_exactly_under_ideal_conditions(): """ Test the helper _sub_pixel_peak_find instead of sub_pixel_peak_find because we don't want to have to reconcile the peak ordering from the synth with the arbitrary order they are found by the peak finder """ with synth.Synth(dim=(512, 512), n_cycles=3) as s: true_n_peaks = 100 peaks = ( synth.PeaksModelGaussianCircular(n_peaks=true_n_peaks) .amps_constant(1000) .widths_uniform(1.5) .locs_grid() .locs_add_random_subpixel() ) s.zero_aln_offsets() chcy_ims = s.render_chcy() chcy_mean_im = np.mean(chcy_ims, axis=(0, 1)) locs = peak_find._pixel_to_subpixel_one_im( chcy_mean_im, HW(peaks.mea, peaks.mea), peaks.locs.astype(int) ) dists = np.linalg.norm(locs - peaks.locs, axis=1) assert np.all(dists < 0.01)
def it_skips_too_oval(): """It doesn't have to work perfectly every time, but pass once in a 10 try loop""" n_fails = 0 for i in range(10): failed = 0 locs = [[64, 54], [64, 70]] with synth.Synth(overwrite=True, dim=(128, 128)) as s: peaks = synth.PeaksModelGaussianCircular( n_peaks=len(locs) ).amps_constant(val=4_000) peaks.std_x = [1.0, 1.0] peaks.std_y = [1.0, 2.0] synth.CameraModel(bias=bg_mean, std=bg_std) peaks.locs = locs im = s.render_chcy()[0, 0] im = im - bg_mean psf, reasons = worker._psf_estimate( im, peaks.locs, mea=17, return_reasons=True ) for loc, reason in zip(peaks.locs, reasons): if loc[1] < 64: if reason[worker.PSFEstimateMaskFields.skipped_too_oval] != 0: failed += 1 else: if reason[worker.PSFEstimateMaskFields.skipped_too_oval] != 1: failed += 1 if failed != 0: n_fails += 1 assert n_fails < 3
def it_find_pixel_accurate(): bg_std = 10 with synth.Synth(dim=(512, 512), n_cycles=3) as s: true_n_peaks = 100 peaks = ( synth.PeaksModelGaussianCircular(n_peaks=true_n_peaks) .amps_constant(1000) .widths_uniform(1.5) .locs_randomize() ) s.zero_aln_offsets() synth.CameraModel(0, bg_std) chcy_ims = s.render_chcy() im = filter_ims.filter_im( chcy_ims[0, 0], low_inflection=0.03, low_sharpness=50.0, high_inflection=0.5, high_sharpness=50.0, ) kernel = approximate_psf() locs = peak_find.pixel_peak_find_one_im(chcy_ims[0, 0], kernel) n_peaks, n_dims = locs.shape assert n_dims == 2 assert n_peaks > 0.80 * true_n_peaks
def _synth_field(n_cycles): with synth.Synth(n_channels=2, n_cycles=n_cycles, dim=dim) as s: peaks = (synth.PeaksModelGaussianCircular( n_peaks=n_peaks).locs_randomize().widths_uniform( psf_width).channel_scale_factor( channel_scale_factor).channel_peak_width_scale_factor( channel_peak_width_scale_factor)) synth.CameraModel(bg_mean=bg_mean, bg_std=bg_std) synth.HaloModel() synth.IlluminationQuadraticFalloffModel() return s, peaks
def _ims(): bg_mean = 145 with synth.Synth(n_channels=2, n_cycles=3, overwrite=True, dim=(256, 256)) as s: ( synth.PeaksModelGaussianCircular(n_peaks=1000) .amps_constant(val=4_000) .locs_randomize() .widths_uniform(1.5) ) synth.CameraModel(bias=bg_mean, std=14) chcy_ims = s.render_chcy() return chcy_ims, s.aln_offsets
def _im(): bg_mean = 145 with synth.Synth(overwrite=True, dim=(512, 512)) as s: ( synth.PeaksModelGaussianCircular(n_peaks=1000) .amps_constant(val=4_000) .locs_randomize() ) synth.CameraModel(bias=bg_mean, std=14) im = s.render_chcy()[0, 0] im = im - bg_mean return im
def _make_image_n_locs(locs): with synth.Synth(overwrite=True, dim=(128, 128)) as s: peaks = ( synth.PeaksModelGaussianCircular(n_peaks=len(locs)) .amps_constant(val=4_000) .widths_uniform(1.5) ) synth.CameraModel(bias=bg_mean, std=bg_std) peaks.locs = locs im = s.render_chcy()[0, 0] im = im - bg_mean return peaks, im
def _synth(im_mea, n_cycles): """ No noise or background to simulate post-filtering images """ dim = (im_mea, im_mea) with synth.Synth(n_channels=1, n_cycles=n_cycles, dim=dim) as s: ( synth.PeaksModelGaussianCircular(n_peaks=int(0.005 * im_mea ** 2)) .uniform_width_and_heights() .amps_constant(5000) .locs_randomize() ) return s.aln_offsets, s.render_chcy()
def _synth(): ch_scale = (1.0, 1.0) ch_aln = np.array([0.0, 0.0, -5.0, -5.0]).reshape((-1, 2)) with synth.Synth(n_channels=2, n_cycles=3, dim=(512, 512)) as s: s.channel_aln_offsets(ch_aln) ( synth.PeaksModelGaussianCircular(n_peaks=500) .uniform_width_and_heights() .amps_constant(5000) .locs_randomize() .channel_scale_factor(ch_scale) ) return s.aln_offsets, s.render_chcy()
def zest_regional_bg_fg_stats(): divs = 5 bg_mean = 100 bg_std = 10 with synth.Synth(overwrite=True) as s: ( synth.PeaksModelGaussianCircular(n_peaks=100) .locs_randomize() .amps_constant(val=10000) ) synth.CameraModel(bias=bg_mean, std=bg_std) im = s.render_chcy()[0, 0] def _check_bg_stats(stats): # Check that bg mean and std are close assert np.all((stats[:, :, 0] - bg_mean) ** 2 < 3 ** 2) assert np.all((stats[:, :, 1] - bg_std) ** 2 < 2 ** 2) def it_returns_stats_regionally(): stats = worker._regional_bg_fg_stats(im, divs=divs) assert stats.shape == (5, 5, 4) _check_bg_stats(stats) def it_varies_divs(): stats = worker._regional_bg_fg_stats(im, divs=10) assert stats.shape == (10, 10, 4) def it_returns_fg_and_bg_ims(): stats, fg_im, bg_im = worker._regional_bg_fg_stats(im, return_ims=True) assert stats.shape == (5, 5, 4) _check_bg_stats(stats) assert fg_im.shape == im.shape assert bg_im.shape == im.shape def it_handles_all_zeros(): im = np.zeros((512, 512)) stats, fg_im, bg_im = worker._regional_bg_fg_stats(im, return_ims=True) assert np.all(stats[:, :, 0] == 0) assert np.all(stats[:, :, 1] == 0) assert np.all(np.isnan(stats[:, :, 2])) assert np.all(np.isnan(stats[:, :, 3])) def it_handles_all_noise(): with synth.Synth(overwrite=True) as s: synth.CameraModel(bias=bg_mean, std=bg_std) im = s.render_chcy()[0, 0] stats, fg_im, bg_im = worker._regional_bg_fg_stats(im, return_ims=True) _check_bg_stats(stats) zest()
def _synth(): ch_scale = (1.0, 1.0) ch_aln = np.array([ [0.0, 0.0], [-5.0, -5.0], ]) with synth.Synth(n_channels=n_channels, n_cycles=3, dim=(im_mea, im_mea)) as s: s.channel_aln_offsets(ch_aln) peaks = (synth.PeaksModelGaussianCircular( n_peaks=500).uniform_width_and_heights().amps_constant( 5000).locs_randomize().channel_scale_factor(ch_scale)) return s.render_chcy(), peaks
def it_finds_peaks_as_they_approach(): for dist in np.linspace(10, 0, 10): with synth.Synth(overwrite=True, dim=(128, 128)) as s: peaks = ( synth.PeaksModelGaussianCircular(n_peaks=2) .amps_constant(val=4_000) .widths_uniform(1.5) ) synth.CameraModel(bias=bg_mean, std=11) peaks.locs[0] = (64, 64 - dist) peaks.locs[1] = (64, 64 + dist) im = s.render_chcy()[0, 0] im = im - bg_mean locs = worker._peak_find(im) if dist > 1.5: # After 1.5 pixels (total of 3 pixels) then they peaks should merge assert locs.shape == (2, 2) assert np.all((locs[:, 0] - 64) ** 2 < 1.5 ** 2)
def _synth_field(fl_i): with synth.Synth(n_channels=n_channels, n_cycles=n_cycles, dim=dim) as s: peaks = (synth.PeaksModelGaussianCircular( n_peaks=n_peaks).locs_randomize().widths_uniform( psf_width).amps_constant(gain)) synth.CameraModel(bg_mean=bg_mean, bg_std=bg_std) synth.HaloModel() synth.IlluminationQuadraticFalloffModel() chcy_ims = s.render_chcy(0) for ch_i in range(chcy_ims.shape[0]): for cy_i in range(chcy_ims.shape[1]): np.save( str(source_folder / f"area_{fl_i:03d}_cell_000_{ch_i:03d}nm_{cy_i:03d}.npy" ), chcy_ims[ch_i, cy_i], )
def it_finds_peaks_as_density_increases(): _expected = [ [100, 88, 10], [125, 117, 10], [150, 134, 20], [175, 151, 25], [200, 172, 25], ] for expected, n_peaks in zip(_expected, np.linspace(100, 200, 5).astype(int)): with synth.Synth(overwrite=True, dim=(256, 256)) as s: peaks = ( synth.PeaksModelGaussianCircular(n_peaks=n_peaks) .amps_constant(val=4_000) .widths_uniform(1.5) .locs_randomize_away_from_edges() ) synth.CameraModel(bias=bg_mean, std=11) im = s.render_chcy()[0, 0] im = im - bg_mean locs = worker._peak_find(im) assert expected[0] == n_peaks assert utils.np_within(expected[1], locs.shape[0], expected[2])
def it_finds_sub_pixel_well_under_typical_conditions(): bg_std = 10 with synth.Synth(dim=(512, 512), n_cycles=3) as s: true_n_peaks = 100 peaks = ( synth.PeaksModelGaussianCircular(n_peaks=true_n_peaks) .amps_constant(1000) .widths_uniform(1.5) .locs_randomize_away_from_edges() ) synth.CameraModel(0, bg_std) s.zero_aln_offsets() chcy_ims = s.render_chcy() chcy_mean_im = np.mean(chcy_ims, axis=(0, 1)) locs = peak_find._pixel_to_subpixel_one_im( chcy_mean_im, HW(peaks.mea, peaks.mea), peaks.locs.astype(int) ) dists = np.linalg.norm(locs - peaks.locs, axis=1) assert (dists < 0.1).sum() > 30 assert (dists < 0.2).sum() > 70
def _make_image(depth, n_peaks=1000): # This table is based on a fitting of the PSF using polymer spheres # s3://erisyon-acquire/Val/ESN/2020/2020_05/ESN_2020_05_21_amb/ESN_2020_21_amb_02_NSdeepredZ # Using the exploration notebook internal/explore/sigproc/psf_with_real_data.ipynb # The depth is set so that 0 microns is optimally in focus z_microns_to_peak_std = np.array( [ [-0.25, 2.187504401090129], [-0.2, 2.063845408774231], [-0.15000000000000002, 2.001985762818282], [-0.1, 1.9433957713576882], [-0.05, 1.9091776019044606], [0.0, 1.8891420470361429], [0.05, 1.8951213618125622], [0.1, 1.9476507707766804], [0.15000000000000002, 2.0169404372571758], [0.2, 2.091394093944999], [0.25, 2.4354449946062364], ] ) # Background parameters based on: # s3://erisyon-acquire/Val/ESN/2020/2020_05/ESN_2020_05_21_amb/ESN_2020_21_amb_01_JSP092Z with synth.Synth(overwrite=True, dim=(1024, 1024)) as s: idx = utils.np_arg_find_nearest(z_microns_to_peak_std[:, 0], depth) std = z_microns_to_peak_std[idx, 1] peaks = ( synth.PeaksModelGaussianCircular(n_peaks=n_peaks) .locs_randomize() .amps_constant(val=4_000) # Amp of 4000 based on fitting of a peptide from: erisyon-acquire/Val/ESN/2020/2020_05/ESN_2020_05_21_amb/ESN_2020_21_amb_01_JSP092Z .widths_uniform(std) ) synth.CameraModel(bias=bg_mean, std=bg_std) im = s.render_chcy()[0, 0] im = im - bg_mean return peaks, im, std
def it_peak_find_very_different_channel_brightnesses_4_channels(): """ Test that as the number of channels increases that the de-duping continues to work """ # fmt: off dyts = [ [[1, 1, 1], [0, 0, 0], [0, 0, 0], [0, 0, 0]], # On in ch 0 [[0, 0, 0], [1, 1, 1], [0, 0, 0], [0, 0, 0]], # On in ch 1 [[0, 0, 0], [0, 0, 0], [1, 1, 1], [0, 0, 0]], # On in ch 2 [[0, 0, 0], [0, 0, 0], [0, 0, 0], [1, 1, 1]], # On in ch 3 [[1, 1, 1], [1, 1, 1], [0, 0, 0], [0, 0, 0]], # Shared 0, 1 [[0, 0, 0], [1, 1, 1], [1, 1, 1], [0, 0, 0]], # Shared 1, 2 [[0, 0, 0], [0, 0, 0], [1, 1, 1], [0, 0, 0]], # Shared 2, 3 [[1, 1, 1], [0, 0, 0], [1, 1, 1], [0, 0, 0]], # Shared 0, 2 # Good enough... ] # fmt: on probs = np.array([10, 10, 10, 10, 3, 3, 3, 3]) probs = probs / np.sum(probs) n_peaks = 500 ch_scale = (1.0, 0.1, 0.1, 0.1) ch_aln = np.array((0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)).reshape((-1, 2)) with synth.Synth(n_channels=4, n_cycles=3, dim=(512, 512)) as s: s.channel_aln_offsets(ch_aln) peaks = ( synth.PeaksModelGaussianCircular(n_peaks=n_peaks) .uniform_width_and_heights() .locs_randomize() .channel_scale_factor(ch_scale) .multichannel_dyt_random_choice(dyts, probs) .gain_constant(5000) ) synth.CameraModel(0, 5) ims = s.render_chcy() approx_psf = approximate_psf() found_locs = peak_find.peak_find_chcy_ims(ims, approx_psf, 0) def _compare(filt_true_locs): dists = cdist(filt_true_locs, found_locs, "euclidean") closest_i = np.argmin(dists, axis=1) closest_d = dists[np.arange(filt_true_locs.shape[0]), closest_i] new_locs_mask = closest_d > 1.5 return filt_true_locs.shape[0], (~new_locs_mask).sum() n_true_ch0, n_found_ch0 = _compare( filt_true_locs=peaks.locs[ (peaks.dyt_iz == 0) | (peaks.dyt_iz == 4) | (peaks.dyt_iz == 7) ] ) assert n_found_ch0 / n_true_ch0 > 0.70 n_true_ch1, n_found_ch1 = _compare( filt_true_locs=peaks.locs[ (peaks.dyt_iz == 1) | (peaks.dyt_iz == 4) | (peaks.dyt_iz == 5) ] ) assert n_found_ch1 / n_true_ch1 > 0.70 n_true_ch2, n_found_ch2 = _compare( filt_true_locs=peaks.locs[ (peaks.dyt_iz == 2) | (peaks.dyt_iz == 5) | (peaks.dyt_iz == 6) | (peaks.dyt_iz == 7) ] ) assert n_found_ch2 / n_true_ch2 > 0.70 n_true_ch3, n_found_ch3 = _compare( filt_true_locs=peaks.locs[(peaks.dyt_iz == 3)] ) assert n_found_ch3 / n_true_ch3 > 0.70
def it_peak_find_very_different_channel_brightnesses_2_channels(): """ Assume the images are already aligned. Synth two channels with 0.2 overlap Ensure that most of the true peaks in each channel are located There will be some collisions so it will not be perfect. """ # fmt: off dyts = [ [[1, 1, 1], [0, 0, 0]], # On in ch 0 [[0, 0, 0], [1, 1, 1]], # On in ch 1 [[1, 1, 1], [1, 1, 1]], # Shared ] # fmt: on frac_shared = 0.20 frac_unshared = (1.0 - frac_shared) / 2 probs = [frac_unshared, frac_unshared, frac_shared] n_peaks = 500 ch_scale = (1.0, 0.2) ch_aln = np.array((0.0, 0.0, 0.0, 0.0)).reshape((-1, 2)) with synth.Synth(n_channels=2, n_cycles=3, dim=(512, 512)) as s: s.channel_aln_offsets(ch_aln) peaks = ( synth.PeaksModelGaussianCircular(n_peaks=n_peaks) .uniform_width_and_heights() .locs_randomize() .channel_scale_factor(ch_scale) .multichannel_dyt_random_choice(dyts, probs) .gain_constant(5000) ) synth.CameraModel(0, 5) ims = s.render_chcy() approx_psf = approximate_psf() found_locs = peak_find.peak_find_chcy_ims(ims, approx_psf, 0) """ In each channel, how many of the TRUE exclusive peaks that were visible in that channel were found? How many of the true shared were found? for each of the three groups: cdist fro true set to the found and answer within 1.5 pixels. peaks.dyt_iz will have 0 (only channel 0), 1 (only ch 1) 2 = both """ def _compare(filt_true_locs): dists = cdist(filt_true_locs, found_locs, "euclidean") closest_i = np.argmin(dists, axis=1) closest_d = dists[np.arange(filt_true_locs.shape[0]), closest_i] new_locs_mask = closest_d > 1.5 return filt_true_locs.shape[0], (~new_locs_mask).sum() n_true_ch0, n_found_ch0 = _compare( filt_true_locs=peaks.locs[(peaks.dyt_iz == 0) | (peaks.dyt_iz == 2)] ) n_true_ch1, n_found_ch1 = _compare( filt_true_locs=peaks.locs[(peaks.dyt_iz == 1) | (peaks.dyt_iz == 2)] ) assert n_found_ch0 / n_true_ch0 > 0.75 assert n_found_ch1 / n_true_ch1 > 0.75