def test_roi_invariance(self, psf): """ Tests whether shifts in x and y with multiples of px size lead to the same ROI Args: psf: fixture """ """Setup""" xyz_0 = torch.zeros((1, 3)) steps = torch.arange(-5 * psf.vx_size[0], 5 * psf.vx_size[0], step=psf.vx_size[0]).unsqueeze(1) # step coordinates in x and y direction xyz_x = torch.cat((steps, torch.zeros((steps.size(0), 2))), 1) xyz_y = torch.cat((torch.zeros( (steps.size(0), 1)), steps, torch.zeros((steps.size(0), 1))), 1) """Run""" roi_ref = psf.forward_rois(xyz_0, torch.ones(1)) roi_x = psf.forward_rois(xyz_x, torch.ones_like(xyz_x[:, 0])) roi_y = psf.forward_rois(xyz_y, torch.ones_like(xyz_y[:, 1])) """Assertions""" # make sure that within a 5 x 5 window the values are the same assert tutil.tens_almeq(roi_ref[0, 10:15, 10:15], roi_x[:, 10:15, 10:15]) assert tutil.tens_almeq(roi_ref[0, 10:15, 10:15], roi_y[:, 10:15, 10:15])
def test_mean_filter(self, extractor): """ Args: extractor: fixture as above """ """Some hard coded setups""" x_in = [] x_in.append(torch.randn((1, 1, 64, 64))) x_in.append(torch.zeros((1, 1, 64, 64))) x_in.append( torch.meshgrid( torch.arange(64), torch.arange(64))[0].unsqueeze(0).unsqueeze(0).float()) # excpt outcome expect = [] expect.append(torch.zeros_like(x_in[0])) expect.append(torch.zeros_like(x_in[0])) expect.append(8) """Run""" out = [] for x in x_in: out.append(extractor._mean_filter(x)) """Assertions""" assert test_utils.tens_almeq(out[0], expect[0], 1) # 10 sigma assert test_utils.tens_almeq(out[1], expect[1]) assert test_utils.tens_almeq(out[2][0, 0, 8, :], 8 * torch.ones_like(out[2][0, 0, 0, :]), 1e-4)
def test_xyz_cr_conversion(self, xyz_scr_input, xy_unit, px_size, expct_px, expct_nm): """ Here we test the cramer rao unit conversion. We can reuse the testdata as for the xyz conversion because it does not make a difference for the test candidate. """ """Init and expect warning if specified""" em = emitter.CoordinateOnlyEmitter(torch.rand_like(xyz_scr_input), xy_unit=xy_unit, px_size=px_size) em.xyz_cr = xyz_scr_input**2 """Test the respective units""" if isinstance(expct_px, str) and expct_px == "err": with pytest.raises(ValueError): _ = em.xyz_cr_px else: assert test_utils.tens_almeq(em.xyz_scr_px, expct_px) if isinstance(expct_nm, str) and expct_nm == "err": with pytest.raises(ValueError): _ = em.xyz_cr_nm else: assert test_utils.tens_almeq(em.xyz_scr_nm, expct_nm)
def test_easy_case(self, post): """ Easy case, i.e. isolated active pixels. Args: post: fixture """ """Setup""" p = torch.zeros((2, 1, 32, 32)) out = torch.zeros((2, 5, 32, 32)) p[0, 0, 0, 0] = 0.3 p[0, 0, 0, 2] = 0.4 p[1, 0, 2, 4] = 0.6 p[1, 0, 2, 6] = 0.6 out[0, 2, 0, 0] = 0.3 out[0, 2, 0, 2] = 0.5 out[1, 2, 2, 4] = 1. out[1, 2, 2, 6] = 1.2 """Run""" p_out, feat_out = post._forward_raw_impl(p, out) em_out = post.forward(torch.cat((p, out), 1)) """Assertions""" assert test_utils.tens_almeq(p, p_out) assert test_utils.tens_almeq(out, feat_out) assert isinstance(em_out, emitter.EmitterSet) assert len(em_out) == 2 assert (em_out.prob >= post.em_th).all()
def test_forward_quant(self, loss_impl): """Run and Assert""" # all zero assert (torch.zeros((2, 6, 32, 32)) == loss_impl.forward(*([torch.zeros((2, 6, 32, 32))] * 3))).all() # check ch weight loss_ch = loss.PPXYZBLoss(device=torch.device('cpu'), chweight_stat=(1., 2., 1., 1., 1., 1.)) out = loss_ch.forward(torch.zeros((2, 6, 32, 32)), torch.ones((2, 6, 32, 32)), torch.ones((2, 6, 32, 32))) assert test_utils.tens_almeq(out[:, 2:], torch.ones_like(out[:, 2:])) assert test_utils.tens_almeq(out[:, 1], torch.ones_like(out[:, 1]) * 2)
def test_rescale_noop(self): """ Tests whether the implementation defaults to no-op when no arguments are specified. """ """Setup""" rescaler = scf.AmplitudeRescale() x = torch.rand((2, 3, 64, 64)) """Run""" x_out = rescaler.forward(x.clone()) """Assert""" t_util.tens_almeq(x, x_out)
def test_nms(self, post, aggr, expct): """Setup, Run, Assert""" post.p_aggregation = post.set_p_aggregation(aggr) p = torch.zeros((2, 32, 32)) p[0, 4:6, 4] = 0.5 p[0, 4, 4] = 0.5 p[0, 5, 4] = 0.500001 p[1, 6, 4] = 0.25 p[1, 7, 4] = 0.251 p_out = post._nms(p, post.p_aggregation, 0.2, 0.6) """Assertions""" assert test_utils.tens_almeq(p_out[0, 4:6, 4], torch.tensor(expct[0])) assert test_utils.tens_almeq(p_out[1, 6:8, 4], torch.tensor(expct[1]))
def test_forward_handcrafted(self, targ): """Test a couple of handcrafted cases""" # one emitter outside fov the other one inside em_set = CoordinateOnlyEmitter(torch.tensor([[-50., 0., 0.], [15.1, 19.6, 250.]]), xy_unit='px') em_set.phot = torch.tensor([5., 4.]) out = targ.forward(em_set)[0] # single frame assert tutil.tens_almeq(out[:, 15, 20], torch.tensor([1., 4., 0.1, -0.4, 250.]), 1e-5) assert tutil.tens_almeq(out[:, 16, 20], torch.tensor([0., 4., -0.9, -0.4, 250.]), 1e-5) assert tutil.tens_almeq(out[:, 15, 21], torch.tensor([0., 4., 0.1, -1.4, 250.]), 1e-5)
def test_forward_backward_equal(self, cam_fix): x = torch.rand((32, 3, 64, 64)) * 10000 x_out_cnt = cam_fix.forward(x.clone()) x_out_phot = cam_fix.backward(x_out_cnt.clone()) assert test_utils.tens_almeq(x_out_phot, x_out_cnt)
def test_redefine_reference(self, psf): """ Tests redefinition of reference Args: psf: fixture """ """Assert and test""" xyz = torch.tensor([[15., 15., 0.]]) roi_0 = psf.forward_rois(xyz, torch.ones(1, )) # modify reference psf.__init__(xextent=psf.xextent, yextent=psf.yextent, img_shape=psf.img_shape, ref0=psf.ref0, coeff=psf._coeff, vx_size=psf.vx_size, roi_size=psf.roi_size_px, ref_re=psf.ref0 - torch.Tensor([1., 2., 0.]), device=psf._device) roi_shift = psf.forward_rois(xyz, torch.ones(1, )) assert tutil.tens_almeq(roi_0[:, 5:10, 5:10], roi_shift[:, 4:9, 3:8])
def test_frame_distribution(self): em = emitter.LooseEmitterSet(xyz=torch.Tensor([[1., 2., 3.], [7., 8., 9.]]), intensity=torch.Tensor([1., 2.]), t0=torch.Tensor([-0.5, 3.2]), ontime=torch.Tensor([0.4, 2.]), id=torch.tensor([0, 1]), sanity_check=True, xy_unit='px', px_size=None) """Distribute""" xyz, phot, frame_ix, id = em._distribute_framewise() # sort by id then by frame_ix ix = np.lexsort((id, frame_ix)) id = id[ix] xyz = xyz[ix, :] phot = phot[ix] frame_ix = frame_ix[ix] """Assert""" assert (xyz[0] == torch.Tensor([1., 2., 3.])).all() assert id[0] == 0 assert frame_ix[0] == -1 assert phot[0] == 0.4 * 1 assert (xyz[1:4] == torch.Tensor([7., 8., 9.])).all() assert (id[1:4] == 1).all() assert (frame_ix[1:4] == torch.Tensor([3, 4, 5])).all() assert test_utils.tens_almeq(phot[1:4], torch.tensor([0.8 * 2, 2, 0.2 * 2]), 1e-6)
def test_forward(self, proc): """Setup""" x = torch.tensor([[1., 2., 3.], [4., 5., 6.]]) x_tar = torch.tensor([[6., 5., 4.], [3., 2., 1.]]) """Run""" x_out = proc.forward(x) """Assert""" assert test_utils.tens_almeq(x_out, x_tar)
def test_forward(self, rend, em): rend_cpu = copy.deepcopy(rend) rend_cuda = rend rend_cuda.device = 'cuda:0' assert test_utils.tens_almeq( rend_cuda.forward(em, torch.arange(len(em))), rend_cpu.forward(em, torch.arange(len(em))), 1e-4)
def test_round(self, inv_offset_rescale, offset_rescale): """ Calculate forth and back to check the values. Args: inv_offset_rescale: offset_rescale: """ """Setup""" x = torch.rand(2, 5, 64, 64) inv_derived = offset_rescale.return_inverse( ) # derived inverse from offset """Run""" x_hat = offset_rescale.forward(inv_offset_rescale.forward(x)) assert t_util.tens_almeq(x, x_hat, 1e-6) x_hat = offset_rescale.forward(inv_derived.forward(x)) assert t_util.tens_almeq(x, x_hat, 1e-6)
def test_xyz_conversion(self, xyz_input, xy_unit, px_size, expct_px, expct_nm): """Init and expect warning if specified""" em = emitter.CoordinateOnlyEmitter(xyz_input, xy_unit=xy_unit, px_size=px_size) """Test the respective units""" if isinstance(expct_px, str) and expct_px == "err": with pytest.raises(ValueError): _ = em.xyz_px else: assert test_utils.tens_almeq(em.xyz_px, expct_px) if isinstance(expct_nm, str) and expct_nm == "err": with pytest.raises(ValueError): _ = em.xyz_nm else: assert test_utils.tens_almeq(em.xyz_nm, expct_nm)
def test_roi_drv_cuda_cpu(self, psf, psf_cuda, onek_rois): """ Tests approximate equality of CUDA and CPU implementation for a few ROIs on the derivatives. Args: psf: psf implementation on CPU psf_cuda: psf implementation on CUDA """ xyz, phot, bg, n = onek_rois phot = torch.ones_like(phot) drv_roi_cpu, roi_cpu = psf.derivative(xyz, phot, bg) drv_roi_cuda, roi_cuda = psf_cuda.derivative(xyz, phot, bg) assert tutil.tens_almeq(drv_roi_cpu, drv_roi_cuda, 1e-7) assert tutil.tens_almeq( roi_cpu, roi_cuda, 1e-5) # side output, seems to be a bit more numerially off
def test_forward_drv_chunks(self, psf, ix_low, ix_high): """ Tests whether chunked drv forward returns the same frames as drv forward method Args: psf: fixture """ """Setup""" n = 100 xyz = torch.rand((n, 3)) * 64 phot = torch.ones(n) bg = torch.rand_like(phot) * 100 """Run""" drv_chunk, roi_chunk = psf._forward_drv_chunks(xyz, phot, bg, add_bg=False, chunk_size=2) drv, roi = psf.derivative(xyz, phot, bg, add_bg=False) """Test""" assert tutil.tens_almeq(drv_chunk, drv) assert tutil.tens_almeq(roi_chunk, roi)
def test_crlb(self, psf, onek_rois): """ Tests the crlb calculation Args: psf_cuda: psf fixture (see above) onek_rois: 1k roi fixture (see above) Returns: """ """Setup""" xyz, phot, bg, n = onek_rois alt_inv = torch.pinverse """Run""" crlb, rois = psf.crlb(xyz, phot, bg) crlb_p, _ = psf.crlb(xyz, phot, bg, inversion=alt_inv) """Test""" assert crlb.size() == torch.Size([n, psf.n_par]), "Wrong CRLB dimension." assert (torch.Tensor([.01, .01, .02])**2 <= crlb[:, :3]).all(), "CRLB in wrong range (lower bound)." assert (torch.Tensor([.1, .1, 100])**2 >= crlb[:, :3]).all(), "CRLB in wrong range (upper bound)." diff_inv = (crlb_p - crlb).abs() assert tutil.tens_almeq(diff_inv[:, :2], torch.zeros_like(diff_inv[:, :2]), 1e-4) assert tutil.tens_almeq(diff_inv[:, 2], torch.zeros_like(diff_inv[:, 2]), 1e-1) assert tutil.tens_almeq(diff_inv[:, 3], torch.zeros_like(diff_inv[:, 3]), 1e2) assert tutil.tens_almeq(diff_inv[:, 4], torch.zeros_like(diff_inv[:, 4]), 1e-3) assert rois.size() == torch.Size([n, *psf.roi_size_px ]), "Wrong dimension of ROIs."
def test_forward_sigma(self, post): post.photxyz_sigma_mapping = [5, 6, 7, 8] detection = torch.tensor([[0.1, 0.0], [0.6, 0.05]]).unsqueeze(0).unsqueeze(0) features = torch.tensor([[1., 2.], [3., 4.]]).unsqueeze(0).unsqueeze(0).repeat( 1, 4, 1, 1) features = features * torch.tensor( [1., 2., 3., 4.]).unsqueeze(0).unsqueeze(-1).unsqueeze(-1) sigma = torch.ones((1, 4, 2, 2)) sigma *= torch.arange(1, 5).view(1, -1, 1, 1) sigma /= detection pseudo_net_ouput = torch.cat( (detection, features, sigma, torch.rand_like(detection)), 1) """Run""" emitter_out = post.forward(pseudo_net_ouput) """Assert""" assert isinstance(emitter_out, emitter.EmitterSet), "Output should be an emitter." assert (emitter_out.frame_ix == 0).all() assert (emitter_out.phot.unique() == torch.tensor([1., 3.])).all() assert not torch.isnan(emitter_out.xyz_sig).any( ), "Sigma values for xyz should not be nan." assert not torch.isnan(emitter_out.phot_sig).any( ), "Sigma values for phot should not be nan." assert torch.isnan(emitter_out.bg_sig).all() assert test_utils.tens_almeq( emitter_out.xyz_sig, torch.tensor([[20., 30., 40.], [2 / 0.6, 3 / 0.6, 4 / 0.6]])) assert test_utils.tens_almeq(emitter_out.phot_sig, torch.tensor([10., 1 / 0.6]))
def test_roi_cuda_cpu(self, psf, psf_cuda, onek_rois): """ Tests approximate equality of CUDA vs CPU implementation for a few ROIs Args: psf: psf implementation on CPU psf_cuda: psf implementation on CUDA """ xyz, phot, bg, n = onek_rois phot = torch.ones_like(phot) roi_cpu = psf.forward_rois(xyz, phot) roi_cuda = psf_cuda.forward_rois(xyz, phot) assert tutil.tens_almeq(roi_cpu, roi_cuda, 1e-7)
def test_uniformity(self, structure): """ Tests whether there are approx. equal amount of fluorophores on all frames. Tested with a high number for statistical reasons. This test can fail by statistical means. """ """Setup""" em_gen = emgen.EmitterSamplerBlinking(structure=structure, intensity_mu_sig=(100, 2000), lifetime=2., frame_range=(0, 1000), xy_unit='px', px_size=(1., 1.), density=None, em_avg=10000) """Run""" emitters = em_gen.sample() """Asserts""" bin_count, _ = np.histogram(emitters.frame_ix, bins=np.arange(1002)) bin_count = torch.from_numpy(bin_count) assert test_utils.tens_almeq(bin_count, torch.ones_like(bin_count) * 10000, 2000) # plus minus 1000 assert bin_count.float().mean() == pytest.approx(10000, rel=0.05)
def test_frame_cuda_cpu(self, psf, psf_cuda): """ Tests approximate equality of CUDA vs CPU implementation for a few frames Args: psf: psf fixture, CPU version psf_cuda: psf fixture, CUDA version Returns: """ n = 10000 xyz = torch.rand((n, 3)) * 64 xyz[:, 2] = torch.randn_like(xyz[:, 2]) * 1000 - 500 phot = torch.ones((n, )) frame_ix = torch.randint(0, 500, size=(n, )) frames_cpu = psf.forward(xyz, phot, frame_ix) frames_cuda = psf_cuda.forward(xyz, phot, frame_ix) assert tutil.tens_almeq(frames_cpu, frames_cuda, 1e-7)
def test_derivatives(self, psf, onek_rois): """ Tests the derivate calculation Args: psf_cuda: psf fixture (see above) onek_rois: 1k roi fixture (see above) Returns: """ """Setup""" xyz, phot, bg, n = onek_rois """Run""" drv, rois = psf.derivative(xyz, phot, bg) """Test""" assert drv.size() == torch.Size([n, psf.n_par, *psf.roi_size_px ]), "Wrong dimension of derivatives." assert tutil.tens_almeq(drv[:, -1].unique(), torch.Tensor( [0., 1.])), "Derivative of background must be 1 or 0." assert rois.size() == torch.Size([n, *psf.roi_size_px ]), "Wrong dimension of ROIs."
def test_forward_chunks(self, psf, ix_low, ix_high): """ Tests whether chunked forward returns the same frames as forward method Args: psf: fixture """ """Setup""" n = 100 xyz = torch.rand((n, 3)) * 64 phot = torch.ones(n) frame_ix = torch.randint(-5, 4, size=(n, )) """Run""" out_chunk = psf._forward_chunks(xyz, phot, frame_ix, ix_low, ix_high, chunk_size=2) out_forward = psf.forward(xyz, phot, frame_ix, ix_low, ix_high) """Test""" assert tutil.tens_almeq(out_chunk, out_forward)
def test_forward_sanity(self, evaluator, tp, ref, expt_err, expt_out): """ General forward sanity checks. 1. Both empty sets of emitters 2. Unequal size """ if expt_err and expt_out is not None: raise RuntimeError("Wrong test setup.") """Run""" if expt_err: with pytest.raises(ValueError): _ = evaluator.forward(tp, ref) return else: out = evaluator.forward(tp, ref) """Assertions""" assert isinstance(out, evaluator._return), "Wrong output type" for out_i, expt_i in zip( out[3:], expt_out): # test only the non reduced outputs assert test_utils.tens_almeq(out_i, expt_i, 1e-4)
def test_weight_hard(self, waiter): """ Tests entire weight unit with hard computed values Args: waiter: fixture """ """Setup""" tar_frames = torch.zeros((1, 6, 5, 5)) tar_frames[:, 5] = torch.rand_like(tar_frames[:, 5]) # let bg be non-zero em = emitter.EmitterSet(xyz=torch.tensor([[1., 1., 0], [3., 3., 0.]]), phot=torch.Tensor([1., 5.]), frame_ix=torch.tensor([0, 0]), xy_unit='px') """Run""" mask = waiter.forward(em, tar_frames, 0, 0) """Assertions""" assert (mask[:, 0] == 1.).all(), "p channel must be weight 1" # some zero value assertions applicaple for both const and phot weight assert (mask[:, 1:-1, 2, 2] == 0.).all(), "intersection must be 0" assert (mask[:, 1:-1, 3:, :2] == 0.).all() assert (mask[:, 1:-1, :2, 3:] == 0.).all() if waiter.weight_mode == 'const': assert (mask[:, 5] == 1.).all(), "bg channel must be weight 1" assert (mask[:, 1:-1].unique() == torch.tensor([0., 1])).all(), "in const. mode values must be 0 or 1" if waiter.weight_mode == 'phot': assert (mask[:, 1:-1, :2, :2] == 1.).all(), "CRLB estimate for photon count 1" assert mask[:, 1, 3, 3] == pytest.approx(0.02468, abs=0.0001), "Photon CRLB estimate for count of 5" assert mask[:, 2, 3, 3] == pytest.approx(40.51641, abs=0.0001), "X CRLB estimate" assert mask[:, 3, 3, 3] == pytest.approx(40.51641, abs=0.0001), "Y CRLB estimate" assert mask[:, 4, 3, 3] == pytest.approx(40.51641, abs=0.0001), "Y CRLB estimate" assert test_utils.tens_almeq(mask[:, 5], 1 / tar_frames[:, 5] ** 2.3, 1e-5), "BG CRLB estimate"
def test_operators(self, mm0, mm1): assert tutil.tens_almeq((mm0 + mm1).vals, mm0.vals + mm1.vals) assert tutil.tens_almeq((mm0 + 42.).vals, mm0.vals + 42.) assert tutil.tens_almeq((42. + mm0).vals, mm0.vals + 42.) assert tutil.tens_almeq((mm0 - mm1).vals, mm0.vals - mm1.vals) assert tutil.tens_almeq((mm0 - 42.).vals, mm0.vals - 42.) assert tutil.tens_almeq((42. - mm0).vals, 42. - mm0.vals) assert tutil.tens_almeq((mm0 * mm1).vals, mm0.vals * mm1.vals) assert tutil.tens_almeq((mm0 * 42.).vals, mm0.vals * 42.) assert tutil.tens_almeq((42. * mm0).vals, mm0.vals * 42.) assert tutil.tens_almeq((mm0 / mm1).vals, mm0.vals / mm1.vals) assert tutil.tens_almeq((mm0 / 42.).vals, mm0.vals / 42.) assert tutil.tens_almeq((mm0**2).vals, mm0.vals**2)
def test_forward(self, extractor, bg, em, expect_bg): """Run""" out = extractor.forward(em, bg) """Assertions""" assert test_utils.tens_almeq(out.bg, expect_bg, 1e-4, nan=True)
def test_rescale(self, amp_rescale): x = torch.rand((2, 3, 4, 5)) assert t_util.tens_almeq(amp_rescale.forward(x.clone()), (x - 5.) / 1000.)
def test_reduction(self, evaluator): """ Args: evaluator: """ """Setup, Run and Test""" # mean and std dxyz, dphot, dbg = torch.randn( (250000, 3)), torch.randn(250000) + 20, torch.rand(250000) dxyz_, dphot_, dbg_ = evaluator._reduce(dxyz, dphot, dbg, 'mstd') assert test_utils.tens_almeq(dxyz_[0], torch.zeros((3, )), 1e-2) assert test_utils.tens_almeq(dxyz_[1], torch.ones((3, )), 1e-2) assert test_utils.tens_almeq(dphot_[0], torch.zeros((1, )) + 20, 1e-2) assert test_utils.tens_almeq(dphot_[1], torch.ones((1, )), 1e-2) assert test_utils.tens_almeq(dbg_[0], torch.zeros((1, )) + 0.5, 1e-2) assert test_utils.tens_almeq(dbg_[1], torch.ones((1, )) * 0.2889, 1e-2) # gaussian fit dxyz, dphot, dbg = torch.randn( (250000, 3)), torch.randn(250000) + 20, torch.randn(250000) dxyz_, dphot_, dbg_ = evaluator._reduce(dxyz, dphot, dbg, 'gaussian') assert test_utils.tens_almeq(dxyz_[0], torch.zeros((3, )), 1e-2) assert test_utils.tens_almeq(dxyz_[1], torch.ones((3, )), 1e-2) assert test_utils.tens_almeq(dphot_[0], torch.zeros((1, )) + 20, 1e-2) assert test_utils.tens_almeq(dphot_[1], torch.ones((1, )), 1e-2) assert test_utils.tens_almeq(dbg_[0], torch.zeros((1, )), 1e-2) assert test_utils.tens_almeq(dbg_[1], torch.ones((1, )), 1e-2)