def calibrate(ims_import_res, n_best_fields=6, divs=5, metadata=None, progress=None): if metadata is None: metadata = {} calib = Calibration({f"metadata.instrument": metadata}) qdf = ims_import_res.qualities() quality = qdf.sort_values(["field_i", "channel_i", "cycle_i"])["quality"].values.reshape( (ims_import_res.n_fields, ims_import_res.n_channels, ims_import_res.n_cycles)) best_field_iz = np.argsort(np.sum( quality, axis=(1, 2)))[::-1][0:n_best_fields].tolist() n_cycles = ims_import_res.n_cycles zstack_depths = [ 0 ] * n_cycles # TASK: This will need to come from ims_import_res metadata calib.add({f"zstack_depths.instrument": zstack_depths}) _calibrate(ims_import_res.ims[best_field_iz, :, :], calib, divs=divs, progress=progress) return calib
def it_adds(): c = Calibration( {"p_failure_to_bind_amino_acid.label[C].batch_2020_03_01": 1.0}) c.add({"p_failure_to_attach_to_dye.label[C].batch_2020_03_01": 2.0}) assert len(c) == 2
def _calibrate(flchcy_ims, divs=5, progress=None, overload_psf=None): """ Accumulate calibration data using a set of fields. Arguments: flchcy_ims: frame, channel, cycles ims to be analyzed These are typically only a small subset of high quality fields. NOTE: "Cycles" here are considered to be z-stack slices, NOT chem-cycles. divs: The regional sub-divisions. """ n_fields, n_channels, n_cycles = flchcy_ims.shape[0:3] n_z_slices = n_cycles # This is just an alias to remind me that cycle=z-slice here. peak_mea = 11 peak_dim = (peak_mea, peak_mea) if overload_psf is not None: # This is used for testing peak_dim = overload_psf.shape calib = Calibration() for ch_i in range(n_channels): z_and_region_to_psf = np.zeros((n_z_slices, divs, divs, *peak_dim)) # BACKGROUND # Masks out the foreground and uses remaining pixels to estimate # regional background mean and std. # -------------------------------------------------------------- flcy_calibs = [ _calibrate_bg_and_psf_im(flchcy_ims[fl_i, ch_i, cy_i]) for fl_i in range(n_fields) for cy_i in range(n_cycles) ] calib.add({ f"regional_bg_mean.instrument_channel[{ch_i}]": np.mean([ np.array( flcy_calibs[f"regional_bg_mean.instrument_channel[{ch_i}]"] ) for c in flcy_calibs ]) }) # reg_psfs = np.sum([ # np.array(c[f"regional_psf_zstack.instrument_channel[{ch_i}]"]) # for c in flcy_calibs # ], axis=(2, 3)) # # denominator = np.sum(z_and_region_to_psf, axis=(2, 3))[:, :, None, None] # calib.add({ # f"regional_psf_zstack.instrument_channel[{ch_i}]": reg_psfs / # }) # # z_and_region_to_psf = utils.np_safe_divide(z_and_region_to_psf, denominator) # # calib.add({ # f"regional_bg_std.instrument_channel[{ch_i}]": np.mean([ # np.array(c[f"regional_bg_std.instrument_channel[{ch_i}]"]) # for c in flcy_calibs # ]) # }) # if overload_psf is not None: # # This is used for testing # z_and_region_to_psf = np.broadcast_to( # overload_psf, (n_z_slices, divs, divs, *peak_dim) # ).tolist() # # else: # # PSF # # Accumulate the PSF regionally over every field # # Then divide each PSF though by it's own mass so that the # # AUC under each PSF is 1. # # -------------------------------------------------------------- # [ # _calibrate_bg_im(flchcy_ims[fl_i, ch_i, cy_i], regional_bg_mean, regional_bg_std) # for fl_i in range(n_fields) # for cy_i in range(n_cycles) # ] # # for fl_i in range(n_fields): # # for cy_i in range(n_cycles): # # Remember: cy_i is a pseudo-cycle: it is really a z-slice # # with z_depths[cy_i] holding the actual depth # # regional_bg_mean = np.array( # calib[f"regional_bg_mean.instrument_channel[{ch_i}]"] # ) # _calibrate_psf_im(flchcy_ims[fl_i, ch_i, cy_i], regional_bg_mean) # # # ACCUMULATE each field, will normalize at the end # z_and_region_to_psf[cy_i] += reg_psfs # # NORMALIZE all psfs # denominator = np.sum(z_and_region_to_psf, axis=(3, 4))[:, :, :, None, None] # z_and_region_to_psf = utils.np_safe_divide(z_and_region_to_psf, denominator) # # calib.add( # { # f"regional_psf_zstack.instrument_channel[{ch_i}]": z_and_region_to_psf.tolist() # } # ) # FOREGROUND # Runs the standard sigproc_field analysis (without balancing) # to get the regional radmats for regional histogram balancing. # This requires that the PSF already be estimated so that the # radiometry can run. # -------------------------------------------------------------- # Spoof the sigproc_v2 worker into bypassing illumination balance by giving it all zeros calib.add({ f"regional_illumination_balance.instrument_channel[{ch_i}]": np.ones((divs, divs)).tolist() }) sigproc_params = SigprocV2Params( calibration=calib, instrument_subject_id=None, radiometry_channels=dict(ch=ch_i), ) fl_radmats = [] fl_locs = [] for fl_i in range(n_fields): if progress is not None: progress(fl_i, n_fields) chcy_ims = flchcy_ims[fl_i, ch_i:(ch_i + 1), :] ( chcy_ims, locs, radmat, aln_offsets, aln_scores, ) = sigproc_field(chcy_ims, sigproc_params) fl_radmats += [radmat] fl_locs += [locs] fl_radmat = np.concatenate(fl_radmats) fl_loc = np.concatenate(fl_locs) # BALANCE sig = np.nan_to_num(fl_radmat[:, ch_i, :, 0].flatten()) noi = fl_radmat[:, ch_i, :, 1].flatten() snr = np.nan_to_num(sig / noi) locs = np.tile(fl_loc, (1, n_cycles)).reshape((-1, 2)) snr_mask = snr > 10 sig = sig[snr_mask] locs = locs[snr_mask] top = np.max((locs[:, 0], locs[:, 1])) y = utils.ispace(0, top, divs + 1) x = utils.ispace(0, top, divs + 1) def regional_locs_mask(yi, xi): """Create a mask for locs inside of a region""" mask = (y[yi] <= locs[:, 0]) & (locs[:, 0] < y[yi + 1]) mask &= (x[xi] <= locs[:, 1]) & (locs[:, 1] < x[xi + 1]) return mask medians = np.zeros((divs, divs)) for yi in range(len(y) - 1): for xi in range(len(x) - 1): loc_mask = regional_locs_mask(yi, xi) bright_mask = sig > 2.0 _sig = sig[loc_mask & bright_mask] medians[yi, xi] = np.median(_sig) center = np.max(medians) balance = np.zeros((divs, divs)) for yi in range(len(y) - 1): for xi in range(len(x) - 1): loc_mask = regional_locs_mask(yi, xi) bright_mask = sig > 2.0 _sig = sig[loc_mask & bright_mask] for yi in range(len(y) - 1): for xi in range(len(x) - 1): loc_mask = regional_locs_mask(yi, xi) bright_mask = sig > 2.0 _sig = sig[loc_mask & bright_mask] median = np.median(_sig) balance[yi, xi] = center / median _sig *= balance[yi, xi] calib.add({ f"regional_illumination_balance.instrument_channel[{ch_i}]": balance.tolist() }) return calib