pyfli.simulator.calibration#
Calibrate the pyfli simulator (priors, IRF, detector noise) to experimental data.
- class CalibrationInputs(data_path, detector_type, model_type, irf_path=None, irf_mode='auto', bg_path=None, mask_path=None, reference_path=None, reference_source='auto', reference_alpha='auto', laser_freq_mhz=80.0, bin_radius=0, bit_depth=None, jitter='auto', mono_fraction=None, prior_inflation=1.0, optimize_irf=True, optimize_noise=True, optimize_priors=True, reference_weight=0.05, n_exp_pixels=20000, n_sim_pixels=2000, n_fit_pixels=1500, n_validation_pixels=6000, n_bank_pixels=20000, bank_rounds=3, max_evals_stage1=120, max_evals_stage2=1500, target_coverage=0.99, n_coverage_pixels=20000, max_coverage_iter=10, seed=0, output_dir=None, selection=<factory>)[source]#
Bases:
objectEverything the calibration needs.
- Parameters:
data_path (
str) – Raw 3D decay data (.sdt, .ptu, .tif, .npy, …), loaded viapyfli.io.DataOperations.detector_type (
str) – “continuous” (gated/ICCD-like) or “discrete” (TCSPC photon counting).model_type (
str) – “bi_exponential” or “mono_exponential”.irf_path (
str | None) – Experimental IRF. When omitted the IRF is simulated withpyfli.simulator.IRFGenerator.gaussianExpIRFand its parameters are optimised.mask_path (
str | None) – Boolean mask of sample pixels. When omitted, one is derived automatically.reference_path (
str | None) – Optional parameter maps (AlliGator folder, SPCImage folder, or a pyfli session .npy file / folder) used to initialise and regularise the priors.target_coverage (
float) – Required fraction of experimental pixels inside the simulated distribution for every coverage statistic (raw phasors h=1/2, photons, rising edge, background); seecoverage. After the best-fit calibration the config is widened until this is reached, so that the experiment is a subset of what the simulator produces. 0 keeps the best-fit config.n_coverage_pixels (
int) – Simulated pixels per coverage check.max_coverage_iter (
int) – Maximum number of widening steps.irf_mode (str)
bg_path (str | None)
reference_source (str)
reference_alpha (str)
laser_freq_mhz (float)
bin_radius (int)
bit_depth (int | None)
mono_fraction (float | None)
prior_inflation (float)
optimize_irf (bool)
optimize_noise (bool)
optimize_priors (bool)
reference_weight (float)
n_exp_pixels (int)
n_sim_pixels (int)
n_fit_pixels (int)
n_validation_pixels (int)
n_bank_pixels (int)
bank_rounds (int)
max_evals_stage1 (int)
max_evals_stage2 (int)
seed (int)
output_dir (str | None)
selection (PixelSelectionCriteria)
- selection: PixelSelectionCriteria#
- class CalibrationResult(config: dict, sim_generator_kwargs: dict, state: CalibrationState, mask: ndarray, selection_report: dict, validation: dict, history: DataFrame, output_dir: str, files: dict = <factory>, best_fit: dict = <factory>)[source]#
Bases:
object- Parameters:
- state: CalibrationState#
- class DetectorSimCalibrator(inputs)[source]#
Bases:
object- Parameters:
inputs (CalibrationInputs)
- exception MaskShapeError[source]#
Bases:
ValueErrorThe mask’s spatial shape differs from the decay cube’s.
- class PixelSelectionCriteria(min_signal_photons=50.0, snr_min=5.0, flatness_sigma=5.0, peak_window_bins=None, use_otsu='auto', otsu_min_separability=0.6, hot_pixel_factor=10.0, min_component_size=9, refine_user_mask=False)[source]#
Bases:
objectThresholds that decide whether a pixel carries sample fluorescence or only detector/background noise. See
pixel_selectionfor the tests.- Parameters:
- class PooledCalibrator(session_jsons, output_dir, weighting='equal', n_per_exp=4000, n_validation=20000, target_coverage=0.90, max_flatten=8, flatten_factor=0.8, seed=0, folders=None)[source]#
Bases:
objectBuild one covering simulator config from per-experiment calibrations.
- Parameters:
session_jsons (
dict[str,str]) – experiment name ->calibrated_sim_config.jsonof that experiment.weighting (
str) – “equal” (each experiment counts the same) or “pixels” (by number of sample pixels).target_coverage (
float) – Required fraction of every experiment’s pixels inside the simulated phasor clouds (harmonics 1 and 2). Priors are flattened up tomax_flattentimes to reach it.output_dir (str)
n_per_exp (int)
n_validation (int)
max_flatten (int)
flatten_factor (float)
seed (int)
- calibrate_folder(folder, detector_type, model_type, output_root=None, mask_folder=None, irf_path=None, extensions=DATA_EXTENSIONS, pattern='*', force=False, combine=True, weighting='equal', verify=True, n_verify_pixels=10000, **calibration_kwargs)[source]#
Calibrate each data file in
folderseparately, then combine the calibrations.mask_folder: folder of masks matched to the data files by name (see the module docstring); files without one get an automatic mask.irf_path: one IRF file for all experiments, or None to simulate each experiment’s IRF.calibration_kwargsgo toCalibrationInputsfor every file (e.g.bin_radius,irf_mode,target_coverage, budgets);mask_pathandreference_pathare per-experiment and not accepted here. A file whose calibration fails is reported infailedand left out of the combination.verifychecks, per experiment, that its pixels are covered by its own values and by the whole combined setup (verify_combined()), simulatingn_verify_pixelspixels per check.
- combination_generator(setup, sensor_type, mono_fraction=None)[source]#
pyfli.data_cc.ConfigCombinationGeneratorfor the combined setup (enumerates every combination).- Parameters:
- Return type:
- combine_calibrations(json_paths, output_dir, weighting='equal')[source]#
Build the combined sweep setup from per-experiment
calibrated_sim_config.jsonfiles (name -> path) and write it tooutput_dirascombined_sim_setup.json,combined_sim_setup.py(same constants, importable) andirfs/<name>.txt.weighting: “equal” (every experiment counts the same) or “pixels” (by number of sample pixels). Returns the setup dict.
- coverage_fractions(exp_stats, sim_stats, q=1e-3)[source]#
Fraction of experimental pixels inside the region populated by the simulation, per statistic.
- covered_pixels(exp_stats, sim_stats, q=1e-3)[source]#
Per experimental pixel and statistic: is it inside the region holding
1 - qof the simulated pixels? Raw phasors use the 2D highest-density region (hdr_covered()); photons, edge and background use the central[q, 1 - q]quantile range.
- detect_clusters(decay, mask, max_components=4, separation=2.0, min_fraction=0.05, use_intensity=False, n_fit=20000, smooth=True, seed=0, max_dip_ratio=0.5)[source]#
Find well-separated pixel populations inside
mask; see the module docstring.Two mixture components stay separate only if Ashman’s D is at least separation and the pixel density projected onto the line joining their means has a real dip between them (
_dip_ratio()below max_dip_ratio). A mixture fitted to a few hundred pixels of one skewed population can reach D > 2 by chance, but its projected density has no dip (ratio ~0.7-1), while separate populations give ~0.
- ensure_coverage(state, ctx, exp_stats, exp_counts, target=0.99, n_sim=20000, max_iter=10, seed=0)[source]#
Widen
stateuntil every statistic inCOVERAGE_STATScovers at leasttargetof the experimental pixels (ormax_iterwidening steps were made).Returns
(state, trace, coverage, sim, sim_stats);tracehas one row per iteration with the coverage of each statistic, the distance to the experiment and the widening applied.- Parameters:
state (CalibrationState)
ctx (ObjectiveContext)
exp_stats (dict)
exp_counts (ndarray)
target (float)
n_sim (int)
max_iter (int)
seed (int)
- Return type:
- find_data_files(folder, extensions=DATA_EXTENSIONS, pattern='*')[source]#
Raw decay files directly inside
folder(not in sub-folders), sorted by name.
- find_session_configs(root, pattern='*')[source]#
name -> calibrated_sim_config.json for every calibrated experiment under
root.
- load_calibrated_generator(json_path)[source]#
Rebuild the calibrated
SimGeneratorfromcalibrated_sim_config.json(and thecalibrated_irf.npynext to it). Use it as:gen = load_calibrated_generator(".../calibrated_sim_config.json") sample = gen.simulate_once() # dict with decay, irf, tau1, tau2, alpha1, ...
- Parameters:
json_path (str)
- Return type:
- load_combined_setup(path)[source]#
Read
combined_sim_setup.jsonback, restoring the tuples the simulator expects.
- load_experimental_data(data_path, irf_path=None, bg_path=None, mask_path=None, bin_radius=0)[source]#
Load the 3D decay cube (and optional IRF / mask) with
pyfli.io.DataOperationsand optionally apply pyfli’s spatial binning (FLIBinner). Binning a provided mask is not needed: it stays in the original pixel grid.
- load_reference_maps(path, source='auto', alpha_kind='auto')[source]#
Load optional parameter maps from AlliGator (
*Map.txtfolder), SPCImage (*.ascfolder) or pyfli (a session.npyfile, or a session folder in which case the first match ofPYFLI_REFERENCE_PATTERNSis used).alpha_kindsays whether the source’s fraction is an intensity fraction (pyfli, AlliGatorf1_i) or an amplitude fraction (SPCImagea1[%], AlliGatorf1_a).- Parameters:
- Return type:
- load_session_calibration(json_path, name='', folder='')[source]#
Rebuild the calibrated
CalibrationStatefrom acalibrated_sim_config.json.- Parameters:
- Return type:
- make_combined_generator(setup_path, run=0, mono_fraction=None, seed=None, exact_ranges=True)[source]#
Simulator for one IRF run of the combined setup:
gen.simulate_once()draws a combination of the calibrated values and simulates one pixel withIRF_RUNS[run].exact_rangesuses that IRF’s own calibrateda_range/b_rangeand inactive time bins (IRF_RUN_INFO); otherwise the globalA_RANGE/B_RANGE.
- match_masks(data_files, mask_folder, extensions=MASK_EXTENSIONS, suffixes=MASK_SUFFIXES)[source]#
data file -> mask filefor every data file whose name matches a mask inmask_folder: the mask’s name (without extension) is the data file’s name plus one ofsuffixes(case-insensitive). With several candidates, the exact name wins. Data files without a mask, and masks without a data file, are logged as warnings.
- select_signal_pixels(decay, user_mask=None, criteria=None, adc_ceiling=None)[source]#
Returns
(mask, report).reportrecords how many pixels each test removed, the background bins and the per-bin background level, for the calibration log.
- simulate_image(json_path, shape=(8, 8), seed=0)[source]#
Simulate an
(H, W)image with the calibrated simulator. Returns the decay cube(H, W, T), the per-pixel IRF cube and(H, W)ground-truth maps (tau1, tau2, tau, alpha1, Efficiency, photon_count, h_shift, v_shift_bgp).
- simulate_multicluster_tile(labels_tile, cluster_configs, irf_json, detector_type='discrete', sample_mask_tile=None, seed=0)[source]#
pyfli
FLIModelImageGeneratorimage for a cluster label tile, as in the pyfli multi-ROI example:roi_paramsfollowsnp.unique(roi_mask). Label 0 (outside the sample) is background with an empty config and is zeroed by the intensity / bool mask.
- verify_combined(setup_path, n_sim=10000, n_exp=20000, seed=0)[source]#
For every experiment of the combined setup, the coverage (
coverage_fractions()) of its sample pixels byown values: the combination made of that experiment’s sweep values, i.e. its own calibration after re-expression on the common supports;combined: the whole combined distribution (all combinations, with weights).
Both use the experiment’s own IRF run. The experimental pixels are re-loaded from the experiment’s data file with its calibration’s bin radius and sample mask. The table is also written next to the setup as
coverage_per_experiment.csv.
Modules
End-to-end calibration: experimental 3D decay cube -> pyfli simulator config. |
|
Inputs and constants for calibrating the pyfli simulator against an experimental cube. |
|
Check that the experimental data lies within what the calibrated simulator produces. |
|
Load the experimental decay cube, IRF, mask and AlliGator / SPCImage / pyfli maps. |
|
Analytic noise-free expected decay of the pyfli simulator for given lifetimes and IRF. |
|
Calibrate every experiment in a folder, then combine them. |
|
IRF template and shift range for the simulator, from a measured IRF or the data. |
|
Multi-cluster priors and images, following the pyfli multi-ROI example. |
|
Simulation-based calibration of the pyfli simulator config. |
|
Separate sample pixels from pixels that only record detector noise / background. |
|
One simulator config that covers several experiments. |
|
Beta priors for the pyfli simulator config. |
|
Command-line calibration of the pyfli simulator against an experimental decay cube. |
|
Build the pyfli simulator config from calibrated quantities and run SimGenerator. |
|
Model-free per-pixel summary statistics and distances between simulated and experimental decays. |
|
Combine per-experiment calibrations into one sweep-style simulator setup. |