From 9f80a203560cbb991bf565fca353423142e8f8bc Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 18 May 2026 15:41:07 +0200 Subject: [PATCH 01/42] spinna - clean progress dialog + use fitting_mode in compare models --- changelog.md | 6 ++- picasso/gui/spinna.py | 9 ++++ picasso/spinna.py | 107 +++++++++++++++++++++++++++++++++++------- 3 files changed, 104 insertions(+), 18 deletions(-) diff --git a/changelog.md b/changelog.md index 600ecf5a..de309981 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ # Changelog -Last change: 16-MAY-2026 CEST +Last change: 18-MAY-2026 CEST + +## 0.10.1 + +- SPINNA: comparing models uses fitting modes and has cleaner progress dialog ## 0.10.0 diff --git a/picasso/gui/spinna.py b/picasso/gui/spinna.py index d6f1c274..def3f7fe 100644 --- a/picasso/gui/spinna.py +++ b/picasso/gui/spinna.py @@ -3870,6 +3870,14 @@ def compare_models(self) -> None: progress = lib.ProgressDialog( "Comparing models, please wait...", 0, 1, self ) + # keep a single dialog visible across all fitting phases + progress.setAutoClose(False) + progress.setAutoReset(False) + fitting_mode = { + "Coarse to fine": "coarse-to-fine", + "Bayesian": "bayesian", + "Brute force": "brute-force", + }[self.settings_dialog.fitting_mode.currentText()] _, idx, best_label_unc, best_mixer, opt_props = spinna.compare_models( models=models, exp_data=self.exp_data, @@ -3885,6 +3893,7 @@ def compare_models(self) -> None: asynch=self.settings_dialog.asynch_check.isChecked(), savedir=savedir, callback=progress, + fitting_mode=fitting_mode, ) progress.close() diff --git a/picasso/spinna.py b/picasso/spinna.py index fcdaeef5..6035f724 100644 --- a/picasso/spinna.py +++ b/picasso/spinna.py @@ -3334,11 +3334,19 @@ def fit_coarse_to_fine( coarse_idx = self._farthest_point_sampling(proportions, n_coarse) N_coarse = N_structures[coarse_idx] + # keep the caller-supplied title as a prefix so that phase + # labels still carry round/model context + base_title = self.progress_title + coarse_title = ( + f"{base_title} | Coarse pass" if base_title else "Coarse pass" + ) + fine_title = f"{base_title} | Fine pass" if base_title else "Fine pass" + # adjust the progress bar if isinstance(callback, lib.ProgressDialog): callback.setMaximum(n_coarse) - callback.setLabelText("Coarse pass") - self.progress_title = "Coarse pass" + callback.setLabelText(coarse_title) + self.progress_title = coarse_title N_coarse, scores_coarse = self._run_brute_force( N_coarse, asynch, callback ) @@ -3356,8 +3364,8 @@ def fit_coarse_to_fine( if isinstance(callback, lib.ProgressDialog): callback.setMaximum(len(N_fine)) callback.setValue(0) - callback.setLabelText("Fine pass") - self.progress_title = "Fine pass" + callback.setLabelText(fine_title) + self.progress_title = fine_title spinna_results = self.fit_stoichiometry( N_fine, fitting_mode="brute-force", @@ -3462,15 +3470,29 @@ def fit_bayesian( n_initial = min(n_initial, n_total) n_iterations = min(n_iterations, n_total - n_initial) + # keep the caller-supplied title as a prefix so that phase + # labels still carry round/model context + base_title = self.progress_title + init_title = ( + f"{base_title} | Bayesian (initial sampling)" + if base_title + else "Bayesian optimization (initial sampling)" + ) + gp_title = ( + f"{base_title} | Bayesian (GP-guided)" + if base_title + else "Bayesian optimization (GP-guided)" + ) + # --- Phase 1: initial space-filling design --- if isinstance(callback, lib.ProgressDialog): - callback.zero_progress("Bayesian optimization (initial sampling)") + callback.zero_progress(init_title) callback.setMaximum(n_initial) progress_bar = None if callback == "console": progress_bar = tqdm( total=n_initial, - desc="Bayesian optimization (initial sampling)", + desc=init_title, ) init_idx = self._farthest_point_sampling(proportions, n_initial) @@ -3489,12 +3511,12 @@ def fit_bayesian( # --- Phase 2: GP-guided acquisition --- if isinstance(callback, lib.ProgressDialog): - callback.zero_progress("Bayesian optimization (GP-guided)") + callback.zero_progress(gp_title) callback.setMaximum(n_iterations) if callback == "console": progress_bar = tqdm( total=n_iterations, - desc="Bayesian optimization (GP-guided)", + desc=gp_title, ) evaluated, scores, _ = self._bayesian_gp_phase( @@ -3980,6 +4002,11 @@ def _fit_label_unc_for_target( asynch: bool, savedir: str, callback, + fitting_mode: Literal[ + "coarse-to-fine", "bayesian", "brute-force" + ] = "coarse-to-fine", + round_counter: list | None = None, + total_rounds: int | None = None, ) -> float: """Find the best-fit label-uncertainty value for a single target. @@ -4021,9 +4048,10 @@ def _fit_label_unc_for_target( best_score = np.inf best_l_unc = 5.0 - for l_unc_ in l_unc: + for k, l_unc_ in enumerate(l_unc): progress_title = ( - f"Spinning with label uncertainty {l_unc_:.2f} nm for {target}" + f"Fitting label uncertainty for {target}: " + f"{l_unc_:.2f} nm ({k + 1}/{len(l_unc)})" ) label_unc_input = deepcopy(label_unc_input_) label_unc_input[target] = l_unc_ @@ -4044,11 +4072,14 @@ def _fit_label_unc_for_target( savedir=savedir, callback=callback, progress_title=progress_title, + fitting_mode=fitting_mode, + round_counter=round_counter, + total_rounds=total_rounds, )[0] if score < best_score: best_score = score best_l_unc = l_unc_ - return best_l_unc + return float(best_l_unc) def _compute_nn_counts( @@ -4097,6 +4128,9 @@ def compare_models( asynch: bool = True, savedir: str = "", callback: lib.ProgressDialog | Literal["console"] | None = None, + fitting_mode: Literal[ + "coarse-to-fine", "bayesian", "brute-force" + ] = "coarse-to-fine", ) -> tuple[float, int, dict, StructureMixer, lib.FloatArray1D]: """Compare different models, i.e., ``StructureMixer``'s with label uncertainties given the experimental dataset and @@ -4151,6 +4185,15 @@ def compare_models( Progress bar to track fitting progress. If "console", the progress bar is displayed in the console. If None, no progress bar is displayed. Default is None. + fitting_mode : {"coarse-to-fine", "bayesian", "brute-force"}, optional + If "coarse-to-fine", the fitting is done in two steps: first, + a coarse grid of structure combinations is tested, (10% of + evenly distributed structure combinations) and then a finer + grid is tested around the best combination from the coarse + grid. If "bayesian", Bayesian optimization with a Gaussian + Process surrogate is used to efficiently search the space. + If "brute-force", all combinations of structures are + tested sequentially. Default is "coarse-to-fine". Returns ------- @@ -4181,7 +4224,18 @@ def compare_models( # used for the fitting of label uncertainties, each target must be # specified - label_unc_input_ = {target: lunc[0] for target, lunc in label_unc.items()} + label_unc_input_ = { + target: float(lunc[0]) for target, lunc in label_unc.items() + } + + # count the total number of SPINNA rounds (one per fit_stoichiometry + # call) so the progress titles can show "Round X/Y" + n_models = len(models) + total_rounds = n_models # final model comparison + for target in targets: + if len(label_unc[target]) > 1: + total_rounds += len(label_unc[target]) * n_models + round_counter = [0] # First we fit label_unc for each target, where we only focus on the # structures that contain the target. The fitting can be skipped if @@ -4205,6 +4259,9 @@ def compare_models( asynch=asynch, savedir=savedir, callback=callback, + fitting_mode=fitting_mode, + round_counter=round_counter, + total_rounds=total_rounds, ) # test the models with the best fitting label uncertainties; note @@ -4215,7 +4272,7 @@ def compare_models( nn_counts = _compute_nn_counts(targets, models, nn_counts) # compare the models - progress_title = f"Spinning with label uncertainties: {label_unc}" + progress_title = f"Final comparison, label_unc={label_unc}" (best_score, best_idx, best_mixer, best_props) = ( compare_models_given_label_unc( models=models, @@ -4234,6 +4291,9 @@ def compare_models( savedir=savedir, callback=callback, progress_title=progress_title, + fitting_mode=fitting_mode, + round_counter=round_counter, + total_rounds=total_rounds, ) ) return best_score, best_idx, label_unc, best_mixer, best_props @@ -4256,6 +4316,11 @@ def compare_models_given_label_unc( savedir: str = "", callback: lib.ProgressDialog | Literal["console"] | None = None, progress_title: str = "Spinning structures", + fitting_mode: Literal[ + "coarse-to-fine", "bayesian", "brute-force" + ] = "coarse-to-fine", + round_counter: list | None = None, + total_rounds: int | None = None, ) -> tuple[float, int, StructureMixer, lib.FloatArray1D]: """Compare different models, i.e., ``StructureMixer``'s given the experimental dataset, stoichiometries-search-space and label @@ -4342,6 +4407,14 @@ def compare_models_given_label_unc( } for i, model in enumerate(models): + if round_counter is not None and total_rounds is not None: + round_counter[0] += 1 + round_prefix = f"[Round {round_counter[0]}/{total_rounds}] " + else: + round_prefix = "" + spinner_title = ( + f"{round_prefix}{progress_title} | model {i + 1}/{len(models)}" + ) search_space = generate_N_structures(model, N_total, granularity) mixer = StructureMixer( structures=model, @@ -4358,9 +4431,7 @@ def compare_models_given_label_unc( mixer=mixer, gt_coords=exp_data, N_sim=N_sim, - progress_title=( - f"{progress_title} and model nr {i+1}/{len(models)}" - ), + progress_title=spinner_title, ) savepath = "" if savedir: @@ -4376,10 +4447,12 @@ def compare_models_given_label_unc( # adjust the progress dialog if isinstance(callback, lib.ProgressDialog): callback.setMaximum(len(list(search_space.values())[0])) + callback.setLabelText(spinner_title) elif callback == "console": - print(f"Model {i+1}/{len(models)}") + print(spinner_title) opt_props, score = spinner.fit_stoichiometry( N_structures=search_space, + fitting_mode=fitting_mode, save=savepath, asynch=asynch, callback=callback, From 1b23b4b00e19a7bff297d4b435be52b6dcdbcfbd Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 18 May 2026 22:09:32 +0200 Subject: [PATCH 02/42] convenient LE fitting in spinna --- changelog.md | 1 + picasso/gui/spinna.py | 474 ++++++++++++++++++++++++++++++++++++++---- picasso/spinna.py | 171 ++++++++++++++- tests/test_spinna.py | 51 ++++- 4 files changed, 640 insertions(+), 57 deletions(-) diff --git a/changelog.md b/changelog.md index de309981..0b304df5 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ Last change: 18-MAY-2026 CEST ## 0.10.1 - SPINNA: comparing models uses fitting modes and has cleaner progress dialog +- SPINNA: convenient fitting of LE ## 0.10.0 diff --git a/picasso/gui/spinna.py b/picasso/gui/spinna.py index def3f7fe..4da26304 100644 --- a/picasso/gui/spinna.py +++ b/picasso/gui/spinna.py @@ -2083,6 +2083,234 @@ def on_model_clicked(self, path: str) -> None: del self.model_buttons[index] +class FitLEDialog(lib.Dialog): + """Dialog to set up a labeling-efficiency (LE) fit. + + Lets the user optionally fit the per-target label uncertainty and the + heterodimer distance. Both knobs default to a single fixed value (the + user's current spin-box value for label uncertainty, a manual entry + for distance); ticking the corresponding checkbox switches the input + to a From / To / Step grid. + """ + + def __init__(self, sim_tab: SimulationsTab) -> None: + super().__init__(sim_tab) + self.setWindowTitle("Fit labeling efficiency") + self.setModal(True) + self.sim_tab = sim_tab + self.targets = list(sim_tab.targets) + layout = QtWidgets.QVBoxLayout(self) + self.setLayout(layout) + + # LABEL UNCERTAINTY + label_unc_layout = QtWidgets.QGridLayout() + layout.addLayout(label_unc_layout) + self.label_unc_checkbox = QtWidgets.QCheckBox("Fit label uncertainty") + self.label_unc_checkbox.setToolTip( + "Search for the best fitting label uncertainty per target?\n" + "When unchecked, the current values from the Simulations tab" + " are used." + ) + self.label_unc_checkbox.setChecked(False) + self.label_unc_checkbox.toggled.connect(self.on_label_unc_toggled) + label_unc_layout.addWidget(self.label_unc_checkbox, 0, 0, 1, 4) + t_label = QtWidgets.QLabel("Target") + t_label.setToolTip("Name of the molecular target.") + label_unc_layout.addWidget(t_label, 1, 0) + f_label = QtWidgets.QLabel("From") + f_label.setToolTip("Lower bound of label uncertainty (nm).") + label_unc_layout.addWidget(f_label, 1, 1) + to_label = QtWidgets.QLabel("To") + to_label.setToolTip("Upper bound of label uncertainty (nm).") + label_unc_layout.addWidget(to_label, 1, 2) + s_label = QtWidgets.QLabel("Step") + s_label.setToolTip("Iteration step of label uncertainty (nm).") + label_unc_layout.addWidget(s_label, 1, 3) + self.label_unc_from_spins = {} + self.label_unc_to_spins = {} + self.label_unc_step_spins = {} + for i, target in enumerate(self.targets): + from_spin = QtWidgets.QDoubleSpinBox() + from_spin.setRange(0, 20) + from_spin.setDecimals(2) + from_spin.setSingleStep(0.1) + from_spin.setValue(3) + to_spin = QtWidgets.QDoubleSpinBox() + to_spin.setRange(0, 20) + to_spin.setDecimals(2) + to_spin.setSingleStep(0.1) + to_spin.setValue(8) + step_spin = QtWidgets.QDoubleSpinBox() + step_spin.setRange(0.1, 10) + step_spin.setDecimals(2) + step_spin.setSingleStep(0.1) + step_spin.setValue(1.0) + for spin in (from_spin, to_spin, step_spin): + spin.setEnabled(False) + spin.valueChanged.connect(self._update_rounds_preview) + self.label_unc_from_spins[target] = from_spin + self.label_unc_to_spins[target] = to_spin + self.label_unc_step_spins[target] = step_spin + label_unc_layout.addWidget(QtWidgets.QLabel(target), 2 + i, 0) + label_unc_layout.addWidget(from_spin, 2 + i, 1) + label_unc_layout.addWidget(to_spin, 2 + i, 2) + label_unc_layout.addWidget(step_spin, 2 + i, 3) + + # HETERODIMER DISTANCE + distance_layout = QtWidgets.QGridLayout() + layout.addLayout(distance_layout) + self.distance_checkbox = QtWidgets.QCheckBox( + "Fit heterodimer distance" + ) + self.distance_checkbox.setToolTip( + "Search for the best fitting heterodimer distance?\n" + "When unchecked, a single fixed distance is used." + ) + self.distance_checkbox.setChecked(False) + self.distance_checkbox.toggled.connect(self.on_distance_toggled) + distance_layout.addWidget(self.distance_checkbox, 0, 0, 1, 4) + + # single fixed-distance spin (active when checkbox is unchecked) + fixed_label = QtWidgets.QLabel("Distance (nm):") + fixed_label.setToolTip("Fixed heterodimer distance (nm).") + distance_layout.addWidget(fixed_label, 1, 0) + self.distance_fixed_spin = QtWidgets.QDoubleSpinBox() + self.distance_fixed_spin.setRange(0.1, 200) + self.distance_fixed_spin.setDecimals(2) + self.distance_fixed_spin.setSingleStep(0.5) + self.distance_fixed_spin.setValue(10.0) + distance_layout.addWidget(self.distance_fixed_spin, 1, 1, 1, 3) + + # from / to / step (active when checkbox is checked) + distance_layout.addWidget(QtWidgets.QLabel("From"), 2, 1) + distance_layout.addWidget(QtWidgets.QLabel("To"), 2, 2) + distance_layout.addWidget(QtWidgets.QLabel("Step"), 2, 3) + distance_layout.addWidget(QtWidgets.QLabel("Range (nm):"), 3, 0) + self.distance_from_spin = QtWidgets.QDoubleSpinBox() + self.distance_from_spin.setRange(0.1, 200) + self.distance_from_spin.setDecimals(2) + self.distance_from_spin.setSingleStep(0.5) + self.distance_from_spin.setValue(5.0) + self.distance_to_spin = QtWidgets.QDoubleSpinBox() + self.distance_to_spin.setRange(0.1, 200) + self.distance_to_spin.setDecimals(2) + self.distance_to_spin.setSingleStep(0.5) + self.distance_to_spin.setValue(25.0) + self.distance_step_spin = QtWidgets.QDoubleSpinBox() + self.distance_step_spin.setRange(0.1, 50) + self.distance_step_spin.setDecimals(2) + self.distance_step_spin.setSingleStep(0.5) + self.distance_step_spin.setValue(5.0) + for spin in ( + self.distance_from_spin, + self.distance_to_spin, + self.distance_step_spin, + ): + spin.setEnabled(False) + spin.valueChanged.connect(self._update_rounds_preview) + distance_layout.addWidget(self.distance_from_spin, 3, 1) + distance_layout.addWidget(self.distance_to_spin, 3, 2) + distance_layout.addWidget(self.distance_step_spin, 3, 3) + + # SAVE FIT SCORES + self.save_fit_scores = QtWidgets.QCheckBox("Save fit scores") + self.save_fit_scores.setToolTip("Save the fit scores per model?") + self.save_fit_scores.setChecked(False) + layout.addWidget(self.save_fit_scores) + + # ROUNDS PREVIEW + self.rounds_label = QtWidgets.QLabel() + self.rounds_label.setToolTip( + "Number of SPINNA fitting rounds that will be executed, " + "roughly one per label uncertainty/dimer distance." + ) + layout.addWidget(self.rounds_label) + self._update_rounds_preview() + + # OK / CANCEL + self.buttons = QtWidgets.QDialogButtonBox( + QtWidgets.QDialogButtonBox.StandardButton.Ok + | QtWidgets.QDialogButtonBox.StandardButton.Cancel, + QtCore.Qt.Orientation.Horizontal, + self, + ) + layout.addWidget(self.buttons) + self.buttons.accepted.connect(self.accept) + self.buttons.rejected.connect(self.reject) + + def on_label_unc_toggled(self, state: bool) -> None: + for target in self.targets: + self.label_unc_from_spins[target].setEnabled(state) + self.label_unc_to_spins[target].setEnabled(state) + self.label_unc_step_spins[target].setEnabled(state) + self._update_rounds_preview() + + def on_distance_toggled(self, state: bool) -> None: + self.distance_fixed_spin.setEnabled(not state) + self.distance_from_spin.setEnabled(state) + self.distance_to_spin.setEnabled(state) + self.distance_step_spin.setEnabled(state) + self._update_rounds_preview() + + def _compute_distances(self) -> lib.FloatArray1D: + if self.distance_checkbox.isChecked(): + return np.arange( + self.distance_from_spin.value(), + self.distance_to_spin.value() + 0.01, + self.distance_step_spin.value(), + ) + return np.array([self.distance_fixed_spin.value()]) + + def _compute_label_unc(self) -> dict: + label_unc = {} + if self.label_unc_checkbox.isChecked(): + for target in self.targets: + label_unc[target] = np.arange( + self.label_unc_from_spins[target].value(), + self.label_unc_to_spins[target].value() + 0.01, + self.label_unc_step_spins[target].value(), + ) + else: + for target, spin in zip( + self.targets, self.sim_tab.label_unc_spins + ): + label_unc[target] = [spin.value()] + return label_unc + + def _update_rounds_preview(self) -> None: + try: + distances = self._compute_distances() + label_unc = self._compute_label_unc() + except Exception: + self.rounds_label.setText("Estimated SPINNA rounds: ?") + return + n_models = len(distances) + total = n_models # final comparison + for target in self.targets: + if len(label_unc[target]) > 1: + total += len(label_unc[target]) * n_models + self.rounds_label.setText(f"Estimated SPINNA rounds: {total}") + + @staticmethod + def getParams( + parent: QtWidgets.QWidget, + sim_tab: SimulationsTab, + ) -> tuple[lib.FloatArray1D, dict, str, bool]: + dialog = FitLEDialog(sim_tab) + result = dialog.exec() + accepted = result == QtWidgets.QDialog.DialogCode.Accepted + distances = dialog._compute_distances() + label_unc = dialog._compute_label_unc() + savedir = "" + if accepted and dialog.save_fit_scores.isChecked(): + savedir = QtWidgets.QFileDialog.getExistingDirectory( + parent, + "Choose folder to save scores", + os.path.dirname(sim_tab.structures_path or ""), + ) + return distances, label_unc, savedir, accepted + + class OptionalSettingsDialog(lib.Dialog): """Dialog for setting optional parameters in the Simulations Tab. @@ -2563,6 +2791,9 @@ class SimulationsTab(lib.Dialog): Experimental data for each target. fit_button : QtWidgets.QPushButton Button for starting the fitting. + fit_le_button : QtWidgets.QPushButton + Button opening the dedicated Fit LE dialog. Visible only when + exactly two molecular targets are loaded. fit_results_display : QtWidgets.QLabel Label for displaying the results of fitting - the proportions of best-fit numbers of structures. @@ -2573,8 +2804,6 @@ class SimulationsTab(lib.Dialog): le_box : ScrollableGroupBox Box with spin boxes for setting labeling efficiency (%) for each target. - le_fitting_check : QtWidgets.QCheckBox - Check box for enabling/disabling fitting of labeling efficiency. le_spins : list of QtWidgets.QDoubleSpinBox Spin boxes for setting labeling efficiency (%) for each target. load_exp_data_box : ScrollableGroupBox @@ -2936,14 +3165,16 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: self.bootstrap_check.setChecked(False) fitting_layout.addWidget(self.bootstrap_check, 1, 1) - self.le_fitting_check = QtWidgets.QCheckBox("Fit labeling efficiency") - self.le_fitting_check.setToolTip( - "Use the loaded structures to fit their labeling efficiencies?" + self.fit_le_button = QtWidgets.QPushButton("Fit labeling efficiency") + self.fit_le_button.setToolTip( + "Open the Fit LE dialog. Visible only when exactly two" + " molecular targets are loaded.\n" + "Assumes experimental data was acquired as per Hellmeier, " + "Strauss, et al, Nature Methods, 2024." ) - self.le_fitting_check.setChecked(False) - self.le_fitting_check.setVisible(False) - self.le_fitting_check.toggled.connect(self.on_le_fitting_toggled) - fitting_layout.addWidget(self.le_fitting_check, 1, 2) + self.fit_le_button.setVisible(False) + self.fit_le_button.released.connect(self.fit_le) + fitting_layout.addWidget(self.fit_le_button, 1, 2) self.fit_button = QtWidgets.QPushButton( "Find best fitting stoichiometry" @@ -3049,11 +3280,7 @@ def load_structures(self) -> None: "background-color : lightgreen" ) self.fit_results_display.setText(" ") - self.le_fitting_check.setChecked(False) - if spinna.check_structures_valid_for_fitting(self.structures): - self.le_fitting_check.setVisible(True) - else: - self.le_fitting_check.setVisible(False) + self.fit_le_button.setVisible(len(self.targets) == 2) def load_target_names(self) -> None: """Load all unique names of molecular targets in @@ -3213,15 +3440,6 @@ def on_depth_button_clicked(self) -> None: ) self.densities_spins[idx].setValue(new_density) - def on_le_fitting_toggled(self, state: bool) -> None: - """If LE fitting box is checked, freeze LE values, else unfreeze - them.""" - - for le_box in self.le_spins: - if state: - le_box.setValue(100.0) - le_box.setEnabled(not state) - def load_densities_widgets(self) -> None: """Load the widgets for inputting observed densities of each target.""" @@ -3687,16 +3905,6 @@ def display_proportions( for structure, prop in zip(self.structures, prop_str): text += f"{structure.title} - {prop:.2f}%, " text = text[:-2] # remove last comma and space - if self.le_fitting_check.isChecked(): - # extract the le values based on the recovered proportions - le_values = spinna.get_le_from_props( - self.structures, - self.opt_props, - ) - text = ( - f"LE {self.targets[0]}: {le_values[self.targets[0]]:.1f}%," - f" LE {self.targets[1]}: {le_values[self.targets[1]]:.1f}%" - ) # only display the information about LE result self.fit_results_display.setText(text) def save_fit_results(self) -> None: @@ -3766,17 +3974,6 @@ def _nn_counts_summary(self, metadata: dict) -> dict: ) return metadata - def _le_fitting_summary(self, metadata: dict) -> dict: - """Adjust the summary of fit results and parameters if LE - fitting was performed.""" - if self.le_fitting_check.isChecked(): - for target in self.targets: - metadata.pop(f"Labeling efficiency (%) ({target})", None) - metadata["Best fitting labeling efficiencies (%)"] = ( - self.fit_results_display.text() - ) - return metadata - def summarize_fit_results(self) -> None: """Summarize fit results and parameters in a dictionary. @@ -3827,7 +4024,6 @@ def summarize_fit_results(self) -> None: "Best fitting score (Kolmogorov-Smirnov 2 sample test statistic)" ] = self.best_score metadata = self._extract_relative_props_for_target(metadata) - metadata = self._le_fitting_summary(metadata) metadata = self._nn_counts_summary(metadata) return metadata @@ -3928,6 +4124,194 @@ def compare_models(self) -> None: text = f"Best fitting model: {model_names[idx]}, already loaded." self.fit_results_display.setText(text) + @check_structures_loaded + @check_exp_data_loaded + @check_search_space_loaded + def fit_le(self) -> None: + """Open the Fit LE dialog and run the LE fit.""" + if len(self.targets) != 2: + QtWidgets.QMessageBox.warning( + self, + "Warning", + "Fit LE requires exactly two molecular targets.", + ) + return + if not isinstance(self.granularity, int): + QtWidgets.QMessageBox.warning( + self, + "Warning", + "Please generate the search space first.", + ) + return + + distances, label_unc, savedir, ok = FitLEDialog.getParams(self, self) + if not ok: + return + if len(distances) == 0: + QtWidgets.QMessageBox.warning( + self, "Warning", "No heterodimer distances specified." + ) + return + + # snapshot the search-space inputs — compare_models mutates the + # label_unc dict in place, so we keep a copy for the summary + label_unc_input = {t: list(v) for t, v in label_unc.items()} + distances_input = list(distances) + + base_mixer = self.setup_mixer(mode="fit") + if base_mixer is None: + return + + progress = lib.ProgressDialog("Fitting LE, please wait...", 0, 1, self) + progress.setAutoClose(False) + progress.setAutoReset(False) + fitting_mode = { + "Coarse to fine": "coarse-to-fine", + "Bayesian": "bayesian", + "Brute force": "brute-force", + }[self.settings_dialog.fitting_mode.currentText()] + + le_values, best_lunc, best_d, score, props, best_mixer = spinna.fit_le( + target_a=self.targets[0], + target_b=self.targets[1], + exp_data=self.exp_data, + granularity=self.granularity, + label_unc=label_unc, + distances=distances, + N_sim=self.n_sim_fit, + mask_dict=base_mixer.mask_dict, + width=base_mixer.roi[0], + height=base_mixer.roi[1], + depth=base_mixer.roi[2], + random_rot_mode=base_mixer.random_rot_mode, + asynch=self.settings_dialog.asynch_check.isChecked(), + savedir=savedir, + callback=progress, + fitting_mode=fitting_mode, + ) + progress.close() + + # push fitted label_unc back into the simulations-tab spin boxes + for spin, l in zip(self.label_unc_spins, best_lunc.values()): + spin.setValue(l) + + # adopt the best fitting structures so subsequent simulations + # use them (mirrors what compare_models does) + self.structures = best_mixer.structures + self.mixer = best_mixer + self.mixer.nn_counts = { + name: self.nn_plot_settings_dialog.nn_counts[name].value() + for name in self.nn_plot_settings_dialog.nn_counts.keys() + } + if self.mask_den_stack.currentIndex() == 1: # homogeneous dist. + roi = self.mixer.roi + self.single_sim_mass = self.mixer.roi_size + if roi[2] is None: + self.roi_button.setText( + f"Area: {self.single_sim_mass:.0f} μm²" + ) + else: + self.roi_button.setText( + f"Volume: {self.single_sim_mass:.0f} μm³" + ) + self.load_single_sim_n_str_widgets() + self.settings_dialog.update_neighbors_widgets() + self.nn_plot_settings_dialog.update_neighbors_widgets() + + # populate the results readout and the proportion spin boxes + self.opt_props = props + self.best_score = score + self.update_prop_str_input_spins(props) + lunc_text = ", ".join( + f"{t}={float(v):.2f} nm" for t, v in best_lunc.items() + ) + text = ( + f"LE {self.targets[0]}: {le_values[self.targets[0]]:.1f}%, " + f"LE {self.targets[1]}: {le_values[self.targets[1]]:.1f}%, " + f"distance: {best_d:.2f} nm, " + f"label_unc: {lunc_text}" + ) + self.fit_results_display.setText(text) + + # save a .txt summary of the fit + self._save_le_fit_summary( + le_values=le_values, + best_lunc=best_lunc, + best_d=best_d, + score=score, + props=props, + label_unc_input=label_unc_input, + distances_input=distances_input, + ) + + # run a single simulation and display the simulated/experimental NNDs + self.mixer = self.setup_mixer(mode="single_sim") + self.sim_and_plot_NND() + + def _save_le_fit_summary( + self, + *, + le_values: dict, + best_lunc: dict, + best_d: float, + score: float, + props, + label_unc_input: dict, + distances_input, + ) -> None: + """Write a .txt summary of the Fit LE results, mirroring the + format produced by ``save_fit_results`` for the stoichiometry + fit.""" + metadata = { + "Date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), + "Molecular targets": ", ".join(self.targets), + "Number of simulations": self.n_sim_fit, + "Parameter search space granularity": self.granularity, + "Dimensionality": self.dim_widget.currentText(), + "Rotations mode": ( + self.settings_dialog.rot_dim_widget.currentText() + ), + "Fitting mode": (self.settings_dialog.fitting_mode.currentText()), + } + for target in self.targets: + metadata[f"File location of experimental data ({target})"] = ( + self.exp_data_paths[target] + ) + for target in self.targets: + metadata[f"Label-uncertainty search space (nm) ({target})"] = ( + ", ".join(f"{float(v):.2f}" for v in label_unc_input[target]) + ) + metadata[f"Fitted label uncertainty (nm) ({target})"] = ( + f"{float(best_lunc[target]):.4f}" + ) + metadata["Heterodimer distance search space (nm)"] = ", ".join( + f"{float(v):.2f}" for v in distances_input + ) + metadata["Fitted heterodimer distance (nm)"] = f"{best_d:.4f}" + for target in self.targets: + metadata[f"Fitted labeling efficiency (%) ({target})"] = ( + f"{float(le_values[target]):.2f}" + ) + metadata["Best fitting structure proportions (%)"] = ", ".join( + f"{structure.title}: {float(p):.2f}" + for structure, p in zip(self.structures, props) + ) + metadata[ + "Best fitting score (Kolmogorov-Smirnov 2 sample test statistic)" + ] = score + + suggested = os.path.join( + self.window.pwd or os.getcwd(), "fit_le_summary.txt" + ) + path, _ = lib.get_save_filename_ext_dialog( + self, "Save Fit LE summary", suggested, filter="*.txt" + ) + if path: + self.window.pwd = os.path.dirname(path) + with open(path, "w") as f: + for key, value in metadata.items(): + f.write(f"{key}: {value}\n") + def on_roi_button_clicked(self) -> None: """Ask the user to input the area/volume to be simulated for a single simulation.""" diff --git a/picasso/spinna.py b/picasso/spinna.py index 6035f724..22e15a9f 100644 --- a/picasso/spinna.py +++ b/picasso/spinna.py @@ -239,15 +239,37 @@ def generate_N_structures( # target species and each column gives one structure n_t = len(targets) n_s = len(structures) - if n_s <= n_t: + if n_s < n_t: raise ValueError( "To generate the search space, the number of unique molecular" - " targets must be lower than the number of structures that are" - " investigated. Otherwise, the numbers of structures to be" - " simulated is constant." + " targets must not exceed the number of structures that are" + " investigated." ) t_counts = _find_target_counts(targets, structures) + # special case: n_s == n_t — the linear system t_counts @ counts = + # N_total has zero degrees of freedom, so the structure counts are + # uniquely determined. Return a single-row search space without + # invoking the sampler. + if n_s == n_t: + N_total_arr = np.asarray( + [N_total[target] for target in targets], dtype=np.float64 + ) + try: + counts = np.linalg.solve(t_counts.astype(np.float64), N_total_arr) + except np.linalg.LinAlgError as err: + raise ValueError( + "Cannot generate a search space: t_counts is singular." + ) from err + counts = np.maximum(np.round(counts), 0).astype(np.int32) + structure_counts = { + s.title: np.array([counts[i]]) for i, s in enumerate(structures) + } + if save: + df = pd.DataFrame(structure_counts) + df.to_csv(save, index=False) + return structure_counts + # ensure that the order of structures is correct, i.e., the free # paramters in the system of linear equations are on the right side p = _get_structures_permutation(t_counts.copy()) @@ -3194,6 +3216,13 @@ def fit_stoichiometry( # check and optionally convert N_structures N_structures = self.mixer.convert_N_structures_to_array(N_structures) + # short-circuit: with a single candidate there is nothing to + # search — the structure counts are already uniquely determined + # (e.g. when the number of structures equals the number of + # targets). Force brute-force on the lone entry to score it. + if N_structures.shape[0] == 1: + fitting_mode = "brute-force" + if fitting_mode == "coarse-to-fine": return self.fit_coarse_to_fine( N_structures, @@ -3345,7 +3374,7 @@ def fit_coarse_to_fine( # adjust the progress bar if isinstance(callback, lib.ProgressDialog): callback.setMaximum(n_coarse) - callback.setLabelText(coarse_title) + callback.zero_progress(coarse_title) self.progress_title = coarse_title N_coarse, scores_coarse = self._run_brute_force( N_coarse, asynch, callback @@ -3363,8 +3392,7 @@ def fit_coarse_to_fine( # adjust the progress bar again if isinstance(callback, lib.ProgressDialog): callback.setMaximum(len(N_fine)) - callback.setValue(0) - callback.setLabelText(fine_title) + callback.zero_progress(fine_title) self.progress_title = fine_title spinna_results = self.fit_stoichiometry( N_fine, @@ -4444,10 +4472,11 @@ def compare_models_given_label_unc( savepath = os.path.join( savedir, f"fit_scores_model_{i+1}_label_unc_{suffix}.csv" ) - # adjust the progress dialog + # adjust the progress dialog (zero_progress also resets the + # time-estimate baseline so the new phase gets a fresh ETA) if isinstance(callback, lib.ProgressDialog): callback.setMaximum(len(list(search_space.values())[0])) - callback.setLabelText(spinner_title) + callback.zero_progress(spinner_title) elif callback == "console": print(spinner_title) opt_props, score = spinner.fit_stoichiometry( @@ -4465,6 +4494,130 @@ def compare_models_given_label_unc( return best_score, best_idx, best_mixer, best_props +def fit_le( + target_a: str, + target_b: str, + exp_data: dict, + granularity: int, + label_unc: dict, + distances: list[float], + N_sim: int = 1, + mask_dict: dict | None = None, + width: float | None = None, + height: float | None = None, + depth: float | None = None, + random_rot_mode: Literal["2D", "3D"] | None = "2D", + asynch: bool = True, + savedir: str = "", + callback: lib.ProgressDialog | Literal["console"] | None = None, + fitting_mode: Literal[ + "coarse-to-fine", "bayesian", "brute-force" + ] = "coarse-to-fine", +) -> tuple[dict, dict, float, float, lib.FloatArray1D, StructureMixer]: + """Fit labeling efficiency (LE) for two molecular target species. + + Builds the three required structures internally (monomer A, monomer B, + and a family of heterodimers AB at the requested distances), forces + LE to 100% during the fit, and delegates to ``compare_models`` which + fits label uncertainty and picks the best heterodimer distance. The + recovered structure proportions are reinterpreted as LE values via + ``get_le_from_props``. + + Parameters + ---------- + target_a, target_b : str + Names of the two molecular target species. Both must appear as + keys in ``exp_data``. + exp_data : dict + Dictionary with molecular target names as keys and spatial + coordinates of the observed molecules as values. + granularity : int + Granularity as in ``generate_N_structures``. + label_unc : dict + Per-target list of label-uncertainty candidates. When a list has + a single entry the fit of that target's label_unc is skipped. + distances : list of float + Heterodimer distances (nm) to test. When a single entry is + given the heterodimer distance is fixed. + N_sim, mask_dict, width, height, depth, random_rot_mode, asynch, + savedir, callback, fitting_mode + Forwarded to ``compare_models``. + + Returns + ------- + le_values : dict + Recovered LE values per target (percent). + fitted_label_unc : dict + Label uncertainty value chosen for each target. + best_distance : float + Heterodimer distance that produced the best fit. + best_score : float + KS2 score of the best fit. + best_props : lib.FloatArray1D + Structure proportions at the best fit (monomer A, monomer B, + heterodimer). + best_mixer : StructureMixer + Mixer corresponding to the best fit (its ``structures`` attribute + holds [monomer_a, monomer_b, heterodimer(best_distance)]). + """ + # validate + if target_a not in exp_data or target_b not in exp_data: + raise ValueError( + "Both target_a and target_b must be present in exp_data." + ) + if target_a == target_b: + raise ValueError("target_a and target_b must be distinct.") + if len(distances) == 0: + raise ValueError("distances must contain at least one value.") + + # build monomers + monomer_a = Structure(title=f"Monomer_{target_a}") + monomer_a.define_coordinates(target_a, [0.0], [0.0], [0.0]) + monomer_b = Structure(title=f"Monomer_{target_b}") + monomer_b.define_coordinates(target_b, [0.0], [0.0], [0.0]) + + # build one model per heterodimer distance + models = [] + for d in distances: + het = Structure(title=f"Het_{target_a}_{target_b}_{float(d):.2f}nm") + het.define_coordinates(target_a, [-float(d) / 2], [0.0], [0.0]) + het.define_coordinates(target_b, [float(d) / 2], [0.0], [0.0]) + models.append([monomer_a, monomer_b, het]) + + # LE-fitting trick: simulate with LE = 100% so that the recovered + # proportions absorb the true LE (le is on the 0-1 scale internally) + le = {target_a: 1.0, target_b: 1.0} + + best_score, idx, fitted_label_unc, best_mixer, best_props = compare_models( + models=models, + exp_data=exp_data, + granularity=granularity, + label_unc=label_unc, + le=le, + N_sim=N_sim, + mask_dict=mask_dict, + width=width, + height=height, + depth=depth, + random_rot_mode=random_rot_mode, + asynch=asynch, + savedir=savedir, + callback=callback, + fitting_mode=fitting_mode, + ) + + best_distance = float(distances[idx]) + le_values = get_le_from_props(best_mixer.structures, best_props) + return ( + le_values, + fitted_label_unc, + best_distance, + best_score, + best_props, + best_mixer, + ) + + def check_structures_valid_for_fitting(structures: list[Structure]) -> bool: """Check if the structures loaded can be used for finding LE. diff --git a/tests/test_spinna.py b/tests/test_spinna.py index 5d45dce1..961581e8 100644 --- a/tests/test_spinna.py +++ b/tests/test_spinna.py @@ -249,9 +249,10 @@ def test_generate_N_structures_hetero_satisfies_balance(het_structures): assert (n_mA >= 0).all() and (n_mB >= 0).all() and (n_het >= 0).all() -def test_generate_N_structures_n_struct_le_n_targets_raises(het_structures): - # 2 targets, 2 structures (only the heterodimer + one monomer) → invalid - structures = [het_structures[0], het_structures[2]] +def test_generate_N_structures_n_struct_lt_n_targets_raises(het_structures): + # 2 targets, 1 structure (only the heterodimer) → invalid: more + # targets than structures, no solution. + structures = [het_structures[2]] with pytest.raises(ValueError): spinna.generate_N_structures( structures=structures, @@ -260,6 +261,21 @@ def test_generate_N_structures_n_struct_le_n_targets_raises(het_structures): ) +def test_generate_N_structures_n_struct_eq_n_targets_returns_single_row( + het_structures, +): + # 2 targets, 2 structures: structure counts are uniquely determined, + # so the search space has a single row. + structures = [het_structures[0], het_structures[2]] + out = spinna.generate_N_structures( + structures=structures, + N_total={"A": 100, "B": 100}, + granularity=5, + ) + for value in out.values(): + assert len(value) == 1 + + def test_generate_N_structures_save_csv(monomer_dimer_structures, tmp_path): out_csv = tmp_path / "search_space.csv" out = spinna.generate_N_structures( @@ -1564,3 +1580,32 @@ def test_compare_models_full_fits_label_unc( assert label_unc_out["target"] in (3.0, 6.0) assert isinstance(best_mixer, StructureMixer) assert len(best_props) == 2 + + +def test_fit_le_smoke(mols_real): + """End-to-end smoke test for spinna.fit_le on a small dataset.""" + coords = mols_real[["x", "y"]].to_numpy() + exp_data = {"A": coords, "B": coords} + np.random.seed(0) + le_values, fitted_lunc, best_d, best_score, best_props, best_mixer = ( + spinna.fit_le( + target_a="A", + target_b="B", + exp_data=exp_data, + granularity=3, + label_unc={"A": [3.0, 6.0], "B": [3.0, 6.0]}, + distances=[10.0, 15.0], + N_sim=1, + width=ROI, + height=ROI, + asynch=False, + ) + ) + assert np.isfinite(best_score) + assert best_d in (10.0, 15.0) + assert fitted_lunc["A"] in (3.0, 6.0) + assert fitted_lunc["B"] in (3.0, 6.0) + assert set(le_values.keys()) == {"A", "B"} + assert len(best_props) == 3 + assert isinstance(best_mixer, StructureMixer) + assert len(best_mixer.structures) == 3 From 4887b6e248c1b6ff195b19808ba58aa63301916a Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 18 May 2026 23:25:20 +0200 Subject: [PATCH 03/42] update spinna docs --- docs/spinna.rst | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/spinna.rst b/docs/spinna.rst index 8775d329..0b17d075 100644 --- a/docs/spinna.rst +++ b/docs/spinna.rst @@ -50,7 +50,7 @@ Load data and parameters 1. Click the *Load structures* button in the top left corner of the window. Upon loading, new widgets will appear in the GUI. 2. For each detected molecular target species, load the experimental data which must be saved in .hdf5 format that is compatible with localizations files in other Picasso modules, see `here `_. 3. Furthermore, input label uncertainty and labeling efficiency and observed density in the *Load data* box. Alternatively, load the mask to simulate heterogeneous distribution by clicking on *Masks* in the bottom left corner of the box. For more information about the mask, see **Mask generation tab**. -4. Moreover, in the *Load data* box, the user can change the dimensionality of the simulation. If 3D simulation is chosen without a mask, the user needs to input the range of z coordinates of molecular targets simulated by clicking *Z range*. In the "Optional settings" dialog, the user can change the mode of rotations (random rotations around z axis (2D), random rotations around 3 axes or no rotations). Additionally, the fitting mode can be adjusted - either "coarse to fine" or "brute force". For more information about the fitting modes, see **Fitting** below. +4. Moreover, in the *Load data* box, the user can change the dimensionality of the simulation. If 3D simulation is chosen without a mask, the user needs to input the range of z coordinates of molecular targets simulated by clicking *Z range*. In the "Optional settings" dialog, the user can change the mode of rotations (random rotations around z axis (2D), random rotations around 3 axes or no rotations). Additionally, the fitting mode can be adjusted - one of "bayesian", "coarse to fine" or "brute force". The chosen fitting mode applies to all fitting workflows (*Find best fitting combination*, *Compare models* and *Fit LE*). For more information about the fitting modes, see **Fitting** below. Fitting ~~~~~~~ @@ -60,19 +60,27 @@ Within the *Fitting* box: 1. To generate the search space, i.e., the set of stoichiometries tested in SPINNA, click the button *Generate parameter search space* and define the number of simulation repeats and granularity. For more information, see Supplementary Figure 2 in the `SPINNA publication `_. 2. To save the fitting scores for each tested stoichiometry, tick *Save fitting scores*. The user will be asked to input the name of the resulting .csv file. 3. To obtain the result’s uncertainty, check the *Bootstrap* box, which will resample from the best fitting model 20 times and rerun SPINNA on the resampled datasets. Note that this will increase the computation time. -4. To test different SPINNA models, click *Compare models*. The dialog will open, asking the user to input the range of tested label uncertainties (the user can choose to fit label uncertainty or not) and the candidate SPINNA models. For example, the user may want to explore the models with different spacings between the structures or different shape. We recommend the choose lower granularity when comparing models since the fitting may take a long time. +4. To test different SPINNA models, click *Compare models*. The dialog will open, asking the user to input the range of tested label uncertainties (the user can choose to fit label uncertainty or not) and the candidate SPINNA models. For example, the user may want to explore the models with different spacings between the structures or different shape. We recommend the choose lower granularity when comparing models since the fitting may take a long time. A single progress dialog is displayed throughout the comparison; its title shows the current round number (``[Round X/Y]``) so the user knows how many SPINNA rounds remain. The fitting mode selected in *Optional settings* is honored. 5. To run SPINNA, click *Find best fitting combination*. The progress dialog will be displayed. 6. After the fitting is finished, specify the name for saving a fit summary file (.txt). This file includes all the information about the fitting, the parameters and the results. The user may also choose not to save the file by clicking *Cancel* in the dialog. Additionally, the fitted stoichiometry is displayed in the *Single simulation* box and the NND histograms are shown in the *Plotting* box, see image below. -If labeling efficiency values are to be fitted, the user needs to load structures first which consists of: +Fitting labeling efficiency +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* Monomer of the reference protein -* Monomer of the target protein -* Heterodimer of the reference and target protein +Since v0.10.1 the labeling efficiency (LE) fit has its own workflow. Whenever exactly two molecular targets are loaded, a *Fit labeling efficiencies* button is shown in the *Fitting* box. The user no longer needs to load the three "monomer A / monomer B / heterodimer AB" structures manually — SPINNA constructs them internally from the two target names alone. -SPINNA will automatically detect if these conditions are met. If so, an extra check box will appear in the *Fitting* box, titled "Fit labeling efficiency". By checking it, LE used for simulations is kept at 100% and the reported fit result will only show the LE values of the reference and target proteins (in practice, which target is named reference or target does not matter and they can be interchanged). Additionally, the saved ``.txt`` file will contain the same information. +Clicking *Fit labeling efficiencies* opens a small dialog with three sections: -Since v0.10.0, in the "Optional settings", the user can choose between three fitting modes: "bayesian" "coarse to fine" and "brute force". In the "bayesian" mode, the search space is explored using Bayesian optimization with Gaussian process regression. This is a more efficient way to explore the search space, especially when it is large, and it is recommended as the default fitting mode. In the "coarse to fine" mode, a coarse grid of structure combinations is tested, which consists of 10% of evenly distributed structure combinations. Then, a finer grid is tested around the best combination from the coarse grid. In the "brute force" mode, all combinations of structures are tested sequentially. The "coarse to fine" mode is recommended for faster fitting, especially when the search space is large. Previously, only brute force mode was available. +1. **Fit label uncertainty** (checkbox) — when checked, the dialog exposes a *From / To / Step* row per target so SPINNA can search for the best label uncertainty. When unchecked, the current value from the *Load data* box is used as a fixed input for that target. +2. **Fit heterodimer distance** (checkbox) — when checked, the dialog exposes a *From / To / Step* row in nm. When unchecked, a single fixed distance is used (entered in the *Distance (nm)* field). +3. **Save fit scores** (checkbox) — when checked, the user selects a folder where SPINNA saves the fit scores for every candidate. + +The dialog also displays a live "Estimated SPINNA rounds" preview that updates as the spin boxes change, so the user can gauge how long the fit will take before starting it. + +Fitting modes +~~~~~~~~~~~~~ + +Since v0.10.0, in the "Optional settings", the user can choose between three fitting modes: "bayesian", "coarse to fine" and "brute force". The chosen mode is honored by *Find best fitting combination*, *Compare models* and *Fit LE...*. In the "bayesian" mode, the search space is explored using Bayesian optimization with Gaussian process regression. This is a more efficient way to explore the search space, especially when it is large, and it is recommended as the default fitting mode. In the "coarse to fine" mode, a coarse grid of structure combinations is tested, which consists of 10% of evenly distributed structure combinations. Then, a finer grid is tested around the best combination from the coarse grid. In the "brute force" mode, all combinations of structures are tested sequentially. The "coarse to fine" mode is recommended for faster fitting, especially when the search space is large. Previously, only brute force mode was available. .. image:: ../docs/spinna_simulate_tab_after_fit.png :alt: simulate_tab_after_fit From 9d1071fbae936c33cdc948df83c81bd2f3d6ba48 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Tue, 19 May 2026 17:47:43 +0200 Subject: [PATCH 04/42] clean up the new spinna fitting again --- changelog.md | 2 +- picasso/gui/spinna.py | 52 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 0b304df5..be765f5a 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -Last change: 18-MAY-2026 CEST +Last change: 19-MAY-2026 CEST ## 0.10.1 diff --git a/picasso/gui/spinna.py b/picasso/gui/spinna.py index 4da26304..a3b9f833 100644 --- a/picasso/gui/spinna.py +++ b/picasso/gui/spinna.py @@ -2178,7 +2178,7 @@ def __init__(self, sim_tab: SimulationsTab) -> None: self.distance_fixed_spin.setRange(0.1, 200) self.distance_fixed_spin.setDecimals(2) self.distance_fixed_spin.setSingleStep(0.5) - self.distance_fixed_spin.setValue(10.0) + self.distance_fixed_spin.setValue(sim_tab.get_heterodimer_distance()) distance_layout.addWidget(self.distance_fixed_spin, 1, 1, 1, 3) # from / to / step (active when checkbox is checked) @@ -3282,6 +3282,28 @@ def load_structures(self) -> None: self.fit_results_display.setText(" ") self.fit_le_button.setVisible(len(self.targets) == 2) + def get_heterodimer_distance(self, default: float = 10.0) -> float: + """Return the molecule-to-molecule distance (nm) of the first + heterodimer found in ``self.structures``. A heterodimer is a + structure with exactly two distinct targets, one molecule each. + Returns ``default`` if none is found.""" + if len(self.targets) != 2: + return default + target_a, target_b = self.targets[0], self.targets[1] + for s in self.structures: + if ( + len(s.targets) == 2 + and target_a in s.targets + and target_b in s.targets + and len(s.x[target_a]) == 1 + and len(s.x[target_b]) == 1 + ): + dx = s.x[target_a][0] - s.x[target_b][0] + dy = s.y[target_a][0] - s.y[target_b][0] + dz = s.z[target_a][0] - s.z[target_b][0] + return float(np.sqrt(dx * dx + dy * dy + dz * dz)) + return default + def load_target_names(self) -> None: """Load all unique names of molecular targets in self.structures to attribute self.targets.""" @@ -4196,7 +4218,10 @@ def fit_le(self) -> None: spin.setValue(l) # adopt the best fitting structures so subsequent simulations - # use them (mirrors what compare_models does) + # use them (mirrors what compare_models does); preserve the + # original structure titles by matching role (monomer A, + # monomer B, heterodimer) + self._restore_le_structure_titles(best_mixer.structures) self.structures = best_mixer.structures self.mixer = best_mixer self.mixer.nn_counts = { @@ -4245,9 +4270,32 @@ def fit_le(self) -> None: ) # run a single simulation and display the simulated/experimental NNDs + for spin in self.le_spins: + spin.setValue(100) self.mixer = self.setup_mixer(mode="single_sim") self.sim_and_plot_NND() + def _restore_le_structure_titles(self, new_structures: list) -> None: + """Rename ``new_structures`` in place so each one inherits the + title of the original Fit LE structure with the same role + (monomer A, monomer B, or heterodimer).""" + target_a, target_b = self.targets[0], self.targets[1] + original_titles = {} + for s in self.structures: + if len(s.targets) == 1 and s.targets[0] == target_a: + original_titles["A"] = s.title + elif len(s.targets) == 1 and s.targets[0] == target_b: + original_titles["B"] = s.title + elif len(s.targets) == 2: + original_titles["AB"] = s.title + for s in new_structures: + if len(s.targets) == 1 and s.targets[0] == target_a: + s.title = original_titles.get("A", s.title) + elif len(s.targets) == 1 and s.targets[0] == target_b: + s.title = original_titles.get("B", s.title) + elif len(s.targets) == 2: + s.title = original_titles.get("AB", s.title) + def _save_le_fit_summary( self, *, From 689ce8780b8c3454c80464b0fa7f54d110ff427f Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Tue, 19 May 2026 19:46:33 +0200 Subject: [PATCH 05/42] faster rendering, low-hanging fruits --- changelog.md | 5 +- picasso/render.py | 350 +++++++++++++++++++++++++++++++++------------- 2 files changed, 253 insertions(+), 102 deletions(-) diff --git a/changelog.md b/changelog.md index 13bcc9ab..1fd78433 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,9 @@ # Changelog -Last change: 19-MAY-2026 CEST +Last change: 20-MAY-2026 CEST + +## 0.10.1 +- Faster rendering, especially in large FOV: ind. loc. precision blurring parallelized + smarter implementation for 2D rendering; some extra general improvements ## 0.10.0 diff --git a/picasso/render.py b/picasso/render.py index 3ece8032..11243904 100755 --- a/picasso/render.py +++ b/picasso/render.py @@ -21,7 +21,7 @@ import pandas as pd import matplotlib.pyplot as plt import imageio.v2 as imageio -from scipy import signal +from scipy import signal, ndimage from scipy.spatial.transform import Rotation from tqdm import tqdm from PyQt6 import QtGui, QtCore, QtSvg @@ -489,7 +489,88 @@ def _fill3d( image[j, i, k] += 1 -@numba.njit +_PER_THREAD_BUFFER_BUDGET_BYTES = 256 * 1024 * 1024 + + +def _n_threads_for_buffers( + n_pixel_y: int, n_pixel_x: int, itemsize: int, n_locs: int +) -> int: + """Decide how many threads to use for per-thread image accumulators. + + Bounded by the available Numba threads, a memory budget on the + per-thread image stack, and the number of localizations.""" + bytes_per_buffer = n_pixel_y * n_pixel_x * itemsize + if bytes_per_buffer <= 0: + return 1 + max_by_budget = max(1, _PER_THREAD_BUFFER_BUDGET_BYTES // bytes_per_buffer) + return int(min(numba.get_num_threads(), max_by_budget, max(1, n_locs))) + + +@numba.njit(parallel=True, cache=True) +def _fill_gaussian_kernel( + buffers: lib.FloatArray3D, + x: lib.FloatArray1D, + y: lib.FloatArray1D, + sx: lib.FloatArray1D, + sy: lib.FloatArray1D, + n_pixel_x: int, + n_pixel_y: int, + n_threads: int, +) -> None: + """Parallel separable-Gaussian accumulator. Each prange iteration + processes a contiguous slice of localizations into its own image + buffer ``buffers[t]`` (race-free).""" + n_locs = len(x) + chunk = (n_locs + n_threads - 1) // n_threads + for t in numba.prange(n_threads): + start = t * chunk + end = start + chunk + if end > n_locs: + end = n_locs + buf = buffers[t] + for k in range(start, end): + x_ = x[k] + y_ = y[k] + sx_ = sx[k] + sy_ = sy[k] + max_y_off = _DRAW_MAX_SIGMA * sy_ + i_min = np.int32(y_ - max_y_off) + if i_min < 0: + i_min = 0 + i_max = np.int32(y_ + max_y_off + 1) + if i_max > n_pixel_y: + i_max = n_pixel_y + max_x_off = _DRAW_MAX_SIGMA * sx_ + j_min = np.int32(x_ - max_x_off) + if j_min < 0: + j_min = 0 + j_max = np.int32(x_ + max_x_off) + 1 + if j_max > n_pixel_x: + j_max = n_pixel_x + nx = j_max - j_min + ny = i_max - i_min + if nx <= 0 or ny <= 0: + continue + inv_2sx2 = 1.0 / (2.0 * sx_ * sx_) + inv_2sy2 = 1.0 / (2.0 * sy_ * sy_) + norm = 1.0 / (2.0 * np.pi * sx_ * sy_) + # Separable kernel: factor exp(-(dx^2/(2sx^2) + dy^2/(2sy^2))) + # into 1D gx * 1D gy. O(K) exp calls per loc instead of O(K^2). + gx = np.empty(nx, dtype=np.float32) + gy = np.empty(ny, dtype=np.float32) + for jj in range(nx): + dx = (j_min + jj) + 0.5 - x_ + gx[jj] = np.exp(-dx * dx * inv_2sx2) + for ii in range(ny): + dy = (i_min + ii) + 0.5 - y_ + gy[ii] = norm * np.exp(-dy * dy * inv_2sy2) + for ii in range(ny): + gy_i = gy[ii] + row = buf[i_min + ii] + for jj in range(nx): + row[j_min + jj] += gy_i * gx[jj] + + def _fill_gaussian( image: lib.FloatArray2D, x: lib.FloatArray1D, @@ -514,37 +595,107 @@ def _fill_gaussian( n_pixel_x, n_pixel_y : int Number of pixels in x and y. """ - # render each localization separately - for x_, y_, sx_, sy_ in zip(x, y, sx, sy): - - # get min and max indeces to draw the given localization - max_y = _DRAW_MAX_SIGMA * sy_ - i_min = np.int32(y_ - max_y) - if i_min < 0: - i_min = 0 - i_max = np.int32(y_ + max_y + 1) - if i_max > n_pixel_y: - i_max = n_pixel_y - max_x = _DRAW_MAX_SIGMA * sx_ - j_min = np.int32(x_ - max_x) - if j_min < 0: - j_min = 0 - j_max = np.int32(x_ + max_x) + 1 - if j_max > n_pixel_x: - j_max = n_pixel_x - - # draw a localization as a 2D guassian PDF - for i in range(i_min, i_max): - for j in range(j_min, j_max): - image[i, j] += np.exp( - -( - (j - x_ + 0.5) ** 2 / (2 * sx_**2) - + (i - y_ + 0.5) ** 2 / (2 * sy_**2) + n_locs = len(x) + if n_locs == 0: + return + n_threads = _n_threads_for_buffers( + n_pixel_y, n_pixel_x, image.dtype.itemsize, n_locs + ) + if n_threads <= 1: + # Single-thread path: write directly into image to skip the + # buffer-stack allocation and reduction. + _fill_gaussian_kernel( + image.reshape(1, n_pixel_y, n_pixel_x), + x, + y, + sx, + sy, + n_pixel_x, + n_pixel_y, + 1, + ) + return + buffers = np.zeros((n_threads, n_pixel_y, n_pixel_x), dtype=image.dtype) + _fill_gaussian_kernel( + buffers, x, y, sx, sy, n_pixel_x, n_pixel_y, n_threads + ) + image += buffers.sum(axis=0) + + +@numba.njit(parallel=True, cache=True) +def _fill_gaussian_rot_kernel( + buffers: lib.FloatArray3D, + x: lib.FloatArray1D, + y: lib.FloatArray1D, + z: lib.FloatArray1D, + sx: lib.FloatArray1D, + sy: lib.FloatArray1D, + sz: lib.FloatArray1D, + n_pixel_x: int, + n_pixel_y: int, + rot_matrix: lib.Array3x3, + rot_matrixT: lib.Array3x3, + n_threads: int, +) -> None: + """Parallel rotated-Gaussian accumulator. Per-thread image buffers + in ``buffers[t]`` are race-free across prange iterations.""" + n_locs = len(x) + chunk = (n_locs + n_threads - 1) // n_threads + for t in numba.prange(n_threads): + start = t * chunk + end = start + chunk + if end > n_locs: + end = n_locs + buf = buffers[t] + for k in range(start, end): + x_ = x[k] + y_ = y[k] + sx_ = sx[k] + sy_ = sy[k] + sz_ = sz[k] + cov = np.zeros((3, 3), dtype=np.float32) + cov[0, 0] = sx_ * sx_ + cov[1, 1] = sy_ * sy_ + cov[2, 2] = sz_ * sz_ + cov_rot = rot_matrix @ cov @ rot_matrixT + s00 = cov_rot[0, 0] + s01 = cov_rot[0, 1] + s10 = cov_rot[1, 0] + s11 = cov_rot[1, 1] + det2d = s00 * s11 - s01 * s10 + if det2d < 1e-10: + continue + inv00 = s11 / det2d + inv01 = -s01 / det2d + inv10 = -s10 / det2d + inv11 = s00 / det2d + norm = 1.0 / (2.0 * np.pi * np.sqrt(det2d)) + eff_sx = np.sqrt(s00) + eff_sy = np.sqrt(s11) + max_x_off = _DRAW_MAX_SIGMA * 2.5 * eff_sx + max_y_off = _DRAW_MAX_SIGMA * 2.5 * eff_sy + j_min = int(x_ - max_x_off) + if j_min < 0: + j_min = 0 + j_max = int(x_ + max_x_off + 1) + if j_max > n_pixel_x: + j_max = n_pixel_x + i_min = int(y_ - max_y_off) + if i_min < 0: + i_min = 0 + i_max = int(y_ + max_y_off + 1) + if i_max > n_pixel_y: + i_max = n_pixel_y + for i in range(i_min, i_max): + b = np.float32(i + 0.5 - y_) + for j in range(j_min, j_max): + a = np.float32(j + 0.5 - x_) + exponent = ( + a * a * inv00 + a * b * (inv01 + inv10) + b * b * inv11 ) - ) / (2 * np.pi * sx_ * sy_) + buf[i, j] += norm * np.exp(-0.5 * exponent) -@numba.njit def _fill_gaussian_rot( image: lib.FloatArray2D, x: lib.FloatArray1D, @@ -575,8 +726,10 @@ def _fill_gaussian_rot( ang : tuple Rotation angles of locs around x, y and z axes (radians). """ - (angx, angy, angz) = ang - + n_locs = len(x) + if n_locs == 0: + return + angx, angy, angz = ang rot_mat_x = np.array( [ [1.0, 0.0, 0.0], @@ -601,67 +754,44 @@ def _fill_gaussian_rot( ], dtype=np.float32, ) - rot_matrix = rot_mat_x @ rot_mat_y @ rot_mat_z - rot_matrixT = np.transpose(rot_matrix) - - for x_, y_, z_, sx_, sy_, sz_ in zip(x, y, z, sx, sy, sz): - - # rotated 3D covariance - cov = np.array( - [ - [sx_**2, 0.0, 0.0], - [0.0, sy_**2, 0.0], - [0.0, 0.0, sz_**2], - ], - dtype=np.float32, - ) - cov_rot = rot_matrix @ cov @ rot_matrixT + rot_matrix = (rot_mat_x @ rot_mat_y @ rot_mat_z).astype(np.float32) + rot_matrixT = np.ascontiguousarray(rot_matrix.T) - # we only need the top-left 2x2 part for rendering - s00 = cov_rot[0, 0] # var_x - s01 = cov_rot[0, 1] # cov_xy - s10 = cov_rot[1, 0] # cov_yx (= s01) - s11 = cov_rot[1, 1] # var_y - - # inverse of 2x2 matrix - det2d = s00 * s11 - s01 * s10 - if det2d < 1e-10: - continue - inv00 = s11 / det2d - inv01 = -s01 / det2d - inv10 = -s10 / det2d - inv11 = s00 / det2d - - norm = 1.0 / (2.0 * np.pi * np.sqrt(det2d)) - - # use the larger effective sigma for draw bounds - eff_sx = np.sqrt(s00) - eff_sy = np.sqrt(s11) - max_x = _DRAW_MAX_SIGMA * 2.5 * eff_sx - max_y = _DRAW_MAX_SIGMA * 2.5 * eff_sy - - j_min = int(x_ - max_x) - if j_min < 0: - j_min = 0 - j_max = int(x_ + max_x + 1) - if j_max > n_pixel_x: - j_max = n_pixel_x - i_min = int(y_ - max_y) - if i_min < 0: - i_min = 0 - i_max = int(y_ + max_y + 1) - if i_max > n_pixel_y: - i_max = n_pixel_y - - # 2D Gaussian (no z-loop!) - for i in range(i_min, i_max): - b = np.float32(i + 0.5 - y_) - for j in range(j_min, j_max): - a = np.float32(j + 0.5 - x_) - exponent = ( - a * a * inv00 + a * b * (inv01 + inv10) + b * b * inv11 - ) - image[i, j] += norm * np.exp(-0.5 * exponent) + n_threads = _n_threads_for_buffers( + n_pixel_y, n_pixel_x, image.dtype.itemsize, n_locs + ) + if n_threads <= 1: + _fill_gaussian_rot_kernel( + image.reshape(1, n_pixel_y, n_pixel_x), + x, + y, + z, + sx, + sy, + sz, + n_pixel_x, + n_pixel_y, + rot_matrix, + rot_matrixT, + 1, + ) + return + buffers = np.zeros((n_threads, n_pixel_y, n_pixel_x), dtype=image.dtype) + _fill_gaussian_rot_kernel( + buffers, + x, + y, + z, + sx, + sy, + sz, + n_pixel_x, + n_pixel_y, + rot_matrix, + rot_matrixT, + n_threads, + ) + image += buffers.sum(axis=0) @numba.njit @@ -1386,7 +1516,8 @@ def _fftconvolve( blur_width: float, blur_height: float, ) -> lib.FloatArray2D: - """Blur (convolves) 2D image using fast fourier transform. + """Blur (convolves) 2D image using fast fourier transform or with + Gaussian filter applied (faster for small kernels). Parameters ---------- @@ -1402,6 +1533,26 @@ def _fftconvolve( """ kernel_width = 10 * int(np.round(blur_width)) + 1 kernel_height = 10 * int(np.round(blur_height)) + 1 + # Spatial separable convolution is faster than FFT for the small + # kernels typical of SMLM precisions (~1-3 px). Switch to FFT only + # when the kernel is large relative to the image. + n_y, n_x = image.shape + spatial = ( + kernel_height < 0.05 * n_y + and kernel_width < 0.05 * n_x + and max(kernel_height, kernel_width) <= 101 + ) + if spatial: + out = np.empty_like(image, dtype=np.float32) + ndimage.gaussian_filter( + image, + sigma=(blur_height, blur_width), + output=out, + mode="constant", + cval=0.0, + truncate=5.0, + ) + return out kernel_y = signal.windows.gaussian(kernel_height, blur_height) kernel_x = signal.windows.gaussian(kernel_width, blur_width) kernel = np.outer(kernel_y, kernel_x) @@ -2767,16 +2918,13 @@ def _render_multi_channel( ) # color the images - Y, X = images.shape[1:] - rgb = np.zeros((Y, X, 3), dtype=np.float32) # float for now if colors is None: # fallback if the user did not specify colors colors = lib.get_colors(len(images)) - for color, image in zip(colors, images): - for i in range(3): - rgb[:, :, i] += color[i] * image - rgb = np.minimum( - rgb, 1.0 - ) # clip to max value of 1 (preserves relative brightness) + colors_arr = np.asarray(colors, dtype=np.float32) + images_f32 = np.ascontiguousarray(images, dtype=np.float32) + rgb = np.tensordot(images_f32, colors_arr, axes=([0], [0])) + # clip to max value of 1 (preserves relative brightness) + np.minimum(rgb, 1.0, out=rgb) rgb = to_8bit(rgb) if invert_colors: rgb = 255 - rgb From 6d453d189cb01a6bc298f977a930188b883bdac0 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 20 May 2026 16:15:52 +0200 Subject: [PATCH 06/42] make gaussian blur gentler on servers --- changelog.md | 2 +- docs/render.rst | 29 +++++++++++++++++++++++- picasso/render.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 1fd78433..ccc4626b 100644 --- a/changelog.md +++ b/changelog.md @@ -3,7 +3,7 @@ Last change: 20-MAY-2026 CEST ## 0.10.1 -- Faster rendering, especially in large FOV: ind. loc. precision blurring parallelized + smarter implementation for 2D rendering; some extra general improvements +- Faster rendering, especially in large FOV: ind. loc. precision blurring parallelized ., see [documentation](https://picassosr.readthedocs.io/en/latest/render.html/CPU-usage-on-shared-servers) + smarter implementation for 2D rendering; improvements for one-pixel-blur and global loc. prec. ## 0.10.0 diff --git a/docs/render.rst b/docs/render.rst index 6c38b87f..f7d0d998 100644 --- a/docs/render.rst +++ b/docs/render.rst @@ -110,6 +110,33 @@ If the outcome of G5M seems unsatisfactory, please check the following: - Adjust min. locs; - Adjust DBSCAN (or other clustering algorithm) parameters. For example, if G5M takes too long to run, the DBSCAN clusters most likely contain too many molecules. In such a case, we recommend splitting such clusters further; +CPU usage on shared servers +--------------------------- + +Rendering with the ``gaussian`` and ``gaussian_iso`` blur methods (and the rotated variants used in the 3D rotation window) is parallelized across CPU cores. On a single workstation this is likely the desired behaviour, but on a shared compute server, most interactions with the canvas (zooming, rotation, changing display settings) would otherwise fan out to **all** available cores — so a handful of simultaneous users can saturate the machine and stall each other. + +To keep Picasso polite on shared hardware, the maximum number of threads used by the parallel render kernels is capped (default: 8). The cap is resolved on the first render call in this order; the first match wins: + +1. **User setting** ``Render -> CPU Threads`` in ``~/.picasso/settings.yaml``. Open ``View > User Settings`` in Picasso: Render to edit, or edit the YAML directly:: + + Render: + CPU Threads: 4 + +2. **Default**: ``min(8, available cores)``. + +The resolved value is clamped to ``[1, available cores]`` and cached for the lifetime of the process. From a script you can override it programmatically:: + + from picasso import render + render.set_render_threads(2) # cap at 2 threads + render.set_render_threads(None) # clear cache, re-read env var / settings + +Recommended values: + +- **Personal workstation (4-16 cores):** leave the default, or set to the core count. +- **Shared server with N concurrent users:** ``max(2, cores // N)`` is a reasonable starting point. For a 64-core box with ~25 simultaneous users, ``PICASSO_RENDER_THREADS=2`` keeps any single render from monopolizing the machine while still giving each user a 2x speedup over single-threaded rendering. + +Note: this only affects the parallel rendering kernels. Other Picasso modules (Localize, Average, postprocessing) use their own multiprocessing controls and are not influenced by this setting. + Dialogs ------- @@ -208,7 +235,7 @@ These columns can be used to plot density profiles of localizations along the re Save pick properties ^^^^^^^^^^^^^^^^^^^^ -Calculates the properties of each pick (i.e., mean frame, mean x mean y as well as kinetic information and saves it as an hdf5 file. +Calculates the properties of each pick (i.e., mean frame, mean x mean y as well as kinetic information) and saves it as an hdf5 file. Save pick regions ^^^^^^^^^^^^^^^^^ diff --git a/picasso/render.py b/picasso/render.py index 11243904..62e52719 100755 --- a/picasso/render.py +++ b/picasso/render.py @@ -33,6 +33,61 @@ N_GROUP_COLORS = 8 POLYGON_POINTER_SIZE = 16 # must be even +# Cap on Numba threads used by the parallel render kernels. Picasso is +# often deployed on shared servers where dozens of users render +# simultaneously, so we don't grab every core by default. Resolved +# lazily from PICASSO_RENDER_THREADS, then ``settings["Render"]["CPU +# Threads"]``, falling back to ``_DEFAULT_RENDER_THREADS``. +_DEFAULT_RENDER_THREADS = 8 +_render_threads_cached: int | None = None + + +def _render_threads() -> int: + """Return the max Numba thread count for parallel render kernels. + + Resolution order (first wins): + 1. env var ``PICASSO_RENDER_THREADS`` + 2. user setting ``Render -> CPU Threads`` (see io.load_user_settings) + 3. ``min(_DEFAULT_RENDER_THREADS, numba.get_num_threads())`` + + The value is clamped to ``[1, numba.get_num_threads()]`` and cached. + Call ``set_render_threads(None)`` to invalidate the cache (e.g. after + editing the settings file at runtime). + """ + global _render_threads_cached + if _render_threads_cached is not None: + return _render_threads_cached + + hw_max = max(1, numba.get_num_threads()) + n: int | None = None + try: + settings = io.load_user_settings() + raw = settings["Render"]["CPU Threads"] + if raw not in ("", None, {}): # AutoDict returns {} on miss + n = int(raw) + except (KeyError, TypeError, ValueError): + n = None + + if n is None: + n = min(_DEFAULT_RENDER_THREADS, hw_max) + + _render_threads_cached = max(1, min(int(n), hw_max)) + return _render_threads_cached + + +def set_render_threads(n: int | None) -> None: + """Set the max thread count for parallel render kernels. + + Pass ``None`` to clear the cache and force re-resolution from env + var / user settings on the next render. + """ + global _render_threads_cached + if n is None: + _render_threads_cached = None + return + hw_max = max(1, numba.get_num_threads()) + _render_threads_cached = max(1, min(int(n), hw_max)) + def render( locs: pd.DataFrame, @@ -503,7 +558,7 @@ def _n_threads_for_buffers( if bytes_per_buffer <= 0: return 1 max_by_budget = max(1, _PER_THREAD_BUFFER_BUDGET_BYTES // bytes_per_buffer) - return int(min(numba.get_num_threads(), max_by_budget, max(1, n_locs))) + return int(min(_render_threads(), max_by_budget, max(1, n_locs))) @numba.njit(parallel=True, cache=True) From 82fdad5b2618650576b90bf4c9f7bf390c434b81 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 20 May 2026 16:22:41 +0200 Subject: [PATCH 07/42] small fix for faster gaussian rot + add more tests --- picasso/render.py | 4 +- tests/test_render.py | 197 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 2 deletions(-) diff --git a/picasso/render.py b/picasso/render.py index 62e52719..d87db0a4 100755 --- a/picasso/render.py +++ b/picasso/render.py @@ -727,8 +727,8 @@ def _fill_gaussian_rot_kernel( norm = 1.0 / (2.0 * np.pi * np.sqrt(det2d)) eff_sx = np.sqrt(s00) eff_sy = np.sqrt(s11) - max_x_off = _DRAW_MAX_SIGMA * 2.5 * eff_sx - max_y_off = _DRAW_MAX_SIGMA * 2.5 * eff_sy + max_x_off = _DRAW_MAX_SIGMA * eff_sx + max_y_off = _DRAW_MAX_SIGMA * eff_sy j_min = int(x_ - max_x_off) if j_min < 0: j_min = 0 diff --git a/tests/test_render.py b/tests/test_render.py index 7ae15ce1..84bc8973 100644 --- a/tests/test_render.py +++ b/tests/test_render.py @@ -7,6 +7,7 @@ import sys +import numba import numpy as np import pandas as pd import pytest @@ -81,6 +82,14 @@ def image(locs, info): return render.render(locs, info, oversampling=13)[1] +@pytest.fixture +def reset_render_threads(): + """Save and restore render's cached thread count around a test.""" + saved = render._render_threads_cached + yield + render._render_threads_cached = saved + + @pytest.fixture(scope="module") def small_qimage(): """Small black QImage used as a canvas for draw_* / export_* tests.""" @@ -207,6 +216,34 @@ def test_no_info_no_viewport_raises(self, locs): with pytest.raises(ValueError): render.render(locs, None, oversampling=5) + def test_empty_locs_gaussian(self, locs, info): + """Empty input must not crash the parallel _fill_gaussian path.""" + empty = locs.iloc[:0] + n, im = render.render( + empty, + info, + oversampling=5, + viewport=FULL_VIEWPORT, + blur_method="gaussian", + ) + assert n == 0 + assert im.shape == (160, 160) + assert (im == 0).all() + + def test_empty_locs_gaussian_rot(self, locs_3d, info): + """Empty input must not crash the parallel _fill_gaussian_rot path.""" + empty = locs_3d.iloc[:0] + n, im = render.render( + empty, + info, + oversampling=5, + viewport=FULL_VIEWPORT, + blur_method="gaussian", + ang=(0.1, 0.2, 0.3), + ) + assert n == 0 + assert (im == 0).all() + def test_3d_rotation_changes_image(self, locs_3d, info): """A non-zero rotation must produce a different image.""" _, im_no_rot = render.render( @@ -228,6 +265,89 @@ def test_3d_rotation_changes_image(self, locs_3d, info): assert not np.array_equal(im_no_rot, im_rot) +# --------------------------------------------------------------------------- +# Render threading +# --------------------------------------------------------------------------- + + +class TestRenderThreads: + """set_render_threads / _render_threads resolution and equivalence.""" + + def test_set_value_caches(self, reset_render_threads): + hw_max = max(1, numba.get_num_threads()) + render.set_render_threads(2) + assert render._render_threads() == min(2, hw_max) + + def test_clamps_above_hw_max(self, reset_render_threads): + hw_max = max(1, numba.get_num_threads()) + render.set_render_threads(hw_max + 100) + assert render._render_threads() == hw_max + + def test_clamps_below_one(self, reset_render_threads): + render.set_render_threads(0) + assert render._render_threads() == 1 + + def test_none_clears_cache(self, reset_render_threads): + render.set_render_threads(3) + render.set_render_threads(None) + assert render._render_threads_cached is None + + @pytest.mark.parametrize("blur_method", ["gaussian", "gaussian_iso"]) + def test_thread_count_does_not_change_result( + self, locs, info, reset_render_threads, blur_method + ): + """Serial and parallel render paths must produce numerically + equivalent images (only float32 sum order differs).""" + hw_max = max(1, numba.get_num_threads()) + if hw_max < 2: + pytest.skip("requires >=2 numba threads") + render.set_render_threads(1) + _, im_serial = render.render( + locs, + info, + oversampling=5, + viewport=FULL_VIEWPORT, + blur_method=blur_method, + ) + render.set_render_threads(min(8, hw_max)) + _, im_parallel = render.render( + locs, + info, + oversampling=5, + viewport=FULL_VIEWPORT, + blur_method=blur_method, + ) + assert im_serial.shape == im_parallel.shape + assert np.allclose(im_serial, im_parallel, rtol=1e-4, atol=1e-4) + + def test_thread_count_does_not_change_result_gaussian_rot( + self, locs_3d, info, reset_render_threads + ): + """Same equivalence check for the rotated-Gaussian kernel.""" + hw_max = max(1, numba.get_num_threads()) + if hw_max < 2: + pytest.skip("requires >=2 numba threads") + render.set_render_threads(1) + _, im_serial = render.render( + locs_3d, + info, + oversampling=5, + viewport=FULL_VIEWPORT, + blur_method="gaussian", + ang=(0.1, 0.2, 0.3), + ) + render.set_render_threads(min(8, hw_max)) + _, im_parallel = render.render( + locs_3d, + info, + oversampling=5, + viewport=FULL_VIEWPORT, + blur_method="gaussian", + ang=(0.1, 0.2, 0.3), + ) + assert np.allclose(im_serial, im_parallel, rtol=1e-4, atol=1e-4) + + # --------------------------------------------------------------------------- # render_hist_numba # --------------------------------------------------------------------------- @@ -545,6 +665,55 @@ def test_determinant_3x3_matches_numpy(self): ) +# --------------------------------------------------------------------------- +# _fftconvolve (spatial / FFT branches) +# --------------------------------------------------------------------------- + + +class TestFftConvolve: + """The spatial vs FFT branch is chosen by kernel size relative to image. + Kernel formula: ``10 * round(blur) + 1``. Spatial branch is taken when + both kernel dims are < 0.05 * image dim and max(kernel) <= 101. + """ + + def test_spatial_branch_preserves_mass(self): + """Small kernel → ndimage.gaussian_filter spatial path.""" + im = np.zeros((64, 64), dtype=np.float32) + im[32, 32] = 1.0 + # blur=0.05 → kernel=1; 1 < 0.05*64=3.2 → spatial + out = render._fftconvolve(im, 0.05, 0.05) + assert out.shape == im.shape + assert out.dtype == np.float32 + assert out.sum() == pytest.approx(1.0, abs=1e-5) + + def test_fft_branch_preserves_mass(self): + """Kernel large relative to image → fftconvolve path.""" + im = np.zeros((64, 64), dtype=np.float32) + im[32, 32] = 1.0 + # blur=2.0 → kernel=21; 21 > 0.05*64=3.2 → FFT + out = render._fftconvolve(im, 2.0, 2.0) + assert out.shape == im.shape + assert out.dtype == np.float32 + assert out.sum() == pytest.approx(1.0, abs=1e-3) + + def test_branches_agree_on_intermediate_blur(self): + """Both branches should produce visually similar results for a + blur that happens to fall near the threshold.""" + rng = np.random.default_rng(0) + im = rng.random((256, 256)).astype(np.float32) + # blur=1.0 → kernel=11. 11 < 0.05*256=12.8 → spatial. + out_spatial = render._fftconvolve(im, 1.0, 1.0) + # blur=1.0 with smaller image forces FFT: kernel=11 > 0.05*100=5 + out_fft = render._fftconvolve(im[:100, :100], 1.0, 1.0) + # Both must be finite and preserve total mass approximately. + assert np.isfinite(out_spatial).all() + assert np.isfinite(out_fft).all() + # Mass approximately preserved; some loss near borders from + # zero-padding (more pronounced on the smaller crop). + assert out_spatial.sum() == pytest.approx(im.sum(), rel=1e-2) + assert out_fft.sum() == pytest.approx(im[:100, :100].sum(), rel=5e-2) + + # --------------------------------------------------------------------------- # Image processing # --------------------------------------------------------------------------- @@ -786,6 +955,34 @@ def test_multi_channel(self, locs, info): assert isinstance(qimage, QtGui.QImage) assert n_locs == 2 * len(locs) + def test_multi_channel_color_isolation(self, locs, info): + """Pure-red channel must leave G and B at zero in the output.""" + qimage, _ = render.render_scene( + [locs], + [info], + disp_px_size=PIXELSIZE, + viewport=FULL_VIEWPORT, + colors=[(1.0, 0.0, 0.0)], + ) + bgra = _qimage_to_array(qimage) + assert (bgra[..., 0] == 0).all() # B + assert (bgra[..., 1] == 0).all() # G + assert bgra[..., 2].max() > 0 # R lights up + + def test_multi_channel_green_isolation(self, locs, info): + """Pure-green channel must leave R and B at zero in the output.""" + qimage, _ = render.render_scene( + [locs], + [info], + disp_px_size=PIXELSIZE, + viewport=FULL_VIEWPORT, + colors=[(0.0, 1.0, 0.0)], + ) + bgra = _qimage_to_array(qimage) + assert (bgra[..., 0] == 0).all() # B + assert (bgra[..., 2] == 0).all() # R + assert bgra[..., 1].max() > 0 # G lights up + def test_empty_locs_list(self, info): qimage, n_locs = render.render_scene( [], [], disp_px_size=PIXELSIZE, viewport=FULL_VIEWPORT From 975629e1f3a0bd4004c80aeb537d0cea472145da Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 21 May 2026 10:04:44 +0200 Subject: [PATCH 08/42] add spatial indexing for efficient rendering of zoomed in fovs --- changelog.md | 3 +- picasso/gui/render.py | 133 ++++++++++++++-- picasso/spatial_index.py | 293 ++++++++++++++++++++++++++++++++++++ tests/test_spatial_index.py | 213 ++++++++++++++++++++++++++ 4 files changed, 632 insertions(+), 10 deletions(-) create mode 100644 picasso/spatial_index.py create mode 100644 tests/test_spatial_index.py diff --git a/changelog.md b/changelog.md index ccc4626b..59639444 100644 --- a/changelog.md +++ b/changelog.md @@ -1,9 +1,10 @@ # Changelog -Last change: 20-MAY-2026 CEST +Last change: 21-MAY-2026 CEST ## 0.10.1 - Faster rendering, especially in large FOV: ind. loc. precision blurring parallelized ., see [documentation](https://picassosr.readthedocs.io/en/latest/render.html/CPU-usage-on-shared-servers) + smarter implementation for 2D rendering; improvements for one-pixel-blur and global loc. prec. +- Multi-level spatial indexing for quick zoomed-in rendering ## 0.10.0 diff --git a/picasso/gui/render.py b/picasso/gui/render.py index e57792f1..4e583d75 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -46,6 +46,7 @@ masking, postprocess, render, + spatial_index, __version__, ) from ..lib import ( @@ -669,6 +670,7 @@ def _close_one_channel(self, i: int, render_=True) -> None: del self.window.view.locs_paths[i] del self.window.view.infos[i] del self.window.view.index_blocks[i] + del self.window.view.render_index[i] # delete zcoord from slicer dialog try: @@ -6279,6 +6281,10 @@ class View(QtWidgets.QLabel): rectangle_pick_start_x, rectangle_pick_start_y : float x and y coordinates of the starting edge of the drawn rectangular pick. + render_index : list of spatial_index.RenderIndexPyramids + For each loaded localization's channel, stores the z-order + (Morton code) indices of localizations for efficient indexing + of zoomed-in FOVs. rubberband : QRubberBand Draws a rectangle used in zooming in. _size_hint : tuple @@ -6321,6 +6327,7 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: self._picks = [] self._points = [] self.index_blocks = [] + self.render_index = [] self._drift = [] self._driftfiles = [] self.currentdrift = [] @@ -6396,6 +6403,12 @@ def add(self, path: str, render_: bool = True) -> None: self.infos.append(info) self.locs_paths.append(path) self.index_blocks.append(None) + try: + self.render_index.append( + spatial_index.build_render_index(locs, info) + ) + except Exception: + self.render_index.append(None) # try to load a drift .txt file: drift = self._load_drift(info[-1]) @@ -9276,16 +9289,90 @@ def pick_similar(self) -> None: self.add_picks(new_picks) status.close() - def _display_locs(self, channel: int) -> pd.DataFrame: + def _display_indices( + self, + channel: int, + viewport: ( + tuple[tuple[float, float], tuple[float, float]] | None + ) = None, + ) -> np.ndarray | None: + """Positional indices into ``self.locs[channel]`` selected for + display, or ``None`` when the full set is used. Combines the + fast-render subset and the viewport pyramid filter. + """ + if viewport is not None: + viewport_indices = self._viewport_indices(channel, viewport) + else: + viewport_indices = None + fast_idx = self.fast_render_indices[channel] + if viewport_indices is None and fast_idx is None: + return None + if fast_idx is None: + return viewport_indices + if viewport_indices is None: + return fast_idx + # Intersect via boolean mask -- viewport_indices is the larger + # set so testing membership against fast_idx is cheaper. + mask = np.zeros(len(self.locs[channel]), dtype=bool) + mask[fast_idx] = True + return viewport_indices[mask[viewport_indices]] + + def _display_locs( + self, + channel: int, + viewport: ( + tuple[tuple[float, float], tuple[float, float]] | None + ) = None, + ) -> pd.DataFrame: """Return the localizations currently selected for display in ``channel``. When ``fast_render_indices[channel]`` is ``None`` the full set is returned; otherwise the rows selected by the - fast-render dialog. Always returns a ``pd.DataFrame``.""" - idx = self.fast_render_indices[channel] + fast-render dialog. If ``viewport`` is given and a render-index + pyramid is available for the channel, the result is also + spatially restricted to that viewport. Always returns a + ``pd.DataFrame``.""" + idx = self._display_indices(channel, viewport) if idx is None: return self.locs[channel] return self.locs[channel].iloc[idx] + def _viewport_indices( + self, + channel: int, + viewport: tuple[tuple[float, float], tuple[float, float]], + ) -> np.ndarray | None: + """Indices of locs in ``channel`` that fall inside ``viewport``. + + Returns ``None`` if the pyramid is unavailable -- the caller + then falls back to the renderer's own brute-force in-view + filter, which is the previous behavior. + """ + pyramid = self._ensure_render_index(channel) + if pyramid is None: + return None + return spatial_index.query_viewport(pyramid, viewport) + + def _ensure_render_index( + self, channel: int + ) -> spatial_index.RenderIndexPyramid | None: + """Lazily (re)build the per-channel render-index pyramid. + + Returns ``None`` if the pyramid cannot be built (e.g., missing + FOV metadata) -- the rendering path then falls back to the + original brute-force viewport scan. + """ + pyramid = self.render_index[channel] + if pyramid is not None: + return pyramid + try: + pyramid = spatial_index.build_render_index( + self.locs[channel], self.infos[channel] + ) + except Exception: + pyramid = None + self.render_index[channel] = pyramid + return pyramid + def picked_locs( self, channel: int, @@ -9509,8 +9596,13 @@ def render_scene( min_blur_width=min_blur_width, blur_method=blur_method, ) - # apply z splicing if enabled + render property - locs, infos = self._prepare_locs_for_rendering() + # apply z splicing if enabled + render property; spatially + # restrict each channel to the active viewport via the + # render-index pyramid so the renderer doesn't have to do a + # full-N viewport scan on every redraw + locs, infos = self._prepare_locs_for_rendering( + viewport=kwargs["viewport"] + ) # prepare other keywords for rendering cmap = self.window.display_settings_dlg.colormap.currentText() @@ -9653,6 +9745,9 @@ def read_relative_intensities(self) -> list[float]: def _prepare_locs_for_rendering( self, + viewport: ( + tuple[tuple[float, float], tuple[float, float]] | None + ) = None, ) -> tuple[list[pd.DataFrame], list[list[dict]]]: """Prepare localizations and metadata for rendering with active filters. @@ -9661,7 +9756,12 @@ def _prepare_locs_for_rendering( - Render-by-property coloring if enabled (splits into x_locs); - Group-based splitting if group column exists; - Z-slice clipping if slicer is enabled; - - Channel filtering to only include checked channels.""" + - Channel filtering to only include checked channels. + + When ``viewport`` is provided, each per-channel locs DataFrame + is additionally restricted to the viewport via the render-index + pyramid for efficient rendering of zoomed-in FOVs. + """ slicer = self.window.slicer_dialog.slicer_radio_button # render by property - use x_locs like multichannel rendering if self.window.display_settings_dlg.render_check.isChecked(): @@ -9670,14 +9770,22 @@ def _prepare_locs_for_rendering( # not need to be rerun locs = self.x_locs.copy() infos = [self.infos[0]] * len(locs) + # Render-by-property: x_locs is precomputed and shares an + # index that depends on the property binning. The renderer's + # own brute-force in-view filter handles this case; the + # pyramid pre-filter is only applied to the multichannel + # path, which is the common redraw cost driver. # if group column is present, split locs by group for rendering else: # project fast-render subset (or full set when no - # subsampling) - locs = [self._display_locs(i) for i in range(len(self.locs))] + # subsampling), restricted to the viewport if given + locs = [ + self._display_locs(i, viewport=viewport) + for i in range(len(self.locs)) + ] infos = self.infos if "group" in locs[0].columns and len(locs) == 1: - idx = self.fast_render_indices[0] + idx = self._display_indices(0, viewport) group_color = ( self.group_color if idx is None else self.group_color[idx] ) @@ -10257,6 +10365,7 @@ def undrift_aim(self) -> None: self.locs[channel] = locs self.infos[channel] = new_info self.index_blocks[channel] = None + self.render_index[channel] = None self.add_drift(channel, drift) self.update_scene(resample_locs=True) self.show_drift() @@ -10302,6 +10411,7 @@ def undrift_rcc(self) -> None: ) # sanity check and assign attributes self.index_blocks[channel] = None + self.render_index[channel] = None self.add_drift(channel, drift) # ignore undrift_locs since we use _apply_drift to # assign attributes @@ -10349,6 +10459,7 @@ def undrift_from_picked(self) -> None: self.infos[channel] = new_info # Cleanup self.index_blocks[channel] = None + self.render_index[channel] = None self.add_drift(channel, drift) status.close() self.update_scene(resample_locs=True) @@ -10383,6 +10494,7 @@ def undrift_from_picked2d(self) -> None: self.infos[channel] = new_info # Cleanup self.index_blocks[channel] = None + self.render_index[channel] = None self.add_drift(channel, drift) status.close() self.update_scene(resample_locs=True) @@ -10410,6 +10522,7 @@ def _undo_drift(self, channel: int) -> None: self.locs[channel], self.infos[channel], drift=drift ) self.index_blocks[channel] = None + self.render_index[channel] = None self.add_drift(channel, drift) self.update_scene(resample_locs=True) @@ -10468,6 +10581,7 @@ def _apply_drift(self, channel: int, drift: pd.DataFrame) -> None: self._drift[channel] = drift self.currentdrift[channel] = copy.copy(drift) self.index_blocks[channel] = None + self.render_index[channel] = None self.update_scene(resample_locs=True) def unfold_groups_square(self) -> None: @@ -10739,6 +10853,7 @@ def _resample_fast_render(self) -> None: if len(dlg.fractions) == 2 and "group" in self.locs[0].columns: self.group_color = render.get_group_color(self.locs[0]) self.index_blocks = [None] * len(self.locs) + self.render_index = [None] * len(self.locs) self.window.display_settings_dlg.silent_maximum_update( factor * self.window.display_settings_dlg.maximum.value() ) diff --git a/picasso/spatial_index.py b/picasso/spatial_index.py new file mode 100644 index 00000000..a280dc05 --- /dev/null +++ b/picasso/spatial_index.py @@ -0,0 +1,293 @@ +"""Multi-resolution spatial index for fast viewport rendering. + +Built once when a channel is loaded; queried per redraw to skip the +O(N) viewport scan that ``picasso.render._render_setup`` would otherwise +perform on every pan/zoom. + +The pyramid stores three grid resolutions sharing a single permutation +sorted by Morton (Z-order) at the finest level. Because Z-order is +hierarchical, each coarser block at level L corresponds to a contiguous +range in the same sorted permutation -- so all levels reuse one ``perm`` +array (~4 N bytes) rather than one per level. + +See :mod:`picasso.postprocess` for the original single-resolution +``get_index_blocks`` used by pick/cluster code; this module is +intentionally separate so the pick code path is unaffected. + +:author: Rafal Kowalewski, 2026 +:copyright: Copyright (c) 2026 Jungmann Lab, MPI of Biochemistry +""" + +from __future__ import annotations + +from dataclasses import dataclass + +import numba +import numpy as np +import pandas as pd + +from . import lib + + +# Target upper bound on blocks per viewport edge at the chosen level. +# Tunable; ~64 keeps the inner gather loop tight while still letting the +# finest level cover small zoomed-in viewports. +_TARGET_BLOCKS_PER_EDGE = 64 + + +@dataclass +class RenderIndexPyramid: + """Multi-resolution spatial index over a single locs DataFrame. + + Attributes + ---------- + perm : IntArray1D, shape (N,), dtype uint32 + ``perm[i]`` is the original-locs index at sort position ``i``, + where the sort key is the Morton code of ``(x // base, y // base)`` + at the finest level. + block_sizes : tuple[float, ...] + Block side lengths in camera pixels, ascending. ``block_sizes[0]`` + is the finest level. + block_starts, block_ends : list[IntArray2D] + Per level, a ``(K_L, L_L)`` uint32 grid where + ``perm[block_starts[i, j]:block_ends[i, j]]`` are the + original-locs indices in block ``(i, j)``. + width, height : float + FOV size copied from ``info``, used by the query to clip block + rectangles. + """ + + perm: lib.IntArray1D + block_sizes: tuple[float, ...] + block_starts: list[lib.IntArray2D] + block_ends: list[lib.IntArray2D] + width: float + height: float + + +def _base_block_size(width: float, height: float) -> float: + """Pick the finest block size based on FOV. + + Targets ~256k blocks at the finest level for the common 512x512 - + 1024x1024 SMLM FOVs. Floor of 1.0 -- sub-pixel blocks would mostly + hold a single loc each and waste grid memory. + """ + return float(max(1.0, np.ceil(np.sqrt(width * height / 256_000.0)))) + + +@numba.njit(cache=True) +def _morton_encode_2d(x: lib.IntArray1D, y: lib.IntArray1D) -> lib.IntArray1D: + """Interleave bits of ``(x, y)`` into a Morton (Z-order) key. + + ``x`` and ``y`` are 32-bit unsigned block coordinates; the returned + key is uint64. Inputs above 2**16 are still handled because the + masks below interleave the full 32-bit input -- but typical SMLM + grids stay well below that. + """ + n = x.shape[0] + out = np.empty(n, dtype=np.uint64) + M0 = np.uint64(0x0000FFFF0000FFFF) + M1 = np.uint64(0x00FF00FF00FF00FF) + M2 = np.uint64(0x0F0F0F0F0F0F0F0F) + M3 = np.uint64(0x3333333333333333) + M4 = np.uint64(0x5555555555555555) + one = np.uint64(1) + for i in range(n): + xi = np.uint64(x[i]) + yi = np.uint64(y[i]) + xi = (xi | (xi << np.uint64(16))) & M0 + xi = (xi | (xi << np.uint64(8))) & M1 + xi = (xi | (xi << np.uint64(4))) & M2 + xi = (xi | (xi << np.uint64(2))) & M3 + xi = (xi | (xi << one)) & M4 + yi = (yi | (yi << np.uint64(16))) & M0 + yi = (yi | (yi << np.uint64(8))) & M1 + yi = (yi | (yi << np.uint64(4))) & M2 + yi = (yi | (yi << np.uint64(2))) & M3 + yi = (yi | (yi << one)) & M4 + out[i] = xi | (yi << one) + return out + + +@numba.njit(cache=True) +def _fill_blocks_from_sorted( + bx: lib.IntArray1D, + by: lib.IntArray1D, + block_starts: lib.IntArray2D, + block_ends: lib.IntArray2D, +) -> None: + """Fill ``block_starts``/``block_ends`` by single linear scan. + + Expects ``bx``/``by`` to be the block coordinates of each loc in the + pyramid sort order; because the sort is by Morton at the finest + level, locs sharing a block at *any* level form one contiguous run. + """ + n = bx.shape[0] + if n == 0: + return + cur_bx = bx[0] + cur_by = by[0] + block_starts[cur_by, cur_bx] = 0 + for k in range(1, n): + if bx[k] != cur_bx or by[k] != cur_by: + block_ends[cur_by, cur_bx] = k + cur_bx = bx[k] + cur_by = by[k] + block_starts[cur_by, cur_bx] = k + block_ends[cur_by, cur_bx] = n + + +def build_render_index( + locs: pd.DataFrame, + info: list[dict], + n_levels: int = 3, +) -> RenderIndexPyramid | None: + """Build the pyramid for one channel's locs. + + Returns ``None`` if required metadata is missing -- callers should + fall back to the existing brute-force viewport filter in that case. + """ + width = lib.get_from_metadata(info, "Width") + height = lib.get_from_metadata(info, "Height") + if width is None or height is None: + return None + width = float(width) + height = float(height) + + base = _base_block_size(width, height) + block_sizes = tuple(base * (4**lvl) for lvl in range(n_levels)) + + n = len(locs) + if n == 0: + block_starts = [] + block_ends = [] + for size in block_sizes: + K = max(1, int(np.ceil(height / size))) + L = max(1, int(np.ceil(width / size))) + block_starts.append(np.zeros((K, L), dtype=np.uint32)) + block_ends.append(np.zeros((K, L), dtype=np.uint32)) + return RenderIndexPyramid( + perm=np.empty(0, dtype=np.uint32), + block_sizes=block_sizes, + block_starts=block_starts, + block_ends=block_ends, + width=width, + height=height, + ) + + x = locs["x"].to_numpy() + y = locs["y"].to_numpy() + + # Block coords at the finest level, clipped to the grid. Out-of-FOV + # locs are pinned to the boundary so they stay queryable -- matches + # the existing renderer, which just doesn't draw them. + n_blocks_x0 = max(1, int(np.ceil(width / base))) + n_blocks_y0 = max(1, int(np.ceil(height / base))) + bx0 = np.clip(np.floor(x / base), 0, n_blocks_x0 - 1).astype(np.uint32) + by0 = np.clip(np.floor(y / base), 0, n_blocks_y0 - 1).astype(np.uint32) + + # Sort by Morton at finest level -> hierarchical contiguity. + keys = _morton_encode_2d(bx0, by0) + perm = np.argsort(keys, kind="stable").astype(np.uint32) + + block_starts = [] + block_ends = [] + for size in block_sizes: + L = max(1, int(np.ceil(width / size))) + K = max(1, int(np.ceil(height / size))) + bx_lvl = np.clip(np.floor(x[perm] / size), 0, L - 1).astype(np.uint32) + by_lvl = np.clip(np.floor(y[perm] / size), 0, K - 1).astype(np.uint32) + bs = np.zeros((K, L), dtype=np.uint32) + be = np.zeros((K, L), dtype=np.uint32) + _fill_blocks_from_sorted(bx_lvl, by_lvl, bs, be) + block_starts.append(bs) + block_ends.append(be) + + return RenderIndexPyramid( + perm=perm, + block_sizes=block_sizes, + block_starts=block_starts, + block_ends=block_ends, + width=width, + height=height, + ) + + +def _select_level(pyramid: RenderIndexPyramid, viewport: tuple) -> int: + """Pick the smallest level whose blocks per viewport edge <= target. + + Walking from finest to coarsest means we pick the finest level that + keeps block iteration bounded -- which also minimises the gathered + locs count (more blocks per coarse cell at coarser levels). + """ + (y_min, x_min), (y_max, x_max) = viewport + vp_dim = max(x_max - x_min, y_max - y_min) + for lvl, size in enumerate(pyramid.block_sizes): + if vp_dim / size <= _TARGET_BLOCKS_PER_EDGE: + return lvl + return len(pyramid.block_sizes) - 1 + + +@numba.njit(cache=True) +def _gather_blocks( + perm: lib.IntArray1D, + block_starts: lib.IntArray2D, + block_ends: lib.IntArray2D, + cy_min: int, + cy_max: int, + cx_min: int, + cx_max: int, +) -> lib.IntArray1D: + """Collect original-locs indices from all blocks in the rectangle.""" + total = 0 + for y in range(cy_min, cy_max + 1): + for x in range(cx_min, cx_max + 1): + total += block_ends[y, x] - block_starts[y, x] + out = np.empty(total, dtype=np.uint32) + pos = 0 + for y in range(cy_min, cy_max + 1): + for x in range(cx_min, cx_max + 1): + s = block_starts[y, x] + e = block_ends[y, x] + for k in range(s, e): + out[pos] = perm[k] + pos += 1 + return out + + +def query_viewport( + pyramid: RenderIndexPyramid, + viewport: tuple, +) -> lib.IntArray1D: + """Indices into the original locs DataFrame for locs in the viewport. + + The returned set is a superset of the strictly-inside locs: a block + at the viewport edge contributes all of its locs (the renderer's + own ``in_view`` test inside ``_render_setup`` then prunes the + overspill, on a tiny array). + """ + if pyramid.perm.shape[0] == 0: + return np.empty(0, dtype=np.uint32) + + lvl = _select_level(pyramid, viewport) + size = pyramid.block_sizes[lvl] + bs = pyramid.block_starts[lvl] + be = pyramid.block_ends[lvl] + K, L = bs.shape + + (y_min, x_min), (y_max, x_max) = viewport + cx_min = int(np.floor(x_min / size)) + cy_min = int(np.floor(y_min / size)) + # x_max/y_max are exclusive in the existing renderer (strict ``<``), + # so a value landing exactly on a block boundary belongs to the + # previous block. + cx_max = int(np.floor((x_max - 1e-9) / size)) + cy_max = int(np.floor((y_max - 1e-9) / size)) + cx_min = max(0, cx_min) + cy_min = max(0, cy_min) + cx_max = min(L - 1, cx_max) + cy_max = min(K - 1, cy_max) + if cx_min > cx_max or cy_min > cy_max: + return np.empty(0, dtype=np.uint32) + + return _gather_blocks(pyramid.perm, bs, be, cy_min, cy_max, cx_min, cx_max) diff --git a/tests/test_spatial_index.py b/tests/test_spatial_index.py new file mode 100644 index 00000000..29fabd3c --- /dev/null +++ b/tests/test_spatial_index.py @@ -0,0 +1,213 @@ +"""Tests for ``picasso.spatial_index``. + +Correctness goal: ``query_viewport`` must return a superset of the +strictly-inside locs (``x_min < x < x_max`` and same for y), and +``picasso.render`` invoked on the pyramid-filtered subset must produce +the same image as on the full locs DataFrame for every blur method. + +:author: Rafal Kowalewski, 2026 +:copyright: Copyright (c) 2026 Jungmann Lab, MPI of Biochemistry +""" + +from __future__ import annotations + +import numpy as np +import pandas as pd +import pytest + +from picasso import render, spatial_index + + +def _make_locs( + n: int, width: float, height: float, seed: int = 0 +) -> pd.DataFrame: + rng = np.random.default_rng(seed) + return pd.DataFrame( + { + "x": rng.uniform(0.0, width, size=n), + "y": rng.uniform(0.0, height, size=n), + "lpx": rng.uniform(0.05, 0.3, size=n), + "lpy": rng.uniform(0.05, 0.3, size=n), + "photons": rng.uniform(500.0, 5000.0, size=n), + "frame": rng.integers(0, 1000, size=n).astype(np.int32), + } + ) + + +def _info(width: float, height: float, n_frames: int = 1000) -> list[dict]: + return [ + { + "Width": width, + "Height": height, + "Frames": n_frames, + "Pixelsize": 130.0, + } + ] + + +def _brute_force_in_view(locs: pd.DataFrame, viewport) -> np.ndarray: + (y_min, x_min), (y_max, x_max) = viewport + x = locs["x"].to_numpy() + y = locs["y"].to_numpy() + mask = (x > x_min) & (y > y_min) & (x < x_max) & (y < y_max) + return np.nonzero(mask)[0].astype(np.uint32) + + +# --------------------------------------------------------------------------- +# Build +# --------------------------------------------------------------------------- + + +class TestBuild: + def test_empty_locs_returns_pyramid(self): + locs = _make_locs(0, 512, 512) + pyr = spatial_index.build_render_index(locs, _info(512, 512)) + assert pyr is not None + assert pyr.perm.shape == (0,) + # Querying anything should return empty. + assert spatial_index.query_viewport( + pyr, ((0, 0), (512, 512)) + ).shape == (0,) + + def test_missing_metadata_returns_none(self): + locs = _make_locs(100, 512, 512) + assert spatial_index.build_render_index(locs, [{}]) is None + + def test_perm_is_a_permutation(self): + n = 10_000 + locs = _make_locs(n, 512, 512) + pyr = spatial_index.build_render_index(locs, _info(512, 512)) + assert pyr.perm.shape == (n,) + assert np.array_equal(np.sort(pyr.perm), np.arange(n, dtype=np.uint32)) + + def test_levels_partition_total_count(self): + locs = _make_locs(5_000, 576, 576) + pyr = spatial_index.build_render_index(locs, _info(576, 576)) + for bs, be in zip(pyr.block_starts, pyr.block_ends): + # Every loc belongs to exactly one block at each level. + assert int((be - bs).sum()) == len(locs) + # Block ranges don't overlap and don't go past N. + assert (be >= bs).all() + assert int(be.max()) <= len(locs) + + def test_block_sizes_geometric(self): + pyr = spatial_index.build_render_index( + _make_locs(100, 512, 512), _info(512, 512) + ) + assert len(pyr.block_sizes) == 3 + # 4x ratio between successive levels. + assert pyr.block_sizes[1] == pytest.approx(4 * pyr.block_sizes[0]) + assert pyr.block_sizes[2] == pytest.approx(4 * pyr.block_sizes[1]) + + +# --------------------------------------------------------------------------- +# Query correctness vs brute force +# --------------------------------------------------------------------------- + + +class TestQuery: + @pytest.mark.parametrize("seed", [0, 1, 2, 3, 4]) + def test_query_superset_of_strict_in_view(self, seed): + rng = np.random.default_rng(seed) + W, H = 512.0, 512.0 + locs = _make_locs(20_000, W, H, seed=seed) + pyr = spatial_index.build_render_index(locs, _info(W, H)) + + # Random viewport inside the FOV. + cx, cy = rng.uniform(50, W - 50), rng.uniform(50, H - 50) + half_w, half_h = rng.uniform(5, 200), rng.uniform(5, 200) + viewport = ( + (cy - half_h, cx - half_w), + (cy + half_h, cx + half_w), + ) + + idx = spatial_index.query_viewport(pyr, viewport) + truth = _brute_force_in_view(locs, viewport) + # Superset: every strictly-in-view loc must be returned. + assert set(int(i) for i in truth).issubset(set(int(i) for i in idx)) + + def test_viewport_covering_full_fov_returns_all_locs(self): + W, H = 512.0, 512.0 + n = 5000 + locs = _make_locs(n, W, H) + pyr = spatial_index.build_render_index(locs, _info(W, H)) + idx = spatial_index.query_viewport(pyr, ((0, 0), (H, W))) + assert set(int(i) for i in idx) == set(range(n)) + + def test_viewport_outside_fov_returns_empty(self): + locs = _make_locs(1000, 512, 512) + pyr = spatial_index.build_render_index(locs, _info(512, 512)) + # Far outside the FOV in both directions. + idx = spatial_index.query_viewport(pyr, ((2000, 2000), (3000, 3000))) + assert idx.shape == (0,) + + def test_tiny_zoomed_viewport_returns_few_locs(self): + W, H = 512.0, 512.0 + n = 50_000 + locs = _make_locs(n, W, H) + pyr = spatial_index.build_render_index(locs, _info(W, H)) + viewport = ((100.0, 100.0), (105.0, 105.0)) # 5x5 patch + idx = spatial_index.query_viewport(pyr, viewport) + # Expected ~ n * (5*5) / (W*H) = ~50_000 * 25 / 262144 ~= 4-5 locs + # plus some overspill from the chosen block size. Cap at a + # generous but tight bound: still tiny fraction of n. + assert len(idx) < 200 + # And every strict-inside loc must be present. + truth = _brute_force_in_view(locs, viewport) + assert set(int(i) for i in truth).issubset(set(int(i) for i in idx)) + + +# --------------------------------------------------------------------------- +# Renderer parity: pre-filtering by pyramid must not change the image +# --------------------------------------------------------------------------- + + +class TestRendererParity: + @pytest.fixture(scope="class") + def locs_pyr(self): + W, H = 512.0, 512.0 + n = 30_000 + locs = _make_locs(n, W, H, seed=42) + info = _info(W, H) + pyr = spatial_index.build_render_index(locs, info) + return locs, info, pyr + + @pytest.mark.parametrize( + "blur_method", [None, "gaussian", "gaussian_iso", "smooth", "convolve"] + ) + def test_parity_with_full_locs(self, locs_pyr, blur_method): + locs, info, pyr = locs_pyr + # Slightly off-center, asymmetric viewport so the chosen pyramid + # level isn't trivially the coarsest. + viewport = ((40.0, 60.0), (180.0, 240.0)) + oversampling = 4.0 + + idx = spatial_index.query_viewport(pyr, viewport) + filtered = locs.iloc[idx] + + n_full, img_full = render.render( + locs, + info, + oversampling=oversampling, + viewport=viewport, + blur_method=blur_method, + ) + n_filt, img_filt = render.render( + filtered, + info, + oversampling=oversampling, + viewport=viewport, + blur_method=blur_method, + ) + + assert n_full == n_filt + # ``gaussian``/``gaussian_iso`` use parallel summation across + # locs; reordering the input via the pyramid query changes the + # summation order and introduces float32 round-off well below + # any visual threshold. Histogram modes are exact. + if blur_method in (None, "smooth", "convolve"): + np.testing.assert_array_equal(img_full, img_filt) + else: + np.testing.assert_allclose( + img_full, img_filt, rtol=1e-5, atol=1e-6 + ) From b2064de85c1646edc52618847ae78186aeadd301 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 21 May 2026 12:54:56 +0200 Subject: [PATCH 09/42] improve spatial indexing for whole FOVs --- picasso/spatial_index.py | 36 ++++++++++++++- tests/test_spatial_index.py | 88 ++++++++++++++++++++++++++++++++----- 2 files changed, 110 insertions(+), 14 deletions(-) diff --git a/picasso/spatial_index.py b/picasso/spatial_index.py index a280dc05..61ae779f 100644 --- a/picasso/spatial_index.py +++ b/picasso/spatial_index.py @@ -34,6 +34,13 @@ # finest level cover small zoomed-in viewports. _TARGET_BLOCKS_PER_EDGE = 64 +# Viewport-to-FOV area ratio at/above which ``query_viewport`` bypasses +# the pyramid and returns ``None``. The caller then renders the full +# locs DataFrame and lets the renderer's vectorised ``in_view`` mask do +# the filtering -- avoiding a pandas ``iloc`` copy of nearly all rows, +# which dominates redraw cost at full-FOV (see ``query_viewport``). +_BYPASS_COVERAGE_RATIO = 0.25 + @dataclass class RenderIndexPyramid: @@ -258,14 +265,40 @@ def _gather_blocks( def query_viewport( pyramid: RenderIndexPyramid, viewport: tuple, -) -> lib.IntArray1D: +) -> lib.IntArray1D | None: """Indices into the original locs DataFrame for locs in the viewport. The returned set is a superset of the strictly-inside locs: a block at the viewport edge contributes all of its locs (the renderer's own ``in_view`` test inside ``_render_setup`` then prunes the overspill, on a tiny array). + + Returns ``None`` when the viewport covers (most of) the FOV -- + above ``_BYPASS_COVERAGE_RATIO`` of the FOV area, or fully + enclosing it. In that regime gathering ~N indices and copying the + DataFrame via ``iloc`` costs more than letting the renderer scan + the full locs with its vectorised ``in_view`` mask. The caller + treats ``None`` as "no pre-filter, use the full locs". """ + (y_min, x_min), (y_max, x_max) = viewport + # Bypass for (near-)full-FOV viewports -- see module-level constant. + if ( + x_min <= 0.0 + and y_min <= 0.0 + and x_max >= pyramid.width + and y_max >= pyramid.height + ): + return None + fov_area = pyramid.width * pyramid.height + if fov_area > 0.0: + cx0 = max(0.0, x_min) + cy0 = max(0.0, y_min) + cx1 = min(pyramid.width, x_max) + cy1 = min(pyramid.height, y_max) + clipped_area = max(0.0, cx1 - cx0) * max(0.0, cy1 - cy0) + if clipped_area / fov_area >= _BYPASS_COVERAGE_RATIO: + return None + if pyramid.perm.shape[0] == 0: return np.empty(0, dtype=np.uint32) @@ -275,7 +308,6 @@ def query_viewport( be = pyramid.block_ends[lvl] K, L = bs.shape - (y_min, x_min), (y_max, x_max) = viewport cx_min = int(np.floor(x_min / size)) cy_min = int(np.floor(y_min / size)) # x_max/y_max are exclusive in the existing renderer (strict ``<``), diff --git a/tests/test_spatial_index.py b/tests/test_spatial_index.py index 29fabd3c..a34935f0 100644 --- a/tests/test_spatial_index.py +++ b/tests/test_spatial_index.py @@ -64,10 +64,9 @@ def test_empty_locs_returns_pyramid(self): pyr = spatial_index.build_render_index(locs, _info(512, 512)) assert pyr is not None assert pyr.perm.shape == (0,) - # Querying anything should return empty. - assert spatial_index.query_viewport( - pyr, ((0, 0), (512, 512)) - ).shape == (0,) + # Sub-threshold viewport hits the gather path and returns empty. + idx = spatial_index.query_viewport(pyr, ((0, 0), (10, 10))) + assert idx is not None and idx.shape == (0,) def test_missing_metadata_returns_none(self): locs = _make_locs(100, 512, 512) @@ -113,9 +112,10 @@ def test_query_superset_of_strict_in_view(self, seed): locs = _make_locs(20_000, W, H, seed=seed) pyr = spatial_index.build_render_index(locs, _info(W, H)) - # Random viewport inside the FOV. + # Random viewport inside the FOV, kept well under the bypass + # coverage ratio so this test exercises the gather path. cx, cy = rng.uniform(50, W - 50), rng.uniform(50, H - 50) - half_w, half_h = rng.uniform(5, 200), rng.uniform(5, 200) + half_w, half_h = rng.uniform(5, 100), rng.uniform(5, 100) viewport = ( (cy - half_h, cx - half_w), (cy + half_h, cx + half_w), @@ -124,22 +124,86 @@ def test_query_superset_of_strict_in_view(self, seed): idx = spatial_index.query_viewport(pyr, viewport) truth = _brute_force_in_view(locs, viewport) # Superset: every strictly-in-view loc must be returned. + assert idx is not None assert set(int(i) for i in truth).issubset(set(int(i) for i in idx)) - def test_viewport_covering_full_fov_returns_all_locs(self): + def test_viewport_covering_full_fov_returns_none(self): + # Above the coverage bypass threshold the pyramid returns None + # so the caller renders the full locs DataFrame without an + # iloc copy -- this is what restores the pre-pyramid full-FOV + # render speed for large N. W, H = 512.0, 512.0 - n = 5000 - locs = _make_locs(n, W, H) + locs = _make_locs(5000, W, H) + pyr = spatial_index.build_render_index(locs, _info(W, H)) + assert spatial_index.query_viewport(pyr, ((0, 0), (H, W))) is None + + def test_viewport_above_bypass_threshold_returns_none(self): + # Half the FOV area in each dimension -> 25% coverage, below + # the default 0.5 threshold, so we still get an array. Doubling + # one dimension to span the full axis brings coverage to 50% + # and triggers the bypass. + W, H = 512.0, 512.0 + locs = _make_locs(5000, W, H) + pyr = spatial_index.build_render_index(locs, _info(W, H)) + sub = spatial_index.query_viewport(pyr, ((0, 0), (H / 2, W / 2))) + assert sub is not None and sub.shape[0] > 0 + assert spatial_index.query_viewport(pyr, ((0, 0), (H, W / 2))) is None + + def test_viewport_with_negative_bounds_enclosing_fov_returns_none(self): + # Zoomed/panned out so the viewport extends past every FOV edge + # (loc coords are always positive but the viewport isn't). + # All locs are in view -> bypass via the full-enclose check. + W, H = 512.0, 512.0 + locs = _make_locs(5000, W, H) pyr = spatial_index.build_render_index(locs, _info(W, H)) - idx = spatial_index.query_viewport(pyr, ((0, 0), (H, W))) - assert set(int(i) for i in idx) == set(range(n)) + assert ( + spatial_index.query_viewport( + pyr, ((-100.0, -200.0), (H + 50.0, W + 300.0)) + ) + is None + ) + + def test_viewport_overhanging_right_bottom_clips_correctly(self): + # Locs are bounded in (0, W) x (0, H) but viewports aren't -- + # users can pan/zoom past the bottom-right. The clipped area + # must drive the bypass decision, not the raw viewport extent. + W, H = 512.0, 512.0 + locs = _make_locs(5000, W, H) + pyr = spatial_index.build_render_index(locs, _info(W, H)) + # ((100, 100), (700, 700)) overhangs by 188 on each side. + # Clipped intersection: 412x412 -> ~65% of FOV -> bypass. + assert ( + spatial_index.query_viewport(pyr, ((100.0, 100.0), (700.0, 700.0))) + is None + ) + # Thin overhanging strip: 412x50 -> ~7.9% of FOV -> gather. + idx = spatial_index.query_viewport( + pyr, ((100.0, 100.0), (150.0, 700.0)) + ) + assert idx is not None + truth = _brute_force_in_view(locs, ((100.0, 100.0), (150.0, 700.0))) + assert set(int(i) for i in truth).issubset(set(int(i) for i in idx)) + + def test_viewport_partially_negative_below_threshold_returns_array(self): + # Viewport overhangs the FOV on one side only; the FOV-clipped + # area is well under the bypass threshold, so the gather path + # still runs and returns a valid array. + W, H = 512.0, 512.0 + locs = _make_locs(5000, W, H) + pyr = spatial_index.build_render_index(locs, _info(W, H)) + idx = spatial_index.query_viewport( + pyr, ((-100.0, -100.0), (100.0, 100.0)) + ) + assert idx is not None + truth = _brute_force_in_view(locs, ((-100.0, -100.0), (100.0, 100.0))) + assert set(int(i) for i in truth).issubset(set(int(i) for i in idx)) def test_viewport_outside_fov_returns_empty(self): locs = _make_locs(1000, 512, 512) pyr = spatial_index.build_render_index(locs, _info(512, 512)) # Far outside the FOV in both directions. idx = spatial_index.query_viewport(pyr, ((2000, 2000), (3000, 3000))) - assert idx.shape == (0,) + assert idx is not None and idx.shape == (0,) def test_tiny_zoomed_viewport_returns_few_locs(self): W, H = 512.0, 512.0 From 001c52625242b3e4f19ecbe3d38bd0153a69078b Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 21 May 2026 16:06:17 +0200 Subject: [PATCH 10/42] Fixed linking saving `lpz` --- changelog.md | 6 +++++- picasso/postprocess.py | 26 +++++++++++++++++++------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index 13bcc9ab..3ba89927 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,10 @@ # Changelog -Last change: 19-MAY-2026 CEST +Last change: 21-MAY-2026 CEST + +## 0.10.1 + +- Fixed linking saving `lpz` ## 0.10.0 diff --git a/picasso/postprocess.py b/picasso/postprocess.py index 7e036f75..9b68581f 100644 --- a/picasso/postprocess.py +++ b/picasso/postprocess.py @@ -2764,13 +2764,25 @@ def _link_loc_groups( # noqa: C901 locs["iterations"].to_numpy(), link_group, n_locs, n_groups, n_ ) if "z" in locs.columns: - columns["z"] = _link_group_mean( - locs["z"].to_numpy(), - link_group, - n_locs, - n_groups, - n_, - ) + if "lpz" in locs.columns: + weights_z = 1 / locs["lpz"].to_numpy() ** 2 + columns["z"], sum_weights_z_ = _link_group_weighted_mean( + locs["z"].to_numpy(), + weights_z, + link_group, + n_locs, + n_groups, + n_, + ) + columns["lpz"] = np.sqrt(1 / sum_weights_z_) + else: + columns["z"] = _link_group_mean( + locs["z"].to_numpy(), + link_group, + n_locs, + n_groups, + n_, + ) if "d_zcalib" in locs.columns: columns["d_zcalib"] = _link_group_mean( locs["d_zcalib"].to_numpy(), link_group, n_locs, n_groups, n_ From 6043a603dd0dc2a894de8f4e91ae925a94551487 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 21 May 2026 16:48:26 +0200 Subject: [PATCH 11/42] anisotropic dbscan + improved dosctrings for smlm clusterer 3d --- changelog.md | 2 + picasso/__main__.py | 33 ++++++++++- picasso/clusterer.py | 43 ++++++++++---- picasso/gui/render.py | 74 +++++++++++++++++++----- readme.rst | 1 + release/one_click_macos_gui/readme.rst | 1 + release/one_click_windows_gui/readme.rst | 1 + tests/test_clusterer.py | 29 ++++++++++ 8 files changed, 156 insertions(+), 28 deletions(-) diff --git a/changelog.md b/changelog.md index 3ba89927..9ce6319c 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,8 @@ Last change: 21-MAY-2026 CEST ## 0.10.1 - Fixed linking saving `lpz` +- Anisotripic DBSCAN (faster implementation) [DOI: 10.1021/acs.jpcb.4c02030](https://doi.org/10.1021/acs.jpcb.4c02030) +- Improved docstrings for 3D SMLM clusterer ## 0.10.0 diff --git a/picasso/__main__.py b/picasso/__main__.py index ed87a8c8..db6f8320 100644 --- a/picasso/__main__.py +++ b/picasso/__main__.py @@ -582,6 +582,7 @@ def _dbscan( radius: float, min_density: float, pixelsize: float | None = None, + radius_z: float | None = None, ) -> None: """Run DBSCAN clustering on localizations in HDF5 files. See ``clusterer.dbscan`` for details.""" @@ -594,14 +595,25 @@ def _dbscan( for path in paths: print("Loading {} ...".format(path)) locs, info = io.load_locs(path) - locs = clusterer.dbscan(locs, radius, min_density, pixelsize) + locs = clusterer.dbscan( + locs, + radius, + min_density, + pixelsize=pixelsize, + radius_z=radius_z, + ) clusters = clusterer.find_cluster_centers(locs, pixelsize) base, _ = os.path.splitext(path) + unit = "cam. px" if pixelsize is None else "nm" + if pixelsize is None: + pixelsize = 1 dbscan_info = { "Generated by": f"Picasso v{__version__} DBSCAN", - "Radius": radius, + f"Radius ({unit})": radius * pixelsize, "Minimum local density": min_density, } + if radius_z is not None: + dbscan_info["Radius_z (nm)"] = radius_z * pixelsize info.append(dbscan_info) io.save_locs(base + "_dbscan.hdf5", locs, info) io.save_locs(base + "_dbclusters.hdf5", clusters, info) @@ -2469,6 +2481,15 @@ def main(): # noqa: C901 help=("camera pixel size in nm (required for 3D localizations only)"), default=None, ) + dbscan_parser.add_argument( + "--radius_z", + type=float, + help=( + "DBSCAN epsilon in z (camera pixels). If set, enables " + "anisotropic 3D clustering." + ), + default=None, + ) # HDBSCAN hdbscan_parser = subparsers.add_parser( @@ -2899,7 +2920,13 @@ def main(): # noqa: C901 elif args.command == "density": _density(args.files, args.radius) elif args.command == "dbscan": - _dbscan(args.files, args.radius, args.density, args.pixelsize) + _dbscan( + args.files, + args.radius, + args.density, + args.pixelsize, + args.radius_z, + ) elif args.command == "hdbscan": _hdbscan( args.files, diff --git a/picasso/clusterer.py b/picasso/clusterer.py index 2b503519..8e9dfe1f 100644 --- a/picasso/clusterer.py +++ b/picasso/clusterer.py @@ -247,18 +247,22 @@ def cluster_3D( ) -> lib.IntArray1D: """Prepare 3D input to be used by ``_cluster``. - Scales z coordinates by radius_xy / radius_z + Scales z coordinates by ``radius_xy / radius_z`` so that a Euclidean + neighborhood search with radius ``radius_xy`` in the scaled space + corresponds to an ellipsoidal neighborhood with semi-axes + ``(radius_xy, radius_xy, radius_z)`` in the original space. Parameters ---------- - x, y, z : np.ndarray - x, y, and z coordinates to be clustered. - frame : np.ndarray - Frame number for each localization. + locs : pd.DataFrame + Localizations to be clustered. radius_xy : float - Clustering radius in x and y directions. + Clustering radius in x and y directions, in the same units as + ``locs["x"]`` / ``locs["y"]`` (camera pixels). radius_z : float - Clustering radius in z direction. + Clustering radius in z direction, in the same units as + ``locs["z"]`` (camera pixels after ``cluster()``'s nm-to-px + conversion). min_locs : int Minimum number of localizations in a cluster. fa : bool @@ -444,19 +448,28 @@ def dbscan( min_samples: int, min_locs: int = 10, pixelsize: float | None = None, + radius_z: float | None = None, return_info: bool = None, # TODO: change to true in v0.11.0 and remove in v0.12.0 ) -> tuple[pd.DataFrame, dict] | pd.DataFrame: """Perform DBSCAN on localizations. See Ester, et al. Inkdd, 1996. (Vol. 96, No. 34, pp. 226-231). + For 3D data with ``radius_z`` set, anisotropic clustering is used: + z coordinates are scaled by ``radius / radius_z`` so that the + isotropic DBSCAN search with epsilon ``radius`` corresponds to an + ellipsoidal neighborhood with semi-axes + ``(radius, radius, radius_z)`` in the original space (same approach + as ``cluster_3D``). + Parameters --------- locs : pd.DataFrame Localizations to be clustered. radius : float - DBSCAN search radius, often referred to as "epsilon". Same units - as locs. + DBSCAN search radius in the xy plane, usually referred to as + "epsilon". Same units as ``locs["x"]`` / ``locs["y"]`` + (camera pixels). min_samples : int Number of localizations within radius to consider a given point a core sample. @@ -465,6 +478,10 @@ def dbscan( fewer localizations will be removed. Default is 0. pixelsize : float, optional Camera pixel size in nm. Only needed for 3D. + radius_z : float, optional + DBSCAN search radius in z (camera pixels). If None (default), + the clustering is isotropic and uses ``radius`` in all + dimensions. Only used for 3D. return_info : bool, optional If True, returns a tuple of (locs, info), where locs is the clustered localizations and info is a dictionary containing @@ -499,21 +516,25 @@ def dbscan( ) X = locs[["x", "y", "z"]].to_numpy() X[:, 2] /= pixelsize + if radius_z is not None: + X[:, 2] *= radius / radius_z else: X = locs[["x", "y"]].to_numpy() labels = _dbscan(X, radius, min_samples, min_locs) locs = extract_valid_labels(locs, labels) n_clusters = len(locs) unit = "nm" if pixelsize is not None else "px" - pixelsize = pixelsize if pixelsize is not None else 1 + pixelsize_unit = pixelsize if pixelsize is not None else 1 info = { "Generated by": f"Picasso v{__version__} DBSCAN", "Number of clusters": len(np.unique(locs["group"])), - f"Radius ({unit})": radius * pixelsize, + f"Radius ({unit})": radius * pixelsize_unit, "Minimum local density": min_samples, "Min. localizations per cluster": min_locs, "Fraction of rejected locs (%)": 100 * (n_raw - n_clusters) / n_raw, } + if "z" in locs.columns and radius_z is not None: + info[f"Radius z ({unit})"] = radius_z * pixelsize_unit if return_info: return locs, info else: diff --git a/picasso/gui/render.py b/picasso/gui/render.py index e57792f1..66fedcf6 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -1697,7 +1697,10 @@ class DbscanDialog(lib.Dialog): min_locs : QSpinBox Contains the minimum number of locs in a cluster. radius : QDoubleSpinBox - Contains epsilon (camera pixels) for DBSCAN (see scikit-learn). + Contains epsilon (nm) for DBSCAN (see scikit-learn). + radius_z : QDoubleSpinBox + Contains epsilon in the z direction (nm) for anisotropic 3D + DBSCAN. Ignored for 2D data. save_areas : QCheckBox Whether to save cluster areas as .csv file. save_centers : QCheckBox @@ -1722,26 +1725,41 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: self.radius.setDecimals(2) self.radius.setSingleStep(0.1) grid.addWidget(self.radius, 0, 1) + radius_z_label = QtWidgets.QLabel("Radius z (3D only, nm):") + radius_z_label.setToolTip( + "DBSCAN epsilon in the z direction. Only used for 3D data.\n" + "Scales z coordinates so the neighborhood is an ellipsoid\n" + "with semi-axes (radius, radius, radius z).\n" + "Anisotropic DBSCAN approach inspired by Lörzing, Schake,\n" + "and Schlierf, Journal of Phys Chem B, 2024." + ) + grid.addWidget(radius_z_label, 1, 0) + self.radius_z = QtWidgets.QDoubleSpinBox() + self.radius_z.setRange(0.01, 1e6) + self.radius_z.setValue(25) + self.radius_z.setDecimals(2) + self.radius_z.setSingleStep(0.1) + grid.addWidget(self.radius_z, 1, 1) min_samples_label = QtWidgets.QLabel("Min. samples:") min_samples_label.setToolTip( "Minimum number of samples in a neighborhood for a point to be\n" "considered a core point." ) - grid.addWidget(min_samples_label, 1, 0) + grid.addWidget(min_samples_label, 2, 0) self.density = QtWidgets.QSpinBox() self.density.setRange(1, int(1e6)) self.density.setValue(4) - grid.addWidget(self.density, 1, 1) + grid.addWidget(self.density, 2, 1) minlocs_label = QtWidgets.QLabel("Min. no. of locs:") minlocs_label.setToolTip( "Minimum number of localizations required to consider a\n" "cluster valid." ) - grid.addWidget(minlocs_label, 2, 0) + grid.addWidget(minlocs_label, 3, 0) self.min_locs = QtWidgets.QSpinBox() self.min_locs.setRange(0, int(1e6)) self.min_locs.setValue(0) - grid.addWidget(self.min_locs, 2, 1) + grid.addWidget(self.min_locs, 3, 1) vbox.addLayout(grid) hbox = QtWidgets.QHBoxLayout() vbox.addLayout(hbox) @@ -1751,14 +1769,14 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: "Save an extra .hdf5 file containing the cluster centers?" ) self.save_centers.setChecked(False) - grid.addWidget(self.save_centers, 3, 0, 1, 2) + grid.addWidget(self.save_centers, 4, 0, 1, 2) # save cluster areas self.save_areas = QtWidgets.QCheckBox("Save cluster areas (.csv)") self.save_areas.setToolTip( "Save an extra .csv file containing the cluster areas?" ) self.save_areas.setChecked(False) - grid.addWidget(self.save_areas, 4, 0, 1, 2) + grid.addWidget(self.save_areas, 5, 0, 1, 2) # OK and Cancel buttons self.buttons = QtWidgets.QDialogButtonBox( @@ -1781,6 +1799,7 @@ def getParams( result = dialog.exec() return { "radius": dialog.radius.value(), + "radius_z": dialog.radius_z.value(), "min_density": dialog.density.value(), "min_locs": dialog.min_locs.value(), "save_centers": dialog.save_centers.isChecked(), @@ -2821,6 +2840,9 @@ def get_cluster_params(self) -> dict: params["radius"] = ( self.test_dbscan_params.radius.value() / pixelsize ) + params["radius_z"] = ( + self.test_dbscan_params.radius_z.value() / pixelsize + ) params["min_samples"] = self.test_dbscan_params.min_samples.value() params["min_locs"] = self.test_dbscan_params.min_locs.value() elif clusterer_name == "HDBSCAN": @@ -2968,6 +2990,7 @@ def _apply_to_all(self, channel: int, path: str) -> None: radius=params["radius"] * pixelsize, min_density=params["min_samples"], min_locs=params["min_locs"], + radius_z=params["radius_z"] * pixelsize, save_centers=save_centers, ) elif self.clusterer_name.currentText() == "HDBSCAN": @@ -3010,7 +3033,7 @@ def __init__(self, dialog): super().__init__() self.dialog = dialog grid = QtWidgets.QGridLayout(self) - radius_label = QtWidgets.QLabel("Radius (nm):") + radius_label = QtWidgets.QLabel("Radius xy (nm):") radius_label.setToolTip( "DBSCAN epsilon; max. distance between two samples for one to be\n" "considered as in the same neighborhood." @@ -3023,31 +3046,44 @@ def __init__(self, dialog): self.radius.setSingleStep(0.1) grid.addWidget(self.radius, 0, 1) + radius_z_label = QtWidgets.QLabel("Radius z (3D only, nm):") + radius_z_label.setToolTip( + "DBSCAN epsilon in the z direction. Only used for 3D data.\n" + "Scales z coordinates so the neighborhood is an ellipsoid\n" + "with semi-axes (radius xy, radius xy, radius z)." + ) + grid.addWidget(radius_z_label, 1, 0) + self.radius_z = QtWidgets.QDoubleSpinBox() + self.radius_z.setRange(0.01, 1e6) + self.radius_z.setValue(25) + self.radius_z.setDecimals(2) + self.radius_z.setSingleStep(0.1) + grid.addWidget(self.radius_z, 1, 1) + min_samples_label = QtWidgets.QLabel("Min. samples:") min_samples_label.setToolTip( "Minimum number of samples in a neighborhood for a point to be\n" "considered a core point." ) - grid.addWidget(min_samples_label, 1, 0) + grid.addWidget(min_samples_label, 2, 0) self.min_samples = QtWidgets.QSpinBox() self.min_samples.setValue(4) self.min_samples.setRange(1, int(1e6)) self.min_samples.setSingleStep(1) - grid.addWidget(self.min_samples, 1, 1) - grid.setRowStretch(2, 1) + grid.addWidget(self.min_samples, 2, 1) minlocs_label = QtWidgets.QLabel("Min. no. of locs:") minlocs_label.setToolTip( "Minimum number of localizations required to consider a\n" "cluster valid." ) - grid.addWidget(minlocs_label, 2, 0) + grid.addWidget(minlocs_label, 3, 0) self.min_locs = QtWidgets.QSpinBox() self.min_locs.setValue(0) self.min_locs.setRange(0, int(1e6)) self.min_locs.setSingleStep(1) - grid.addWidget(self.min_locs, 2, 1) - grid.setRowStretch(3, 1) + grid.addWidget(self.min_locs, 3, 1) + grid.setRowStretch(4, 1) class TestHDBSCANParams(QtWidgets.QWidget): @@ -6715,6 +6751,7 @@ def _dbscan( radius: float, min_density: int, min_locs: int, + radius_z: float | None = None, save_centers: bool = False, save_areas: bool = False, ) -> None: @@ -6733,6 +6770,9 @@ def _dbscan( Minimum local density for DBSCAN clustering. min_locs : int Minimum number of localizations in a cluster. + radius_z : float, optional + Radius in z for anisotropic 3D DBSCAN, in nm. Ignored for + 2D data. save_centers : bool, optional Specifies if cluster centers should be saved. Default is False. @@ -6751,12 +6791,18 @@ def _dbscan( locs = self.locs[channel] pixelsize = self.pixelsize + # Only pass radius_z for 3D data; convert nm -> camera pixels. + is_3d = "z" in locs.columns + radius_z_px = ( + radius_z / pixelsize if (is_3d and radius_z is not None) else None + ) locs, dbscan_info = clusterer.dbscan( locs, radius / pixelsize, # convert to camera pixels min_density, pixelsize=pixelsize, min_locs=min_locs, + radius_z=radius_z_px, return_info=True, ) io.save_locs(path, locs, self.infos[channel] + [dbscan_info]) diff --git a/readme.rst b/readme.rst index de37dd23..06661d35 100644 --- a/readme.rst +++ b/readme.rst @@ -145,6 +145,7 @@ If you use Picasso in your research, please cite our Nature Protocols publicatio - AIM undrifting. DOI: `10.1126/sciadv.adm776 `__ - SMLM clusterer. DOIs: `10.1038/s41467-021-22606-1 `__ and `10.1038/s41586-023-05925-9 `__ - DBSCAN: Ester, et al. Inkdd, 1996. (Vol. 96, No. 34, pp. 226-231). +- Anisotropic DBSCAN inspired by: `10.1021/acs.jpcb.4c02030 `__ - HDBSCAN. DOI: `10.1007/978-3-642-37456-2_14 `__ - RESI. DOI: `10.1038/s41586-023-05925-9 `__ - Nanotron. DOI: `10.1093/bioinformatics/btaa154 `__ diff --git a/release/one_click_macos_gui/readme.rst b/release/one_click_macos_gui/readme.rst index 02429d23..441caf9f 100644 --- a/release/one_click_macos_gui/readme.rst +++ b/release/one_click_macos_gui/readme.rst @@ -64,6 +64,7 @@ If you use Picasso in your research, please cite our Nature Protocols publicatio - AIM undrifting. DOI: `10.1126/sciadv.adm776 `__ - SMLM clusterer. DOIs: `10.1038/s41467-021-22606-1 `__ and `10.1038/s41586-023-05925-9 `__ - DBSCAN: Ester, et al. Inkdd, 1996. (Vol. 96, No. 34, pp. 226-231). +- Anisotropic DBSCAN inspired by: `10.1021/acs.jpcb.4c02030 `__ - HDBSCAN. DOI: `10.1007/978-3-642-37456-2_14 `__ - RESI. DOI: `10.1038/s41586-023-05925-9 `__ - Nanotron. DOI: `10.1093/bioinformatics/btaa154 `__ diff --git a/release/one_click_windows_gui/readme.rst b/release/one_click_windows_gui/readme.rst index 081dd3a7..db6ee176 100644 --- a/release/one_click_windows_gui/readme.rst +++ b/release/one_click_windows_gui/readme.rst @@ -69,6 +69,7 @@ If you use Picasso in your research, please cite our Nature Protocols publicatio - AIM undrifting. DOI: `10.1126/sciadv.adm776 `__ - SMLM clusterer. DOIs: `10.1038/s41467-021-22606-1 `__ and `10.1038/s41586-023-05925-9 `__ - DBSCAN: Ester, et al. Inkdd, 1996. (Vol. 96, No. 34, pp. 226-231). +- Anisotropic DBSCAN inspired by: `10.1021/acs.jpcb.4c02030 `__ - HDBSCAN. DOI: `10.1007/978-3-642-37456-2_14 `__ - RESI. DOI: `10.1038/s41586-023-05925-9 `__ - Nanotron. DOI: `10.1093/bioinformatics/btaa154 `__ diff --git a/tests/test_clusterer.py b/tests/test_clusterer.py index 1d057808..8be4b683 100644 --- a/tests/test_clusterer.py +++ b/tests/test_clusterer.py @@ -61,6 +61,13 @@ def db_locs(locs): (50.0, 50.0, 200.0), (10.0, 50.0, -200.0), ] +# Same relative geometry as BLOB_CENTERS_3D but translated far from z=0 +# to guard against any z-offset dependence in the anisotropic scaling. +BLOB_CENTERS_3D_OFFSET = [ + (10.0, 10.0, 10_000.0), + (50.0, 50.0, 10_200.0), + (10.0, 50.0, 9_800.0), +] LOCS_PER_BLOB = 30 SIGMA_XY_PX = 0.005 # ~0.65 nm SIGMA_Z_NM = 1.0 @@ -106,6 +113,11 @@ def synth_locs_3d(): return _make_synthetic_locs(BLOB_CENTERS_3D) +@pytest.fixture +def synth_locs_3d_offset(): + return _make_synthetic_locs(BLOB_CENTERS_3D_OFFSET) + + @pytest.fixture def synth_info(): return [{"Pixelsize": PIXELSIZE, "Width": 100, "Height": 100}] @@ -145,6 +157,7 @@ def _run_dbscan_3d(locs): DBSCAN_MIN_SAMPLES, min_locs=0, pixelsize=PIXELSIZE, + radius_z=DBSCAN_EPS * 2.5, ) @@ -254,6 +267,22 @@ def test_recovers_known_clusters_3d(synth_locs_3d, run_clusterer): _match_truth_to_recovered(centers_scaled, truth_scaled, tol=0.5) +@pytest.mark.parametrize("run_clusterer", CLUSTERERS_3D) +def test_recovers_known_clusters_3d_offset( + synth_locs_3d_offset, run_clusterer +): + """Anisotropic clustering still works when z is far from zero.""" + out = run_clusterer(synth_locs_3d_offset) + assert out["group"].nunique() == len(BLOB_CENTERS_3D_OFFSET) + centers = out.groupby("group")[["x", "y", "z"]].mean().to_numpy() + centers_scaled = centers.copy() + centers_scaled[:, 2] /= PIXELSIZE + truth_scaled = [ + (c[0], c[1], c[2] / PIXELSIZE) for c in BLOB_CENTERS_3D_OFFSET + ] + _match_truth_to_recovered(centers_scaled, truth_scaled, tol=0.5) + + # --------------------------------------------------------------------- # Error paths: 3D without required parameters # --------------------------------------------------------------------- From 9202b4b8a13bf79d0f0ca69575f8fe832925dbc0 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Fri, 22 May 2026 09:35:47 +0200 Subject: [PATCH 12/42] spinna le batch analysis --- changelog.md | 1 + picasso/__main__.py | 415 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 372 insertions(+), 44 deletions(-) diff --git a/changelog.md b/changelog.md index be765f5a..be501aef 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ Last change: 19-MAY-2026 CEST - SPINNA: comparing models uses fitting modes and has cleaner progress dialog - SPINNA: convenient fitting of LE +- Batch analysis in SPINNA for LE fitting ## 0.10.0 diff --git a/picasso/__main__.py b/picasso/__main__.py index ed87a8c8..31fd4a96 100644 --- a/picasso/__main__.py +++ b/picasso/__main__.py @@ -1377,6 +1377,52 @@ def render_many( ) +def _parse_float_list(value) -> list: + """Parse a CSV cell into a list of floats. Accepts a numeric scalar + or a comma-separated string like ``"3,4,5"``.""" + if isinstance(value, bool): + raise ValueError("Expected a number or comma-separated string.") + if isinstance(value, (int, float)): + return [float(value)] + return [float(x.strip()) for x in str(value).split(",") if x.strip()] + + +def _spinna_targets_from_row(row) -> list: + """Derive target names from the ``exp_data_*`` columns of a CSV row. + + LE fitting does not load a structures yaml, so targets come from + column names. The first non-empty ``exp_data_*`` column maps to + ``target_a`` in ``spinna.fit_le``. + """ + import pandas as pd + + prefix = "exp_data_" + targets = [ + c[len(prefix) :] + for c in row.index + if c.startswith(prefix) and pd.notna(row[c]) + ] + if len(targets) != 2: + raise ValueError( + "LE fitting requires exactly two targets (two non-empty " + f"exp_data_* columns); found: {targets}" + ) + return targets + + +def _spinna_parse_distances(row) -> list: + """Parse the ``distances`` column of an LE-fitting row into a list + of candidate heterodimer distances (nm).""" + import pandas as pd + + if "distances" not in row.index or pd.isna(row["distances"]): + raise ValueError("Column 'distances' is required when le_fitting=1.") + distances = _parse_float_list(row["distances"]) + if not distances: + raise ValueError("'distances' must contain at least one value.") + return distances + + def _spinna_validate_parameters( parameters_filename: str, ) -> tuple: @@ -1413,7 +1459,6 @@ def _spinna_validate_parameters( i += 1 for column in [ - "structures_filename", "granularity", "save_filename", "NND_bin", @@ -1428,9 +1473,19 @@ def _spinna_validate_parameters( return parameters, result_dir -def _spinna_load_target_data(row, targets: list, io) -> tuple: +def _spinna_load_target_data( + row, + targets: list, + io, + le_fitting: bool = False, +) -> tuple: """Load per-target experimental data and parameters from a CSV row. + When ``le_fitting`` is True, ``label_unc_TARGET`` is parsed as a + comma-separated list of candidates (single values are still accepted), + ``le_TARGET`` is not read (LE is what is being fit), and + ``n_simulated`` is the raw localisation count (no LE division). + Returns ------- tuple[dict, dict, dict, dict, int] @@ -1438,10 +1493,10 @@ def _spinna_load_target_data(row, targets: list, io) -> tuple: """ import numpy as np - label_unc = {} - le = {} - exp_data = {} - n_simulated = {} + label_unc: dict = {} + le: dict = {} + exp_data: dict = {} + n_simulated: dict = {} dim = 2 for target in targets: @@ -1450,15 +1505,21 @@ def _spinna_load_target_data(row, targets: list, io) -> tuple: raise ValueError( f"Column {col_name} not found in the parameters file." ) - if f"le_{target}" not in row.index and ( - "le_fitting" in row.index and row["le_fitting"] == 0 - ): + if not le_fitting and f"le_{target}" not in row.index: raise ValueError( f"Column le_{target} not found in the parameters file." ) - label_unc[target] = float(row[f"label_unc_{target}"]) - le[target] = float(row[f"le_{target}"]) / 100 + if le_fitting: + label_unc[target] = _parse_float_list(row[f"label_unc_{target}"]) + if not label_unc[target]: + raise ValueError( + f"label_unc_{target} must contain at least one value." + ) + le[target] = 1.0 + else: + label_unc[target] = float(row[f"label_unc_{target}"]) + le[target] = float(row[f"le_{target}"]) / 100 locs, info = io.load_locs(str(row[f"exp_data_{target}"])) pixelsize = 130 @@ -1482,7 +1543,10 @@ def _spinna_load_target_data(row, targets: list, io) -> tuple: ).T dim = 2 - n_simulated[target] = int(len(locs) / le[target]) + if le_fitting: + n_simulated[target] = len(locs) + else: + n_simulated[target] = int(len(locs) / le[target]) return label_unc, le, exp_data, n_simulated, dim @@ -1527,21 +1591,22 @@ def _spinna_resolve_roi(row, dim: int, targets: list) -> tuple: return apply_mask, mask_paths, area, volume, z_range -def _spinna_build_mixer( - spinna, - structures: list, +def _spinna_compute_roi( targets: list, - label_unc: dict, - le: dict, - random_rot_mode: str, apply_mask: bool, mask_paths: dict, dim: int, area, volume, z_range, -): - """Build a ``StructureMixer`` from resolved ROI and target data.""" +) -> tuple: + """Resolve the simulation ROI for a row. + + Returns + ------- + tuple[dict | None, float | None, float | None, float | None] + ``(mask_dict, width, height, depth)`` + """ import os import yaml import numpy as np @@ -1567,6 +1632,33 @@ def _spinna_build_mixer( depth = z_range width = height = np.sqrt(volume * 1e9 / depth) + return mask_dict, width, height, depth + + +def _spinna_build_mixer( + spinna, + structures: list, + targets: list, + label_unc: dict, + le: dict, + random_rot_mode: str, + apply_mask: bool, + mask_paths: dict, + dim: int, + area, + volume, + z_range, +): + """Build a ``StructureMixer`` from resolved ROI and target data.""" + mask_dict, width, height, depth = _spinna_compute_roi( + targets, + apply_mask, + mask_paths, + dim, + area, + volume, + z_range, + ) return spinna.StructureMixer( structures=structures, label_unc=label_unc, @@ -1600,13 +1692,74 @@ def _spinna_collect_results( z_range, n_simulated: dict, spinna, + le_fitting: bool = False, + label_unc_search: dict | None = None, + distances_search: list | None = None, + best_distance: float | None = None, + le_values: dict | None = None, ) -> dict: - """Assemble the full results dict from fitting output.""" + """Assemble the full results dict from fitting output. + + When ``le_fitting`` is True, the dict reports the recovered LE + values, fitted label uncertainty and heterodimer distance (plus the + search spaces used), mirroring the GUI's Fit LE summary keys. + """ import numpy as np from datetime import datetime results: dict = {} results["Date"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + if le_fitting: + results["Molecular targets"] = targets + results["File location of experimental data"] = [ + str(row[f"exp_data_{target}"]) for target in targets + ] + results["Parameters search space granularity"] = granularity + results["Dimensionality"] = f"{dim}D" + results["Rotation mode"] = random_rot_mode + results["Number of simulation repeats"] = sim_repeats + if label_unc_search is not None: + for target in targets: + results[ + f"Label-uncertainty search space (nm) for {target}" + ] = ", ".join( + f"{float(v):.2f}" for v in label_unc_search[target] + ) + for target in targets: + results[f"Fitted label uncertainty (nm) for {target}"] = ( + f"{float(label_unc[target]):.4f}" + ) + if distances_search is not None: + results["Heterodimer distance search space (nm)"] = ", ".join( + f"{float(v):.2f}" for v in distances_search + ) + if best_distance is not None: + results["Fitted heterodimer distance (nm)"] = ( + f"{float(best_distance):.4f}" + ) + if le_values is not None: + for target in targets: + results[f"Fitted labeling efficiency (%) for {target}"] = ( + f"{float(le_values[target]):.2f}" + ) + results["Best fitting structure proportions (%)"] = ", ".join( + f"{s.title}: {float(p):.2f}" for s, p in zip(structures, opt_props) + ) + results["Modified Kolmogorov-Smirnov score"] = score + + if apply_mask: + results["File location of masks"] = [ + row[f"mask_filename_{target}"] for target in targets + ] + else: + if dim == 2: + results["Area (um^2)"] = area + elif dim == 3: + results["Volume (um^3)"] = volume + results["Z range (nm)"] = z_range + return results + results["File location of structures"] = row["structures_filename"] results["Molecular targets"] = targets results["File location of experimenal data"] = [ @@ -1663,13 +1816,6 @@ def _spinna_collect_results( results["Volume (um^3)"] = volume results["Z range (nm)"] = z_range - if "le_fitting" in row.index and row["le_fitting"] == 1: - le_values = spinna.get_le_from_props(structures, opt_props) - results["Labeling efficiency fitting"] = ( - f"LE {targets[0]}: {le_values[targets[0]]:.1f}%," - f" LE {targets[1]}: {le_values[targets[1]]:.1f}%" - ) - return results @@ -1748,10 +1894,15 @@ def _spinna_process_row( ) -> dict: """Run a single SPINNA analysis row and return the results dict.""" import os + import pandas as pd print(f"Running SPINNA on row {index+1} out of {len(parameters)}.") - structures_filename = row["structures_filename"] - structures, targets = spinna.load_structures(structures_filename) + + le_fitting = ( + "le_fitting" in row.index + and pd.notna(row["le_fitting"]) + and int(row["le_fitting"]) == 1 + ) granularity = row["granularity"] NND_bin = row["NND_bin"] @@ -1774,15 +1925,57 @@ def _spinna_process_row( else: nn_plotted = int(row["nn_plotted"]) + if le_fitting: + targets = _spinna_targets_from_row(row) + else: + if "structures_filename" not in row.index or pd.isna( + row["structures_filename"] + ): + raise ValueError( + f"Row {index}: structures_filename is required when " + "le_fitting != 1." + ) + structures, targets = spinna.load_structures( + row["structures_filename"] + ) + label_unc, le, exp_data, n_simulated, dim = _spinna_load_target_data( - row, targets, io + row, + targets, + io, + le_fitting=le_fitting, ) apply_mask, mask_paths, area, volume, z_range = _spinna_resolve_roi( row, dim, targets ) - if "le_fitting" in row.index and row["le_fitting"] == 1: - le = {target: 1.0 for target in targets} + if not os.path.isdir(result_dir): + os.mkdir(result_dir) + + if le_fitting: + return _spinna_process_row_le( + row=row, + targets=targets, + label_unc=label_unc, + exp_data=exp_data, + n_simulated=n_simulated, + dim=dim, + granularity=granularity, + sim_repeats=sim_repeats, + NND_bin=NND_bin, + NND_maxdist=NND_maxdist, + nn_plotted=nn_plotted, + apply_mask=apply_mask, + mask_paths=mask_paths, + area=area, + volume=volume, + z_range=z_range, + random_rot_mode=random_rot_mode, + save_filename=save_filename, + asynch=asynch, + verbose=verbose, + spinna=spinna, + ) N_structures = spinna.generate_N_structures( structures, n_simulated, granularity @@ -1803,9 +1996,6 @@ def _spinna_process_row( z_range, ) - if not os.path.isdir(result_dir): - os.mkdir(result_dir) - opt_props, score = spinna.SPINNA( mixer=mixer, gt_coords=exp_data, @@ -1864,6 +2054,128 @@ def _spinna_process_row( return results +def _spinna_process_row_le( + *, + row, + targets: list, + label_unc: dict, + exp_data: dict, + n_simulated: dict, + dim: int, + granularity, + sim_repeats: int, + NND_bin: float, + NND_maxdist: float, + nn_plotted: int, + apply_mask: bool, + mask_paths: dict, + area, + volume, + z_range, + random_rot_mode: str, + save_filename: str, + asynch: bool, + verbose: bool, + spinna, +) -> dict: + """LE-fitting branch of ``_spinna_process_row``: builds monomer/ + heterodimer structures via ``spinna.fit_le`` and recovers per-target + LE from the fitted structure proportions.""" + import os + + distances = _spinna_parse_distances(row) + mask_dict, width, height, depth = _spinna_compute_roi( + targets, + apply_mask, + mask_paths, + dim, + area, + volume, + z_range, + ) + + # snapshot the search-space inputs before calling fit_le — + # compare_models mutates label_unc in place + label_unc_input = {t: list(v) for t, v in label_unc.items()} + distances_input = list(distances) + + ( + le_values, + fitted_label_unc, + best_distance, + score, + best_props, + best_mixer, + ) = spinna.fit_le( + target_a=targets[0], + target_b=targets[1], + exp_data=exp_data, + granularity=int(granularity), + label_unc=label_unc, + distances=distances, + N_sim=int(sim_repeats), + mask_dict=mask_dict, + width=width, + height=height, + depth=depth, + random_rot_mode=random_rot_mode, + asynch=asynch, + savedir=os.path.dirname(save_filename), + callback="console" if verbose else None, + fitting_mode="coarse-to-fine", + ) + + structures = best_mixer.structures + results = _spinna_collect_results( + row, + targets, + structures, + best_mixer, + best_props, + score, + fitted_label_unc, + {t: 1.0 for t in targets}, + random_rot_mode, + dim, + granularity, + {s.title: None for s in structures}, + sim_repeats, + apply_mask, + mask_paths, + area, + volume, + z_range, + n_simulated, + spinna, + le_fitting=True, + label_unc_search=label_unc_input, + distances_search=distances_input, + best_distance=best_distance, + le_values=le_values, + ) + + with open(f"{save_filename}_fit_summary.txt", "w") as f: + for key, value in results.items(): + f.write(f"{key}: {value}\n") + print(f"Results saved to {save_filename}_fit_summary.txt") + + _spinna_plot_nnd( + spinna, + best_mixer, + targets, + exp_data, + best_props, + n_simulated, + sim_repeats, + NND_bin, + NND_maxdist, + nn_plotted, + save_filename, + ) + + return results + + def _spinna_batch_analysis( parameters_filename: str, asynch: bool = True, @@ -1877,16 +2189,20 @@ def _spinna_batch_analysis( run. The parameters (columns) are: - "structures_filename" : Name of the files with structures saved - (.yaml). + (.yaml). Required unless ``le_fitting=1``, in which case the + monomer/heterodimer structures are built internally and targets + are taken from the two ``exp_data_TARGET`` columns. - "exp_data_TARGET" : Name of the file with experimental data (.hdf5). Each target in the structures must have a corresponding column, for example, "exp_data_EGFR". - "le_TARGET" : Labeling efficiency (%) for each target. Each target in the structures must have a corresponding column, - for example, "le_EGFR". + for example, "le_EGFR". Ignored when ``le_fitting=1``. - "label_unc_TARGET" : Label uncertainty (nm) for each target. Each target in the structures must have a corresponding column, - for example: "label_unc_EGFR". + for example: "label_unc_EGFR". When ``le_fitting=1``, this may + be a comma-separated list of candidates (e.g. ``"3,4,5,6"``); + a single value disables the per-target search. - "granularity" : Granularity used in parameters search space generation. The higher the value the more combinations of structure counts will be tested. @@ -1920,11 +2236,22 @@ def _spinna_batch_analysis( - "nn_plotted" : Number of nearest neighbors plotted in the NND. Only integer values are accepted. Default: 4. - "le_fitting" : 0 if standard SPINNA is ran, 1 if labeling - efficiency fitting is to be performed. Then, 100% LE is used in - the pipeline and different output file is saved. If the column - is not provided, standard SPINNA is ran. For more details about - the LE fitting, see Hellmeier, Strauss, et al. Nature Methods - 2024. + efficiency fitting is to be performed. If the column is not + provided, standard SPINNA is ran. When set to 1, the batch + calls ``picasso.spinna.fit_le``: monomer A, monomer B and + heterodimer(d) structures are built internally for each + candidate ``distances`` value, label uncertainty is fit per + target from the comma-separated candidates in + ``label_unc_TARGET``, and the per-target LE is recovered from + the fitted structure proportions. Exactly two ``exp_data_*`` + columns must be present; the first column maps to ``target_a``. + ``-b/--bootstrap`` is ignored on LE-fitting rows. For more + details about the LE fitting, see Hellmeier, Strauss, et al. + Nature Methods, 2024. + - "distances" : Comma-separated list of candidate heterodimer + distances in nm (e.g. ``"5,10,15,20"``). A single value fixes + the distance. Required when ``le_fitting=1``; ignored + otherwise. When saving, each analysis run index is used as the prefix for filename, for example, "analysis_run1_fit_summary.txt". From c411ce4604b476cced1f4160c823da62939c5d90 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 25 May 2026 10:08:48 +0200 Subject: [PATCH 13/42] supress convergence warning for bayesian spinna fitting --- changelog.md | 2 +- picasso/spinna.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index be501aef..f75a8e5e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -Last change: 19-MAY-2026 CEST +Last change: 25-MAY-2026 CEST ## 0.10.1 diff --git a/picasso/spinna.py b/picasso/spinna.py index 22e15a9f..e4cc8fca 100644 --- a/picasso/spinna.py +++ b/picasso/spinna.py @@ -13,6 +13,7 @@ import os import time +import warnings from typing import Literal from concurrent import futures from multiprocessing import cpu_count @@ -31,6 +32,7 @@ from scipy.stats import ks_2samp, norm from sklearn.gaussian_process import GaussianProcessRegressor from sklearn.gaussian_process.kernels import Matern +from sklearn.exceptions import ConvergenceWarning from tqdm import tqdm from . import io, lib, masking, render, __version__ @@ -3658,7 +3660,9 @@ def _bayesian_gp_phase( normalize_y=True, alpha=1e-6, ) - gp.fit(X_train, y_train) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ConvergenceWarning) + gp.fit(X_train, y_train) unevaluated_mask = ~evaluated mu, sigma = gp.predict( proportions[unevaluated_mask], return_std=True From 0ce77eed2f5ae3405d37f5ddf32673d1bf033b05 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 25 May 2026 11:07:56 +0200 Subject: [PATCH 14/42] cleaner saving of spinna batch analysis --- picasso/__main__.py | 6 ++---- picasso/spinna.py | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/picasso/__main__.py b/picasso/__main__.py index 31fd4a96..dcd61ff0 100644 --- a/picasso/__main__.py +++ b/picasso/__main__.py @@ -1909,7 +1909,7 @@ def _spinna_process_row( NND_maxdist = row["NND_maxdist"] sim_repeats = row["sim_repeats"] save_filename, _ = os.path.splitext(row["save_filename"]) - save_filename = os.path.join(result_dir, save_filename) + save_filename = os.path.join(result_dir, os.path.basename(save_filename)) random_rot_mode = "2D" if "rotation_mode" in row.index: @@ -1949,9 +1949,6 @@ def _spinna_process_row( row, dim, targets ) - if not os.path.isdir(result_dir): - os.mkdir(result_dir) - if le_fitting: return _spinna_process_row_le( row=row, @@ -2272,6 +2269,7 @@ def _spinna_batch_analysis( from . import io, spinna parameters, result_dir = _spinna_validate_parameters(parameters_filename) + os.makedirs(result_dir, exist_ok=True) summary = [] for index, row in parameters.iterrows(): diff --git a/picasso/spinna.py b/picasso/spinna.py index e4cc8fca..be49b7a8 100644 --- a/picasso/spinna.py +++ b/picasso/spinna.py @@ -4469,7 +4469,7 @@ def compare_models_given_label_unc( if savedir: suffix = ("_").join( [ - f"{target}_{lunc:2f}_nm" + f"{target}_{lunc:.2f}_nm" for target, lunc in label_unc.items() ] ) From b39f3b3edfd0fd343a042a5ed87ce6a95823edd8 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 25 May 2026 11:28:52 +0200 Subject: [PATCH 15/42] SPINNA: area/volume button removed + batch analysis does not require area --- changelog.md | 2 + picasso/__main__.py | 48 +++++++++---- picasso/gui/spinna.py | 156 ++++-------------------------------------- 3 files changed, 52 insertions(+), 154 deletions(-) diff --git a/changelog.md b/changelog.md index f75a8e5e..02893667 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,8 @@ Last change: 25-MAY-2026 CEST - SPINNA: comparing models uses fitting modes and has cleaner progress dialog - SPINNA: convenient fitting of LE - Batch analysis in SPINNA for LE fitting +- Batch analysis does not require area input (if found in metadata) +- SPINNA: area/volume button removed (deduced automatically from densities and number of molecules in the exp. data) ## 0.10.0 diff --git a/picasso/__main__.py b/picasso/__main__.py index dcd61ff0..90ddca1b 100644 --- a/picasso/__main__.py +++ b/picasso/__main__.py @@ -1488,8 +1488,8 @@ def _spinna_load_target_data( Returns ------- - tuple[dict, dict, dict, dict, int] - ``(label_unc, le, exp_data, n_simulated, dim)`` + tuple[dict, dict, dict, dict, int, dict] + ``(label_unc, le, exp_data, n_simulated, dim, infos)`` """ import numpy as np @@ -1497,6 +1497,7 @@ def _spinna_load_target_data( le: dict = {} exp_data: dict = {} n_simulated: dict = {} + infos: dict = {} dim = 2 for target in targets: @@ -1522,6 +1523,7 @@ def _spinna_load_target_data( le[target] = float(row[f"le_{target}"]) / 100 locs, info = io.load_locs(str(row[f"exp_data_{target}"])) + infos[target] = info pixelsize = 130 for element in info: if ( @@ -1548,17 +1550,26 @@ def _spinna_load_target_data( else: n_simulated[target] = int(len(locs) / le[target]) - return label_unc, le, exp_data, n_simulated, dim + return label_unc, le, exp_data, n_simulated, dim, infos -def _spinna_resolve_roi(row, dim: int, targets: list) -> tuple: +def _spinna_resolve_roi( + row, dim: int, targets: list, infos: dict | None = None +) -> tuple: """Determine ROI parameters for a row: homogeneous or masked. + For 2D rows, if the ``area`` column is missing or empty, the area is + recovered from the experimental data metadata key ``"Area (um^2)"`` + (taken from the first target's info). + Returns ------- tuple[bool, dict, float | None, float | None, float | None] ``(apply_mask, mask_paths, area, volume, z_range)`` """ + import pandas as pd + from . import lib + apply_mask = True area = volume = z_range = None mask_paths = {} @@ -1575,9 +1586,17 @@ def _spinna_resolve_roi(row, dim: int, targets: list) -> tuple: ) z_range = float(row["z_range"]) elif dim == 2: - if "area" in row.index: + if "area" in row.index and pd.notna(row["area"]): area = float(row["area"]) apply_mask = False + elif infos: + first_target = targets[0] + meta_area = lib.get_from_metadata( + infos[first_target], "Area (um^2)" + ) + if meta_area is not None: + area = float(meta_area) + apply_mask = False if apply_mask: for target in targets: @@ -1939,14 +1958,16 @@ def _spinna_process_row( row["structures_filename"] ) - label_unc, le, exp_data, n_simulated, dim = _spinna_load_target_data( - row, - targets, - io, - le_fitting=le_fitting, + label_unc, le, exp_data, n_simulated, dim, infos = ( + _spinna_load_target_data( + row, + targets, + io, + le_fitting=le_fitting, + ) ) apply_mask, mask_paths, area, volume, z_range = _spinna_resolve_roi( - row, dim, targets + row, dim, targets, infos ) if le_fitting: @@ -2217,7 +2238,10 @@ def _spinna_batch_analysis( * For homogeneous distribution: - "area" or "volume" : Area (2D simulation) or volume (3D - simulation) of the simulated ROI (um^2 or um^3). + simulation) of the simulated ROI (um^2 or um^3). For 2D rows, + "area" is optional: if omitted, the area is read from the + experimental data metadata key "Area (um^2)" (written by Picasso + when picks/areas are saved). - "z_range" : Applicable only when "volume" is provided. Defines the range of z coordinates (nm) of simulated molecular targets. diff --git a/picasso/gui/spinna.py b/picasso/gui/spinna.py index a3b9f833..1e1edd09 100644 --- a/picasso/gui/spinna.py +++ b/picasso/gui/spinna.py @@ -2862,9 +2862,6 @@ class SimulationsTab(lib.Dialog): granularity : int Granularity used in generating the search space for fitting. - roi_button : QtWidgets.QPushButton - Buttons for loading area/volume of the ROI. Used for - homogenous distribution only. rot_dim_widget : QtWidgets.QComboBox Combo box for setting the dimension of the random rotations of simulated structures (2D/3D). @@ -2879,9 +2876,6 @@ class SimulationsTab(lib.Dialog): settings_dialog : OptionalSettingsDialog Dialog for setting optional parameters (rotations, numbers of neighbors to consider at fitting). - single_sim_mass : float - Area/volume of a single simulation (um^2(or um^3)). Used for - homogenous distribution only. targets : list of str Names of all unique molecular targets in the loaded structures. window : QtWidgets.QMainWindow @@ -2925,7 +2919,6 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: self.depth = None self.granularity = None self.n_sim_fit = None - self.single_sim_mass = None self.mixer = None self.current_score = 0.0 self.structures_path = "" @@ -3216,13 +3209,6 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: self.save_sim_result_check.setChecked(False) single_sim_layout.addWidget(self.save_sim_result_check, 1, 0) - self.roi_button = QtWidgets.QPushButton("Area (\u03bcm\u00b2)") - self.roi_button.setToolTip( - "Set the area (2D) or volume (3D) of the simulation." - ) - self.roi_button.released.connect(self.on_roi_button_clicked) - single_sim_layout.addWidget(self.roi_button, 1, 1) - self.run_single_sim_button = QtWidgets.QPushButton( "Run single simulation" ) @@ -3400,8 +3386,6 @@ def on_dim_changed(self, index: int) -> None: "Observed densities (\u03bcm\u207b\u00b2)" ) self.depth_stack.setCurrentIndex(0) - self.roi_button.setText("Area (\u03bcm\u00b2)") - self.single_sim_mass = None # adjust the observed densities if data is already available # and depth was specified previously if self.check_exp_loaded() and self.depth is not None: @@ -3415,8 +3399,6 @@ def on_dim_changed(self, index: int) -> None: self.densities_box.setTitle( "Observed densities (\u03bcm\u207b\u00b3)" ) - self.roi_button.setText("Volume (\u03bcm\u00b3)") - self.single_sim_mass = None # adjust the observed densities if data is already available # and depth was specified previously if self.check_exp_loaded() and self.depth is not None: @@ -3609,7 +3591,6 @@ def set_mask_den_stack(self, name: str): self.mask_button.setStyleSheet("background-color : lightgreen") self.rect_roi_button.setStyleSheet("background-color : gray") self.depth_stack.setCurrentIndex(0) - self.roi_button.setEnabled(False) else: self.mask_den_stack.setCurrentIndex(1) self.rect_roi_button.setStyleSheet("background-color : lightgreen") @@ -3620,7 +3601,6 @@ def set_mask_den_stack(self, name: str): self.depth_stack.setCurrentIndex(0) else: self.depth_stack.setCurrentIndex(1) - self.roi_button.setEnabled(True) @check_structures_loaded @check_exp_data_loaded @@ -3807,21 +3787,14 @@ def fit_n_str(self) -> None: if self.mixer is None: return - # update area/volume in case of rectangluar ROI + # discard the z component if 3D data is loaded but 2D simulation + # is conducted if self.mask_den_stack.currentIndex() == 1: # rect. ROI - roi_size = self.mixer.roi_size if self.dim_widget.currentIndex() == 0: # 2D - self.roi_button.setText(f"Area: {roi_size:.0f} \u03bcm\u00b2") - # discard the z component if 3D data is loaded self.exp_data = { target: coords[:, :2] for target, coords in self.exp_data.items() } - else: # 3D - self.roi_button.setText( - f"Volume: {roi_size:.0f} (\u03bcm\u00b3)" - ) - self.single_sim_mass = roi_size save = "" if self.save_fit_results_check.isChecked(): @@ -4125,17 +4098,6 @@ def compare_models(self) -> None: name: self.nn_plot_settings_dialog.nn_counts[name].value() for name in self.nn_plot_settings_dialog.nn_counts.keys() } - if self.mask_den_stack.currentIndex() == 1: # homogeneus dist. - roi = self.mixer.roi - self.single_sim_mass = self.mixer.roi_size - if roi[2] is None: - self.roi_button.setText( - f"Area: {self.single_sim_mass:.0f} \u03bcm\u00b2" - ) - else: - self.roi_button.setText( - f"Volume: {self.single_sim_mass:.0f} \u03bcm\u00b3" - ) self.load_single_sim_n_str_widgets() self.settings_dialog.update_neighbors_widgets() self.nn_plot_settings_dialog.update_neighbors_widgets() @@ -4228,17 +4190,6 @@ def fit_le(self) -> None: name: self.nn_plot_settings_dialog.nn_counts[name].value() for name in self.nn_plot_settings_dialog.nn_counts.keys() } - if self.mask_den_stack.currentIndex() == 1: # homogeneous dist. - roi = self.mixer.roi - self.single_sim_mass = self.mixer.roi_size - if roi[2] is None: - self.roi_button.setText( - f"Area: {self.single_sim_mass:.0f} μm²" - ) - else: - self.roi_button.setText( - f"Volume: {self.single_sim_mass:.0f} μm³" - ) self.load_single_sim_n_str_widgets() self.settings_dialog.update_neighbors_widgets() self.nn_plot_settings_dialog.update_neighbors_widgets() @@ -4360,38 +4311,6 @@ def _save_le_fit_summary( for key, value in metadata.items(): f.write(f"{key}: {value}\n") - def on_roi_button_clicked(self) -> None: - """Ask the user to input the area/volume to be simulated for a - single simulation.""" - if self.mask_den_stack.currentIndex() == 1: # rectangular ROI - # here mass refers to area/volume - if self.dim_widget.currentIndex() == 0: # 2D - mass, ok = QtWidgets.QInputDialog.getInt( - self, - "", - "Area (\u03bcm\u00b2):", - 100, - 0, - 1_000_000, - ) - if ok: - self.single_sim_mass = mass - self.roi_button.setText(f"Area: {mass:.0f} \u03bcm\u00b2") - else: # 3D - mass, ok = QtWidgets.QInputDialog.getInt( - self, - "", - "Volume (\u03bcm\u00b3):", - 100, - 0, - 1_000_000, - ) - if ok: - self.single_sim_mass = mass - self.roi_button.setText( - f"Volume: {mass:.0f} \u03bcm\u00b3" - ) - @check_structures_loaded def single_sim_n_total(self) -> int: """Find the total number of molecules for a single simulation. @@ -4406,23 +4325,14 @@ def single_sim_n_total(self) -> int: n_total : int Total number of molecules to simulate. """ - if self.mask_den_stack.currentIndex() == 0: # mask - n_total = int( - sum( - [ - len(self.exp_data[t]) / self.le_spins[i].value() * 100 - for i, t in enumerate(self.targets) - ] - ) + n_total = int( + sum( + [ + len(self.exp_data[t]) / self.le_spins[i].value() * 100 + for i, t in enumerate(self.targets) + ] ) - else: - tot_densities = [ - self.densities_spins[i].value() - / self.le_spins[i].value() - * 100 - for i in range(len(self.densities_spins)) - ] - n_total = int(self.single_sim_mass * sum(tot_densities)) + ) return n_total @check_structures_loaded @@ -4591,7 +4501,7 @@ def _setup_label_unc_and_le(self) -> tuple[dict, dict]: return label_unc, le def _setup_roi( - self, mode: Literal["fit", "single_sim"] + self, ) -> tuple[float | None, float | None, float | None, dict | None]: fail_return = (None, None, None, None) if self.mask_den_stack.currentIndex() == 0: # masks @@ -4615,7 +4525,7 @@ def _setup_roi( QtWidgets.QMessageBox.information(self, "Warning", message) return fail_return mask_dict = None - width, height, depth = self.find_roi(mode=mode) + width, height, depth = self.find_roi() if width is None: return fail_return return width, height, depth, mask_dict @@ -4650,7 +4560,7 @@ def setup_mixer( ) label_unc, le = self._setup_label_unc_and_le() - width, height, depth, mask_dict = self._setup_roi(mode=mode) + width, height, depth, mask_dict = self._setup_roi() if width is None and mask_dict is None: return @@ -4768,35 +4678,15 @@ def check_prop_str_sum(self, value: float, name: str) -> None: self.prop_str_input_spins[idx].value() - (sum_ - 100) ) - def find_roi( - self, - mode: Literal["fit", "single_sim"] = "fit", - ) -> tuple[float, float, float]: + def find_roi(self) -> tuple[float, float, float]: """Find width, height, depth to conduct simulation(s) with homogeneous distribution. - Parameters - ---------- - mode : {'fit' or 'single_sim'} - Specifies how to find the numbers of structures to be - considered. - Returns ------- result : tuple Width, height, depth (all nm). """ - assert mode in ["fit", "single_sim"] - - if mode == "fit": - return self.find_roi_fit() - elif mode == "single_sim": - return self.find_roi_single_sim() - - def find_roi_fit(self) -> tuple[float, float, float]: - """Find width, height, depth to conduct simulation(s) with - homogeneous distribution for fitting, based on the input - densities and the exp. data.""" target = self.targets[0] density = self.densities_spins[0].value() # convert density from um^-2 to nm^-2 @@ -4808,7 +4698,7 @@ def find_roi_fit(self) -> tuple[float, float, float]: tot_density = density / le n_mol = self.find_n_mol_from_target(target) - # obtain depth (only 3D) + # get depth (only 3D) depth = None if self.dim_widget.currentIndex() == 0 else self.depth # find width and height - ROI is a square @@ -4818,24 +4708,6 @@ def find_roi_fit(self) -> tuple[float, float, float]: width = height = np.sqrt(n_mol / tot_density / depth) return width, height, depth - def find_roi_single_sim(self) -> tuple[float, float, float]: - """Find width, height, depth to conduct a single simulation with - homogeneous distribution, based on the user-selected - area/volume.""" - if self.single_sim_mass is None: - message = "Please input the area/volume of the ROI first." - QtWidgets.QMessageBox.information(self, "Warning", message) - return [None, None, None] - - if self.dim_widget.currentIndex() == 0: # 2D: - depth = None - width = height = np.sqrt(self.single_sim_mass * 1e6) - else: # 3D - depth = self.depth - width = height = np.sqrt(self.single_sim_mass * 1e9 / depth) - - return width, height, depth - def find_n_mol_from_target(self, target: str) -> int: """Find number of molecules of the given molecular target that are to be simulated in fitting. From d43acca3ac067ae15e624022cce5c563e85b68d6 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 25 May 2026 12:10:58 +0200 Subject: [PATCH 16/42] correct documentation and enhance visibilty of spinna batch analysis inputs --- changelog.md | 1 + docs/spinna.rst | 17 ++++++++++------- picasso/__main__.py | 27 +++++++++++++++++++++++---- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index 02893667..a216dbad 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,7 @@ Last change: 25-MAY-2026 CEST - SPINNA: convenient fitting of LE - Batch analysis in SPINNA for LE fitting - Batch analysis does not require area input (if found in metadata) +- Batch analysis: clear instructions on what columns are required - SPINNA: area/volume button removed (deduced automatically from densities and number of molecules in the exp. data) ## 0.10.0 diff --git a/docs/spinna.rst b/docs/spinna.rst index 0b17d075..9a43bfab 100644 --- a/docs/spinna.rst +++ b/docs/spinna.rst @@ -137,10 +137,10 @@ SPINNA can be run directly from the command window to allow fast and efficient b Each row in the .csv file will specify parameters for which SPINNA is run. In the file, define the following column names (i.e., the values typed into the first row) as follows: -- *structures_filename* : Path to the file with structures saved (.yaml), see **Structures tab** above. -- *exp_data_TARGET* : Path to the file with experimental data (.hdf5) for each molecular target species. Each target in the structures must have a corresponding column, for example, *exp_data_EGFR". -- *le_TARGET* : Labeling efficiency (%) for each molecular target species. -- *label_unc_TARGET* : Label uncertainty (nm) for each molecular target species. +- *structures_filename* : Path to the file with structures saved (.yaml), see **Structures tab** above. Required unless ``le_fitting=1``, in which case the monomer/heterodimer structures are built internally from the two ``exp_data_TARGET`` columns. +- *exp_data_TARGET* : Path to the file with experimental data (.hdf5) for each molecular target species. Each target in the structures must have a corresponding column, for example, *exp_data_EGFR*. +- *le_TARGET* : Labeling efficiency (%) for each molecular target species. Ignored when ``le_fitting=1``. +- *label_unc_TARGET* : Label uncertainty (nm) for each molecular target species. When ``le_fitting=1``, this may be a comma-separated list of candidates (e.g. ``"3,4,5,6"``); a single value disables the per-target search. - *granularity* : Granularity used in parameters search space generation. The higher the value the more combinations of structure counts will be tested. - *save_filename* : Name of the .txt file where the results will be saved. - *NND_bin* : Bin size (nm) for plotting the NND histogram(s). @@ -150,16 +150,19 @@ Each row in the .csv file will specify parameters for which SPINNA is run. In th Depending on whether a homo- or heterogeneous distribution is used, the following columns must be present: For a homogeneous distribution: -- *area* or *volume* : Area (2D simulation) or volume (3D simulation) of the simulated ROI (um^2 or um^3). +- *area* or *volume* : Area (2D simulation) or volume (3D simulation) of the simulated ROI (um^2 or um^3). For 2D rows, *area* is optional: if omitted, the area is read from the experimental data metadata key ``Area (um^2)`` (written by Picasso when picks/areas are saved). - *z_range* : Applicable only when *volume* is provided. Defines the range of z coordinates (nm) of simulated molecular targets. For a heterogeneous distribution: - *mask_filename_TARGET* : Name of the .npy file with the mask saved for each molecular target species. Optional columns are: -- *rotation_mode* : Random rotations mode used in analysis. Values must be one of {*3D*, *2D*, *None*}. Default: *3D*. +- *rotation_mode* : Random rotations mode used in analysis. Values must be one of {*3D*, *2D*, *None*}. Default: *2D*. - *nn_plotted* : Number of nearest neighbors plotted, default: 4. -- *le_fitting* : 0 if standard SPINNA is ran, 1 if labeling efficiency fitting is to be performed. Then, 100% LE is used in the pipeline and different output file is saved. If the column is not provided, standard SPINNA is ran. +- *le_fitting* : 0 if standard SPINNA is ran, 1 if labeling efficiency fitting is to be performed. When set to 1, monomer A, monomer B and heterodimer structures are built internally for each candidate ``distances`` value, label uncertainty is fit per target from the comma-separated candidates in ``label_unc_TARGET``, and the per-target LE is recovered from the fitted structure proportions. Exactly two ``exp_data_*`` columns must be present; the first maps to ``target_a``. ``-b/--bootstrap`` is ignored on LE-fitting rows. If the column is not provided, standard SPINNA is ran. For more details, see `Hellmeier, Strauss, et al. Nature Methods, 2024 `_. +- *distances* : Comma-separated list of candidate heterodimer distances in nm (e.g. ``"5,10,15,20"``). A single value fixes the distance. Required when ``le_fitting=1``; ignored otherwise. + +The full column reference can also be printed from the command line via ``python -m picasso spinna --columns``. SPINNA in Python diff --git a/picasso/__main__.py b/picasso/__main__.py index 90ddca1b..0d15f31a 100644 --- a/picasso/__main__.py +++ b/picasso/__main__.py @@ -2555,21 +2555,31 @@ def main(): # noqa: C901 ) # spinna + _spinna_docs_url = ( + "https://picassosr.readthedocs.io/en/latest/spinna.html" + "#command-window-batch-analysis" + ) spinna_parser = subparsers.add_parser( "spinna", help=( "picasso single protein investigation via nearest neighbor " "analysis" ), + description=( + "Run SPINNA. Without -p, the GUI is launched. With -p, batch " + "analysis is run on the rows of the given .csv file. For the " + "full .csv column reference, run `picasso spinna --columns` " + f"or see {_spinna_docs_url}." + ), + epilog=f"Documentation: {_spinna_docs_url}", ) spinna_parser.add_argument( "-p", "--parameters", type=str, help=( - ".csv file containing the parameters for spinna batch analysis;" - " see the documentation for details explaining the .csv file" - " structure." + ".csv file containing the parameters for spinna batch analysis." + " Run `picasso spinna --columns` for the column reference." ), ) spinna_parser.add_argument( @@ -2590,6 +2600,11 @@ def main(): # noqa: C901 action="store_true", help="display progress bar for each row", ) + spinna_parser.add_argument( + "--columns", + action="store_true", + help=("print the .csv column reference for batch analysis and exit"), + ) # nanotron subparsers.add_parser("nanotron", help="segmentation with deep learning") @@ -3209,7 +3224,11 @@ def main(): # noqa: C901 elif args.command == "server": _start_server() elif args.command == "spinna": - if args.parameters: + if args.columns: + import inspect + + print(inspect.getdoc(_spinna_batch_analysis)) + elif args.parameters: _spinna_batch_analysis( args.parameters, args.asynch, From de072d648fd5970f30dd4a27d4e8183aac1daa6c Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 25 May 2026 14:30:38 +0200 Subject: [PATCH 17/42] Multichannel rendering supports colormaps, not only a single RGB color --- changelog.md | 1 + picasso/gui/render.py | 903 ++++++++++++++++++++++++++++++++++------ picasso/gui/rotation.py | 18 +- picasso/render.py | 95 ++++- 4 files changed, 874 insertions(+), 143 deletions(-) diff --git a/changelog.md b/changelog.md index 6c8211e8..66c2bd10 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,7 @@ Last change: 25-MAY-2026 CEST ## 0.10.1 - Faster rendering, especially in large FOV: ind. loc. precision blurring parallelized ., see [documentation](https://picassosr.readthedocs.io/en/latest/render.html/CPU-usage-on-shared-servers) + smarter implementation for 2D rendering; improvements for one-pixel-blur and global loc. prec. - Multi-level spatial indexing for quick zoomed-in rendering +- Multichannel rendering supports colormaps, not only a single RGB color - Fixed linking saving `lpz` - Anisotripic DBSCAN (faster implementation) [DOI: 10.1021/acs.jpcb.4c02030](https://doi.org/10.1021/acs.jpcb.4c02030) - Improved docstrings for 3D SMLM clusterer diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 5599701f..fe6fb6fd 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -81,7 +81,40 @@ MAX_ROUNDS_WITHOUT_BEST_BIC_G5M = 3 MIN_SIGMA_FACTOR_G5M = 0.8 MAX_SIGMA_FACTOR_G5M = 1.5 -N_COMPONENTS_MAX_G5M = 100 + + +# Per-channel colormap support for multichannel rendering. Each channel +# resolves to a (256, 3) float32 LUT, which the renderer indexes by the +# scaled intensity. Solid colors become black->color ramps so that the +# legacy "intensity * rgb" math is preserved bit-for-bit. Built-in +# colormaps share the names of the 14 default solid colors with the +# suffix BUILTIN_CMAP_SUFFIX, and are 3-stop black -> color -> white +# gradients. +BUILTIN_CMAP_SUFFIX = "_gradient" +# Index into a 256-row LUT used to pick a representative RGB for legends, +# histograms, and similar single-color UI elements. Avoids the near-white +# peaks of gradients that pass through white at intensity 1. +LEGEND_SAMPLE_IDX = 200 + + +def _gradient_pixmap( + lut: lib.FloatArray2D, width: int = 80, height: int = 14 +) -> QtGui.QPixmap: + """Build a horizontal gradient QPixmap that visualizes a (256, 3) + LUT. Used as the per-channel color preview.""" + samples = np.linspace(0, 255, width).astype(np.int32) + row = (lut[samples] * 255).clip(0, 255).astype(np.uint8) + rgba = np.empty((height, width, 4), dtype=np.uint8) + rgba[..., :3] = row[None, :, :] + rgba[..., 3] = 255 + image = QtGui.QImage( + rgba.data, + width, + height, + 4 * width, + QtGui.QImage.Format.Format_RGBA8888, + ) + return QtGui.QPixmap.fromImage(image.copy()) def check_pick(f: Callable) -> Callable: @@ -398,6 +431,14 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: self.colorselection = [] self.colordisp_all = [] self.intensitysettings = [] + # Per-channel resolved LUTs (each shape (256, 3) float32) and + # caches for the built-in and user-defined cmap LUTs. + self._channel_luts = [] + self._builtin_cmap_lut_cache = {} + self._user_cmap_lut_cache = {} + # Built-in colormap stops are filled in once default_colors / + # rgb are defined further below; see _init_builtin_cmaps. + self.builtin_cmap_stops = {} layout = QtWidgets.QGridLayout() self.setLayout(layout) self.setMaximumHeight(1000) @@ -443,6 +484,14 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: layout.addWidget(load_button, 1, 2) load_button.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) load_button.clicked.connect(self.load_colors) + edit_cmaps_button = QtWidgets.QPushButton("Edit custom colormaps") + edit_cmaps_button.setToolTip( + "Create, edit, rename or delete custom colormaps.\n" + "Custom colormaps appear in the per-channel selector." + ) + layout.addWidget(edit_cmaps_button, 2, 2) + edit_cmaps_button.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) + edit_cmaps_button.clicked.connect(self.open_custom_cmap_editor) # add scrollable area which will display all channels, below # the non-scrollable elements @@ -511,6 +560,16 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: [0, 0.5, 0.5], [0, 0.5, 1], ] + # Built-in colormaps: one 3-stop (black -> color -> white) + # gradient per default solid color, named "_gradient". + self.builtin_cmap_stops = { + f"{name}{BUILTIN_CMAP_SUFFIX}": [ + [0.0, 0.0, 0.0, 0.0], + [0.5, float(rgb[0]), float(rgb[1]), float(rgb[2])], + [1.0, 1.0, 1.0, 1.0], + ] + for name, rgb in zip(self.default_colors, self.rgb) + } def add_entry(self, path: str) -> None: """Add the new channel for the given path.""" @@ -553,13 +612,16 @@ def add_entry(self, path: str) -> None: # create the self.colorselection widget colordrop = QtWidgets.QComboBox(self) colordrop.setToolTip( - "Choose the color for this dataset.\n" - "You can also enter a hexadecimal color code (e.g., #FF5733)." + "Choose the color or colormap for this dataset.\n" + "Solid colors are rendered as a black→color gradient.\n" + "Colormaps map intensity 0 (left of the preview) to\n" + "intensity 1 (right of the preview).\n" + "You can also enter a hexadecimal color code " + "(e.g., #FF5733)." ) colordrop.setEditable(True) - colordrop.lineEdit().setMaxLength(12) - for color in self.default_colors: - colordrop.addItem(color) + colordrop.lineEdit().setMaxLength(40) + self._populate_color_combo(colordrop) index = np.min([len(self.checks) - 1, len(self.rgb) - 1]) colordrop.setCurrentText(self.default_colors[index]) colordrop.activated.connect(self.update_colors) @@ -568,24 +630,27 @@ def add_entry(self, path: str) -> None: partial(self.set_color, t.objectName()) ) - # create the label widget to show current color - colordisp = QtWidgets.QLabel(" ") - colordisp.setToolTip("Current color for this dataset.") - palette = colordisp.palette() + # resolve the initial LUT for this channel if self.auto_colors.isChecked(): colors = lib.get_colors(len(self.checks) + 1) - r, g, b = colors[-1] - palette.setColor( - QtGui.QPalette.ColorRole.Window, - QtGui.QColor.fromRgbF(r, g, b, 1), - ) + initial_lut = render.solid_to_lut(colors[-1]) else: - palette.setColor( - QtGui.QPalette.ColorRole.Window, - QtGui.QColor.fromRgbF(*self.rgb[index], 1), - ) - colordisp.setAutoFillBackground(True) - colordisp.setPalette(palette) + try: + initial_lut = self.resolve_color_identifier( + self.default_colors[index] + ) + except ValueError: + initial_lut = render.solid_to_lut(self.rgb[index]) + self._channel_luts.append(initial_lut) + + # create the gradient preview widget + colordisp = QtWidgets.QLabel() + colordisp.setToolTip( + "Gradient preview: intensity 0 (left) → intensity 1 (right)." + ) + colordisp.setFixedSize(80, 14) + colordisp.setFrameShape(QtWidgets.QFrame.Shape.Box) + colordisp.setPixmap(_gradient_pixmap(initial_lut)) self.colordisp_all.append(colordisp) # create the relative intensity widget @@ -664,6 +729,7 @@ def _close_one_channel(self, i: int, render_=True) -> None: del self.colordisp_all[i] del self.intensitysettings[i] del self.closebuttons[i] + del self._channel_luts[i] # delete all the View attributes del self.window.view.locs[i] @@ -751,41 +817,131 @@ def update_viewport(self) -> None: self.window.view.update_scene() def set_color(self, n: int | str) -> None: - """Set colorsdisp_all and colorselection in the given channel, - defined by its index or name.""" + """Resolve the current channel selection to a (256, 3) LUT, + cache it, and refresh the gradient preview.""" if isinstance(n, str): for j in range(len(self.title)): if n == self.title[j].objectName(): n = j + if n is None or n >= len(self.colordisp_all): + return # widget already torn down (closing a channel) - palette = self.colordisp_all[n].palette() - color = self.colorselection[n].currentText() if self.auto_colors.isChecked(): n_channels = len(self.checks) - r, g, b = lib.get_colors(n_channels)[n] - palette.setColor( - QtGui.QPalette.ColorRole.Window, - QtGui.QColor.fromRgbF(r, g, b, 1), - ) - elif lib.is_hexadecimal(color): - color = color.lstrip("#") - r, g, b = tuple(int(color[i : i + 2], 16) / 255 for i in (0, 2, 4)) - palette.setColor( - QtGui.QPalette.ColorRole.Window, - QtGui.QColor.fromRgbF(r, g, b, 1), - ) - elif color in self.default_colors: - i = self.default_colors.index(color) - palette.setColor( - QtGui.QPalette.ColorRole.Window, - QtGui.QColor.fromRgbF( - self.rgb[i][0], - self.rgb[i][1], - self.rgb[i][2], - 1, - ), - ) - self.colordisp_all[n].setPalette(palette) + rgb = lib.get_colors(n_channels)[n] + lut = render.solid_to_lut(rgb) + else: + color = self.colorselection[n].currentText() + try: + lut = self.resolve_color_identifier(color) + except ValueError: + # Keep the previous LUT so the preview doesn't flicker; + # read_colors will raise the user-facing warning. + lut = self._channel_luts[n] + self._channel_luts[n] = lut + self.colordisp_all[n].setPixmap(_gradient_pixmap(lut)) + + def resolve_color_identifier(self, name: str) -> lib.FloatArray2D: + """Resolve a free-text identifier from a channel combobox into + a (256, 3) float32 LUT. Raises ``ValueError`` for unknown + identifiers. + + Resolution order: default_colors → hex code → built-in + gradient colormap → user-defined custom colormap. + """ + if name in self.default_colors: + rgb = self.rgb[self.default_colors.index(name)] + return render.solid_to_lut(rgb) + if lib.is_hexadecimal(name): + hexstr = name.lstrip("#") + rgb = tuple(int(hexstr[i : i + 2], 16) / 255 for i in (0, 2, 4)) + return render.solid_to_lut(rgb) + if name in self.builtin_cmap_stops: + cached = self._builtin_cmap_lut_cache.get(name) + if cached is None: + cached = render.stops_to_lut(self.builtin_cmap_stops[name]) + self._builtin_cmap_lut_cache[name] = cached + return cached + user_cmaps = getattr(self.window, "custom_colormaps_stops", {}) + if name in user_cmaps: + cached = self._user_cmap_lut_cache.get(name) + if cached is None: + cached = render.stops_to_lut(user_cmaps[name]) + self._user_cmap_lut_cache[name] = cached + return cached + raise ValueError(f"Unknown color identifier: '{name}'") + + def _populate_color_combo(self, combobox: QtWidgets.QComboBox) -> None: + """Fill a per-channel combobox with solid colors, curated + matplotlib colormaps, and any user-defined custom colormaps.""" + model = QtGui.QStandardItemModel(combobox) + + def add_header(text: str) -> None: + item = QtGui.QStandardItem(text) + item.setFlags(QtCore.Qt.ItemFlag.NoItemFlags) + font = item.font() + font.setBold(True) + item.setFont(font) + model.appendRow(item) + + def add_item(text: str) -> None: + model.appendRow(QtGui.QStandardItem(text)) + + add_header("— Solid colors —") + for c in self.default_colors: + add_item(c) + add_header("— Built-in colormaps —") + for c in self.builtin_cmap_stops: + add_item(c) + user_cmaps = getattr(self.window, "custom_colormaps_stops", {}) + if user_cmaps: + add_header("— Custom —") + for c in sorted(user_cmaps.keys()): + add_item(c) + combobox.setModel(model) + + def refresh_color_lists(self) -> None: + """Rebuild every channel combobox to reflect changes in the + custom-colormap registry. Preserves each channel's current + selection where possible.""" + # invalidate any stale cached user-cmap LUTs first + user_cmaps = getattr(self.window, "custom_colormaps_stops", {}) + self._user_cmap_lut_cache = { + k: v + for k, v in self._user_cmap_lut_cache.items() + if k in user_cmaps + } + for i, combo in enumerate(self.colorselection): + previous = combo.currentText() + combo.blockSignals(True) + self._populate_color_combo(combo) + try: + self.resolve_color_identifier(previous) + combo.setCurrentText(previous) + except ValueError: + fallback_idx = min(i, len(self.default_colors) - 1) + combo.setCurrentText(self.default_colors[fallback_idx]) + combo.blockSignals(False) + self.set_color(i) + + def open_custom_cmap_editor(self) -> None: + """Open the modal dialog for editing user-defined colormaps.""" + dialog = CustomColormapDialog(self.window) + dialog.exec() + + def legend_color(self, n: int) -> tuple[float, float, float]: + """Return a representative ``(r, g, b)`` in [0, 1] for channel + ``n``, sampled from its LUT at :data:`LEGEND_SAMPLE_IDX`. + + Used by legend / histogram / profile drawing code that needs + a single color per channel. + """ + lut = self._channel_luts[n] + return tuple(float(v) for v in lut[LEGEND_SAMPLE_IDX]) + + def legend_color_8bit(self, n: int) -> tuple[int, int, int]: + """8-bit variant of :meth:`legend_color` (each component 0–255).""" + return tuple(int(round(v * 255)) for v in self.legend_color(n)) def save_colors(self) -> None: """Save the list of colors as a .yaml file.""" @@ -802,7 +958,8 @@ def save_colors(self) -> None: file.write(color + "\n") def load_colors(self) -> None: - """Load a list of colors from a .yaml file.""" + """Load a list of colors / colormap identifiers from a .txt + file (one identifier per line).""" path, ext = QtWidgets.QFileDialog.getOpenFileName( self, "Load colors from .txt", @@ -819,15 +976,16 @@ def load_colors(self) -> None: if len(self.checks) > len(colornames): raise ValueError("Txt file contains too few names") - # check that all the names are valid + # check that all the names resolve through the same lookup + # used by the renderer (default colors, hex codes, curated + # matplotlib cmaps, or known user-defined custom cmaps). for i, color in enumerate(colornames): - if ( - color not in self.default_colors - and not lib.is_hexadecimal(color) - ): + try: + self.resolve_color_identifier(color) + except ValueError as e: raise ValueError( f"'{color}' at position {i+1} is invalid." - ) + ) from e # add the names to the 'Color' column (self.colorseletion) for i, color_ in enumerate(self.colorselection): @@ -838,6 +996,454 @@ def sizeHint(self) -> QtCore.QSize: return QtCore.QSize(600, 350) +class CustomColormapDialog(lib.Dialog): + """Modal editor for user-defined colormaps. + + Each colormap is a named list of 2-5 color stops; each stop is + ``(position, R, G, B)`` with ``position`` in [0, 1], strictly + increasing, first stop at 0.0 and last at 1.0. Stops are linearly + interpolated into a (256, 3) LUT when applied. + + Custom colormaps are stored on the main window as + ``custom_colormaps_stops`` and persisted in ``~/.picasso/settings.yaml``. + + Every mutation (new / duplicate / rename / delete / stop edit) is + committed immediately to the window state and to settings.yaml — + there is no "save vs. cancel" working-copy. + """ + + _MAX_STOPS = 5 + _MIN_STOPS = 2 + + def __init__(self, window: QtWidgets.QMainWindow) -> None: + super().__init__(window) + self.window = window + self.setWindowTitle("Custom colormaps") + self.setModal(True) + self.resize(640, 360) + + # The source of truth is ``window.custom_colormaps_stops``; the + # dialog mutates it in place and persists on each change. + if not hasattr(window, "custom_colormaps_stops"): + window.custom_colormaps_stops = {} + self._current_name: str | None = None + self._suppress_signals = False + + layout = QtWidgets.QHBoxLayout(self) + + # ---- left pane: list + new/duplicate/rename/delete buttons + left = QtWidgets.QVBoxLayout() + self.name_list = QtWidgets.QListWidget() + self.name_list.currentItemChanged.connect(self._on_name_selected) + left.addWidget(self.name_list) + + btn_row = QtWidgets.QHBoxLayout() + self.new_btn = QtWidgets.QPushButton("New") + self.new_btn.clicked.connect(self._on_new) + btn_row.addWidget(self.new_btn) + self.dup_btn = QtWidgets.QPushButton("Duplicate") + self.dup_btn.clicked.connect(self._on_duplicate) + btn_row.addWidget(self.dup_btn) + left.addLayout(btn_row) + btn_row2 = QtWidgets.QHBoxLayout() + self.rename_btn = QtWidgets.QPushButton("Rename") + self.rename_btn.clicked.connect(self._on_rename) + btn_row2.addWidget(self.rename_btn) + self.delete_btn = QtWidgets.QPushButton("Delete") + self.delete_btn.clicked.connect(self._on_delete) + btn_row2.addWidget(self.delete_btn) + left.addLayout(btn_row2) + layout.addLayout(left, 1) + + # ---- right pane: editor for the currently-selected colormap + right = QtWidgets.QVBoxLayout() + right.addWidget(QtWidgets.QLabel("Stops (position 0 → 1):")) + self.stops_table = QtWidgets.QTableWidget(0, 4) + self.stops_table.setHorizontalHeaderLabels(["Position", "R", "G", "B"]) + self.stops_table.horizontalHeader().setStretchLastSection(True) + self.stops_table.cellChanged.connect(self._on_cell_changed) + self.stops_table.cellDoubleClicked.connect( + self._on_cell_double_clicked + ) + right.addWidget(self.stops_table) + + stop_btn_row = QtWidgets.QHBoxLayout() + self.add_stop_btn = QtWidgets.QPushButton("Add stop") + self.add_stop_btn.clicked.connect(self._on_add_stop) + stop_btn_row.addWidget(self.add_stop_btn) + self.remove_stop_btn = QtWidgets.QPushButton("Remove stop") + self.remove_stop_btn.clicked.connect(self._on_remove_stop) + stop_btn_row.addWidget(self.remove_stop_btn) + right.addLayout(stop_btn_row) + + right.addWidget(QtWidgets.QLabel("Preview:")) + self.preview_label = QtWidgets.QLabel() + self.preview_label.setFixedHeight(20) + self.preview_label.setFrameShape(QtWidgets.QFrame.Shape.Box) + right.addWidget(self.preview_label) + right.addStretch(1) + + action_row = QtWidgets.QHBoxLayout() + self.close_btn = QtWidgets.QPushButton("Close") + self.close_btn.setDefault(True) + self.close_btn.clicked.connect(self.accept) + self._focus_buttons.append("Close") + action_row.addStretch(1) + action_row.addWidget(self.close_btn) + right.addLayout(action_row) + + layout.addLayout(right, 2) + + self._refresh_name_list() + if self.name_list.count(): + self.name_list.setCurrentRow(0) + else: + self._set_editor_enabled(False) + + @property + def _stops(self) -> dict[str, list[list[float]]]: + """Live reference to the window-level cmap registry. All + mutations to the registry should be written through this + property so that ``_commit`` picks them up.""" + return self.window.custom_colormaps_stops + + def _commit(self) -> None: + """Persist the current cmap registry to settings.yaml, drop + cached LUTs, and refresh every channel combobox. Called after + every mutation so the editor has no working-copy state.""" + # invalidate any cached LUTs that no longer match current stops + cache = self.window.dataset_dialog._user_cmap_lut_cache + for name in list(cache.keys()): + if name not in self._stops: + del cache[name] + elif cache[name] is not None: + # stops may have changed for this name; force re-resolve + del cache[name] + # persist to ~/.picasso/settings.yaml + try: + settings = io.load_user_settings() + settings["Render"]["CustomColormaps"] = { + name: [list(stop) for stop in stops] + for name, stops in self._stops.items() + } + io.save_user_settings(settings) + except Exception as exc: + # Surface the failure rather than silently dropping it so + # the user knows their change didn't reach disk. + QtWidgets.QMessageBox.warning( + self, + "Could not save settings", + f"Failed to persist custom colormaps to disk:\n{exc}", + ) + self.window.dataset_dialog.refresh_color_lists() + + # ---------------- list management ---------------- + + def _refresh_name_list(self) -> None: + self.name_list.blockSignals(True) + self.name_list.clear() + for name in sorted(self._stops.keys()): + self.name_list.addItem(name) + self.name_list.blockSignals(False) + + def _on_name_selected( + self, + current: QtWidgets.QListWidgetItem | None, + _previous: QtWidgets.QListWidgetItem | None, + ) -> None: + if current is None: + self._current_name = None + self._set_editor_enabled(False) + self.stops_table.setRowCount(0) + self.preview_label.clear() + return + self._current_name = current.text() + self._set_editor_enabled(True) + self._load_stops_into_table(self._stops[self._current_name]) + + def _set_editor_enabled(self, enabled: bool) -> None: + for w in ( + self.stops_table, + self.add_stop_btn, + self.remove_stop_btn, + self.rename_btn, + self.delete_btn, + self.dup_btn, + ): + w.setEnabled(enabled) + + def _on_new(self) -> None: + name = self._prompt_new_name(default="Custom1") + if not name: + return + self._stops[name] = [ + [0.0, 0.0, 0.0, 0.0], + [1.0, 1.0, 1.0, 1.0], + ] + self._commit() + self._refresh_name_list() + items = self.name_list.findItems( + name, QtCore.Qt.MatchFlag.MatchExactly + ) + if items: + self.name_list.setCurrentItem(items[0]) + + def _on_duplicate(self) -> None: + if self._current_name is None: + return + name = self._prompt_new_name(default=f"{self._current_name}_copy") + if not name: + return + self._stops[name] = [ + list(stop) for stop in self._stops[self._current_name] + ] + self._commit() + self._refresh_name_list() + items = self.name_list.findItems( + name, QtCore.Qt.MatchFlag.MatchExactly + ) + if items: + self.name_list.setCurrentItem(items[0]) + + def _on_rename(self) -> None: + if self._current_name is None: + return + name = self._prompt_new_name(default=self._current_name) + if not name or name == self._current_name: + return + self._stops[name] = self._stops.pop(self._current_name) + self._current_name = name + self._commit() + self._refresh_name_list() + items = self.name_list.findItems( + name, QtCore.Qt.MatchFlag.MatchExactly + ) + if items: + self.name_list.setCurrentItem(items[0]) + + def _on_delete(self) -> None: + if self._current_name is None: + return + confirm = QtWidgets.QMessageBox.question( + self, + "Delete colormap", + f"Delete custom colormap '{self._current_name}'?", + ) + if confirm != QtWidgets.QMessageBox.StandardButton.Yes: + return + del self._stops[self._current_name] + self._current_name = None + self._commit() + self._refresh_name_list() + if self.name_list.count(): + self.name_list.setCurrentRow(0) + else: + self._set_editor_enabled(False) + self.stops_table.setRowCount(0) + self.preview_label.clear() + + def _prompt_new_name(self, default: str = "") -> str | None: + # ensure default is unique + candidate = default + i = 1 + while candidate in self._stops: + i += 1 + candidate = f"{default}_{i}" + while True: + name, ok = QtWidgets.QInputDialog.getText( + self, + "Colormap name", + "Enter a unique name for the colormap:", + text=candidate, + ) + if not ok: + return None + name = name.strip() + error = self._validate_name(name) + if error is None: + return name + QtWidgets.QMessageBox.warning(self, "Invalid name", error) + candidate = name + + def _validate_name(self, name: str) -> str | None: + if not name: + return "Name must not be empty." + dlg = self.window.dataset_dialog + if name in dlg.default_colors: + return f"'{name}' collides with a built-in solid color." + if name in dlg.builtin_cmap_stops: + return f"'{name}' collides with a built-in colormap." + if name in self._stops and name != self._current_name: + return f"A custom colormap named '{name}' already exists." + return None + + # ---------------- stop editor ---------------- + + def _load_stops_into_table(self, stops: list[list[float]]) -> None: + self._suppress_signals = True + self.stops_table.setRowCount(len(stops)) + for r, stop in enumerate(stops): + for c, value in enumerate(stop): + item = QtWidgets.QTableWidgetItem(f"{value:.3f}") + self.stops_table.setItem(r, c, item) + self._refresh_color_swatches(r) + self._suppress_signals = False + self._update_preview() + + def _read_table(self) -> list[list[float]]: + rows = self.stops_table.rowCount() + stops: list[list[float]] = [] + for r in range(rows): + stop: list[float] = [] + for c in range(4): + item = self.stops_table.item(r, c) + stop.append(float(item.text()) if item else 0.0) + stops.append(stop) + return stops + + def _refresh_color_swatches(self, row: int) -> None: + rgb: list[float] = [] + for c in (1, 2, 3): + item = self.stops_table.item(row, c) + try: + rgb.append(max(0.0, min(1.0, float(item.text())))) + except (AttributeError, ValueError): + rgb.append(0.0) + color = QtGui.QColor.fromRgbF(*rgb, 1.0) + text_color = ( + QtCore.Qt.GlobalColor.white + if sum(rgb) / 3 < 0.5 + else QtCore.Qt.GlobalColor.black + ) + for c in (1, 2, 3): + item = self.stops_table.item(row, c) + if item is None: + continue + item.setBackground(QtGui.QBrush(color)) + item.setForeground(QtGui.QBrush(QtGui.QColor(text_color))) + + def _on_cell_changed(self, row: int, col: int) -> None: + if self._suppress_signals or self._current_name is None: + return + item = self.stops_table.item(row, col) + try: + value = float(item.text()) + except ValueError: + value = 0.0 + value = max(0.0, min(1.0, value)) + self._suppress_signals = True + item.setText(f"{value:.3f}") + self._suppress_signals = False + self._refresh_color_swatches(row) + self._stops[self._current_name] = self._read_table() + self._update_preview() + self._commit() + + def _on_cell_double_clicked(self, row: int, col: int) -> None: + if col not in (1, 2, 3): + return + item = self.stops_table.item(row, 1) + r = float(item.text()) if item else 0.0 + item = self.stops_table.item(row, 2) + g = float(item.text()) if item else 0.0 + item = self.stops_table.item(row, 3) + b = float(item.text()) if item else 0.0 + initial = QtGui.QColor.fromRgbF( + max(0.0, min(1.0, r)), + max(0.0, min(1.0, g)), + max(0.0, min(1.0, b)), + 1.0, + ) + chosen = QtWidgets.QColorDialog.getColor( + initial, self, "Pick stop color" + ) + if not chosen.isValid(): + return + self._suppress_signals = True + self.stops_table.item(row, 1).setText(f"{chosen.redF():.3f}") + self.stops_table.item(row, 2).setText(f"{chosen.greenF():.3f}") + self.stops_table.item(row, 3).setText(f"{chosen.blueF():.3f}") + self._suppress_signals = False + self._refresh_color_swatches(row) + if self._current_name is not None: + self._stops[self._current_name] = self._read_table() + self._update_preview() + self._commit() + + def _on_add_stop(self) -> None: + if self._current_name is None: + return + if self.stops_table.rowCount() >= self._MAX_STOPS: + QtWidgets.QMessageBox.information( + self, + "Stop limit", + f"At most {self._MAX_STOPS} stops are supported.", + ) + return + stops = self._read_table() + # insert before the last stop at the midpoint + last_pos = stops[-1][0] + prev_pos = stops[-2][0] if len(stops) >= 2 else 0.0 + mid_pos = (last_pos + prev_pos) / 2 + new_stop = [mid_pos, 0.5, 0.5, 0.5] + stops.insert(len(stops) - 1, new_stop) + self._stops[self._current_name] = stops + self._load_stops_into_table(stops) + self._commit() + + def _on_remove_stop(self) -> None: + if self._current_name is None: + return + if self.stops_table.rowCount() <= self._MIN_STOPS: + QtWidgets.QMessageBox.information( + self, + "Stop limit", + f"At least {self._MIN_STOPS} stops are required.", + ) + return + row = self.stops_table.currentRow() + if row in (-1, 0, self.stops_table.rowCount() - 1): + QtWidgets.QMessageBox.information( + self, + "Cannot remove", + "The first and last stops are required.", + ) + return + stops = self._read_table() + del stops[row] + self._stops[self._current_name] = stops + self._load_stops_into_table(stops) + self._commit() + + def _update_preview(self) -> None: + if self._current_name is None: + self.preview_label.clear() + return + try: + stops = self._stops[self._current_name] + self._sanity_check_stops(stops) + lut = render.stops_to_lut(stops) + except (ValueError, IndexError): + self.preview_label.clear() + return + width = max(40, self.preview_label.width()) + self.preview_label.setPixmap( + _gradient_pixmap(lut, width=width, height=18) + ) + + def _sanity_check_stops(self, stops: list[list[float]]) -> None: + """Light-touch validation used by the preview. Full validation + is run again on save.""" + if len(stops) < self._MIN_STOPS: + raise ValueError("not enough stops") + positions = [s[0] for s in stops] + if positions[0] != 0.0 or positions[-1] != 1.0: + raise ValueError("positions must start at 0 and end at 1") + for a, b in zip(positions, positions[1:]): + if b <= a: + raise ValueError("positions must be strictly increasing") + + class PlotDialog(lib.Dialog): """Plot a 3D scatter of picked localizations. Allows the user to keep the selected picks or remove them.""" @@ -6083,12 +6689,9 @@ def calculate_histogram(self) -> None: self.ax.clear() # get colors for each channel (from dataset dialog) - colors = [ - _.palette().color(QtGui.QPalette.ColorRole.Window) - for _ in self.window.dataset_dialog.colordisp_all - ] self.colors = [ - [_.red() / 255, _.green() / 255, _.blue() / 255] for _ in colors + list(self.window.dataset_dialog.legend_color(i)) + for i in range(len(self.window.dataset_dialog.colordisp_all)) ] # get bins, starting with minimum z and ending with max z @@ -7469,13 +8072,9 @@ def draw_legend(self, image: QtGui.QImage) -> QtGui.QImage: if self.window.dataset_dialog.checks[i].isChecked(): channel_name = self.window.dataset_dialog.checks[i].text() channel_names.append(channel_name) - colordisp = self.window.dataset_dialog.colordisp_all[i] - color = colordisp.palette().color( - QtGui.QPalette.ColorRole.Window + channel_colors.append( + self.window.dataset_dialog.legend_color_8bit(i) ) - # Convert QColor to RGB tuple (0-255 range) - color_rgb = (color.red(), color.green(), color.blue()) - channel_colors.append(color_rgb) if self.window.dataset_dialog.legend.isChecked(): image = render.draw_legend( image=image, @@ -8074,9 +8673,26 @@ def load_screenshot(self, file: dict) -> None: # noqa: C901 elif "Min. blur (nm)" in file: disp_dlg.min_blur_width.setValue(file["Min. blur (nm)"]) if "Colors" in file and len(file["Colors"]) == len(self.locs): + dataset_dialog = self.window.dataset_dialog + missing: list[str] = [] for i, color in enumerate(file["Colors"]): - self.window.dataset_dialog.colorselection[i].setCurrentText( - color + try: + dataset_dialog.resolve_color_identifier(color) + except ValueError: + missing.append(color) + fallback = dataset_dialog.default_colors[ + min(i, len(dataset_dialog.default_colors) - 1) + ] + dataset_dialog.colorselection[i].setCurrentText(fallback) + continue + dataset_dialog.colorselection[i].setCurrentText(color) + if missing: + QtWidgets.QMessageBox.information( + self, + "Unknown colors", + "Some saved color identifiers were not found and " + "have been replaced with defaults: " + + ", ".join(sorted(set(missing))), ) if "Scalebar length (nm)" in file: disp_dlg.scalebar.setValue(file["Scalebar length (nm)"]) @@ -8992,11 +9608,8 @@ def _display_pick_count_histogram(self, loccount, channels: list) -> None: ax = fig.add_subplot(111) ax.set_title("Localizations in Picks ") colors = [ - _.palette().color(QtGui.QPalette.ColorRole.Window) - for _ in self.window.dataset_dialog.colordisp_all - ] - colors = [ - [_.red() / 255, _.green() / 255, _.blue() / 255] for _ in colors + list(self.window.dataset_dialog.legend_color(i)) + for i in range(len(self.window.dataset_dialog.colordisp_all)) ] bins = lib.calculate_optimal_bins(loccount.flatten(), max_n_bins=1000) for i, channel in enumerate(channels): @@ -9139,7 +9752,7 @@ def index_locs(self, channel: int) -> None: status.close() self.index_blocks[channel] = index_blocks - def get_index_blocks(self, channel: int) -> np.ndarray: + def get_index_blocks(self, channel: int) -> tuple: """Call ``self.index_locs`` if not calculated earlier. Return indexed localizations from a given channel.""" if self.index_blocks[channel] is None: @@ -9237,11 +9850,8 @@ def _plot_profile(self, channels: list[int]) -> None: ax = self.canvas.figure.add_subplot(111) colors = [ - _.palette().color(QtGui.QPalette.ColorRole.Window) - for _ in self.window.dataset_dialog.colordisp_all - ] - colors = [ - [_.red() / 255, _.green() / 255, _.blue() / 255] for _ in colors + list(self.window.dataset_dialog.legend_color(i)) + for i in range(len(self.window.dataset_dialog.colordisp_all)) ] concat = np.concatenate(self.profiles) bin_edges = lib.calculate_optimal_bins(concat, max_n_bins=1000) @@ -9341,7 +9951,7 @@ def _display_indices( viewport: ( tuple[tuple[float, float], tuple[float, float]] | None ) = None, - ) -> np.ndarray | None: + ) -> lib.IntArray1D | None: """Positional indices into ``self.locs[channel]`` selected for display, or ``None`` when the full set is used. Combines the fast-render subset and the viewport pyramid filter. @@ -9386,7 +9996,7 @@ def _viewport_indices( self, channel: int, viewport: tuple[tuple[float, float], tuple[float, float]], - ) -> np.ndarray | None: + ) -> lib.IntArray1D | None: """Indices of locs in ``channel`` that fall inside ``viewport``. Returns ``None`` if the pyramid is unavailable -- the caller @@ -9683,12 +10293,20 @@ def render_scene( return qimage - def read_colors(self, n_channels: int | None = None) -> list[list[float]]: - """Find currently selected colors for multicolor rendering. + def read_colors( + self, n_channels: int | None = None + ) -> list[lib.FloatArray2D]: + """Find currently selected colors/colormaps for multicolor + rendering. + + Each returned entry is a ``(256, 3)`` float32 LUT. Solid colors + become black→color linear ramps (math-equivalent to the + original "intensity × rgb" multichannel blend). Matplotlib + colormaps and user-defined custom colormaps are also LUTs. - If multiple channels are loaded, ensure that only the ones which - are checked in the Dataset Dialog are rendered in their selected - colors. + If multiple channels are loaded, ensure that only the ones + which are checked in the Dataset Dialog are rendered in their + selected colors. Parameters ---------- @@ -9698,67 +10316,57 @@ def read_colors(self, n_channels: int | None = None) -> list[list[float]]: Returns ------- - colors : list - List of lists with RGB values from 0 to 1 for each channel. + colors : list of 2D arrays + One ``(256, 3)`` float32 LUT per channel. """ if n_channels is None: n_channels = len(self.locs) - colors = lib.get_colors(n_channels) # automatic colors + dataset_dialog = self.window.dataset_dialog + # automatic colors: HSV-spaced solid colors → black→color LUTs + colors = [ + render.solid_to_lut(rgb) for rgb in lib.get_colors(n_channels) + ] # color each channel one by one for i in range(len(self.locs)): # change colors if not automatic coloring - if not self.window.dataset_dialog.auto_colors.isChecked(): - # get color from Dataset Dialog - color = self.window.dataset_dialog.colorselection[ - i - ].currentText() - # if default color - if color in self.window.dataset_dialog.default_colors: - colors_array = np.array( - self.window.dataset_dialog.default_colors, - dtype=object, - ) - index = np.where(colors_array == color)[0][0] - # assign color - colors[i] = tuple(self.window.dataset_dialog.rgb[index]) - # if hexadecimal is given - elif lib.is_hexadecimal(color): - colorstring = color.lstrip("#") - rgbval = tuple( - int(colorstring[i : i + 2], 16) / 255 - for i in (0, 2, 4) + if not dataset_dialog.auto_colors.isChecked(): + color_name = dataset_dialog.colorselection[i].currentText() + try: + colors[i] = dataset_dialog.resolve_color_identifier( + color_name ) - # assign color - colors[i] = rgbval - else: + except ValueError: warning = ( - "The color selection not recognised in the channel " - f"{self.window.dataset_dialog.checks[i].text()}. Please" - " choose one of the options provided or type the " - "hexadecimal code for your color of choice, " - " starting with '#', e.g. '#ffcdff' for pink." + "The color selection not recognised in the " + f"channel {dataset_dialog.checks[i].text()}. " + "Please choose one of the options provided, " + "type a hexadecimal code (e.g. '#ffcdff'), or " + "define a custom colormap." ) QtWidgets.QMessageBox.information(self, "Warning", warning) break # use only the checked channels if len(self.locs) > 1: - colors_ = [] - for i in range(len(self.locs)): - if self.window.dataset_dialog.checks[i].isChecked(): - colors_.append(colors[i]) - colors = colors_ + colors = [ + colors[i] + for i in range(len(self.locs)) + if dataset_dialog.checks[i].isChecked() + ] elif len(self.locs) == 1 and "group" in self.locs[0].columns: - colors = lib.get_colors( - N_GROUP_COLORS - ) # automatic colors for groups + # automatic colors for groups + colors = [ + render.solid_to_lut(rgb) + for rgb in lib.get_colors(N_GROUP_COLORS) + ] # render properties if self.x_render_state: - colors = render.get_colors_from_colormap( + prop_rgbs = render.get_colors_from_colormap( len(self.x_locs), self.window.display_settings_dlg.colormap_prop.currentText(), ) + colors = [render.solid_to_lut(rgb) for rgb in prop_rgbs] return colors @@ -11098,6 +11706,11 @@ def initUI(self, plugins_loaded: bool) -> None: self.view.setMinimumSize(1, 1) self.setCentralWidget(self.view) + # User-defined colormaps, keyed by name, each a list of + # [position, R, G, B] stops in [0, 1]. Populated from + # ~/.picasso/settings.yaml in load_user_settings. + self.custom_colormaps_stops: dict[str, list[list[float]]] = {} + # set up dialogs self.display_settings_dlg = DisplaySettingsDialog(self) self.tools_settings_dialog = ToolsSettingsDialog(self) @@ -11507,6 +12120,10 @@ def closeEvent(self, event: QtGui.QCloseEvent) -> None: settings["Render"]["PWD"] = os.path.dirname( self.view.locs_paths[0] ) + settings["Render"]["CustomColormaps"] = { + name: [list(stop) for stop in stops] + for name, stops in self.custom_colormaps_stops.items() + } io.save_user_settings(settings) QtWidgets.QApplication.instance().closeAllWindows() @@ -11978,8 +12595,15 @@ def export_fov_ims(self) -> None: # noqa: C901 s_image = np.stack(all_img, axis=-1).T.copy() + # Imaris expects a single RGB per channel. Sample each + # channel's LUT at LEGEND_SAMPLE_IDX to get a representative + # color (this matches the legend/histogram convention and + # avoids near-white peaks of reversed single-hue cmaps). colors = self.view.read_colors() - colors_ims = [PW.Color(*list(colors[_]), 1) for _ in to_render] + colors_ims = [ + PW.Color(*[float(v) for v in colors[_][LEGEND_SAMPLE_IDX]], 1) + for _ in to_render + ] numpy_to_imaris( s_image, @@ -12046,6 +12670,29 @@ def load_user_settings(self) -> None: # noqa: C901 pwd = [] self.pwd = pwd + # User-defined colormaps for per-channel rendering + try: + stored = settings["Render"]["CustomColormaps"] + except (KeyError, TypeError): + stored = {} + parsed: dict[str, list[list[float]]] = {} + for name, stops in (stored or {}).items(): + try: + parsed[name] = [ + [ + float(stop[0]), + float(stop[1]), + float(stop[2]), + float(stop[3]), + ] + for stop in stops + ] + except (TypeError, ValueError, IndexError): + continue + self.custom_colormaps_stops = parsed + if hasattr(self, "dataset_dialog"): + self.dataset_dialog.refresh_color_lists() + def open_apply_dialog(self) -> None: """Load expression and apply it to locs.""" cmd, channel, ok = ApplyDialog.getCmd(self) diff --git a/picasso/gui/rotation.py b/picasso/gui/rotation.py index bc083ed3..1ecc80ac 100644 --- a/picasso/gui/rotation.py +++ b/picasso/gui/rotation.py @@ -1021,18 +1021,14 @@ def draw_legend(self, image: QtGui.QImage) -> QtGui.QImage: if self.window.dataset_dialog.checks[i].isChecked(): channel_name = self.window.dataset_dialog.checks[i].text() channel_names.append(channel_name) - colordisp = self.window.dataset_dialog.colordisp_all[i] - color = colordisp.palette().color( - QtGui.QPalette.ColorRole.Window + channel_colors.append( + self.window.dataset_dialog.legend_color_8bit(i) ) - # Convert QColor to RGB tuple (0-255 range) - color_rgb = (color.red(), color.green(), color.blue()) - channel_colors.append(color_rgb) - image = render.draw_legend( - image=image, - channel_names=channel_names, - channel_colors=channel_colors, - ) + image = render.draw_legend( + image=image, + channel_names=channel_names, + channel_colors=channel_colors, + ) return image def draw_rotation(self, image: QtGui.QImage) -> QtGui.QImage: diff --git a/picasso/render.py b/picasso/render.py index d87db0a4..ec14816f 100755 --- a/picasso/render.py +++ b/picasso/render.py @@ -1627,7 +1627,7 @@ def rotation_matrix(angx: float, angy: float, angz: float) -> Rotation: Returns ------- scipy.spatial.transform.Rotation - Scipy class that can be applied to rotate an Nx3 np.ndarray + Scipy class that can be applied to rotate an Nx3 array. """ rot_mat_x = np.array( [ @@ -1759,6 +1759,76 @@ def export_qimage_to_svg(image: QtGui.QImage, path: str): painter.end() +def solid_to_lut(rgb: tuple[float, float, float]) -> lib.FloatArray2D: + """Build a (256, 3) float32 LUT that linearly ramps from black to + the given RGB color. + + The returned LUT is the input format expected by + :func:`_render_multi_channel` (and therefore :func:`render_scene`) + when colors are passed as per-channel lookup tables. A solid-color + channel rendered through this LUT is mathematically identical to + the legacy ``intensity * rgb`` blend. + + Parameters + ---------- + rgb : sequence of 3 floats + Target RGB color, each component in range [0, 1]. + + Returns + ------- + lut : lib.FloatArray2D + LUT with generated colormap of shape (256, 3). + + Examples + -------- + >>> lut = solid_to_lut((1.0, 0.0, 0.0)) # black -> red + >>> render_scene(locs=..., info=..., colors=[lut, ...], ...) + """ + rgb_arr = np.asarray(rgb, dtype=np.float32).reshape(3) + return np.linspace( + np.zeros(3, dtype=np.float32), rgb_arr, 256, dtype=np.float32 + ) + + +def stops_to_lut( + stops: list[tuple[float, float, float, float]], +) -> lib.FloatArray2D: + """Build a (256, 3) float32 LUT by linearly interpolating between + color stops. + + Parameters + ---------- + stops : sequence of (position, r, g, b) tuples + Each ``position`` must be in [0, 1], strictly increasing, with + the first stop at 0.0 and the last at 1.0. ``r``, ``g``, ``b`` + are also in [0, 1]. + + Returns + ------- + lut : lib.FloatArray2D + LUT with generated colormap of shape (256, 3). + + Examples + -------- + A 3-stop "fire" gradient (black -> red -> yellow): + + >>> lut = stops_to_lut([ + ... (0.0, 0, 0, 0), + ... (0.5, 1, 0, 0), + ... (1.0, 1, 1, 0), + ... ]) + >>> render_scene(locs=..., info=..., colors=[lut], ...) + """ + arr = np.asarray(stops, dtype=np.float32) + positions = arr[:, 0] + rgb = arr[:, 1:4] + x = np.linspace(0.0, 1.0, 256, dtype=np.float32) + lut = np.empty((256, 3), dtype=np.float32) + for c in range(3): + lut[:, c] = np.interp(x, positions, rgb[:, c]) + return lut + + def get_colors_from_colormap( n_channels: int, cmap: str = "gist_rainbow", @@ -2928,7 +2998,7 @@ def _render_multi_channel( info: list[list[dict]], *, disp_px_size: float, - colors: list[tuple[int, int, int]], + colors: list[tuple[int, int, int]] | list[np.ndarray], viewport: tuple[tuple[float, float], tuple[float, float]] | None = None, blur_method: ( Literal["gaussian", "gaussian_iso", "smooth", "convolve"] | None @@ -2941,7 +3011,14 @@ def _render_multi_channel( raw_image_cache: lib.FloatArray3D | None = None, ) -> tuple[int, lib.IntArray3D, tuple[float, float], lib.FloatArray3D]: """Render multi-channel localizations into an RGB 8bit image - (numpy array). See ``render_scene`` for more details.""" + (numpy array). See ``render_scene`` for more details. + + ``colors`` may be either a list of ``(r, g, b)`` triplets (legacy + behaviour: each channel rendered as ``intensity × rgb``, additive + blend) or a list of ``(256, 3)`` LUTs (each channel indexed into + its LUT before additive blending — supports per-channel + matplotlib colormaps and user-defined colormaps from the GUI). + """ if raw_image_cache is not None: assert raw_image_cache.ndim == 3, "raw_image_cache must be a 3D array." raw_image = raw_image_cache @@ -2977,7 +3054,17 @@ def _render_multi_channel( colors = lib.get_colors(len(images)) colors_arr = np.asarray(colors, dtype=np.float32) images_f32 = np.ascontiguousarray(images, dtype=np.float32) - rgb = np.tensordot(images_f32, colors_arr, axes=([0], [0])) + if colors_arr.ndim == 2: + # legacy path: each channel is a single (r, g, b) + rgb = np.tensordot(images_f32, colors_arr, axes=([0], [0])) + else: + # LUT path: each channel is a (256, 3) lookup table + idx = np.clip((images_f32 * 255.0).astype(np.int32), 0, 255) + rgb = np.zeros( + (images_f32.shape[1], images_f32.shape[2], 3), dtype=np.float32 + ) + for c in range(images_f32.shape[0]): + rgb += colors_arr[c][idx[c]] # clip to max value of 1 (preserves relative brightness) np.minimum(rgb, 1.0, out=rgb) rgb = to_8bit(rgb) From 5879c506451c56574214e7f51e30e8f5dbe895d5 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 25 May 2026 15:12:42 +0200 Subject: [PATCH 18/42] add documentation of the colormaps in multichannel and the files dialog --- docs/render.rst | 54 ++++++++++++++++++++++++++++++++++++++++++- picasso/gui/render.py | 9 +++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/docs/render.rst b/docs/render.rst index f7d0d998..76f7a9ef 100644 --- a/docs/render.rst +++ b/docs/render.rst @@ -299,7 +299,59 @@ Opens the Display Settings Dialog. Files (CTRL + F) ^^^^^^^^^^^^^^^^ -Open a dialog to select the color and toggle visibility for each loaded dataset. +Opens the **Datasets** dialog, which lists every loaded channel and lets you +control its title, visibility, color (or colormap), and relative intensity. +A small horizontal gradient next to each channel previews what that channel +will look like at intensity 0 → intensity 1. + +Each channel's *Color* dropdown is organised into three sections: + +* **Solid colors** — the 14 default named colors (``red``, ``cyan``, + ``green``, …). You can also type a hexadecimal code such as ``#FF5733`` + directly into the dropdown. Solid colors are rendered as a black → + color ramp, exactly matching the previous "intensity × RGB" behaviour. +* **Built-in colormaps** — one 3-stop *black → color → white* gradient + per default solid color, named ``_gradient`` (e.g. + ``blue_gradient``, ``red_gradient``). +* **Custom** — any user-defined colormaps (see "Edit custom colormaps…" + below). This section only appears once at least one custom colormap + has been defined. + +Channels are blended additively in the final image and clipped to 1.0, +so overlapping high-intensity regions saturate toward the sum of the +channel colors. + +The ``Automatic coloring`` checkbox overrides per-channel selections with +HSV-spaced colors for as long as it's ticked. ``Save colors`` / +``Load colors`` write / read a one-identifier-per-line ``.txt`` file — +any name from the three dropdown sections (or a hex code) is valid. + +Edit custom colormaps ++++++++++++++++++++++ +Opens a small editor where you can create, rename, duplicate, or delete +your own colormaps. Each custom colormap is a list of 2-5 *stops*; each +stop has a position in [0, 1] and an RGB color. Stops are linearly +interpolated into the 256-row look-up table (LUT) used at render time. Click any of the R / G / B cells to type a value, or **double-click** the row to pick the +stop color from a standard color dialog. Use ``Add stop`` / +``Remove stop`` to grow or shrink the gradient. + +Programmatic use +++++++++++++++++ +The underlying conversion from solid colors or stops to a ``(256, 3)`` +LUT is also exposed as part of ``picasso.render``:: + + from picasso import render + lut_red = render.solid_to_lut((1.0, 0.0, 0.0)) # black → red + lut_fire = render.stops_to_lut([(0, 0, 0, 0), + (0.5, 1, 0, 0), + (1, 1, 1, 0)]) # black → red → yellow + qimage, *_ = render.render_scene( + locs=..., info=..., colors=[lut_red, lut_fire], ... + ) + +Passing a list of LUTs to ``render_scene`` selects the per-channel +colormap path; passing a list of plain RGB triplets (legacy) still works +and is equivalent to ``solid_to_lut`` per channel. Left / Right / Up / Down ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/picasso/gui/render.py b/picasso/gui/render.py index fe6fb6fd..e503b09d 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -420,6 +420,8 @@ class DatasetDialog(lib.Dialog): Main window instance. """ + DOCS_URL = "https://picassosr.readthedocs.io/en/latest/render.html#files-ctrl-f" # noqa: E501 + def __init__(self, window: QtWidgets.QMainWindow) -> None: super().__init__(window) self.window = window @@ -490,8 +492,13 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: "Custom colormaps appear in the per-channel selector." ) layout.addWidget(edit_cmaps_button, 2, 2) - edit_cmaps_button.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus) edit_cmaps_button.clicked.connect(self.open_custom_cmap_editor) + layout.addWidget( + lib.HelpButton(self.DOCS_URL), + 3, + 2, + alignment=QtCore.Qt.AlignmentFlag.AlignRight, + ) # add scrollable area which will display all channels, below # the non-scrollable elements From e8e5a028f195c63db940717c6363ed136a7afbc2 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 25 May 2026 15:19:46 +0200 Subject: [PATCH 19/42] add new tests for multichannel colormaps --- tests/test_render.py | 250 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) diff --git a/tests/test_render.py b/tests/test_render.py index 84bc8973..8134bff4 100644 --- a/tests/test_render.py +++ b/tests/test_render.py @@ -822,6 +822,256 @@ def test_get_group_color_modulo(self): assert (out == np.arange(20) % render.N_GROUP_COLORS).all() +# --------------------------------------------------------------------------- +# Per-channel LUT helpers (solid_to_lut, stops_to_lut) +# --------------------------------------------------------------------------- + + +class TestSolidToLut: + """``render.solid_to_lut`` builds a (256, 3) float32 black->color ramp. + For solid-color channels this LUT path is mathematically identical to + the legacy ``intensity * rgb`` blend used by ``_render_multi_channel``. + """ + + def test_shape_and_dtype(self): + lut = render.solid_to_lut((1.0, 0.0, 0.0)) + assert lut.shape == (256, 3) + assert lut.dtype == np.float32 + + def test_endpoints(self): + """First row is black; last row is the target color.""" + lut = render.solid_to_lut((1.0, 0.5, 0.25)) + assert np.allclose(lut[0], [0.0, 0.0, 0.0]) + assert np.allclose(lut[-1], [1.0, 0.5, 0.25]) + + def test_linear_ramp(self): + """Row i is i / 255 * target_rgb.""" + rgb = np.array([0.8, 0.4, 0.2], dtype=np.float32) + lut = render.solid_to_lut(rgb) + expected = np.linspace(np.zeros(3), rgb, 256, dtype=np.float32) + assert np.allclose(lut, expected) + + def test_accepts_tuple_list_array(self): + """Helper accepts any 3-element rgb container.""" + for rgb in [ + (1.0, 0.0, 0.0), + [1.0, 0.0, 0.0], + np.array([1.0, 0.0, 0.0], dtype=np.float32), + ]: + lut = render.solid_to_lut(rgb) + assert lut.shape == (256, 3) + assert np.allclose(lut[-1], [1.0, 0.0, 0.0]) + + def test_black_target_is_all_zero(self): + lut = render.solid_to_lut((0.0, 0.0, 0.0)) + assert (lut == 0).all() + + +class TestStopsToLut: + """``render.stops_to_lut`` linearly interpolates a list of color stops + into the same (256, 3) LUT shape consumed by ``_render_multi_channel``. + """ + + def test_shape_and_dtype(self): + lut = render.stops_to_lut([(0.0, 0, 0, 0), (1.0, 1.0, 1.0, 1.0)]) + assert lut.shape == (256, 3) + assert lut.dtype == np.float32 + + def test_endpoints_match_first_and_last_stop(self): + lut = render.stops_to_lut([(0.0, 0.1, 0.2, 0.3), (1.0, 0.7, 0.8, 0.9)]) + assert np.allclose(lut[0], [0.1, 0.2, 0.3]) + assert np.allclose(lut[-1], [0.7, 0.8, 0.9]) + + def test_two_stop_matches_linspace(self): + """A 2-stop gradient is equivalent to a per-channel linspace.""" + lut = render.stops_to_lut([(0.0, 0, 0, 0), (1.0, 1, 0, 0)]) + expected_r = np.linspace(0.0, 1.0, 256, dtype=np.float32) + assert np.allclose(lut[:, 0], expected_r) + assert (lut[:, 1] == 0).all() + assert (lut[:, 2] == 0).all() + + def test_three_stop_midpoint(self): + """Middle stop at position 0.5 is hit exactly at index 128 (≈0.502).""" + lut = render.stops_to_lut( + [(0.0, 0, 0, 0), (0.5, 1, 0, 0), (1.0, 1, 1, 1)] + ) + # At LUT index where x == 0.5 exactly (no such integer index for + # 256 samples over [0, 1] inclusive), the closest is index 128 + # (x = 128/255 ≈ 0.502) which is just past the middle stop. + # Channel R should already be at 1 at the middle stop. + # Channels G and B should still be very close to 0 at the middle + # stop and rising linearly toward 1 at the end. + mid = lut[128] + assert mid[0] == pytest.approx(1.0, abs=2 / 255) + assert mid[1] == pytest.approx(0.0, abs=2 / 255) + assert mid[2] == pytest.approx(0.0, abs=2 / 255) + # quarter-way past the middle stop -> halfway from red to white + three_quarter = lut[192] + assert three_quarter[0] == pytest.approx(1.0, abs=1e-3) + assert three_quarter[1] == pytest.approx(0.5, abs=2 / 255) + assert three_quarter[2] == pytest.approx(0.5, abs=2 / 255) + + def test_clamped_to_input_range(self): + """All LUT values stay within the RGB span of the input stops.""" + stops = [(0.0, 0.1, 0.0, 0.0), (1.0, 0.9, 0.0, 0.0)] + lut = render.stops_to_lut(stops) + assert lut[:, 0].min() >= 0.1 - 1e-6 + assert lut[:, 0].max() <= 0.9 + 1e-6 + + def test_monotonic_when_stops_are_monotonic(self): + """Strictly increasing color values produce a strictly non-decreasing + LUT column.""" + stops = [(0.0, 0.0, 0, 0), (0.5, 0.4, 0, 0), (1.0, 1.0, 0, 0)] + lut = render.stops_to_lut(stops) + assert (np.diff(lut[:, 0]) >= 0).all() + + def test_accepts_numpy_array(self): + stops = np.array( + [[0.0, 0, 0, 0], [1.0, 1.0, 1.0, 1.0]], dtype=np.float32 + ) + lut = render.stops_to_lut(stops) + assert lut.shape == (256, 3) + assert np.allclose(lut[-1], [1.0, 1.0, 1.0]) + + +# --------------------------------------------------------------------------- +# _render_multi_channel: LUT path +# --------------------------------------------------------------------------- + + +class TestRenderSceneLutPath: + """``_render_multi_channel`` accepts both legacy RGB triplets and the + new (256, 3) LUT shape. The two paths must agree on solid colors and + the LUT path must additionally handle non-solid colormaps.""" + + def test_accepts_lut_list(self, locs, info): + """Passing per-channel LUTs returns a valid QImage.""" + lut_red = render.solid_to_lut((1.0, 0.0, 0.0)) + lut_green = render.solid_to_lut((0.0, 1.0, 0.0)) + qimage, n_locs = render.render_scene( + [locs, locs], + [info, info], + disp_px_size=PIXELSIZE, + viewport=FULL_VIEWPORT, + colors=[lut_red, lut_green], + ) + assert isinstance(qimage, QtGui.QImage) + assert qimage.width() == 32 and qimage.height() == 32 + assert n_locs == 2 * len(locs) + + def test_lut_path_equivalent_to_triplets_for_solid_colors( + self, locs, info + ): + """For solid colors, a black->color LUT must produce the same + 8-bit output (within 1 LSB) as passing the RGB triplet directly.""" + triplets = [(1.0, 0.0, 0.0), (0.0, 0.5, 1.0)] + luts = [render.solid_to_lut(c) for c in triplets] + + qimage_triplet, _ = render.render_scene( + [locs, locs], + [info, info], + disp_px_size=PIXELSIZE, + viewport=FULL_VIEWPORT, + colors=triplets, + ) + qimage_lut, _ = render.render_scene( + [locs, locs], + [info, info], + disp_px_size=PIXELSIZE, + viewport=FULL_VIEWPORT, + colors=luts, + ) + diff = np.abs( + _qimage_to_array(qimage_triplet).astype(int) + - _qimage_to_array(qimage_lut).astype(int) + ) + # LUT path quantizes intensity*255 to 256 entries; expect ≤1 LSB diff. + assert diff.max() <= 1 + # ...and a strong majority of pixels exactly match. + assert (diff == 0).mean() > 0.7 + + def test_lut_path_isolates_pure_red(self, locs, info): + """A black->red LUT must leave G and B at zero in the output.""" + lut_red = render.solid_to_lut((1.0, 0.0, 0.0)) + qimage, _ = render.render_scene( + [locs], + [info], + disp_px_size=PIXELSIZE, + viewport=FULL_VIEWPORT, + colors=[lut_red], + ) + bgra = _qimage_to_array(qimage) + assert (bgra[..., 0] == 0).all() # B + assert (bgra[..., 1] == 0).all() # G + assert bgra[..., 2].max() > 0 # R lights up + + def test_lut_path_with_white_endpoint(self, locs, info): + """A black->color->white gradient (the picasso "built-in colormap" + shape) produces a saturated output at high intensity, with all + three channels rising above zero.""" + stops = [ + (0.0, 0.0, 0.0, 0.0), + (0.5, 0.0, 0.0, 1.0), # blue at midpoint + (1.0, 1.0, 1.0, 1.0), # white at peak + ] + lut = render.stops_to_lut(stops) + qimage, _ = render.render_scene( + [locs], + [info], + disp_px_size=PIXELSIZE, + viewport=FULL_VIEWPORT, + colors=[lut], + ) + bgra = _qimage_to_array(qimage) + # All three channels should have at least one non-zero pixel: + # high-intensity samples pull the gradient toward white. + assert bgra[..., 0].max() > 0 # B + assert bgra[..., 1].max() > 0 # G + assert bgra[..., 2].max() > 0 # R + + def test_lut_clipped_at_one(self, locs, info): + """When two saturated channels overlap, the per-pixel sum is + clipped to 1.0 → output bytes stay in [0, 255].""" + lut_red = render.solid_to_lut((1.0, 0.0, 0.0)) + lut_green = render.solid_to_lut((0.0, 1.0, 0.0)) + qimage, _ = render.render_scene( + [locs, locs], + [info, info], + disp_px_size=PIXELSIZE, + viewport=FULL_VIEWPORT, + colors=[lut_red, lut_green], + ) + bgra = _qimage_to_array(qimage) + # No overflow above uint8 range (trivially true by dtype, but the + # underlying float32 rgb is clipped to <= 1.0 before to_8bit). + assert bgra.dtype == np.uint8 + assert bgra[..., :3].max() <= 255 + + def test_lut_path_with_raw_image_cache(self, locs, info): + """LUT shape works with the raw_image_cache fast-redraw path.""" + # First call to populate the cache for two channels. + _, _, raw = render.render_scene( + [locs, locs], + [info, info], + disp_px_size=PIXELSIZE, + viewport=FULL_VIEWPORT, + colors=[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0)], + return_raw_image=True, + ) + lut_red = render.solid_to_lut((1.0, 0.0, 0.0)) + lut_green = render.solid_to_lut((0.0, 1.0, 0.0)) + qimage, n_locs = render.render_scene( + [locs, locs], + [info, info], + disp_px_size=PIXELSIZE, + viewport=FULL_VIEWPORT, + colors=[lut_red, lut_green], + raw_image_cache=raw, + ) + assert isinstance(qimage, QtGui.QImage) + assert n_locs == 0 # cached path doesn't re-count locs + + # --------------------------------------------------------------------------- # Localization splitting # --------------------------------------------------------------------------- From ba3b0c010f8e3b3ebb40ac1a5b1d454ea1bc3e10 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 25 May 2026 16:11:08 +0200 Subject: [PATCH 20/42] flake8 cleanup --- .github/workflows/main.yml | 4 +- changelog.md | 1 + distribution/create_linux_shortcuts.py | 22 ++- picasso/__main__.py | 99 ++++++------ picasso/average.py | 134 ++++++++++------ picasso/g5m.py | 48 ++++-- picasso/gui/filter.py | 70 ++++---- picasso/gui/localize.py | 4 +- picasso/gui/render.py | 10 +- picasso/gui/rotation.py | 149 +++++++++-------- picasso/gui/spinna.py | 4 +- picasso/lib.py | 2 +- picasso/localize.py | 143 +++++++++++------ picasso/postprocess.py | 54 ++++--- picasso/render.py | 212 ++++++++++++++----------- picasso/spinna.py | 139 +++++++++------- 16 files changed, 660 insertions(+), 435 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ed472b5b..e2289849 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,9 +37,9 @@ jobs: run: | pip install flake8 # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=tests,picasso/ext # exit-zero treats all errors as warnings. Line length and E203 match black configuration - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=79 --extend-ignore=E203 --statistics + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=79 --extend-ignore=E203 --statistics --exclude=tests,picasso/ext - name: Test with pytest run: | pip install pytest diff --git a/changelog.md b/changelog.md index 66c2bd10..7f3a271f 100644 --- a/changelog.md +++ b/changelog.md @@ -15,6 +15,7 @@ Last change: 25-MAY-2026 CEST - Batch analysis does not require area input (if found in metadata) - Batch analysis: clear instructions on what columns are required - SPINNA: area/volume button removed (deduced automatically from densities and number of molecules in the exp. data) +- Flake8 clean-up ## 0.10.0 diff --git a/distribution/create_linux_shortcuts.py b/distribution/create_linux_shortcuts.py index 96464a3d..ec94dd36 100755 --- a/distribution/create_linux_shortcuts.py +++ b/distribution/create_linux_shortcuts.py @@ -4,7 +4,7 @@ import os.path as op try: - import picasso + import picasso # noqa: F401 except ImportError: print( "This script must be run within an environment " @@ -14,15 +14,29 @@ raise SUBCMD = ( - "average", "design", "filter", "localize", "render", "simulate", "spinna", + "average", + "design", + "filter", + "localize", + "render", + "simulate", + "spinna", ) SCRIPT_PATH_ROOT = (os.sep, "usr", "bin", "picasso") DESKTOP_PATH_ROOT = ( - os.sep, "usr", "share", "applications", "picasso_{subcmd}.desktop", + os.sep, + "usr", + "share", + "applications", + "picasso_{subcmd}.desktop", ) SCRIPT_PATH_USER = ("~", "bin", "picasso") DESKTOP_PATH_USER = ( - "~", ".local", "share", "applications", "picasso_{subcmd}.desktop", + "~", + ".local", + "share", + "applications", + "picasso_{subcmd}.desktop", ) DESKTOP_TEMPLATE = """[Desktop Entry] diff --git a/picasso/__main__.py b/picasso/__main__.py index 20e76deb..d5acb26e 100644 --- a/picasso/__main__.py +++ b/picasso/__main__.py @@ -522,7 +522,7 @@ def _undrift_fiducials(files: str) -> None: detected. """ import glob - from . import io, postprocess, imageprocess + from . import io, postprocess segmentation = 2000 @@ -780,7 +780,6 @@ def _join(files: list[str], keep_index: bool = True) -> None: from .io import load_locs, save_locs from .lib import merge_locs from os.path import splitext - import pandas as pd all_locs = merge_locs( [load_locs(file)[0] for file in files], @@ -1261,6 +1260,53 @@ def _localize(args: argparse.Namespace) -> None: # noqa: C901 ) +def _render_many( + locs, + info, + path, + oversampling, + blur_method, + min_blur_width, + vmin, + vmax, + scaling, + cmap, + silent, +): + import sys + from os.path import splitext + from matplotlib.pyplot import imsave + from .render import render + + if sys.platform == "win32": + from os import startfile + + if blur_method == "none": + blur_method = None + N, image = render( + locs, + info, + oversampling, + blur_method=blur_method, + min_blur_width=min_blur_width, + ) + base, ext = splitext(path) + out_path = base + ".png" + im_max = image.max() / 100 + if scaling == "yes": + imsave( + out_path, + image, + vmin=vmin * im_max, + vmax=vmax * im_max, + cmap=cmap, + ) + else: + imsave(out_path, image, vmin=vmin, vmax=vmax, cmap=cmap) + if not silent and sys.platform == "win32": + startfile(out_path) + + def _render(args: argparse.Namespace) -> None: """Render localization files to images. @@ -1291,56 +1337,11 @@ def _render(args: argparse.Namespace) -> None: If True, the rendered images are not opened automatically. """ from .lib import locs_glob_map - from .render import render - from os.path import splitext - from matplotlib.pyplot import imsave - import sys - - if sys.platform == "win32": - from os import startfile from os.path import isdir from .io import load_user_settings, save_user_settings from tqdm import tqdm from glob import glob - def render_many( - locs, - info, - path, - oversampling, - blur_method, - min_blur_width, - vmin, - vmax, - scaling, - cmap, - silent, - ): - if blur_method == "none": - blur_method = None - N, image = render( - locs, - info, - oversampling, - blur_method=blur_method, - min_blur_width=min_blur_width, - ) - base, ext = splitext(path) - out_path = base + ".png" - im_max = image.max() / 100 - if scaling == "yes": - imsave( - out_path, - image, - vmin=vmin * im_max, - vmax=vmax * im_max, - cmap=cmap, - ) - else: - imsave(out_path, image, vmin=vmin, vmax=vmax, cmap=cmap) - if not silent and sys.platform == "win32": - startfile(out_path) - settings = load_user_settings() cmap = args.cmap if cmap is None: @@ -1358,7 +1359,7 @@ def render_many( for path in tqdm(paths): locs_glob_map( - render_many, + _render_many, path, args=( args.oversampling, @@ -1374,7 +1375,7 @@ def render_many( else: locs_glob_map( - render_many, + _render_many, args.files, args=( args.oversampling, diff --git a/picasso/average.py b/picasso/average.py index 0eda2a01..acb060c2 100644 --- a/picasso/average.py +++ b/picasso/average.py @@ -287,6 +287,70 @@ def prepare_locs_for_save( return locs, new_info +def _make_pbars(use_tqdm, iterations, n_groups): + if use_tqdm: + return ( + tqdm(total=iterations, desc="Averaging", unit="iter"), + tqdm(total=n_groups, desc="Groups", unit="group"), + ) + return None, None + + +def _finalize_average( + locs, + info, + aborted, + return_shifted_locs, + display_pixel_size, + iterations, +): + if aborted: + return None + if return_shifted_locs: + params = {"disp_px_size": display_pixel_size, "it": iterations} + locs, info = prepare_locs_for_save(locs, info, params) + return locs, info + return locs + + +def _wait_for_alignment( + result, + abort_callback, + counter, + n_groups, + use_tqdm, + group_pbar, + progress_callback, + it, + iterations, + locs, + x, + y, +): + last_count = 0 + while not result.ready(): + if callable(abort_callback) and abort_callback(): + return True + if use_tqdm: + current = int(counter.value) + group_pbar.update(current - last_count) + last_count = current + elif callable(progress_callback): + locs_current = locs.copy() + locs_current["x"] = np.ctypeslib.as_array(x) + locs_current["y"] = np.ctypeslib.as_array(y) + progress_callback( + it + 1, + iterations, + locs_current, + int(counter.value), + n_groups, + ) + if use_tqdm: + group_pbar.update(n_groups - last_count) + return False + + def average( locs: pd.DataFrame, info: list[dict], @@ -378,12 +442,7 @@ def average( ) use_tqdm = progress_callback == "console" - if use_tqdm: - iter_pbar = tqdm(total=iterations, desc="Averaging", unit="iter") - group_pbar = tqdm(total=n_groups, desc="Groups", unit="group") - else: - iter_pbar = None - group_pbar = None + iter_pbar, group_pbar = _make_pbars(use_tqdm, iterations, n_groups) aborted = False try: @@ -423,36 +482,22 @@ def average( result = pool.map_async(fc, range(n_groups), groups_per_worker) # Wait for completion and report progress - if use_tqdm: - last_count = 0 - while not result.ready(): - if callable(abort_callback) and abort_callback(): - aborted = True - break - current = int(counter.value) - group_pbar.update(current - last_count) - last_count = current - if aborted: - break - group_pbar.update(n_groups - last_count) - else: - while not result.ready(): - if callable(abort_callback) and abort_callback(): - aborted = True - break - if callable(progress_callback): - locs_current = locs.copy() - locs_current["x"] = np.ctypeslib.as_array(x) - locs_current["y"] = np.ctypeslib.as_array(y) - progress_callback( - it + 1, - iterations, - locs_current, - int(counter.value), - n_groups, - ) - if aborted: - break + aborted = _wait_for_alignment( + result, + abort_callback, + counter, + n_groups, + use_tqdm, + group_pbar, + progress_callback, + it, + iterations, + locs, + x, + y, + ) + if aborted: + break # Update localizations from shared arrays locs["x"] = np.ctypeslib.as_array(x) @@ -475,12 +520,11 @@ def average( pool.close() pool.join() - if aborted: - return None - - if return_shifted_locs: - params = {"disp_px_size": display_pixel_size, "it": iterations} - locs, info = prepare_locs_for_save(locs, info, params) - return locs, info - else: - return locs + return _finalize_average( + locs, + info, + aborted, + return_shifted_locs, + display_pixel_size, + iterations, + ) diff --git a/picasso/g5m.py b/picasso/g5m.py index f478ec1e..4c62d19e 100644 --- a/picasso/g5m.py +++ b/picasso/g5m.py @@ -1210,6 +1210,31 @@ def _e_step_3D( return np.mean(log_prob_norm), log_resp.astype(np.float64) +@njit +def _clip_covs_3D( + covs, + min_cov_x, + max_cov_x, + min_cov_y, + max_cov_y, + min_cov_z, + max_cov_z, +): + for i in range(len(covs)): + if covs[i, 0] < min_cov_x[i]: + covs[i, 0] = min_cov_x[i] + elif covs[i, 0] > max_cov_x[i]: + covs[i, 0] = max_cov_x[i] + if covs[i, 1] < min_cov_y[i]: + covs[i, 1] = min_cov_y[i] + elif covs[i, 1] > max_cov_y[i]: + covs[i, 1] = max_cov_y[i] + if covs[i, 2] < min_cov_z[i]: + covs[i, 2] = min_cov_z[i] + if covs[i, 2] > max_cov_z[i]: + covs[i, 2] = max_cov_z[i] + + @njit def _m_step_3D( X: lib.FloatArray2D, @@ -1280,20 +1305,15 @@ def _m_step_3D( min_cov_z = np.full(covs.shape[0], sigma_bounds[0] ** 2 * 2.0**2) max_cov_z = np.full(covs.shape[0], sigma_bounds[1] ** 2 * 2.5**2) - # apply the bounds to xy covariances - for i in range(len(covs)): - if covs[i, 0] < min_cov_x[i]: - covs[i, 0] = min_cov_x[i] - elif covs[i, 0] > max_cov_x[i]: - covs[i, 0] = max_cov_x[i] - if covs[i, 1] < min_cov_y[i]: - covs[i, 1] = min_cov_y[i] - elif covs[i, 1] > max_cov_y[i]: - covs[i, 1] = max_cov_y[i] - if covs[i, 2] < min_cov_z[i]: - covs[i, 2] = min_cov_z[i] - if covs[i, 2] > max_cov_z[i]: - covs[i, 2] = max_cov_z[i] + _clip_covs_3D( + covs, + min_cov_x, + max_cov_x, + min_cov_y, + max_cov_y, + min_cov_z, + max_cov_z, + ) # impose the ratio of x and y covariances based on the spot width # and height ratio diff --git a/picasso/gui/filter.py b/picasso/gui/filter.py index 26f503f2..995be372 100644 --- a/picasso/gui/filter.py +++ b/picasso/gui/filter.py @@ -819,15 +819,29 @@ def remove_columns(self) -> None: else: self.filter_log["Removed columns"] = to_remove - def apply_filters_from_metadata(self) -> None: - """Replay filter steps recorded in another file's .yaml metadata - onto the currently loaded localizations.""" - if self.locs is None: - QtWidgets.QMessageBox.information( - self, "Apply filters from metadata", "No file loaded." - ) - return + def _build_filter_summary(self, ranges, to_remove, missing) -> str: + lines = [] + if ranges: + lines.append("Filters to apply:") + for field, (xmin, xmax) in ranges.items(): + lines.append(f" {field}: [{xmin}, {xmax}]") + if to_remove: + if lines: + lines.append("") + lines.append("Columns to remove:") + for c in to_remove: + lines.append(f" {c}") + if missing: + if lines: + lines.append("") + lines.append("Not found in current data (will be skipped):") + for c in missing: + lines.append(f" {c}") + lines.append("") + lines.append("Apply these steps?") + return "\n".join(lines) + def _load_filter_metadata(self): directory = self.pwd if self.pwd else "" path, _ = QtWidgets.QFileDialog.getOpenFileName( self, @@ -836,11 +850,23 @@ def apply_filters_from_metadata(self) -> None: filter="*.yaml", ) if not path: - return - + return None try: - info = io.load_info(path, qt_parent=self) + return io.load_info(path, qt_parent=self) except io.NoMetadataFileError: + return None + + def apply_filters_from_metadata(self) -> None: + """Replay filter steps recorded in another file's .yaml metadata + onto the currently loaded localizations.""" + if self.locs is None: + QtWidgets.QMessageBox.information( + self, "Apply filters from metadata", "No file loaded." + ) + return + + info = self._load_filter_metadata() + if info is None: return ranges, to_remove, missing = lib.extract_filter_steps( @@ -859,30 +885,10 @@ def apply_filters_from_metadata(self) -> None: ) return - lines = [] - if ranges: - lines.append("Filters to apply:") - for field, (xmin, xmax) in ranges.items(): - lines.append(f" {field}: [{xmin}, {xmax}]") - if to_remove: - if lines: - lines.append("") - lines.append("Columns to remove:") - for c in to_remove: - lines.append(f" {c}") - if missing: - if lines: - lines.append("") - lines.append("Not found in current data (will be skipped):") - for c in missing: - lines.append(f" {c}") - lines.append("") - lines.append("Apply these steps?") - reply = QtWidgets.QMessageBox.question( self, "Apply filters from metadata", - "\n".join(lines), + self._build_filter_summary(ranges, to_remove, missing), QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.Cancel, ) diff --git a/picasso/gui/localize.py b/picasso/gui/localize.py index 6a4ee89f..33717b15 100644 --- a/picasso/gui/localize.py +++ b/picasso/gui/localize.py @@ -671,9 +671,9 @@ class ParametersDialog(lib.Dialog): CALIB_URL = "https://picassosr.readthedocs.io/en/latest/localize.html#d-calibration" # noqa: E501 IDENT_URL = "https://picassosr.readthedocs.io/en/latest/localize.html#identification-and-fitting-of-single-molecule-spots" # noqa: E501 - def __init__( + def __init__( # noqa: C901 self, parent: QtWidgets.QMainWindow | None = None - ) -> None: # noqa: C901 + ) -> None: super().__init__(parent) self.window = parent self.setWindowTitle("Parameters") diff --git a/picasso/gui/render.py b/picasso/gui/render.py index e503b09d..3e7bbbbe 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -3103,7 +3103,6 @@ def load_calibration(self) -> None: def load_calibration_(self, path: str) -> None: """Load calibration from the given path.""" # picasso.io.load_info takes in .hdf5 path - info_path = os.path.splitext(path)[0] + ".hdf5" calib = io.load_calibration(path) if "X Coefficients" not in calib.keys(): @@ -8326,12 +8325,13 @@ def export_grayscale(self, suffix: str, dpi: int = 96) -> None: locs_ = ( locs.drop(columns="group") if "group" in locs.columns else locs ) + invert_colors = self.window.dataset_dialog.wbackground.isChecked() qimage = render.render_scene( locs_, self.infos[i], **kwargs, contrast=(vmin, vmax), - invert_colors=self.window.dataset_dialog.wbackground.isChecked(), + invert_colors=invert_colors, single_channel_colormap="gray", relative_intensities=[ self.window.dataset_dialog.intensitysettings[i].value() @@ -10715,9 +10715,10 @@ def save_pick_properties(self, path: str, channel: int) -> None: kinetics_progress=kinetics_progress.set_value, groupprops_progress=groupprops_progress.set_value, ) + gen_by = f"Picasso v{__version__}: Render Pick Properties" info = self.infos[channel] + [ { - "Generated by": f"Picasso v{__version__}: Render Pick Properties", + "Generated by": gen_by, "Influx rate": influx, } ] @@ -11407,11 +11408,12 @@ def update_pick_info_long(self) -> None: progress = lib.ProgressDialog( "Calculating pick statistics", 0, len(self._picks), self ) + max_dark_time = self.window.info_dialog.max_dark_time.value() N, n_events, rmsd, rmsd_z, length, dark, new_locs = ( postprocess.evaluate_picks( picked_locs=self.picked_locs(channel), info=self.infos[channel], - max_dark_time=self.window.info_dialog.max_dark_time.value(), + max_dark_time=max_dark_time, progress_callback=progress.set_value, ) ) diff --git a/picasso/gui/rotation.py b/picasso/gui/rotation.py index 1ecc80ac..f7a59ce7 100644 --- a/picasso/gui/rotation.py +++ b/picasso/gui/rotation.py @@ -591,6 +591,7 @@ def build_animation(self) -> None: "Rendering frames", 0, n_frames, self.window ) adjust_display_pixel = disp_dlg.dynamic_disp_px.isChecked() + intensities = self.window.window.view.read_relative_intensities() render.build_animation( path, locs, @@ -610,7 +611,7 @@ def build_animation(self) -> None: invert_colors=data_dlg.wbackground.isChecked(), single_channel_colormap=disp_dlg.colormap.currentText(), colors=self.window.window.view.read_colors(), - relative_intensities=self.window.window.view.read_relative_intensities(), + relative_intensities=intensities, fps=self.fps.value(), adjust_pixel_size=adjust_display_pixel, progress_callback=progress.set_value, @@ -729,59 +730,43 @@ def sizeHint(self) -> QtCore.QSize: def resizeEvent(self, event: QtGui.QResizeEvent) -> None: self.update_scene() - def load_locs(self, update_window=False): - """Load localizations from a pick in the main window. + def _sync_from_main_window(self, w): + # get pixelsize + self.pixelsize = w.view.pixelsize + # update blur and colormap + b = w.display_settings_dlg.blur_buttongroup.checkedId() + color = w.display_settings_dlg.colormap.currentText() + self.window.display_settings_dlg.blur_buttongroup.button(b).setChecked( + True + ) + self.window.display_settings_dlg.colormap.setCurrentText(color) - Called when updating rotation window from there or when - shifting the pick from rotation window. + # remove measurement points + self._points = [] - Parameters - ---------- - update_window : bool, optional - If True, load attributes, such as blur method, from the - main window. - """ - fast_render = False # should locs be reindexed - w = self.window.window # main window - if update_window: - fast_render = True - # get pixelsize - self.pixelsize = w.view.pixelsize - # update blur and colormap - b = w.display_settings_dlg.blur_buttongroup.checkedId() - color = w.display_settings_dlg.colormap.currentText() - self.window.display_settings_dlg.blur_buttongroup.button( - b - ).setChecked(True) - self.window.display_settings_dlg.colormap.setCurrentText(color) - - # remove measurement points - self._points = [] - - # save the pick information - self.pick = w.view._picks[0] - self.pick_shape = w.view._pick_shape - self.pick_size = w.view._pick_size - - # update view, dataset_dialog for multichannel data and - # paths - self.viewport = self.fit_in_view_rotated(get_viewport=True) - self.window.dataset_dialog = w.dataset_dialog - self.paths = w.view.locs_paths - - # copy render property state from the main window - self.x_render_state = w.view.x_render_state - if self.x_render_state: - ds = w.display_settings_dlg - self.x_property = ds.parameter.currentText() - self.x_n_colors = ds.color_step.value() - self.x_min_val = ds.minimum_render.value() - self.x_max_val = ds.maximum_render.value() - self.x_colormap = ds.colormap_prop.currentText() - else: - self.x_locs = [] + # save the pick information + self.pick = w.view._picks[0] + self.pick_shape = w.view._pick_shape + self.pick_size = w.view._pick_size + + # update view, dataset_dialog for multichannel data and paths + self.viewport = self.fit_in_view_rotated(get_viewport=True) + self.window.dataset_dialog = w.dataset_dialog + self.paths = w.view.locs_paths - # load locs in the pick and their metadata + # copy render property state from the main window + self.x_render_state = w.view.x_render_state + if self.x_render_state: + ds = w.display_settings_dlg + self.x_property = ds.parameter.currentText() + self.x_n_colors = ds.color_step.value() + self.x_min_val = ds.minimum_render.value() + self.x_max_val = ds.maximum_render.value() + self.x_colormap = ds.colormap_prop.currentText() + else: + self.x_locs = [] + + def _collect_picked_locs(self, w, fast_render): n_channels = len(self.paths) self.locs = [] self.infos = [] @@ -798,37 +783,57 @@ def load_locs(self, update_window=False): drop=True ) temp["z"] /= self.pixelsize - # same for lpz if present if "lpz" in temp.columns: temp["lpz"] /= self.pixelsize self.locs.append(temp) self.infos.append(w.view.infos[i]) + def _apply_render_property_split(self): + if not (self.x_render_state and len(self.locs) == 1): + return + if self.x_property in self.locs[0].columns: + self.x_locs = render.split_locs_by_property( + self.locs[0], + property_name=self.x_property, + n_colors=self.x_n_colors, + min_value=self.x_min_val, + max_value=self.x_max_val, + ) + else: + self.x_render_state = False + self.x_locs = [] + + def load_locs(self, update_window=False): + """Load localizations from a pick in the main window. + + Called when updating rotation window from there or when + shifting the pick from rotation window. + + Parameters + ---------- + update_window : bool, optional + If True, load attributes, such as blur method, from the + main window. + """ + w = self.window.window # main window + fast_render = update_window + if update_window: + self._sync_from_main_window(w) + + self._collect_picked_locs(w, fast_render) + # shift z positions of locs so that the middle of the dataset is # at z = 0 all_locs_z = np.concatenate([_["z"].to_numpy() for _ in self.locs]) z_shift = all_locs_z.mean() - for i in range(n_channels): + for i in range(len(self.locs)): self.locs[i]["z"] -= z_shift - # assign self.group_color if single channel and group info - # present + # assign self.group_color if single channel and group info present if len(self.locs) == 1 and "group" in self.locs[0].columns: self.group_color = render.get_group_color(self.locs[0]) - # index locs by property for render property mode - if self.x_render_state and len(self.locs) == 1: - if self.x_property in self.locs[0].columns: - self.x_locs = render.split_locs_by_property( - self.locs[0], - property_name=self.x_property, - n_colors=self.x_n_colors, - min_value=self.x_min_val, - max_value=self.x_max_val, - ) - else: - self.x_render_state = False - self.x_locs = [] + self._apply_render_property_split() def render_scene( self, @@ -871,6 +876,7 @@ def render_scene( cmap = self.window.display_settings_dlg.colormap.currentText() contrast = None if autoscale else (vmin, vmax) raw_image = self.image if use_cache else None + intensities = self.window.window.view.read_relative_intensities() qimage, n_locs, (vmin, vmax), raw_image = render.render_scene( locs=locs, @@ -881,7 +887,7 @@ def render_scene( invert_colors=self.window.dataset_dialog.wbackground.isChecked(), single_channel_colormap=cmap, colors=self.window.window.view.read_colors(), - relative_intensities=self.window.window.view.read_relative_intensities(), + relative_intensities=intensities, raw_image_cache=raw_image, return_contrast_limits=True, return_raw_image=True, @@ -1708,6 +1714,9 @@ def _prepare_locs_for_rendering( # if multiple channels are loaded, selected only the ones which # are checked in the Dataset Dialog + render_check = ( + self.window.window.display_settings_dlg.render_check.isChecked() + ) if len(self.locs) > 1: locs_ = [] info_ = [] @@ -1720,7 +1729,7 @@ def _prepare_locs_for_rendering( elif ( len(self.locs) == 1 and "group" not in self.locs[0].columns - and not self.window.window.display_settings_dlg.render_check.isChecked() + and not render_check ): locs = locs[0] infos = infos[0] diff --git a/picasso/gui/spinna.py b/picasso/gui/spinna.py index 1e1edd09..97b535a0 100644 --- a/picasso/gui/spinna.py +++ b/picasso/gui/spinna.py @@ -2362,8 +2362,8 @@ def __init__(self, sim_tab: spinna.SimulationsTab) -> None: "Bayesian: use a Gaussian Process surrogate model to efficiently\n" " search the space with minimal evaluations.\n" r"Coarse to fine: first test 10% of selected search space, then" - "\n rerun SPINNA around the best fitting proportions from the first" - " round.\n" + "\n rerun SPINNA around the best fitting proportions from the " + "first round.\n" "Brute force: test all possible combinations of proportions of " "structures." ) diff --git a/picasso/lib.py b/picasso/lib.py index 26f12e11..c1385947 100644 --- a/picasso/lib.py +++ b/picasso/lib.py @@ -923,7 +923,7 @@ def get_from_metadata( def extract_filter_steps( info: list[dict], current_columns, -) -> tuple[dict[str, list[float]], list[str], list[str]]: +) -> tuple[dict[str, list[float]], list[str], list[str]]: # noqa: C901 """Parse filter steps out of a Picasso Filter metadata list. Iterates ``info`` oldest -> newest. A dict is treated as a filter diff --git a/picasso/localize.py b/picasso/localize.py index c3721d32..9bef4f67 100755 --- a/picasso/localize.py +++ b/picasso/localize.py @@ -558,6 +558,84 @@ def identify_async( return current, f +def _identify_threaded( + movie, + minimum_ng, + box, + roi, + frame_bounds, + progress_callback, + abort_callback, +): + """Run identify_async and drive its progress loop. + + Returns the identifications, or None if aborted. + """ + N = len(movie) + use_tqdm = progress_callback == "console" + iter_range = ( + tqdm(total=N, desc="Identifying spots", unit="frame") + if use_tqdm + else None + ) + current, futures = identify_async( + movie, minimum_ng, box, roi=roi, frame_bounds=frame_bounds + ) + last = 0 + while current[0] < N: + if abort_callback is not None and abort_callback(): + for f in futures: + f.cancel() + if use_tqdm: + iter_range.close() + return None + if use_tqdm: + iter_range.update(current[0] - last) + last = current[0] + elif callable(progress_callback): + progress_callback(current[0]) + time.sleep(0.2) + if use_tqdm: + iter_range.update(N - last) + iter_range.close() + return identifications_from_futures(futures) + + +def _identify_serial( + movie, + minimum_ng, + box, + roi, + frame_bounds, + progress_callback, +): + """Identify spots frame-by-frame in the current thread.""" + N = len(movie) + use_tqdm = progress_callback == "console" + iter_range = ( + tqdm(total=N, desc="Identifying spots", unit="frame") + if use_tqdm + else range(N) + ) + identifications = [] + for i in iter_range: + identifications.append( + identify_by_frame_number( + movie, + minimum_ng, + box, + i, + roi=roi, + frame_bounds=frame_bounds, + ) + ) + if callable(progress_callback): + progress_callback(i) + ids = pd.concat(identifications, ignore_index=True) + ids.sort_values(by="frame", kind="quicksort", inplace=True) + return ids + + def identify( movie: lib.IntArray3D, minimum_ng: float, @@ -637,53 +715,27 @@ def identify( "that picasso.localize.identify() will always return both " "the identifications and the metadata dictionary." ) - N = len(movie) - use_tqdm = progress_callback == "console" - if use_tqdm: - iter_range = tqdm(total=N, desc="Identifying spots", unit="frame") - else: - iter_range = range(N) if threaded: - current, futures = identify_async( - movie, minimum_ng, box, roi=roi, frame_bounds=frame_bounds + ids = _identify_threaded( + movie, + minimum_ng, + box, + roi, + frame_bounds, + progress_callback, + abort_callback, ) - last = 0 - while current[0] < N: - # abort if requested - if abort_callback is not None and abort_callback(): - for f in futures: - f.cancel() - if use_tqdm: - iter_range.close() - return - - if use_tqdm: - iter_range.update(current[0] - last) - last = current[0] - elif callable(progress_callback): - progress_callback(current[0]) - time.sleep(0.2) - if use_tqdm: - iter_range.update(N - last) - iter_range.close() - ids = identifications_from_futures(futures) + if ids is None: + return else: - identifications = [] - for i in iter_range: - identifications.append( - identify_by_frame_number( - movie, - minimum_ng, - box, - i, - roi=roi, - frame_bounds=frame_bounds, - ) - ) - if callable(progress_callback): - progress_callback(i) - ids = pd.concat(identifications, ignore_index=True) - ids.sort_values(by="frame", kind="quicksort", inplace=True) + ids = _identify_serial( + movie, + minimum_ng, + box, + roi, + frame_bounds, + progress_callback, + ) if return_info: info = { "Generated by": f"Picasso: v{__version__} Identify", @@ -1399,7 +1451,6 @@ def fit2D( ) camera_info["Pixelsize"] = 130 - N = len(identifications) spots = get_spots(movie, identifications, box, camera_info) em = camera_info["Gain"] > 1 if fitting_method == "gausslq": @@ -1797,7 +1848,7 @@ def localize_3D( the specified parameters. First runs 2D localizations, followed by z position fitting assuming astigmatism, see Huang, et al. Science, 2008. - + Parameters ---------- movie : lib.IntArray3D diff --git a/picasso/postprocess.py b/picasso/postprocess.py index 9b68581f..13a0e52c 100644 --- a/picasso/postprocess.py +++ b/picasso/postprocess.py @@ -1746,6 +1746,35 @@ def evaluate_picks( return N, n_events, rmsd, rmsd_z, length, dark, new_locs +def _pick_kinetics_single( + pick_locs: pd.DataFrame, + info: list[dict], + max_dark_time: int, +) -> tuple[pd.DataFrame, float, float] | None: + """Compute kinetics for a single picked region. Returns None if the + region has no usable data or kinetic rate estimation fails.""" + if not len(pick_locs): + return None + if "len" not in pick_locs.columns: + pick_locs = link( + pick_locs, + info, + r_max=999999, # link all locs in the pick + max_dark_time=max_dark_time, + ) + if not len(pick_locs): + return None + pick_locs = compute_dark_times(pick_locs) + if not len(pick_locs): + return None + try: + l_ = lib.estimate_kinetic_rate(pick_locs["len"].to_numpy()) + d_ = lib.estimate_kinetic_rate(pick_locs["dark"].to_numpy()) + except RuntimeError: + return None + return pick_locs, l_, d_ + + def pick_kinetics( picked_locs: list[pd.DataFrame], info: list[dict], @@ -1806,27 +1835,10 @@ def pick_kinetics( for i in iter_range: if callable(progress_callback): progress_callback(i) - - pick_locs = picked_locs[i] - if not len(pick_locs): - continue - if "len" not in pick_locs.columns: - pick_locs = link( - pick_locs, - info, - r_max=999999, # link all locs in the pick - max_dark_time=max_dark_time, - ) - if not len(pick_locs): - continue - pick_locs = compute_dark_times(pick_locs) - if not len(pick_locs): - continue - try: - l_ = lib.estimate_kinetic_rate(pick_locs["len"].to_numpy()) - d_ = lib.estimate_kinetic_rate(pick_locs["dark"].to_numpy()) - except RuntimeError: + result = _pick_kinetics_single(picked_locs[i], info, max_dark_time) + if result is None: continue + pick_locs, l_, d_ = result length.append(l_) dark.append(d_) no_locs.append(len(pick_locs)) @@ -3790,7 +3802,7 @@ def resi( suffix_centers : str, optional Suffix appended to output_paths for saved cluster centers from individual channels. Default is "_cluster_centers". - progress_callback : Callable[[int], None] | Literal["console"] | None, optional + progress_callback : {callable, "console", None}, optional Callback function to report progress where the input integer is the index of the channel currently processed. If "console", uses a simple console print. If None, no progress is reported. diff --git a/picasso/render.py b/picasso/render.py index ec14816f..08567dda 100755 --- a/picasso/render.py +++ b/picasso/render.py @@ -561,6 +561,55 @@ def _n_threads_for_buffers( return int(min(_render_threads(), max_by_budget, max(1, n_locs))) +@numba.njit(cache=True) +def _draw_gaussian_loc( + buf: lib.FloatArray2D, + x_: float, + y_: float, + sx_: float, + sy_: float, + n_pixel_x: int, + n_pixel_y: int, +) -> None: + """Render a single separable 2D Gaussian into ``buf``.""" + max_y_off = _DRAW_MAX_SIGMA * sy_ + i_min = np.int32(y_ - max_y_off) + if i_min < 0: + i_min = 0 + i_max = np.int32(y_ + max_y_off + 1) + if i_max > n_pixel_y: + i_max = n_pixel_y + max_x_off = _DRAW_MAX_SIGMA * sx_ + j_min = np.int32(x_ - max_x_off) + if j_min < 0: + j_min = 0 + j_max = np.int32(x_ + max_x_off) + 1 + if j_max > n_pixel_x: + j_max = n_pixel_x + nx = j_max - j_min + ny = i_max - i_min + if nx <= 0 or ny <= 0: + return + inv_2sx2 = 1.0 / (2.0 * sx_ * sx_) + inv_2sy2 = 1.0 / (2.0 * sy_ * sy_) + norm = 1.0 / (2.0 * np.pi * sx_ * sy_) + # Separable kernel: factor exp(-(dx^2/(2sx^2) + dy^2/(2sy^2))) + # into 1D gx * 1D gy. O(K) exp calls per loc instead of O(K^2). + gx = np.empty(nx, dtype=np.float32) + gy = np.empty(ny, dtype=np.float32) + for jj in range(nx): + dx = (j_min + jj) + 0.5 - x_ + gx[jj] = np.exp(-dx * dx * inv_2sx2) + for ii in range(ny): + dy = (i_min + ii) + 0.5 - y_ + gy[ii] = norm * np.exp(-dy * dy * inv_2sy2) + for ii in range(ny): + gy_i = gy[ii] + row = buf[i_min + ii] + for jj in range(nx): + row[j_min + jj] += gy_i * gx[jj] + + @numba.njit(parallel=True, cache=True) def _fill_gaussian_kernel( buffers: lib.FloatArray3D, @@ -584,46 +633,9 @@ def _fill_gaussian_kernel( end = n_locs buf = buffers[t] for k in range(start, end): - x_ = x[k] - y_ = y[k] - sx_ = sx[k] - sy_ = sy[k] - max_y_off = _DRAW_MAX_SIGMA * sy_ - i_min = np.int32(y_ - max_y_off) - if i_min < 0: - i_min = 0 - i_max = np.int32(y_ + max_y_off + 1) - if i_max > n_pixel_y: - i_max = n_pixel_y - max_x_off = _DRAW_MAX_SIGMA * sx_ - j_min = np.int32(x_ - max_x_off) - if j_min < 0: - j_min = 0 - j_max = np.int32(x_ + max_x_off) + 1 - if j_max > n_pixel_x: - j_max = n_pixel_x - nx = j_max - j_min - ny = i_max - i_min - if nx <= 0 or ny <= 0: - continue - inv_2sx2 = 1.0 / (2.0 * sx_ * sx_) - inv_2sy2 = 1.0 / (2.0 * sy_ * sy_) - norm = 1.0 / (2.0 * np.pi * sx_ * sy_) - # Separable kernel: factor exp(-(dx^2/(2sx^2) + dy^2/(2sy^2))) - # into 1D gx * 1D gy. O(K) exp calls per loc instead of O(K^2). - gx = np.empty(nx, dtype=np.float32) - gy = np.empty(ny, dtype=np.float32) - for jj in range(nx): - dx = (j_min + jj) + 0.5 - x_ - gx[jj] = np.exp(-dx * dx * inv_2sx2) - for ii in range(ny): - dy = (i_min + ii) + 0.5 - y_ - gy[ii] = norm * np.exp(-dy * dy * inv_2sy2) - for ii in range(ny): - gy_i = gy[ii] - row = buf[i_min + ii] - for jj in range(nx): - row[j_min + jj] += gy_i * gx[jj] + _draw_gaussian_loc( + buf, x[k], y[k], sx[k], sy[k], n_pixel_x, n_pixel_y + ) def _fill_gaussian( @@ -677,6 +689,60 @@ def _fill_gaussian( image += buffers.sum(axis=0) +@numba.njit(cache=True) +def _draw_gaussian_rot_loc( + buf: lib.FloatArray2D, + x_: float, + y_: float, + sx_: float, + sy_: float, + sz_: float, + n_pixel_x: int, + n_pixel_y: int, + rot_matrix: lib.Array3x3, + rot_matrixT: lib.Array3x3, +) -> None: + """Render a single rotated 2D Gaussian (projected from 3D) into + ``buf``.""" + cov = np.zeros((3, 3), dtype=np.float32) + cov[0, 0] = sx_ * sx_ + cov[1, 1] = sy_ * sy_ + cov[2, 2] = sz_ * sz_ + cov_rot = rot_matrix @ cov @ rot_matrixT + s00 = cov_rot[0, 0] + s01 = cov_rot[0, 1] + s10 = cov_rot[1, 0] + s11 = cov_rot[1, 1] + det2d = s00 * s11 - s01 * s10 + if det2d < 1e-10: + return + inv00 = s11 / det2d + inv01 = -s01 / det2d + inv10 = -s10 / det2d + inv11 = s00 / det2d + norm = 1.0 / (2.0 * np.pi * np.sqrt(det2d)) + max_x_off = _DRAW_MAX_SIGMA * np.sqrt(s00) + max_y_off = _DRAW_MAX_SIGMA * np.sqrt(s11) + j_min = int(x_ - max_x_off) + if j_min < 0: + j_min = 0 + j_max = int(x_ + max_x_off + 1) + if j_max > n_pixel_x: + j_max = n_pixel_x + i_min = int(y_ - max_y_off) + if i_min < 0: + i_min = 0 + i_max = int(y_ + max_y_off + 1) + if i_max > n_pixel_y: + i_max = n_pixel_y + for i in range(i_min, i_max): + b = np.float32(i + 0.5 - y_) + for j in range(j_min, j_max): + a = np.float32(j + 0.5 - x_) + exponent = a * a * inv00 + a * b * (inv01 + inv10) + b * b * inv11 + buf[i, j] += norm * np.exp(-0.5 * exponent) + + @numba.njit(parallel=True, cache=True) def _fill_gaussian_rot_kernel( buffers: lib.FloatArray3D, @@ -703,52 +769,18 @@ def _fill_gaussian_rot_kernel( end = n_locs buf = buffers[t] for k in range(start, end): - x_ = x[k] - y_ = y[k] - sx_ = sx[k] - sy_ = sy[k] - sz_ = sz[k] - cov = np.zeros((3, 3), dtype=np.float32) - cov[0, 0] = sx_ * sx_ - cov[1, 1] = sy_ * sy_ - cov[2, 2] = sz_ * sz_ - cov_rot = rot_matrix @ cov @ rot_matrixT - s00 = cov_rot[0, 0] - s01 = cov_rot[0, 1] - s10 = cov_rot[1, 0] - s11 = cov_rot[1, 1] - det2d = s00 * s11 - s01 * s10 - if det2d < 1e-10: - continue - inv00 = s11 / det2d - inv01 = -s01 / det2d - inv10 = -s10 / det2d - inv11 = s00 / det2d - norm = 1.0 / (2.0 * np.pi * np.sqrt(det2d)) - eff_sx = np.sqrt(s00) - eff_sy = np.sqrt(s11) - max_x_off = _DRAW_MAX_SIGMA * eff_sx - max_y_off = _DRAW_MAX_SIGMA * eff_sy - j_min = int(x_ - max_x_off) - if j_min < 0: - j_min = 0 - j_max = int(x_ + max_x_off + 1) - if j_max > n_pixel_x: - j_max = n_pixel_x - i_min = int(y_ - max_y_off) - if i_min < 0: - i_min = 0 - i_max = int(y_ + max_y_off + 1) - if i_max > n_pixel_y: - i_max = n_pixel_y - for i in range(i_min, i_max): - b = np.float32(i + 0.5 - y_) - for j in range(j_min, j_max): - a = np.float32(j + 0.5 - x_) - exponent = ( - a * a * inv00 + a * b * (inv01 + inv10) + b * b * inv11 - ) - buf[i, j] += norm * np.exp(-0.5 * exponent) + _draw_gaussian_rot_loc( + buf, + x[k], + y[k], + sx[k], + sy[k], + sz[k], + n_pixel_x, + n_pixel_y, + rot_matrix, + rot_matrixT, + ) def _fill_gaussian_rot( @@ -2856,7 +2888,7 @@ def render_scene( localizations and/or the contrast limits used for scaling to be returned together with the rendered QImage and number of localizations rendered. - + Parameters ---------- locs: pd.DataFrame or list of pd.DataFrame @@ -3457,7 +3489,7 @@ def build_animation( ) -> None: """Build an animation of rendered localizations given the checkpoints (angle, viewport, etc) and the time between them. - + Parameters ---------- path : str @@ -3480,7 +3512,7 @@ def build_animation( positions : list Each element determines the checkpoint of the animation, which is a tuple of 4 elements: (angle_x, angle_y, angle_z, viewport). - Angles are in radians. Viewport is given as ((y_min, x_min), + Angles are in radians. Viewport is given as ((y_min, x_min), (y_max, x_max)) in camera pixels. durations : list List of durations in seconds between the checkpoints. Must have @@ -3538,7 +3570,7 @@ def build_animation( ), "locs must be a pd.DataFrame or a list of pd.DataFrames." if isinstance(locs, list): assert all( - isinstance(l, pd.DataFrame) for l in locs + isinstance(locs_, pd.DataFrame) for locs_ in locs ), "All elements of locs must be pd.DataFrames." assert len(locs) >= 1, "locs must contain at least one DataFrame." assert ( diff --git a/picasso/spinna.py b/picasso/spinna.py index be49b7a8..c3976695 100644 --- a/picasso/spinna.py +++ b/picasso/spinna.py @@ -3515,40 +3515,20 @@ def fit_bayesian( ) # --- Phase 1: initial space-filling design --- - if isinstance(callback, lib.ProgressDialog): - callback.zero_progress(init_title) - callback.setMaximum(n_initial) - progress_bar = None - if callback == "console": - progress_bar = tqdm( - total=n_initial, - desc=init_title, - ) - - init_idx = self._farthest_point_sampling(proportions, n_initial) - eval_count = 0 - for idx in init_idx: - scores[idx] = self._evaluate_single(N_structures[idx]) - evaluated[idx] = True - eval_count += 1 - if callback == "console": - progress_bar.update(1) - elif callback is not None and callback != "console": - callback.set_value(eval_count) - - if callback == "console": - progress_bar.close() + self._bayesian_initial_phase( + proportions=proportions, + N_structures=N_structures, + evaluated=evaluated, + scores=scores, + n_initial=n_initial, + callback=callback, + title=init_title, + ) # --- Phase 2: GP-guided acquisition --- - if isinstance(callback, lib.ProgressDialog): - callback.zero_progress(gp_title) - callback.setMaximum(n_iterations) - if callback == "console": - progress_bar = tqdm( - total=n_iterations, - desc=gp_title, - ) - + progress_bar = self._init_gp_phase_progress( + n_iterations, callback, gp_title + ) evaluated, scores, _ = self._bayesian_gp_phase( proportions=proportions, N_structures=N_structures, @@ -3557,32 +3537,16 @@ def fit_bayesian( n_iterations=n_iterations, callback=callback, eval_count=0, - progress_bar=progress_bar if callback == "console" else None, + progress_bar=progress_bar, ) - - if callback == "console" and progress_bar is not None: - progress_bar.close() - elif isinstance(callback, lib.ProgressDialog): - callback.set_value(callback.maximum()) + self._finalize_progress(callback, progress_bar) # collect results for evaluated candidates only - eval_mask = evaluated - N_evaluated = N_structures[eval_mask] - scores_evaluated = scores[eval_mask] + N_evaluated = N_structures[evaluated] + scores_evaluated = scores[evaluated] if save: - props_eval = self.mixer.convert_counts_to_props(N_evaluated) - df = pd.DataFrame( - np.hstack( - (N_evaluated, props_eval, scores_evaluated.reshape(-1, 1)) - ), - columns=[ - f"N_{name}" for name in self.mixer.get_structure_names() - ] - + [f"Prop_{name}" for name in self.mixer.get_structure_names()] - + ["Kolmogorov-Smirnov statistic"], - ) - df.to_csv(save, header=True, index=False) + self._save_bayesian_csv(N_evaluated, scores_evaluated, save) # find best index = np.argmin(scores_evaluated) @@ -3600,6 +3564,75 @@ def fit_bayesian( ) return opt_proportions, score + def _bayesian_initial_phase( + self, + proportions: lib.FloatArray2D, + N_structures: lib.IntArray2D, + evaluated: np.ndarray, + scores: np.ndarray, + n_initial: int, + callback, + title: str, + ) -> None: + """Run Phase 1 of Bayesian optimisation: farthest-point sampling. + + Modifies ``evaluated`` and ``scores`` in place. + """ + if isinstance(callback, lib.ProgressDialog): + callback.zero_progress(title) + callback.setMaximum(n_initial) + progress_bar = None + if callback == "console": + progress_bar = tqdm(total=n_initial, desc=title) + + init_idx = self._farthest_point_sampling(proportions, n_initial) + for eval_count, idx in enumerate(init_idx, start=1): + scores[idx] = self._evaluate_single(N_structures[idx]) + evaluated[idx] = True + self._tick_progress(callback, progress_bar, eval_count) + + if progress_bar is not None: + progress_bar.close() + + def _init_gp_phase_progress(self, n_iterations, callback, title): + """Set up progress tracking for the GP-guided acquisition phase.""" + if isinstance(callback, lib.ProgressDialog): + callback.zero_progress(title) + callback.setMaximum(n_iterations) + if callback == "console": + return tqdm(total=n_iterations, desc=title) + return None + + @staticmethod + def _tick_progress(callback, progress_bar, eval_count): + """Advance whichever progress tracker is active.""" + if progress_bar is not None: + progress_bar.update(1) + elif callback is not None: + callback.set_value(eval_count) + + @staticmethod + def _finalize_progress(callback, progress_bar): + """Close the progress tracker once the GP phase finishes.""" + if progress_bar is not None: + progress_bar.close() + elif isinstance(callback, lib.ProgressDialog): + callback.set_value(callback.maximum()) + + def _save_bayesian_csv(self, N_evaluated, scores_evaluated, path): + """Write evaluated candidates and their scores to ``path``.""" + props_eval = self.mixer.convert_counts_to_props(N_evaluated) + names = self.mixer.get_structure_names() + df = pd.DataFrame( + np.hstack( + (N_evaluated, props_eval, scores_evaluated.reshape(-1, 1)) + ), + columns=[f"N_{name}" for name in names] + + [f"Prop_{name}" for name in names] + + ["Kolmogorov-Smirnov statistic"], + ) + df.to_csv(path, header=True, index=False) + def _bayesian_gp_phase( self, proportions: lib.FloatArray2D, From 0e8c856dc69fc6f54ed523adfb406363ebeb7769 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Mon, 25 May 2026 21:56:01 +0200 Subject: [PATCH 21/42] more efficient Filter --- changelog.md | 4 + picasso/gui/filter.py | 437 ++++++++++++++++++++++++++---------------- picasso/lib.py | 92 ++++++++- tests/test_lib.py | 55 ++++++ 4 files changed, 420 insertions(+), 168 deletions(-) diff --git a/changelog.md b/changelog.md index 7f3a271f..e244d476 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,9 @@ Last change: 25-MAY-2026 CEST ## 0.10.1 + +This patch adds a number of new, useful features rather than simply fixing the bugs found in 0.10.0. + - Faster rendering, especially in large FOV: ind. loc. precision blurring parallelized ., see [documentation](https://picassosr.readthedocs.io/en/latest/render.html/CPU-usage-on-shared-servers) + smarter implementation for 2D rendering; improvements for one-pixel-blur and global loc. prec. - Multi-level spatial indexing for quick zoomed-in rendering - Multichannel rendering supports colormaps, not only a single RGB color @@ -16,6 +19,7 @@ Last change: 25-MAY-2026 CEST - Batch analysis: clear instructions on what columns are required - SPINNA: area/volume button removed (deduced automatically from densities and number of molecules in the exp. data) - Flake8 clean-up +- Efficient Filter: much lower RAM usage + faster filtering by histogram selection (1D/2D), especially for very large datasets ## 0.10.0 diff --git a/picasso/gui/filter.py b/picasso/gui/filter.py index 995be372..8adedc87 100644 --- a/picasso/gui/filter.py +++ b/picasso/gui/filter.py @@ -35,45 +35,36 @@ class TableModel(QtCore.QAbstractTableModel): - """Class for handling the localization data. + """Qt model for the localization table view. - ... - - Attributes - ---------- - _column_count : int - Number of columns. - index : QtCore.QModelIndex - Row/column. - locs : pd.DataFrame - Localizations. - _row_count : int - Number of rows. + A single instance is reused for the lifetime of the main window — + the ``set_data`` method swaps in the small slice of rows currently + visible. Parameters ---------- - locs : pd.DataFrame - Localizations. - index : QtCore.QModelIndex - Row/column. parent : QtWidgets.QWidget, optional - Parent widget. Can be set to None. + Parent widget. """ def __init__( self, - locs: pd.DataFrame, - index: int, parent: QtWidgets.QWidget | None = None, ) -> None: super().__init__(parent) + self.locs = pd.DataFrame() + self.index = 0 + self._column_count = 0 + self._row_count = 0 + + def set_data(self, locs: pd.DataFrame, index: int) -> None: + """Replace the visible slice. Called on every scroll/refresh.""" + self.beginResetModel() self.locs = locs self.index = index - try: - self._column_count = len(locs.columns) - except IndexError: - self._column_count = 0 - self._row_count = self.locs.shape[0] + self._column_count = len(locs.columns) + self._row_count = locs.shape[0] + self.endResetModel() def columnCount(self, parent: QtCore.QModelIndex | None = None) -> int: return self._column_count @@ -161,14 +152,14 @@ def dropEvent(self, event: QtCore.QDropEvent) -> None: class PlotWindow(QtWidgets.QWidget): """Window for displaying 1D/2D histograms. - ... + Holds only a reference to the main window and the field name(s) it + plots. The localization data is pulled from the main window on + demand to avoid retaining per-window copies (as before v0.10.1). Attributes ---------- main_window : QtWidgets.QMainWindow Main window. - locs : pd.DataFrame - Localization data. figure : plt.Figure Matplotlib figure. @@ -176,18 +167,11 @@ class PlotWindow(QtWidgets.QWidget): ---------- main_window : QtWidgets.QMainWindow Main window. - locs : pd.DataFrame - Localization data. """ - def __init__( - self, - main_window: QtWidgets.QMainWindow, - locs: pd.DataFrame, - ) -> None: + def __init__(self, main_window: QtWidgets.QMainWindow) -> None: super().__init__() self.main_window = main_window - self.locs = locs self.figure = plt.Figure(constrained_layout=True) self.canvas = FigureCanvasQTAgg(self.figure) self.plot() @@ -202,8 +186,7 @@ def __init__( icon = QtGui.QIcon(icon_path) self.setWindowIcon(icon) - def update_locs(self, locs: pd.DataFrame) -> None: - self.locs = locs + def refresh(self) -> None: self.plot() self.update() @@ -214,8 +197,6 @@ def plot(self) -> None: class HistWindow(PlotWindow): """Window for displaying 1D histograms. - ... - Attributes ---------- field : str @@ -227,32 +208,32 @@ class HistWindow(PlotWindow): Field name for the histogram. main_window : QtWidgets.QMainWindow Main window. - locs : pd.DataFrame - Localization data. """ def __init__( self, main_window: QtWidgets.QMainWindow, - locs: pd.DataFrame, field: str, ) -> None: self.field = field - super().__init__(main_window, locs) + super().__init__(main_window) def plot(self) -> None: - # Prepare the data - data = self.locs[self.field] - data = data[np.isfinite(data)] - bins = lib.calculate_optimal_bins(data, 1000) - # Prepare the figure + data = self.main_window.get_column(self.field) + if data.dtype.kind == "f": + data = data[np.isfinite(data)] self.figure.clear() self.figure.suptitle(self.field) axes = self.figure.add_subplot(111) + if len(data) == 0: + self.canvas.draw() + return + bins = lib.calculate_optimal_bins(data, 1000) axes.hist(data, bins, rwidth=1, linewidth=0) - data_range = data.max() - data.min() + data_max = data.max() + data_range = data_max - data.min() axes.set_xlim( - [bins[0] - 0.05 * data_range, data.max() + 0.05 * data_range] + [bins[0] - 0.05 * data_range, data_max + 0.05 * data_range] ) self.span = SpanSelector( axes, @@ -264,14 +245,8 @@ def plot(self) -> None: self.canvas.draw() def on_span_select(self, xmin: float, xmax: float) -> None: - """Update the localization data based on the selected in the - histogram plot.""" - x = self.locs[self.field] - valid_idx = np.isfinite(x) & (x > xmin) & (x < xmax) - self.locs = self.locs[valid_idx] - self.main_window.update_locs(self.locs) - self.main_window.log_filter(self.field, xmin.item(), xmax.item()) - self.plot() + """Apply the selected range as a filter on the main window.""" + self.main_window.apply_range(self.field, float(xmin), float(xmax)) def closeEvent(self, event: QtGui.QCloseEvent) -> None: self.main_window.hist_windows[self.field] = None @@ -281,8 +256,6 @@ def closeEvent(self, event: QtGui.QCloseEvent) -> None: class Hist2DWindow(PlotWindow): """Window for displaying 2D histograms. - ... - Attributes ---------- field_x, field_y : str @@ -292,8 +265,6 @@ class Hist2DWindow(PlotWindow): ---------- main_window : QtWidgets.QMainWindow Main window. - locs : pd.DataFrame - Localization data. field_x : str Field name for the x-axis. field_y : str @@ -303,35 +274,61 @@ class Hist2DWindow(PlotWindow): def __init__( self, main_window: QtWidgets.QMainWindow, - locs: pd.DataFrame, field_x: str, field_y: str, ) -> None: self.field_x = field_x self.field_y = field_y - super().__init__(main_window, locs) + super().__init__(main_window) self.resize(1000, 800) def plot(self) -> None: - # Prepare the data - x = self.locs[self.field_x] - y = self.locs[self.field_y] - valid = np.isfinite(x) & np.isfinite(y) - x = x[valid] - y = y[valid] - # Prepare the figure + x, y = self.main_window.get_columns([self.field_x, self.field_y]) self.figure.clear() axes = self.figure.add_subplot(111) - # Start hist2 version + if len(x) == 0: + axes.get_xaxis().set_label_text(self.field_x) + axes.get_yaxis().set_label_text(self.field_y) + self.canvas.draw() + return bins_x = lib.calculate_optimal_bins(x, 1000) bins_y = lib.calculate_optimal_bins(y, 1000) - counts, x_edges, y_edges, image = axes.hist2d( - x, y, bins=[bins_x, bins_y], norm=LogNorm() + nx = len(bins_x) - 1 + ny = len(bins_y) - 1 + x_min, x_max = float(bins_x[0]), float(bins_x[-1]) + y_min, y_max = float(bins_y[0]), float(bins_y[-1]) + counts = lib.hist2d_numba( + np.ascontiguousarray(x), + np.ascontiguousarray(y), + x_min, + x_max, + y_min, + y_max, + nx, + ny, + ) + masked = np.ma.masked_equal(counts.T, 0) + image = axes.pcolormesh( + bins_x, bins_y, masked, norm=LogNorm(), shading="flat" + ) + if x.dtype.kind == "f": + x_data_max = float(np.nanmax(x)) + x_data_min = float(np.nanmin(x)) + y_data_max = float(np.nanmax(y)) + y_data_min = float(np.nanmin(y)) + else: + x_data_max = float(x.max()) + x_data_min = float(x.min()) + y_data_max = float(y.max()) + y_data_min = float(y.min()) + x_range = x_data_max - x_data_min + y_range = y_data_max - y_data_min + axes.set_xlim( + [bins_x[0] - 0.05 * x_range, x_data_max + 0.05 * x_range] + ) + axes.set_ylim( + [bins_y[0] - 0.05 * y_range, y_data_max + 0.05 * y_range] ) - x_range = x.max() - x.min() - axes.set_xlim([bins_x[0] - 0.05 * x_range, x.max() + 0.05 * x_range]) - y_range = y.max() - y.min() - axes.set_ylim([bins_y[0] - 0.05 * y_range, y.max() + 0.05 * y_range]) self.figure.colorbar(image, ax=axes) axes.grid(False) axes.get_xaxis().set_label_text(self.field_x) @@ -339,7 +336,7 @@ def plot(self) -> None: self.selector = RectangleSelector( axes, self.on_rect_select, - useblit=False, + useblit=True, props=dict(facecolor="green", alpha=0.2, fill=True), ) self.canvas.draw() @@ -349,25 +346,17 @@ def on_rect_select( press_event: QtGui.QMouseEvent, release_event: QtGui.QMouseEvent, ) -> None: - """Handle rectangle selection over the 2D histogram. Update - localizations.""" + """Apply the rectangular selection as a 2D filter on the main + window.""" x1, y1 = press_event.xdata, press_event.ydata x2, y2 = release_event.xdata, release_event.ydata - xmin = min(x1, x2) - xmax = max(x1, x2) - ymin = min(y1, y2) - ymax = max(y1, y2) - x = self.locs[self.field_x] - y = self.locs[self.field_y] - finite_idx = np.isfinite(x) & np.isfinite(y) - valid_idx = ( - (x > xmin) & (x < xmax) & (y > ymin) & (y < ymax) & finite_idx + xmin = float(min(x1, x2)) + xmax = float(max(x1, x2)) + ymin = float(min(y1, y2)) + ymax = float(max(y1, y2)) + self.main_window.apply_range2d( + self.field_x, xmin, xmax, self.field_y, ymin, ymax ) - self.locs = self.locs[valid_idx] - self.main_window.update_locs(self.locs) - self.main_window.log_filter(self.field_x, xmin.item(), xmax.item()) - self.main_window.log_filter(self.field_y, ymin.item(), ymax.item()) - self.plot() def closeEvent(self, event: QtGui.QCloseEvent) -> None: self.main_window.hist2d_windows[self.field_x][self.field_y] = None @@ -451,21 +440,17 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: def filter(self) -> None: """Filters locs given the range values.""" - # check that min value < max value xmin = self.min.value() xmax = self.max.value() if xmin < xmax: field = self.attributes.currentText() - locs = self.window.locs - locs = locs[(locs[field] >= xmin) & (locs[field] <= xmax)] - self.window.update_locs(locs) - self.window.log_filter(field, xmin, xmax) + self.window.apply_range(field, xmin, xmax, inclusive=True) def on_locs_loaded(self) -> None: """Changes attributes in the dialog according to locs.dtypes.""" while self.attributes.count(): self.attributes.removeItem(0) - names = self.window.locs.columns + names = self.window.locs_full.columns for name in names: self.attributes.addItem(name) @@ -537,7 +522,7 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: def plot(self) -> None: """Plot the subclustering test histogram. Optionally can save the histogram values to a .csv file.""" - if "n_events" not in self.window.locs.columns: + if "n_events" not in self.window.locs_full.columns: raise ValueError("Data must have the 'n_events' attribute.") if self.save_vals.isChecked(): base, ext = os.path.splitext(self.window.locs_path) @@ -555,7 +540,7 @@ def plot(self) -> None: dist_clustered = self.distance_clustered.value() dist_sparse = self.distance_sparse.value() - mols = self.window.locs + mols = self.window.materialize_filtered() clustered_nevents, sparse_nevents = clusterer.test_subclustering( mols, self.window.info, dist_clustered, dist_sparse ) @@ -582,22 +567,29 @@ def plot(self) -> None: class Window(QtWidgets.QMainWindow): """Main window for the application. - ... + The localization data is stored once in ``locs_full`` and never + copied. The currently-visible subset is tracked by ``filtered_idx``, + an integer index array into ``locs_full``. All filters re-index + this array, so memory cost is O(N) once (the master) plus O(M) + for the index (M = current filtered count), independent of how + many filter steps have been applied. Attributes ---------- filter_log : dict Dictionary of filter logs, i.e., data on what attribute/field - of ``self.locs`` was filtered and the corresponding min/max - values. + was filtered and the corresponding min/max values. filter_num : FilterNum Filter dialog for numeric values. hist_windows : dict Dictionary of histogram windows. hist2d_windows : dict Dictionary of 2D histogram windows. - locs : pd.DataFrame - Localizations loaded. + locs_full : pd.DataFrame + Master localizations table, set on load. + filtered_idx : np.ndarray + Integer indices into ``locs_full`` of the currently-visible + rows. pwd : str Current working directory. table_view : TableView @@ -616,6 +608,8 @@ def __init__(self) -> None: icon = QtGui.QIcon(icon_path) self.setWindowIcon(icon) self.table_view = TableView(self, self) + self.table_model = TableModel(self) + self.table_view.setModel(self.table_model) self.filter_num = FilterNum(self) self.metadata_dialog = lib.MetadataDialog(self) self.user_settings_dialog = lib.UserSettingsDialog(self) @@ -675,7 +669,8 @@ def __init__(self) -> None: self.hist_windows = {} self.hist2d_windows = {} self.filter_log = {} - self.locs = None + self.locs_full = None + self.filtered_idx = None # load user settings (working directory) settings = io.load_user_settings() @@ -691,9 +686,126 @@ def __init__(self) -> None: self.plugin_menu = menu_bar.addMenu("Plugins") # do not delete + @property + def locs(self) -> pd.DataFrame: + """Materialise the currently-filtered localizations. + + Provided for backward compatibility (e.g. plugins). This + allocates a fresh DataFrame on every access — prefer + ``get_column``, ``get_columns`` or ``materialize_filtered``. + """ + return self.materialize_filtered() + + @property + def n_filtered(self) -> int: + """Number of currently-visible rows.""" + if self.locs_full is None: + return 0 + if self.filtered_idx is None: + return len(self.locs_full) + return len(self.filtered_idx) + + def get_column(self, field: str) -> np.ndarray: + """Return the currently-filtered values of one column as a + numpy array. When no filter is applied this is a view into the + master DataFrame (no copy). + """ + vals = self.locs_full[field].values + return vals if self.filtered_idx is None else vals[self.filtered_idx] + + def get_columns( + self, fields: tuple[str] + ) -> tuple[lib.FloatArray1D | lib.IntArray1D]: + """Return the currently-filtered values of several columns as + numpy arrays.""" + if self.filtered_idx is None: + return tuple(self.locs_full[f].values for f in fields) + return tuple( + self.locs_full[f].values[self.filtered_idx] for f in fields + ) + + def materialize_filtered(self) -> pd.DataFrame: + """Build a DataFrame of the currently-filtered localizations.""" + if self.filtered_idx is None: + return self.locs_full.reset_index(drop=True) + return self.locs_full.iloc[self.filtered_idx].reset_index(drop=True) + + def _idx_dtype(self, n: int): + return np.uint32 if n <= np.iinfo(np.uint32).max else np.int64 + + def apply_range( + self, + field: str, + xmin: float, + xmax: float, + *, + inclusive: bool = False, + ) -> None: + """Filter the active index to rows with ``field`` in the given + range. Non-finite values are always dropped. + + ``inclusive=False`` matches the histogram-selection semantics + (strict ``>`` / ``<``); ``inclusive=True`` matches the numeric + filter dialog (``>=`` / ``<=``). + """ + col = self.get_column(field) + if inclusive: + keep = (col >= xmin) & (col <= xmax) + else: + keep = (col > xmin) & (col < xmax) + if col.dtype.kind == "f": + keep &= np.isfinite(col) + if self.filtered_idx is None: + self.filtered_idx = np.flatnonzero(keep).astype( + self._idx_dtype(len(self.locs_full)), copy=False + ) + else: + self.filtered_idx = self.filtered_idx[keep] + self.log_filter(field, xmin, xmax) + self.refresh() + + def apply_range2d( + self, + field_x: str, + xmin: float, + xmax: float, + field_y: str, + ymin: float, + ymax: float, + ) -> None: + """Apply a 2D rectangular filter in one pass.""" + cx, cy = self.get_columns([field_x, field_y]) + keep = (cx > xmin) & (cx < xmax) & (cy > ymin) & (cy < ymax) + if cx.dtype.kind == "f": + keep &= np.isfinite(cx) + if cy.dtype.kind == "f": + keep &= np.isfinite(cy) + if self.filtered_idx is None: + self.filtered_idx = np.flatnonzero(keep).astype( + self._idx_dtype(len(self.locs_full)), copy=False + ) + else: + self.filtered_idx = self.filtered_idx[keep] + self.log_filter(field_x, xmin, xmax) + self.log_filter(field_y, ymin, ymax) + self.refresh() + + def refresh(self) -> None: + """Refresh the table view and any open histogram windows.""" + n = self.n_filtered + self.vertical_scrollbar.setMaximum(max(0, n - 1)) + self.display_locs(self.vertical_scrollbar.value()) + for w in self.hist_windows.values(): + if w: + w.refresh() + for d in self.hist2d_windows.values(): + for w in d.values(): + if w: + w.refresh() + def show_metadata(self) -> None: """Open the metadata dialog.""" - if self.locs is None: + if self.locs_full is None: QtWidgets.QMessageBox.information( self, "Metadata", "No file loaded." ) @@ -721,22 +833,29 @@ def open(self, path: str) -> None: locs, self.info = io.load_filter(path, qt_parent=self) except io.NoMetadataFileError: return - if self.locs is not None: - for column in self.locs.columns: - if self.hist_windows[column]: + if self.locs_full is not None: + for column in self.locs_full.columns: + if self.hist_windows.get(column): self.hist_windows[column].close() - for column_y in self.locs.columns: - if self.hist2d_windows[column][column_y]: - self.hist_windows[column][column_y].close() + for column_y in self.locs_full.columns: + if self.hist2d_windows.get(column, {}).get(column_y): + self.hist2d_windows[column][column_y].close() self.locs_path = path - self.update_locs(locs) - for column in self.locs.columns: + self.locs_full = locs + # No filter applied yet — filtered_idx stays None so that we + # avoid allocating a 1:1 index of every row. + self.filtered_idx = None + self.filter_log = {} + self.hist_windows = {} + self.hist2d_windows = {} + for column in self.locs_full.columns: self.hist_windows[column] = None self.hist2d_windows[column] = {} - for column_y in self.locs.columns: + for column_y in self.locs_full.columns: self.hist2d_windows[column][column_y] = None self.filter_log[column] = None self.filter_num.on_locs_loaded() + self.refresh() self.setWindowTitle( f"Picasso v{__version__}: Filter. File: {os.path.basename(path)}" @@ -749,13 +868,9 @@ def plot_histogram(self) -> None: if len(indices) > 0: for index in indices: index = index.column() - field = self.locs.columns[index] + field = self.locs_full.columns[index] if not self.hist_windows[field]: - self.hist_windows[field] = HistWindow( - self, - self.locs, - field, - ) + self.hist_windows[field] = HistWindow(self, field) self.hist_windows[field].show() def plot_hist2d(self) -> None: @@ -763,10 +878,12 @@ def plot_hist2d(self) -> None: indices = selection_model.selectedColumns() if len(indices) == 2: indices = [index.column() for index in indices] - field_x, field_y = [self.locs.columns[index] for index in indices] + field_x, field_y = [ + self.locs_full.columns[index] for index in indices + ] if not self.hist2d_windows[field_x][field_y]: self.hist2d_windows[field_x][field_y] = Hist2DWindow( - self, self.locs, field_x, field_y + self, field_x, field_y ) self.hist2d_windows[field_x][field_y].show() @@ -774,28 +891,20 @@ def plot_subclustering(self) -> None: self.subcluster_num = SubclusterNum(self) self.subcluster_num.show() - def update_locs(self, locs: pd.DataFrame) -> None: - self.locs = locs - self.vertical_scrollbar.setMaximum(len(locs) - 1) - self.display_locs(self.vertical_scrollbar.value()) - for field, hist_window in self.hist_windows.items(): - if hist_window: - hist_window.update_locs(locs) - for field_x, hist2d_windows in self.hist2d_windows.items(): - for field_y, hist2d_window in hist2d_windows.items(): - if hist2d_window: - hist2d_window.update_locs(locs) - def display_locs(self, index: int) -> None: - if self.locs is not None: - view_height = self.table_view.viewport().height() - n_rows = int(view_height / ROW_HEIGHT) + 2 - table_model = TableModel( - self.locs[index : index + n_rows], - index, - self, - ) - self.table_view.setModel(table_model) + if self.locs_full is None: + return + view_height = self.table_view.viewport().height() + n_rows = int(view_height / ROW_HEIGHT) + 2 + end = min(index + n_rows, self.n_filtered) + if end <= index: + visible = self.locs_full.iloc[0:0] + elif self.filtered_idx is None: + visible = self.locs_full.iloc[index:end] + else: + rows_idx = self.filtered_idx[index:end] + visible = self.locs_full.iloc[rows_idx] + self.table_model.set_data(visible, index) def log_filter(self, field: str, xmin: float, xmax: float) -> None: if self.filter_log[field]: @@ -806,14 +915,14 @@ def log_filter(self, field: str, xmin: float, xmax: float) -> None: def remove_columns(self) -> None: """Remove columns from the loaded dataset.""" - if self.locs is None: + if self.locs_full is None: return - columns = self.locs.columns.to_list() + columns = self.locs_full.columns.to_list() to_remove, ok = lib.RemoveColumnsDialog.getParams(self, columns) if not ok or len(to_remove) == 0: return - self.locs.drop(columns=to_remove, inplace=True) - self.update_locs(self.locs) + self.locs_full.drop(columns=to_remove, inplace=True) + self.refresh() if "Removed columns" in self.filter_log: self.filter_log["Removed columns"].extend(to_remove) else: @@ -859,7 +968,7 @@ def _load_filter_metadata(self): def apply_filters_from_metadata(self) -> None: """Replay filter steps recorded in another file's .yaml metadata onto the currently loaded localizations.""" - if self.locs is None: + if self.locs_full is None: QtWidgets.QMessageBox.information( self, "Apply filters from metadata", "No file loaded." ) @@ -870,7 +979,7 @@ def apply_filters_from_metadata(self) -> None: return ranges, to_remove, missing = lib.extract_filter_steps( - info, self.locs.columns + info, self.locs_full.columns ) if not ranges and not to_remove: @@ -895,18 +1004,18 @@ def apply_filters_from_metadata(self) -> None: if reply != QtWidgets.QMessageBox.StandardButton.Yes: return - locs, _, _, _ = lib.apply_filter_steps(self.locs, info) for field, (xmin, xmax) in ranges.items(): - self.log_filter(field, xmin, xmax) + self.apply_range(field, xmin, xmax) if to_remove: + self.locs_full.drop(columns=list(to_remove), inplace=True) if "Removed columns" in self.filter_log: self.filter_log["Removed columns"].extend(to_remove) else: self.filter_log["Removed columns"] = list(to_remove) - self.update_locs(locs) + self.refresh() def export_csv_dialog(self) -> None: - if self.locs is None: + if self.locs_full is None: return base, ext = os.path.splitext(self.locs_path) out_path = base + ".csv" @@ -917,10 +1026,12 @@ def export_csv_dialog(self) -> None: filter="*.csv", ) if path: - self.locs.to_csv(path, index=False) + self.materialize_filtered().to_csv(path, index=False) def save_file_dialog(self) -> None: - if "x" in self.locs.columns: # Saving only for locs + if self.locs_full is None: + return + if "x" in self.locs_full.columns: # Saving only for locs base, ext = os.path.splitext(self.locs_path) out_path = base + "_filter.hdf5" path, exe = lib.get_save_filename_ext_dialog( @@ -936,7 +1047,7 @@ def save_file_dialog(self) -> None: {"Generated by": f"Picasso v{__version__} Filter"} ) info = self.info + [filter_info] - io.save_locs(path, self.locs, info) + io.save_locs(path, self.materialize_filtered(), info) else: raise NotImplementedError("Saving only implemented for locs.") @@ -951,7 +1062,7 @@ def resizeEvent(self, event: QtGui.QResizeEvent) -> None: def closeEvent(self, event: QtGui.QCloseEvent) -> None: settings = io.load_user_settings() - if self.locs is not None: + if self.locs_full is not None: settings["Filter"]["PWD"] = self.pwd io.save_user_settings(settings) QtWidgets.QApplication.instance().closeAllWindows() diff --git a/picasso/lib.py b/picasso/lib.py index c1385947..114cfe20 100644 --- a/picasso/lib.py +++ b/picasso/lib.py @@ -1540,6 +1540,7 @@ def unpack_calibration( def calculate_optimal_bins( data: FloatArray1D | IntArray1D, max_n_bins: int | None = None, + sample_size: int = 1_000_000, ) -> FloatArray1D: """Calculate the optimal bins for display, for example, in Picasso: Filter. @@ -1550,30 +1551,111 @@ def calculate_optimal_bins( Data to be binned. max_n_bins : int | None, optional Maximum number of bins. + sample_size : int, optional + For large arrays, estimate the IQR from a random sample of this + size instead of sorting the full array. min/max are still taken + from the full data (cheap O(N) reductions). Set to a value >= + ``len(data)`` to disable sampling. Default 1_000_000. Returns ------- bins : FloatArray1D Bins for display. """ - iqr = np.subtract(*np.percentile(data, [75, 25])) + n = len(data) + if n == 0: + return np.array([0.0, 1.0]) + if data.dtype.kind == "f": + data_min = np.nanmin(data) + data_max = np.nanmax(data) + else: + data_min = data.min() + data_max = data.max() + if n > sample_size: + rng = np.random.default_rng(0) + sample = data[rng.choice(n, sample_size, replace=False)] + else: + sample = data + if sample.dtype.kind == "f": + sample = sample[np.isfinite(sample)] + if len(sample) == 0: + return np.array([data_min - 1.0, data_max + 1.0]) + iqr = np.subtract(*np.percentile(sample, [75, 25])) if iqr == 0: return np.array([data[0] - 1.0, data[0] + 1.0]) - bin_size = 2 * iqr * len(data) ** (-1 / 3) + bin_size = 2 * iqr * n ** (-1 / 3) if data.dtype.kind in ("u", "i") and bin_size < 1: bin_size = 1 - bin_min = data.min() - bin_size / 2 + bin_min = data_min - bin_size / 2 try: - n_bins = (data.max() - bin_min) / bin_size + n_bins = (data_max - bin_min) / bin_size n_bins = int(n_bins) except Exception: n_bins = 10 if max_n_bins and n_bins > max_n_bins: n_bins = max_n_bins - bins = np.linspace(bin_min, data.max(), n_bins) + bins = np.linspace(bin_min, data_max, n_bins) return bins +@numba.njit(parallel=True, nogil=True) +def hist2d_numba( + x: FloatArray1D, + y: FloatArray1D, + x_min: float, + x_max: float, + y_min: float, + y_max: float, + nx: int, + ny: int, +) -> np.ndarray: + """Fast 2D histogram with uniform bin edges. + + Non-finite points are skipped. Edge values (== x_max / == y_max) are + folded into the last bin to match the inclusive-right behaviour of + ``np.histogram2d``. + + Parameters + ---------- + x, y : FloatArray1D + Sample coordinates. + x_min, x_max, y_min, y_max : float + Outer edges of the histogram. + nx, ny : int + Number of bins along each axis. + + Returns + ------- + counts : np.ndarray, shape (nx, ny), dtype int64 + Bin counts, indexed as counts[ix, iy]. + """ + n_threads = numba.get_num_threads() + local = np.zeros((n_threads, nx, ny), dtype=np.int64) + dx = (x_max - x_min) / nx + dy = (y_max - y_min) / ny + n = x.shape[0] + chunk = (n + n_threads - 1) // n_threads + for t in numba.prange(n_threads): + start = t * chunk + end = start + chunk + if end > n: + end = n + for i in range(start, end): + xi = x[i] + yi = y[i] + if not (np.isfinite(xi) and np.isfinite(yi)): + continue + ix = int((xi - x_min) / dx) + iy = int((yi - y_min) / dy) + if ix == nx: + ix -= 1 + if iy == ny: + iy -= 1 + if 0 <= ix < nx and 0 <= iy < ny: + local[t, ix, iy] += 1 + return local.sum(axis=0) + + def append_to_rec( rec_array: np.recarray, data: FloatArray1D | IntArray1D, diff --git a/tests/test_lib.py b/tests/test_lib.py index 000e3852..ca8744d2 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -214,6 +214,61 @@ def test_zero_iqr_returns_two_bins(self): # zero-iqr branch returns the constant ±1 fallback assert bins.size == 2 + def test_sampled_iqr_close_to_full(self): + rng = np.random.default_rng(42) + data = rng.normal(size=200_000) + full = lib.calculate_optimal_bins( + data, max_n_bins=1000, sample_size=len(data) + 1 + ) + sampled = lib.calculate_optimal_bins( + data, max_n_bins=1000, sample_size=20_000 + ) + # Same range, similar bin count (Freedman-Diaconis is stable + # under sub-sampling of an iid sample). + assert sampled[0] == pytest.approx(full[0], rel=0.05) + assert sampled[-1] == pytest.approx(full[-1], rel=0.05) + assert abs(sampled.size - full.size) <= max(2, full.size // 10) + + def test_handles_nan_data(self): + data = np.concatenate([np.full(10, np.nan), np.linspace(0, 1, 1000)]) + bins = lib.calculate_optimal_bins(data, max_n_bins=50) + # bin range is finite even though some values are NaN + assert np.isfinite(bins[0]) and np.isfinite(bins[-1]) + + +class TestHist2DNumba: + def test_matches_numpy_histogram2d(self): + rng = np.random.default_rng(7) + x = rng.normal(size=50_000) + y = rng.normal(size=50_000) + x_min, x_max = -3.0, 3.0 + y_min, y_max = -3.0, 3.0 + nx, ny = 40, 30 + # restrict to points strictly inside [x_min, x_max] x [y_min, y_max] + # to side-step floating-point boundary differences between the two + # implementations + inside = (x > x_min) & (x < x_max) & (y > y_min) & (y < y_max) + x = x[inside] + y = y[inside] + counts = lib.hist2d_numba(x, y, x_min, x_max, y_min, y_max, nx, ny) + x_edges = np.linspace(x_min, x_max, nx + 1) + y_edges = np.linspace(y_min, y_max, ny + 1) + expected, _, _ = np.histogram2d(x, y, bins=[x_edges, y_edges]) + assert counts.shape == (nx, ny) + assert counts.sum() == len(x) + # per-cell counts may differ by ~1 due to bin-edge rounding; total + # mismatch should be small relative to N + assert np.abs(counts - expected.astype(np.int64)).sum() < 0.001 * len( + x + ) + + def test_skips_non_finite(self): + x = np.array([0.0, 1.0, np.nan, 2.0, np.inf], dtype=np.float64) + y = np.array([0.0, 1.0, 1.0, np.nan, 2.0], dtype=np.float64) + counts = lib.hist2d_numba(x, y, 0.0, 3.0, 0.0, 3.0, 3, 3) + # only two points are fully finite and inside the range + assert counts.sum() == 2 + # --------------------------------------------------------------------------- # Recarray manipulation (deprecated path — explicit warnings expected) From db5db5d12834ebfe63f313bc862c441c58e2722a Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Tue, 26 May 2026 10:13:08 +0200 Subject: [PATCH 22/42] Fixed Gauss-fitting error when spot's sum is zero (zero division error) --- changelog.md | 3 ++- picasso/gausslq.py | 4 ++++ picasso/gaussmle.py | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index e244d476..561fe2ff 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -Last change: 25-MAY-2026 CEST +Last change: 26-MAY-2026 CEST ## 0.10.1 @@ -20,6 +20,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - SPINNA: area/volume button removed (deduced automatically from densities and number of molecules in the exp. data) - Flake8 clean-up - Efficient Filter: much lower RAM usage + faster filtering by histogram selection (1D/2D), especially for very large datasets +- Fixed Gauss-fitting error when spot's sum is zero (zero division error) ## 0.10.0 diff --git a/picasso/gausslq.py b/picasso/gausslq.py index 58edfb52..12f9735c 100644 --- a/picasso/gausslq.py +++ b/picasso/gausslq.py @@ -62,6 +62,10 @@ def _sum_and_center_of_mass( y += spot[i, j] * i x += spot[i, j] * j _sum_ += spot[i, j] + if _sum_ <= 0.0: + # Degenerate (flat) spot: fall back to geometric center so the + # caller can still produce sane initial parameters. + return 0.01, (size - 1) / 2.0, (size - 1) / 2.0 y /= _sum_ x /= _sum_ return _sum_, y, x diff --git a/picasso/gaussmle.py b/picasso/gaussmle.py index 7982cd7b..c1474e0e 100644 --- a/picasso/gaussmle.py +++ b/picasso/gaussmle.py @@ -39,6 +39,10 @@ def _sum_and_center_of_mass( y += spot[i, j] * i x += spot[i, j] * j _sum_ += spot[i, j] + if _sum_ <= 0.0: + # Degenerate (flat) spot: fall back to geometric center so the + # caller can still produce sane initial parameters. + return 0.01, (size - 1) / 2.0, (size - 1) / 2.0 y /= _sum_ x /= _sum_ return _sum_, y, x From e2cc782406d12978d2d7455ccb6feccb41eee07e Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Tue, 26 May 2026 11:13:17 +0200 Subject: [PATCH 23/42] adjust the spatial indexing ratio for smoother large FOV rendering --- picasso/spatial_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/picasso/spatial_index.py b/picasso/spatial_index.py index 61ae779f..0e72f6b2 100644 --- a/picasso/spatial_index.py +++ b/picasso/spatial_index.py @@ -39,7 +39,7 @@ # locs DataFrame and lets the renderer's vectorised ``in_view`` mask do # the filtering -- avoiding a pandas ``iloc`` copy of nearly all rows, # which dominates redraw cost at full-FOV (see ``query_viewport``). -_BYPASS_COVERAGE_RATIO = 0.25 +_BYPASS_COVERAGE_RATIO = 0.1 @dataclass From 7f6cecf86306664cfe3ef971a94d3c39419c0d1e Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Tue, 26 May 2026 12:44:24 +0200 Subject: [PATCH 24/42] remove parallel gaussian blur rendering --- changelog.md | 3 +- docs/render.rst | 27 ------ picasso/render.py | 226 +++++-------------------------------------- tests/test_render.py | 92 ------------------ 4 files changed, 23 insertions(+), 325 deletions(-) diff --git a/changelog.md b/changelog.md index 561fe2ff..39e1f7ac 100644 --- a/changelog.md +++ b/changelog.md @@ -6,8 +6,7 @@ Last change: 26-MAY-2026 CEST This patch adds a number of new, useful features rather than simply fixing the bugs found in 0.10.0. -- Faster rendering, especially in large FOV: ind. loc. precision blurring parallelized ., see [documentation](https://picassosr.readthedocs.io/en/latest/render.html/CPU-usage-on-shared-servers) + smarter implementation for 2D rendering; improvements for one-pixel-blur and global loc. prec. -- Multi-level spatial indexing for quick zoomed-in rendering +- Faster rendering through improvements for all blur methods and multi-level spatial indexing for quick zoomed-in rendering - Multichannel rendering supports colormaps, not only a single RGB color - Fixed linking saving `lpz` - Anisotripic DBSCAN (faster implementation) [DOI: 10.1021/acs.jpcb.4c02030](https://doi.org/10.1021/acs.jpcb.4c02030) diff --git a/docs/render.rst b/docs/render.rst index 76f7a9ef..85edc7a2 100644 --- a/docs/render.rst +++ b/docs/render.rst @@ -110,33 +110,6 @@ If the outcome of G5M seems unsatisfactory, please check the following: - Adjust min. locs; - Adjust DBSCAN (or other clustering algorithm) parameters. For example, if G5M takes too long to run, the DBSCAN clusters most likely contain too many molecules. In such a case, we recommend splitting such clusters further; -CPU usage on shared servers ---------------------------- - -Rendering with the ``gaussian`` and ``gaussian_iso`` blur methods (and the rotated variants used in the 3D rotation window) is parallelized across CPU cores. On a single workstation this is likely the desired behaviour, but on a shared compute server, most interactions with the canvas (zooming, rotation, changing display settings) would otherwise fan out to **all** available cores — so a handful of simultaneous users can saturate the machine and stall each other. - -To keep Picasso polite on shared hardware, the maximum number of threads used by the parallel render kernels is capped (default: 8). The cap is resolved on the first render call in this order; the first match wins: - -1. **User setting** ``Render -> CPU Threads`` in ``~/.picasso/settings.yaml``. Open ``View > User Settings`` in Picasso: Render to edit, or edit the YAML directly:: - - Render: - CPU Threads: 4 - -2. **Default**: ``min(8, available cores)``. - -The resolved value is clamped to ``[1, available cores]`` and cached for the lifetime of the process. From a script you can override it programmatically:: - - from picasso import render - render.set_render_threads(2) # cap at 2 threads - render.set_render_threads(None) # clear cache, re-read env var / settings - -Recommended values: - -- **Personal workstation (4-16 cores):** leave the default, or set to the core count. -- **Shared server with N concurrent users:** ``max(2, cores // N)`` is a reasonable starting point. For a 64-core box with ~25 simultaneous users, ``PICASSO_RENDER_THREADS=2`` keeps any single render from monopolizing the machine while still giving each user a 2x speedup over single-threaded rendering. - -Note: this only affects the parallel rendering kernels. Other Picasso modules (Localize, Average, postprocessing) use their own multiprocessing controls and are not influenced by this setting. - Dialogs ------- diff --git a/picasso/render.py b/picasso/render.py index 08567dda..22d1bc6e 100755 --- a/picasso/render.py +++ b/picasso/render.py @@ -33,61 +33,6 @@ N_GROUP_COLORS = 8 POLYGON_POINTER_SIZE = 16 # must be even -# Cap on Numba threads used by the parallel render kernels. Picasso is -# often deployed on shared servers where dozens of users render -# simultaneously, so we don't grab every core by default. Resolved -# lazily from PICASSO_RENDER_THREADS, then ``settings["Render"]["CPU -# Threads"]``, falling back to ``_DEFAULT_RENDER_THREADS``. -_DEFAULT_RENDER_THREADS = 8 -_render_threads_cached: int | None = None - - -def _render_threads() -> int: - """Return the max Numba thread count for parallel render kernels. - - Resolution order (first wins): - 1. env var ``PICASSO_RENDER_THREADS`` - 2. user setting ``Render -> CPU Threads`` (see io.load_user_settings) - 3. ``min(_DEFAULT_RENDER_THREADS, numba.get_num_threads())`` - - The value is clamped to ``[1, numba.get_num_threads()]`` and cached. - Call ``set_render_threads(None)`` to invalidate the cache (e.g. after - editing the settings file at runtime). - """ - global _render_threads_cached - if _render_threads_cached is not None: - return _render_threads_cached - - hw_max = max(1, numba.get_num_threads()) - n: int | None = None - try: - settings = io.load_user_settings() - raw = settings["Render"]["CPU Threads"] - if raw not in ("", None, {}): # AutoDict returns {} on miss - n = int(raw) - except (KeyError, TypeError, ValueError): - n = None - - if n is None: - n = min(_DEFAULT_RENDER_THREADS, hw_max) - - _render_threads_cached = max(1, min(int(n), hw_max)) - return _render_threads_cached - - -def set_render_threads(n: int | None) -> None: - """Set the max thread count for parallel render kernels. - - Pass ``None`` to clear the cache and force re-resolution from env - var / user settings on the next render. - """ - global _render_threads_cached - if n is None: - _render_threads_cached = None - return - hw_max = max(1, numba.get_num_threads()) - _render_threads_cached = max(1, min(int(n), hw_max)) - def render( locs: pd.DataFrame, @@ -544,26 +489,9 @@ def _fill3d( image[j, i, k] += 1 -_PER_THREAD_BUFFER_BUDGET_BYTES = 256 * 1024 * 1024 - - -def _n_threads_for_buffers( - n_pixel_y: int, n_pixel_x: int, itemsize: int, n_locs: int -) -> int: - """Decide how many threads to use for per-thread image accumulators. - - Bounded by the available Numba threads, a memory budget on the - per-thread image stack, and the number of localizations.""" - bytes_per_buffer = n_pixel_y * n_pixel_x * itemsize - if bytes_per_buffer <= 0: - return 1 - max_by_budget = max(1, _PER_THREAD_BUFFER_BUDGET_BYTES // bytes_per_buffer) - return int(min(_render_threads(), max_by_budget, max(1, n_locs))) - - @numba.njit(cache=True) def _draw_gaussian_loc( - buf: lib.FloatArray2D, + image: lib.FloatArray2D, x_: float, y_: float, sx_: float, @@ -571,7 +499,7 @@ def _draw_gaussian_loc( n_pixel_x: int, n_pixel_y: int, ) -> None: - """Render a single separable 2D Gaussian into ``buf``.""" + """Render a single separable 2D Gaussian into ``image``.""" max_y_off = _DRAW_MAX_SIGMA * sy_ i_min = np.int32(y_ - max_y_off) if i_min < 0: @@ -605,39 +533,12 @@ def _draw_gaussian_loc( gy[ii] = norm * np.exp(-dy * dy * inv_2sy2) for ii in range(ny): gy_i = gy[ii] - row = buf[i_min + ii] + row = image[i_min + ii] for jj in range(nx): row[j_min + jj] += gy_i * gx[jj] -@numba.njit(parallel=True, cache=True) -def _fill_gaussian_kernel( - buffers: lib.FloatArray3D, - x: lib.FloatArray1D, - y: lib.FloatArray1D, - sx: lib.FloatArray1D, - sy: lib.FloatArray1D, - n_pixel_x: int, - n_pixel_y: int, - n_threads: int, -) -> None: - """Parallel separable-Gaussian accumulator. Each prange iteration - processes a contiguous slice of localizations into its own image - buffer ``buffers[t]`` (race-free).""" - n_locs = len(x) - chunk = (n_locs + n_threads - 1) // n_threads - for t in numba.prange(n_threads): - start = t * chunk - end = start + chunk - if end > n_locs: - end = n_locs - buf = buffers[t] - for k in range(start, end): - _draw_gaussian_loc( - buf, x[k], y[k], sx[k], sy[k], n_pixel_x, n_pixel_y - ) - - +@numba.njit(cache=True) def _fill_gaussian( image: lib.FloatArray2D, x: lib.FloatArray1D, @@ -665,33 +566,16 @@ def _fill_gaussian( n_locs = len(x) if n_locs == 0: return - n_threads = _n_threads_for_buffers( - n_pixel_y, n_pixel_x, image.dtype.itemsize, n_locs - ) - if n_threads <= 1: - # Single-thread path: write directly into image to skip the - # buffer-stack allocation and reduction. - _fill_gaussian_kernel( - image.reshape(1, n_pixel_y, n_pixel_x), - x, - y, - sx, - sy, - n_pixel_x, - n_pixel_y, - 1, + + for i in range(n_locs): + _draw_gaussian_loc( + image, x[i], y[i], sx[i], sy[i], n_pixel_x, n_pixel_y ) - return - buffers = np.zeros((n_threads, n_pixel_y, n_pixel_x), dtype=image.dtype) - _fill_gaussian_kernel( - buffers, x, y, sx, sy, n_pixel_x, n_pixel_y, n_threads - ) - image += buffers.sum(axis=0) @numba.njit(cache=True) def _draw_gaussian_rot_loc( - buf: lib.FloatArray2D, + image: lib.FloatArray2D, x_: float, y_: float, sx_: float, @@ -703,7 +587,7 @@ def _draw_gaussian_rot_loc( rot_matrixT: lib.Array3x3, ) -> None: """Render a single rotated 2D Gaussian (projected from 3D) into - ``buf``.""" + ``image``.""" cov = np.zeros((3, 3), dtype=np.float32) cov[0, 0] = sx_ * sx_ cov[1, 1] = sy_ * sy_ @@ -740,54 +624,14 @@ def _draw_gaussian_rot_loc( for j in range(j_min, j_max): a = np.float32(j + 0.5 - x_) exponent = a * a * inv00 + a * b * (inv01 + inv10) + b * b * inv11 - buf[i, j] += norm * np.exp(-0.5 * exponent) - - -@numba.njit(parallel=True, cache=True) -def _fill_gaussian_rot_kernel( - buffers: lib.FloatArray3D, - x: lib.FloatArray1D, - y: lib.FloatArray1D, - z: lib.FloatArray1D, - sx: lib.FloatArray1D, - sy: lib.FloatArray1D, - sz: lib.FloatArray1D, - n_pixel_x: int, - n_pixel_y: int, - rot_matrix: lib.Array3x3, - rot_matrixT: lib.Array3x3, - n_threads: int, -) -> None: - """Parallel rotated-Gaussian accumulator. Per-thread image buffers - in ``buffers[t]`` are race-free across prange iterations.""" - n_locs = len(x) - chunk = (n_locs + n_threads - 1) // n_threads - for t in numba.prange(n_threads): - start = t * chunk - end = start + chunk - if end > n_locs: - end = n_locs - buf = buffers[t] - for k in range(start, end): - _draw_gaussian_rot_loc( - buf, - x[k], - y[k], - sx[k], - sy[k], - sz[k], - n_pixel_x, - n_pixel_y, - rot_matrix, - rot_matrixT, - ) + image[i, j] += norm * np.exp(-0.5 * exponent) +@numba.njit(cache=True) def _fill_gaussian_rot( image: lib.FloatArray2D, x: lib.FloatArray1D, y: lib.FloatArray1D, - z: lib.FloatArray1D, sx: lib.FloatArray1D, sy: lib.FloatArray1D, sz: lib.FloatArray1D, @@ -844,41 +688,19 @@ def _fill_gaussian_rot( rot_matrix = (rot_mat_x @ rot_mat_y @ rot_mat_z).astype(np.float32) rot_matrixT = np.ascontiguousarray(rot_matrix.T) - n_threads = _n_threads_for_buffers( - n_pixel_y, n_pixel_x, image.dtype.itemsize, n_locs - ) - if n_threads <= 1: - _fill_gaussian_rot_kernel( - image.reshape(1, n_pixel_y, n_pixel_x), - x, - y, - z, - sx, - sy, - sz, + for i in range(n_locs): + _draw_gaussian_rot_loc( + image, + x[i], + y[i], + sx[i], + sy[i], + sz[i], n_pixel_x, n_pixel_y, rot_matrix, rot_matrixT, - 1, ) - return - buffers = np.zeros((n_threads, n_pixel_y, n_pixel_x), dtype=image.dtype) - _fill_gaussian_rot_kernel( - buffers, - x, - y, - z, - sx, - sy, - sz, - n_pixel_x, - n_pixel_y, - rot_matrix, - rot_matrixT, - n_threads, - ) - image += buffers.sum(axis=0) @numba.njit @@ -1302,9 +1124,7 @@ def _render_gaussian( sx = blur_width[in_view] sz = blur_depth[in_view] - _fill_gaussian_rot( - image, x, y, z, sx, sy, sz, n_pixel_x, n_pixel_y, ang - ) + _fill_gaussian_rot(image, x, y, sx, sy, sz, n_pixel_x, n_pixel_y, ang) n = len(x) return n, image @@ -1401,9 +1221,7 @@ def _render_gaussian_iso( sx = sy sz = blur_depth[in_view] - _fill_gaussian_rot( - image, x, y, z, sx, sy, sz, n_pixel_x, n_pixel_y, ang - ) + _fill_gaussian_rot(image, x, y, sx, sy, sz, n_pixel_x, n_pixel_y, ang) return len(x), image diff --git a/tests/test_render.py b/tests/test_render.py index 8134bff4..6669c40f 100644 --- a/tests/test_render.py +++ b/tests/test_render.py @@ -7,7 +7,6 @@ import sys -import numba import numpy as np import pandas as pd import pytest @@ -82,14 +81,6 @@ def image(locs, info): return render.render(locs, info, oversampling=13)[1] -@pytest.fixture -def reset_render_threads(): - """Save and restore render's cached thread count around a test.""" - saved = render._render_threads_cached - yield - render._render_threads_cached = saved - - @pytest.fixture(scope="module") def small_qimage(): """Small black QImage used as a canvas for draw_* / export_* tests.""" @@ -265,89 +256,6 @@ def test_3d_rotation_changes_image(self, locs_3d, info): assert not np.array_equal(im_no_rot, im_rot) -# --------------------------------------------------------------------------- -# Render threading -# --------------------------------------------------------------------------- - - -class TestRenderThreads: - """set_render_threads / _render_threads resolution and equivalence.""" - - def test_set_value_caches(self, reset_render_threads): - hw_max = max(1, numba.get_num_threads()) - render.set_render_threads(2) - assert render._render_threads() == min(2, hw_max) - - def test_clamps_above_hw_max(self, reset_render_threads): - hw_max = max(1, numba.get_num_threads()) - render.set_render_threads(hw_max + 100) - assert render._render_threads() == hw_max - - def test_clamps_below_one(self, reset_render_threads): - render.set_render_threads(0) - assert render._render_threads() == 1 - - def test_none_clears_cache(self, reset_render_threads): - render.set_render_threads(3) - render.set_render_threads(None) - assert render._render_threads_cached is None - - @pytest.mark.parametrize("blur_method", ["gaussian", "gaussian_iso"]) - def test_thread_count_does_not_change_result( - self, locs, info, reset_render_threads, blur_method - ): - """Serial and parallel render paths must produce numerically - equivalent images (only float32 sum order differs).""" - hw_max = max(1, numba.get_num_threads()) - if hw_max < 2: - pytest.skip("requires >=2 numba threads") - render.set_render_threads(1) - _, im_serial = render.render( - locs, - info, - oversampling=5, - viewport=FULL_VIEWPORT, - blur_method=blur_method, - ) - render.set_render_threads(min(8, hw_max)) - _, im_parallel = render.render( - locs, - info, - oversampling=5, - viewport=FULL_VIEWPORT, - blur_method=blur_method, - ) - assert im_serial.shape == im_parallel.shape - assert np.allclose(im_serial, im_parallel, rtol=1e-4, atol=1e-4) - - def test_thread_count_does_not_change_result_gaussian_rot( - self, locs_3d, info, reset_render_threads - ): - """Same equivalence check for the rotated-Gaussian kernel.""" - hw_max = max(1, numba.get_num_threads()) - if hw_max < 2: - pytest.skip("requires >=2 numba threads") - render.set_render_threads(1) - _, im_serial = render.render( - locs_3d, - info, - oversampling=5, - viewport=FULL_VIEWPORT, - blur_method="gaussian", - ang=(0.1, 0.2, 0.3), - ) - render.set_render_threads(min(8, hw_max)) - _, im_parallel = render.render( - locs_3d, - info, - oversampling=5, - viewport=FULL_VIEWPORT, - blur_method="gaussian", - ang=(0.1, 0.2, 0.3), - ) - assert np.allclose(im_serial, im_parallel, rtol=1e-4, atol=1e-4) - - # --------------------------------------------------------------------------- # render_hist_numba # --------------------------------------------------------------------------- From ec0a3c38a5f710b8c0948b39fe3d30e0e4ee1cae Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Tue, 26 May 2026 13:16:28 +0200 Subject: [PATCH 25/42] Fixed NND plot reindexing after fitting (#665) --- changelog.md | 1 + picasso/gui/spinna.py | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 39e1f7ac..d4d2b1d8 100644 --- a/changelog.md +++ b/changelog.md @@ -20,6 +20,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - Flake8 clean-up - Efficient Filter: much lower RAM usage + faster filtering by histogram selection (1D/2D), especially for very large datasets - Fixed Gauss-fitting error when spot's sum is zero (zero division error) +- Fixed NND plot reindexing after fitting (#665) ## 0.10.0 diff --git a/picasso/gui/spinna.py b/picasso/gui/spinna.py index 97b535a0..abf80c47 100644 --- a/picasso/gui/spinna.py +++ b/picasso/gui/spinna.py @@ -4753,9 +4753,18 @@ def display_current_nnd_plot(self) -> None: if self.mixer is None else self.mixer ) - t1, t2, _ = mixer.get_neighbor_idx(duplicate=True)[ - self.current_nnd_idx - ] + neighbor_idx = mixer.get_neighbor_idx(duplicate=True) + # clamp index — after a fit the mixer may have fewer pairs than + # the previously-shown index + N = min( + len(neighbor_idx), + max(len(self.nnd_hist_data_exp), len(self.nnd_hist_data_sim)), + ) + if N == 0: + return + if self.current_nnd_idx >= N: + self.current_nnd_idx = 0 + t1, t2, _ = neighbor_idx[self.current_nnd_idx] # set the title (with ks2 score if available) title = f"{plot_params['title']}{t1} \u2192 {t2}" From bab01493c51e1d49530c44fe94505dcd601f44c7 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 27 May 2026 10:47:16 +0200 Subject: [PATCH 26/42] faster cluster centers + add lpz to cluster centers --- changelog.md | 3 +- picasso/clusterer.py | 271 +++++++++++++++++++++++++++++-------------- 2 files changed, 188 insertions(+), 86 deletions(-) diff --git a/changelog.md b/changelog.md index d4d2b1d8..1e2573c1 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,6 @@ # Changelog -Last change: 26-MAY-2026 CEST +Last change: 27-MAY-2026 CEST ## 0.10.1 @@ -21,6 +21,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - Efficient Filter: much lower RAM usage + faster filtering by histogram selection (1D/2D), especially for very large datasets - Fixed Gauss-fitting error when spot's sum is zero (zero division error) - Fixed NND plot reindexing after fitting (#665) +- Faster cluster centers calculation for SMLM clusterer, DBSCAN and HDBSCAN + `lpz` is saved if applicable ## 0.10.0 diff --git a/picasso/clusterer.py b/picasso/clusterer.py index 8e9dfe1f..48c9f452 100644 --- a/picasso/clusterer.py +++ b/picasso/clusterer.py @@ -688,13 +688,124 @@ def extract_valid_labels( return locs +def _aggregate_cluster_stats( + locs: pd.DataFrame, has_z: bool +) -> tuple[pd.core.groupby.DataFrameGroupBy, dict]: + """One vectorised pass for per-group means, stds and sizes. + + Returns the underlying ``groupby`` object (for downstream use such + as ``group_input.first()``) and a dict of plain NumPy arrays, one + per statistic, indexed positionally by sorted group id.""" + mean_cols = [ + "frame", + "x", + "y", + "photons", + "sx", + "sy", + "bg", + "net_gradient", + ] + std_cols = ["frame", "x", "y"] + if has_z: + mean_cols.append("z") + std_cols.append("z") + + gb = locs.groupby("group", sort=True) + means = gb[mean_cols].mean() + stds = gb[std_cols].std() + + stats = {f"{c}_mean": means[c].to_numpy() for c in mean_cols} + stats.update({f"{c}_std": stds[c].to_numpy() for c in std_cols}) + stats["n_locs"] = gb.size().to_numpy() + stats["unique_groups"] = means.index.to_numpy() + return gb, stats + + +def _count_binding_events( + group_arr: lib.IntArray1D, frame_arr: lib.IntArray1D +) -> tuple[lib.IntArray1D, lib.IntArray1D, lib.IntArray1D]: + """Number of binding events per cluster. + + A new event starts whenever consecutive frames within a cluster are + more than 3 frames apart. Vectorised across all groups with one + stable sort + one diff pass. + + Returns + ------- + n_events : lib.IntArray1D + One value per sorted unique group. + order : lib.IntArray1D + Stable argsort by group id; reused for the convex hull pass. + group_s : lib.IntArray1D + ``group_arr`` reindexed by ``order``. + """ + order = np.argsort(group_arr, kind="stable") + group_s = group_arr[order] + frame_s = frame_arr[order] + new_event = np.empty(len(frame_s), dtype=bool) + new_event[0] = True + new_event[1:] = (group_s[1:] != group_s[:-1]) | ( + (frame_s[1:] - frame_s[:-1]) > 3 + ) + n_events = ( + pd.Series(new_event).groupby(group_s, sort=True).sum().to_numpy() + ) + return n_events, order, group_s + + +def _cluster_convex_hulls( + locs: pd.DataFrame, + order: lib.IntArray1D, + group_s: lib.IntArray1D, + unique_groups: lib.IntArray1D, + has_z: bool, + pixelsize: float | None, +) -> lib.FloatArray1D: + """Convex-hull area (2D) or volume (3D) per cluster. + + The only per-cluster Python loop in ``find_cluster_centers``; runs + on raw NumPy slices of a group-sorted coordinate array. + """ + coord_cols = ["x", "y", "z"] if has_z else ["x", "y"] + coords_sorted = ( + locs[coord_cols].to_numpy()[order].astype(np.float64, copy=True) + ) + if has_z: + coords_sorted[:, 2] /= pixelsize + group_offsets = np.searchsorted(group_s, unique_groups, side="left") + group_offsets = np.append(group_offsets, len(group_s)) + convexhull = np.zeros(len(unique_groups), dtype=np.float64) + for i in range(len(unique_groups)): + X = coords_sorted[group_offsets[i] : group_offsets[i + 1]] + try: + convexhull[i] = ConvexHull(X).volume + except QhullError: + convexhull[i] = 0.0 + return convexhull + + +def _weighted_z_means( + locs: pd.DataFrame, group_arr: lib.IntArray1D +) -> lib.FloatArray1D: + """Per-cluster z mean weighted by 1/(lpx + lpy)^2 (per-row weights).""" + w = 1.0 / (locs["lpx"].to_numpy() + locs["lpy"].to_numpy()) ** 2 + wz = ( + pd.Series(locs["z"].to_numpy() * w).groupby(group_arr, sort=True).sum() + ) + ws = pd.Series(w).groupby(group_arr, sort=True).sum() + return (wz / ws).to_numpy() + + def find_cluster_centers( locs: pd.DataFrame, pixelsize: float | None = None, ) -> pd.DataFrame: """Calculate cluster centers. - Uses ``pandas.groupby`` to quickly run across all cluster ids. + Aggregations are computed in vectorised pandas/NumPy passes; the + only per-cluster Python loop is the convex hull, which operates on + raw NumPy slices. Parameters ---------- @@ -709,92 +820,78 @@ def find_cluster_centers( centers : pd.DataFrame Cluster centers saved in the format of localizations. """ - # group locs by their cluster id (group) - grouplocs = locs.groupby(locs["group"]) - - # get cluster centers - res = grouplocs.apply(_cluster_center, pixelsize, include_groups=False) - centers_ = res.values - - # convert to DataFrame and save - frame = np.array([_[0] for _ in centers_]) - std_frame = np.array([_[1] for _ in centers_]) - x = np.array([_[2] for _ in centers_]) - y = np.array([_[3] for _ in centers_]) - std_x = np.array([_[4] for _ in centers_]) - std_y = np.array([_[5] for _ in centers_]) - photons = np.array([_[6] for _ in centers_]) - sx = np.array([_[7] for _ in centers_]) - sy = np.array([_[8] for _ in centers_]) - bg = np.array([_[9] for _ in centers_]) - lpx = np.array([_[10] for _ in centers_]) - lpy = np.array([_[11] for _ in centers_]) - ellipticity = np.array([_[12] for _ in centers_]) - net_gradient = np.array([_[13] for _ in centers_]) - n = np.array([_[14] for _ in centers_]) - n_events = np.array([_[15] for _ in centers_]) # number of locs in cluster - - if "z" in locs.columns: - z = np.array([_[16] for _ in centers_]) - std_z = np.array([_[17] for _ in centers_]) - volume = np.array([_[18] for _ in centers_]) - convexhull = np.array([_[19] for _ in centers_]) - centers = pd.DataFrame( - { - "frame": frame.astype(np.float32), - "std_frame": std_frame.astype(np.float32), - "x": x.astype(np.float32), - "y": y.astype(np.float32), - "std_x": std_x.astype(np.float32), - "std_y": std_y.astype(np.float32), - "z": z.astype(np.float32), - "photons": photons.astype(np.float32), - "sx": sx.astype(np.float32), - "sy": sy.astype(np.float32), - "bg": bg.astype(np.float32), - "lpx": lpx.astype(np.float32), - "lpy": lpy.astype(np.float32), - "std_z": std_z.astype(np.float32), - "ellipticity": ellipticity.astype(np.float32), - "net_gradient": net_gradient.astype(np.float32), - "n_locs": n.astype(np.uint32), - "n_events": n_events.astype(np.int32), - "volume": volume.astype(np.float32), - "convexhull": convexhull.astype(np.float32), - "group": res.index.astype(np.int32), # group id - } + has_z = "z" in locs.columns + if has_z and pixelsize is None: + raise ValueError( + "Camera pixel size must be specified as an integer for 3D" + " cluster centers calculation." ) + + group_arr = locs["group"].to_numpy() + frame_arr = locs["frame"].to_numpy() + + gb, s = _aggregate_cluster_stats(locs, has_z) + + lpx = s["x_std"] / np.sqrt(s["n_locs"]) + lpy = s["y_std"] / np.sqrt(s["n_locs"]) + ellipticity = s["sx_mean"] / s["sy_mean"] + n_events, order, group_s = _count_binding_events(group_arr, frame_arr) + convexhull = _cluster_convex_hulls( + locs, order, group_s, s["unique_groups"], has_z, pixelsize + ) + + columns = { + "frame": s["frame_mean"].astype(np.float32), + "std_frame": s["frame_std"].astype(np.float32), + "x": s["x_mean"].astype(np.float32), + "y": s["y_mean"].astype(np.float32), + "std_x": s["x_std"].astype(np.float32), + "std_y": s["y_std"].astype(np.float32), + } + if has_z: + columns["z"] = _weighted_z_means(locs, group_arr).astype(np.float32) + columns.update( + { + "photons": s["photons_mean"].astype(np.float32), + "sx": s["sx_mean"].astype(np.float32), + "sy": s["sy_mean"].astype(np.float32), + "bg": s["bg_mean"].astype(np.float32), + "lpx": lpx.astype(np.float32), + "lpy": lpy.astype(np.float32), + } + ) + if has_z: + columns["lpz"] = (s["z_std"] / np.sqrt(s["n_locs"])).astype(np.float32) + columns["std_z"] = s["z_std"].astype(np.float32) + columns.update( + { + "ellipticity": ellipticity.astype(np.float32), + "net_gradient": s["net_gradient_mean"].astype(np.float32), + "n_locs": s["n_locs"].astype(np.uint32), + "n_events": n_events.astype(np.int32), + } + ) + if has_z: + volume = ( + np.power( + (s["x_std"] + s["y_std"] + s["z_std"] / pixelsize) / 3 * 2, 3 + ) + * 4.18879 + ) # assume radius = 2 * std_xyz + columns["volume"] = volume.astype(np.float32) else: - area = np.array([_[16] for _ in centers_]) - convexhull = np.array([_[17] for _ in centers_]) - centers = pd.DataFrame( - { - "frame": frame.astype(np.float32), - "std_frame": std_frame.astype(np.float32), - "x": x.astype(np.float32), - "y": y.astype(np.float32), - "std_x": std_x.astype(np.float32), - "std_y": std_y.astype(np.float32), - "photons": photons.astype(np.float32), - "sx": sx.astype(np.float32), - "sy": sy.astype(np.float32), - "bg": bg.astype(np.float32), - "lpx": lpx.astype(np.float32), - "lpy": lpy.astype(np.float32), - "ellipticity": ellipticity.astype(np.float32), - "net_gradient": net_gradient.astype(np.float32), - "n_locs": n.astype(np.uint32), - "n_events": n_events.astype(np.int32), - "area": area.astype(np.float32), - "convexhull": convexhull.astype(np.float32), - "group": res.index.astype(np.int32), # group id - } - ) + # assume radius = 2 * std_xy + area = np.power(s["x_std"] + s["y_std"], 2) * np.pi + columns["area"] = area.astype(np.float32) + columns["convexhull"] = convexhull.astype(np.float32) + columns["group"] = s["unique_groups"].astype(np.int32) + if "group_input" in locs.columns: - group_input = np.array([_[-1] for _ in centers_]) - centers["group_input"] = group_input.astype(np.int32) + columns["group_input"] = ( + gb["group_input"].first().to_numpy().astype(np.int32) + ) - return centers + return pd.DataFrame(columns) def cluster_center( @@ -806,7 +903,7 @@ def cluster_center( future release. Kept for backward compatibility.""" lib.deprecation_warning( "cluster_center is deprecated and will be removed in v0.11.0." - " Use _cluster_center instead." + " Use find_cluster_centers instead." ) return _cluster_center(grouplocs, pixelsize, separate_lp) @@ -815,7 +912,7 @@ def _cluster_center( grouplocs: pd.SeriesGroupBy, pixelsize: float | None = None, separate_lp: bool = False, -) -> list: +) -> list: # TODO: remove in v0.11.0 """Find cluster centers and their attributes, such as mean number of photons per localization, etc. @@ -839,6 +936,10 @@ def _cluster_center( Cluster center attributes. For each group, a list of values is returned: x, y, (z, optional), etc. """ + lib.deprecation_warning( + "_cluster_center is deprecated and will be removed in v0.11.0." + " Use find_cluster_centers instead." + ) # mean and std frame frame = grouplocs.frame.mean() std_frame = grouplocs.frame.std() From 56a8fc17a3746348610e3a815b2aefabda42dcb2 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 27 May 2026 12:14:39 +0200 Subject: [PATCH 27/42] Removed render property cache --- changelog.md | 1 + picasso/gui/render.py | 51 ++++++------------------------------------- 2 files changed, 8 insertions(+), 44 deletions(-) diff --git a/changelog.md b/changelog.md index 1e2573c1..00fc89d6 100644 --- a/changelog.md +++ b/changelog.md @@ -22,6 +22,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - Fixed Gauss-fitting error when spot's sum is zero (zero division error) - Fixed NND plot reindexing after fitting (#665) - Faster cluster centers calculation for SMLM clusterer, DBSCAN and HDBSCAN + `lpz` is saved if applicable +- Removed render property cache since it did not provide any significant speed improvement ## 0.10.0 diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 3e7bbbbe..8b45336c 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -6939,9 +6939,6 @@ class View(QtWidgets.QLabel): x_locs : list of pd.DataFrames Contains pd.DataFrames with locs to be rendered by property; one per color. - x_render_cache : list of dicts - Contains dictionaries with caches for storing info about locs - rendered by a property. x_render_state : bool Indicates if rendering by property is used. """ @@ -6976,7 +6973,6 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: self._drift = [] self._driftfiles = [] self.currentdrift = [] - self.x_render_cache = [] self.x_render_state = False def _load_locs(self, path: str) -> tuple[pd.DataFrame, list[dict]]: @@ -10294,7 +10290,6 @@ def render_scene( if cache: self.n_locs = n_locs self.image = raw_image - self.window.display_settings_dlg.silent_minimum_update(vmin) self.window.display_settings_dlg.silent_maximum_update(vmax) @@ -10787,45 +10782,13 @@ def activate_render_property(self) -> None: n_colors = self.window.display_settings_dlg.color_step.value() min_val = self.window.display_settings_dlg.minimum_render.value() max_val = self.window.display_settings_dlg.maximum_render.value() - - x_locs = [] - - # attempt using cached data - for cached_entry in self.x_render_cache: - if cached_entry["parameter"] == parameter: - if cached_entry["colors"] == n_colors: - if (cached_entry["min_val"] == min_val) & ( - cached_entry["max_val"] == max_val - ): - x_locs = cached_entry["locs"] - break - - # if no cached data found - if x_locs == []: - x_locs = render.split_locs_by_property( - locs=self._display_locs(0), - property_name=parameter, - n_colors=n_colors, - min_value=min_val, - max_value=max_val, - ) - - # cache - entry = {} - entry["parameter"] = parameter - entry["colors"] = n_colors - entry["locs"] = x_locs - entry["min_val"] = min_val - entry["max_val"] = max_val - - # Do not store too many datasets in cache - if len(self.x_render_cache) < 10: - self.x_render_cache.append(entry) - else: - self.x_render_cache.insert(0, entry) - del self.x_render_cache[-1] - - self.x_locs = x_locs + self.x_locs = render.split_locs_by_property( + locs=self._display_locs(0), + property_name=parameter, + n_colors=n_colors, + min_value=min_val, + max_value=max_val, + ) else: self.x_render_state = False self.update_scene() From f07492fbf7f84320b9ff66bf0f0e5745dd221e9b Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 27 May 2026 12:20:37 +0200 Subject: [PATCH 28/42] udpate readme + cleanup --- picasso/gui/render.py | 5 ++--- readme.rst | 11 ++--------- release/one_click_macos_gui/readme.rst | 1 - release/one_click_windows_gui/readme.rst | 1 - 4 files changed, 4 insertions(+), 14 deletions(-) diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 8b45336c..730bd5d4 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -10302,9 +10302,8 @@ def read_colors( rendering. Each returned entry is a ``(256, 3)`` float32 LUT. Solid colors - become black→color linear ramps (math-equivalent to the - original "intensity × rgb" multichannel blend). Matplotlib - colormaps and user-defined custom colormaps are also LUTs. + become black→color linear ramps. Matplotlib colormaps and + user-defined custom colormaps are also LUTs. If multiple channels are loaded, ensure that only the ones which are checked in the Dataset Dialog are rendered in their diff --git a/readme.rst b/readme.rst index 06661d35..f6697426 100644 --- a/readme.rst +++ b/readme.rst @@ -48,7 +48,7 @@ Installation Check out the `Picasso release page `__ to download and run the latest compiled one-click installer for Windows or MacOS (the latter is experimental and feedback is welcome). Here you will also find the Nature Protocols legacy version (v0.1.0). -Python is also distributed as a PyPI package that is platform-independent (``pip install picassosr``) which grants not only GUI but also access to Picasso’s internal routines in custom Python programs. For more details, see the "Via PyPI" section below. For examples of how to use Picasso in Python scripts, see the section "Example Usage" below. +Python is also distributed as a PyPI package that is platform-independent (``pip install picassosr``) which grants not only GUI but also access to Picasso’s internal routines in custom Python programs. For more details, see the `Via PyPI `__ section below. For examples of how to use Picasso in Python scripts, see the section `Example Usage `__ below. Note: Since v0.10.0 Picasso is more flexible in terms of dependencies and Python versions. Previously only Python 3.10 was supported, now newer versions are encouraged. @@ -59,6 +59,7 @@ Via PyPI 2. Activate the environment: ``conda activate picasso``. 3. Install Picasso package using: ``pip install picassosr``. 4. You can now run any Picasso function directly from the console/terminal by running: ``picasso render``, ``picasso localize``, etc, or import Picasso functions in your own Python scripts. +5. To update Picasso (you should get a notification about available updates since v0.10.0) run ``pip install --upgrade picassosr``. For Developers (local, editable installation) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,13 +75,6 @@ If you wish to use your local version of Picasso with your own modifications: 7. To create a *local* Picasso package to use it in other Python scripts, run ``pip install -e ".[dev]"``. When you change the code in the ``picasso`` directory, the changes will be reflected in the package. 8. You can now run any Picasso module directly from the console/terminal by running: ``picasso render``, ``picasso localize``, etc, or import Picasso functions in your own Python scripts. -Updating -^^^^^^^^ - -If Picasso was installed from PyPI (not the developer version), run the following command: - -``pip install --upgrade picassosr`` - Creating shortcuts on Windows (*optional*) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -118,7 +112,6 @@ Contributions & Copyright | Contributors: Joerg Schnitzbauer, Maximilian Strauss, Rafal Kowalewski, Adrian Przybylski, Andrey Aristov, Hiroshi Sasaki, Alexander Auer, Johanna Rahm | Copyright (c) 2015-2025 Jungmann Lab, Max Planck Institute of Biochemistry -| Copyright (c) 2020-2021 Maximilian Strauss .. SYNC-END: contributions diff --git a/release/one_click_macos_gui/readme.rst b/release/one_click_macos_gui/readme.rst index 441caf9f..51520bcc 100644 --- a/release/one_click_macos_gui/readme.rst +++ b/release/one_click_macos_gui/readme.rst @@ -37,7 +37,6 @@ Contributions & Copyright | Contributors: Joerg Schnitzbauer, Maximilian Strauss, Rafal Kowalewski, Adrian Przybylski, Andrey Aristov, Hiroshi Sasaki, Alexander Auer, Johanna Rahm | Copyright (c) 2015-2025 Jungmann Lab, Max Planck Institute of Biochemistry -| Copyright (c) 2020-2021 Maximilian Strauss .. SYNC-END: contributions diff --git a/release/one_click_windows_gui/readme.rst b/release/one_click_windows_gui/readme.rst index db6ee176..7dc5018d 100644 --- a/release/one_click_windows_gui/readme.rst +++ b/release/one_click_windows_gui/readme.rst @@ -42,7 +42,6 @@ Contributions & Copyright | Contributors: Joerg Schnitzbauer, Maximilian Strauss, Rafal Kowalewski, Adrian Przybylski, Andrey Aristov, Hiroshi Sasaki, Alexander Auer, Johanna Rahm | Copyright (c) 2015-2025 Jungmann Lab, Max Planck Institute of Biochemistry -| Copyright (c) 2020-2021 Maximilian Strauss .. SYNC-END: contributions From 9374fdf984bdf57a903abe8c007437c67ead60ef Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 27 May 2026 12:37:13 +0200 Subject: [PATCH 29/42] Installers are distributed with readme.txt files (previously .rst) --- changelog.md | 1 + readme.rst | 2 +- release/one_click_macos_gui/readme.rst | 91 ---------------------- release/one_click_macos_gui/readme.txt | 91 ++++++++++++++++++++++ release/one_click_windows_gui/readme.rst | 96 ------------------------ release/one_click_windows_gui/readme.txt | 96 ++++++++++++++++++++++++ release/sync_installer_readmes.py | 32 ++++++-- 7 files changed, 216 insertions(+), 193 deletions(-) delete mode 100644 release/one_click_macos_gui/readme.rst create mode 100644 release/one_click_macos_gui/readme.txt delete mode 100644 release/one_click_windows_gui/readme.rst create mode 100644 release/one_click_windows_gui/readme.txt diff --git a/changelog.md b/changelog.md index 00fc89d6..0010eb13 100644 --- a/changelog.md +++ b/changelog.md @@ -23,6 +23,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - Fixed NND plot reindexing after fitting (#665) - Faster cluster centers calculation for SMLM clusterer, DBSCAN and HDBSCAN + `lpz` is saved if applicable - Removed render property cache since it did not provide any significant speed improvement +- Installers are distributed with readme.txt files (previously .rst) ## 0.10.0 diff --git a/readme.rst b/readme.rst index f6697426..277c6000 100644 --- a/readme.rst +++ b/readme.rst @@ -133,7 +133,7 @@ If you use Picasso in your research, please cite our Nature Protocols publicatio - Theoretical lateral localization precision (Gauss LQ). DOI: `10.1038/nmeth.1447 `__ - Theoretical axial localization precision (Gauss LQ and MLE). DOI: `10.1038/s41467-026-70198-5 `__ - MLE fitting. DOI: `10.1038/nmeth.1449 `__ -- GPU fitting (LQ). DOI: `10.1038/s41598-017-15313-9 `__. License can be found `here `__/ +- GPU fitting (LQ). DOI: `10.1038/s41598-017-15313-9 `__. License can be found `here `__. - RCC undrifting: DOI: `10.1364/OE.22.015982 `__ - AIM undrifting. DOI: `10.1126/sciadv.adm776 `__ - SMLM clusterer. DOIs: `10.1038/s41467-021-22606-1 `__ and `10.1038/s41586-023-05925-9 `__ diff --git a/release/one_click_macos_gui/readme.rst b/release/one_click_macos_gui/readme.rst deleted file mode 100644 index 51520bcc..00000000 --- a/release/one_click_macos_gui/readme.rst +++ /dev/null @@ -1,91 +0,0 @@ -One-click installer for macOS -============================= - -This is the one-click installer for Picasso on macOS. Please visit our `Github repository `__ for details. - -The Picasso software is complemented by our `Nature Protocols publication `__. - -A comprehensive documentation can be found here: `Read the Docs `__. - -How to install --------------- - -1. Download the latest release from the `release page `__. -2. Open the downloaded dmg file and drag the "picasso" icon to your Applications folder first. -3. Repeat the same for all other icons with separate Picasso modules (Localize, Render, etc) that you wish to use. -4. **Note:** The main picasso app must remain in the Applications folder, the shortcuts for the other modules can be moved to the desktop or elsewhere if desired. - -Picasso is distributed without an Apple Developer ID, which means that macOS may block the installation of the software. If the steps above do not work, please follow the instructions under the `link `__. Alternatively, you can create the dmg file yourself by cloning our GitHub repo and running the bash script ``picasso/release/one_click_macos_gui/create_macos_dmg.sh`` from the Terminal. Note that you must have conda installed on your computer. - -Adding camera configuration and plugins ---------------------------------------- - -Camera configuration is essential for correct photon conversion and thus correct localization precision calculation. For more details, see `documentation `__. - -To add your config.yaml file, navigate to your Applications folder and right-click on the picasso app, then select "Show Package Contents". Add your config file to Contents/Frameworks/picasso. - -Similarly, you can add Picasso plugins under the folder Contents/Frameworks/picasso/gui/plugins. For more details on how to create plugins, see `documentation `__. - -Changelog ---------- -To see all changes introduced across releases, see `here `_. - -.. SYNC-START: contributions - -Contributions & Copyright -------------------------- - -| Contributors: Joerg Schnitzbauer, Maximilian Strauss, Rafal Kowalewski, Adrian Przybylski, Andrey Aristov, Hiroshi Sasaki, Alexander Auer, Johanna Rahm -| Copyright (c) 2015-2025 Jungmann Lab, Max Planck Institute of Biochemistry - -.. SYNC-END: contributions - -.. SYNC-START: citing - -Citing Picasso --------------- - -If you use Picasso in your research, please cite our Nature Protocols publication describing the software. - -| J. Schnitzbauer*, M.T. Strauss*, T. Schlichthaerle, F. Schueder, R. Jungmann -| Super-Resolution Microscopy with DNA-PAINT -| Nature Protocols (2017). 12: 1198-1228 DOI: `10.1038/nprot.2017.024 `__ -| -| If you use some of the functionalities provided by Picasso, please also cite the respective publications: - -- NeNA. DOI: `10.1007/s00418-014-1192-3 `__ -- FRC. DOI: `10.1038/nmeth.2448 `__ -- Theoretical lateral localization precision (Gauss LQ). DOI: `10.1038/nmeth.1447 `__ -- Theoretical axial localization precision (Gauss LQ and MLE). DOI: `10.1038/s41467-026-70198-5 `__ -- MLE fitting. DOI: `10.1038/nmeth.1449 `__ -- GPU fitting (LQ). DOI: `10.1038/s41598-017-15313-9 `__. License can be found `here `__/ -- RCC undrifting: DOI: `10.1364/OE.22.015982 `__ -- AIM undrifting. DOI: `10.1126/sciadv.adm776 `__ -- SMLM clusterer. DOIs: `10.1038/s41467-021-22606-1 `__ and `10.1038/s41586-023-05925-9 `__ -- DBSCAN: Ester, et al. Inkdd, 1996. (Vol. 96, No. 34, pp. 226-231). -- Anisotropic DBSCAN inspired by: `10.1021/acs.jpcb.4c02030 `__ -- HDBSCAN. DOI: `10.1007/978-3-642-37456-2_14 `__ -- RESI. DOI: `10.1038/s41586-023-05925-9 `__ -- Nanotron. DOI: `10.1093/bioinformatics/btaa154 `__ -- Picasso: Server. DOI: `10.1038/s42003-022-03909-5 `__ -- SPINNA. DOI: `10.1038/s41467-025-59500-z `__ -- SPINNA for LE fitting. DOI: `10.1038/s41592-024-02242-5 `__ -- G5M. DOI: `10.1038/s41467-026-70198-5 `__ - -.. SYNC-END: citing - -.. SYNC-START: credits - -Credits -------- - -- Design icon based on “Hexagon by Creative Stalls" from the Noun Project -- Simulate icon based on “Microchip by Futishia" from the Noun Project -- Localize icon based on “Mountains" by MONTANA RUCOBO from the Noun Project -- Filter icon based on “Funnel" by José Campos from the Noun Project -- Render icon based on “Paint Palette" by Vectors Market from the Noun Project -- Average icon based on “Layers" by Creative Stall from the Noun Project -- Server icon based on “Database" by Nimal Raj from the Noun Project -- SPINNA icon based on "Spinner" by Viktor Ostrovsky from the Noun Project - -.. SYNC-END: credits diff --git a/release/one_click_macos_gui/readme.txt b/release/one_click_macos_gui/readme.txt new file mode 100644 index 00000000..e7320c77 --- /dev/null +++ b/release/one_click_macos_gui/readme.txt @@ -0,0 +1,91 @@ +One-click installer for macOS +============================= + +This is the one-click installer for Picasso on macOS. Please visit our Github repository (https://github.com/jungmannlab/picasso) for details. + +The Picasso software is complemented by our Nature Protocols publication (https://www.nature.com/nprot/journal/v12/n6/abs/nprot.2017.024.html). + +A comprehensive documentation can be found here: https://picassosr.readthedocs.io/en/latest/ + +How to install +-------------- + +1. Download the latest release from the release page: https://github.com/jungmannlab/picasso/releases/ +2. Open the downloaded dmg file and drag the "picasso" icon to your Applications folder first. +3. Repeat the same for all other icons with separate Picasso modules (Localize, Render, etc) that you wish to use. +4. Note: The main picasso app must remain in the Applications folder, the shortcuts for the other modules can be moved to the desktop or elsewhere if desired. + +Picasso is distributed without an Apple Developer ID, which means that macOS may block the installation of the software. If the steps above do not work, please follow the instructions here: https://support.apple.com/en-gb/guide/mac-help/mh40616/mac. Alternatively, you can create the dmg file yourself by cloning our GitHub repo and running the bash script picasso/release/one_click_macos_gui/create_macos_dmg.sh from the Terminal. Note that you must have conda installed on your computer. + +Adding camera configuration and plugins +--------------------------------------- + +Camera configuration is essential for correct photon conversion and thus correct localization precision calculation. For more details, see documentation: https://picassosr.readthedocs.io/en/latest/localize.html#camera-config + +To add your config.yaml file, navigate to your Applications folder and right-click on the picasso app, then select "Show Package Contents". Add your config file to Contents/Frameworks/picasso. + +Similarly, you can add Picasso plugins under the folder Contents/Frameworks/picasso/gui/plugins. For more details on how to create plugins, see documentation: https://picassosr.readthedocs.io/en/latest/plugins.html + +Changelog +--------- +To see all changes introduced across releases, see: https://github.com/jungmannlab/picasso/blob/master/changelog.md + +.. SYNC-START: contributions + +Contributions & Copyright +------------------------- + +Contributors: Joerg Schnitzbauer, Maximilian Strauss, Rafal Kowalewski, Adrian Przybylski, Andrey Aristov, Hiroshi Sasaki, Alexander Auer, Johanna Rahm +Copyright (c) 2015-2025 Jungmann Lab, Max Planck Institute of Biochemistry + +.. SYNC-END: contributions + +.. SYNC-START: citing + +Citing Picasso +-------------- + +If you use Picasso in your research, please cite our Nature Protocols publication describing the software. + +J. Schnitzbauer*, M.T. Strauss*, T. Schlichthaerle, F. Schueder, R. Jungmann +Super-Resolution Microscopy with DNA-PAINT +Nature Protocols (2017). 12: 1198-1228 DOI: 10.1038/nprot.2017.024 (https://doi.org/10.1038/nprot.2017.024) + +If you use some of the functionalities provided by Picasso, please also cite the respective publications: + +- NeNA. DOI: 10.1007/s00418-014-1192-3 (https://doi.org/10.1007/s00418-014-1192-3) +- FRC. DOI: 10.1038/nmeth.2448 (https://doi.org/10.1038/nmeth.2448) +- Theoretical lateral localization precision (Gauss LQ). DOI: 10.1038/nmeth.1447 (https://doi.org/10.1038/nmeth.1447) +- Theoretical axial localization precision (Gauss LQ and MLE). DOI: 10.1038/s41467-026-70198-5 (https://doi.org/10.1038/s41467-026-70198-5) +- MLE fitting. DOI: 10.1038/nmeth.1449 (https://doi.org/10.1038/nmeth.1449) +- GPU fitting (LQ). DOI: 10.1038/s41598-017-15313-9 (https://doi.org/10.1038/s41598-017-15313-9). License can be found here (https://github.com/jungmannlab/picasso/tree/master/picasso/ext/pygpufit). +- RCC undrifting: DOI: 10.1364/OE.22.015982 (https://doi.org/10.1364/OE.22.015982) +- AIM undrifting. DOI: 10.1126/sciadv.adm776 (https://www.science.org/doi/10.1126/sciadv.adm7765) +- SMLM clusterer. DOIs: 10.1038/s41467-021-22606-1 (https://doi.org/10.1038/s41467-021-22606-1) and 10.1038/s41586-023-05925-9 (https://doi.org/10.1038/s41586-023-05925-9) +- DBSCAN: Ester, et al. Inkdd, 1996. (Vol. 96, No. 34, pp. 226-231). +- Anisotropic DBSCAN inspired by: 10.1021/acs.jpcb.4c02030 (https://doi.org/10.1021/acs.jpcb.4c02030) +- HDBSCAN. DOI: 10.1007/978-3-642-37456-2_14 (https://doi.org/10.1007/978-3-642-37456-2_14) +- RESI. DOI: 10.1038/s41586-023-05925-9 (https://doi.org/10.1038/s41586-023-05925-9) +- Nanotron. DOI: 10.1093/bioinformatics/btaa154 (https://doi.org/10.1093/bioinformatics/btaa154) +- Picasso: Server. DOI: 10.1038/s42003-022-03909-5 (https://doi.org/10.1038/s42003-022-03909-5) +- SPINNA. DOI: 10.1038/s41467-025-59500-z (https://doi.org/10.1038/s41467-025-59500-z) +- SPINNA for LE fitting. DOI: 10.1038/s41592-024-02242-5 (https://doi.org/10.1038/s41592-024-02242-5) +- G5M. DOI: 10.1038/s41467-026-70198-5 (https://doi.org/10.1038/s41467-026-70198-5) + +.. SYNC-END: citing + +.. SYNC-START: credits + +Credits +------- + +- Design icon based on “Hexagon by Creative Stalls" from the Noun Project +- Simulate icon based on “Microchip by Futishia" from the Noun Project +- Localize icon based on “Mountains" by MONTANA RUCOBO from the Noun Project +- Filter icon based on “Funnel" by José Campos from the Noun Project +- Render icon based on “Paint Palette" by Vectors Market from the Noun Project +- Average icon based on “Layers" by Creative Stall from the Noun Project +- Server icon based on “Database" by Nimal Raj from the Noun Project +- SPINNA icon based on "Spinner" by Viktor Ostrovsky from the Noun Project + +.. SYNC-END: credits diff --git a/release/one_click_windows_gui/readme.rst b/release/one_click_windows_gui/readme.rst deleted file mode 100644 index 7dc5018d..00000000 --- a/release/one_click_windows_gui/readme.rst +++ /dev/null @@ -1,96 +0,0 @@ -One-click installer for Windows -=============================== - -This is the one-click installer for Picasso on Windows. Please visit our `Github repository `__ for details. - -The Picasso software is complemented by our `Nature Protocols publication `__. - -A comprehensive documentation can be found here: `Read the Docs `__. - -How to install --------------- - -1. Download the latest release from the `release page `__. -2. Open the downloaded exe file and follow the installation instructions. - -⚠️ If installed in ``Program Files``, Render and Localize may not be available for non-administrator users. Therefore, we recommend installing Picasso outside of ``Program Files``. The current default location is ``C:\Picasso``. -⚠️ When using Windows installer, camera config file needs to be moved to ``C:\Picasso\_internal\picasso``. *Before v0.9.7 under* ``C:\Picasso\picasso``. -⚠️ Windows Safety features and Windows Defender may ask multiple times for permission during the installation and download. - -Creating your own installer ---------------------------- - -You can create the exe file yourself by cloning our GitHub repo and running the script ``picasso/release/one_click_windows_gui/create_installer_windows.bat`` from the Command Prompt. Note that you must have conda installed on your computer. - -Adding camera configuration and plugins ---------------------------------------- - -Camera configuration is essential for correct photon conversion and thus correct localization precision calculation. For more details, see `documentation `__. - -To add your config.yaml file, navigate to your Picasso folder (by default ``C:/Picasso``) and find the subdirectory ``_internal/picasso``. Add the config file there. - -Similarly, you can add Picasso plugins under the folder ``_internal/picasso/gui/plugins``. For more details on how to create plugins, see `documentation `__. - -Changelog ---------- -To see all changes introduced across releases, see `here `_. - -.. SYNC-START: contributions - -Contributions & Copyright -------------------------- - -| Contributors: Joerg Schnitzbauer, Maximilian Strauss, Rafal Kowalewski, Adrian Przybylski, Andrey Aristov, Hiroshi Sasaki, Alexander Auer, Johanna Rahm -| Copyright (c) 2015-2025 Jungmann Lab, Max Planck Institute of Biochemistry - -.. SYNC-END: contributions - -.. SYNC-START: citing - -Citing Picasso --------------- - -If you use Picasso in your research, please cite our Nature Protocols publication describing the software. - -| J. Schnitzbauer*, M.T. Strauss*, T. Schlichthaerle, F. Schueder, R. Jungmann -| Super-Resolution Microscopy with DNA-PAINT -| Nature Protocols (2017). 12: 1198-1228 DOI: `10.1038/nprot.2017.024 `__ -| -| If you use some of the functionalities provided by Picasso, please also cite the respective publications: - -- NeNA. DOI: `10.1007/s00418-014-1192-3 `__ -- FRC. DOI: `10.1038/nmeth.2448 `__ -- Theoretical lateral localization precision (Gauss LQ). DOI: `10.1038/nmeth.1447 `__ -- Theoretical axial localization precision (Gauss LQ and MLE). DOI: `10.1038/s41467-026-70198-5 `__ -- MLE fitting. DOI: `10.1038/nmeth.1449 `__ -- GPU fitting (LQ). DOI: `10.1038/s41598-017-15313-9 `__. License can be found `here `__/ -- RCC undrifting: DOI: `10.1364/OE.22.015982 `__ -- AIM undrifting. DOI: `10.1126/sciadv.adm776 `__ -- SMLM clusterer. DOIs: `10.1038/s41467-021-22606-1 `__ and `10.1038/s41586-023-05925-9 `__ -- DBSCAN: Ester, et al. Inkdd, 1996. (Vol. 96, No. 34, pp. 226-231). -- Anisotropic DBSCAN inspired by: `10.1021/acs.jpcb.4c02030 `__ -- HDBSCAN. DOI: `10.1007/978-3-642-37456-2_14 `__ -- RESI. DOI: `10.1038/s41586-023-05925-9 `__ -- Nanotron. DOI: `10.1093/bioinformatics/btaa154 `__ -- Picasso: Server. DOI: `10.1038/s42003-022-03909-5 `__ -- SPINNA. DOI: `10.1038/s41467-025-59500-z `__ -- SPINNA for LE fitting. DOI: `10.1038/s41592-024-02242-5 `__ -- G5M. DOI: `10.1038/s41467-026-70198-5 `__ - -.. SYNC-END: citing - -.. SYNC-START: credits - -Credits -------- - -- Design icon based on “Hexagon by Creative Stalls" from the Noun Project -- Simulate icon based on “Microchip by Futishia" from the Noun Project -- Localize icon based on “Mountains" by MONTANA RUCOBO from the Noun Project -- Filter icon based on “Funnel" by José Campos from the Noun Project -- Render icon based on “Paint Palette" by Vectors Market from the Noun Project -- Average icon based on “Layers" by Creative Stall from the Noun Project -- Server icon based on “Database" by Nimal Raj from the Noun Project -- SPINNA icon based on "Spinner" by Viktor Ostrovsky from the Noun Project - -.. SYNC-END: credits diff --git a/release/one_click_windows_gui/readme.txt b/release/one_click_windows_gui/readme.txt new file mode 100644 index 00000000..f00160bc --- /dev/null +++ b/release/one_click_windows_gui/readme.txt @@ -0,0 +1,96 @@ +One-click installer for Windows +=============================== + +This is the one-click installer for Picasso on Windows. Please visit our Github repository (https://github.com/jungmannlab/picasso) for details. + +The Picasso software is complemented by our Nature Protocols publication (https://www.nature.com/nprot/journal/v12/n6/abs/nprot.2017.024.html). + +A comprehensive documentation can be found here: https://picassosr.readthedocs.io/en/latest/ + +How to install +-------------- + +1. Download the latest release from the release page: https://github.com/jungmannlab/picasso/releases/ +2. Open the downloaded exe file and follow the installation instructions. + +[!] If installed in "Program Files", Render and Localize may not be available for non-administrator users. Therefore, we recommend installing Picasso outside of "Program Files". The current default location is C:\Picasso. +[!] When using Windows installer, camera config file needs to be moved to C:\Picasso\_internal\picasso. Before v0.9.7 under C:\Picasso\picasso. +[!] Windows Safety features and Windows Defender may ask multiple times for permission during the installation and download. + +Creating your own installer +--------------------------- + +You can create the exe file yourself by cloning our GitHub repo and running the script picasso/release/one_click_windows_gui/create_installer_windows.bat from the Command Prompt. Note that you must have conda installed on your computer. + +Adding camera configuration and plugins +--------------------------------------- + +Camera configuration is essential for correct photon conversion and thus correct localization precision calculation. For more details, see documentation: https://picassosr.readthedocs.io/en/latest/localize.html#camera-config + +To add your config.yaml file, navigate to your Picasso folder (by default C:/Picasso) and find the subdirectory _internal/picasso. Add the config file there. + +Similarly, you can add Picasso plugins under the folder _internal/picasso/gui/plugins. For more details on how to create plugins, see documentation: https://picassosr.readthedocs.io/en/latest/plugins.html + +Changelog +--------- +To see all changes introduced across releases, see: https://github.com/jungmannlab/picasso/blob/master/changelog.md + +.. SYNC-START: contributions + +Contributions & Copyright +------------------------- + +Contributors: Joerg Schnitzbauer, Maximilian Strauss, Rafal Kowalewski, Adrian Przybylski, Andrey Aristov, Hiroshi Sasaki, Alexander Auer, Johanna Rahm +Copyright (c) 2015-2025 Jungmann Lab, Max Planck Institute of Biochemistry + +.. SYNC-END: contributions + +.. SYNC-START: citing + +Citing Picasso +-------------- + +If you use Picasso in your research, please cite our Nature Protocols publication describing the software. + +J. Schnitzbauer*, M.T. Strauss*, T. Schlichthaerle, F. Schueder, R. Jungmann +Super-Resolution Microscopy with DNA-PAINT +Nature Protocols (2017). 12: 1198-1228 DOI: 10.1038/nprot.2017.024 (https://doi.org/10.1038/nprot.2017.024) + +If you use some of the functionalities provided by Picasso, please also cite the respective publications: + +- NeNA. DOI: 10.1007/s00418-014-1192-3 (https://doi.org/10.1007/s00418-014-1192-3) +- FRC. DOI: 10.1038/nmeth.2448 (https://doi.org/10.1038/nmeth.2448) +- Theoretical lateral localization precision (Gauss LQ). DOI: 10.1038/nmeth.1447 (https://doi.org/10.1038/nmeth.1447) +- Theoretical axial localization precision (Gauss LQ and MLE). DOI: 10.1038/s41467-026-70198-5 (https://doi.org/10.1038/s41467-026-70198-5) +- MLE fitting. DOI: 10.1038/nmeth.1449 (https://doi.org/10.1038/nmeth.1449) +- GPU fitting (LQ). DOI: 10.1038/s41598-017-15313-9 (https://doi.org/10.1038/s41598-017-15313-9). License can be found here (https://github.com/jungmannlab/picasso/tree/master/picasso/ext/pygpufit). +- RCC undrifting: DOI: 10.1364/OE.22.015982 (https://doi.org/10.1364/OE.22.015982) +- AIM undrifting. DOI: 10.1126/sciadv.adm776 (https://www.science.org/doi/10.1126/sciadv.adm7765) +- SMLM clusterer. DOIs: 10.1038/s41467-021-22606-1 (https://doi.org/10.1038/s41467-021-22606-1) and 10.1038/s41586-023-05925-9 (https://doi.org/10.1038/s41586-023-05925-9) +- DBSCAN: Ester, et al. Inkdd, 1996. (Vol. 96, No. 34, pp. 226-231). +- Anisotropic DBSCAN inspired by: 10.1021/acs.jpcb.4c02030 (https://doi.org/10.1021/acs.jpcb.4c02030) +- HDBSCAN. DOI: 10.1007/978-3-642-37456-2_14 (https://doi.org/10.1007/978-3-642-37456-2_14) +- RESI. DOI: 10.1038/s41586-023-05925-9 (https://doi.org/10.1038/s41586-023-05925-9) +- Nanotron. DOI: 10.1093/bioinformatics/btaa154 (https://doi.org/10.1093/bioinformatics/btaa154) +- Picasso: Server. DOI: 10.1038/s42003-022-03909-5 (https://doi.org/10.1038/s42003-022-03909-5) +- SPINNA. DOI: 10.1038/s41467-025-59500-z (https://doi.org/10.1038/s41467-025-59500-z) +- SPINNA for LE fitting. DOI: 10.1038/s41592-024-02242-5 (https://doi.org/10.1038/s41592-024-02242-5) +- G5M. DOI: 10.1038/s41467-026-70198-5 (https://doi.org/10.1038/s41467-026-70198-5) + +.. SYNC-END: citing + +.. SYNC-START: credits + +Credits +------- + +- Design icon based on “Hexagon by Creative Stalls" from the Noun Project +- Simulate icon based on “Microchip by Futishia" from the Noun Project +- Localize icon based on “Mountains" by MONTANA RUCOBO from the Noun Project +- Filter icon based on “Funnel" by José Campos from the Noun Project +- Render icon based on “Paint Palette" by Vectors Market from the Noun Project +- Average icon based on “Layers" by Creative Stall from the Noun Project +- Server icon based on “Database" by Nimal Raj from the Noun Project +- SPINNA icon based on "Spinner" by Viktor Ostrovsky from the Noun Project + +.. SYNC-END: credits diff --git a/release/sync_installer_readmes.py b/release/sync_installer_readmes.py index 4bf7c64e..8f1880db 100644 --- a/release/sync_installer_readmes.py +++ b/release/sync_installer_readmes.py @@ -6,8 +6,12 @@ ... .. SYNC-END: -are copied verbatim into the matching sentinel pairs in each installer -readme. Run from the repo root, or via the pre-commit hook. +are copied into the matching sentinel pairs in each installer readme. +The installer readmes are plain-text ``.txt`` files (shipped alongside +the installers), so reStructuredText markup in the synced bodies is +converted to a reader-friendly plain-text form before insertion. + +Run from the repo root, or via the pre-commit hook. Usage: python release/sync_installer_readmes.py # rewrite if needed @@ -23,8 +27,8 @@ REPO_ROOT = Path(__file__).resolve().parent.parent SOURCE = REPO_ROOT / "readme.rst" TARGETS = [ - REPO_ROOT / "release" / "one_click_macos_gui" / "readme.rst", - REPO_ROOT / "release" / "one_click_windows_gui" / "readme.rst", + REPO_ROOT / "release" / "one_click_macos_gui" / "readme.txt", + REPO_ROOT / "release" / "one_click_windows_gui" / "readme.txt", ] SECTION_RE = re.compile( @@ -34,6 +38,24 @@ re.DOTALL, ) +# `label `__ or `label `_ -> label (url) +RST_LINK_RE = re.compile(r"`([^`<]+?)\s*<([^`>]+)>`_{1,2}") +# ``code`` -> code +RST_CODE_RE = re.compile(r"``([^`]+)``") +# leading "| " of an rst line block +RST_LINE_BLOCK_RE = re.compile(r"^\| ?", re.MULTILINE) + + +def rst_to_text(body: str) -> str: + body = RST_LINK_RE.sub( + lambda m: f"{m.group(1).strip()} ({m.group(2)})", body + ) + body = RST_CODE_RE.sub(r"\1", body) + body = RST_LINE_BLOCK_RE.sub("", body) + # Drop bare "|" lines used as vertical spacers in rst line blocks. + body = re.sub(r"^\|\s*$", "", body, flags=re.MULTILINE) + return body + def extract_sections(text: str, path: Path) -> dict[str, str]: sections: dict[str, str] = {} @@ -52,7 +74,7 @@ def _sub(match: re.Match) -> str: raise SystemExit( f"key '{key}' present in target but missing from {SOURCE}" ) - return match.group(1) + sections[key] + match.group(4) + return match.group(1) + rst_to_text(sections[key]) + match.group(4) return SECTION_RE.sub(_sub, text) From 2f4afecb73a26b6b457bed48bc10a6924814fe53 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 27 May 2026 13:00:03 +0200 Subject: [PATCH 30/42] Load FOV keeps the aspect ratio of the input .txt file --- changelog.md | 1 + picasso/gui/render.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/changelog.md b/changelog.md index 0010eb13..28b297c8 100644 --- a/changelog.md +++ b/changelog.md @@ -24,6 +24,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - Faster cluster centers calculation for SMLM clusterer, DBSCAN and HDBSCAN + `lpz` is saved if applicable - Removed render property cache since it did not provide any significant speed improvement - Installers are distributed with readme.txt files (previously .rst) +- Render: Load FOV keeps the aspect ratio of the input .txt file ## 0.10.0 diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 730bd5d4..dfab8319 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -4233,6 +4233,7 @@ def load_fov(self) -> None: self.y_box.setValue(y) self.w_box.setValue(w) self.h_box.setValue(h) + self.window.resize_view_to_fov(w, h) self.update_scene() def update_scene(self) -> None: @@ -8593,6 +8594,7 @@ def load_fov_drop(self, fov: FloatArray1D) -> None: FOV.""" (x, y, w, h) = fov if w > 0 and h > 0: + self.window.resize_view_to_fov(w, h) viewport = [(y, x), (y + h, x + w)] self.update_scene(viewport=viewport) self.window.info_dialog.xy_label.setText(f"{x:.2f} / {y:.2f} ") @@ -12790,6 +12792,47 @@ def open_rotated_locs(self) -> None: self.window_rot.view_rot.angz = self.view.infos[0][-1]["angz"] self.rot_win() + def resize_view_to_fov(self, w: float, h: float) -> None: + """Resize the main window so that ``view`` has aspect ratio w/h. + + Only triggers a resize when the current view aspect differs from + the target. The longer of the current view dimensions is + preserved; the other is recomputed from the target aspect, then + both are clipped to the available screen geometry. + """ + if w <= 0 or h <= 0: + return + view_w = self.view.width() + view_h = self.view.height() + if view_w <= 0 or view_h <= 0: + return + target_aspect = w / h + current_aspect = view_w / view_h + if abs(current_aspect - target_aspect) < 1e-3: + return + + if target_aspect >= 1.0: + new_view_w = max(view_w, view_h) + new_view_h = new_view_w / target_aspect + else: + new_view_h = max(view_w, view_h) + new_view_w = new_view_h * target_aspect + + screen = self.screen() or QtWidgets.QApplication.primaryScreen() + avail = screen.availableGeometry() + chrome_w = self.width() - view_w + chrome_h = self.height() - view_h + max_view_w = max(1, avail.width() - chrome_w) + max_view_h = max(1, avail.height() - chrome_h) + scale = min(1.0, max_view_w / new_view_w, max_view_h / new_view_h) + new_view_w *= scale + new_view_h *= scale + + self.resize( + int(round(new_view_w + chrome_w)), + int(round(new_view_h + chrome_h)), + ) + def resizeEvent(self, even: QtGui.QResizeEvent) -> None: """Update window size.""" self.update_info() From 16167f5ca7e82360c4dd32741ba9d49a646a87f2 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 27 May 2026 13:17:13 +0200 Subject: [PATCH 31/42] Files dialog resizing fixed --- changelog.md | 1 + picasso/gui/render.py | 35 ++++++++++++++++++++--------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/changelog.md b/changelog.md index 28b297c8..29045a0b 100644 --- a/changelog.md +++ b/changelog.md @@ -25,6 +25,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - Removed render property cache since it did not provide any significant speed improvement - Installers are distributed with readme.txt files (previously .rst) - Render: Load FOV keeps the aspect ratio of the input .txt file +- Render: Files dialog resizing fixed ## 0.10.0 diff --git a/picasso/gui/render.py b/picasso/gui/render.py index dfab8319..9097a383 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -443,7 +443,6 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: self.builtin_cmap_stops = {} layout = QtWidgets.QGridLayout() self.setLayout(layout) - self.setMaximumHeight(1000) # add non-scrollable elements - left side self.legend = QtWidgets.QCheckBox("Show legend") @@ -502,13 +501,16 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: # add scrollable area which will display all channels, below # the non-scrollable elements - scroll = QtWidgets.QScrollArea(self) - scroll.setWidgetResizable(True) + self._scroll = QtWidgets.QScrollArea(self) + self._scroll.setWidgetResizable(True) + self._scroll.setHorizontalScrollBarPolicy( + QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff + ) self.container = QtWidgets.QWidget() - scroll.setWidget(self.container) + self._scroll.setWidget(self.container) self.scroll_area = QtWidgets.QGridLayout(self.container) self.scroll_area.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop) - layout.addWidget(scroll, 4, 0, 1, 3) + layout.addWidget(self._scroll, 4, 0, 1, 3) self.checks = [] self.title = [] @@ -677,9 +679,16 @@ def add_entry(self, path: str) -> None: self.scroll_area.addWidget(intensity, currentline, 4) self.scroll_area.addWidget(p, currentline, 5) - # adjust the size of the dialog - hint = self.container.sizeHint() - lib.adjust_widget_size(self, hint, 45, 150) + self._fit_scroll_width() + + def _fit_scroll_width(self) -> None: + """Ensure the dialog is wide enough that the scroll area's + contents fit horizontally without a scrollbar. Only ever grows + the minimum width; never shrinks the dialog.""" + self.container.adjustSize() + needed = self.container.sizeHint().width() + frame = 2 * self._scroll.frameWidth() + self._scroll.setMinimumWidth(needed + frame) def update_colors(self) -> None: """Change colors in self.colordisp_all and updates the scene in @@ -709,13 +718,11 @@ def change_title(self, button_name: str) -> None: else: self.checks[i].setText(new_title) self.update_viewport() - # change size of the dialog - hint = self.scroll_area.sizeHint() - lib.adjust_widget_size(self, hint, 45, 150) # change name in the fast render dialog self.window.fast_render_dialog.channel.setItemText( i + 1, new_title ) + self._fit_scroll_width() break def _close_one_channel(self, i: int, render_=True) -> None: @@ -799,9 +806,7 @@ def _close_one_channel(self, i: int, render_=True) -> None: # remove the channel from test clustering dialog self.window.test_clusterer_dialog.channels.removeItem(i) - # adjust the size of the dialog - hint = self.scroll_area.sizeHint() - lib.adjust_widget_size(self, hint, 45, 150) + self._fit_scroll_width() def close_file(self, i: int | str, render=True) -> None: """Close a given channel (defined by its index of name) and @@ -1000,7 +1005,7 @@ def load_colors(self) -> None: self.update_colors() def sizeHint(self) -> QtCore.QSize: - return QtCore.QSize(600, 350) + return QtCore.QSize(700, 500) class CustomColormapDialog(lib.Dialog): From f54d7a97b391d34bfc72da9cc390ac59adbbb9d7 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 27 May 2026 14:00:53 +0200 Subject: [PATCH 32/42] Render GUI: show 3D clustering widgets only when 3D data is loaded --- changelog.md | 1 + picasso/gui/render.py | 98 ++++++++++++++++++++++++++++++------------- 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/changelog.md b/changelog.md index 29045a0b..be74dd23 100644 --- a/changelog.md +++ b/changelog.md @@ -26,6 +26,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - Installers are distributed with readme.txt files (previously .rst) - Render: Load FOV keeps the aspect ratio of the input .txt file - Render: Files dialog resizing fixed +- Render GUI: show 3D clustering widgets only when 3D data is loaded ## 0.10.0 diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 9097a383..34d8d47a 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -2320,17 +2320,21 @@ class DbscanDialog(lib.Dialog): Contains epsilon (nm) for DBSCAN (see scikit-learn). radius_z : QDoubleSpinBox Contains epsilon in the z direction (nm) for anisotropic 3D - DBSCAN. Ignored for 2D data. + DBSCAN. Shown only if 3D data is present. save_areas : QCheckBox Whether to save cluster areas as .csv file. save_centers : QCheckBox Whether to save cluster centers. """ - def __init__(self, window: QtWidgets.QMainWindow) -> None: + def __init__( + self, + window: QtWidgets.QMainWindow, + flag_3D: bool = False, + ) -> None: super().__init__(window) self.window = window - self.setWindowTitle("Enter parameters") + self.setWindowTitle(f"Enter parameters ({'3D' if flag_3D else '2D'})") vbox = QtWidgets.QVBoxLayout(self) grid = QtWidgets.QGridLayout() radius_label = QtWidgets.QLabel("Radius (nm):") @@ -2345,21 +2349,22 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: self.radius.setDecimals(2) self.radius.setSingleStep(0.1) grid.addWidget(self.radius, 0, 1) - radius_z_label = QtWidgets.QLabel("Radius z (3D only, nm):") - radius_z_label.setToolTip( - "DBSCAN epsilon in the z direction. Only used for 3D data.\n" - "Scales z coordinates so the neighborhood is an ellipsoid\n" - "with semi-axes (radius, radius, radius z).\n" - "Anisotropic DBSCAN approach inspired by Lörzing, Schake,\n" - "and Schlierf, Journal of Phys Chem B, 2024." - ) - grid.addWidget(radius_z_label, 1, 0) self.radius_z = QtWidgets.QDoubleSpinBox() self.radius_z.setRange(0.01, 1e6) self.radius_z.setValue(25) self.radius_z.setDecimals(2) self.radius_z.setSingleStep(0.1) - grid.addWidget(self.radius_z, 1, 1) + if flag_3D: + radius_z_label = QtWidgets.QLabel("Radius z (nm):") + radius_z_label.setToolTip( + "DBSCAN epsilon in the z direction. Scales z coordinates\n" + "so the neighborhood is an ellipsoid with semi-axes\n" + "(radius, radius, radius z).\n" + "Anisotropic DBSCAN approach inspired by Lörzing, Schake,\n" + "and Schlierf, Journal of Phys Chem B, 2024." + ) + grid.addWidget(radius_z_label, 1, 0) + grid.addWidget(self.radius_z, 1, 1) min_samples_label = QtWidgets.QLabel("Min. samples:") min_samples_label.setToolTip( "Minimum number of samples in a neighborhood for a point to be\n" @@ -2412,10 +2417,11 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: @staticmethod def getParams( parent: QtWidgets.QMainWindow | None = None, + flag_3D: bool = False, ) -> tuple[dict, bool]: """Create the dialog and return the requested values for DBSCAN.""" - dialog = DbscanDialog(parent) + dialog = DbscanDialog(parent, flag_3D=flag_3D) result = dialog.exec() return { "radius": dialog.radius.value(), @@ -3249,6 +3255,7 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: # parameters - channel self.channels = QtWidgets.QComboBox() self.channels.setToolTip("Select the channel to test clustering on.") + self.channels.currentIndexChanged.connect(self._update_3d_visibility) parameters_grid.addWidget(self.channels, 0, 0, 1, 2) # parameters - choose clusterer @@ -3381,6 +3388,21 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: zoomout_action.triggered.connect(self.view.zoom_out) self.addAction(zoomout_action) + def _update_3d_visibility(self) -> None: + """Show or hide Z-specific widgets based on whether the selected + channel has a `z` column.""" + idx = self.channels.currentIndex() + if idx < 0 or idx >= len(self.window.view.locs): + is_3d = False + else: + is_3d = "z" in self.window.view.locs[idx].columns + self.test_dbscan_params.set_3d(is_3d) + self.test_smlm_params.set_3d(is_3d) + + def showEvent(self, event: QtGui.QShowEvent) -> None: + self._update_3d_visibility() + super().showEvent(event) + def on_xy_proj(self) -> None: self.view.ang = None self.view.update_scene() @@ -3665,19 +3687,21 @@ def __init__(self, dialog): self.radius.setSingleStep(0.1) grid.addWidget(self.radius, 0, 1) - radius_z_label = QtWidgets.QLabel("Radius z (3D only, nm):") - radius_z_label.setToolTip( - "DBSCAN epsilon in the z direction. Only used for 3D data.\n" - "Scales z coordinates so the neighborhood is an ellipsoid\n" - "with semi-axes (radius xy, radius xy, radius z)." + self.radius_z_label = QtWidgets.QLabel("Radius z (nm):") + self.radius_z_label.setToolTip( + "DBSCAN epsilon in the z direction. Scales z coordinates so\n" + "the neighborhood is an ellipsoid with semi-axes\n" + "(radius xy, radius xy, radius z)." ) - grid.addWidget(radius_z_label, 1, 0) + grid.addWidget(self.radius_z_label, 1, 0) self.radius_z = QtWidgets.QDoubleSpinBox() self.radius_z.setRange(0.01, 1e6) self.radius_z.setValue(25) self.radius_z.setDecimals(2) self.radius_z.setSingleStep(0.1) grid.addWidget(self.radius_z, 1, 1) + self.radius_z_label.setVisible(False) + self.radius_z.setVisible(False) min_samples_label = QtWidgets.QLabel("Min. samples:") min_samples_label.setToolTip( @@ -3704,6 +3728,11 @@ def __init__(self, dialog): grid.addWidget(self.min_locs, 3, 1) grid.setRowStretch(4, 1) + def set_3d(self, is_3d: bool) -> None: + """Show or hide the Z radius widget.""" + self.radius_z_label.setVisible(is_3d) + self.radius_z.setVisible(is_3d) + class TestHDBSCANParams(QtWidgets.QWidget): """Choose parameters for HDBSCAN testing.""" @@ -3772,18 +3801,20 @@ def __init__(self, dialog): self.radius_xy.setDecimals(2) grid.addWidget(self.radius_xy, 0, 1) - radius_z_label = QtWidgets.QLabel("Radius z (3D only):") - radius_z_label.setToolTip( + self.radius_z_label = QtWidgets.QLabel("Radius z (nm):") + self.radius_z_label.setToolTip( "Radius in which localizations are considered part of the\n" - "same cluster. Applied in z direction (3D only)." + "same cluster. Applied in z direction." ) - grid.addWidget(radius_z_label, 1, 0) + grid.addWidget(self.radius_z_label, 1, 0) self.radius_z = QtWidgets.QDoubleSpinBox() self.radius_z.setValue(25) self.radius_z.setRange(0.01, 1e6) self.radius_z.setSingleStep(0.1) self.radius_z.setDecimals(2) grid.addWidget(self.radius_z, 1, 1) + self.radius_z_label.setVisible(False) + self.radius_z.setVisible(False) min_locs_label = QtWidgets.QLabel("Min. no. of locs") min_locs_label.setToolTip( @@ -3803,6 +3834,11 @@ def __init__(self, dialog): grid.addWidget(self.fa, 3, 0, 1, 2) grid.setRowStretch(4, 1) + def set_3d(self, is_3d: bool) -> None: + """Show or hide the Z radius widget.""" + self.radius_z_label.setVisible(is_3d) + self.radius_z.setVisible(is_3d) + class TestG5MParams(QtWidgets.QWidget): """Choose parameters for G5M testing.""" @@ -7331,7 +7367,12 @@ def dbscan(self) -> None: channel = self.get_channel_all_seq("DBSCAN") # get DBSCAN parameters - params, ok = DbscanDialog.getParams() + if channel is None or channel == len(self.locs_paths): + # no channel selected or "apply to all" + flag_3D = any("z" in _.columns for _ in self.locs) + else: + flag_3D = "z" in self.locs[channel].columns + params, ok = DbscanDialog.getParams(flag_3D=flag_3D) if ok: if channel == len(self.locs_paths): # apply to all channels # get saving name suffix @@ -7577,10 +7618,11 @@ def smlm_clusterer(self) -> None: # get clustering parameters pixelsize = self.pixelsize - if any(["z" in _.columns for _ in self.locs]): - flag_3D = True + if channel is None or channel == len(self.locs_paths): + # no channel selected or "apply to all" + flag_3D = any("z" in _.columns for _ in self.locs) else: - flag_3D = False + flag_3D = "z" in self.locs[channel].columns params, ok = SMLMDialog.getParams(flag_3D=flag_3D) # convert to camera pixels params["radius_xy"] = params["radius_xy"] / pixelsize From fb2451a4f3ea5d892dea0f622e6e0986fe2adba1 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 27 May 2026 14:07:18 +0200 Subject: [PATCH 33/42] ...and the same for spinna's mask generation --- changelog.md | 1 + picasso/gui/spinna.py | 47 ++++++++++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/changelog.md b/changelog.md index be74dd23..6fcf7306 100644 --- a/changelog.md +++ b/changelog.md @@ -27,6 +27,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - Render: Load FOV keeps the aspect ratio of the input .txt file - Render: Files dialog resizing fixed - Render GUI: show 3D clustering widgets only when 3D data is loaded +- SPINNA GUIL show 3D masking widgets only if 3D mask is selected ## 0.10.0 diff --git a/picasso/gui/spinna.py b/picasso/gui/spinna.py index abf80c47..5cbf1d75 100644 --- a/picasso/gui/spinna.py +++ b/picasso/gui/spinna.py @@ -462,9 +462,7 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: mask_layout.addWidget(self.load_locs_button, 0, 0, 1, 3) # isotropic mask - self.isotropic_mask_check = QtWidgets.QCheckBox( - "Isotropic mask (3D only)" - ) + self.isotropic_mask_check = QtWidgets.QCheckBox("Isotropic mask") self.isotropic_mask_check.setToolTip( "Keep mask pixel/voxel size and blur isotropic?" ) @@ -475,12 +473,12 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: mask_layout.addWidget(self.isotropic_mask_check, 1, 0) # anisotropic mask labels - xy_label = QtWidgets.QLabel("xy") - xy_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) - mask_layout.addWidget(xy_label, 1, 1) - z_label = QtWidgets.QLabel("z (3D only)") - z_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) - mask_layout.addWidget(z_label, 1, 2) + self.xy_label = QtWidgets.QLabel("xy") + self.xy_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + mask_layout.addWidget(self.xy_label, 1, 1) + self.z_label = QtWidgets.QLabel("z") + self.z_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) + mask_layout.addWidget(self.z_label, 1, 2) # mask pixel / voxel size pixel_label = QtWidgets.QLabel("Mask pixel/voxel size (nm):") @@ -524,6 +522,16 @@ def __init__(self, window: QtWidgets.QMainWindow) -> None: self.mask_blur_z.valueChanged.connect(self.on_mask_blur_changed) mask_layout.addWidget(self.mask_blur_z, 3, 2) + # default to 2D: hide anisotropic widgets + for widget in ( + self.isotropic_mask_check, + self.xy_label, + self.z_label, + self.mask_binsize_z, + self.mask_blur_z, + ): + widget.setVisible(False) + # ndimensions: ndim_label = QtWidgets.QLabel("Mask dimensionality:") ndim_label.setToolTip( @@ -873,15 +881,22 @@ def on_isotropic_mask_changed(self, state: int) -> None: self.mask_blur_z.blockSignals(False) def on_mask_ndim_changed(self, index: int) -> None: - """Show/hide the z-slicing options for 3D masks.""" - if index == 0: # 2D - self.zslice_check.setVisible(False) + """Show/hide the anisotropic mask and z-slicing widgets + depending on whether a 2D or 3D mask is selected.""" + is_3d = index == 1 + for widget in ( + self.isotropic_mask_check, + self.xy_label, + self.z_label, + self.mask_binsize_z, + self.mask_blur_z, + self.zslice_check, + self.zslice_slider, + ): + widget.setVisible(is_3d) + if not is_3d: self.zslice_check.setChecked(False) - self.zslice_slider.setVisible(False) self.zslice_slider.setValue(0) - elif index == 1: # 3D - self.zslice_check.setVisible(True) - self.zslice_slider.setVisible(True) def on_mask_binsize_changed(self, value: int) -> None: """If isotropic mask is checked, set the same value for all From 8b11096ad78806bc17408e3a0467ddb1974f8b55 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 27 May 2026 14:46:33 +0200 Subject: [PATCH 34/42] Fixed high-resolution display of mask in Render (#666) --- changelog.md | 1 + picasso/gui/render.py | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 6fcf7306..21a10fbd 100644 --- a/changelog.md +++ b/changelog.md @@ -28,6 +28,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - Render: Files dialog resizing fixed - Render GUI: show 3D clustering widgets only when 3D data is loaded - SPINNA GUIL show 3D masking widgets only if 3D mask is selected +- Fixed high-resolution display of mask in Render (#666) ## 0.10.0 diff --git a/picasso/gui/render.py b/picasso/gui/render.py index 34d8d47a..640bc868 100644 --- a/picasso/gui/render.py +++ b/picasso/gui/render.py @@ -5587,11 +5587,6 @@ def render_to_pixmap( image = render.apply_colormap(image, cmap) # create a 4 channel (rgb, alpha) array qimage = render.rgb_to_qimage(image) - qimage = qimage.scaled( - 300, - 300, - QtCore.Qt.AspectRatioMode.KeepAspectRatioByExpanding, - ) pixmap = QtGui.QPixmap.fromImage(qimage) return pixmap From 686b19a7b5500040f3b5edffe45e7bda513fb0ee Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 27 May 2026 14:55:59 +0200 Subject: [PATCH 35/42] Fixed subcluster check plot when one of the two populations is empty (#667) --- changelog.md | 1 + picasso/lib.py | 110 +++++++++++++++++++++++++++++-------------------- 2 files changed, 67 insertions(+), 44 deletions(-) diff --git a/changelog.md b/changelog.md index 21a10fbd..587cb5bf 100644 --- a/changelog.md +++ b/changelog.md @@ -29,6 +29,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - Render GUI: show 3D clustering widgets only when 3D data is loaded - SPINNA GUIL show 3D masking widgets only if 3D mask is selected - Fixed high-resolution display of mask in Render (#666) +- Fixed subcluster check plot when one of the two populations is empty (#667) ## 0.10.0 diff --git a/picasso/lib.py b/picasso/lib.py index 114cfe20..4a1512cc 100644 --- a/picasso/lib.py +++ b/picasso/lib.py @@ -2410,6 +2410,9 @@ def plot_subclustering_check( Figure and axes if ``return_fig`` is True, otherwise (None, None). """ + has_clustered = len(clustered_n_events) > 0 + has_sparse = len(sparse_n_events) > 0 + m_clustered = clustered_n_events.mean() m_sparse = sparse_n_events.mean() s_clustered = clustered_n_events.std() @@ -2417,54 +2420,73 @@ def plot_subclustering_check( # create the plot fig, ax1 = plt.subplots(1, figsize=(6, 4), constrained_layout=True) - min_bin, max_bin = np.percentile(clustered_n_events, [2.5, 97.5]) - vals, counts = np.unique(clustered_n_events, return_counts=True) - if clustering_dist is not None: - label = ( - f"Clustered (d < {clustering_dist:.1f} nm) " - f"{m_clustered:.1f} +/- {s_clustered:.1f}" + if has_clustered or has_sparse: + all_events = np.concatenate((sparse_n_events, clustered_n_events)) + min_bin, max_bin = np.percentile(all_events, [2.5, 97.5]) + + if has_clustered: + vals, counts = np.unique(clustered_n_events, return_counts=True) + if clustering_dist is not None: + label = ( + f"Clustered (d < {clustering_dist:.1f} nm) " + f"{m_clustered:.1f} +/- {s_clustered:.1f}" + ) + else: + label = f"Clustered {m_clustered:.1f} +/- {s_clustered:.1f}" + ax1.bar( + vals, + counts, + width=0.8, + alpha=0.5, + label=label, + color="C0", ) - else: - label = f"Clustered {m_clustered:.1f} +/- {s_clustered:.1f}" - ax1.bar( - vals, - counts, - width=0.8, - alpha=0.5, - label=label, - color="C0", - ) - ax1.axvline(m_clustered, color="C0", linestyle="--") - vals, counts = np.unique(sparse_n_events, return_counts=True) - if sparse_dist is not None: - label = ( - f"Sparse (d > {sparse_dist:.1f} nm) " - f"{m_sparse:.1f} +/- {s_sparse:.1f}" + ax1.axvline(m_clustered, color="C0", linestyle="--") + + if has_sparse: + vals, counts = np.unique(sparse_n_events, return_counts=True) + if sparse_dist is not None: + label = ( + f"Sparse (d > {sparse_dist:.1f} nm) " + f"{m_sparse:.1f} +/- {s_sparse:.1f}" + ) + else: + label = f"Sparse {m_sparse:.1f} +/- {s_sparse:.1f}" + ax1.bar( + vals, + counts, + width=0.8, + alpha=0.5, + label=label, + color="C1", + ) + ax1.axvline(m_sparse, color="C1", linestyle="--") + + if has_clustered or has_sparse: + ax1.set_xlabel("Number of events") + ax1.set_ylabel("Counts") + ax1.set_xlim(min_bin - 1, max_bin + 1) + ax1.legend() + + if has_clustered and has_sparse: + stat, p_perm, p = permutation_test(clustered_n_events, sparse_n_events) + p_value_str = r"$p_{value}$" + title = ( + f"KS test: stat={stat:.4f}\n" + f"permutation {p_value_str}={p_perm:.4f}\n" + f"theoretical {p_value_str}={p:.4f}" + ) + elif has_clustered or has_sparse: + title = ( + "Only one population found, no statistical test performed; " + "adjust distance parameters." ) else: - label = f"Sparse {m_sparse:.1f} +/- {s_sparse:.1f}" - ax1.bar( - vals, - counts, - width=0.8, - alpha=0.5, - label=label, - color="C1", - ) - ax1.axvline(m_sparse, color="C1", linestyle="--") - ax1.set_xlabel("Number of events") - ax1.set_ylabel("Counts") - ax1.set_xlim(min_bin - 1, max_bin + 1) - # add stat. tests in the title: - stat, p_perm, p = permutation_test(clustered_n_events, sparse_n_events) - p_value_str = r"$p_{value}$" - title = ( - f"KS test: stat={stat:.4f}\n" - f"permutation {p_value_str}={p_perm:.4f}\n" - f"theoretical {p_value_str}={p:.4f}" - ) + title = ( + "No molecules found in either population, adjust distance" + " parameters." + ) ax1.set_title(title, fontsize=10) - ax1.legend() if len(plot_path): if isinstance(plot_path, str): plot_path = [plot_path] From e394615bcb4129406b8b465784cc99f5f6d78e31 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Wed, 27 May 2026 16:22:45 +0200 Subject: [PATCH 36/42] fix test --- tests/test_spatial_index.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/test_spatial_index.py b/tests/test_spatial_index.py index a34935f0..a8a9e9c4 100644 --- a/tests/test_spatial_index.py +++ b/tests/test_spatial_index.py @@ -138,16 +138,21 @@ def test_viewport_covering_full_fov_returns_none(self): assert spatial_index.query_viewport(pyr, ((0, 0), (H, W))) is None def test_viewport_above_bypass_threshold_returns_none(self): - # Half the FOV area in each dimension -> 25% coverage, below - # the default 0.5 threshold, so we still get an array. Doubling - # one dimension to span the full axis brings coverage to 50% - # and triggers the bypass. + # Coverage well below the bypass threshold still returns an + # array; coverage well above triggers the bypass. Sized off the + # module constant so the test tracks any future re-tuning. W, H = 512.0, 512.0 locs = _make_locs(5000, W, H) pyr = spatial_index.build_render_index(locs, _info(W, H)) - sub = spatial_index.query_viewport(pyr, ((0, 0), (H / 2, W / 2))) + ratio = spatial_index._BYPASS_COVERAGE_RATIO + below = float(np.sqrt(ratio * 0.5)) * W + sub = spatial_index.query_viewport(pyr, ((0.0, 0.0), (below, below))) assert sub is not None and sub.shape[0] > 0 - assert spatial_index.query_viewport(pyr, ((0, 0), (H, W / 2))) is None + above = float(np.sqrt(min(1.0, ratio * 2.0))) * W + assert ( + spatial_index.query_viewport(pyr, ((0.0, 0.0), (above, above))) + is None + ) def test_viewport_with_negative_bounds_enclosing_fov_returns_none(self): # Zoomed/panned out so the viewport extends past every FOV edge From 39d5b742bcda6543bbdde9dc0012ab24f64e461d Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 28 May 2026 08:14:13 +0200 Subject: [PATCH 37/42] expand sample notebook 1 + tqdm error fix --- changelog.md | 2 + picasso/avgroi.py | 2 +- picasso/gausslq.py | 2 +- picasso/gaussmle.py | 2 +- picasso/localize.py | 2 +- picasso/postprocess.py | 6 +- samples/sample_notebook_1_localize.ipynb | 566 +++++++++++++++-------- 7 files changed, 385 insertions(+), 197 deletions(-) diff --git a/changelog.md b/changelog.md index 587cb5bf..c4fab1e8 100644 --- a/changelog.md +++ b/changelog.md @@ -30,6 +30,8 @@ This patch adds a number of new, useful features rather than simply fixing the b - SPINNA GUIL show 3D masking widgets only if 3D mask is selected - Fixed high-resolution display of mask in Render (#666) - Fixed subcluster check plot when one of the two populations is empty (#667) +- Fixed 2D fitting with console printout and no multiprocessing +- Expanded the scope of sample notebooks ## 0.10.0 diff --git a/picasso/avgroi.py b/picasso/avgroi.py index bba412e9..94ab7ff3 100644 --- a/picasso/avgroi.py +++ b/picasso/avgroi.py @@ -52,7 +52,7 @@ def fit_spots( theta.fill(np.nan) use_tqdm = progress_callback == "console" if use_tqdm: - iter_range = tqdm(len(spots), desc="Fitting...", unit="spot") + iter_range = tqdm(range(len(spots)), desc="Fitting...", unit="spot") else: iter_range = range(len(spots)) for i in iter_range: diff --git a/picasso/gausslq.py b/picasso/gausslq.py index 12f9735c..1ddb8b34 100644 --- a/picasso/gausslq.py +++ b/picasso/gausslq.py @@ -278,7 +278,7 @@ def fit_spots( theta.fill(np.nan) use_tqdm = progress_callback == "console" if use_tqdm: - iter_range = tqdm(len(spots), desc="Fitting...", unit="spot") + iter_range = tqdm(range(len(spots)), desc="Fitting...", unit="spot") else: iter_range = range(len(spots)) for i in iter_range: diff --git a/picasso/gaussmle.py b/picasso/gaussmle.py index c1474e0e..963fcde0 100644 --- a/picasso/gaussmle.py +++ b/picasso/gaussmle.py @@ -465,7 +465,7 @@ def gaussmle( raise ValueError("Method not available.") use_tqdm = progress_callback == "console" if use_tqdm: - iter_range = tqdm(N, desc="Fitting...", unit="spot") + iter_range = tqdm(range(N), desc="Fitting...", unit="spot") else: iter_range = range(N) for i in iter_range: diff --git a/picasso/localize.py b/picasso/localize.py index 9bef4f67..d45225fa 100755 --- a/picasso/localize.py +++ b/picasso/localize.py @@ -613,7 +613,7 @@ def _identify_serial( N = len(movie) use_tqdm = progress_callback == "console" iter_range = ( - tqdm(total=N, desc="Identifying spots", unit="frame") + tqdm(range(N), desc="Identifying spots", unit="frame") if use_tqdm else range(N) ) diff --git a/picasso/postprocess.py b/picasso/postprocess.py index 13a0e52c..98708fad 100644 --- a/picasso/postprocess.py +++ b/picasso/postprocess.py @@ -3614,7 +3614,9 @@ def groupprops( use_tqdm = callback == "console" if use_tqdm: iter_range = tqdm( - len(group_ids), desc="Calculating group statistics", unit="Groups" + total=len(group_ids), + desc="Calculating group statistics", + unit="Groups", ) else: iter_range = range(len(group_ids)) @@ -3913,7 +3915,7 @@ def _resi( resi_channels = [] if progress_callback == "console": iter_range = tqdm( - len(locs), desc="Processing channels", unit="Channels" + total=len(locs), desc="Processing channels", unit="Channels" ) else: iter_range = range(len(locs)) diff --git a/samples/sample_notebook_1_localize.ipynb b/samples/sample_notebook_1_localize.ipynb index e50fac51..780de3fa 100644 --- a/samples/sample_notebook_1_localize.ipynb +++ b/samples/sample_notebook_1_localize.ipynb @@ -31,7 +31,7 @@ ], "source": [ "%matplotlib inline\n", - "from picasso import io, localize, gausslq\n", + "from picasso import io, localize, zfit\n", "path = \"data/raw_movie.raw\" # Picasso supports multiple formats, including .raw and .ome.tif\n", "movie, info = io.load_movie(path)\n", "print(f\"{movie.shape[0]} frames, width: {movie.shape[1]} pixels, height: {movie.shape[2]} pixels\")" @@ -246,7 +246,8 @@ "source": [ "min_ng = 3500 # minimum net gradient\n", "box = 7 # box side length (pixels)\n", - "identifications = localize.identify(movie, min_ng, box, threaded=False)\n", + "# the idenitification info (metadata) can be used downstream to save localizations\n", + "identifications, identification_info = localize.identify(movie, min_ng, box, threaded=False, return_info=True)\n", "identifications.head()" ] }, @@ -295,6 +296,14 @@ "## After idenfitication, it is time to fit 2D Gaussians to estimate single-molecule positions" ] }, + { + "cell_type": "markdown", + "id": "6ba9068c", + "metadata": {}, + "source": [ + "Camera info will depend on your specific set up, see https://picassosr.readthedocs.io/en/latest/localize.html#example-default-camera. Camera info is used for converting the digital signal (pixel values) to photons. This is used for estimating localization precision.\n" + ] + }, { "cell_type": "code", "execution_count": 6, @@ -302,49 +311,100 @@ "metadata": {}, "outputs": [], "source": [ - "# camera info will depend on your specific set up, see https://picassosr.readthedocs.io/en/latest/localize.html#example-default-camera\n", + "# the values here correspond to simulations\n", "camera = {\n", " \"Gain\": 1, # em gain\n", - " \"Baseline\": 0.0,\n", - " \"Sensitivity\": 1.0,\n", - " \"Pixelsize\": 130,\n", + " \"Baseline\": 0.0, # pixel values measured without light\n", + " \"Sensitivity\": 1.0, # pixel -> photon convertion value; should be provided by the camera manufacturer\n", + " \"Pixelsize\": 130, # effective camera pixel size after magnification in nm; e.g., if physical pixel size is 6.5 um and magnification x100 with 2x2 binning is used, we get 130 nm\n", "}" ] }, { "cell_type": "code", "execution_count": 7, - "id": "2d80dc93-1dd5-42ef-93d7-3333d81e4b30", + "id": "0a494441", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "2399 spots were extracted.\n", - "Spots are an array of shape (2399, 7, 7)\n" + "Help on function fit2D in module picasso.localize:\n", + "\n", + "fit2D(\n", + " movie: lib.IntArray3D,\n", + " movie_info: list[dict],\n", + " camera_info: dict,\n", + " identifications: pd.DataFrame,\n", + " box: int,\n", + " fitting_method: Literal['gausslq', 'gausslq-gpu', 'gaussmle', 'avg'] = 'gausslq',\n", + " eps: float = 0.001,\n", + " max_it: int = 100,\n", + " mle_method: Literal['sigma', 'sigmaxy'] = 'sigmaxy',\n", + " multiprocess: bool = True,\n", + " progress_callback: Callable[[int], None] | Literal['console'] | None = None,\n", + " abort_callback: Callable[[], bool] | None = None\n", + ") -> tuple[pd.DataFrame | None, dict]\n", + " Fit 2D localizations to a movie, given positions of the detected\n", + " spots (identifications).\n", + "\n", + " Parameters\n", + " ----------\n", + " movie : lib.IntArray3D\n", + " The input movie data as a 3D numpy array.\n", + " movie_info : list of dicts\n", + " Movie metadata.\n", + " camera_info : dict\n", + " A dictionary containing camera information: \"Baseline\",\n", + " \"Sensitivity\", \"Gain\" and \"Pixelsize\".\n", + " identifications : pd.DataFrame\n", + " Data frame containing the identified spots. Contains fields\n", + " `frame`, `x`, `y`, and `net_gradient`.\n", + " box : int\n", + " Size of the box to cut out around each spot. Should be an odd\n", + " integer.\n", + " fitting_method : {\"gausslq\", \"gausslq-gpu\", \"gaussmle\" or \"avg\"}, optional\n", + " Which 2D fitting algorithm to use. \"gausslq\" for least-squares\n", + " fitting of a 2D Gaussian. \"gausslq-gpu\" for its GPU\n", + " implemntation (if available). \"gaussmle\" for MLE 2D Gaussian\n", + " fitting. \"avg\" for taking the average of each spot.\n", + " eps : float, optional\n", + " The convergence criterion for MLE fitting. Ignored for other\n", + " methods. Default is 0.001.\n", + " max_it : int, optional\n", + " The maximum number of iterations for MLE fitting. Ignored for\n", + " other methods. Default is 100.\n", + " mle_method : Literal[\"sigma\", \"sigmaxy\"], optional\n", + " The method used for MLE fitting (impose same sigma in x and y or\n", + " not, respectively). Default is \"sigmaxy\".\n", + " multiprocess: bool, optional\n", + " Whether or not to use multiprocessing. Ignored for GPU fitting.\n", + " Default is True.\n", + " progress_callback : callable, \"console\" or None, optional\n", + " If a callable provided, it must accept one integer input (number\n", + " of localized spots). If \"console\", tqdm is used to display\n", + " progress. If None, progress is not tracked.\n", + " abort_callback : callable or None, optional\n", + " A callable for aborting multiprocessing in the GUI. If a\n", + " callable provided, it must accept no input and return a boolean\n", + " indicating whether the fitting should be aborted. Default is\n", + " None.\n", + "\n", + " Returns\n", + " -------\n", + " locs : pd.DataFrame\n", + " Data frame containing the localized spots. Returns None if\n", + " fitting was aborted.\n", + " new_info : dict\n", + " New metadata.\n", + "\n" ] - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" } ], "source": [ - "# start by extracting the idenitified spots\n", - "spots = localize.get_spots(movie, identifications, box, camera)\n", - "print(f\"{len(spots)} spots were extracted.\")\n", - "print(f\"Spots are an array of shape {spots.shape}\")\n", - "\n", - "plt.imshow(spots[0], cmap=\"gray\")\n", - "plt.title(\"Example spot\")\n", - "plt.show()" + "# this function summarizes all possible ways we can fit 2D spots using different fitting methods etc\n", + "help(localize.fit2D)" ] }, { @@ -353,58 +413,196 @@ "id": "0b5952ed-e152-40c6-85ee-ed904a319e9b", "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Fitting...: 100%|██████████| 2399/2399 [00:01<00:00, 1515.47spot/s]" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "Help on function fit_spots in module picasso.gausslq:\n", + "Spots fitted\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "locs, locs_info = localize.fit2D(\n", + " movie=movie,\n", + " movie_info=info,\n", + " camera_info=camera,\n", + " identifications=identifications,\n", + " box=box,\n", + " fitting_method=\"gausslq\", # gaussian least-squares, also available via GPU (if present) or MLE fitting\n", + " multiprocess=False, # small dataset, no need for multiprocessing\n", + " progress_callback=\"console\", # print progress to console\n", + ")\n", + "print(f\"Spots fitted\")" + ] + }, + { + "cell_type": "markdown", + "id": "2803127d", + "metadata": {}, + "source": [ + "### We can now save localizations with all the metadata dictionaries." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "065d1681", + "metadata": {}, + "outputs": [], + "source": [ + "info.append(identification_info)\n", + "info.append(locs_info)\n", + "io.save_locs(path.replace(\".raw\", \"_locs.hdf5\"), locs, info)" + ] + }, + { + "cell_type": "markdown", + "id": "c69aad16-937c-447b-a286-65fd1982ae8c", + "metadata": {}, + "source": [ + "## Alternatively, identification and fitting can be combined:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "4f7e1383-bf2d-43d6-8a80-a3b8066e0d5b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function localize in module picasso.localize:\n", + "\n", + "localize(\n", + " movie: lib.IntArray3D,\n", + " camera_info: dict,\n", + " parameters: dict,\n", + " *,\n", + " roi: tuple[tuple[int, int], tuple[int, int]] | None = None,\n", + " frame_bounds: tuple[int, int] | None = None,\n", + " movie_info: list[dict] | None = None,\n", + " fitting_method: Literal['gausslq', 'gausslq-gpu', 'gaussmle', 'avg'] = 'gausslq',\n", + " eps: float = 0.001,\n", + " max_it: int = 100,\n", + " mle_method: Literal['sigma', 'sigmaxy'] = 'sigmaxy',\n", + " threaded: bool = True,\n", + " identification_progress_callback: Callable[[int], None] | Literal['console'] | None = None,\n", + " fit_progress_callback: Callable[[int], None] | Literal['console'] | None = None,\n", + " return_info: bool = None\n", + ") -> pd.DataFrame | tuple[pd.DataFrame, list[dict]]\n", + " Localize (i.e., identify and fit) spots in 2D in a movie using\n", + " the specified parameters.\n", "\n", - "fit_spots(\n", - " spots: lib.FloatArray3D,\n", - " progress_callback: Callable[[int], None] | Literal['console'] | None = None\n", - ") -> lib.FloatArray2D\n", - " Fit multiple spots using least squares optimization. Each spot is\n", - " a 2D array representing the pixel values of the spot image. The\n", - " function returns a 2D array with the optimized parameters for each\n", - " spot, where each row corresponds to a spot and the columns are the\n", - " parameters in the following order: [x, y, photons, bg, sx, sy].\n", + " Since v0.10.0: support for frame bounds and ROI for identification +\n", + " all fitting methods.\n", "\n", " Parameters\n", " ----------\n", - " spots : lib.FloatArray3D\n", - " A 3D array of shape (n_spots, size, size), where n_spots is the\n", - " number of spots and size is the length of one side of the square\n", - " spot image. Each slice along the first axis represents a single\n", - " spot image.\n", - " progress_callback : callable or None\n", - " If a callable provided, it must accept one integer input (number\n", - " of localized spots). If \"console\", tqdm is used to display\n", - " progress. If None, progress is not tracked.\n", + " movie : lib.IntArray3D\n", + " The input movie data as a 3D numpy array.\n", + " camera_info : dict\n", + " A dictionary containing camera information such as\n", + " `Baseline`, `Sensitivity`, and `Gain`.\n", + " parameters : dict\n", + " A dictionary containing localization parameters, including:\n", + " - `Min. Net Gradient`: Minimum net gradient for spot\n", + " identification.\n", + " - `Box Size`: Size of the box to cut out around each spot.\n", + " threaded : bool, optional\n", + " Whether to use multithreading/multiprocessing. Default is True.\n", + " movie_info : list[dict], optional\n", + " Movie metadata. If None, an empty list is used. Default is None.\n", + " roi : tuple, optional\n", + " Region of interest (ROI) defined as a tuple of two tuples,\n", + " where the first tuple contains the start coordinates\n", + " (y_start, x_start) and the second tuple contains the end\n", + " coordinates (y_end, x_end). If None, the entire frame is used.\n", + " Default is None.\n", + " frame_bounds : tuple, optional\n", + " Minimum and maximum frame numbers to consider for the\n", + " identification. If None, all frames are used. Default is None.\n", + " fitting_method : {\"gausslq\", \"gausslq-gpu\", \"gaussmle\" or \"avg\"}, optional\n", + " Which 2D fitting algorithm to use. Default is \"gausslq\".\n", + " eps : float, optional\n", + " The convergence criterion for MLE fitting. Default is 0.001.\n", + " max_it : int, optional\n", + " The maximum number of iterations for MLE fitting. Default is\n", + " 100.\n", + " mle_method : Literal[\"sigma\", \"sigmaxy\"], optional\n", + " The method used for MLE fitting. Default is \"sigmaxy\".\n", + " identification_progress_callback : callable or \"console\" or None\n", + " A callback for progress updates during identification. If\n", + " \"console\", progress will be printed to the console. If None,\n", + " progress is not reported. Default is None.\n", + " fit_progress_callback : callable or \"console\" or None\n", + " A callback for progress updates during fitting. If \"console\",\n", + " progress will be printed to the console. If None, progress is\n", + " not reported. Default is None.\n", + " return_info : bool, optional\n", + " Whether to return additional information about the fitting\n", + " process. Default is None, which is treated as False. If True,\n", + " a tuple of (locs, info) is returned.\n", "\n", " Returns\n", " -------\n", - " theta : lib.FloatArray2D\n", - " A 2D array with the optimized parameters for each spot. The\n", - " columns correspond to [x, y, photons, bg, sx, sy].\n", - "\n", - "\n", - "Spots fitted\n" + " locs : pd.DataFrame\n", + " Data frame containing the localized spots.\n", + " info : list[dict], optional\n", + " A list of dictionaries containing metadata about the movie and\n", + " the fitting process. Only returned if `return_info` is True.\n", + "\n" ] } ], "source": [ - "# here we will use 2D Gaussian least-squares fitting, note the structure of the returned array\n", - "help(gausslq.fit_spots)\n", - "theta = gausslq.fit_spots(spots)\n", - "print(f\"\\nSpots fitted\")" + "help(localize.localize)" ] }, { "cell_type": "code", - "execution_count": 9, - "id": "b78716d6-de0d-498e-aa19-0e55fe918ba2", + "execution_count": 11, + "id": "dd090701-30b6-422e-9a29-1ec19309af8a", "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Identifying spots: 100%|██████████| 5000/5000 [00:00<00:00, 16863.91frame/s]\n", + "Fitting...: 100%|██████████| 2399/2399 [00:00<00:00, 25390.33spot/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "These localizations can be saved with io.save_locs like above.\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, { "data": { "text/html": [ @@ -530,155 +728,98 @@ "4 0.017286 0.017727 0.027557 12072.339844 " ] }, - "execution_count": 9, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# convert to localizations\n", - "em = camera[\"Gain\"] > 1\n", - "locs = gausslq.locs_from_fits(identifications, theta, box, em)\n", + "params = {\n", + " \"Min. Net Gradient\": min_ng,\n", + " \"Box Size\": box,\n", + "}\n", + "locs, locs_info = localize.localize(movie, camera, params, threaded=False, identification_progress_callback=\"console\", fit_progress_callback=\"console\", return_info=True)\n", + "print(\"\\nThese localizations can be saved with io.save_locs like above.\")\n", "locs.head()" ] }, { - "cell_type": "code", - "execution_count": 10, - "id": "0dd86952-e141-46f8-ac4b-7f9f86a92719", + "cell_type": "markdown", + "id": "decfb7f1", "metadata": {}, - "outputs": [], "source": [ - "# save localizations with extra info\n", - "new_info = {\n", - " \"Generated by\": \"Picasso Sample Notebook Localize\",\n", - " \"Box size\": box,\n", - " \"Min net gradient\": min_ng,\n", - " \"Fit method\": \"LQ, Gaussian\",\n", - "}\n", - "info.append(new_info)\n", - "io.save_locs(path.replace(\".raw\", \"_locs.hdf5\"), locs, info)" + "# What do the columns mean?\n", + "The columns are explained here: https://picassosr.readthedocs.io/en/latest/files.html" ] }, { "cell_type": "markdown", - "id": "c69aad16-937c-447b-a286-65fd1982ae8c", + "id": "9da48a73", "metadata": {}, "source": [ - "## Alternatively, identification and fitting can be combined (as of v0.9.1 only MLE fitting is supported):" + "## 3D fitting" ] }, { - "cell_type": "code", - "execution_count": 11, - "id": "4f7e1383-bf2d-43d6-8a80-a3b8066e0d5b", + "cell_type": "markdown", + "id": "cc06a1db", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Help on function localize in module picasso.localize:\n", - "\n", - "localize(\n", - " movie: lib.IntArray3D,\n", - " camera_info: dict,\n", - " parameters: dict,\n", - " *,\n", - " roi: tuple[tuple[int, int], tuple[int, int]] | None = None,\n", - " frame_bounds: tuple[int, int] | None = None,\n", - " movie_info: list[dict] | None = None,\n", - " fitting_method: Literal['gausslq', 'gausslq-gpu', 'gaussmle', 'avg'] = 'gausslq',\n", - " eps: float = 0.001,\n", - " max_it: int = 100,\n", - " mle_method: Literal['sigma', 'sigmaxy'] = 'sigmaxy',\n", - " threaded: bool = True,\n", - " identification_progress_callback: Callable[[int], None] | Literal['console'] | None = None,\n", - " fit_progress_callback: Callable[[int], None] | Literal['console'] | None = None,\n", - " return_info: bool = None\n", - ") -> pd.DataFrame | tuple[pd.DataFrame, list[dict]]\n", - " Localize (i.e., identify and fit) spots in 2D in a movie using\n", - " the specified parameters.\n", - "\n", - " Since v0.10.0: support for frame bounds and ROI for identification +\n", - " all fitting methods.\n", - "\n", - " Parameters\n", - " ----------\n", - " movie : lib.IntArray3D\n", - " The input movie data as a 3D numpy array.\n", - " camera_info : dict\n", - " A dictionary containing camera information such as\n", - " `Baseline`, `Sensitivity`, and `Gain`.\n", - " parameters : dict\n", - " A dictionary containing localization parameters, including:\n", - " - `Min. Net Gradient`: Minimum net gradient for spot\n", - " identification.\n", - " - `Box Size`: Size of the box to cut out around each spot.\n", - " threaded : bool, optional\n", - " Whether to use multithreading/multiprocessing. Default is True.\n", - " movie_info : list[dict], optional\n", - " Movie metadata. If None, an empty list is used. Default is None.\n", - " roi : tuple, optional\n", - " Region of interest (ROI) defined as a tuple of two tuples,\n", - " where the first tuple contains the start coordinates\n", - " (y_start, x_start) and the second tuple contains the end\n", - " coordinates (y_end, x_end). If None, the entire frame is used.\n", - " Default is None.\n", - " frame_bounds : tuple, optional\n", - " Minimum and maximum frame numbers to consider for the\n", - " identification. If None, all frames are used. Default is None.\n", - " fitting_method : {\"gausslq\", \"gausslq-gpu\", \"gaussmle\" or \"avg\"}, optional\n", - " Which 2D fitting algorithm to use. Default is \"gausslq\".\n", - " eps : float, optional\n", - " The convergence criterion for MLE fitting. Default is 0.001.\n", - " max_it : int, optional\n", - " The maximum number of iterations for MLE fitting. Default is\n", - " 100.\n", - " mle_method : Literal[\"sigma\", \"sigmaxy\"], optional\n", - " The method used for MLE fitting. Default is \"sigmaxy\".\n", - " identification_progress_callback : callable or \"console\" or None\n", - " A callback for progress updates during identification. If\n", - " \"console\", progress will be printed to the console. If None,\n", - " progress is not reported. Default is None.\n", - " fit_progress_callback : callable or \"console\" or None\n", - " A callback for progress updates during fitting. If \"console\",\n", - " progress will be printed to the console. If None, progress is\n", - " not reported. Default is None.\n", - " return_info : bool, optional\n", - " Whether to return additional information about the fitting\n", - " process. Default is None, which is treated as False. If True,\n", - " a tuple of (locs, info) is returned.\n", - "\n", - " Returns\n", - " -------\n", - " locs : pd.DataFrame\n", - " Data frame containing the localized spots.\n", - " info : list[dict], optional\n", - " A list of dictionaries containing metadata about the movie and\n", - " the fitting process. Only returned if `return_info` is True.\n", - "\n" - ] - } - ], "source": [ - "help(localize.localize)" + "### Calibration\n", + "The default method for Picasso 3D localization is via astigmatic imaging (cylindrical lens). This requires a calibration measurement where the PSF width and height are measured as z position is varied. See https://www.science.org/doi/10.1126/science.1153529 for details. The calibration movie is a zstack acquisition where each frame corresponds to a different z position where images of several fluorescent beads are taken. We do not provide an example .tif file as this occupies too much space but the code below can be used to obtain the calibration.\n", + "\n", + "The resulting calibration (saved with extension .yaml) contains the X/Y coefficients of the 6th order polynomial used for fitting the calibration curve and some additional info. See https://picassosr.readthedocs.io/en/latest/localize.html#d-calibration." ] }, { "cell_type": "code", "execution_count": 12, - "id": "dd090701-30b6-422e-9a29-1ec19309af8a", + "id": "0ec558d6", + "metadata": {}, + "outputs": [], + "source": [ + "# # load movie\n", + "# calib_movie, calib_movie_info = io.load_movie(PATH_TO_CALIBRATION_MOVIE)\n", + "\n", + "# # localize\n", + "# params = {\n", + "# \"Min. Net Gradient\": min_ng,\n", + "# \"Box Size\": 13, # larger box size to include the entire spots that are out of focus\n", + "# }\n", + "# calib_locs, calib_locs_info = localize.localize(\n", + "# movie=calib_movie,\n", + "# camera_info=camera,\n", + "# **params,\n", + "# return_info=True,\n", + "# )\n", + "\n", + "# # calibrate (setting path saves the calibration already)\n", + "# zfit.calibrate_z(\n", + "# locs=calib_locs,\n", + "# info=calib_locs_info,\n", + "# d=5, # z step size in nm\n", + "# magnification_factor=0.79, # see Huang, et al. Science 2008 (link above) for details\n", + "# path=PATH_TO_SAVE_CALIBRATION_YAML,\n", + "# )" + ] + }, + { + "cell_type": "markdown", + "id": "a0230f8d", + "metadata": {}, + "source": [ + "### 3D fitting\n", + "There are two ways to fit z coordinates. Either `picasso.localize.localize_3D` which takes in the raw movie, runs identification, 2D fitting and 3D fitting or `picasso.zfit.zfit` which runs only the last step.\n", + "\n", + "#### Note: the example movie used here simulated 2D data, the code below is only to represent the pipeline used for 3D fitting." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "18c1e846", "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "These localizations can be saved with io.save_locs like above.\n" - ] - }, { "data": { "text/html": [ @@ -711,6 +852,9 @@ " lpy\n", " ellipticity\n", " net_gradient\n", + " z\n", + " d_zcalib\n", + " lpz\n", " \n", " \n", " \n", @@ -727,6 +871,9 @@ " 0.024666\n", " 0.013855\n", " 6020.876465\n", + " -5.360920\n", + " 0.264343\n", + " 9.241731\n", " \n", " \n", " 1\n", @@ -741,6 +888,9 @@ " 0.025510\n", " 0.046506\n", " 5863.750000\n", + " 1.509111\n", + " 0.238180\n", + " 9.391612\n", " \n", " \n", " 2\n", @@ -755,6 +905,9 @@ " 0.025361\n", " 0.031594\n", " 6260.936523\n", + " -7.224281\n", + " 0.256004\n", + " 9.381765\n", " \n", " \n", " 3\n", @@ -769,6 +922,9 @@ " 0.026085\n", " 0.052251\n", " 5956.500488\n", + " -9.725312\n", + " 0.228956\n", + " 9.388224\n", " \n", " \n", " 4\n", @@ -783,6 +939,9 @@ " 0.017727\n", " 0.027557\n", " 12072.339844\n", + " -6.818561\n", + " 0.240040\n", + " 6.526498\n", " \n", " \n", "\n", @@ -796,33 +955,58 @@ "3 6 25.522266 23.429760 2828.268555 0.869465 0.917401 38.669426 \n", "4 15 8.463343 20.024418 5370.642090 0.866196 0.890742 36.802505 \n", "\n", - " lpx lpy ellipticity net_gradient \n", - "0 0.024360 0.024666 0.013855 6020.876465 \n", - "1 0.026631 0.025510 0.046506 5863.750000 \n", - "2 0.024640 0.025361 0.031594 6260.936523 \n", - "3 0.024848 0.026085 0.052251 5956.500488 \n", - "4 0.017286 0.017727 0.027557 12072.339844 " + " lpx lpy ellipticity net_gradient z d_zcalib lpz \n", + "0 0.024360 0.024666 0.013855 6020.876465 -5.360920 0.264343 9.241731 \n", + "1 0.026631 0.025510 0.046506 5863.750000 1.509111 0.238180 9.391612 \n", + "2 0.024640 0.025361 0.031594 6260.936523 -7.224281 0.256004 9.381765 \n", + "3 0.024848 0.026085 0.052251 5956.500488 -9.725312 0.228956 9.388224 \n", + "4 0.017286 0.017727 0.027557 12072.339844 -6.818561 0.240040 6.526498 " ] }, - "execution_count": 12, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "params = {\n", - " \"Min. Net Gradient\": min_ng,\n", - " \"Box Size\": box,\n", + "# here we use example calibration, you can load yours using: calibration = io.load_calibration(CALIBRATION_PATH)\n", + "calibration = {\n", + " \"X Coefficients\": [\n", + " -8.798738075777796e-18,\n", + " -1.2624724788669146e-14,\n", + " -3.2361967940486395e-12,\n", + " 3.739453946638915e-09,\n", + " 7.1480069644491725e-06,\n", + " 0.0018544484650548124,\n", + " 1.2257149771744922,\n", + " ],\n", + " \"Y Coefficients\": [\n", + " 1.9327784560673312e-17,\n", + " 2.1532799564334975e-14,\n", + " 3.990733230095126e-12,\n", + " -1.1887950870815448e-09,\n", + " 3.5206416130963054e-06,\n", + " -0.0016858542466850245,\n", + " 1.225839389454983,\n", + " ],\n", "}\n", - "locs = localize.localize(movie, camera, params, threaded=False)\n", - "print(\"\\nThese localizations can be saved with io.save_locs like above.\")\n", + "\n", + "locs, info = zfit.zfit(\n", + " locs=locs,\n", + " info=info,\n", + " calibration=calibration,\n", + " magnification_factor=0.79,\n", + " pixelsize=130,\n", + " fitting_method=\"gausslq\", # used for calculating axial localization precision\n", + ")\n", + "\n", "locs.head()" ] }, { "cell_type": "code", "execution_count": null, - "id": "0ec558d6", + "id": "fa034292", "metadata": {}, "outputs": [], "source": [] From b55f03d46aeb22c0f4261ace745d47eddb78b40b Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 28 May 2026 10:33:42 +0200 Subject: [PATCH 38/42] expand notebook 2 + small improvements --- changelog.md | 1 + docs/files.rst | 2 +- samples/sample_notebook_1_localize.ipynb | 5 +- .../sample_notebook_2_basic_analysis.ipynb | 510 +++++++++++------- samples/sample_notebook_3_clustering.ipynb | 15 +- 5 files changed, 327 insertions(+), 206 deletions(-) diff --git a/changelog.md b/changelog.md index c4fab1e8..fcf974d4 100644 --- a/changelog.md +++ b/changelog.md @@ -32,6 +32,7 @@ This patch adds a number of new, useful features rather than simply fixing the b - Fixed subcluster check plot when one of the two populations is empty (#667) - Fixed 2D fitting with console printout and no multiprocessing - Expanded the scope of sample notebooks +- Minor changes to documentation ## 0.10.0 diff --git a/docs/files.rst b/docs/files.rst index f5eda32c..09f3b1fa 100644 --- a/docs/files.rst +++ b/docs/files.rst @@ -73,6 +73,6 @@ YAML Metadata Files YAML files are document-oriented text files that can be opened and changed with any text editor. In Picasso, YAML files are used to store metadata of movie or localization files. Each localization HDF5 file must always be accompanied with a YAML file of the same filename, except for the extension, which is ``.yaml``. **Deleting this YAML metadata file will result in failure of the Picasso software!** -The metadata file must contain the keys: ``Width``, ``Height`` (size of the field of view in camera pixels), ``Frames`` (number of frames in the movie), and ``Pixelsize`` (camera pixel size in nm). Example files can be found `here `_ +The metadata file must contain the keys: ``Width``, ``Height`` (size of the field of view in camera pixels), ``Frames`` (number of frames in the movie), and ``Pixelsize`` (effective camera pixel size after magnification in nm). Example files can be found `here `_ Raw binary files (i.e., with extension ``.raw``) may be accompanied by a YAML metadata file to store data about the movie dimensions, etc. While the metadata file, in this case, is not required, it reduces the effort of typing in this metadata each time the movie is loaded with ``Picasso: Localize``. To generate such a YAML metadata file, load the raw movie into ``Picasso: Localize``, then enter all required information in the appearing dialog. Check the checkbox ``Save info to yaml file`` and click ok. The movie will be loaded and the metadata saved in a YAML file. This file will be detected the next time this raw movie is loaded, and the metadata does not need to be entered again. \ No newline at end of file diff --git a/samples/sample_notebook_1_localize.ipynb b/samples/sample_notebook_1_localize.ipynb index 780de3fa..2c9b5e65 100644 --- a/samples/sample_notebook_1_localize.ipynb +++ b/samples/sample_notebook_1_localize.ipynb @@ -12,7 +12,7 @@ "\n", "This notebook shows some basic interaction with the ``picasso`` library for localizing raw movies. It assumes to have a working picasso installation, for example, from PyPI (see: https://github.com/jungmannlab/picasso?tab=readme-ov-file#via-pypi).\n", "\n", - "The sample data was created using Picasso:Simulate." + "The sample data was created using Picasso: Simulate." ] }, { @@ -766,6 +766,9 @@ "metadata": {}, "source": [ "### Calibration\n", + "\n", + "Note: calibration is easier to run via GUI and it needs to be done only once for a given optical setup.\n", + "\n", "The default method for Picasso 3D localization is via astigmatic imaging (cylindrical lens). This requires a calibration measurement where the PSF width and height are measured as z position is varied. See https://www.science.org/doi/10.1126/science.1153529 for details. The calibration movie is a zstack acquisition where each frame corresponds to a different z position where images of several fluorescent beads are taken. We do not provide an example .tif file as this occupies too much space but the code below can be used to obtain the calibration.\n", "\n", "The resulting calibration (saved with extension .yaml) contains the X/Y coefficients of the 6th order polynomial used for fitting the calibration curve and some additional info. See https://picassosr.readthedocs.io/en/latest/localize.html#d-calibration." diff --git a/samples/sample_notebook_2_basic_analysis.ipynb b/samples/sample_notebook_2_basic_analysis.ipynb index dd4cce5a..170e1d8f 100644 --- a/samples/sample_notebook_2_basic_analysis.ipynb +++ b/samples/sample_notebook_2_basic_analysis.ipynb @@ -5,11 +5,13 @@ "metadata": {}, "source": [ "# Sample Notebook 2 for Picasso\n", - "This notebook shows some basic interaction with the ``picasso`` library. It assumes to have a working picasso installation, for example, from PyPI (see: https://github.com/jungmannlab/picasso?tab=readme-ov-file#via-pypi).\n", + "This notebook shows some basic interaction with the ``picasso`` library.\n", + "\n", + "We assume that you have a working Picasso installation, for example, from PyPI (see: https://github.com/jungmannlab/picasso?tab=readme-ov-file#via-pypi).\n", "\n", "As you will see below, many values, such as x and y coordinates are saved in the units of camera pixel size. For more details, see https://picassosr.readthedocs.io/en/latest/files.html#localization-hdf5-files.\n", "\n", - "The sample data was created using Picasso:Simulate." + "The sample data was created using Picasso: Simulate." ] }, { @@ -69,54 +71,54 @@ " \n", " 0\n", " 2\n", - " 25.577408\n", - " 23.390867\n", - " 2726.824707\n", - " 0.840655\n", - " 0.852466\n", - " 36.663101\n", - " 0.024360\n", - " 0.024666\n", - " 0.013855\n", + " 25.577694\n", + " 23.390852\n", + " 2726.332764\n", + " 0.840439\n", + " 0.852427\n", + " 36.674107\n", + " 0.024357\n", + " 0.024668\n", + " 0.014063\n", " 6020.876465\n", " \n", " \n", " 1\n", " 4\n", - " 25.570419\n", - " 23.395596\n", - " 2663.022461\n", - " 0.903010\n", - " 0.861014\n", - " 39.829990\n", - " 0.026631\n", - " 0.025510\n", - " 0.046506\n", + " 25.570528\n", + " 23.395626\n", + " 2662.613037\n", + " 0.902836\n", + " 0.860956\n", + " 39.837715\n", + " 0.026629\n", + " 0.025511\n", + " 0.046387\n", " 5863.750000\n", " \n", " \n", " 2\n", " 5\n", - " 25.622509\n", - " 23.387653\n", - " 2728.561523\n", - " 0.843685\n", - " 0.871210\n", - " 40.546947\n", - " 0.024640\n", - " 0.025361\n", - " 0.031594\n", + " 25.622553\n", + " 23.387604\n", + " 2728.365723\n", + " 0.843607\n", + " 0.871191\n", + " 40.550835\n", + " 0.024639\n", + " 0.025362\n", + " 0.031663\n", " 6260.936523\n", " \n", " \n", " 3\n", " 6\n", - " 25.522266\n", - " 23.429760\n", - " 2828.268555\n", - " 0.869465\n", - " 0.917401\n", - " 38.669426\n", + " 25.522259\n", + " 23.429758\n", + " 2828.362549\n", + " 0.869489\n", + " 0.917426\n", + " 38.667923\n", " 0.024848\n", " 0.026085\n", " 0.052251\n", @@ -125,15 +127,15 @@ " \n", " 4\n", " 15\n", - " 8.463343\n", - " 20.024418\n", - " 5370.642090\n", - " 0.866196\n", - " 0.890742\n", - " 36.802505\n", - " 0.017286\n", - " 0.017727\n", - " 0.027557\n", + " 8.463214\n", + " 20.024517\n", + " 5371.983887\n", + " 0.866434\n", + " 0.890847\n", + " 36.769318\n", + " 0.017287\n", + " 0.017726\n", + " 0.027405\n", " 12072.339844\n", " \n", " \n", @@ -142,18 +144,18 @@ ], "text/plain": [ " frame x y photons sx sy bg \\\n", - "0 2 25.577408 23.390867 2726.824707 0.840655 0.852466 36.663101 \n", - "1 4 25.570419 23.395596 2663.022461 0.903010 0.861014 39.829990 \n", - "2 5 25.622509 23.387653 2728.561523 0.843685 0.871210 40.546947 \n", - "3 6 25.522266 23.429760 2828.268555 0.869465 0.917401 38.669426 \n", - "4 15 8.463343 20.024418 5370.642090 0.866196 0.890742 36.802505 \n", + "0 2 25.577694 23.390852 2726.332764 0.840439 0.852427 36.674107 \n", + "1 4 25.570528 23.395626 2662.613037 0.902836 0.860956 39.837715 \n", + "2 5 25.622553 23.387604 2728.365723 0.843607 0.871191 40.550835 \n", + "3 6 25.522259 23.429758 2828.362549 0.869489 0.917426 38.667923 \n", + "4 15 8.463214 20.024517 5371.983887 0.866434 0.890847 36.769318 \n", "\n", " lpx lpy ellipticity net_gradient \n", - "0 0.024360 0.024666 0.013855 6020.876465 \n", - "1 0.026631 0.025510 0.046506 5863.750000 \n", - "2 0.024640 0.025361 0.031594 6260.936523 \n", + "0 0.024357 0.024668 0.014063 6020.876465 \n", + "1 0.026629 0.025511 0.046387 5863.750000 \n", + "2 0.024639 0.025362 0.031663 6260.936523 \n", "3 0.024848 0.026085 0.052251 5956.500488 \n", - "4 0.017286 0.017727 0.027557 12072.339844 " + "4 0.017287 0.017726 0.027405 12072.339844 " ] }, "execution_count": 1, @@ -165,7 +167,7 @@ "import os.path as ospath\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", - "from picasso import io, aim, postprocess, render\n", + "from picasso import io, lib, aim, postprocess, render\n", "path = 'data/raw_movie_locs.hdf5'\n", "locs, info = io.load_locs(path)\n", "\n", @@ -230,99 +232,99 @@ " \n", " mean\n", " 2400.694039\n", - " 14.210517\n", - " 16.662828\n", - " 5844.617676\n", - " 0.880052\n", - " 0.872100\n", - " 42.190269\n", - " 0.018858\n", - " 0.018733\n", - " 0.025217\n", + " 14.210602\n", + " 16.663191\n", + " 5840.870605\n", + " 0.878801\n", + " 0.871979\n", + " 42.291435\n", + " 0.018848\n", + " 0.018737\n", + " 0.024737\n", " 13409.862305\n", " \n", " \n", " std\n", " 1440.586301\n", - " 4.661452\n", - " 5.879241\n", - " 2967.100586\n", - " 0.103770\n", - " 0.079539\n", - " 12.318582\n", - " 0.005660\n", - " 0.005619\n", - " 0.048567\n", + " 4.661649\n", + " 5.879419\n", + " 2973.533936\n", + " 0.097646\n", + " 0.083435\n", + " 12.622068\n", + " 0.005650\n", + " 0.005617\n", + " 0.046073\n", " 6639.604980\n", " \n", " \n", " min\n", " 2.000000\n", - " 8.463343\n", - " 7.556455\n", - " 1444.641602\n", - " 0.742193\n", - " 0.728289\n", - " -42.423695\n", + " 8.463214\n", + " 7.556459\n", + " 1442.730225\n", + " 0.742131\n", + " 0.728386\n", + " -48.022243\n", " 0.008150\n", " 0.008407\n", - " 0.000005\n", + " 0.000007\n", " 3512.745850\n", " \n", " \n", " 25%\n", " 1129.000000\n", - " 11.279828\n", - " 8.468372\n", - " 3623.330200\n", - " 0.858390\n", - " 0.857402\n", - " 38.915630\n", + " 11.280156\n", + " 8.468384\n", + " 3623.458496\n", + " 0.858428\n", + " 0.857364\n", + " 38.915249\n", " 0.014714\n", - " 0.014609\n", - " 0.007652\n", + " 0.014608\n", + " 0.007649\n", " 8238.303223\n", " \n", " \n", " 50%\n", " 2260.000000\n", - " 13.567047\n", - " 19.033222\n", - " 5489.199707\n", - " 0.870371\n", - " 0.869745\n", - " 40.054169\n", + " 13.567043\n", + " 19.030941\n", + " 5472.675781\n", + " 0.870440\n", + " 0.869625\n", + " 40.058571\n", " 0.017381\n", " 0.017230\n", - " 0.015987\n", + " 0.016005\n", " 12530.303711\n", " \n", " \n", " 75%\n", " 3664.000000\n", - " 15.394628\n", - " 22.085923\n", - " 7498.452637\n", - " 0.881088\n", - " 0.881157\n", - " 41.237906\n", - " 0.021735\n", + " 15.394680\n", + " 22.085943\n", + " 7490.443604\n", + " 0.881051\n", + " 0.881045\n", + " 41.237020\n", + " 0.021720\n", " 0.021776\n", - " 0.028119\n", + " 0.028078\n", " 17443.564453\n", " \n", " \n", " max\n", " 4979.000000\n", - " 25.686373\n", - " 23.435787\n", - " 22818.808594\n", - " 2.804243\n", - " 2.882848\n", - " 196.916763\n", - " 0.038511\n", + " 25.686314\n", + " 23.435871\n", + " 22819.363281\n", + " 2.800622\n", + " 2.983101\n", + " 196.932922\n", + " 0.038396\n", " 0.037770\n", - " 0.664899\n", + " 0.664855\n", " 53586.023438\n", " \n", " \n", @@ -332,23 +334,23 @@ "text/plain": [ " frame x y photons sx \\\n", "count 2399.000000 2399.000000 2399.000000 2399.000000 2399.000000 \n", - "mean 2400.694039 14.210517 16.662828 5844.617676 0.880052 \n", - "std 1440.586301 4.661452 5.879241 2967.100586 0.103770 \n", - "min 2.000000 8.463343 7.556455 1444.641602 0.742193 \n", - "25% 1129.000000 11.279828 8.468372 3623.330200 0.858390 \n", - "50% 2260.000000 13.567047 19.033222 5489.199707 0.870371 \n", - "75% 3664.000000 15.394628 22.085923 7498.452637 0.881088 \n", - "max 4979.000000 25.686373 23.435787 22818.808594 2.804243 \n", + "mean 2400.694039 14.210602 16.663191 5840.870605 0.878801 \n", + "std 1440.586301 4.661649 5.879419 2973.533936 0.097646 \n", + "min 2.000000 8.463214 7.556459 1442.730225 0.742131 \n", + "25% 1129.000000 11.280156 8.468384 3623.458496 0.858428 \n", + "50% 2260.000000 13.567043 19.030941 5472.675781 0.870440 \n", + "75% 3664.000000 15.394680 22.085943 7490.443604 0.881051 \n", + "max 4979.000000 25.686314 23.435871 22819.363281 2.800622 \n", "\n", " sy bg lpx lpy ellipticity \\\n", "count 2399.000000 2399.000000 2399.000000 2399.000000 2399.000000 \n", - "mean 0.872100 42.190269 0.018858 0.018733 0.025217 \n", - "std 0.079539 12.318582 0.005660 0.005619 0.048567 \n", - "min 0.728289 -42.423695 0.008150 0.008407 0.000005 \n", - "25% 0.857402 38.915630 0.014714 0.014609 0.007652 \n", - "50% 0.869745 40.054169 0.017381 0.017230 0.015987 \n", - "75% 0.881157 41.237906 0.021735 0.021776 0.028119 \n", - "max 2.882848 196.916763 0.038511 0.037770 0.664899 \n", + "mean 0.871979 42.291435 0.018848 0.018737 0.024737 \n", + "std 0.083435 12.622068 0.005650 0.005617 0.046073 \n", + "min 0.728386 -48.022243 0.008150 0.008407 0.000007 \n", + "25% 0.857364 38.915249 0.014714 0.014608 0.007649 \n", + "50% 0.869625 40.058571 0.017381 0.017230 0.016005 \n", + "75% 0.881045 41.237020 0.021720 0.021776 0.028078 \n", + "max 2.983101 196.932922 0.038396 0.037770 0.664855 \n", "\n", " net_gradient \n", "count 2399.000000 \n", @@ -378,7 +380,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAj8AAAHMCAYAAAA6QskdAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAPjlJREFUeJzt3Qd0U/X///F3oS0UKrOFgrLKUECmA2V8QZaAdeBCEb4oggPEdfj7daACMqxfB86fCwQUZSmCCg4cCKKigsrwCyij0MFQhuyO/M/747kxKWmb1Da5t/f5OCe0yb1JPrmfJnnxWTfK4/F4BAAAwCXKRboAAAAA4UT4AQAArkL4AQAArkL4AQAArkL4AQAArkL4AQAArkL4AQAArkL4AQAArkL4AQAArkL4ARxi27ZtEhUVJddff32ki2JLelz0+OhxstMx0+fv1q2b321jx441t3/xxRcRK5cdjg0QKYQfIIL0y8f3Ur58eUlISJDu3bvLm2++Gfby8IXoT8OJ1ouGFScKFLwAiERzEIDIe/jhh83P7Oxs+d///icLFy6Uzz//XL7//nt58sknI108xzr11FPll19+kapVq0asDPr8lSpVEruxw7EBIoXwA9hA/paFTz/9VHr16iVTpkyR22+/XRo2bBixsjlZTEyMnHHGGREtQ6Sf387HBogUur0AG+rRo4f5YvJ4PPLdd98F7J665pprTBdZxYoV5eyzz5b3338/4GMdP35cHn30UWnVqpVpgahSpYp06dJF5s6de1IAa9Sokfl9xowZft1x06dP9+6Xl5cnL774opxzzjkSHx8vlStXNr//3//9n9lWUNfL3r175aabbpI6depIhQoVpGXLlvLaa6+FfGyWLl1qyq/PW6NGDbnssstMa1ko3Xi7du2S0aNHy+mnn24ep1q1auZ33W/Lli1mH/39ggsuML+PGzfO73hYY3X0uFjH58MPPzSvU1tS9Lb8r78geqzbtWsncXFxUqtWLRk6dKhkZWWdtJ8G4IJCcP4xRFa51LJly/zKbgXtwro4MzMzZeTIkeb5YmNjJTExUS6//HL54YcfTtrX9xhoa6W+1lNOOcX8nV100UWmdQmwG1p+AJvS4KN8v0jV9u3b5dxzz5Xk5GQZPHiw/PHHHzJnzhy59NJLTTCwvrDViRMn5MILLzRfgBqm9AvtyJEjMn/+fBkwYID8+OOPMmnSJLOvfmnt379fnn76aWnTpo0JFZa2bdt6f9fn1PFI9erVk2HDhpnyLViwQEaMGCErVqyQWbNmnfRa9HE7depkvkivvPJKE8jmzZtnvujLlSsnQ4YMCeqYWOXWx9GfGqT0Oc8//3xp3bp1UI+hr1/L8ttvv5nWtYsvvtgcaz2u2t2o5dNja71+DSddu3b1CzD5Q4iWS8NP37595ZZbbjGPFYynnnpKPv74Y/Na+vTpY16LBkINMd9++60JHcWh9aVdqRraGjRo4BdwihoDtHXrVuncubNkZGSYsWfXXnut7Nixw9TXBx98IG+//bakpKScdD8N33r8rGOwYcMGWbx4sQnv+rsGdcA2PAAiRt+Cgd6Gn3zyiScqKspctm3bZm7bunWrd/+xY8f67f/hhx+a2/v27et3+6RJk7y3Z2dne2/ftWuXp0GDBmbbV1995b3deo4hQ4YELO+bb75ptrdr187z559/em8/dOiQ56yzzjLbZs2aFfA13njjjZ6cnBzv7evXr/eUL1/e07x586COlT5fjRo1PNHR0Z7vvvvOb9udd97pfR59DYW9nkWLFpnb9D75HT9+3HPw4EHv9c8//9zs+/DDDwcs02uvvWa2az0tWbIk4D66vWvXrn636ePp7TExMZ7Vq1cHfC1Dhw71u13rSy+BWI+n5S3quYuq6969e5vbJ0yY4He7/p1ofWkd+Na9dQx029KlS/3uc++995ptqampAcsARArdXoANaFeEXh544AHT8qCtAPrddeedd5r/ufvS62PGjPG7TVt36tevL6tWrfK7fdq0aaZlRgdNR0f/3dCr3SsPPvig+f3VV18Nupz6eEq70bTLy6JdR6mpqQU+nna3aRl0NpulRYsWpgVGu0UOHTpU5HNrq4K2cg0cONB08/nSYxfqwF3tZspPW5S0yyZU2uqmdRYqbUXTLq9Ar0Vb17SFLJx27txpWqL0b+mee+7x29axY0fTCqR18M4775x0X+2G1e5aX9rNqfL/XQKRRvgBbEC7J/QyefJk+eyzz8yYltdffz3gTC/t0vANERbthtq3b5/3+p9//im//vqr1K1bN+DAVu3SUGvWrAm6nKtXrzbdVIG6TrRrSMsV6PGaNm1qxoAEKrPyLXdhz209T34aFny75gqj99eZThrgNLA888wzZixLbm6uFJd2QxZHYa/l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qZI0adJENm3aZMKP/tTnt4KPatWqlen++vXXX/260AAAgDsVO/yE+5xdGnxU1apV/W7X69Y2/alT7H2VL19e4uPjvfsEkp2dbS4WDUtxcXHe3wE7ssPfplUGO5QFgVFHzkA9lZEBz06yYMECv268Ro0aSWpqqiQmJkppzIYLRXG+VEK9j9P3D8dz2G1//TvyHegfaUlJSZEuAopAHTkD9eSQ8KMztFavXi179uwx1zUwtG/fXlq0aCElqVq1aubngQMHpHr16t7b9boOkrb2OXjwoN/9cnNzzQww6/6B9O/fX1JSUk76ItLXpOsWlaS/Hi8m6P11rFOoQr2P0/cPx3PYbX/9O8rMzJRI0/eKflhnZWUVq95Q+qgjZ6CeSob+xzCYhotihx/98J0yZYoZbGyNv1FHjhyR9957z4yvueOOO0Ju6ShIrVq1TIBZu3atN+zoc+lYnt69e5vrzZo1MwOut2zZIsnJf50qYN26deZDWccGFSQmJsZcAuEDHXZlp79NLYudyoOTUUfOQD05YMCzBp+LL77YtJr4tsxo+NGLdiVdc801QT/msWPHzP8gfQc5b9u2zYzZSUhIkH79+sk777xjmvs1DM2ePdu0Almzv0477TRp27atmdquA641oOmaPx07dmSmFwAA+GfhZ8WKFdK1a1cZNGjQSQOQ9TYNQcuXLw8p/Pz2228ybtw47/WZM2ean/o8I0eONFPndS0gDTfa6qOLHN5///0SGxvrvc/tt99uVpYeP368d5FDne4OAADwj8KPzp4qrCupadOmsnLlypAes2XLlmYF54JomBkwYIC5FERbibS7DQAAoERXeNYFA3Wwc0F0G4sKAgCAMhN+tCvq66+/lpdfflkyMjIkLy/PXPT3V155xWzr1q1byZYWAAAgUt1eepqJXbt2mZOM6qVcub9ylAYgKxzpFHIAAIAyEX407OggZJ3ptWbNGr91fvT8XHpWdwAAAEeHHz17+vTp06VevXrSt29fc5uGnPxBR09A+sknn8j1119fYuv8APhbWnq6DOnTK6RDUqV2kjw743UOIwDXCymZLF26VJYtWyZPPvlkofvpCs+zZs2S+vXrexcgBFByYj15Mq3eX+efC9bQHX+voQUAbhbSgGcdxKzr5uhZ2wujy92fd9558tVXX/3T8gEAAEQu/KSlpZmFBYNx+umny/bt24tbLgAAgMiHHz1dRLBjeHS/7Ozs4pYLAACgVIQUfnTRQm39CYbuxyKHAADA0eGnVatW8uWXX5rzdhVGt+t+uj8AAIBjw4+eWFS7svSkoZs3bw64j96u23W/Sy65pKTKCQAAEP6p7jrL66677pKnn35axowZY67rdPaKFSvKsWPHZMeOHZKVlSUVKlQwJxfVWV8AAAB2EvIKhLqGz3//+19ZuHChrF69Wr777jvvturVq0uPHj1MC1FR0+EBAAAioVjLL9eqVUuGDx9ufj969Ki5xMXFmQsAAICd/eNzTxB6AABAmR3wDAAA4HSEHwAA4CqEHwAA4CqEHwAA4Cr/eMAzAGdIS0+XIX16Bb1/ldpJ8uyM10u1TAAQCYQfwCViPXkyrV7wy1EM3ZFVquUBgEih2wsAALgK4QcAALgK4QcAALgK4QcAALgK4QcAALgK4QcAALgK4QcAALgK4QcAALgK4QcAALgK4QcAALgK4QcAALiK487tNXLkSNmzZ89Jt/fu3VuGDRsmY8eOlQ0bNvht69mzp9x0001hLCUAALArx4WfyZMnS15envd6WlqaTJgwQc4//3zvbT169JABAwZ4r8fGxoa9nAAAwJ4cF36qVKnid/3dd9+V2rVrS4sWLby3VahQQapVqxaB0gEAALtzXPjxlZOTI8uXL5eLLrpIoqKivLfrbXrRAHTWWWfJFVdcYQIRAACAo8PPqlWr5PDhw9KtWzfvbZ07d5aEhASpUaOGbN++XWbNmiUZGRkyevToAh8nOzvbXCwapOLi4ry/A25V1N+/tZ33iX1RR85APYWXo8PP559/Lm3btjVBx3dws6V+/fpSvXp1GT9+vGRlZUlSUlLAx1mwYIHMnz/fe71Ro0aSmpoqiYmJJV7m6OjQDnlxvlRCvY/T9w/Hczh9/+LcR/9W69SpE9S+Bb23YB/UkTNQT+Hh2PCjM75+/vnnQlt0VJMmTczPwsJP//79JSUl5aQvCX0O7VorSX89XkzQ+3s8npCfI9T7OH3/cDyH0/cvzn30bzUzM7PQffS9ou8rfX8Vp0wofdSRM1BPJUP/0xZMw0W0k1t9qlatKu3bty90v23btpmf2gJUkJiYGHMJhA90uFmwf/+6H+8Ve6OOnIF6Cg9Hhh+d6v7FF19I165dpXz58t7b9X+fK1asMIEoPj7eTIOfMWOGNG/eXBo0aBDRMgMAAHtwZPhZu3at7N27Vy644IKTmrt02+LFi+X48eNSs2ZN6dChg1x++eURKysAALAXR4afNm3ayNy5c0+6XWd5jRs3LiJlAgAAzsC5vQAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKsQfgAAgKtER7oAAOwpLT1dhvTpVeR+0dHRkpOTI1VqJ8mzM14PS9kA4J8g/AAIKNaTJ9PqxQV5dGJk6I4sjiQAR6DbCwAAuArhBwAAuArhBwAAuArhBwAAuArhBwAAuArhBwAAuIqjprrPnTtX5s+f73db3bp1ZcqUKeb3EydOyMyZM2XlypWSnZ0tbdq0kWHDhkm1atUiVGIAAGA3jgo/ql69evLggw96r5cr93fj1YwZM2T16tVy9913S6VKlWTq1KnyxBNPyCOPPBKh0gIAALtxXLeXhh1tybEuVapUMbcfOXJEPvvsMxkyZIiceeaZkpycLCNGjJCNGzfKpk2bIl1sAABgE45r+cnKypKbb75ZYmJipFmzZjJw4EBJSEiQLVu2SG5urrRq1cq776mnnmq2afjRfQEAABwVfpo2bWpac3Scz759+8z4n4ceesh0be3fv9+cY6hy5cp+96latarZVhgdH6QXS1RUlMTFxXl/BxAc3i/2rRPqxt6op/ByVPhp166d9/cGDRp4w9DXX38tsbGxxX7cBQsW+A2kbtSokaSmpkpiYqKUNA1ooSjOB1ao93H6/uF4DqfvH47n0L/tOnXqBL3/wJR+si8jPej9q9c9Vd58f3FIZcLfkpKSOBwOQD2Fh6PCT37ayqOtQNoV1rp1a3Nm6cOHD/u1/hw4cKDI2V79+/eXlJSUkz709+zZYx6zJP31eDFB7+/xeEJ+jlDv4/T9w/EcTt8/HM+hf9uZmZlB778nbXsIJ04VGZq2PaTHx9+fZ/qFqp+Txfm7QXhQTyVD/xMWTMOFo8PPsWPHzBu6S5cuZoBz+fLlZe3atXLeeeeZ7RkZGbJ3794ix/vo+CG9BMKHBRC80n6/8H78Z8eO42d/1FN4OCr86Bo+Z599thnErGN+dN0fnf3VuXNnM7W9e/fuZp/4+Hhzfdq0aSb4MNgZAAA4Mvz88ccf8vTTT8uff/5pprifccYZMnHiRO90d53mrk2HOgBam+CtRQ4BAAAcGX7uvPPOQrfroGcNOwQeIPzS0tNlSJ9eQe+/OytTpF5yqZYJABwffgDYV6wnL6QBzP12luxkAgAosys8AwAA/BOEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CqEHwAA4CrRkS4AAAQjLT1dhvTpFdLBqlI7SZ6d8ToHGIBzw8+CBQtk1apVkp6eLrGxsdKsWTMZNGiQ1K1b17vP2LFjZcOGDX7369mzp9x0000RKDGAkhLryZNp9eJCus/QHVlUAABnhx8NNRdeeKE0btxYcnNz5a233pIJEybIk08+KRUrVvTu16NHDxkwYID3ugYlAAAAx4WfBx54wO/6yJEjZdiwYbJlyxZp0aKF9/YKFSpItWrVIlBCAABgd44KP/kdOXLE/IyPj/e7ffny5eaiAeiss86SK664wgQiAAAAx4afvLw8mT59upx++ulSv3597+2dO3eWhIQEqVGjhmzfvl1mzZolGRkZMnr06AIfKzs721wsUVFREhcX5/0dgHO5/T1svX63Hwe7o57Cy7HhZ+rUqbJjxw4ZP378SYObLRqKqlevbvbJysqSpKSkAgdSz58/33u9UaNGkpqaKomJiSVe7ujo0A55cT6wQr2P0/cPx3M4ff9wPIfd9rfeb3Xq1An5fmVRQZ9/sBfqKTyinRp8Vq9eLePGjZOaNWsWum+TJk3Mz8LCT//+/SUlJeWkD9k9e/ZITk5OiZb9r8eLCXp/j8cT8nOEeh+n7x+O53D6/uF4Drvtb73fMjMzg97/tn8PloO7gt+/Su068txMe0+l188z/ezTz8DiHEOEB/VUMvQ/PME0XDgq/Ogbd9q0aWa6u05pr1WrVpH32bZtm/mpLUAFiYmJMZeCnhOAc4XyHtbgE8p0+qE7Mh3zGaHldEpZ3Yx6Co9op7X4rFixQu655x4zJmf//v3m9kqVKpnp7Po/G93evn17Mwg6LS1NZsyYIc2bN5cGDRpEuvgAAMAGHBV+Pv74Y/NTW318jRgxQrp162aau9auXSuLFy+W48ePmy6xDh06yOWXXx6hEgMAALtxVPiZO3duodt1lpeOAwIAACgIJzYFAACuQvgBAACuQvgBAACuQvgBAACuQvgBAACuQvgBAACuQvgBAACuQvgBAACu4qhFDgEgFGnp6TKkT6+g99+dlSlSL5mDDJRxhB8AZVasJy+kE5X225lTquUBYA90ewEAAFch/AAAAFch/AAAAFch/AAAAFch/AAAAFch/AAAAFch/AAAAFch/AAAAFch/AAAAFch/AAAAFch/AAAAFch/AAAAFch/AAAAFch/AAAAFeJjnQBAMCp0tLTZUifXkHvX6V2kjw74/VSLROAohF+AKCYYj15Mq1eXND7D92RxbEGbIBuLwAA4Cq0/ABAmNBNBtgD4QcAwoRuMsAeCD8AgFIzashgObgr+LFODApHOBB+AAClRoMPg8JhN4QfAIBrWpYUrUslz2ktfIQfALApBkiXfMuSYsmBkue0Fr4yG34+/PBDee+992T//v3SoEEDGTp0qDRp0iTSxQKAoDFAGigdZTL8rFy5UmbOnCnDhw+Xpk2bygcffCATJ06UKVOmSNWqVSNdPACwDad1VwAloUyGn/fff1969OghF1xwgbmuIWj16tXy+eefy2WXXRbp4gGAbTituwIoCWUu/OTk5MiWLVv8Qk65cuWkVatWsmnTpoiWDQDcNg5pd1amSL1ksROnj6UqziDvjL17pW5CQqntv9uG9eyq8HPw4EHJy8uTatWq+d2u1zMyMgLeJzs721wsUVFREhcXJ9HRJX94mjRvLjG1KwS9f/PDcRLTuH5IzxHqfZy+vx3LZLf97VgmXnPJH6Mm8cclJibG7zb9PFN6u8fj+cefSW0Ox8kzrYMv0+0/1/jHr6HQ/UMsf3Few7hdoZWpOIqqJ1+1alSX55snhfT4t/+cFmK9pdmqnoMV7Pd2lKeoo+wwf/zxh9xyyy0yYcIEadasmff2N954QzZs2CCTJk066T5z586V+fPne6936tRJ7rjjjrCVGQAAhE+ZO7FplSpVTDeXzvLypdfztwZZ+vfvL9OnT/dedIyQb0sQwu/o0aPyn//8x/yEfVFP9kcdOQP1FF5lLvxok1dycrKsW7fOe5t2g+l135YgX9r0VqlSJb9LaTdxonDaILl169Yim38RWdST/VFHzkA9hVeZG/OjUlJS5PnnnzchSNf2Wbx4sRw/fly6desW6aIBAIAIK5Php2PHjmbgs47l0e6uhg0byv33319gtxcAAHCPMhl+VJ8+fcwFzqTdjldeeSXdjzZHPdkfdeQM1FN4lbnZXgAAAK4a8AwAAFAYwg8AAHAVwg8AAHAVwg8AAHCVMjvbC5GX/7Qhqm7dujJlyhTz+4kTJ2TmzJmycuVKs6J2mzZtZNiwYX5LEuzdu1deeeUVWb9+vVSsWFG6du0qAwcOlPLly3v30W36ODt27JCaNWvKFVdcwZpOBdBTvCxatMgsILlv3z4ZPXq0nHvuud7tOv9B6+3TTz+Vw4cPyxlnnGHqpE6dOt59Dh06JNOmTZMffvjBnI+oQ4cOcsMNN5j6sWzfvl2mTp0qv/32m1l1XWdeXnrppX5l+frrr2XOnDmyZ88eSUpKkuuuu07at29f/D84F9WTrmO2bNkyv/vo++eBBx7wXqeeSteCBQtk1apVkp6eLrGxsWYR3UGDBpnPOEs4P+M+/PBDee+998zyLg0aNJChQ4eade4QGC0/KFX16tWTl19+2XsZP368d9uMGTPMF+jdd98t48aNMx/yTzzxhN/K3JMnT5acnBxzrraRI0fKF198Yb4wLbt375ZHH31UWrZsKY899phcdNFF8uKLL8qPP/5IzQagi33qulc33nhjwOOzcOFCWbJkiTnFi54Hr0KFCjJx4kTzIW555plnzIfwmDFj5N5775VffvlFXnrpJe/2I0eOmPpKSEgwdaNfCPPmzZOlS5d699m4caM8/fTT0r17d0lNTZVzzjlH/vvf/0paWhr1FkQ9qbZt2/q9t/Kfj5B6Kv2AeuGFF5r3h74XcnNzzd/9sWPHwv4Zp+FKw5EuD6LvJw0/Wq4DBw6U8lFwMJ3qDpSGOXPmeEaPHh1w2+HDhz3XXHON5+uvv/betnPnTs9VV13l2bhxo7m+evVqz9VXX+3Zt2+fd5+PPvrI8+9//9uTnZ1trr/++uueu+++2++xn3rqKc+ECROo1CLosf7222+91/Py8jzDhw/3LFy40K+eBg4c6FmxYoW5vmPHDnO/X3/91bvPmjVrTD39/vvv3jq6/vrrvXWk3njjDc8dd9zhvf7kk096Jk+e7Fee+++/3/PSSy9Rb0XUk3ruuec8qampBR4r6in8Dhw4YOpq/fr1Yf+Mu++++zyvvvqq93pubq7npptu8ixYsKAUX7Gz0fKDUpWVlSU333yz3HbbbeZ/otrEq7Zs2WL+p9SqVSvvvqeeeqppLdi0aZO5rj/r16/v10Ss/9vVEwBqy4PavHmz32MobVq2HgPB0/9hapN569atvbfpee606dy3TipXriyNGzf27qPHX7u/fv31V+8+zZs3N+fZ862TjIwM0xVj7ROo3rQ+EXzLg3ahaIuPdpv8+eef3m3UU/hpi6eKj48P62ecthrpc/nuoyf31ut8DhaMMT8oNU2bNpURI0aYPnBt7tXxPw899JBp9tUvWf1y1C9SX1WrVjXblP7Mf0oS3W5ts35at/nuox8e2lWjffEIjnVMAx1P3+OtY3h86dgE/cD33adWrVp++1j1qNusfQt7HhROvyB1rJUeZ/0PxltvvWW6KbWrQ7/4qKfw0u6r6dOny+mnn27CjArXZ5z+h0KfP//j6HX9DwcCI/yg1LRr1877u/ZBW2FIB7oSSoDi69Spk/d3/bLV99eoUaPMwNj8rQQofTq4X1tqfMc0wt7o9kLY6P+AtBVI/6eq/yvR5lqdUeRLB+hZ/4PRn/lbAqwBfL775B/Up9fj4uIIWCGyjmmg4+l7vPWkwb60aV//91lYvVnXi6o3Tj5cPLVr15ZTTjnFvLeop/AHn9WrV8vDDz9sZmJZwvUZpy2xVmufr0CtSvgb4Qdho7MgrOCTnJxsukvWrl3r3a5NtDomSKeMKv2ps3983/g///yzedOfdtpp5rq2Jvk+hrWP9RgInnahaN34Hk8dx6BjeXzrRD/MdYyBZd26dWaKvDWtVvfRGWD6we9bJxp8rfEQuk+getP6ROh+//13E0CrV69OPYWJ/s1r8NHp7tqdn7+rN1yfcdq1ps+l70OLdoPpdT4HC0b4QanRqZc6KFMH0urUZp3KrP9D6dy5sxlIq9OcdR99k+qX6QsvvGDerNYbVgf16QfAc889J9u2bTNTO2fPnm2ml+oZkFXv3r3N47/xxhtmvY2PPvrIdKvpdFAEDqB6LPWi9Njp7/qBrIOW+/XrJ++88458//335kNZj71+oepUdKX1oeNNdGq7hqL//e9/Zs2fjh07So0aNcw+Wr/6gazTcbUrQKfh6vT5lJQUbzn0eX766SezLonWm64tpGsC6XpAKLyedNvrr79uBrPq7frFqFOgda0kfc9QT+GhwWf58uVmwLmGFW1p0Yu1LEQ4P+P0vaVrc+k0+Z07d8qrr75qlkvIvxYQ/sZZ3VFqdDFDbQHQWSjaNKsL5l1zzTXmQ9p3AbCvvvrKtBIEWgBMF8DTN7KOZdA1Z3QBMF0ML/8CYLqehr7pWeSwcHqsdL2R/PS46hoj1iKHuiaPtvponelaM74Lt2kLg37w+y5yqAuqFbTIoXbHaKi57LLL/J5TP8D1g17rWBdRZJHD4OpJ12DS/0joAojaCqehU2foDRgwwO+9Qz2Vrquvvjrg7Tqu0Qod4fyM00UOdWFMDWC6RpQuPEpLasEIPwAAwFXo9gIAAK5C+AEAAK5C+AEAAK5C+AEAAK5C+AEAAK5C+AEAAK5C+AEAAK5C+AFgS7parS4kpwslAkBJ4qzuACISbHSpf4su5Z+QkGBWKtbVa0vrhIwrVqww51Hi9CeAuxF+AESMtuzoCSGzs7PNecI+/vhjWbNmjTzxxBOlFn70fGOEH8DdCD8AIqZdu3bSuHFj83uPHj3MecDef/99+e6776gVAKWG8APANs4880wTfvQs1tZZ4rVVSE/q+OWXX5oTRWrX2M0332xOlutLz3atl6ysLBOi9Ez01157rVSuXNlsHzt2rGzYsMHvpJSJiYny/PPPm9+1O+zNN9+U1atXm5O66slctYXI9wSSWq7bbrtNBg0aZM7avXDhQvn999+lQYMG5gSwTZo08e6rJ5jUx/v555/l4MGDEh8fb7Zff/31prULQOQQfgDYhgYXpeHF8tprr5kAc9VVV5nwsXjxYnPG+Lvuusu7j56Jfv78+dKqVSvp3bu3ZGRkmC40HSz9yCOPSHR0tFx++eUm1GhYGTJkiLmfdSZ6DVUajvT59Qz0Gk6++eYbMy5J79OvXz+/cupZuo8ePSo9e/Y0Z7bXEKRddc8++6x5LqXXtYvNejwNVxqE9u7dS/gBIozwAyBiNFhoq4i27mzcuFHefvttiY2NlbPOOssEBaUtJmPGjDEhQ3k8HlmyZIm5r7a+6P3fffddadOmjdx3331Srtxfk1i15WbatGmyfPlyueCCC0yLkbYmHT58WP71r3/5lWPp0qWSnp4uo0aNki5dupjbevXqZQLR7Nmzzf3j4uK8+2uAefrpp03ZrOd67LHH5KeffjJl1+fQ16MtRJdccon3fv379w/DUQVQFKa6A4gYbZUZNmyY3HrrrTJlyhTTEjN69Ghvl5eyWlcszZs3l7y8PNmzZ4+5riEpJyfHtM5Ywce6nwYW7cYqig6y1hlmnTp18t6mLTh9+/aVY8eOebvLLOeff743+KgzzjjD/Ny1a5f5qQFO76/3O3ToUDGPDoDSQssPgIjRcTJ16tSR8uXLS9WqVU0Lim+AUToF3pc1hkdbV6xWGKX39aXho3bt2t7thdEgpeXI/9ynnnqqd3thZbKCkFUmnbp/3XXXycyZM2X48OHSrFkzad++vXTt2rXUpvEDCB7hB0DE6ABga7ZXQfIHEot2f0VKMGXSwdLaBaYz17Q7bM6cOaZ77qGHHpJGjRqFsbQA8qPbC4CjWa0wOsjZl3aF6QDp/K00geisr8zMTNOd5st6TN1eHElJSXLxxRebMUs6AFrL9N577xXrsQCUHMIPAEfTgczaxaWDoH1bXj777DMzKFq7myw6pkhvC7TekE5NX7lypfe23Nxc85h6nxYtWoRUpuPHj5sZZL60C04fSwMQgMii2wuAo+l6P5dddpmZ6j5p0iTT1WRNddcuNWv2lkpOTjYBR9cN0m0aRs4++2wzOPqTTz4xU9u3bNninequM7Z0XR7fmV7B0Fak8ePHm4HRp512mhnTtGrVKjPdvWPHjqVwFACEgvADwPF00UINQbrIoQYbHYCsgUYXObTW3VG6BtC2bdvMucU++OAD052l4UdnZ+m09lmzZsmyZcvMGj46gHrEiBF+ixwGq2bNmmbm2Lp168zijBp+dPC0rk103nnnlfCrBxCqKE8kRw0CAACEGWN+AACAqxB+AACAqxB+AACAqxB+AACAqxB+AACAqxB+AACAqxB+AACAqxB+AACAqxB+AACAqxB+AACAqxB+AACAqxB+AACAqxB+AACAuMn/B2uPTuqVvMVtAAAAAElFTkSuQmCC", + "image/png": "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", "text/plain": [ "
" ] @@ -396,6 +398,18 @@ "plt.show()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Picking localizations\n", + "This step is recommended to be done via GUI. Picking localizations is done via `picasso.postprocess.picked_locs`. By default, picking assigns a new column to localizations called `group`. In Render GUI, localizations with this column are color-coded. Note that some other processes (such as clustering) assign `group` as well, so this can be overwritten.\n", + "\n", + "The user can pick regions of interest defined by circles, rectangles, squares or polygons. The examples below show circular picks.\n", + "\n", + "There are more functions related to picking, for example: `postprocess.pick_similar` and `postprocess.remove_locs_in_picks`." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -568,48 +582,78 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjsAAAHMCAYAAAAzqWlnAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAWj9JREFUeJzt3QeYVNX5+PH3LkVpuywdpEgREAtNFESaCkIWRCJRSmI3xujPxDxqNOr/ZxL8GWNijCHGWIIxCkKIIogKRkFXRbGgYEFFpaiArMBSLJS9/+c9M3d2ZnZmd2Z3yp0z38/zLMO0nVtm733ve95zjuO6risAAACWKsj2AgAAAKQTwQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ6AnDBy5EhxHEdyyZYtW+Tcc8+Vjh07Sr169czy79y5M2W//6abbjK/c/ny5bX+Hfp+3baAzQh2gDQ4ePCg3HvvvTJixAhp0aKFNGjQQNq0aSPHHnusXHTRRbJw4cKMbvfqTmjr1683z5933nmSDzJ5ctdt+q9//ct8D2644Qb53//9Xzn00EOrfc/hhx9ultH7KSgokObNm8uJJ54of/3rX+XAgQPiV9u3b5ef//znZh0OOeQQ6dChg1xwwQXy2WefZXvRkOfqZ3sBABsDnfHjx8vTTz9tTlIlJSXmyn7fvn3y7rvvyuzZs2Xt2rVy+umnZ3tRkUa6v5955hk59dRT5eGHH076/T/72c/M90e/T59++qn85z//kRUrVsizzz4rjz76qHnN5ZdfLlOmTJHOnTtLtn311VcmIPvwww/l5JNPNsul3/NZs2bJ4sWLzbJ369Yt24uJPEWwA6TYnDlzTKDTt29fef7556WoqCji+a+//lpeffVVtrvltAmroqLCZDdqw8uQeK677joZNGiQPPbYY+Z7pdmiVq1amR8/+NWvfmUCnV/84hfyxz/+MfT4nXfeaQK3n/70p+bvAsgGmrGAFHv55ZdDTRjRgY5q3LixjBo1KuZ7586dK6eccopp+tLmDj3ZTZ06VV5//fXQa8rLy+W2224zV8+aMWrYsKG0bt3aZIr06jncAw88EKpz0RNkePOI1nvoT9euXc3z//znPyOe1/eGW7JkiXzve98zJ1dtoujevbtcffXVMWtQdLn1Z9euXebkp//Xpjz9vOhaE/3c/v37S6NGjUxTnzZ7aKCQKA0o7r77bhMING3aVJo0aWL+/7e//c08l+i2SMRHH30k55xzjhx22GFmu2sgo/f18ej179KlS5XtWpemwqOOOirU/LZy5coaa3Y0q6Lb0mtS0m07bNgws10Sod8xbUIbOnSoaZ6qzp49e0xznW776G2p2SfdFvr9+eSTT5JYYyB1yOwAKdayZUtzq1e5iXJdV84//3xzYtRg4vvf/74JYLTWYdmyZdKrVy857rjjzGvff/99uf7662X48OGmiay4uFg2btxo6oCeeuopWbRokYwdO9a8tl+/fqZO5Ne//rU54YSfbL0TpwYrf/7zn00m6owzzgg9r+/16Pv1JKZBmDbR6Ylz9erV8oc//EGefPJJE2QVFhZWacbRgExPlGPGjDHPe4GV509/+pMsXbpUzj77bLPML774omn20JO3Zr90G9TkRz/6kWka7NSpk6mH0pO/Zj80k6C/z2tCSmRbVOe1114zTVK7d+82gWWfPn1MQPHQQw/J448/Lv/9739NkOVlZbQWKnq7hm/T2tDviaqpUFubjX7wgx/Id999Z7arBsy6n99++235/e9/L5deemnc92qAqMv/l7/8xXwPdfvVVGf0yiuvyDfffGP2c7NmzSKe04DptNNOk3vuucd8l2nKQla4AFLqzTffdBs0aOA6juP+8Ic/dP/zn/+469evr/Y9f//73/Us5g4aNMjduXNnxHMHDhxwv/jii9B9fX7btm1VfsemTZvc9u3bu717967ynP7uESNGxPzsTz/91Dx/7rnnxnz+ueeeM88PGTLE3bFjR8Rzs2bNMs/9/Oc/j3i8S5cu5vFTTjnF3bNnT5Xf+b//+7/med1Our3C6e/S5y644IKIx3X5ow9Zs2fPNo/179/f3b17d+hx/cyBAwea5x5++OGEt0U8FRUVZrvqex966KGI5x555BHzeK9evdyDBw8mvF3j8badvj/cO++84zZq1Mg898ILL0Rsx2XLloVep9+NwsJCs22XL18e83sSb3t888037ve//33z2OWXXx6xPtWZOXNm6D2x3Hbbbeb5a665JqHfB6QazVhAimmTjF7tt23b1tyeeeaZpilBMz6TJk0ymZdoehWt/v73v1dp+tIuy+3btw/d1+dj1Wlok9bkyZNNtkEzPamiNRdKe5dpwWw4zY5o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ALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALAawQ4AALBaffGRiooKmTdvnpSWlsrOnTulRYsWMmLECDnzzDPFcZy473v33XflwQcflE2bNknLli3N60eOHJnRZQcAAP7kq2BnwYIF8swzz8hll10mHTt2lE8++UTuuusuady4sXzve9+L+Z4vv/xSfve738no0aPlf/7nf+Sdd96Ru+++W5o3by79+vXL+DoAAAB/8VWw8+GHH8pxxx0nAwYMMPfbtGkjL774oqxbty7ue5YuXWped84555j7GiStXbtWFi9eTLADAAD8VbPTs2dPk5n54osvzP3169fLBx98IP3794/7no8++kiOOeaYiMf69u1rAicAAABfZXbOOOMM+eabb+TKK6+UgoICU8MzZcoUGTZsWNz3aG1PUVFRxGN6X3/Pvn37pGHDhhHP7d+/3/x4tBaoUaNGof/7kbdcfl2+VGN97cb+tRv7125Ojp6PfBXsrFixwjRbXXHFFdKpUyeT2XnggQekuLg4ZQXHjz32mMyfPz90v2vXrnLrrbdK69atxe/atWsn+YT1tRv7127sX7u1y7Hzka+CnYceekgmTpwoQ4cONfc7d+4s27ZtM4XL8YIdLUQuLy+PeEzva7YmOqujJk2aJOPHjw/d96JT/ZwDBw6IH+ky6hdry5Yt4rqu2I71tRv7127sX7s5Pjof1a9fP+FEha+Cne+++840X4XT+9Vt0COOOEJWrVoV8djq1atN/U8sDRo0MD+xZHvH1USXz+/LmEqsr93Yv3Zj/9rNzbHzka8KlAcOHCiPPvqovPnmm6ZL+cqVK+WJJ56QQYMGhV4ze/ZsmTlzZuj+mDFjzGs1K/T555/LkiVLTHNYSUlJltYCAAD4ia8yOxdccIHMnTtX7rvvPtMUpYMK6vg5kydPDr1mx44dUlZWFrqv3c6vvfZa+ec//ylPPvmkGVTwJz/5Cd3OAQCA/4IdrbM577zzzE88OuBgtKOOOkp+//vfp3npAABALvJVMxYAAECqEewAAACrEewAAACrEewAAACrEewAAACrEewAAACrEewAAACrEewAAACrEewAAJCD3J3bpWLhbHOL6hHsAACQi8p3iLvoEXOL6hHsAAAAq/lqbiwAABCfabIKZnLcjR9H3BpFxeI0b8EmjEKwAwBAjnBfeDrQdBX+2IMzxQ3+35kwRZzTp2Vl2fyMYAcAgCxmajSAcYaPTSgjY17X94TAezd+bAId55zLxencPfCCouJ0L3JOomYHAIAcKTI2AVFRsbhvvypS3CrwWOfu4nQJ/tCEFRPBDgAAuRgg7dmV7SXJGTRjAQDg0yLjapu5mhaaGh2armpGsAMAgF+LjL0sTrfeVQIk2VEWqN/R13hNXIiJYAcAgAyqTZGx++bL4pYujXyMXlgJI9gBACCDTAYmLAvjhhUZh5quNkQ2bzltDxO58BeBxzTDM38WvbCSQLADACnoEgyktZlr/qzKO8NOMzfhARKqR7ADALEEayVMcwPBDtJFi5GjioxrauZyd5eLW7qEfZIEgh0AeYvsDbJNs4bRIx7X1Mwl2puLXlhJIdgBfISTb3azN8w7hFwNkFA9gh3AT2g6ySrmHUIuNHMheQQ7APJKtdmbbr3F+dlN4jQrYt4hWJ/FcfOoCJ9gB8gyPeDs21subtm2GkdTRWYHdKtSKwHYpDx/ivAJdoAsq3j+adm6aE6tBwvLp6uzVIjV00WOHSTOqBKT0aG5ALAPwQ6QZQUjxkqrU0ukrGybVGxIbDTVfL06q06iQV+sni6y+jUTUEZkcKiVgIXcJOblsgnBDpBlemBp2L69OE2KxHFpOqm1JII+LzAy8w1Vs1/o8QLbuMnMy2URgh0gB+Xr1VmquJ+tDxzwp/7YNGFpZofth3zg1GJeLhsQ7AB+kmDTSb5enaUq6HPfXBH4z5x78nr7If84NQ1YaCmCHcBHEm06sfnqLJmC66R6VoUFRtK2Q+C25CxxxBF38VxxJp8vTu9jc377wR50Pkgdgh0gVw9+YVdiVl2dJVF7k0zQFyswksXzQoGRu/VzcU4YEdq+QNalu/NBUf4MWEiwA+TSFRg9r2qdko/X5VyO6m+as5wBJ7J9kXc9E508aa4l2AFyPQix4Oqsptob13FE3noldPCuTXAZt8v5qBIzqaLT8fDKZi4glzsfcFFUBcEO4PPsTyIHv4IcvzqrqfZGho0RKV1aGUzGOpgnEPTF63Jufk/5DqlY9Yq5r7cF3pP0bEMG0fkgPQh2gExdgdXyaisfDn411d64u8vFLV1a/e+oJiXvBTlu28NEorucL1ss7urXIt+weK5ULJ5rzfZF7qht5wPveGT+VpYtDr3f3O4uF/e9VVIwZlLeDklBsAPUUqaCkJgHv8nnBwpqB5wYaH5JdJl9OrVEzCam4laVL9hRZm4qVq0QZ/MmkS2fB+4vnB0xzUPcdQoGmnL0gCpdzjXgCenYVeSzT0VOGi0FI78XeCyHmweRP13DK5Y+JvLM4/Gzo+qEkXk7yjrBDpDGK7CEsj/FLZMPBPQEPH+WOCPGJVfDkkNt+ToWjlu6JG7vKUMzM/pTTXCp26bi4/cDd+o3CNyeNFrkkENFnl0kctIYkZatA49/sKZyO2lQpRzHV4EhEIvTp7+4GuxMnC7y+MOBB0+ZILLvOxG9IAoP8PMQwQ6QxiswzTzUlP0xB6e6yqEgpkZe7U2/weKMGBvZe0rHxWnXUdz160SeXWiecy78hTjtO1XJwHiBpk60Kl7Q9NargdsXn6l84aoVInt3Ry7DmtfFXfN64P89jxK5+hbfZsVgJxOkv7pcZPTEmgcZ1e/6nl2BO++uqnxix1cib74cCHryfJR1gh0gjVI5+J852e4uFxl2mrjR2SIvC2HB1BKxam/Meqv2nQLBzmfrK5/cv68y4POC0Hjj6oQ77HCRz9eL9D1O5JtvRHRAwQ/fFXnjJZGBQ8XpF9hv0qGzfQEl/E//Zp95XApu+FPcv8/KgP4pU8BvrHuv8gUa6KhnF1lZ65cMgh0gFeL0BErl0Ow19liKE8RYUeDsXbW+8bKp20loXfoNFmlaKLLsSZEtn1X9nRroqM2fi3z6oal/cjWL88ZLJtApGDwyrasE1FWNAX0Up+RskR5Hhmrc8gnBDpACNQ3OZVLSevWVhOhmk5iD4kW/J8aJ34qpJTRoUV26izNgSKCJaeULgcc0A6PNU6d9P3Lk47deCfS8qsmnH5ob9/7bRfoPyemsGHJb0t83Dei/3CzSrVfgNpjBifv7F8/NysWNH5qACXaATNADmKaZh50WCi68A0DBiHEi7dvHfE94s0nMLFGwXqW6ICYXJv6LdTCMmMsq2BtLFjwUWaAcXofz+fqIA6kJfNp0CAQxxw+vDI482my1drXIMceZGh2tBzLb7KsvxdUr3zzp9g//qDF7O3qiOIc2qrwAcl1xX31eRH+qU3KWKe43xwtv/rdM8kETMMEOkEEFI8KubLzu0F5tSC1ooOMFLX4MYupyMEw2Ra9BSsUry8X9eo9Ik2ZS0K5jZbC0OUYzlgY6yitEXjyv8rkn54l7WBc7smKwa7ypP99UJWjQIMY8r4G96tEnULsT7Jnl6H0NzHsfm7eZSIIdIFtTIHhFt7VJY9d2iogcmloi5oFfaw7aHSbuv/8hsmtn5Bs2f1Z5sNeecOHPbfqk8v+atdFt7zV/aTf0F58JnVS02Nv8nvIdgcDR51kx2KPGLOyGj81jXoeE0LFBi/R1XKr+g0VWvSKOZnI+WSty+BGB39GsKCtNV35qAibYAbKVktYRfPWxDR/LvlatxS3bZjIT1Q0MFqrDia4RSjCI8dPEf6bpSg+I1R0Mo2d27z/YPHbwqy9Nk5ZoE+C+b0VWLBMZMiow1khYZqdi7r0iH4X1TlFekKmBTs+jxBk4VFwNdsJOKlWayoAsMRdFGyL/PjQYj+iYEFW/5wU35u8rSxc3rs+agAl2gDSJO8u2N2Jv8LbiwZmy1TtYjZ5oupom22zipyAmUTr+jbtoTq0Ohk6XHoHgR7Mze3aJu2KZCXS0B1VErY9uNw12vCyO0jFHnl1UWb8Q7LIefbUcHXjlUlYMFgh+33SaBzNYYHU04/nFBpPVMaMkh9XrJXtccFNUTOy3JmBfBTuXXXaZbNu2rcrjY8aMkYsuuqjK4wcOHJAFCxbI888/L9u3b5cOHTrI9OnTpV+/fhlaYiDJlLRObRA8+IT3qGpx1W+lvFFTcQsjU7s2N5to/ZLb9/jEDoZRgYaZIiM4U7nr1d4Exaz18QIdtWd3IJAK1i+Y4KrnUTGvlsMDL51sNdcCSuQuL1AxwbsGMDH+Tiq03kxHS65fPxDoqIaH1C1wKU9NMbHfOkb4Kti55ZZbpKKisqV948aNMmPGDBkyJLI7qOeRRx6R0tJSueSSS+Swww6Tt99+W2677Tbznq5du2ZwyQFJqNu5SS9HNc2oBp26itOkSMR1c7p7ZzK82qNEDobRV6jh910d9E/HxwkO/uddUVbo2DraFVe7lg8cGhgsUHXrKU73IyN+V8HFV0fUF2T7KhRIKGjwpkEJNgcbpUsC40VpL00taGYgTP8FO4WFwbE0gjRr07ZtW+nTp0/M12ugM2nSJBkwYEAoA7R69WpZtGiRXHHFFRlZZiDZbueh2YnDRj3e9/FacYtaBmKd4Ik10aHiwz8nHw9sBZ27mekcqpwcNMAMjqETCnTUnHsCxctR3Xiruwo1AatOOpojgSTsZorotZZHa/xU1BheoSylHnP8UExclP0mYF8FO9FNVBrMlJSUiOM4MV+zf/9+adiwYcRjev+DDz7I0FICyXc7jzVf1o47Z4T+b5pYNGCpYah4q6ThYGhGRA4Ooa+9uMyAagl0440pTwNJ+Iz+fQSbXGvU6xg9IZr/VmhT7/NPmwE5tek3+piS7mJiP9QU+jbYWblypezdu1dGjow/ZHvfvn3liSeekCOPPNJkgN555x3zvvCmsFgBkv54NJBq1KhR6P9+5C2XX5cv1WxY38CVUjC17F0h6a0j4nTvLc7Pfy1Os0LTE0sLlIuvuEF2N28lrqZ2ilpUvndPuVQsnBM5Pk+Cn2MUBQcj9Pn+NTO/p2BC1PDt4ewoCxysjz1epKh54LEWraTg8B6B125YJwcDk5pHftf0wDxhauAA7T3u3US/Nk++z8lgfdO4bYtbivPja8x33N29S9x174n7xNzYL/5gTeBHzZ9lblydEHfCVCmYGBl4mIFNg+N9ecekAr0w8JqU9TgS9T3Ote+zb4OdZcuWmULjFi3iH6jPP/98ufvuu+XnP/+52fAa8GhwpO+N57HHHpP58+eH7mttz6233iqtW7cWv2vXrp3kk1xe3/LnFsqu2fdGPKYHEE/htIulaPQlpsu59sRq2L23tGnRSg5uD4wUvK/8K9GkcpMtm2T3ojlS1KuPHNq2rdRr0Sr5z5l+ieTL/o21PWT1SnFXrzT/bbjyeam39m1pNPRkORjcxoXlX0nDvYG/f92+9Y48SuTIo8y+iN4fVV4btT9s/T7XBuubJmGjre/r2l22PjFXCn90qez6199CjzebcqE06Hi47P9svex+5H5zX2+1I8ShfQdV/d6G/87gMan1wMHSsEdva/avL4Md7ZGltTdXXXVVjTU+11xzjezbt0/27NkjxcXF8vDDD5ugJx6t8Rk/fnzovhed6mdq05kf6TLqF2vLli2BK3/L2bC+7oCTpF73PnGvlPYWtZCvN282Y+t4tvz7nyaLE04PUGr7H2402YZ6UVdkiX6OLfs3UOz9dPxMV5ztIS1aS8UbL8u3OqHoy4/J3qcfi9OEWLmNDz6uzY2R+yPea9O1vrmI9c0c7/ix55BA64SZ223VCvn6iKNNNqbii0Bd4O7PN5rbHes/kYJGTavN+nq/s6xsW6DThI/3b/369RNOVPgy2NHMTFFRUajwuCZap6MZIA1WXn311bi9t1SDBg3MTyzZ3nE10eXz+zKmUk6vb3BcFsNbBa0V8Xr3eOunXc31pNmilSl+LTj2+EAtiQ71rtMXDBllBswzIwd36yUV69dFFgsm+Dm27F/T42zRnECX9Vj1PXG2hwZ/9Y7qLxXvvCnuqhWB4fX374vZ68pbJmf4aeLU0DU+meXP6e9zLbC+GdjG5vgxRdz2nQM1b916m++3fs3c55+qrMMpfSbw+vmzTNNt+HAKcX9nYXG1vUNzbf/6LtjRepvly5fLiBEjpF69ehHPzZw50wQ106YFdtBHH31kxtc5/PDDze2///1vs/EnTpyYpaUHkmO6PU+cFgh2vtsfOIEunF05T5OODBycrdjcxupFZLnwLvV1HYE2NKGoDq/fINi5obhV3O7ufhonBKi28Ldzt4gRk/XvxW1aaHofyqBhIq+VmglBCzT7Y9kApTkZ7KxZs0bKyspk1KhRVZ7Tx8OLorTQWMfa+fLLL+XQQw+V/v37y+WXXy5NmjTJ8FIDqetpFD5bd1K9iHzQvTMd3M/WB65Q23QIBCkJdIs1zV3BrvuxRqANH17fffNlkaMH5PSYRYAy31U9fmjvQQ3yvfnjvv0mcBt8XMf7yje+C3a0h9W8eWGzD4e56aabIu7r+Dt/+lNgaH3Ar5K9UjIHLJ3GIJiWljiTAdb1c3KFCUbCZ3SOnmOsW08puPRXkUGJjhkS7Lpvgr8YI9BqRsd9c4U4I8fV3NXc0kAS9ok5gvia1wO3Lz5j5oFzszA3Vbb5LtgBEDZUvDfLcazJALM4g3C6hQ9y5rQ9LHBw1iyX/k+b+CafL442+WkA9MmHgdfGKrbU7fb2qxEZmojmqKiMTjy2BpKwjzeCeETt38DgCOKnTBDn6IGBzE6eBe4EO0COTgaYzRmE0y3mIGfBuiVj6xdmbiu3hlFgZd37gfdpE5hmy7I5iiyQAV6tmRZ8uM2KpGLxPDNhrvvGS1Iw5OS8rTkj2AFyfDJAw7KrtHgzJrtas6MFl42biOtNfKgdG1atENFutnobJlTYrRkgDQj198Zpjkr3KLIAsodgB8gB+dYzKN76ulp0rJY8Glm35PVeUyeMEKdVu0CgE+y6r71QTP1T+Y64RcfxAixbA0rkAa/WrEOwa3oef4cJdgDkjIIxk8Tt019EBwfc8nll01bJWeK06yjStDDQ5Oc9Huy6r8GQqX2qJkMTK8ByP/1InGOOo/kKVnRNz2cEO0CuybeeQWHraw7eXqGxFm8HgxodOyQ0Q3nHw8XVLJA2XWlGR2sWapuh0bmERoxl8k8gxxVkewGAfGXGgtEZ0LUeJ9mBCE+fljfZhmTX1wREvY8NZHB6BKaO8Jr8zE8iv0cDomFj6rroAHyCzA6QLcGxXLzZhpEkDUhGT4yZrYnuul+rLu9de4pbupQeWYAFCHYA5CQNaOqddWHc58NHUU606YoeWYCdCHaADIo5lsuGj2Vfq9ZmtmEzCV+eNE+lXdgoygk3gdEjC7ASwQ6QQbEyBxUPzpStwbmaGMslu/Ktiz+QLwh2gAyKlTkoOOdyaT1wsJQFMzuoPUZBBhALwQ6Q7cxBl+7SsEdvcZoUibjJlNMirTU3+dbFH7AYwQ4Aa6Sy5obJPwF7EOwA2RLKHFCQnCrU3ACIhWAHyBIvc+A4Oj8xACBdGEEZgJ2ouQEQRGYHgJWouQHgIbMDAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrADAACsRrAD5DF353apWDjb3AKArQh2gHxWvkPcRY+YWwCwFcEOAACwWv1sLwCAzDJNVsFMjrvx44hbo6hYnOYt2C0ArEGwA+QZ94WnA01X4Y89OFPc4P+dCVPEOX1aVpYNANKBYAfIM87wseL0PSGU0dFAxznncnE6dw+8oKg4uwsIAClGsAPkGdNEFdZMpRkdDXScLsFgBwAsQ4EyAACwGsEOkM+0GHnCFJquAFjNV81Yl112mWzbtq3K42PGjJGLLroo5nsWL14sS5culbKyMiksLJQTTjhBpk2bJg0bNszAEgO536RFMTKQuZ6Q2kHA1M1lqMdjKj/T1UFIX1giB886V3KNr4KdW265RSoqKkL3N27cKDNmzJAhQ4bEfP2LL74os2fPlksvvVR69uwpmzdvlrvuukscx5Fzz829nQEAyM2gIplBPE0HgUwtVyo/s1x/1xw5eGqJSJMiySW+CnY0MxNuwYIF0rZtW+nTp0/M13/wwQfSq1cvOemkk8z9Nm3ayNChQ+Wjjz7KyPICAHwoG0EFfM1XwU64AwcOSGlpqZSUlJhMTSwa6Ohr1q1bJz169JCtW7fKqlWrZNiwYRlfXgAA/DCIZ6o+09Vmq6WPidOnvzjNikK/Y9/Ha8UtaimumzuDkPo22Fm5cqXs3btXRo4cGfc1mtHZtWuX3Hjjjeb+wYMHZfTo0fL9738/7nv2799vfjwaSDVq1Cj0fz/ylsuvy5dqrK/d2L92y9b+DZzggxPaeid2vfUWo6hFWk7KNa2v1rho00/8QTynSsHE1A7imbLP3LVD5JnHxdWfsId33Dkj9P90LH86OK5rYjPfufnmm6VevXpy7bXXxn3Nu+++K3fccYdMmTJFjjjiCNmyZYvMmjVLTjnlFJk8eXLM98ybN0/mz58fut+1a1e59dZb07IOAJDPDm4vkz1P/UeajjtT6rVoldbPKn/477Jr9r1xny+cdrEUTb9EsrEN9MfLiGigUHzFDdKwe2/zmG6XVG+bVH3mvnVrZevPfigtrvqtNOjUNWPLnzeZHe2RtXr1arnqqquqfd3cuXNl+PDhJrhRnTt3lm+//Vbuuecek90pKKjas37SpEkyfvz40H0vGtfP1KazVDIpwOefloIRdSuS02Vs166dCeZ8GpumFOtrN/Zv/uzfivUfycHZ98re7n3E6dIjrZ/rDjhJ6nUP1He6Gz6WigdnSoGODB4cLHNvUQv5evPm7Hyfg8W82vSjdhW1FMcr8P1uv0galqu2n+mGZch0O6qdX24Vp1FTcb/+2tzXQGd70+aB9U3X8iegfv360rp168ReKz60bNkyKSoqkgEDBlT7uu+++65K6jBWgBOuQYMG5ieWVAcSpkfAojni9j0+JeOY6PLlQ7DjYX3txv7Nh/3r/d/7J430GOsdZ72P0ilQvGlQ0nCMT/b7nNHtUcvPrHj+qSpz52ngmOvnI98FO9r1fPny5TJixAjTjBVu5syZ0qJFCzOOjho4cKAZZ0eborxmLM326OM1BT0AgNTTi7x9e8vFLduWsYLcnJGNQTyT/Eynmrnz3N3l4r73VqDZSjM6OcR3wc6aNWvMAIGjRo2q8pw+Hp7JOfPMM839Rx55RLZv3266rmugM3XqVMmnynsA8Attut9abXHslPQPZOnTkcGzMYhnsp/pVDN3np59nWMGBoKdLDVdWVegnGlasxPeS6u2KhbOrpICDKd/gAVJftk1oGvfvr0ZNDEfdhfrazf2r+XKd0irBvWkrGybVGwIZgZKzhZ38VxxLvyFOL2PteqCLxe+z24tB1k0tU8zrpSCG/4Uqn3y0/pqSUpO1+zksupSgIbPrjQAILUn1SVS76xzTTGs4wYyA25hsDC2aaFVgY71gywW+TNDVhsUtqQjZajpPv0JBjihFKD+8IeO8N56mgnUpk/ABt50AtvLAoHP5k2Bxzd9am7cde+ZbIH5CfveV/e3UNPfCX9H6eM0b2FaImw4b5HZgTV8Ox9ODVdb0i+QCQRsoWPrHDzoijyzIPDAi88EbhfPk4rF86rW7lSXeagpK8HUEDFRPxqJYCedLEoB5gQOekDWTqqmp86yxeaxvU8/JjLpRyITp4s0biKy/iORFctEBg7VLrcivY8R6RF7zkOkaL+88HSV+lE304XiPkKwY1nlPXLwamvDx7KvVetAV93C7PbWy7nsGHx5UjUe+1fVx954KXC7aoW4w08Td/jYmD1XNXBS4fMxhfdqdR1He9fEfK9Br9eI+tGKVStMVk1KzpKC/kNC2yifEOxkGCeT/O7qH+vEoAN2bQ0O2pX1qy2yY6jFSdVte5hI6VKRD9ZEPjlinEiHTiJfbhF5dqHISaMrm7S++9b09ImXeTD34z3X8yhxP3w37nuz/nfkA+FdyJ3NmwJdyNt1DPWqyjcEO5nGySSvU7WxeuvpkPYtjuglZUufEOk3ONuLCCR/sfHGy1UDHfX8U4Hb1u0Ct4ccWvlct17iHD3QZGlk5Qsiq1+LGLwuPLOjfyfmvnZdb9+pSmaHXq/V7Bu15fPQrTcFhN8uBNONYCfDGR3p1js0JLctVe7ZlEtd/WNl9czVVpfuUq+wWKR0iTgjxib9O/ItOwb/qFj6mJkVOy4dfK5Zc5EN6wL3n11U+dycewIXJf0Hm0BHogavCxe6eGnfKe7z3ntRzYXg4rnmx48XgulGsJMB5gS1dnWgR0HJ2YEHS5eK2/NokfadOJnUQXWjfVqZ1UtDZjDXsmPwBxMkt2obuBPePBVOZ94Ozr4d16pX4v9+/b573dcJwq29EMwEgp0MCD+ZeFG1+f/9twdOzpxM8pJJ3w87zaTs95V/VaeMSl0yPhwUURsmU+0FybECHY8OrfDWq4H/DxkV6JWl3zu98Gt3mLhbPgsUzx47KNB8pc0sRcWJB+E+7/WarTrNnLoQzACCnTQKNQ9o09UpEwIp3GOOE1nzeuAFp0wQ5/AjzKii+lqaCurIhwe9io2fiLv8SXEGnCiyoyyyqUjrGUqXSkXpEtlRzcE8kWamumR8OCgi+e/0UyK9jja9e0ygEhbEyKhxUrDmDako+zJw3wt0lPeaqAs/Y/Vr4uqP991PMDPh+16vGarTTDSocvO0x2XCwc71118vl1xyiXTu3Dm9S2R7l0wv0FHPLqrsXUB2p878eNAzJwVtstSeKrF6lQw7TeqNHCuF5V/JjjtnxDyYJ3SFGzwpACn/DkedHDV4Nz2vSpfEDGJk2VNSkeDv1oJj2b8vZiBDEF77oMpM0REe0BSFXQjmaSeZ+slMlHnttdfK+PHjZfLkydKwYcP0Lplt4xysXS0yf1ZE27Yz+XwzKZ7ho2wEUscZMERcLTyu7qBe3FIa7g1MZhcrzRzvCleKW4ns2RXIDKaqsNiH2TFkWdTJUbOUGryHf6dDGZ6pP5aCtodJ84b1ZPvdfxDZtbPaX60FxzY2sWS96D96nzWvvBB0vR5adaDTgRx8fLY4w0/LmexQwsHOHXfcIbNnz5aFCxfKK6+8IhdddJEce2zwRI2Ywq9MdBIyvdpxBg4V1ykI9LzR2X8t+eNGnC6fwaYrPSlIg+AFQnGrpPZ7vCtc9+1XU15Y7MfsGPwhVCjsNce+VipyVP/AY9pLSoPkHn3EfetVOfSsc0U0oH/njep/pwYAwb8LrdeJ7mWVq0F4por+4wZVUdvUTXHwZeY+WzRHnL7H50x2KOFgp3HjxibAGTlypNx7771y8803y0knnSTnnnuuFBYWpncpLaLjRmj3Yq3TgP+koj073oEu9P83XxY5ekDE8/VatBJnwtSkDuYUFiNdYp4cgx0qQrS7eLDLuHzwjhScc5kZw6VCJwI9tUScYweKq8HO4FEiryyL/Tnhfxfvraryd5GrQXim/jZrPNYsXSAH9Xiz7zuRV5/P6x6XSRco9+jRQ2655RZ5+umnZe7cufLmm29Ky5Ytq7zOcRy57bbbUrWcuS/qyiSXrlLySgras6trdnLfXCHOyHFV3qPBTr2J08QNDpSWyPeImgZkfAqIaFN/LPLZ+tB32hsMcL/Ocr5vf+A1LduI9DomMOjgad+XgkHDYvxdvCyOBkWWMLVNXo+14Nhq4c103kztdS0SjiiVWDi7Mvj0vP9W5f+HnSYFI8bWOvgKBcCO1Ln3aM70xqqoqJBdu3bJ/v37pVmzZuYH1Yu+MrE9is5n1QYhca5ck/ndfHeQ7oxmzIB98vmBLuFlW0UefzjwfdTmEu1p+MVGqdi1MzQR6PY/3Fj5y8N7Xe38qvKEH/y7MP/XGiCdWsLGCyetbYr3XAqKhLU52+yvUSWmN1tEfWDJ2abXm6MjT2vZRPhgpknWSIUHwNX1HrUm2Fm9erXcf//9snXrVhkzZoxMnTpVGjVqlJ6lA/KhmDAVcqymAT4To6A1OmB3t34e6GQRp8kkrmMHBU7EYVlNLwNkakqaFYnVzdtNC9P3txm238zUGsHPMx0XzH5zK2sGy3eY4mRv2yfLC4B1eLDqeo/mfLCjmZwHHnhAXnrpJdP9fMaMGaZJC8h1aS0mDAtC0jm+BRkfpJv2wtLsi54sTQYnOJdVeK9ALV6ODoA00CnQjGZYVtPduztw++UXVcaf8uUFRoKZmFgXTrp+pjv4Z+vNT7yZ3Ou8zsFjjal98qbw0B5y0cNdjJ5Yq+DLC4C1RKW63qM5H+z87Gc/kwMHDsj06dNN9/OCAu1fBOS+dBYTRnT53PBxXo5vgRzPaAZPok7HwwPfZ31dsyKp0GAn6mRnvuOJfPgH70TOj5VjTSK1vXAy9+M8l+g6x9tvZl/osUVTL336m0yatO1gsnFOrDGM8kzCwU7Pnj3lwgsvlDZt2qR3iYAMS7bQl5FKkU8ZzUSyhuZvQjMX6z+q+tza1YFBBgubh2YqNydhVXKWOOIE6kp8Nu5YbZq3q7twijWTe20uqhLZbzrJtGbSTO+4NGRg6tWi92jOBDvXXXddepcEyBWJFhcGXydtOphxSOIeMIur9mbMhHwdNh4pyGhG1YhV23tryaPiLnlUpOdR4n74buRzi+dVZna2fi4Fp03K6ebt6i6cUjVTux+GnKiXaO9RH2FuLCDNhb7R45NEHzBl4vTs7IM8HTYeAXUZuqBK79LhY00Xa1ODo93OwycG1VHjO3UVadJMnHE/qJLZ0AHw9G/EzB/nI34IKuq83+i4EEKwA4SJl7JPNKUds0BRU/XtOops+TyQrvfBARNIpVCXZq0TiZ4BPfx+WDYk1PVc/waCNUF+yjjWeRyr6gKNDAUh6ei44O7cLuXPLRR3wEk5dfwi2AFSmNKOmc4PS9VLHdrPUzK6sw3d7JF6KTj5mgH0qhsZftiYQAaophOyJRnH6gKNlAUh0c2JmQgUy7fLrtn3Sr3ufQh2ANskmtKutkBx8yaTrq+1FJwEMjVnD3JLKk6+oe/+nl3S4Pkn5btVrwYe1wHtdMLP8EA615pXfLq8tQ0UXZ9kzzKJzA6QwpR2ta8LpuuzecD0ax0Ccl/4OCzNu3aXrV6wo5OExvg7CT9J+z3jaN04VuXJXThFTG7s7Z8NH1f2o8+BjDDBDuDjA2aqTwLMp4WMGnZaYvMukXGstUwEim6M/VMRNnhkLmSECXaAdKW0U1QHQbMT/KamZpDQOCz9TgjU8tTQXELGsfbbO5FjhAwfKxVLHxOnT/9ajeAcvn80s6OBToH2pMuhjDDBDpCmDE1K6yDS0ezk0zoE5H4ziDcOS8X6dVKRQHMJGcfab++EjhHlO8wUEq7+1KJeL2L/BAcMMk2T3mfkAIIdwMfSeRKwrg4ByEOJHCPcYDOXVyyej/V6BDtADsvHXhXIjmTGmtq3t1zcsm21qyEh42i2YV2aneLtL9HZz1WDhuamVhdORS2kcNrFsrcot443BDtAroh1ErBkTBL4X6L1YxXPPy1bF82p8XXxkHGU2jc7hR0jEpmUtDZ0/xRNv0S+3ryZ6SIApB4nAWRTovVjBSPGSqtTS6SsbJtUbMi/5pJUSrbZKeIYUcOkpO57q/JqX5DZAXKM38ckgZ2SGWuqYfv24jQpEsdNbZ2ZzWrb7BSrKTv8MXP/0w8j3m9qjI8eIPmEYAfIMXRHB+xT62anWE3Z4Y+p0qWS7wh2gBzDmCTIp7Gm8oWT7manYYkN8Ggrgh0gxzAmCfJprKl8Ud3fdXSzU6wmr4q1q8XZvCnw2NbPA7erXhHXyw217RDI+Oj78rCpm2AHAIAcb/KS+bOqNHm5i+dGPF/h/X/0RKl31oWSTwh2gFxGMwGQd3/XsZq8ZPL54gRfbzI7T8wVp+TsQGZn8bzQ8+79t5vxe/INwQ6Qw2gmQK5jYMzk/65jNXkV9D421FNLZySv0GCn/2DTBFaxeJ553nutDlSYbwh2AADZw8CYaaXFzcrVep5gV/Z8HKqCYAcAAJuavMJHUn51uXlIm69qM6K1LQh2AAAZxcCY6W3yCn+sYMwkkRNGBrZ7Hk4A6iHYAQBkFANjZg5DVQQQ7AAAMoqBMZFpBDsAgIwi25AlRfk7onVBthcAAJB8zUvFwtmB2hcgiSCz4PRpedH7KhrBDgDkaHdtb8qAnJbH2QbkaTPWZZddJtu2bavy+JgxY+Siiy6q8vhNN90k7733XpXH+/fvL9ddd13alhMAkBoMjIm8C3ZuueUWqagIzd4hGzdulBkzZsiQIUNivv6qq66SAwcOhO7v3r1brr766rivB4BcRXdtwJJgp7CwMOL+ggULpG3bttKnT5+Yr2/atGnE/ZdeekkOOeQQGTx4cFqXEwAyje7agCXBTjjN2JSWlkpJSYk4jpngvkbPPfecnHjiiXLooYemffkAIJPorg1YGOysXLlS9u7dKyNHBkZ+rMm6detk06ZNcumll1b7uv3795sfjwZSjRo1Cv3fj7zl8uvypRrrazf2by23W3FLEf3RLI8jclB7mHTpLk6XHuIn7F+7OTl6PvJtsLNs2TLp16+ftGjRIuGsTufOnaVHj+r/8B977DGZP39+6H7Xrl3l1ltvldatW4vftWvXTvIJ62s39m/t7dtbLltFpFWr1tKwfXvxI/av3drl2PnIl8GO9shavXq1KUBOxLfffmvqdc4+++waXztp0iQZP3586L4Xnepnhhc7+4kuo36xtmzZIq7rTd9mL9bXbuzfunP3HxRnwlQp09vNm8VP2L92c3x0Pqpfv37CiQpfBjua1SkqKpIBAwYk9PpXXnnFBCrDhg2r8bUNGjQwP7Fke8fVRJfP78uYSqyv3di/dVBULAWnTw1tRz9i/9rNzbHzke8GFdSu58uXL5cRI0ZIvXr1Ip6bOXOmzJ49O2YT1qBBg6RZs2YZXFIAAJALfBfsrFmzRsrKymTUqFFVntPHd+yIHDH0iy++kLVr18rJJ5+cwaUEAAC5wnfNWH379pV58+bFfE5HTI7WoUOHuK8HAADwXWYHAAAglQh2AACA1Qh2AACA1Qh2AACA1Qh2AACA1Qh2AACA1Qh2AACA1Qh2AACA1Qh2AACA1Qh2AACA1Qh2AACA1Qh2AADIMe7O7VKxcLa5Rc0IdgAAGcfJuo7Kd4i76BFzi5oR7AAAMo+TNTKofiY/DAAA1I5psgpmctyNH0fcGkXF4jRvweaNgWAHAJARnKzruP1eeDrQdBX+2IMzxQ3+35kwRZzTp9XxU+xEsAMAyAhO1nXjDB8rTt8TAtty48cm0HHOuVyczt0DLygqrvtOshTBDgAgIzhZ13H7aRNVWDOVZnQ00HG6BIMdxEWwAwDICE7WyBZ6YwEAkGu0GHnClIw0XbkWjOlDsAMAsPpkbWuWrOD0aZnpfVWe+2P60IwFAMg4PUnTcwiZQrADAAASGibAdUT27S0Xd//BnMrKEewAAICEhgk4KCJbzZg+U6Xg9KmSKwh2AAAZodkCPYmaLuiM9Ovrbe/EGdOnoEt3adWqtZRpZieHUKAM+JANvR8AGwtd82XbO1pT1SUwho83aGFgTJ8e0rBH75wLVgl2AD/ipAAAKUMzFgDAF/Nh0cyVvW1v+zABBDuAT5gDfbDZihmNkZfzYQUzmqZWJMeaSfzECxrdb78ReebxOk8casMwAQQ7gE9UPK8nhTkRjzGjMdIhkxmUTM2HRVYojBc0/uwmcU4YmfZtnwsIdgCfKBgxVty+x5v/5/uBKd3y/sSYwQxKTfNhmX2x4eO6N7VYlhVKxXfUaVYUMUmom8cThxLsAD5hDmhhAU0+H5jSffLwy4kx74OuZJu58kmS39Ga6nPc3eWSzwh2AOTfycMvMhh0paxYtS5iFLrWpZnLF+uUI0GjjJ6Y80XGdUGwA/iRBb0f/Ma7snU3bxLZvy/vTox+yKDEKnStqZnL7+uUSnUJ3hIJGh2Lv981IdgBfNjsYEPvB9+dPJYtDtzef3tWT4zZykZkqlBYHdxeJgcfny3O8NPMuqTr7yaT65QJdQne6hI05gOCHSAZPqn1QO1PHhGOHSQF3skjQyfGbGUjMnky1GBHexY6fY83n6OjgUvp0ip/N2akcO2B+Nl6caZfKk6SGU3bTvC2BW9+QrADWIri1/gnD2nQ0GR4nFElGT8x5t0JTbNYpUvjP/fCksD/v9goTudueZ3RTFnwFhU0usxJRrAD1CRniyDJQsU9eYT+36woZZs70ROKL7IRtagJq2n9Qn8njsi+8q/MYxWrVojUb1D5mqi/m7p+Zl3XyVZVmsHLyUiT2QHyrAgSaTox5tAJpVY1YTWsX/jfSWiqycXz4vcO6nWMSOt2lc+tfEEOlm0VadxEnB59pKBzt6S2qXV1bhYFb64PMksEO4BFzQ45m4XK8MmjphNjxg7OFp3QvL8TxxFpsORR+XZlafVv+GBN4Mez5vXAj27/nkeJXH1LZQ+63eWaMMor0d/RZL+TtTkWuOn63vvgQoBgB6iBL5odEkQWKv5BO1aAE+/g7n62PpCl6Na72oN+QieU4pY5m41I5oTp/Z04jiNNJ5xlgh3nwl+Ybv56gWBeo/ebFoq7bbPI3j0iG9aJvPVq4Hcdc5xIt15ioqVWbQOjKq97P/DcuvfF9Zoc8zVgT3aQwdpkpMuzH5SkC8EOYJFcykKlVaIH7Xiv27Mr8jaOiqWP1TjRokycLrmqtsFzvcLA98xp3ynwHu/17TtFXCRUvLJc3GCw4xw/XAoGjzQ9t9z7/igV4Z+5eK75if5MPzSP+FW2jwWuz7LMBDuAz5sdkjmg51IWym/CD86y5fPQrTdvU6yDs9Onv7jPPB6RwbApuKztCbNei1biTJgaeN7bpsNOM/djbmf9/WVbzbZ2ux8Z2J762HurRFYsExkyymxro0PnvMhE1HmQwQSPBW6aghK/ZZkJdoAkZKXZwfIDeqoketAOf13F2tWVt5s+EXn1+cjfGZZR0OH2C8ZMigg8tTeXG5XBsCm4rG3wrMFOvYnTxHXdwHv0xBbcZiZzE2vso8cflorHHxbpeZS4H74b+dyKZeJq0ONly7R4OQ9kImBw0/QZ2c4sRSPYAWxlUfFrKg/aMQcanD8rqcBT2nQQad8pMqhq0DBvi2mTuUCo6SToOo44rlvZdV17dJWcJQX9h4SeT8ks6TkgZQFDUfxjQbqCEr9lmQl2gCzTTEP5cwvFHXBSxCBgdU0t+734NdUSPWiHvy50MlVTfxy4/eQDU0Qrzy6KOMmGN8nogIRuVFAV+r82vRw9QHJdlebTFAXPNZ0EwwNFZ3d54PkefULPuwtnS4WPmkfSKVUBg1PNscBvQUm6EOwA2a63Kd8uu2bfK/W696kMdnzW3p0Lajpom6kJdNqCfoNDJ1RHnMpt2rhp4HXalBUsKnbadawMcjSrEww4nZKzRdodJu6Wz0ywZFOdTrzm02wEz6FmwrDBH2MGtZPPF3fr5+IMOFGcjodndBmRG1lmgh0gw/U2iQRAfmvvtoLXBLVzh7ilwSkKwoRPEGq6PyvtJh0r8PTqeLyXW3gl7IuTYIznYwW15vn5s8QZMc6aJqysBAxF6fkMP2SZCXaAJEUHK0l3f42q+5BgtkCLZN3lT4WuTsNPnqFpDrQ3S/CzkNzcQBXPP23+7wwYIqZsNt58TeqDd0SGjQn8v99gKYgTeLqbN0UGSTku092F/XASzBWZ2FaOxfvDV8HOZZddJtu2bavy+JgxY+Siiy6K+Z69e/fKnDlzZOXKlbJnzx5p3bq1nHvuuTJgQO63mSNHsjVR92s6YYRGhY2u+/h3oEjW1ZNwvGYqemYlxd21M7DNNm8S8bI5O8pMQCk9jxb36z0ic+6p+sb33wrti+gmw4jmMQ2mLCoCT2fzaa3GxKnm+25+n/4tDTtN3Ki/N9sKlWFZsHPLLbdIRUXlUFIbN26UGTNmyJAhwQLBKAcOHDDPFxYWyi9+8Qtp0aKFlJWVSePGjTO41EByJww5dlDgtuSsQE3I1s/FfWKuOOPPDtxe+Atxeh8bO0vhjU+CWjdZhRcTywkjQv+tzVg5tl0Jp7X5NMWBOnVtyNlgR4OWcAsWLJC2bdtKnz59Yr7+ueeeM9mc3/72t1K/fmBV2rRpk5FlRX6Jma1Z9UqgGUOLVMOvKnWKgZ/dFHhs2WKR1a8FAhy9Vd7t4nmRmZ1vvw78Z/++wIlBP8/rQaQniLACWa5gE9SxSyCI0cEBvTobL8jUepy9u0Nj6yQ0Vo4PCi3TyQ89cxJtSqOuLf37wbVodGpfBTvRWZvS0lIpKSkxc63E8sYbb8gRRxwh999/v7z++usmWBo6dKicccYZUlBQkPFlhr0SKVKtku7ve4K4wcDGGVVSOcR92BWz++mHlbUj/10U8/eYx+iZVauTpDZRhQeU4UGmblutxTG5ZK3PSSBzZlsmJ91qNRllgk1pfgjMrFZu12Cmvg12tAZH63FGjhwZ9zVbt241NT4nnXSSXHfddbJlyxa577775ODBg/KDH/wg5nv2799vfjwaSDVq1Cj0fz/ylsuvy5cP61swYpxIvxNMJqfivkBBqjY7Odr9ePNn4i6eJwWa7vcOtEVay7O98v2FReJ06WH+7zoiB/Uxfe2xg8Tt3FUqHv67OKeeLu5/F1b9PapfsGlhw8dS8eDMKq/x07bK1v6teGGJuIvm1Pz5034c6GauY+kET7wFXXuKs2uHuDoX1ugzAkFNipbPL99nr0i7YESCV+q6DSZMTXpbhK+vG2OfRAYuU6Vg4rSYf2tJfd+Dd/XheMua9Prn2P5NOSf2Ns3V9fVtsLNs2TLp16+fqcOJR4ci12zOJZdcYjI53bp1k+3bt8vChQvjBjuPPfaYzJ8/P3S/a9eucuutt5rCZr9r166d5BNfrW/79uZm37q1sjX4UJvRE6Rhj96BxxbPk9YDB5th8g9uLwu8tvwr0WvaQ48fJk0b1Jd6e8sDz7dqbX5H0Td7pEGr1rKvZWvzusZNmshebc5t3Fga9zzSvDbaPn3vgzPNZ+ln57JU79+DZ50rB08tMf/f9/Fa2XHnDCm+4gZp2L237N/0qWz/w43SZOwkcb7ZK3tmRxYl6wnV03TSdCk+8iix7fu8b2+5bF00R1qdWiINg9/naulr6rAddH2r2yehaSWiv+dhy5bo9/3gIQ1kz7SLpWmcv5tarX+O7d9UOLi9rMrxq7D8K2m4t3WV/ZVr6+vLYEezNatXr5arrrqq2tc1b97c1OqEN1kddthhsnPnTtMM5tXxhJs0aZKMHz8+dN+LTvUz9T1+pMuoXyzNXGmAZzu/rW8gFR/I0oQmhdTvzJo3xSnbJu7uwMzYZWXbpOK/i6tcyX67stT8hK5kR4w1PYH05Btu7+OB9+28+/dS/vkmM7dQlWUp2xb6LKdJ5UBruSSt+ze4TdyiluZ2V1FLs53cRoEBA78dNNxkBuodfVyVzIE0bGiydl93OUK+3bzZuu9zpr47VdY3zj4xvtsvUs22TmqZTz5dvq7m96Vr/f2yf1Ph4OOzqxy/NED16PGr/hnTfbO+eo5PNFFR369ZnaKiohq7j/fq1Uteeukl04PLC3g2b94sxcXFMQMd1aBBA/MTS7Z3XE3MpHo+X0Yb17fi+adiTlzoNWfpBJFaR+AWFgfS7zu3m7FctIuz6flz7KBAzY6OAqt1IUXFUnDxVZX1IRsDJ1w5ZUJgioKpPzZdo2Ote4XmlnseZW4LfLBt/Lp/vV9rbvVzdN+MnigHX1luJvMM9S7yPt67r5oWpWW5svF9jlUzU7HhY3Hc9HfPjl7f6H2S0O8oDBSEm/1Xi22XyfX3y/GqLpzhp4nT9/hqe+N565hr6+u7YEcDl+XLl8uIESOkXr16Ec/NnDnTNGtNmzYtNP7OkiVL5IEHHpCxY8eaSFObqcaNG5elpYeNEun1ETpgalFf6RJxNHsTHOpee1+ZgsqwwkmvuNIcjBsGJpB0GjUOFFnqhJJhvbHCD8Y6QaLOCO1NlIg4onpN6TYsOGGkVMy4UuSEkZWjWXtjHuk4PNoLzrKebr4aN6cWPdnqWhBO9/TkOBYXffsu2FmzZo0ZK2fUqFFVntPHw4uiWrVqJddff73885//lKuvvtoEQhroaG8sIBcOAOEHYx1jx9wyB1adJXqSNJN2Rk0VYdP299O4OenqyVZd0EX3dPg22Onbt6/MmxechTjKTTcFxi4J17NnT7n55pszsGRAkmPwFBaJ9Ogjsu69uNkCczDufqQ0WvuWfN2suRlFuUraOMND+Nukpm3nDB4ljmZ6LJ2DLBWBuu/HW6km6LI5U1EXbiL71LIxpXwX7AC+FuMAUKsxeIJXuOZqt7iltBhdIt++Umq6pEcfjHWmbsbZyUwzBifD6oMJs91qCryLA4XI8LHymrNyto0pRbADJCHWASBWqlyOOErkuKEiOjfT4nlm1F69rTIVRCKfyQzotca2S+2VeiLBo0ycXuvfn/By1CbbaVmmAskh2AHqKFaqXD56VwrODkxeW6FBTruOgaxB+07VpI1bxDwYk4pP7b7J56kgEr1SjxtM6FQoOv2GDsgY7G2YjWa/2hQe25apSJab583hBDtAmpiePjoir9ryeY0HF9NjKI8PxtmW7yfDRIIJiZ4OJUs1MGTskuemsWdeLiDYAVJ41WQCnOCkn6FJQMNqeOp8cEky++D74tJMSnHmxuZtm1AwUcM8YmldPgqPk99mw9PYMy8HEOwAabpqCs1uroIBUF0PLklnHyybzM9XmRuLt20iwYQ3martJ0lbOHkeIBLsABm4atKMj86Ank8HF9jNN81+ltdaITUIdoBMXDVt+DjUfJVu+V6ImE55uW19Hkz4JujKJUX+3qfpQLADWHZwyfdCxHTKx21LMGEfJw8DRIIdIAOBTSYPLvleiJhO+b5tbS7Kht0IdgDLrpryvRAxnfJ+21pclA27FWR7AQAAANKJzA5gszwsRMyYPNm2eVmUDesQ7AAWy3aTms3yZdvmY1E27EOwAwCIK9+LsmEHgh0AQFx5X5QNK1CgDCDvaB1KxcLZgXoUANYj2AGQf4JdqLM5mWVOypOibNiHZiwAQELypSgb9iHYAZAX6EIN5C+CHQB5gS7UQP4i2AGQF+hCDeQvgh0AeYEu1ED+ojcWAACwGsEOgPxDF2ogr9CMBSDv0IUayC9kdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNUIdgAAgNXqi49cdtllsm3btiqPjxkzRi666KIqjy9fvlzuuuuuiMcaNGggDz/8cFqXEwAA5A5fBTu33HKLVFRUhO5v3LhRZsyYIUOGDIn7nkaNGsmf//znDC0hAADINb4KdgoLCyPuL1iwQNq2bSt9+vSJ+x7HcaR58+YZWDoAAJCLfBXshDtw4ICUlpZKSUmJCWji+fbbb+WnP/2puK4rXbt2lalTp0qnTp0yuqwAAMC/fBvsrFy5Uvbu3SsjR46M+5oOHTrIpZdeKl26dJGvv/5aFi5cKDfccIPcfvvt0rJly5jv2b9/v/nxaCClTWHe//3IWy6/Ll+qsb52Y//ajf1rNydHz0eOqykRH7r55pulXr16cu211yaVDbryyitl6NChMmXKlJivmTdvnsyfPz90X7NBt956a0qWGQAA+I8vMzvaI2v16tVy1VVXJfW++vXrm+Bly5YtcV8zadIkGT9+fOi+F53qZ2qw5Ee6jO3atTPr5dPYNKVYX7uxf+3G/rWb46PzkZ7zW7dundhrxYeWLVsmRUVFMmDAgKTepz25tAdX//79475Gu6brTyzZ3nE10eXz+zKmEutrN/av3di/dnNz7Hzku2BHAxYdP2fEiBGmGSvczJkzpUWLFjJt2jRzX5ujjjjiCBNlan2P1uxohuaUU07J0tIDAAC/8V2ws2bNGikrK5NRo0ZVeU4fDy+K2rNnj/z973+XnTt3SpMmTaRbt25mXJ6OHTtmeKkBAIBf+S7Y6du3rykijuWmm26KuH/eeeeZHwAAgHiYGwsAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFiNYAcAAFitfrYXwC/q1/f/psiFZUwl1tdu7F+7sX/tVt8H56NklsFxXddN69IAAABkEc1YOeCbb76RX/7yl+Y2H7C+dmP/2o39a7dvcvR8RLCTAzT59umnn5rbfMD62o39azf2r93cHD0fEewAAACrEewAAACrEezkgAYNGsjkyZPNbT5gfe3G/rUb+9duDXL0fERvLAAAYDUyOwAAwGoEOwAAwGoEOwAAwGoEOwAAwGrZn9wiz7z33nuycOFCMyjTjh075KqrrpLjjz8+9Py8efPk5Zdflq+++srM+9GtWzeZMmWKHHHEEQn9/gULFsjs2bPle9/7npx33nli4/rqe+bPnx/xWIcOHeSOO+4QW/fv9u3b5aGHHpK33npLvvvuO2nXrp389Kc/le7du4tt63vZZZfJtm3bqjw+ZswYueiii8S29a2oqDDvKy0tlZ07d0qLFi1kxIgRcuaZZ4rjOGLb+urIu3PnzpWVK1dKeXm5dO3a1RyrevToIdlW0/qGu+eee+S///2vnHvuuVJSUlLt73366adl0aJFZv926dJFLrjgAmvX970kfmcmEexkmJ6oDj/8cDn55JPlD3/4Q5Xn9aStfwht27aVffv2yeLFi2XGjBnyl7/8RQoLC6v93evWrZNnnnnG/DHZvr6dOnWSG2+8MXS/oKDA2vXds2ePWdejjjpKfvWrX5nXbd68WZo0aSI2ru8tt9xiAgDPxo0bzXuGDBkiNq6vXqDo360GeR07dpRPPvlE7rrrLmncuLG5aLFtfe+++27ZtGmTXH755Sawe+GFF+S3v/2t/OlPfzL3s6mm9fVooPbRRx9JcXFxjb9Tg8EHH3xQLr74YhME6ja6+eabzcVZUVGR2La+3yX4OzONYCfD+vfvb37iOemkkyLun3POOfLcc8/Jhg0b5Jhjjon7vm+//dYcYC655BJ59NFHxfb11eCmefPm4jfpWN/HH39cWrZsaTI5njZt2oit6xt9ktRgQE+mffr0ERvX98MPP5TjjjtOBgwYENq3L774orl4sW19NSB69dVX5Zprrgntz7POOkveeOMNWbp0qckK+Xl9vSzrP/7xD7n++uvld7/7XY2/84knnpBTTjlFRo0aZe5r0PPmm2/KsmXL5IwzzhDb1rd/Ar8zG/xxOYyYDhw4YNKGeoVXU7bmvvvuM1+wY489Ni/Wd8uWLSaw06vDO++8U8rKysTW9X399ddN88Dtt99umnH0RKHvs3n/hr9Hm3f0RJHtJp10rW/Pnj3lnXfekS+++MLcX79+vXzwwQe+PGHUdX0PHjxosnbRA9I1bNhQ1q5dK36ny64XlaeffrrJLieyTTRTFx746YWa3tcg17b19TMyOz6kVzma4tSrIM1e3HDDDdU26bz00kumfVTT//mwvpoK1iyHptC1TVjrd/7f//t/8sc//lEaNWoktq3vl19+aZo5tJ180qRJ8vHHH8usWbNMjcTIkSPFtvWNTp/v3bs3J9aztuurV/dax3LllVeaE6GeYDTDMWzYMLFtffXvU4O7//znP3LYYYeZ12sWS0/8Wofmd5plrVevnowbNy6h1+/atcvsz+gstN73glub1tfPyOz4kNZm3HbbbaYdu1+/fqYtWwv5YtGMxgMPPCBXXHGFuTqyfX2VXvFq/YZePerrr7vuOnNCXLFihdi4vnqw1CLOadOmmdtTTz3VpMU1ALJxfcNpql/fk+1ajnSur35v9YSvf8O33nqrqd3RYtbly5eLjeur2VidMfsnP/mJ+U4/9dRTMnToUN/U3cWjGZonn3zSXGjlWpaxNmxbX39/u/LUoYceaq5y9Aro0ksvNZG1toPH+0LqgeWXv/yluRrUH62G1wOI/j+80NOG9Y1FC3U1y6NNW7kg2fXVokAtXA2n93Ol6a62+1d7ZK1evdoEdrkk2fXVXnYTJ040J/zOnTvL8OHDTRZPa5VsXF997a9//WtTtPu3v/3NZKS1ecsvdWjxvP/++yZToyd/71ir31FdDw1QY9EMlwZx2gsrnN73Y81hXdfXz2jGygF6FbR///6Yz2nbb3TFux5A9OSvB1C/Xy0lu77xirM10MmVtH+y69urV68qKW+937p1a8lFie5fzepobxWvcDdX1bS+2nsl+u9U7+v7bN6/GiTpj/Y2fPvtt+WHP/yh+JkGodFF19qrSh/3io+jed3xtSbL636tF6B6f+zYsWLb+voZwU6GeSfm8HoMLUhs2rSp+dGeVNozQ6/md+/ebcZn0Gr48G63v/nNb8wfjv6xaBu4Xg2GO+SQQ6RZs2ZVHrdhfZVeWeh7WrVqZWp2dKwPPTlE9wyxZX31Kl+7nut7TzzxRNNL59lnn5Uf//jHYuP6eicEbcbR8WY0U+AX6VjfgQMHmvfp91kzdvr7tAePH04o6VhfHStKednYf/3rX6Z+xw91WdWtr+4fPa5GBzOaodF1ibe+48ePl7/+9a8m6NGxdbRpSANcW9f32xp+Z7YQ7GSYFpdqCtejJ26lB3XtkqhX7FpoqwcO/aLpoHH6+vBK+K1bt5r0Yr6urx5M//znP5v3aJq4d+/e5ooj0aLXXFtfPUDqwFw6WKQWdmq6Xwf28kMmK13f5zVr1phmOj+c8NO9vjpOjQ6ypz0qtUla65NGjx4tkydPFhvX9+uvv5Y5c+aYgQj1BHjCCSfI1KlTzYnUz+ubaNNN9PrqBYre14sybb7SMWh0vCw/NGN9nIb1TcXvTAfHzdVcKQAAQAJyr6ADAAAgCQQ7AADAagQ7AADAagQ7AADAagQ7AADAagQ7AADAagQ7AADAagQ7AADAagQ7AKxy5513yvTp06vMJ6Z0cs2zzjpL3njjjawsG4DsINgBYBWdSqNhw4Zy7733Rjyuc/TMnz/fTE+g81EByB8EOwCsojOla2bn3XffNZOJenTuKZ1U9Pzzz8/q8gHIPIIdANY55ZRTpFevXmZGbZ2k8qWXXjKzbU+ZMsVMtAkgvzARKAArbdq0Sa655hoZNGiQrF27Vlq2bCk333yzFBRwjQfkG/7qAVipU6dOMmHCBHnllVdk165dcvHFFxPoAHmKYAeAtQoLC81tcXGxdO7cOduLAyBLCHYAWKmsrEzmzZtnMjxfffWVPP7449leJABZQrADwEr/+Mc/zO2vfvUrGTx4sDz66KOydevWbC8WgCwg2AFgnZUrV8rrr78uZ599tilMPu+886R+/fpy//33Z3vRAGQBwQ4Aq3zzzTcya9Ys6dq1q4wbN848pt3NNfDR7ucrVqzI9iICyDCCHQBWeeSRR2T79u1Vel+NHTvWBEAPPPCACYgA5A+CHQDW+OSTT2TJkiVy2mmnSY8ePSKe08BHA6CdO3eagAhA/mBQQQAAYDUyOwAAwGoEOwAAwGoEOwAAwGoEOwAAwGoEOwAAwGoEOwAAwGoEOwAAwGoEOwAAwGoEOwAAwGoEOwAAwGoEOwAAwGoEOwAAwGoEOwAAQGz2/wFetjqvjrg9PQAAAABJRU5ErkJggg==", + "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" - }, + } + ], + "source": [ + "# Display a scatterplot of localizations of group 0\n", + "# Select pick 0\n", + "pick_one = picked[picked.group == 0]\n", + "\n", + "# Scatterplot\n", + "plt.plot(pick_one['x'], pick_one['y'],'+')\n", + "plt.axis('equal')\n", + "plt.title('Scatter plot of Pick 0')\n", + "plt.xlabel('X')\n", + "plt.ylabel('Y')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plotting trace (the timestamps when localizations were recoreded) is useful for discovering sticking events, i.e., instances of fluorophores sticking unspecifically. Such signal usually shows a single burst of localizations (think of the fluorophore sticking and photobleaching). Normal signal shows repetitive signal." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlYAAAHMCAYAAAAXsOanAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAakVJREFUeJzt3QeYFEXaB/B3ll12WXIOIknAgAQDqBjAcIrKiYjpgM/MHWc8PQMqBjxQMJ4ep3gCCnIeSRBFkmJEUBQkIzkHWWADm8PU97wFPdszO3m6p6u7/z8f3Ak93dXV1d1vV1VXe4QQggAAAAAgYSmJzwIAAAAAEFgBAAAAGAg1VgAAAAAGQWAFAAAAYBAEVgAAAAAGQWAFAAAAYBAEVgAAAAAGQWAFAAAAYBAEVgAAAAAGQWAF4GAffPABeTwe+RecIS8vjx588EFq06YNpaamyu27atUqpcoMp43/AbgRAisAm+CTXSz/7BRMaSfzWP651eOPP07/+te/qHPnzvTkk0/Sc889R82aNQv7m969e1fJv9q1a9M555xDL774IhUVFZGqOG28jqeeeiplZGRQkyZN6Oabb6aNGzdanTSAoDx4ViCAPTz//PNVPvvnP/9Jubm59NBDD1G9evX8vrv++uupbdu2dODAAWrevDnVrVuXVMU1Lp988onfZzt37qRJkyZR69at6Y477ogqP9ygZcuWVLNmTdq0aVPUv+HA6ttvv6Xbb79d1iTxI2L37t1Ls2bNopycHOrRowctWbKE0tLSZHlKtMxotVW8DRNRUlJCl19+Of3www907rnn0mWXXUZ79uyhGTNmUPXq1emrr76i8847L6FlABiOAysAsKfWrVvzQ9TFjh07hNN8/fXXct169epldVKU4vF4Ys4Tnp7zkvNUb//+/aJp06byuw8++MDQcsn/EvXiiy/KtN14442ioqLC9/knn3wiPz/jjDP8PgdQAZoCARwsVH8ZrQ9Mfn4+Pfzww3TyySdTjRo1qFu3br6ao/Lycho1ahR16NBBNsGccsopNHbs2JDLWrhwIV1zzTXUqFEjSk9Pl9M/9thjskbESFwLwuvEtVibN2+mW265RTYPpaSk0DfffCOnWbFihazF69q1KzVo0ECmn9fj73//O2VnZ4ec97Rp02QNifYbzqM//elP9Msvv1SZ9n//+x9deumlsqaQpz399NNp5MiRspYlFlw7dN9998llcS1M48aN6YYbbpDrEKw5j2ubuPZJa9Ljz+PFtVK8LLZ8+fKIfay4lov7d3FecnnhfOLarn/84x9RLe+jjz6SZYPzKlJtFq/nuHHj5OuXX35Zbl9Nv3796OKLL6YNGzbIvABQSarVCQAAa5SVldEf/vAHOnr0qDxRlZaWymBhwIABtGjRInr77bfpp59+oquvvlqeDLn55YEHHpAnfg5m9EaMGCGb5vhE27dvXxnorFmzhl599VWaN28eLVu2jOrUqWNo+rdt2yabgTp27EiDBg2SfXG0Zbz33ns0e/Zs6tWrF11xxRXk9XploPL666/T/Pnz5XpxHyP9SfzOO++UTY8cGHKwwevJgcTXX38t+/dwU5Tmrrvuovfff182y3F+cXD1448/0jPPPEOLFy+mL774QnYsj2THjh100UUX0f79+2UzFwdxWlPX559/Th9//LHMT8aBJAdRnNf65tFEO4nzurNI/dY4uLzqqqtkebnkkktkHhUWFsrghrc9r3s4HBwNGzaMevbsSZ9++qksK5G27+7du+X25SbtQFwuv//+e9kcyAEugDKsrjIDAPOaAt9//335Pf8N9ru+ffuK4uJi3+ffffed/Lx+/fri3HPPFdnZ2b7vtm3bJtLS0kS3bt385vXVV1/J31xwwQV+0+uX/7e//c2wpkBeV/6c/z355JNBf7tz505RXl5e5fPx48fL340ePdrv83fffVd+3r17d5GTk+P3Hc+Hm8wC16l///6isLDQb9rnnntOfvfPf/4zqnW88sor5fQjR470+/yHH34Q1apVEw0aNBDHjh3z+y6e5tFwTYFNmjSR302ePDlkmSkpKRFt2rSRn//3v/+tMv89e/aEbArkprr7779f/vaGG24QRUVFUaV57ty5vjIazIwZM+T3N998c1TzA0gWBFYALg6stm7dWuU3bdu2ld8tXry4yne9e/cWqampfkHL9ddfL6dft25d0DRwINa4cWPDAyvuG6QPCqPh9XpFnTp1xKWXXur3+ZlnninnuXLlyojz4PXhPAgMIhnnS8OGDWWAFgkHI7zMVq1aidLS0irfDx48WH4/adIkwwKr22+/XQZ/zz77rLjrrrtEvXr15Oc9evTwpSFYmZk5c6b87LrrrotqeVpgxUEUB6D82wceeCCm/lAcwPHvBg0aFPT7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", + "image/png": "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mPXee+/55/nPf/5jlrtt2zYrWlRXPIN59913reHDh1unT5+2okV1x3P37t3WmDFjrIMHDzp670hTnfF8+OGHrXnz5lnRrDrjefvtt1urV68uNt+oUaOst956y4oWt4YonpMnT7amT59ebNrUqVOtZ555xvxf43r33Xdbr776arHlpqenW+vWravUOlPjhFJOnz4tb731lsnUW7Ro4fg12kTaq1cv/69LzfjPnDkjl156qX++pk2bSv369c0vU68IVTyDOXr0qNSqVcv8sveKUMZTf/k+88wz8uMf/zjiu4/DHc/8/HzZvn27JCYmyqOPPip33323/OpXv5JPP/1UvCSU+2f79u1Nl5V202vr1DvvvGNKHi655BLxitMO49muXTvZunWrfP311+axtnRu27ZNOnfubB7v37/fdOFddtll/tfoMtu0aVPp4xFddfDbuHGjTJs2zTSN6kFEv/zKahYtacOGDaZJuWfPnv5pupNq/3SdOnWKzatfrPpctAt1PEs6fPiwvPzyy3LdddeJF1RFPLUrWQ9OWivmNaGO5759+8z9woULTXeydsWsXr1afv3rX5vu+crWkUSaqtg/77//frNMu9tTu5O0HiglJUWi3cZKxvPmm2+WY8eOmZhpbZ0mmloXdc0115jn7WOOHn/O9XhEixP89FfM1KlTTf1Rp06dTF+6/op0YuXKleY1WpCHqo+ntjRpnYP25d96662eCHmo4/nvf//b/EK98847xYtCHU/7CvWayGvLidbgaGy1XkXnj3ZV8feuNTiaUD322GOmJqp///5muVoPFe0uqWQ833vvPVMDdu+998qUKVNMHVNWVpasWrUq5OtG4gS/mjVrml8y2uR5zz33mF84b7/9doUR0jND9KyP3r17F5uuvxK0mVX/8APpzu+FbpFQx9Omv6p++9vfmi46/fWprXpeEOp4atKkrSR6cNdfpnpT2jry+OOPS7QLdTyTkpLMvSbzgbR7Xk9kiHahjufevXtlxYoVZlla7qAtePojqXXr1mZ6tKtZyXj+/e9/l0GDBsnVV18tzZs3lx49eki/fv1M0biyjzklk6+zOR554xsXZ0V/QWp/upNfS9rc2aVLl2LT9RRS3dm3bNniP31W+5/1S1T/GLzmXONptzTpWSB6UcsJEyZE9Cne4Y6nNu3rWT2BNBEdOXKkXHHFFeI15xrPBg0amOTJrjGx7dmzx7QYeM25xlO7qFTJGkfthrJb97zEqiCeWq+osSkrVnoWnSZIejzSJNT+PtVhM3QIiMogcfIIHQNEf8HYtFBOi+fq1q1rbq+88oo5WOgX37fffmt+0eip74Gnxmqtgo610bdvX/807UfWplAdL6NkgbIW3umBScdx0vfQxy+++KJJmiI9cQpHPO2kSb8gfvazn5mWJ70p7fsv+aURScIRT/0SDfZLU09e0C/ZSBaOeOoBfuDAgWacHD0w6U3n/eqrr8xwJJEsHPHULk5tcdEhR7RmTN/nX//6l2md0uEJItnxKojn5Zdfbl6nf7/a6qnLW7Zsmek2tvdPHZZA59F6O/0bz8zMNO9R2RpHEieP2LlzpzzxxBP+x5rMKP2D1bNf9FeidlHoTqrjhWhzsM7frFkz/2u0W0MLkgNp9q4tSPbOWZL+etcdVpet3Xb2AJiRLhzx1MHi9Kwlpf34gXS8l0g+2Idr/4xW4Yqndo1oq4AW3euZYHoWlNbnRHoxczjiqV3wDz30kMyZM8fU7GiyoXHU2p1grdFej+ddd91lasKef/550/2m9WLXX3+9DB061D+PduXpD8+//OUv5oeoDoD58MMPV7rl3qdjEpxjDAAAADwhctv2AQAAqhmJEwAAgEMkTgAAAA6ROAEAADhE4gQAAOAQiRMAAIBDJE4AAAAOkTgBAAA4ROIEwHP++Mc/yogRI0pdV03pRUFvu+022bhxY1jWDYC7kTgB8By9FJBeZkGvAxZIr5m1aNEi6dq1q7n2FQCUROIEwHP0avTa4vTRRx+Zi6za9DpXerHVUaNGhXX9ALgXiRMAT+rdu7e0b99eXnrpJXMx0XfeeUc+/PBDGTZsmLlAKAAEw0V+AXjWl19+KRMmTJDvf//78umnn8oFF1wgkyZNkpgYflMCCI5vBwCe1axZMxkwYIC8//77cvjwYbn77rtJmgCUi8QJgKclJCSY+6SkJGnevHm4VweAy5E4AfCs3NxcWbBggWl5+uabb+TVV18N9yoBcDkSJwCe9eKLL5r7hx9+WK666ip55ZVXZN++feFeLQAuRuIEwJM2bNgg//73v+WHP/yhKQq/8847JS4uTl544YVwrxoAFyNxAuA5x44dk5kzZ0qrVq3kxhtvNNN0CAJNonRIgvfeey/cqwjApUicAHhOZmam5OXllTqLrm/fviaZmjVrlkmuAKAkEicAnrJr1y75xz/+ITfccIO0adOm2HOaRGkydejQIZNcAUBJDIAJAADgEC1OAAAADpE4AQAAOETiBAAA4BCJEwAAgEMkTgAAAA6ROAEAADhE4gQAAOAQiRMAAIBDJE4AAAAOkTgBAAA4ROIEAABA4gQAABBatDgBAACIM/8P/DzThlSBzUoAAAAASUVORK5CYII=", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" + }, + { + "data": { + "image/png": "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9u2ycOFCTdZz5swZGTZsmEuNBCS4wCyKlStXysWLF6Vy5crSt29fKVasmGObCxcuyNSpU+X333/XOgkNGjSQBx98ULJly+bYZv/+/fLRRx/J7t27NTFT69atpV27dhKuIEX0L7/8IocPH5YsWbJoTglkiixevLhjm2vXrmm1SVSdRHawmjVrquycZ6GcOnVKJk+eLNu2bVN5NW3aVLp37y4xMTGObbAOxzl48KAUKFBAOnXqFNbTWL/55ht9nTx5Uj+XLFlSo6lr166tnyk375g/f7589tlncscdd8gDDzxA2aUCnmHOKd0B7lVUfrXaNceRfxBLAePBM2HCBFX+KAV89uxZsQvIh4CpkX369PG4fsGCBbJ06VKtlPjiiy9K1qxZVUa4WUzefvttvdiffvppGTlypPz555/ywQcfONajwNLzzz8vBQsWlJdfflmV5JdffikrVqyQcO40tWrVSmWB752YmKjf8cqVK45tpk+frh0iFJ0aP368dq5ef/11l2RUL730khanwr4DBgyQ77//3qVs9okTJ1Rm1apVk1deeUXuvPNOef/992XTpk0SruTPn18fmvhe+P433HCDfjdcQ4ByS5tdu3bJt99+q88sZyi7lLnuuuvkww8/dLyeffZZa8oNVf1IYBk1apQxZcoUx+fExESjf//+xrx582wp+nvuucf4+eefHZ+TkpKMfv36GQsWLHAsu3jxotG9e3djzZo1+vngwYO6365duxzbbNy40ejSpYvxzz//6Ofly5cbDzzwgBEfH+/Y5tNPPzUGDRpkRApnz55VOWzbts0hp27duhnr1693bHPo0CHdZseOHfp5w4YNKqczZ844toGsevbs6ZDVJ598YgwdOtTlXG+++abx/PPPG5EEro+VK1dSbl5w+fJlY+DAgcbmzZuNsWPHGtOmTdPlvOZSZtasWcawYcM8rrOa3DjyDzDowe3Zs0eqV6/uWIYaAfj8999/B/r0YQF6snCH1KhRw7EMtRDgFjFlhPecOXNK+fLlHdtAhjD/Y3RiblOlShVNxWwCsxqqLsJlEAnAugFiY2P1HdcWrAHO11eJEiXU+uEsu1KlSrmYFmvVqqWFRMxR8M6dO12OYcouUq5RjKiQNx0WKLhOKLe0mTJlirpInO9LQNmlzrFjx+Shhx6Sxx57TK2VMONbUW70+VuwFLDdMIsl5cmT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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "# Display a scatterplot of localizations of group 0\n", - "# Select pick 0\n", - "pick_one = picked[picked.group == 0]\n", - "\n", - "# Scatterplot\n", - "plt.plot(pick_one['x'], pick_one['y'],'+')\n", + "# let's look at the trace of a single dot in pick0\n", + "locs_single_site = pick_one[\n", + " (pick_one[\"x\"] > 13.7)\n", + " & (pick_one[\"x\"] < 13.8)\n", + " & (pick_one[\"y\"] > 7.8)\n", + " & (pick_one[\"y\"] < 8.0)\n", + "]\n", + "plt.plot(locs_single_site['x'], locs_single_site['y'],'+')\n", "plt.axis('equal')\n", - "plt.title('Scatterplot of Pick 0')\n", + "plt.title('Scatter plot of the single site')\n", "plt.xlabel('X')\n", "plt.ylabel('Y')\n", "plt.show()\n", "\n", - "# Time trace with photon values\n", - "xvec = np.arange(max(pick_one[\"frame\"]) + 1)\n", - "yvec = xvec[:] * 0\n", - "yvec[pick_one[\"frame\"]] = pick_one['photons']\n", - "\n", - "plt.plot(xvec, yvec)\n", - "plt.title('Time Trace of Pick 0')\n", - "plt.xlabel('Frame')\n", - "plt.ylabel('Photons')\n", - "plt.show()\n" + "fig, trace_data = lib.plot_trace(locs_single_site, info, return_trace=True)" ] }, { @@ -617,32 +661,32 @@ "metadata": {}, "source": [ "## Info file\n", - "Back to the original localizations. The info file is now a list of dictionaries. Each step in picasso adds an element to the list." + "Back to the original localizations. The info file is now a list of dictionaries. Many functions in Picasso add an element to this list or return a single dictionary.\n", + "\n", + "Function `picasso.lib.get_from_metadata` lets the user extract the given parameter, start from the last dictionary. See example below on how to extract the size of the imaged field of view." ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Generated by: Picasso v0.9.1 Simulate\n", - "Generated by: Picasso Sample Notebook Localize\n", - "Image height: 32, width: 32\n" + "Image height: 32, width: 32 (camera pixels)\n", + "Effective camera pixel size: 130 nm\n" ] } ], "source": [ - "for i in range(len(info)):\n", - " print(f\"Generated by: {info[i]['Generated by']}\")\n", - " \n", "# extract width and height:\n", - "width, height = info[0]['Width'], info[0]['Height']\n", - "print(f'Image height: {height}, width: {width}')\n", - "pixelsize = info[0][\"Pixelsize\"] # camera pixel size" + "width = lib.get_from_metadata(info, \"Width\") # optional arguments let you set default value (if the key was not found) or to raise an error\n", + "height = lib.get_from_metadata(info, \"Height\")\n", + "print(f'Image height: {height}, width: {width} (camera pixels)')\n", + "pixelsize = lib.get_from_metadata(info, \"Pixelsize\") # effective camera pixel size in nm\n", + "print(f\"Effective camera pixel size: {pixelsize} nm\")" ] }, { @@ -654,7 +698,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -666,40 +710,81 @@ } ], "source": [ - "_, nena = postprocess.nena(locs, info)\n", + "nena_result, nena = postprocess.nena(locs, info)\n", "nena *= pixelsize # convert to nm\n", "print(f\"Experimental loc. precision: {nena:.1f} nm\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It's also possible to estimate resolution using Fourier Ring Correlation (FRC), see https://doi.org/10.1038/nmeth.2448. This uses a specific field of view (`viewport`) so it's easier to run via GUI. See function `picasso.postprocess.frc`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also plot the nena to visualize if the fitting looks alright:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAosAAAHrCAYAAACn9tfQAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAbZtJREFUeJzt3Qd8G+X5wPFHy07sxM6ehBAIYYYQdhlll9GwQ0uBslMK4c/eO5SwyvyXUcqmlD8jJWwoIxQIYaQQICQ0QBLIJs6wk3hbuv/neeWTT7Jky7Z0Otm/7+eTaFp3enV3evS87/ucz7IsSwAAAIAk/MnuBAAAAAgWAQAA0CIyiwAAAEiJYBEAAAApESwCAAAgJYJFAAAApESwCAAAgJQIFgEAAJASwSIAAABSIlhEUv/3f/8nY8eOlZ49e4rP55Pzzz+flsqCU045xbTvjz/+2KHXefzxx83r6KXby86FZOuu1/U+fQxtc/3115u2+/e//5323+yzzz7mbwB0fgSLHaQHS/03fPhwqampSfqcTTbZxDynoaFB3KIHfV2mfgm01ccffywnnHCCrF+/Xs466yy57rrr5OCDD87KegJe0ZF9Bm2nx0X9h65pyZIlMnnyZDn22GNl5MiR4vf7zf73ww8/tPvHcqp/f/3rX1P+rS5vwoQJMmLECOnWrZv069dPdtttN7njjjs6+A47l2CuV6CzWLRokdx9991y+eWXS7577bXXRE8Z/uSTT8ruu++e69Xp1G6++WazzQwdOjTXq5L3tA2//fZbKS0tzfWqdAl6fKiqqsr1aiBP/ec//5Grr77aBHMaqOl+W15e3qHXPOKII2T77bdvdv9OO+2U9PkvvPCCHH/88RIKhWTcuHFmPSoqKmTevHnmsYsuuqhD69OZECxmQO/evc0Gf8stt8gZZ5xhfpnks2XLlpnLIUOG5HpVOr3Bgwebf+g4PeBvueWWNKVLNt54Y9oa7aYB3AcffCBjxoyRkpISM6zh/fff71CLHnnkkWkPQ/nmm29MoLj11lvL66+/LoMGDYp7vL6+vkPr0tnQDZ0BRUVFcs0115hfJJMmTWrT33766acyfvx4s6EWFBTIsGHD5Mwzz4wFbM6dQAPS//3f/232Grpsfez00083t3Vn2Xfffc11XR9nOr6lMUl2Kv+xxx4zt/VXlv139tgwu+to3bp1cuGFF5rr+iVtd93pet9www2yxx57xN6TBp26U86dO7fZMp3jzObPn2/aom/fvmas5K9+9SuzQ6uysjL5wx/+YAIr7SrYeeed5b333kv6PrS7//777zddCXoQ0s9Hx1/ee++9EolE2jwmS1/vpptuks0331wKCwvNZ3TZZZdJXV1d0r/773//a96PPk/f/8CBA83711+r6Y4b1MzuPffcYw5k+n41a3bOOeeYbay17jttF113bUN9/7/+9a9Nxi0VbZM777zTBFq6rI022kguuOAC8xkn8/nnn8sxxxwjAwYMMO2hQzDOPvtsWb58ecr3t2DBAvnLX/4i2223nXTv3t2sXzreeecd2WuvvaS4uFj69Olj9gNt32RSjVn8+eef5eKLL5YtttjCvE6vXr3MdX2erle6+4y2/Z///GfZb7/9TBvpZ9u/f385/PDDzdCNZPTv9b2uWrUqtv1qm22zzTax/SyZt956Sw477LBYG+u2pFkTbY9E//rXv+TQQw81P1L1uZtttplccskl7c7STJkyRXbZZRez32ibH3fccbJ06dK0xizqdvvEE0+YHgltG92edN0POuggefbZZ+O6+3/66Sfzz9nWiZ/du+++a4bA6Hroexs1apTJxOtnkczMmTPNccPe9g844ADz2aQak2l/PitWrDA/9HU/CwQCsbG/3333nVmeBjb6fuztXT9L7UZtaSiDZs503TVjpgkF3WcWL15snqfbnbarvqbuD7rtffXVV5Ip2tZ77723Wa7dvZv4ry3jm7NB9yHdt/VzyoUrr7zSHMP/8Y9/NAsUlX6vZfO7wte47enx6bTTTjPfE3p80n3nww8/NM+prKw0+7Juc/Zx4/nnn5dcILOYIRMnTjTByIMPPijnnnuu2Vha8+ijj5qDjm4E+oWjG9b3338vDz/8sLzyyivyySefxH6963M14Ln00kvNDqbX7YOpbpwaVOiXsdIvVKUHbT1gOL+YWwoyNH2v4xNffPFFc+A677zzzBersi+Vbvj6hblmzRpzYNadXQNLpb8UNcOqBz89OPbo0cO8J/0Cevnll+Wjjz4yvySTfdHvuuuustVWW5kvDL09depUs+56sNeDri7nt7/9rVnuM888I4cccog5mDszHPprUL9k9QtUAwIN0vQLSwOo//mf/zHB+d///ndpC30N3Xl1eboO+iv0tttuk5UrVzb7wn/zzTfl6KOPjq2HjsXRLxXt0tDufV2PHXbYIa3t6YEHHjCBtm4jGpho+3322WfmtVMdyF599VV56aWXzLr+8Y9/NAG6rq9+ier1ZFlvDQz1c/vNb35jAhJtOx1Soe95+vTppv2cr6+fqwYFGtjrQUyDR11XXa4+394WnHRb0tfTwFUDG/1Cbo1uM/p563vXSw209PV/8YtfmKAzHdpNqj9c9IfIgQceaD4TXXcNUnR99T1suummae0zGnBfddVV8stf/tK8D/0i1uEn+rm88cYbZp9NNrZXgzZdB30furza2lpzwNcvCP0iP/nkk+Oer/ug/uDSfUfXS48L+iNsxowZ8tRTT5kAyKaBrQYmGkxpN5oGl19//bXcfvvt5nPXfactX8b6I0vfjx6PtB10f9HAQ48HX375pTlWtUTbR4dW6Dag25MGSvojQrc/fc/6OWp76nvUbUw5J885uxD1WKpjpvULVMe16XvTYOzWW281ba3HEudxSbdhPR6Fw2GzD2rQPHv2bHMs0uNVKno80R+W2t76d/qZ6Be30v1Wx7vpa+iXuH6Gc+bMiR2jNSBMNoRE36+up7ahjofT9dDX0h+/ut3tueee5sfZSSedZLZFfUy3Tw0idT06Qo9NGqDoZ6XtptuPfn66PSj9Ma7L6OhyvEi3Ud2udP6Afi76uWlQmkh/COvxWL+L9DtHj6t6bNFtR2/rdqSfdVsd34bvCuexQX/c/O53v4t9t+mPK913NXGk9+m+rcd9nXiq+5B+prrNuspCh2gTDh061Fx//vnnze2jjjoq7jnDhw8399fX18fumzdvnhUKhazNNtvMWrJkSdzz33nnHcvv91tHHnlk3P0fffSRFQwGrc0339xav369tWLFCmvQoEFW9+7drW+++Sbuue+9955Z5nXXXdfm93TyySebv124cGGzx+z3sv/++1sbNmxo9vjPP/9srVu3rtn9X375pVVcXGwdfPDBcffrMvT19N+NN94Y99gNN9xg7u/du7d15plnWuFwOPbYk08+aR47//zz4/5G36/ef84551gNDQ2x+/X6aaedZh578cUX02qHvffe2zx/hx12sFavXh27X9+3fm76GS1fvjx2/5o1a6xevXpZffv2tebMmRP3WrNnzzbvf+zYsa229QcffGDuGzVqlLV27drY/bW1tdZee+1lHtPPwemxxx4z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006TkSNHZmtxeW3q1Kny2WefydKlS6WgoEBGjRolJ554ogwZMiT2nLq6OnnyySdlxowZUl9fL2PGjJEzzjhDevXqldN196oXX3xRnn76aTn00EPllFNOMffRhq1bs2aNPPXUU/Lll19KbW2tDBo0SM4++2zZbLPNYsV8n3vuOXn33XelsrJSttxyS7MdDh48OMufaH6IRCKmfT788ENz7OvTp4/svffecswxx4jP5zPPoQ2bmzt3rrz88svmRA5r166Viy++WHbZZZfY4+m02YYNG+TRRx+Vzz//3LT1rrvuKqeeeqp069ZNunobNjQ0yDPPPCOzZs2SlStXSlFRkYwePVqOP/54s43aunobtoTMYhZoQKOBjZbOufXWW02wOHnyZKmoqMjG4jrFTn7QQQeZNrr66qslHA7LjTfeKDU1NbHnPPHEE2YHvvDCC2XSpEnmYHDHHXfkdL296ocffpC3337bbHdOtGHL9IvimmuukWAwKFdeeaXcddddctJJJ0lxcXHsOS+99JK88cYbMmHCBLnpppuksLDQbLcaiCP6I0W3vdNPP9203wknnGC+wLXNaMPU9IfJJptsYtotmXS2u//93/+VxYsXm2Po5ZdfLt9++608+OCDXWazbKkNtZ00iNQfLfqdfNFFF8myZcvktttui3teV2/DFlnIuCuuuMJ6+OGHY7fD4bD1hz/8wZo6dSqtnYaKigrr2GOPtebMmWNuV1ZWWscdd5z18ccfx56zZMkS85x58+bRpg7V1dXWueeea3311VfWddddZz322GO0YZqeeuop65prrkn5eCQSsSZMmGC99NJLsft02zz++OOt6dOnsx1alnXzzTdb999/f1xb/PnPf7buuece2jBNelz79NNP27TdLV682PzdDz/8EHvOrFmzrN/85jfW6tWrra7ehsl8//335nllZWXmNm3YMjKLGabp7gULFpgUt83v95vb3333XaYX1ylVVVWZyx49ephLbU/NNjrbdOjQodKvXz/aNMHDDz8sY8eOle222y7uftqwdf/5z39k0003lTvvvNN08V166aXyzjvvxB7X7ivtWnW2rXZn6fAS9u0oHULyzTffmKyN+vHHH2XevHlmm6QN2yed7U4vNQNuD5dQerzUrlTtaUDy7xltH21L2rB1jFnMsHXr1plxO4lj6fS2fQBFatp2jz/+uGyxxRay8cYbm/v0QKldg87uQFVaWmoeQ9RHH31kulpuvvnmZk1CG6b3paxdqL/+9a/lqKOOkvnz58tjjz1mtr199tkntq3pdsd2mNyRRx4p1dXVcsEFF5gfybo/H3fccbLXXnvFtkPasG3SaTO9LCkpiXs8EAiYH9wcIyVpt/Q//vEP2WOPPWLBIm3YMoJFeMojjzxixozccMMNuV6VvLJq1SoTZOtYG50khLbTwEYzMzroXY0YMUIWLVpkAkgNFtG6jz/+WKZPny7nnnuuDBs2zGQWdbvs3bs3bQjP9P7peFqlPQhID8FihumvO/1FnfhrTm8zc7f1QPGLL74wE1j69u0bu1/bTXdwnQXozC7qhCHatKmbWdvjsssuiwt+dIC2zsy/6qqraMNWaECz0UYbxd2ntz/99NPYdmhvd/pc53aoA+shZib5EUccYTI2SnsHysrKzMQXDbhpw7ZLp830Odqr5aRDd3TSFsfI5oGi/ri+9tprY1lF2rB1jFnMMO2y0nFPOm7H+aWtt3U8D5rTshAaKGr5HN2BBwwYEPe4tqd2qcyePTt2n3bp6w5PmzaNT7r99tvN7D77n2bJ9txzz9h12rBlOvQhcaiI3u7fv7+5rtulfvE6t0Md96RjwtgOm2ak6o9lJ72t+zht2D7pbHd6qT+m9UejTb9ztN0p2RYfKK5YscJUPejZs2dcO9OGLSOzmAXjxo2T++67zwQ5uqO+/vrr5iBKV1ZyGihq15VOKOjevXssK6u/+rRLVS/3228/U45Ix+Doba2FpTs3X9JR2m72GE+bltfQA6J9P23YMh2rqF8iL7zwguy+++7my1jr2v3hD38wj+tgeK1bqY9rfTv9EtfabZrt2XnnnTt41OgcdtxxR9M+OvlMs7LaDf3qq6/Kvvvuax6nDZPTMmEaxDjHz2rb6fFO27K17U7bevvttzdlXrS8jgZGeozU7dhZR7CrtqEG2zpxTcd0a++LJnDs7xl9XJM8tGHLfDolupXnoB2060/ri+kGqV0FWthz8803py2T+M1vfpO0XbQYsh1g2wWldRKHHggpyt2666+/3mx7iUW5acPUtJanFjPXLx39UtYA8oADDmhWHFlnSWt2R4sja103ZwH5rkwntzz77LOml0C7STVQ0S5prTmrX8iKNmxuzpw5ZvhNIi1oPnHixLTaTLuc9Ye3s6C0ngyiqxSUbqkNjz32WDnnnHOS/t11110n22yzjbne1duwJQSLAAAASIkxiwAAAEiJYBEAAAApESwCAAAgJYJFAAAApESwCAAAgJQIFgEAAJASwSIAAABSIlgEAABASgSLAPKent0i1ZmAupqXXnpJzj//fHNKM7d9+eWX8vvf/17WrVvn+rIBZA/nhgbgKf/+97/l/vvvj90OhULm/K16juuxY8ea8wzrubA7as2aNeb0abvssos5LWJnoKeC02BRAza/3/1cgJ6feNCgQTJ16lQ5+eSTXV8+gOwgswjAkzRTqOdzPeOMM+Tggw829z3xxBNy8cUXy08//RT33GOOOUaeeuqpNr3+2rVrZcqUKfLjjz9KZ/Hee+9JOBw252POlQMPPNAE4XqeaACdA8EiAE/SLOIvf/lLk0k86qij5KqrrpJrrrlGKioq5LbbbpO6urrYcwOBgBQUFEhXp1nZnXbaKadtseuuu0p9fb18/PHHOVsHAJlFNzSAvLHtttuaLOL//d//yQcffCAHHHBAbMyiZgn10vb111/L888/L4sXLzbZtj59+phA5vjjj5c5c+bIpEmTzPO0y9vu9j777LNln332kW+//VbeeOMN+f77701wWlpaGvtbZyB23333ySeffCL33HOPPPzwwzJ79mzz+N577y0nnnhiXFewjiF888035d1335UVK1ZIt27dZNNNN5XjjjtONttss9jz9H299tprsmTJEvNaY8aMMa/Vr1+/Fttm5cqVJuP661//utn9mqHV1ygqKjLd1KtXr5bhw4fL6aefLiNHjmz2fu666y7zfrSd9G80WNfs7qJFi+Sxxx6TH374QXr27GnaY88994xbnraVvvZ//vMf2W+//dr8GQPwHjKLAPKKZhvtYDAVDRBvueUWaWhoMN3ZJ510ksm4zZs3zzw+dOjQ2IQYDTg1mNJ/W221lblPs2K1tbXyq1/9Sk477TQTsGmgd++99zZblgaBkydPNsGTjhXceuut5dVXXzVdsU5//etf5fHHHzdB3wknnCBHHnmkGY+pAanthRdeMAHb4MGDzZg/Dfw0AL3uuuuksrKyxXax35sGoMl89NFH8vLLL5v3qwGqBpF33HGHaaPE93PTTTdJ3759TYA5YMAAefTRR03WUt+nBra6/jpuVNtDXyfRiBEjYusDIP+RWQSQVzSI0WzXzz//nPI5GkhqEHTFFVdISUlJs8d79eplurk1Ezlq1KhYAGrTIMmZQdQASyduaEZz1apVcVk+7XL9xS9+IePHjze3NcC87LLLZNq0aea6+uabb0ywdcghh8ipp54a+9vDDjtMLMsy18vKysz6/Pa3v5Wjjz469hydgKOv969//Svu/kRLly41lxrcJaPrrRlQnSykhgwZYrrzv/rqK9lxxx3j3s9ee+1lsolKM4dnnnmmPPDAA3LeeefJ7rvvbu7fbrvtzKxrfV+JM9EHDhwo69evj2VlAeQ3MosA8o524bY0gaK4uNhcaldoe0rIOAPFmpoaUwpGg0oN7BYuXNjs+XZQaNtyyy3jgtlPP/1UfD6fHHvssc3+Vu+3n6Ovr8GYLs/+p4GtBqraJdySDRs2mLGb2jbJaEBrB4r2OqpkQff+++8f15YaWBYWFprXsOl9+liyzKLd/howAsh/ZBYB5B0N4FrKWGnApWMDtev3H//4h4wePdpk6Hbbbbe0SspoFu7ZZ581wWZi96+Wp3HSruTE7KUGS86/04Csd+/eccFaIh3HqMHiueeem/TxYLBjh+vEMY/2uiS+v2TvRzO5mtG1A1vn/RqkAujcCBYB5BWdnKEBm3Z1tpQZ1Aksmo374osvTLHoGTNmmAkyV199dYsBo2Yi//SnP5kg6IgjjjDjGzWrpnUZdSKM3W1sy1Q9Q12uBmPadZ7sNVNlDJ3Bn07k0YxrsjqUqdYz3ffTlvdpB5DJhgAAyD8EiwDyis4WtgtAt0SDG80o6j+dLKKTR5555hkzflDH2yVmyWw643f58uUyceJEM6vZ1tKEmtZoYKtjAzWISpVd1K5mDdx0zKF28baVBrVKu4V1NnIu6TrohB+CRaBzYMwigLyhgd4///lPE1AllmxxStY1ap+lxZ79q9nCZN2wdgbNmXHT66+//nq711vL7uhraCmfRPZytJtcl60lgBKzfXq7tfF/OqZSzZ8/X3JNx3Xa6wMg/5FZBOBJs2bNMjN8tXu2vLzcdClrdk/H3l166aUtFp7WgEtrJeqM5/79+5tZuW+99ZYZd2dP7NBsn44tfPvtt023rQaPm2++ucnq6WN///vfTdezjsvTyScdGZun3d8641prN+rYRC3FowGgrqM+pjUMNbOoJW2efvppMzN65513Nl3PmqWbOXOmmXRy+OGHp1yGrvOwYcNMqZ1c1jfUttZ6jwcddFDO1gFAZhEsAvAku8C2Tuywzw2t3cnpnBtaaypqkKWnv9OMnHaJav1DLfGiwZ/9utrVrMHZQw89ZMb72UW5tVSNFp9+8cUXzYQPzfppQHfJJZe0+/3oa+t70HXSUxPqemjNQmcGTmsvao1FLcptZyE1ONZuc31PrdG20XbTs9vk6iwuGlhrmzlnTgPIbz4rsb8DAJCXdOKPfbaWXGUXNeurgfkpp5ySk+UDyDzGLAJAJ6HZSu2q1jO1tKe+ZEfprHOdHGQX9AbQOZBZBAAAQEpkFgEAAJASwSIAAABSIlgEAABASgSLAAAASIlgEQAAACkRLAIAACAlgkUAAACkRLAIAACAlAgWAQAAkBLBIgAAACSV/wecvySkCTwVjwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# note this is a small dataset, so the data histogram (red line) will not be smooth\n", + "postprocess.plot_nena(nena_result)" + ] + }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Filter localizations\n", "\n", - "Filter localizations, i.e., via sx and sy: Remove all localizations that are not within a circle around a center position." + "Filter localizations, i.e., via sx and sy, i.e., the width and height of the fitted Gaussians to the single-emitter images:" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Length of locs before filtering 2399, after filtering 163.\n" + "Length of locs before filtering 2399, after filtering 2037.\n" ] } ], "source": [ - "sx_center = 0.82 # units of camera pixels! in our case, pixelsize = 130 nm\n", - "sy_center = 0.82\n", + "sx_center = 0.87 # units of camera pixels! in our case, pixelsize = 130 nm\n", + "sy_center = 0.87\n", + "photons_min, photons_max = (100, 15_000)\n", "\n", "radius = 0.04 \n", "\n", - "to_keep = (locs.sx-sx_center)**2 + (locs.sy-sy_center)**2 < radius**2\n", + "to_keep = (\n", + " ((locs[\"sx\"] - sx_center) ** 2 + (locs[\"sy\"] - sy_center) ** 2 < radius ** 2)\n", + " & (locs[\"photons\"] > photons_min)\n", + " & (locs[\"photons\"] < photons_max)\n", + ")\n", "\n", "filtered_locs = locs[to_keep]\n", "print(f'Length of locs before filtering {len(locs)}, after filtering {len(filtered_locs)}.')" @@ -709,15 +794,22 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Undrifting\n", - "Real world data requires undrifting (unless an active stage stabilization is implemented). Here, we show the interface to use AIM, see https://doi.org/10.1126/sciadv.adm7765.\n", + "Since Filter GUI saves the filter parameters use, you can use the metadata of previously filtered localizations to apply the same filter ranges to another datasets, see functions `picasso.lib.extract_filter_steps` and `picasso.lib.apply_filter_steps` let you apply the same " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Drift correction\n", + "Real world data requires drift correction (for historical reasons named undrifting in Picasso) unless an active stage stabilization is implemented. Here, we show the interface to use AIM, see https://doi.org/10.1126/sciadv.adm7765.\n", "\n", "Note: we use synthetic data here, so undrifting is not needed." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -771,22 +863,15 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Undrifting by AIM (1/2): 0%| | 0/49 [00:00\n", " \n", " 0\n", - " -0.073586\n", - " -0.040849\n", + " -0.012017\n", + " -0.076087\n", " \n", " \n", " 1\n", - " -0.072025\n", - " -0.039640\n", + " -0.012011\n", + " -0.073864\n", " \n", " \n", " 2\n", - " -0.070484\n", - " -0.038453\n", + " -0.012007\n", + " -0.071676\n", " \n", " \n", " 3\n", - " -0.068963\n", - " -0.037287\n", + " -0.012004\n", + " -0.069523\n", " \n", " \n", " 4\n", - " -0.067462\n", - " -0.036142\n", + " -0.012003\n", + " -0.067405\n", " \n", " \n", "\n", @@ -856,14 +941,14 @@ ], "text/plain": [ " x y\n", - "0 -0.073586 -0.040849\n", - "1 -0.072025 -0.039640\n", - "2 -0.070484 -0.038453\n", - "3 -0.068963 -0.037287\n", - "4 -0.067462 -0.036142" + "0 -0.012017 -0.076087\n", + "1 -0.012011 -0.073864\n", + "2 -0.012007 -0.071676\n", + "3 -0.012004 -0.069523\n", + "4 -0.012003 -0.067405" ] }, - "execution_count": 11, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -876,7 +961,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Note: Picasso supports two other undrifting modalities - RCC (https://doi.org/10.1364/OE.22.015982, ``picasso.postprocess.undrift``) and undrifting from picked localizations (usually based on fiducial markers, ``picasso.postprocess.undrift_from_fiducials`` and ``picasso.postprocess.undrift_from_picked``)" + "Note: Picasso supports two other undrifting modalities - RCC (https://doi.org/10.1364/OE.22.015982, ``picasso.postprocess.undrift``) and undrifting from picked localizations (usually based on fiducial markers, ``picasso.postprocess.undrift_from_fiducials`` or ``picasso.postprocess.undrift_from_picked``)" ] }, { @@ -889,25 +974,26 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "163 locs saved to data/raw_movie_locs_fitted_sigma_filter.hdf5.\n" + "2037 locs saved to data/raw_movie_locs_fitted_sigma_filter.hdf5.\n" ] } ], "source": [ - "# Create a new dictionary for the new info\n", + "# it's also possible to create your own metadata! simply append a new dictionary to info:\n", "new_info = {}\n", "new_info[\"Generated by\"] = \"Picasso Jupyter Notebook\"\n", "new_info[\"Filtered\"] = 'Circle'\n", "new_info[\"sx_center\"] = sx_center\n", "new_info[\"sy_center\"] = sy_center\n", - "new_info[\"radius\"] = radius\n", + "new_info[\"sx_sy_filter_radius\"] = radius\n", + "new_info[\"photon_min_max\"] = [photons_min, photons_min]\n", "\n", "info.append(new_info)\n", "new_path = path.replace('.hdf5', '_fitted_sigma_filter.hdf5')\n", @@ -927,7 +1013,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -963,7 +1049,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -976,7 +1062,7 @@ "ON Measured 291.22 ms \t Simulated 333.00 ms\n", "OFF Measured 52888.85 ms \t Simulated 66666.67 ms\n", "\n", - "Note: some error is expected due to low localization count per site!\n" + "Note: some error is expected due to low localization count per site.\n" ] } ], @@ -1003,7 +1089,25 @@ "\n", "print(f'ON Measured {(np.mean(linked_locs_dark.n)*integration_time):.2f} ms \\t Simulated {tau_b:.2f} ms')\n", "print(f'OFF Measured {(np.mean(linked_locs_dark.dark)*integration_time):.2f} ms \\t Simulated {tau_d:.2f} ms')\n", - "print('\\nNote: some error is expected due to low localization count per site!')" + "print('\\nNote: some error is expected due to low localization count per site.')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is it for the description of simple analysis that can be done with Picasso API. There's a lot more to explore, for example, in `picasso.postprocess` the following functions might be useful:\n", + "\n", + "- `pick_kinetics`\n", + "- `pick_properties`\n", + "- `pair_correlation`\n", + "- `nn_analysis`\n", + "- `distance_histogram`\n", + "- `groupprops`\n", + "\n", + "You can find more functions by exploring the script.\n", + "\n", + "See more notebooks on clustering of localizations and SPINNA." ] }, { diff --git a/samples/sample_notebook_3_clustering.ipynb b/samples/sample_notebook_3_clustering.ipynb index 3c4d2a4f..9d234c14 100644 --- a/samples/sample_notebook_3_clustering.ipynb +++ b/samples/sample_notebook_3_clustering.ipynb @@ -5,7 +5,13 @@ "metadata": {}, "source": [ "# Sample Notebook 3 for Picasso\n", - "This notebook shows how to perform DBSCAN clustering with picasso.\n" + "This notebook shows how to perform clustering with the `picasso` library. \n", + "\n", + "We assume that you have a working Picasso installation, for example, from PyPI (see: https://github.com/jungmannlab/picasso?tab=readme-ov-file#via-pypi).\n", + "\n", + "As you will see below, many values, such as x and y coordinates are saved in the units of camera pixel size. For more details, see https://picassosr.readthedocs.io/en/latest/files.html#localization-hdf5-files.\n", + "\n", + "The sample data was created using Picasso: Simulate." ] }, { @@ -39,6 +45,13 @@ "print(f'Loaded {len(locs)} locs.')" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "markdown", "metadata": {}, From 9c00dc32e10c9201c1e0a46995671acb73437b96 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 28 May 2026 13:00:49 +0200 Subject: [PATCH 39/42] expand notebook 3 + small improvements in docstrings --- picasso/clusterer.py | 7 +- samples/sample_notebook_3_clustering.ipynb | 401 +++++++++++++++++++-- 2 files changed, 367 insertions(+), 41 deletions(-) diff --git a/picasso/clusterer.py b/picasso/clusterer.py index 48c9f452..6c6f1c3f 100644 --- a/picasso/clusterer.py +++ b/picasso/clusterer.py @@ -296,7 +296,7 @@ def cluster( radius_z: float | None = None, pixelsize: float | None = None, return_info: bool = None, # TODO: change to true in v0.11.0 and remove in v0.12.0 -) -> pd.DataFrame: +) -> tuple[pd.DataFrame, dict] | pd.DataFrame: """Cluster localizations from single molecules (SMLM clusterer). The general workflow is as follows: @@ -347,6 +347,9 @@ def cluster( Clusterered localizations, with column 'group' added, which specifies cluster label for each localization. Noise (label -1) is removed. + info : dict, optional + Dictionary containing clustering information, only returned if + return_info is True. """ if return_info is None: return_info = False @@ -586,7 +589,7 @@ def hdbscan( pixelsize: float | None = None, cluster_eps: float = 0.0, return_info: bool = None, # TODO: change to true in v0.11.0 and remove in v0.12.0 -) -> pd.DataFrame: +) -> tuple[pd.DataFrame, dict] | pd.DataFrame: """Perform HDBSCAN on localizations. See Campello, et al. PAKDD, 2013 (DOI: 10.1007/978-3-642-37456-2_14). diff --git a/samples/sample_notebook_3_clustering.ipynb b/samples/sample_notebook_3_clustering.ipynb index 9d234c14..682c2a75 100644 --- a/samples/sample_notebook_3_clustering.ipynb +++ b/samples/sample_notebook_3_clustering.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "# Sample Notebook 3 for Picasso\n", - "This notebook shows how to perform clustering with the `picasso` library. \n", + "This notebook shows how to perform clustering with the `picasso` library. DBSCAN and SMLM clusterer are described. Additionally frame analysis for sticking events is shown. We conclude with RESI and molecular mapping.\n", "\n", "We assume that you have a working Picasso installation, for example, from PyPI (see: https://github.com/jungmannlab/picasso?tab=readme-ov-file#via-pypi).\n", "\n", @@ -38,20 +38,13 @@ "import os.path as ospath\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", - "from picasso import io, clusterer, postprocess, g5m\n", + "from picasso import io, lib, clusterer, postprocess, g5m\n", "path = 'data/raw_movie_locs.hdf5'\n", "locs, info = io.load_locs(path)\n", "\n", "print(f'Loaded {len(locs)} locs.')" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -77,19 +70,28 @@ " min_samples: int,\n", " min_locs: int = 10,\n", " pixelsize: float | None = None,\n", + " radius_z: float | None = None,\n", " return_info: bool = None\n", ") -> tuple[pd.DataFrame, dict] | pd.DataFrame\n", " Perform DBSCAN on localizations.\n", "\n", " See Ester, et al. Inkdd, 1996. (Vol. 96, No. 34, pp. 226-231).\n", "\n", + " For 3D data with ``radius_z`` set, anisotropic clustering is used:\n", + " z coordinates are scaled by ``radius / radius_z`` so that the\n", + " isotropic DBSCAN search with epsilon ``radius`` corresponds to an\n", + " ellipsoidal neighborhood with semi-axes\n", + " ``(radius, radius, radius_z)`` in the original space (same approach\n", + " as ``cluster_3D``).\n", + "\n", " Parameters\n", " ---------\n", " locs : pd.DataFrame\n", " Localizations to be clustered.\n", " radius : float\n", - " DBSCAN search radius, often referred to as \"epsilon\". Same units\n", - " as locs.\n", + " DBSCAN search radius in the xy plane, usually referred to as\n", + " \"epsilon\". Same units as ``locs[\"x\"]`` / ``locs[\"y\"]``\n", + " (camera pixels).\n", " min_samples : int\n", " Number of localizations within radius to consider a given point\n", " a core sample.\n", @@ -98,6 +100,10 @@ " fewer localizations will be removed. Default is 0.\n", " pixelsize : float, optional\n", " Camera pixel size in nm. Only needed for 3D.\n", + " radius_z : float, optional\n", + " DBSCAN search radius in z (camera pixels). If None (default),\n", + " the clustering is isotropic and uses ``radius`` in all\n", + " dimensions. Only used for 3D.\n", " return_info : bool, optional\n", " If True, returns a tuple of (locs, info), where locs is the\n", " clustered localizations and info is a dictionary containing\n", @@ -128,24 +134,210 @@ "source": [ "eps = 5 / 130 # from nm to camera pixels\n", "min_samples = 4\n", - "dbscan_locs = clusterer.dbscan(locs, eps, min_samples)" + "dbscan_locs, dbscan_info = clusterer.dbscan(locs, eps, min_samples, return_info=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We can also extract the cluster centers in the format of localizations." + "We can also extract the cluster centers in the format of localizations. See https://picassosr.readthedocs.io/en/latest/files.html#molecular-maps-cluster-centers-hdf5-files for explanation of the columns" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
framestd_framexystd_xstd_yphotonssxsybglpxlpyellipticitynet_gradientn_locsn_eventsareaconvexhullgroup
01283.4073491096.22827125.58850923.3931870.0150560.0177274676.5463870.8713610.87050039.3873060.0028970.0034121.00098910423.1308592770.0033760.0033600
11888.6922612082.97973614.23465822.2330700.0213420.0145075850.6430660.8554240.85900348.7574920.0059190.0040230.99583312998.0068361350.0040370.0016622
22176.9799801420.31994614.19753521.9345650.0169750.0222677494.5107420.8670920.87411541.0957950.0024010.0031490.99196517770.90429750100.0048380.0050073
32306.9707031433.19873014.21084322.0736790.0151390.0199757727.9970700.8717350.87612739.9271580.0025960.0034260.99498718290.5292973490.0038730.0027454
41077.944458757.89215113.6093517.9112860.0183430.0179664337.9306640.8765070.86314740.3520200.0043230.0042351.01547810069.5859381840.0041420.0028865
\n", + "
" + ], + "text/plain": [ + " frame std_frame x y std_x std_y \\\n", + "0 1283.407349 1096.228271 25.588509 23.393187 0.015056 0.017727 \n", + "1 1888.692261 2082.979736 14.234658 22.233070 0.021342 0.014507 \n", + "2 2176.979980 1420.319946 14.197535 21.934565 0.016975 0.022267 \n", + "3 2306.970703 1433.198730 14.210843 22.073679 0.015139 0.019975 \n", + "4 1077.944458 757.892151 13.609351 7.911286 0.018343 0.017966 \n", + "\n", + " photons sx sy bg lpx lpy \\\n", + "0 4676.546387 0.871361 0.870500 39.387306 0.002897 0.003412 \n", + "1 5850.643066 0.855424 0.859003 48.757492 0.005919 0.004023 \n", + "2 7494.510742 0.867092 0.874115 41.095795 0.002401 0.003149 \n", + "3 7727.997070 0.871735 0.876127 39.927158 0.002596 0.003426 \n", + "4 4337.930664 0.876507 0.863147 40.352020 0.004323 0.004235 \n", + "\n", + " ellipticity net_gradient n_locs n_events area convexhull group \n", + "0 1.000989 10423.130859 27 7 0.003376 0.003360 0 \n", + "1 0.995833 12998.006836 13 5 0.004037 0.001662 2 \n", + "2 0.991965 17770.904297 50 10 0.004838 0.005007 3 \n", + "3 0.994987 18290.529297 34 9 0.003873 0.002745 4 \n", + "4 1.015478 10069.585938 18 4 0.004142 0.002886 5 " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# save cluster centers too\n", - "centers = clusterer.find_cluster_centers(dbscan_locs)" + "centers = clusterer.find_cluster_centers(dbscan_locs)\n", + "centers.head()" ] }, { @@ -170,12 +362,7 @@ ], "source": [ "base, ext = ospath.splitext(path)\n", - "dbscan_info = {\n", - " \"Generated by\": \"Picasso DBSCAN\",\n", - " \"Min samples\": min_samples,\n", - " \"Radius (cam. pixels)\": eps,\n", - "}\n", - "dbscan_info = info + [dbscan_info]\n", + "dbscan_info = info + [dbscan_info] # clusterer.dbscan returns only the dictionary with dbscan metadata\n", "io.save_locs(path.replace(\".hdf5\", \"_dbscan.hdf5\"), dbscan_locs, dbscan_info)\n", "io.save_locs(path.replace(\".hdf5\", \"_dbscan_centers.hdf5\"), centers, dbscan_info)\n", "print('Complete')" @@ -208,7 +395,7 @@ " radius_z: float | None = None,\n", " pixelsize: float | None = None,\n", " return_info: bool = None\n", - ") -> pd.DataFrame\n", + ") -> tuple[pd.DataFrame, dict] | pd.DataFrame\n", " Cluster localizations from single molecules (SMLM clusterer).\n", "\n", " The general workflow is as follows:\n", @@ -259,6 +446,9 @@ " Clusterered localizations, with column 'group' added, which\n", " specifies cluster label for each localization. Noise (label -1)\n", " is removed.\n", + " info : dict, optional\n", + " Dictionary containing clustering information, only returned if\n", + " return_info is True.\n", "\n" ] } @@ -282,17 +472,12 @@ ], "source": [ "_, nena = postprocess.nena(locs, info)\n", - "radius_xy = 2 * nena\n", + "radius_xy = 2.5 * nena\n", "min_locs = 10\n", - "frame_analysis = False # useful for rejecting sticking events, see below\n", - "clustered_locs = clusterer.cluster(locs, radius_xy, min_locs, frame_analysis)\n", + "frame_analysis = False # useful for rejecting sticking events, see below (we will do slightly more intricate analysis)\n", + "clustered_locs, new_info = clusterer.cluster(locs, radius_xy, min_locs, frame_analysis, return_info=True)\n", "centers = clusterer.find_cluster_centers(clustered_locs)\n", - "new_info = {\n", - " \"Generated by\": \"Picasso SMLM clusterer\",\n", - " \"Min locs\": min_locs,\n", - " \"Radius (cam. pixels)\": radius_xy,\n", - "}\n", - "info = dbscan_info + [new_info]\n", + "info = info + [new_info]\n", "io.save_locs(path.replace(\".hdf5\", \"_clustered.hdf5\"), clustered_locs, info)\n", "io.save_locs(path.replace(\".hdf5\", \"_clustered_centers.hdf5\"), centers, info)\n", "print('Complete')" @@ -303,9 +488,9 @@ "metadata": {}, "source": [ "## Frame analysis\n", - "Sticking events can happen, for example, due to the fluorophore sticking to a non-specific structures in the sample. This results in a series of localizations that are detected in a row. Therefore, these can be filtered out by rejecting the clusters whose st. dev. of frame is low.\n", + "Sticking events can happen, for example, due to the fluorophore sticking to non-specific structures in the sample. This results in a series of localizations that are detected in a row. Therefore, these can be filtered out by rejecting the clusters whose st. dev. of frame is low. On the other hand, normal signal is repetitive with higher st. dev. of frame.\n", "\n", - "Note: here, we use synthetic data without sticking simulated, thus in real data the st. dev. frame histogram will likely look different." + "Note: here, we use synthetic data without sticking simulated, thus in real data the st. dev. frame histogram will look different." ] }, { @@ -315,7 +500,7 @@ "outputs": [ { "data": { - "image/png": "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//3vJQbBBAAACHS4Wb58ue3YsaPc13fu3OmmAQAAqE6H7fLBOoMq+VRxAACAwPW5eeutt+ztt99OPJ4+fbrNnz//gOl2795tn332mXXp0iU1SwkAAHA4ws2+fftcc1PcN998YxkZGSWm0WNdyO/888+3Sy655FBmDwBV9u2wf493V102VPH9NSYxVA2QtnBzwQUXuJuMGDHCfv7zn1vXrl1TukAAAABpORX84YcfrtI/BgAACFS4SW6a0hhSu3btslgsdsDrGiEcAAAg8OFGfW+efPJJ+/DDD23//v3lTjd16tTv+i8AAACqL9xMnDjRPv74Y7vwwgutffv27qrEAAAAoQ03f/3rX6137942ZMiQ1C4RAABAOi7ip9O9c3JyqvK/AQAAghNuzjzzTFu4cGFqlwYAACBdzVKnn366Gztq/Pjxdt5551mDBg3siCMOzErf+973qrqMAAAAhz/cjBkzJnH/b3/7W7nTcbYUAAAIRbj55S9/mdolAQAASGe4Ofvss1Px/wEAAILRoRgAAMCrmptHHnnkoNNohHCarwAAQCjCzbJlyw54TsMwbN++3f2tW7euuxYOAABAqEcFLy4utnnz5tmcOXPstttuq8qyAQAApL/PTWZmpv34xz+2E0880Z544olUzx4AAODw1NwcTIsWLeydd95J6TzV3DVt2jR79913XfPXscceaz169LCLL77Y9e8BAAA4bOFGF/ZLdZ+bmTNn2uuvv24jRoywpk2b2tq1a13H5qOOOsouuuiilP4vAAAQsXCTn59f5vO7du2yFStW2Lp166xfv36WSitXrrSuXbvaySef7B43atTI3nvvPVu9enVK/w8AAIhguHnhhRfKfL527dp23HHH2bBhw+zcc8+tyrIdoG3btjZ//nzbtGmTNWnSxNavX2+ffvqpXXnlleW+p6ioyN3i1HyVnZ2duF8V8fdHuUks6mUQ9fVHaoR9++F7QBlkBGxfmBGLxWIWEupz8/zzz9vs2bPdIJ16PGjQIOvfv3+571EfneRaplatWlleXl41LTHwbxt6d6U4UKZmcxZRMkAY+twcDh988IFrhvrVr35lzZo1czU3Tz/9tNWvX7/c4SAUfPr06ZN4HE+VhYWF7rT1qtC8cnNzraCgwEKUEVMq6mUQ9fVHamzevDnURcn3gDLIqIZ9oc7GzsnJqdy0Vf1ny5cv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2AZk4MCBZb5vyJAh1rdv39BvAxqqZ+HChfbFF1+4wZh17TStm/b/cana/+s1zWfDhg3WoEEDu/jiiwNxOZMZBykDbePaF/71r39166nP79RTT7VBgwa5/UJF24qGPdLwR9VVBoSbMjRr1sxuu+22xGNdFTnuT3/6ky1evNhuvPFG92HqS3r//ffbb37zm8RFBe+++263sd95551uJ/GHP/zBbdjawMNAy6/1iPv888/duvzwhz9MPKeR2S+77LLEY30R4sJWBrpOki4pcM4559h99913wOuzZs2yl19+2e24GzVqZFOnTrXx48fbhAkTEuv9+9//3q3nrbfeat9++6098sgj9thjj7kvtOzevduVhUKBhgBRmT766KNWu3ZtFwyDXAbaoeuAp52PptEOTkOZ3HvvvXbPPfeUmFY7teT1ST6wB7kMDrYNyEknnWTDhw8vdxA/n7cBmThxYonHCrt//OMfXYjzYRtQuOvVq5e1bt3afX7PP/+8W1Z9z+PrkIr9v34g6Htz/vnn23/+53+6i9CqHPUebWNBLoOtW7e6289+9jNr2rRpIshpPW+66aYS89J3JXl9ksNPtZSBTgXHv02dOjU2atSoMotk165dsUGDBsU++OCDxHMbN26MXXrppbFPP/3UPV68eHFs4MCBsW3btiWmefXVV2NXXnllrKioKJRF/dRTT8Wuv/762P79+93j22+/3T1XnjCXgT7LDz/8MPFY6zxs2LDYrFmzSmwHgwcPjr333nvu8YYNG9z7Vq9enZhmyZIlrgz++c9/Jtb/6quvLrH+zzzzTGzkyJGxoJdBWVatWuWmKywsTDw3fPjw2EsvvVTue8JSBmWt/x/+8IdYXl5eue+J4jag8hg7dmyJ53zZBmTHjh2uHJYtW5bS/f+f//zn2I033ljifz3wwAOxO++8Mxb0MijLggULYpdffnmsuLi40ttPdZQBfW7KUFBQYNddd51df/317teY0qmsXbvWpVn96og7/vjj3SiwK1eudI/1t3nz5iWqKZVENZiYqt/CRk0S7777rvXs2dNVtcfpuaFDh7q0/txzz7lffXE+lYF+YehikZ07dy7xC0RXx07+zPXLU7924rSNqLxWr16dmOb73/9+iV/7qtLWaPaqCQkb/QLX+iX/GpOZM2faNddcY7/+9a9dE4e+L3FhLwP9qlUThGpi9Gv1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", + "image/png": "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XiHcAAAArxBuAACAVwg3AADAK4QbAADgFcINAADwCuEGAAB4hXADAAC8QrgBAABeIdwAAACvpFuALF++3GbOnGnr1q2zHTt22LBhw6xz586x5yORiE2ZMsXmzZtne/bssVNOOcUGDhxoJ5xwQlLLDQAAPGi5+eKLL1wQiff+++/bbbfdZoMGDbLx48dbUVFRpV5z//791qJFCxswYECpz8+YMcPeeust9/oPPvig1a5d28aMGWMHDhyoajUAAIBnqhxuXn/9dVu/fn3s/tdff23PP/+81atXz9q1a+dCSMnwcygdO3a0vn37FmutiW+1mTNnjl199dV21llnWfPmze322293LTyfffZZVasBAAA8U+VuqW+++cbOPvvs2P2//vWvlpmZaaNHj3YtKuPGjXOPXXXVVQkp6NatW23nzp126qmnxh47+uijrXXr1rZy5Urr0qVLqX9XUFDgblFpaWmunNHfwypa9jDXobJSsc5IHam0XafiZznV6pyW5PpWOdzs27cvFhJk6dKldvrpp7tgIwod8+fPT0wpzVywkWOPPbbY47offa4006ZNs7y8vNj9li1bWm5urmVlZZkPsrOzLdWEtc4bkl0ABNaGnp0sbJrNXpSyn+XDkWp1zk5Sfascbho1amRr1qyxiy66yLZs2WIbNmywXr16xZ7//vvvLSMjw5ItJyenWLmiKTI/P98KCwstrFQPbTR679VllwpSsc5AUG3evLnKf5uKn+VUq3NaNdQ3PT29wg0TVQ435513nmsR2b59u23cuNHq1KnjxsJErV27NqFnMdWvX9/93LVrlzVo0CD2uO5rEHJZFLDKClk+bGCqgw/1qIxUrDMQNIn4DKbiZznV6hxJUn2rPKBYA3s1nua7775zrTj//d//7QJOtNVm2bJl1qlT4ppaGzdu7AKOztKK2rt3r61evdratGmTsP8DAADCrcotNzVr1rQbbrjB3UqqW7euO3OqKuN41IQVP4hYZ2Tp9RSgLr/8cps6daprEVLYmTRpkmvFiW8xAgAAqa3K4WbUqFGu9aZDhw6lPv+Pf/zD3njjDRs5cmSFX1NjePS6URMmTHA/u3btakOGDLErr7zSXQvnueeec602uojf8OHDrVatWlWtBgAA8Ez64VxN+OKLLy7z+d27d7tlKqN9+/buCsTlDVC6/vrr3Q0AAOCIzi2l7qX4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fb1Gu3btXLlVvhYtWtitt97qxva899577rX0Xp588snuf2u25XhqpdPfrly50o0latWqld1www1uhnkgFRFugJDTwVsHNY270cGwLDqgP/fcc9a6dWt3sBeFjcr64x//6ILNeeed5w7q6rJ5+OGHD1pOweWee+5xv+vgXa9ePVu6dKn7+x9++MF69uzputB0UFcQKRluFixYYDVq1DgoDJQnEonYI488YsuWLbMLL7zQhYToeBmFA3VBqRx6LxRGFIQUAuTEE08s9TW17Lx582z16tUucEjbtm1jz6v+Kr8CmMbiRIPHW2+95QYp631Sa47qM3bsWPuf//kfO+OMM4r9j3/+85+2aNEi9z7J9OnT3Xvau3dvN4hZj3///fcu5Dz77LMuiMX//wcffNCNG9J7qAD3wQcf2OjRo91N6xtINYQbIOTUIqGDm8700YFM39Y7dOjgWiHiu1EuuOACe/75593BV79Xxfr1612w6d69e6y1Rgf1J5980r766qtiy6rFRt00jz76aGwArv7uiSeecIOfL730Utd9c+6559q4ceMOCmcKAwo+CkAVpYCgg33fvn3t6quvjpVPoUJhQ78r0Kn+77//vgtPh3ov9PwXX3xha9euLXVZtcw89thjrmUm3u9///ti3VP633fffbe9+eabB4Ubvcbjjz8eC0YaEK73RF2Oep3MzEz3uN5PBR+17mhZhTmtU63r4cOHu2Ajem/vvPNOtw5GjBhR4fcP8AXXuQFC7tRTT7UHHnjAnRmlgKFv92PGjHHdPTrYJ5K6l0RnGMUreV8H3U8//dS1XOj33bt3x26nn3667d2714UFURdOzZo1XZiJUtDZuHGjCz6VLZ8CS/wgalFXmMqhlqNEUwArGWwkPtio1UV1/ulPf2rr1q07aNmf/exnxbqaoq0tZ599dizYiLqlROEmGjbVFabWoX/961+x91gtUnrNFStWuEAEpBpabgAP6GA4bNgw1/2hA57GesyePdu1KOh6LKUdfKtC13hR68Dxxx9f7PEmTZoUu68DrMbYzJ07191Ko2VE3UQ6EKtrRy0uoqCjwFPZU9ZVvgYNGhQLBBKtv55PtJLjX6I+//xz1/Ki9aGxSVHR1pV4JU9D1wBxadiwYamPKyyJgo08/fTTZZZPoUotQUAqIdwAHlE3lIKObgoczzzzTKnjWaqbWklE14XRoOXSNG/ePPZ7ly5dXFkVBDRORmVW4FHwCbrSzoxSi4nG/qilZsCAAS5wKaxpLMxHH3100PJqbSpNWY+XfJ/79+/v3rfSHHXUURWsCeAPwg3gKQ0wlR07dpTbalDZwcs6oOoMrfjWGo0ZiadQotYTdYmo2+xQzjrrLBfMol1TapHQmUVVKZ/Gx2jAcnzrzTfffBN7/khQl1xGRoYbUK2fUQo3iRRtQVOLTkXeZyBVMOYGCDkNoI1+gy9tfEx8CKldu7brLipJpyorAES7isrSsWNH91MXB4xX8r5aHDReRAd5jZ8pqeT/0Wnsp512mmux0XQICjoKPPFdKyqffh6qfApUb7/9drHH1UWnYKfxPkeC6q//Fz/eReNkPvvss4QHWAWcWbNmuXE2JR1qfQK+ouUGCLmXX37ZhRONT1GQ0bgbnRquVhC1VOiU6PiDoVo2dMaOuko0XkSDVHWas65yfKi5odT1oS4knZ6soKFTovV6askpqV+/fu6UbLVe6NRzjXvRWBENJNbfqNzxNHj4qaeecq+toBN/3R6NIVK3VfT6MmXRAGadOaSzhDS+Rl1fOhVcA6s16Lkqp75Xhc6G0nuss9j0filkvPPOO+7/lzyr7HBDlAaO6//o7Ci9N7rqsk5713uv1iudeg6kGsINEHK/+MUvXIuHWmo0eFfhRgNUddq1Lo4XHxJuuukmd60bHfwPHDjgxsNEz8CpKE1BoG4njR1RS4TGxugAqsfj6RRuHXTz8vJcC44O7jolXBccvPHGGw96XZ3tpfEr6lKq7FlS8Qd7nW49efJkF+40f5QCnMak6JT5I0XviULHjBkz7E9/+pMrg+qs1ptEhhtRmNPZcXqf9R6rBUfvvcZd6ZRwIBWlRUprzwYAAAgpxtwAAACvEG4AAIBXCDcAAMArhBsAAOAVwg0AAPAK4QYAAHiFcAMAALxCuAEAAF4h3AAAAK8QbgAAgFcINwAAwCuEGwAAYD75f8sS7cxz83e4AAAAAElFTkSuQmCC", "text/plain": [ "
" ] @@ -352,9 +537,8 @@ "mask = std_frame > std_frame_min\n", "centers_filter = centers[mask]\n", "\n", - "# we can also remove the clustered localizations based on the group column\n", - "valid_groups = centers_filter[\"group\"]\n", - "clustered_locs_filter = clustered_locs[np.isin(clustered_locs[\"group\"], valid_groups)]\n", + "# we can also remove the clustered localizations that correspond to the filtered centers\n", + "centers_filter, clustered_locs_filter = lib.sync_groups([centers_filter, clustered_locs])\n", "\n", "# save both clustered localizations and cluster centers\n", "new_info = {\n", @@ -372,7 +556,7 @@ "metadata": {}, "source": [ "## G5M - molecular mapping \n", - "Although not really a clustering algorithm, G5M (released in Picasso 0.9.5, DOI: 10.1038/s41467-026-70198-5) is a useful postprocessing tool for finding molecules' positions from single-molecule-resolution datasets. Below, the basic workflow is shown:" + "Although not really a clustering algorithm, G5M (released in Picasso 0.9.5, https://doi.org/10.1038/s41467-026-70198-5) is a useful postprocessing tool for finding molecules' positions from single-molecule-resolution datasets. Below, the basic workflow is shown:" ] }, { @@ -488,7 +672,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Running G5M...: 70%|███████ | 56/80 [00:06<00:02, 11.42it/s]" + "Running G5M...: 71%|███████ | 56/79 [00:06<00:01, 11.76it/s]" ] } ], @@ -504,6 +688,145 @@ "source": [ "io.save_locs(path.replace(\".hdf5\", \"_g5m.hdf5\"), g5m_mols, g5m_info)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# RESI\n", + "\n", + "Published in https://doi.org/10.1038/s41586-023-05925-9. RESI uses sequential imaging of the same target for sparser probing. From the analysis point of view, we simply run SMLM clusterer on all RESI channels and then merge the resulting cluster centers. One could use other algorithms such as G5M for the molecular mapping step.\n", + "\n", + "We do not provide a RESI dataset but it should be pretty clear by now how to use it once the data is loaded.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function resi in module picasso.postprocess:\n", + "\n", + "resi(\n", + " locs: list[pd.DataFrame],\n", + " infos: list[list[dict]],\n", + " radius_xy: float | list[float],\n", + " radius_z: float | list[float] | None = None,\n", + " min_locs: int | list[int] = 10,\n", + " apply_fa: bool = True,\n", + " save_clustered_locs: bool = False,\n", + " save_cluster_centers: bool = False,\n", + " resi_path: str | None = None,\n", + " output_paths: list[str] | None = None,\n", + " suffix_locs: str = '_clustered',\n", + " suffix_centers: str = '_cluster_centers',\n", + " progress_callback: Callable[[int], None] | Literal['console'] | None = None\n", + ") -> tuple[pd.DataFrame, list[dict]]\n", + " Perform RESI (REsolution by Sequential Imaging) analysis on\n", + " multiple channels.\n", + "\n", + " Clusters localizations from each channel using the SMLM clusterer,\n", + " extracts cluster centers, and combines them into a single DataFrame\n", + " with channel IDs.\n", + "\n", + " Parameters\n", + " ----------\n", + " locs : list of pd.DataFrames\n", + " List of localization datasets, one DataFrame per channel.\n", + " infos : list of list of dicts\n", + " List of metadata dictionaries for each channel.\n", + " radius_xy : float or list of float\n", + " Clustering radius in xy (camera pixels). If a single float is\n", + " provided, it is applied to all channels. If a list, must have\n", + " length equal to the number of channels.\n", + " radius_z : float, list of float, or None, optional\n", + " Clustering radius in z (camera pixels). Only used for 3D data.\n", + " If a float, applied to all channels. If a list, must have length\n", + " equal to the number of channels. Default is None.\n", + " min_locs : int or list of int, optional\n", + " Minimum number of localizations in a cluster. If an int, applied\n", + " to all channels. If a list, must have length equal to the number\n", + " of channels. Default is 10.\n", + " apply_fa : bool, optional\n", + " If True, apply basic frame analysis to clustered localizations.\n", + " Default is True.\n", + " save_clustered_locs : bool, optional\n", + " If True, save clustered localizations for each channel to a\n", + " file. Requires output_paths to be provided. Default is False.\n", + " save_cluster_centers : bool, optional\n", + " If True, save cluster centers for each channel to a file.\n", + " Requires output_paths to be provided. Default is False.\n", + " resi_path : str or None, optional\n", + " Path to save the combined RESI cluster centers with metadata. If\n", + " None, the combined cluster centers will not be saved. Default is\n", + " None.\n", + " output_paths : list of str or None, optional\n", + " List of paths to save cluster centers for each channel. If None\n", + " and save_* parameters are True, clustered data will not be\n", + " saved. Default is None.\n", + " suffix_locs : str, optional\n", + " Suffix appended to output_paths for saved clustered\n", + " localizations. Default is \"_clustered\".\n", + " suffix_centers : str, optional\n", + " Suffix appended to output_paths for saved cluster centers from\n", + " individual channels. Default is \"_cluster_centers\".\n", + " progress_callback : {callable, \"console\", None}, optional\n", + " Callback function to report progress where the input integer is\n", + " the index of the channel currently processed. If \"console\", uses\n", + " a simple console print. If None, no progress is reported.\n", + " Default is None.\n", + "\n", + " Returns\n", + " -------\n", + " resi_centers : pd.DataFrame\n", + " Combined cluster centers from all channels. Contains all columns\n", + " from the original localizations plus a 'resi_channel_id' column\n", + " indicating which channel each cluster belongs to. The 'group'\n", + " column is renamed to 'cluster_id'.\n", + " resi_info : list of dicts\n", + " Metadata for the RESI cluster centers, containing clustering\n", + " parameters for each channel.\n", + "\n", + " Raises\n", + " ------\n", + " ValueError\n", + " If fewer than 2 channels are provided, or if list parameters\n", + " have incorrect lengths.\n", + "\n", + " Notes\n", + " -----\n", + " RESI (REsolution by Sequential Imaging) relies on sequential imaging\n", + " to ensure sufficient sparsity of binding sites. Therefore, at least\n", + " 2 channels are required.\n", + "\n", + " If output_paths are provided, the combined RESI cluster centers will\n", + " be saved with a new metadata entry containing clustering parameters\n", + " for each channel.\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Running G5M...: 80it [00:20, 11.76it/s] " + ] + } + ], + "source": [ + "help(postprocess.resi)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From 8b2aef87a3dab7953ad47dd675dfec41806b8f5c Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 28 May 2026 13:17:03 +0200 Subject: [PATCH 40/42] expand sample notebook 4 and correct notebook 1 too --- samples/sample_notebook_1_localize.ipynb | 20 +++ samples/sample_notebook_4_spinna.ipynb | 151 ++++++++++++++++++----- 2 files changed, 142 insertions(+), 29 deletions(-) diff --git a/samples/sample_notebook_1_localize.ipynb b/samples/sample_notebook_1_localize.ipynb index 2c9b5e65..1e766130 100644 --- a/samples/sample_notebook_1_localize.ipynb +++ b/samples/sample_notebook_1_localize.ipynb @@ -15,6 +15,26 @@ "The sample data was created using Picasso: Simulate." ] }, + { + "cell_type": "markdown", + "id": "48f357db", + "metadata": {}, + "source": [ + "# A general note on how to find the useful functions\n", + "\n", + "Most functions that Picasso uses are available via the API in the `picasso` folder. The scripts in `picasso/gui` should only focus on the user interface while the proper calculations are run in the `picasso` folder scripts that can be easily imported in your Python scripts.\n", + "\n", + "A fairly easy appraoch to find a function from the GUI is as follows:\n", + "\n", + "- Note the name of the button/action item that runs the function you are intersted in, for example: Render -> Tools -> Pick fiducials\n", + "- In the corresponding GUI script, look for the class `Window` (or `MainWindow`) and inside search for the action item with that name, in our example: `picasso/gui/render.py` and we look for \"Pick fiducials\".\n", + "- The action item connects to a method inside the script which we can follow. In our case, it's in `View.pick_fiducials`. \n", + "- This function may contain a lot of code but it's only to read from the GUI, etc. You should be able to pinpoint the call to the API, in our case `imageprocess.find_fiducials`.\n", + "- Simply inspect the function, for example by running `from picasso import imageprocess; help(imageprocess.find_fiducials)`.\n", + "\n", + "In v0.12.0, Picasso should have a browsable documentation online making it super easy to find all the important functions." + ] + }, { "cell_type": "code", "execution_count": 1, diff --git a/samples/sample_notebook_4_spinna.ipynb b/samples/sample_notebook_4_spinna.ipynb index 7bb30d6a..e1d1f443 100644 --- a/samples/sample_notebook_4_spinna.ipynb +++ b/samples/sample_notebook_4_spinna.ipynb @@ -157,8 +157,8 @@ "name": "stderr", "output_type": "stream", "text": [ - "Coarse pass: 97%|█████████▋| 57/59 [00:04<00:00, 11.96it/s]\n", - "Coarse pass: 26%|██▌ | 9/35 [00:04<00:11, 2.23it/s]\n" + "Spinning structures | Coarse pass: 98%|█████████▊| 58/59 [00:04<00:00, 11.65it/s]\n", + "Spinning structures | Fine pass: 97%|█████████▋| 34/35 [00:03<00:00, 8.65it/s]\n" ] } ], @@ -183,8 +183,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Best numbers of structures (m/d/t): [6839 512 854]\n", - "Corresponding proportions: [60.63 9.08 30.29]\n" + "Best numbers of structures (m/d/t): [6837 683 769]\n", + "Corresponding proportions: [60.619995 12.11 27.27 ]\n" ] } ], @@ -200,7 +200,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABWgAAAOmCAYAAAB2QXy6AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAuIwAALiMBeKU/dgABAABJREFUeJzs3QeUE+XbBfCH7bv0Lr1KR4qACFJUioqCDRVEERuoKKiIiiDFBlgAGyoiiAX9g4IFUIoUAaVJb9K79LbsLtvynfvyTZiZZHeT3SQzSe7vnOhOSDKTSX/mee+bz+FwOISIiIiIiIiIiIiIAi4i8KskIiIiIiIiIiIiImCBloiIiIiIiIiIiMgiLNASERERERERERERWYQFWiIiIiIiIiIiIiKLsEBLREREREREREREZBEWaImIiIiIiIiIiIgswgItERERERERERERkUVYoCUiIiIiIiIiIiKyCAu0RERERERERERERBZhgZaIiIiIiIiIiIjIIizQEhEREREREREREVmEBVoiIiIiIiIiIiIii7BAS0RERERERERERGQRFmiJiIiIiIiIiIiILMICLREREREREREREZFFWKAlIiIiIiIiIiIisggLtEREREREREREREQWYYGWiIiIiIiIiIiIyCIs0BIRERERERERERFZhAVaIiIiIiIiIiIiIouwQEtERERERERERERkERZoiYiIiIiIiIiIiCzCAi0RERERERERERGRRVigJSIiIiIiIiIiIrIIC7REREREREREREREFmGBloiIiIiIiIiIiMgiLNASERERERERERERWYQFWiIiIiIiIiIiIiKLsEBLREREREREREREZBEWaImIiMit5cuXy7PPPistWrSQsmXLSnx8vOTLl89wmjlzpuE6lStXNvz7Qw89FDJ71+FwSKtWrZz3LTIyUjZt2mT1ZoWVRYsWuTwHcV6wmTVrluE+9OjRQ4LZ3r17XR6XrE7B+HgREYW6/v37e/Qe3rZtW6s3lShkRVm9AURERGQvhw4dUgUjFlKMpkyZIkuXLnUu9+rVS+rVqxfwx4eCX6dOnaR169ayZMkStfzNN99Inz595LrrrrN604LuoMmWLVtk69at6n0rMTFRoqKipFChQlKxYkW58sor1QlFBV86fvy4rFq1Snbt2iXnzp2T6OhoKV68uNSpU0eaNGmiloPB6dOn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95cFERERkQ8ge1WfX4muan90k9oFhlli5noNugOtmIE+L8zxBhi6arfiLHz00UfO4izcd999QVecJSIiIqK8C4+0dyIiIqI8eOutt9QEUfplDEX357BCq/z8889qyLOmVatWKhM53Cc488dw23HjxjmXMSQfuZ+h4tVXX5WxY8e6nP/5559LkyZNLNkmIiJyD3NGuJsIVR8VRET+xQItERERUQ4wiRxyWDF5CGAYOiYMQsdjqAmG4mZ2kGWLfE39pFt27AAeM2aMHD9+3Ln87LPPqizEUIFsWJzMEhMTLdkeIiLKfvQMcr2JyDqMOCAiIiLysKCGLFMNZkrGhB2h5rffflMdtNoJk1gFkzJlykhmZqZz+9PT0wMyAY03kIc7evRo5zImZRoyZIil20RERERE1mEHLREREZEHKlSooIb/rV271jA5Uo0aNbj/yCuY1Rwds/oObczMTUREREThKZ9DHzJGRERERERERERERAHDiAMiIiIiIiIiIiIii7BAS0RERERERERERGQRFmiJiIiIiIiIiIiILMICLREREREREREREZFFWKAlIiIiIiIiIiIisggLtEREREREREREREQWYYGWiIiIiIiIiIiIyCIs0BIRERERERERERFZhAVaIiIiIiIiIiIiIouwQEtERERERERERERkERZoiYiIiIiIiIiIiCzCAi0RERERERERERGRRVigJSIiIiIiIiIiIrIIC7REREREREREREREFomyasVE5BtnzpyRxYsXO5crVKggsbGx3L1EREREREREZBsXL16UAwcOOJfbtGkjRYoUsXSb7IIFWqIgh+Ls7bffbvVmEBERERERERF5bObMmdKlSxfuMUYcEBEREREREREREVmHGbREREREREREREREFmHEAVGQQ+aseYhA9erVLdseIiIiIiIiIiKznTt3GiIazfWMcMYCLVGQM08IhuJs3bp1LdseIiIiIiIiIqKccILzyxhxQERERERERERERGQRFmiJiIiIiIiIiIiILMICLREREREREREREZFFWKAlIiIiIiIiIiIisggLtEREREREREREREQWYYGWiIiIiIiIiIiIyCIs0BIRERERERERERFZhAVaIiIiIiIiIiIiIouwQEtERERERERERERkERZoiYiIiIiIiIiIiCzCAi0RERERERERERGRRVigJSIiIiIiIiIiIrIIC7REREREREREREREFmGBloiIiIiIiIiIiMgiUVatmIiIiIiIiOzL4XBIZmam+j8REYWmfPnySUREhPo/WYcFWiIiIiIiIlKF2JSUFDl//rw6paamcq8QEYWJmJgYKViwoDrFxcWxYBtgLNASERERERGFuaSkJDl8+LCkpaVZvSlERGQBHJQ7efKkOkVHR0vZsmUlISGBj0WAMIOWiIiIiIgozIuz+/fvZ3GWiIgUHKzD5wI+HygwWKAlIiIiIiIK8+Isc2aJiEgPnwss0gYOIw6IiIiIiIjC9Mc3Yg3MxVkMbS1UqJAUKFBA/c2JY4iIQhc+A9Axm5iYKOfOnTOMptA+J6pVq8bPAj9jgZaIiIiIiCgMYUIwc+YsJocpV64cf4gTEYURHIxD3mzJkiXl0KFDaqJIDT4nLl68qCYOI/9hxAEREREREVEY0v8A136gszhLRBS+MGICnwP4PNBDZy35Fwu0REREREREYchcoEWsAeMMiIjCGz4H8HmQ3ecF+R4LtERERERERGEGuYKpqamG85A5S0REZP48wOcFJ5P0LxZoiYiIiIiIwkxmZqbLeeYhrUREFJ6ioqI8+twg32GBloiIiIiIKMy464RivAEREUFEhGu5kB20/sUCLRHR/3/YzNl8Ql6fvVvWHmAAOhEREREREREFhmvPMhFRGJr012F5e95e9fcPa4/KD70bSNUSCVZvFhERERERERGFOHbQElHYW7brjLw7/1JxFlLSM+XDRQfCfr8QERERERERkf+xQEtEYe3g6RR5/oftkmmKYUPcwfajF6zaLCIiIiIiIiIKEyzQElHYSkrNkL7fb5Wzyelu//2DRfsDvk1EREREREREFF5YoCWisJ0UbMgvO2X70STneXFREZIQffltccG2U7L5cKJFW0hERERERERE4YAFWiIK20nBZm86YTjvtc7V5cHmZQ3nvc8uWiIiIiIiIiLyIxZoiUjCfVIweKh5Wbm1fknp1aKcFIqLdJ6/ZMdpWXvgnAVbSUREREREREThgAVaIpJwnxSseZXC8nz7yurvQnFR8tC15QzXeX8hs2iJiIiIiIiIyD9YoCWisJ4UrGzhWHnv7poSFZHPed6D15SRIvFRzuW/95yVlXvPBnx7iYiIiIhC1eTJkyVfvnzOE5aJiMIVC7REFDYm/XXIZVKwD++rJUUTog2Xyx8bJY+2dO2ixcRiRERERERERES+xAItEYUFFFd/3XjccN7rnatL7SsKuL1892ZlpET+y4XbNfvPyfLdZ/y+nUREREREFJ4OHTokM2bMkJdeekluuOEGKVSokKHLuHLlS7FsJIb9op1at26dq12jv40iRYrkeHk8DuZ1V6tWTdLS0rxet/m2zpzhb85wxQItEYWFnceTZe/JFOdyw/IFpVP9kllePj46Uh5vVd5w3rg/2EVLRERERES+s2zZMrnzzjulXLlyUr58efX3qFGjZOHChXL+/Hnb72p9gdHqAvKff/4pv//+uyXr3r17t3zxxReWrJtCAwu0RBQW5m09aVjuULt4jte55+or5IpCMc7ljYcTZdG/p/2yfUREREREFH5WrVqlumYPHz5s9aaEhMGDB1u27tdee01SUi43BRF5gwVaIgoL87cZC7TtPCjQxkZFSJ9WFVyyaDOZRUtERERElCcPPfSQiiHTTlgmowIF3MexUdZWr16tCt5WRVSMHz/eknVT8GOBlohC3oHTKbL1vwvO5dpX5JcKReM8uu4djUpJuSKxzuVtRy/I0p3MBSIiIiIiIt8pWLCgtG3bVl544QWZNm2a7N27V3755Rfu4lwYMmSIZGZmWrLv3nrrLUlMTLRk3RTcWKAlorCLN2hXK+fuWU1MZIQ80drYRfv3HhZoiYiIiIgo72677TbZvHmzmhwKubOjR4+Wu+++WypVqsTd6yFMplavXj3nMvbnt99+G7D916JFC+ffx48fl7FjxwZs3RQ6WKAlovDLn63jeYEWrq9RzLC87qD9w/qJiIiIiMj+qlWrJnXq1JGICJZncgsTlCH/VW/YsGGSnp4ugfD666+rbdC88847cvo05y4h70R5eXkioqBy7PxFQ0G1SvF4qVYi3qvbKJY/WioWi5P9py4Fvm8+nCipGZmqu5aIiIiIKJycP39e1q5dK9u3b1ddnxcvXpSEhAQpWrSoVK5cWRUbS5cuHfDt2rVrl9ou5IAmJydL+fLlpXXr1lKxYsVsr4f8W+SWrlu3TnU/5s+fX92PG264QcUOhKp///1X1q9fL0eOHFGPaVRUlLrv5cqVcxaNcV6wuP3226Vp06Zq0jXt+fDFF1/I448/7vd1N2rUSO666y6ZPn26Wj579qy8/fbb8uabb/p93RQ6gufVRkSUC/O2njIst69d3HB001MN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6l4me0h2mSAsMIH0DXUxB+ji3XrkQkDWS0REREREREShgQVamzl58qQaPq/31ltvyfjx46Vs2bKGoO877rhDli9fbshfRawAiqP+MHfuXJkzZ45zGbmyv//+u8pkRSC4PtPm2Wefld9++82QPfvLL7/IwoULPV4frotZJlGcnjhxomzcuNHnxeeWLVuq+4CiM3J8EZxuzstFdzCyZ1EYR1et3pAhQ1SxnKx3QZc/CwnRgXl7a1COMQdERERERERElHss0NrM6NGj5fz5yzmWyJRFHmtWkOv6+eefG84bM2aMKvT6GoqRei+99FK2mbfIeDVv++DBg3NcDwrOf//9t9oPq1evVsVpdN/Wq1dPFaZ9AbEFv/76q5oYrEOHDh5lyRYtWlRN2oYCtAbdtT/88INPtonyJtmCiANoVKGQYXndgXMBWS8RERERERERhQYWaG2WPTtp0iTDeRh2n1Px8MYbb5RWrVo5l1HY/N///ufTbUP3Kob8a1CkRA5rTgYOHGgoaKLjd+vWrdlep0iRInLNNddIbGys+AsKtJ06dfL6euhi7tmzp+E8dOCS9ZJMEQfxASrQXlkqQfLr1rVm/zm/ZScTERERERERUehhgdZGULzEZF+aqlWrStu2bT267iOPPGJYRqenL5kzcZF1W7CgcWi3O7hM165d/bptgaYvhgOygcl+k4TFByjiIDIinzTS5dAeT0yTg2cuBmTdRERERERERBT8WKC1kVmzZhmW27dv79HQe+2yeosWLZILFy74bdsQC+Ap87YhWiCYIepAz5+TspHnknQZtHHREapwGiiNKxpjDtBFS0RERERERETkCRZobWTdunWGZUxY5c3Qe/1kYampqbJlyxafbBeGa2/YsCHX24aJuPTWr18f1EPADx06ZFguXry4ZdtCl2Q6HJKsizhIiA5MvIHmalOB9h8WaImIiIiIiIjIQyzQ2og5m7VOnTpeXd98+ZyyXj21b98+SUpKci4jU7ZixYoeX79SpUqSkJDgXEZn74EDByRY/fnnn4blGjVqWLYtdElKWqboS/4JMYF9a6tfroBE6Tp2WaAlIiIiIiIiIk+xQGsTycnJLlmmFSpU8Oo2zJffvn27T7bNfDvebpc/ty3Qzp07J9OnTzecd8stt1i2PeQ+fzYhQBOEaeKjI6VOmcuT4e06kSynk9L48BARERERERFRjqJyvggFwokTJwzD/qOjo6VUqVJe3Ua5cuUMy8eOHfPJtplvp3z58l7fBrZNX5T11bYF2uuvvy6JiYnO5RIlSsitt97qs9vHftFPFOeJnTt3SrhL0sUbWNFBC40rFJINhy4/N9YeOCc31GT8BRERERERERFljwVam9AX/QCRAJ5OEKaPHsjuNn21beb1WLltgbR8+XJ57733DOcNHjzYEN+QVx9//LEMHz7cZ7cXjhOEWZFBq00UNvnvw87lNfvPs0BLRERERERERDlixIFNmAuWcXFxXt9GfHx8trcZitsWKOhsve+++yQj43IhsGnTptK3b19Lt4vsEXEAjSsWNCyjg5aIiIiIiIiIKCcs0NpESkqKYTkmJsbr24iNjXXJtQ31bQuEixcvyh133GGY2KxgwYLy7bffSmRk4AuB5CrZBhEHxfPHSOXilw9ebDqUKCmmzl4iIiIiIiIiIjNGHNiEuSs1NTU1V4XE7G4zFLfN3zIzM6VHjx4q3kCDouw333wj1atX9/n6nnzySenatavXGbS33367hDN9B21EPpHYKGuOPSGHdu/JSwc00jIdsulwojSpVNiSbSEiIiIiIiKi4MACrU0UKFAg265VT5i7Us23GYrb5m8omE6fPt25jFzgCRMmyG233eaX9WFiOG8nhyNjBi3iDbzNb/aVqysWkh/XXZ4A75/951mgJSIiIiIiIqJsMeLAJswFy6SkJHE4HF7dxoULF7K9TV9tm3k9Vm6bP7388svy6aefGs579913pVevXpZtE7lKz3TIxfTLr5X4aOve1jBRmN4a5tASERERERERUQ5YoLWJEiVKGLr+0tLS1MRU3jh06JBh2VedmObbOXjwoNe34a9t85eRI0eqk96rr74qzz77rGXbRO4l22CCME2lYnFSPH+0c3nt/nOS6eWBFiIiIiIiIiIKLyzQ2kR8fLxUrFjRcN7+/fu9ug3z5WvVquWTbatZs6ZhWT9ZlqfM1/HVtvnDRx99pLpn9fr16yfDhw+3bJsoa0lpmbYp0OIgi76L9vzFDNl5LMmy7SEiIiIiIiIi+2OB1kbMRcstW7Z4df2tW7dme3u5ValSJVVA1scV7Nu3z+Pr47KIbNDkz59fKlSoIHY0ZcoUefrppw3nPfzwwzJmzBjLtok8nyAMEmKsfVtrXKGgYXnN/nOWbQsRERERERER2R8LtDbSsGFDw/Ly5cs9vu6RI0dk7969zuXo6GipU6eOz7oCr7rqqlxv27JlywzLuC2rJnHKzg8//KCKsfrs33vuuUdNCmbH7aUsCrTR1nXQusuh/Yc5tERERERERESUDRZobeTWW281LM+fP9/jicLmzp1rWL7++ut9OhGXedvmzZvn8XXNl73tttvEbubMmSPdu3eXjIzLxb5OnTrJ119/LRERfJnYWVKqfSIOoPYV+SUu6vJzZs3+85ZuDxERERERERHZGytPNtKiRQs1WZhm9+7dsmjRIo+uO3HiRMNyly5dfLptnTt3NixPmzZNEhMTc7ze+fPn1WX9uW15tXjxYrnrrrskNTXVUOCePn266kQme0tOs1fEQXRkhDQofznm4MjZi+pEREREREREROQOC7Q2gk7Nhx56yHAeJqbKqYt2wYIF8ueffzqXCxYsqIbm+xJiCZo2bepcRnF29OjROV4Pl0FmraZ58+Y+i17whdWrV6uO3uTkZMM2/vzzzxIXF2fptpH3EQfREflUgdRqjSsac2gZc0BEREREREREWbG+kkEGL774oiGaAN2do0aNynIvHTp0SB599FHDef369TN04rqDTFX9yZNO3REjRhiWR44cKUuWLMny8u62/fXXXxe72Lx5s9x0002qy1efA4y4A1/GQ5D/4OCFPuLA6ngDzdXmHFpOFEZEREREREREWYjK6h/IGiisDho0SJ00L7/8suzfv18GDx4sZcuWVedlZmaqLk8UY/FvGvz7888/75dtQzGzQ4cOzrzbtLQ06dixoyrUPvbYY5KQkKDOR8csJtbCduMymltuuUVuvPFGj9a1ZcsWOXz4sMeTkO3cudPl/Pj4eGnZsmWWk6rhvpw8edJ5Xv78+WXgwIGqq9Zb7dq18/o6lHdpGQ5Jz7zcYR5vcbyBBhEHEflEtE37hzm0RERERERERJQFFmht2kW7fPly+fXXX53njR8/Xj777DOpVKmSFC5cWPbs2SNnzpxxKUj+73//kyJFivht26ZMmSLXXnutWj+kpKRI//79VTG2atWqqqMR2bk4X69atWoyefJkj9eDaIQvv/zSo8v26NHD7fnYV3v37nX7b9u3b3cpAKOwjInCcsPTydzIf/EGOXXQ4jHKSL90wEDLG9Z3T/valSXjZPuxS6+D7UcvyPmUdCkYx7dcIiIiIiIiIjJitcCm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X75cunfvLjVq1JA333xT9u/frwq2devWNRRq8ffcuXPl119/tfquEoUlc4E22CMOCsdHSZlCMc7lTYcSJS3DeB+JiIiIiIiIKPQFfYF2586dKoKgXLlyquiKicD03bL6GINKlSrJqFGj5ODBgzJx4kRVyDWLiopSBVt04Y4bN04t63311VcBvHdEpElJC62IA2hcsZDz76S0TNl85IKl20NEREREREREgWesPgaJzMxMmTlzpsqXXbhwodv8WNCKtMiPRdTBrbfe6vy3nOByTz/9tFy4cEEGDRrkLPSuWrXKr/eNiMIj4gCaVS4sszadcC6v2ntWGpYvaOk2EREREREREVFgBVWF49ChQzJs2DCpWLGiyoNFLiyKtfpuWcByoUKF5JlnnlHZsr///rvcdtttHhdn9fr16yeRkZeHUv/3338+vU9ElMsCbUxwRxxoBVq9lXvPWrYtRERERERERGSNoOigRfYrumVnzZolGRkZzm5Z0BdlAdmxTz31lDzwwAOSP3/+PK87Pj5eKlSoIPv27XNm3RJR4CXrIg4i8onERHp/wMVuKhWLk1IFY+TY+VS1/M/+cyqHNjoyqI6dEREREREREVEoF2irV68ue/bsUX/rYww0OA85sV26dFExBm3atPH5NhQubOxyI6LAS9F10MZFR+SqI95ucB+aVS4kv2484cyh3XQ4URpVuJxNS0REREREREShzfZtWrt371b/dzfpV8mSJeWVV15RBdxp06b5pTir0XftElFgZTochgJtfHTwxxtomlYyHgBatfecZdtCRERERERERIFn+w5ajVaYhebNm6sYg3vuuUeio6P9vu7OnTtLw4YN/b4eInLvYnqmOEJsgjDNNVVMObT7zsrjrcpbtj1EREREREREFFhBUaBFYTYuLk7uu+8+FWPQuHHjgK5/+PDhAV0fEWU/QRgiDkJFxaJxUrpgjBzV5dCmZmRKDHNoiYiIiIiIiMKC7asclStXlpEjR8rBgwfliy++CHhxloisl5JqLNCGUsTBpRzawoZi9ObDiZZuExEREREREREFju07aHft2hUSkwERUe4lp2UYlkMp4gCaVi4kv2w87lxeufcsJwojIiIiIiIiChO2r3KwOEtE5oiDUCvQXqProIUVe89ati1EREREREREFFihVeUgojAp0IZOxAFUKBonVxSKcS6v3X9e5dAS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AG1Q6qsqU9Tgnt0ef5q8w+FISoC2L/ett9tK1nbi2RYREREREREREek1NOiq2g5qDNCmCZfLpbtv7UENLZvNFlHXtr/tW2+3laztxLMtIiIiIiIiIiLSa2oKliEkljhIG+EZnB6PJ+51SM3artbZH/ZN5re1tfV4W/FsJx1f78svvxxnnXVWXI/56quvcP7553fcf+aZZzB58uRe7wtRsm3YsAGnnXZax/2XXnpJ1ZEm6m/YlmkgYDumgYJtmQYKtmUaCMLH0JkxY0ZK9yedsAZtmsjKyuoywzMW4Rmc4evsD/sm80MDtPFuK57tpOPrXVJSoqbekODslClTer0vRKkmwVm2ZRoI2JZpIGA7poGCbZkGCrZlGghycnJSvQtpgyUO0kR4cE+ClJoM4xeH1tbWLteZqH0L304i962320rWduLZFhERERERERERUWcYoE0TRUVFMBgMHfe9Xi+qqqriWseOHTt093ubidnZeiorK+NeR6z71tttJWs78WyLiIiIiIiIiIioMwzQpgmHw4ERI0bo5m3bti2udYQvP3HixITs24QJE3T3t2/fHvc6wh/T2b6Fb6uvXoNkPiciIiIiIiIiIqLOMECbRsIDfFI8OR5r1qzpcn09NXLkSBVADu3av3Xr1pgfL8uG1pXNzMzE8OHDU/oaJPM5ERERERERERERdYYB2jQybdo03f0lS5bE/Nhdu3Zhy5YtHfctFosaLCoRpPTC/vvv3+N9++ijj3T3ZV2h5RwS9RpE21b4+lLxnIiIiIiIiIiIiDrDAG0aOemkk3T3FyxYEPNAYW+//bbu/lFHHZXQQavC9+2dd96J+bHhy5588smdLnvkkUeqbNR269atizmzVQLU69ev77ifnZ2t1pfq50RERERERERERNQZBmjTyJw5c9RgYe02bdqEhQsXxvTYhx9+WHf/1FNPTei+nXLKKbr7zz77LFpaWrp9XHNzs1o21n2z2+049thjdfMeeeSRmPYxfLlvfetbsFqtKX9OREREREREREREnWGANo0YjUZcfPHFunk33XRTt1m07777LhYvXqzLHD377LMTum/ShX/mzJkd9yWQ+cc//rHbx8kyUt+13SGHHNJt6YVLLrlEd//vf/87qquru3xMVVUV7r///i7Xk8rnREREREREREREFA0DtGnmmmuu0ZUmWLRoEe68885Ol9+xYwd+8IMf6Ob94he/0GXiRiP1UkOnWDJ1//CHP+ju33HHHfjggw86XT7avt9yyy3dbufEE09UQc92tbW1Ktjq9XqjLu/xeNT/y3LtDj/8cBx33HFp85yIiIiIiIiIiIiiYYA2zUhg9brrrtPNu/baa3H55Zdj586dHfMCgQBeeuklVRYhdHCwIUOG4Je//GWf7JuUDAgtPyABUwmC3nvvvWhra+uYL9ml99xzj1o+NKh6wgkn4JhjjolpW3fddZfKKG736quvqm1/+eWXuuWWLl2q5r/22msd80wmU0yZsMl+TkREREREREREROEYoE3TLNrwAaz+7//+DyNGjEBFRQUOOuggFBYW4vTTT8e2bds6lnE4HHjmmWeQl5fXZ/v2r3/9C6NHj+6473K5cMUVV6jA8tSpUzFlyhT195VXXqn+r53s92OPPRbzdg477DDcfvvtunmS5Tt9+nQMHToUM2bMUMFouZWs1lASnA3NwE2X50RERERERERERBTOHDGHUk4yR2UQqu9973v473//2zHf7/ergcOikYDtc889h0MPPbRP9620tBTvv/++GhRr+fLlHfOdTidWrVoV9THTpk3DK6+8guLi4ri29etf/1plw0rAWp57O8kkDs0mbifL3n333Sq4mq7PqS/IPtx44426+0T9EdsyDRRsyzQQsB3TQMG2TAMF2zINBGzHnTNo3Y1ARSn1/PPPqxqnX331VdT/z8zMxEUXXaQCdCUlJTGvV+rOhpIA5ZFHHhnz46Xuq3T5l1IA0YKlQjJcJVgqNXGtVit6Sp779ddfjzfffFOVdogW0JZSA/I6HXDAAT3eTjKfExERERERERERkWCAtp/YsGEDPv30UzUomAQSpYzBpEmTVMas3W5P2X5JwFTqwErmaVVVlZongWLJMJVSDKF1ZHurpqYGH374ocoilpqwEpyWMgPyGnQ3KFq6PiciIiIiIiIiIhrcGKAlIiIiIiIiIiIiShGmAhIRERERERERERGlCAO0RERERERERERERCnCAC0RERERERERERFRijBAS0RERERERERERJQiDNASERERERERERERpQgDtEREREREREREREQpwgAtERERERERERERUYowQEtERERERERERESUIgzQEhEREREREREREaUIA7REREREREREREREKcIALREREREREREREVGKMEBLRERE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xev111/XJYjJBQnpCRDLMfnRRx+NKbls48aNWLRokS5DV9pqZ+T8Q0qLtJPj+TPPPIN+HSPRiKhf+NWvfiVHtY7piCOO0AKBQJePWbBgge4x2dnZWnV1ddL2mSjcJZdcomuTVqtV27lzZ6cv1J49e7TMzEzdY959990uX1j5XMydO1f3mN/85jd8Mwa5999/X9cmRo4c2aP1JLNN8rhPfdmWe2L16tXqXCJ0+z/72c+6fRzb8uBUW1urTZkyRddeiouLVTtKpJUrV2oGg0F3btHdNpxOpzZu3Djdvv3jH//odltnnnmm7jEXXHBBt4956KGHIj6zbrc7rudIg6Mt9/Q7wWQy6fbtT3/6U7ePY1umWLW1tWnjx4/XtbHvfe97nS7vcrm0YcOG6ZZ/+OGHu93O+eefr3vMd77znW4fc9999+keI/spx/euyOdWvifaH2M0GrVVq1Zp6YABWqJ+QgKrWVlZugPQ7bff3unylZWV2qhRo3TLX3/99UndZxq4pO198cUXMS/v9Xq1q666StceZfr5z3/e7WOvueYa3WNGjx6t7dixo9Plb731Vt3yubm56sSaBrdEBrWS1SZ53Ke+asvLli3T/vznP2utra1xPWbEiBG6bTscji7bfju25cGnqalJmzlzpq695OXlqXbUF8455xzdtmTbjY2NnV40u+yyy3TLjxkzRvN4PN1uR37Ey4/50Mc++eSTXS4vzzt0eQnYUv+RrLb89ttva4888og6Z46VXCDOz8/X7Vt5eXlMx3a25cHn6quv1j777LO4HiPnq/PmzdO1MbkgsGLFii4f93//93+6x0g77SoI+p///CdiG9988023+ycXu8LPTX70ox91msgm3wszZszQLS+B4XTBAC1RP3LbbbdFBLh+/OMf634c+f1+7cUXX4w4UA0ZMkSrr69P6f7TwCEZ3NKu5syZo91zzz0qeyXaCWVDQ4P64TJt2rSItltRUaHV1NTEdGJQVlYWEZB4+eWXdV++27dvj/jBJdMf//jHhD9/Sl8ffvih9s4770RMd999t65dlJaWRl1Opu6uoiezTfK4P3j1ZVtuD/IWFhZql156qfbGG29E7WEj7Vl+hEmWrM1mi2jLcvyPFdvy4HLkkUdGtJc//OEPnbbVrqa6urput7d+/XotIyNDt70DDjhAtfVQ8oP/jDPOiNi3Z555Jubn9sMf/lD3WAnY/u53v9PtpwR7H3300Yjg2f777x9XAI4GT1uW9iLrHjp0qHbFFVdo7733njqPDufz+bRPPvlEu/DCCyMuFsj9F154IebnxrY8uMgxUdrJrFmzVJa1XGSIdmFKvvvXrFmj2nlRUVFE+5deMd2R9YZnnRcUFGiPP/647hgo59SSRBbeli+//PKYn5f81gzfR8kQX7duXcQFDTkGhy4nCXCbNm3S0gUDtET9iARfTzrppIgDkFxhkiv/Bx54YMRV+vYMF/mhR5ToAG3oJD/eJeh60EEHqUwDaZPhX7btkwS3wr80u7Jo0SLNbrdHrEfau7R7yWAM794l06mnntptKRAaWCRQGq3NxTNddNFFadMmedwfvPqyLYdn4YYGe+UH1SGHHKJNnTo1IrgUOv3yl7+M6/mwLQ8uvW27oVN4kLUzTz31lK7UQWhX9OnTp2vDhw+P+v+xlOkIJdmJ4RlYMkmX2QkTJqgAQHivN5kk0BFLRhgNzrbcHqANnyRgu99++6nj8uTJk6O2LZmkbf/1r3+N67mxLQ/OAG34cUvOWeXc9eCDD1ZtLLyUUfh5hXyfx0JKCUhQNnwd0oZlX6QcgcViifj/WbNmqbIK8ZCktWifCUlak+N/tECz/E599tlntXTCAC1RPyM1VaQeS6wnApIdE+uJLVFvArSxTieccIKq4xkvueoZ7Uu+s+ncc89VNZBocElWgDaZbZLH/cEpFQHaWKacnBztiSee6NFzYlsePHrbdkOneM5jJZNKEhNiXbdkgvXkQq5kfR199NExb0fKjnXXJZgGd1vuLEAbyyRlDaREQk+wLQ/uAG083/33339/3MfLr776Kq7zmXnz5vWo168Eja+88sqYtyM9Lp5++mkt3TBAS9RPPffcc1G7jbdPMoiNdA3oSSCMqDtyEij1fSTTKlqWYLQrpWeddZbKOuyN3bt3qyuk4d0YQye5Avz888/zTRykkhmgTXab5HF/cOnLtizlZe68807tW9/6VswXGSZOnKjKc8TS3bw7bMsDX2/bbugUb6LBxo0b1QWxaJlZ7ZMM3Lhw4cJePUcJCDz44IPa2LFjO92OfL6uu+46rbm5uVfbooHflrdu3arddNNNqqRCVxmModl/0mtNBreLp554NGzLg4NktMp3vwRBJeDaXRuTDFTpEXDXXXdpVVVVvarjfO2113bZK0cGbfznP//Z656PUhrk8MMP73Q7kjF83nnnpVVZg1AG+QdE1G9t2LABn376KXbs2AGPx4O8vDxMmjQJhx56KOx2e6p3jwaBtrY2rF69Glu2bMGuXbvQ0tKCQCCg2mJ+fj4mT56M/fbbDyaTKWHbdDqdWLJkCdasWYOGhgZYrVYMHToUBx98MMaOHZuw7RClY5vkcZ8SbevWrVi/fj22bduG+vp61Z7lHEKO4eXl5aodFxYWJny7bMvUl5qamvDhhx+qtt3c3Kza9IgRI9Q5shyfE2nlypX48ssv1XmQ3+9Xn5epU6eqz47FYknotmjgkxDNxo0b1TFy+/bt6rzC5XIhMzNTHZeHDx+OWbNmIScnJ+HbZlseHOS3mhwbpY3Jd78cL71eL7Kzs5Gbm4tRo0bhoIMOSmgbk/VL3OLrr79GbW2t+m0o5xiyHfmtmEiVlZXqvFyem3x25HmNGzcOhx12WJ98bhKFAVoiIiIiIiIiIiKiFDGmasNEREREREREREREgx0DtEREREREREREREQpwgAtERERERERERERUYowQEtERERERERERESUIgzQEhEREREREREREaUIA7REREREREREREREKcIALREREREREREREVGKMEBLRERERERERERElCIM0BIRERERERERERGlCAO0RERERERERERERCnCAC0RERERERERERFRijBAS0RERERERERERJQiDNASERERERERERERpQgDtEREREREREREREQpwgAtERERERERERERUYowQEtERERERERERESUIgzQEhEREREREREREaUIA7REREREREREREREKcIALREREREREREREVGKMEBLRERERERERERElCIM0BIRERERERERERGlCAO0RERERERERERERCnCAC0RERERERERERFRijBAS0RERERERERERJQiDNASERERDTBHHnkkDAZDxyT3iSg1XC4Xxo4d2/F5zMnJQVVVFd+OPrRnzx5kZ2d3vOby+rvdbr7mRESUthigJSIiIiIi6iN33nknNm7c2HH/17/+NUpKSvh696HS0lJcddVVHffl9b/rrrv4mhMRUdoyaJqmpXoniIiIiPqbLVu2YPTo0TEtazKZYLfb1VRYWKiCM6NGjcLEiRNxwAEHYM6cOSgoKEjYvknG7KJFizruH3HEEVi4cGHC1k9Esdm8eTMm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6q7KYyUIKcEhCei+/fbbKnDVTl5TCazut99+OPHEE9FTDzzwAG6//faO+xKcO/7441UAVf6WoK08fwmEr1q1KqKepwQu4wnCS/3RuXPn6l7Pdjk5OTjqqKMwe/ZslJSUICsrC42NjSq4t3TpUixZskTVpo3Hj3/8Y/Ucw0k9XxkIa/r06WpbUotU2og8RwkMfvPNN7rlH3zwQVXH+M477+xyewsXLsStt94aMT83N1dtT+rbDh8+XLUhaYPt77Fst70mbrJIPdjFixfr5s2YMSNh6//qq69w7bXXwuPxqPtSc1eOEfL+S71Xs9msavNK23733Xcjgqs/+MEP1Osir11PXH311XjjjTc67svnSwbfGjdunFqntKUPP/wQzz//vO54IIH473//++p4YTKZ1IUnqTkr5DgmwVp5L+V9lOck7VMCpwsWLNBtX9b/k5/8BK+//jp6Sj6HoUHeRYsW9XhdRERECZWwXFwiIiKiQaSvSxyIL774Imp332+++SahJQ6kRqXUYwzvUvzKK6/EtJ9Sv1S6vJ944onaueee2+c1csNJ1/7Zs2erGrix1pSUurO///3vI2qnyvNua2vrcYmD9rIGFotFu/322zvtVh8IBFR3+Gg1PD/++OOYti91OaXrebRyBbfddpvW2tra5eN9Pp/2zjvvaN/97ndVOY14a3jKVFBQoLqJd1U+QJ6r1AcuKSmJePyrr77a5TaPOeaYiMf88pe/1JqamrrdX9mufIauvvpqtZ99XeLg66+/jthXqXHcE9FKHISWzLjgggu0nTt3dvl4ec7h65A22ZMSB/I5aT9GSM3prurqSh3eCRMmRGxb2mRobe2DDjpIvT+defPNN6PWb16yZInWU7fcckvE+tavX9/j9RERESUKA7REREREaRqglUGYJNAXvh0JXCQyACr1PcO3sXDhwh7tc3dBwZ7sX3caGhp6/FgJUIbXbY21LmW0AG173c0FCxbEtI5bb7014vFS4zMWUu83/LHDhg3TVqxYocWrqwHY2v8/PFgmg7HFM8iSXAiQ/QtdhwzUJoHUaGQgrPA6zBKM7wkJune2nUR57LHHIt6P//3vfwkL0LZPN998c0zrWLx4ccSFF6kb3JMAbftUVlamrV27NqYa2uE1mAsLCzsuSBx55JExDUz3xBNPROzDpZdeqvWU1OINX1+0GrlERETJxhq0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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAABWgAAAOmCAYAAAB2QXy6AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAuIwAALiMBeKU/dgABAABJREFUeJzs3QeUE+XbBfCH7bv0Lr0rTaSLIMWCqCCgiBT5C1hRUVARFUEEG2ABFMWGIhYsqFiwIEgTlCZIFem9t2XZXr5zX77Jzkyyu0k2yUyS+zsnuhOSzGTSn3ne+xbJycnJESIiIiIiIiIiIiIKuIjAr5KIiIiIiIiIiIiIgAVaIiIiIiIiIiIiIouwQEtERERERERERERkERZoiYiIiIiIiIiIiCzCAi0RERERERERERGRRVigJSIiIiIiIiIiIrIIC7REREREREREREREFmGBloiIiIiIiIiIiMgiLNASERERERERERERWYQFWiIiIiIiIiIiIiKLsEBLREREREREREREZBEWaImIiIiIiIiIiIgswgItERERERERERERkUVYoCUiIiIiIiIiIiKyCAu0RERERERERERERBZhgZaIiIiIiIiIiIjIIizQEhEREREREREREVmEBVoiIiIiIiIiIiIii7BAS0RERERERERERGQRFmiJiIiIiIiIiIiILMICLREREREREREREZFFWKAlIiIiIiIiIiIisggLtEREREREREREREQWYYGWiIiIiIiIiIiIyCIs0BIRERERERERERFZhAVaIiIiIiIiIiIiIouwQEtERERERERERERkERZoiYiIiIiIiIiIiCzCAi0RERERERERERGRRVigJSIiClMrVqyQRx55RNq2bSuVK1eW+Ph4KVKkiOE0d+5cw3Vq1qxp+PdBgwZJqMjJyZH27ds77ltkZKRs2rTJ6s0KK4sXL3Z6DuK8YDNv3jzDfRgwYIAEsz179jg9LnmdgvHxIiKi/A0fPtytz4BOnTpxVxJ5KcrbKxIREVFwOnjwoCoYsZBiNGvWLPnjjz8cy4MHD5bGjRsH/PGh4Ne1a1fp0KGDLF26VC1/+umnMmTIELnyyiut3rSgO2iyZcsW2bp1q3rfSkpKkqioKClRooRUr15d6tWrp04oCvjS8ePHZfXq1bJz505JTEyU6OhoKVu2rDRs2FBatmyploPB6dOnZf369bJjxw71d3p6uhQtWlTKlSsnderUkUaNGknJkiWt3kxbOHr0qDogt3fvXjlz5oykpqaq51np0qWlbt260qxZM4mJibF6M0PSuXPnZPPmzbJ9+3b1PMUynqfY91WqVJFWrVrxeZoHvD/h9b1//345fPiweo/EczchIUHts0qVKqnnLv5PRPbHAi0RURhBF1StWrU8uk5ERIQUK1ZM/VDBF+XLLrtMfVm+5ZZbpEyZMn7bVvLfj9DWrVvLoUOHuIt1zp49K0888YRjGd3E48eP5z4SkTlz5kjv3r0d+wLvASj6BJNu3bqprlbN5MmTVTeQP02aNEnatGnjWB46dKisXbtWdWZT/rCf3n77bdXBf+LEiXwviyLE5ZdfLtdff716nlatWrVQz/WpU6fK8uXLVXHYleLFi8ttt90mI0eOlIsvvth2D+X58+fVwaaPPvpIFZmzs7PzvCwK25dccolcddVV6qACTlZA4RjF0TVr1jhOWM7IyDBc7sMPP/TZqA0UYn/55Rf5/fffZcmSJeqzMT+xsbFyxRVXyL333iu9evWyVbEW90W/7/D6QaFTr2PHjrY5KIvPW+z7RYsWqdN///2X7+XxPMXB0jvuuEPuvPNOr797orMTj7U/BGL/4nWycuVK9f6EEVAbNmxQj7078L7Yt29fdeAZB5qIyKZyiIgobOzevRu/OH1yiomJyenTp0/Onj17rL5btrRo0SKnfYbzrNa9e3eXj2eJEiVy6tevn3PZZZcZTr///rvh+jVq1DBcb+DAgW6t17y+sWPH5tjJU089Zdi+hx9+2OpNso0BAwYE7LHzx+smKSkpJy4uznCbeC8069ixo+EyWC6sLl26GG7zww8/zAmVz45q1ao5vV/gtHr1aq/Xc+DAgZybb77Z68+l5557zuv1mh9/dz4Dx48fn5OdnZ1jF3h+XXTRRV7tu8jIyIBt57lz53Lef//9nCFDhuS0bNlS7Ut3ttEXr59XX3015/LLLy/U9x8893/99dccq8yfPz9n9OjROddff31OuXLl3NpmX7yfFdZ3332X061bN7cfb1enhISEnMmTJ+dkZWV5vH5PX+OenDp16pTjbz///HOht7NIkSI599xzT87Jkyc9Xv+kSZNcvucXLVrUds81omDFAi0RURjxZYFW/2X5o48+svqu2Y4dC7Tbtm1TX87123TxxRfnLF261O0iQygWaI8cOWL4gREdHZ2zb98+qzfLFjIyMnJKly5teOz+/vvvoHrdfP3114bba9KkicvL+aNAu3jxYsNt1qxZMyctLS0nFD47fF1sRsHL/FwLRIEW74uVKlXyep39+vXLyczMzLFScnJyzq233lqofRfIAu26deu82kZfPOd8+f3niSeeyLECimKebqsdima+LJBec8016uCbVes3n3CQNxgKtPrPop07d/pku/zx2UkUrhhxQEQU5pDzhXy1vGCIIYajIdvK1VDJ5ORkNeQwKytLDZ0i+/ruu++chu1+8803KocwnL344otqWLAG+bzVqlWzdJvsAhmq+qGyyP1Enl0wMU9016NHj4CtG8NeMfwew1K1mJn3339fHnjggYBtQzD46quvpH///pKZmen0b/Xr11fxBTVq1JCKFSuq85ARirzKf/75Rw31ReaiN06ePCmdO3dWn29mLVq0UM8VxAKlpKSoYdifffaZUzzM7NmzpUKFCjJlyhSxAt67unTpooY9u/p8v+aaa1SsDbaxVKlSKqMSw/k3btwoq1atUvmVdOG9DZNE4vOwfPnyKjoDzzNko2I4/rZt25x208SJE1VkyQsvvMBdWAh4XrZr107lO+N5ioxkfLfcvXu3iqDQZ8NrFi5cKN27d5effvpJxU+4A9918ZgW9vVmfs0gguGuu+6SQEMe96WXXqoiC3DfMNkrIliQkY0cX3zeIO5iwYIFkpaWZrgu/g056Ygrwv4mIpuwukJMRETWdkG5e6T7/PnzOQsXLszp37+/UxcmTuhAZNyBvTtozR1WjRs3Dti67dpBe+LECdUFrt+2wgzRDjWIetDvm6FDhwbV6wadjWXKlDHc3po1awLaBTRz5kzD7dauXdur4bmh2kG7ZMkSl0Oer7322pwNGza41T36/fff5/Ts2TNnwoQJHq37lltucVpv8eLF1VBsV9LT03OeffZZlx1pP/30U06g4Xl04403Om1LqVKlcl577bWclJQUtzqIn3/++Zw6derk2KGDFqM0evXqldO+fXu/POfM63rmmWdytm/fnu91MMIEnfgVKlRwuc2B/mzPq4MWUUV437rzzju9/q7nT/r32JIlS+bce++9OStWrCjw/RDPlxYtWri8z4H+LvHSSy85bcNVV10VkHVjlEHTpk1Vty6ec/he7o7Tp0/nPP744zkREREu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PfneTkSBwwItERF5DT/uMPurWX4/7jp27KgmF9P7+uuvZfv27bZ+JFx1ueU3JNfV5ElJSUkSrsz7w+p9Ye7kzi83OZShE/y3334znOdpx5irbMb33nvPq5mzEXniTQewvpiKgrE5SsUOz1HkeiLjuqARBeGgUqVKcs011zh1deU3wWReQ9PR5Whmvm0zdEyaffjhhx49/9Bxu3r1aqfHMxCRFQMGDHA67+OPP/b4dj755BOnonjbtm1dDoEONRg1gKx5c8ckirPYL71797Zs28LBW2+9JcOGDXM6/8EHH5SpU6dask2uJge76667JJSkpKQ4dYtrI2eIyHos0BIRkdeee+45l5OJdO/ePd/hpRi6pofiCn5wpqWl2fbRcJWTm98w0NKlSzudF86TMJj3h9X7wjyBFCaZslMWcqDMnz9f/WDTH1xxNYFTflDQNRdEp02b5lFsBHJwvZ0IxlfxBubnKIaMe9PRmZc///zTKee0S5cuEq4eeughp/Mwg7snncso8iArXK9q1apOXd1mjRs3liuvvNLpOYjh1u566qmnnM67//77JRBQRDVn3a5cudJpgquChnO/8sorTudjoqxwKM4OHjxYZs+ebTgfXd04UOSqgE++gwN4Q4cOdTp/yJAh6rPDCphk0txZiu+r/fr1k1Dy/PPPO33XwXtmy5YtLdsmIsrFAi0REXllypQpLn/MXn311VK3bt18r4sCrTnvatWqVeqHobcTxaD7avTo0U7dgPrhn+jK8HYiBFcdHRienJdq1ao5dQp78uM51Fx66aWG5SVLljgNJQykG2+80em8RYsWSbjxRXETQ/QfeOABw3noROzVq5ecPHnSrS5eDCVGR76nUFw2v+a9LdCan6N4L/Jm2HhezM8vdCni/dJdiH/AhGz6k6vZuIMFYijME16hQDJq1Ci3ro+CpKsiKSYbcic2Ap8XZmPHjpW///67wOuiiGTu9sXkg/379xd34bEzP56eRHy4OqCBbj93Dn6hCI7L7tu3z6lQg0kyQxkOutx7771qIlBzcXbmzJkq8iAQGczmxx7rDgcogN93331OB7/wmOCAi1Vcdc+ii9rVRLiFMWjQIKfH/tlnny3weni/QWHbPCmhp9/bMcGtq4Nl5sk+icgaLNASEZHbcNQdQ//atWvn1AUL+FH8xhtvFHg7+MKLYanmzMcff/xRWrRoIZ9++qlbM2qjexcFJnTf1qhRQ1544YU8C7wo4A4fPlz9AEWXBjoH3fmii2HOTzzxhPpiq4fia34z3ePLrnnI2IIFC1RBwdUEDaEOHV96eJz69OnjlH8YKBdffLFTFAcen3CCIg1ec74obmKSl0suucSpIwldiq7y7jSbNm1SxQptsqX4+HiP1ovXMQq8GmyDeTu8fY4CilUorOonUfOW+fmFPG5P72+oefvtt50+BzAxECblyatgj8IOimvXXnutoftb64x1d0IfdC+bn+8YxXHVVVep2AxX8JmBkSOuun+RpxnIPGHk0JozUtFNjNdcfhNYoiiLUS5fffWV079NnDgxpGardwWdm+ZiHIqzH3zwgYo8IP/B9z68p5qLszhYgPcCq4qEOKDoKiLETvEG+P6NIja+u6Cg68l3J3wG4/3O1fd2vGe6Op+IrOE8gwkREYWVNWvWSNOmTfO9DDodT506pU55wY9sFFYbNmzo1nqREYgftObOOww1R8H1scceU4UbFGwRJVCsWDHV/XrmzBnVIYTZ3tevX+/0A70giYmJ8s4776gTMicxTBQTyiA3sFSpUmpIGwq/Bw4cUJ1U6FpwVfTF0NCCiiv4IWL+oTxhwgR1QgYjJqoyFyfww9nbod52dscdd6iONX3hfd68eeqEoeUYIm8uDFSuXNmvXcd4nj399NOO5e+//15tn6sJ3kIR8qMxwZYGz0lvM1HRDYoMT7xm9TmemCwMhU+c0LWMznIUQw4ePKiKq+gY1Ia0I/sRHYzogPS2A7gwM67jvuP9a8uWLY7zkBmLLle81nFwJyEhwel6eI7iuZqfQ4cOORWq8ZoId3j/ffXVV52yKFFAQzY5umwRV4ARFzhYhtgMvE5dTUKH9xFkK3pSYERHGj5L8H6v/4zA+zCG/KKAi88GfM4gJx2fcXjuuir6uZq4y9/ef/999Tmo75pFIQe50OhOxjZh+7FPULzFyIWff/7ZZTQRis6edAAXFgpyOOXFHF0BzzzzjNPBUj0cfMUpv/c8V12aeP/CiCBPIi7M/P15ZX4/cTUKROMqS7mg73qB2H4UwF0d7EKWc0ET+xXEnffh/D5H9J+F2rwD5hgUO9izZ4+KgsGpevXqar9hNFeFChXUd1i81vFdGaNXMJEtRoHge7UrVapUURPkheNElUR2FR6/QIiIKN/ia2EnSMLsr/ihm19HqSvI68OXSmTBmaMHjh49qmbUxclfsE78YMXJE+g2cKdLC0O8UYheuHCh07/hR7SrzNOCiuXBCsU/FGhdDeVDp5yrbjkU4/0JBVpsk9bNgx80KBiiMy8cmIubKIYVpoMJHeMoquF5ry8QYP+iMOJqQkE9dN97Mns8fuj7qgNYvw3IJzbnoGoFOlfcmVgKP4L1XWMoJmJ/04UZ2/H6N7834PWPrjZ3Jr/C+8u3334rderU8WiX4uAfDhSgCG8uCKKghVNBbrvttnyLhv6E5xE6s/E5g8xkc9HLPIlZXtCZ99prr0kgYX97+t1j//796pTfbeYnr1Ez6MIv7Pcgf39emd9zPN3egr7rBWL78xoZhUJiYXkzwWSwTw6GbniczJ/l7sDBSBzs8vQ9k4j8ixEHRETkNXQ1jRgxQnUzeVqc1aCYgx/B3l5fg65H3EaTJk1c/jsiCQo7MzV+zOOLvLs/ZNEpOGfOnIB2JdnZmDFjVAxFTEyM2AG6T8xdb+iQCxe+mlxLD69BdEWjU9Zd6E7F68rTCZZQ8MXEThp0YXvbAaxBoQ6FPvOkZ4X12WefOeUQhvpQck+gcxoH48qWLevxdXFABZ8h3j72OCiADHRPu+XQdYbtxkRT6P62CjpkUYj1pnscBV5tREm4jBwg0kPB35xjjte23UY4YPSGLyIgMEoME4WtW7eOxVkiG2KBloiICoSCGoqxONKOHCvkqCKjD8PsXn75ZafJsDyFTC3cHro77rnnHvWD0x34MY+JxfDjEsNOcRu4LVeQK4gOSQyBRawCOlXd/VGNmAVEGqCDztMJVDDkDEU/FLHRIYYCFvYjfhiH27AyFKwxARAeK0yygwxa5J/huVXY4rm3Hn30UcPyl19+6fVEdcEE2a/6YY+IEEEXni+gYIaYAMRH5FeoRWF24MCBsnHjRq8mJnLVAYznWGHhdjCMFM8FdFG1bt1ajRLAD1tvfiBjX//555+OZbzvmIf004VO1B07dqiM14ImP8NjgccJox9QXPF2aLMGz9OlS5eqIjHiOPJ7nPFawagPdP3hPd0Xz7nCwmchDiz8/vvvKp6hoINgyGnGEGl03aJ7lihcIZrHHLuA9xaM7rLb+yO+OyErGaN/6tWr5/Z1EeeFz2VcF13m+Gy2y4FyIjIqkmNO6SYiIrIBDNvCD2DkgqGwisw8/DDGBGPofEQ+GPKzCgND/lB0RX4fvrQi8gBDILEeFJ3r1q2rOnILW4Ame0MBXj9zO4a5I1MylKGTWT+LPTrZ0e3ta/iaie5GZLki0gPDUJG9jNcvOh4LM0kWfqCioKfBAZrCduL7A4qxyNvWzwyO4m8wQcHafOAMhQ10AvsLnjM4aIcONwxBx/syDuZgO1A09+cBLkzkiK5afDYgkxbdpVg3um2R7Wr34gY+29BVi8xexEfgdYgiLopO2P7CfnYSkfUQS4HPQBxowXsWXvf4jNW+K+OzFlEG+Kz05wRsyJ7XR4V17NhRxUURkedYoCUiIqKwhk5M/fBg/JhBx7MdOuP8BUUafb4mcj7RlRMsNm/erLqv9R2VOJhjVSd2fj+gcUBJy9jGcwqTOl166aUSTKwo0BIRkf2xQEvkO6H7y4OIiIjIDche1edXoqvaH92kdoFhkpi5XoPuQCtmoC8Mc7wBolfsVpyFN9980zABYt++fYOuOEtERERE/sc0eCIiIgp7L730kpogSr+Moej+HBZoFczcrE+4at++vcpEDvcJznwNw/KnTp3qWMaQfOR+hopnnnlGpkyZ4nT++++/Ly1btrRkm4iIyD8w54SriVT1UUNEVDgs0BIREVHYwyRyyGH9+uuv1b7AMHRMGISOx1ATDMXN/CDLFvma+km37NgBPHnyZDl+/Lhj+ZFHHlG51qEC2bA4mSUlJVmyPURE5N/RN8gFJyL/YcQBERER0f8X1JBlqsFMx5hwI9T88ssvqoNWO2ESq2BSqVIlNeu2tv2ZmZlqAiQ7QR7upEmTHMuYlGnMmDGWbhMRERER2Rc7aImIiIhEpFq1amr43rp16wyTI1188cXcP+SRXbt2qY5ZfYc2ZtYmIiIiInKlSI4+hIyIiIiIiIiIiIiIAoYRB0REREREREREREQWYYGWiIiIiIiIiIiIyCIs0BIRERERERERERFZhAVaIiIiIiIiIiIiIouwQEtERERERERERERkERZoiYiIiIiIiIiIiCzCAi0RERERERERERGRRVigJSIiIiIiIiIiIrIIC7REREREREREREREFmGBloiIiIiIiIiIiMgiLNASERERERERERERWYQFWiIiIiIiIiIiIiKLsEBLREREREREREREZBEWaImIiIiIiIiIiIgsEmXVionIN86cOSNLlixxLFerVk1iY2O5e4mIiIiIiIjINtLS0mT//v2O5Y4dO0qpUqUs3Sa7YIGWKMihONuzZ0+rN4OIiIiIiIiIyG1z586VHj16cI8x4oCIiIiIiIiIiIjIOsygJSIiIiIiIiIiIrIIIw6IghwyZ81DBOrWrWvZ9hARERERERERme3YscMQ0WiuZ4QzFmiJgpx5QjAUZxs1amTZ9hARERERERERFYQTnOdixAERERERERERERGRRVigJSIiIiIiIiIiIrIIC7REREREREREREREFmGBloiIiIiIiIiIiMgiLNASERERERERERERWYQFWiIiIiIiIiIiIiKLsEBLREREREREREREZBEWaImIiIiIiIiIiIgswgItERERERERERERkUVYoCUiIiIiIiIiIiKyCAu0RERERERERERERBZhgZaIiIiIiIiIiIjIIizQEhEREREREREREVmEBVoiIiIiIiIiIiIii0RZtWIiIiIiIiKyr5ycHMnOzlb/JyKi0FSkSBGJiIhQ/yfrsEBLREREREREqhCbmpoq586dU6f09HTuFSKiMBETEyPFixdXp7i4OBZsA4wFWiIiIiIiojCXnJwshw4dkoyMDKs3hYiILICDcidPnlSn6OhoqVy5siQkJPCxCBBm0BIREREREYV5cXbfvn0szhIRkYKDdfhcwOcDBQYLtERERERERGFenGXOLBER6eFzgUXawGHEARERERERUZj++Easgbk4i6GtJUqUkGLFiqm/OXEMEVHowmcAOmaTkpIkMTHRMJpC+5yoU6cOPwv8jAVaIiIiIiKiMIQJwcyZs5gcpkqVKvwhTkQURnAwDnmz5cuXl4MHD6qJIjX4nEhLS1MTh5H/MOKAiIiIiIgoDOl/gGs/0FmcJSIKXxgxgc8BfB7oobOW/IsFWiIiIiIiojBkLtAi1oBxBkRE4Q2fA/g8yO/zgnyPBVoiIiIiIqIwg1zB9PR0w3nInCUiIjJ/HuDzgpNJ+hcLtERERERERGEmOzvb6TzzkFYiIgpPUVFRbn1ukO+wQEtERERERBRmXHVCMd6AiIggIsK5XMgOWv9yLokTEYWh08kZ8t0/x2TXiRS5qUl5aVWjpNWbRERERERERERhgAVaIgpbOAK4/sA5mb3miPyy+YRkZF3oJPlhw3GZN7S5VC4Za/UmEhEREREREVGIY4GWiMJOcnqW/LjxuHy+5ohsPXLe6d9TM7Nl3sbjcs+VVS3ZPiIiIiIiIiIKHyzQElHYSM/MlmmL96mO2aS0rHwv+/PmEyzQEhEREREREZHfcZIwIgobH/11SN5bftBlcTYhOkJKxeces0Jn7d5TKQHeQiIiIiIiIiIKNyzQElHYQFesWZ1y8TL6htqy+NFWMrBNZcO//br5ZAC3joiIiIiIiIjCEQu0RBQWjp1LM+TNVikVKx8NbCw/PNBMbm9dSYrHRUmXRuUM1/lli3NBl4iIiIiIiIjIl1igJaKwsGzHGcNyt8blpXXNklKkSBHHebXKxkv9ikUdyyjo7jnJmAMiIiIiIiIi8h8WaIkoLCzdftqw3KFeaZeXu75RWcPyr+yiJSIiIiIiIiI/YoGWiEJeRla2LN+Z20FbMi5KmlQt7vKyXRoaYw5+3cIcWiIiIiIiX5s5c6YazaadsExEFK5YoCWikLdu/zk5n57lWG5Xt5REReRGG+jVLBsvDS4yxhzsZswBEREREREREfkJC7REFPKWmOIN2td1HW+g6dLQFHOwmZOFERERERGRfx08eFC+/fZbefLJJ+Xqq6+WEiVKGLqMa9asyYfg/+n3i3bq0KGDV/tHfxulSpUq8PJ4HMzrrlOnjmRkZHi8bvNtnTljnDuFwgcLtEQUdvmz7evm/6HLmAMiIiIiIgqE5cuXyy233CJVqlSRqlWrqr8nTpwoixYtknPnztn+QdAXGK0uIC9btkx+/fVXS9a9a9cu+eCDDyxZN4UGFmiJKKQdPJMqO44nO5YvrVxMyhaNyfc65piDf48y5oCIiIiIiHxv9erVqmv20KFD3L0+MHr0aMv243PPPSepqamWrZ+CGwu0RBTSlu0wds92qJd/vIHmevNkYYw5ICIiIiLymUGDBklOTo7jhGUyKlasGHeJh9asWaMK3lZFVEyfPt2SdVPwY4GWiELash1nvCrQdmlkzKH9ZQtzaImIiIiIyD+KFy8unTp1kscff1y++uor2bNnj/zwww/c3V4YM2aMZGdnW7LvXnrpJUlKSrJk3RTcWKAlopCVnpktf+7KLdCWSYiWxpXdOwpdo4wx5mDb0WTZfSI3KoGIiIiIiKiwbrrpJtm8ebOaHAq5s5MmTZJbb71VatSowZ3rJkym1rhxY8cy9udnn30WsP3Xtm1bx9/Hjx+XKVOmBGzdFDpYoCWikLV6b6KkZOQeOW1Xt5REFCni9vVvaGSMOfhly0mfbh8REREREYW3OnXqSMOGDSUiguUZb2GCMuS/6j377LOSmZkpgfD888+rbdC88sorcvq0MWqPqCBRBV6CiChILd1+yrDcsa578QaaLg3LymsL9zqWf9l8Qu7vUM1n20dEREREFGzOnTsn69atk23btqmuz7S0NElISJDSpUtLzZo1VbGxYsWKAd+unTt3qu1CDmhKSopUrVpVOnToINWrV8/3esi/RW7p+vXrVfdj0aJF1f24+uqrVexAqPrvv//kn3/+kcOHD6vHNCoqSt33KlWqOIrGOC9Y9OzZU1q1aqUmXdOeDx988IHce++9fl93s2bNpFevXjJnzhy1fPbsWXn55ZflxRdf9Pu6KXQEz6uNiMhDS3UThEUUEWlbp5RH169eJl4aVioqWw6fV8v/HUuWXSeSpXa5BD4WRERERBRW/v77b9UpOG/ePElPT8/3srVq1ZKuXbvK/fffrwp9rsycOVMGDx7sWP7www/znShM36HYsWNHWbx4sfr7559/VoWwP/74w+V1ULh7/fXXVcHWXJh977331HX37s1tytDExsbKQw89JOPGjVMF6FCAYjqG3+N+o4CZn/j4eLniiiukd+/eMmTIEMO/4XH66KOPnK6D/ah/nMwGDhyoHnd/wfOzS5cujmV01d5xxx0SFxcn/jZ+/Hg1OVlWVpZaxnNu2LBhlhysoODEHnoiCkl7T6XInpOpjuXLqhaX0gnRHt/O9Q1NMQebGXNAREREROFlwoQJqjsRBaiCirOwe/dumTZtmt9zQEeNGiU33nijy+KsVoTFNrdu3Vp1jGrQYYvs1/vuu89lcVYrZmKoOgp+589faNgIZvv27ZOmTZvKk08+WWBxVttHv//+uyqyByoqoLCuu+46VbzXHDhwQKZPnx6QdTdo0EAGDBjgWMZzhh205AkWaIkoJC3bbsz86eBhvIE+5kDv1y0nCrVdRERERETBZMaMGfLUU09Jdnbu3A6A4f+XXnqptGnTRi677DKpVq1avt2TvoYh5C+99JJjuVSpUmo7cMJQfT0M47/55pslIyND3Q9MwoVOYE2lSpWkRYsW0qhRI6dh/Sj+Dh8+XIIZiq3XXnut/Pvvv4bzkXuLychw31HErl+/vhQr5t6kynb1wgsvGJbxHElKSgrIuseOHSvR0blNQe+8847s378/IOum4McCLRGF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l+rVqxvO27dvn0e3Yb58/fr1fbJtl1xyiWFZP1mWu8zX8dW2+cObb76pumf1hg0bJuPGjbNsmyhvqRnZkmPhBGH6HFq9VXsYc0BEREREREREBWOB1kbMRcstW7Z4dP2tW7fme3veqlGjhiog6+MK9u7d6/b1cVlENmiKFi0q1apVEzuaNWuWPPTQQ4bz7rzzTpk8ebJl20QeThAWY00HbbNqxSUKAbj/b/VedtASERERERERUcFYoLWRpk2bGpZXrFjh9nUPHz4se/bscSxHR0dLw4YNfbJdiF5o0qSJ19u2fPlywzJuK9AZoe74+uuvVTFWn/172223qUnB7Li9dEFKeu4EYVZ20CbERErjyrk5tLtPpsjxpHRLtoWIiIiIiIiIggcLtDbSrVs3w/KCBQvcnihs/vz5huWrrrrKpxNxmbftt99+c/u65svedNNNYjc///yz9O/fX7Kycot9Xbt2lU8++UQiIvgyCaYO2gSLCrTQqkYJw/JqxhwQERERERERUQFYebKRtm3bqsnCNLt27ZLFixe7dd0ZM2YYlnv06OHTbevevbth+auvvpKkpKQCr3fu3Dl1WX9uW2EtWbJEevXqJenp6YYC95w5c1QnMtlbSoa5g9aaiANobZ4ojDEHRERERERERFQAFmhtBJ2agwYNMpyHiakK6qJduHChLFu2zLFcvHhxNTTflxBL0KpVK8cyirOTJk0q8Hq4DDJrNW3atPFZ9IIvrFmzRnX0pqSkGLbx+++/l7i4OEu3jbzroI2zsIMWObSRujQMdtASERERERERUUFYoLWZJ554whBNgO7OiRMn5nn5gwcPyt133204b9iwYYZOXFeQqao/udOpO378eMPyhAkTZOnSpXle3tW2P//882IXmzdvluuvv151+epzgBF34Mt4CApwBq1Fk4RB0dgoaaTLod15IkVOnmcOLRERERERERHlLSqffyMLoLA6atQoddI89dRTsm/fPhk9erRUrlxZnZedna26PFGMxb9p8O+PPfaYX7YNxczrrrvOkXebkZEhXbp0UYXae+65RxISEtT56JjFxFrYblxGc+ONN8o111zj1rq2bNkihw4dcnsSsh07djidHx8fL+3atctzUjXcl5MnTzrOK1q0qIwcOVJ11Xrq2muv9fg65PsO2pjIIhIVYe2Ebq1qlJQNB3PjP1bvSZTrG+V/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09IuxLggGxaFUxwc8DfEHEydOlWOHTumltE5jcnwUMCm0GXftjMiIjfyZ+3eQQs1ysQbltlFS0RERETBAt2byHE1w6ROEydOVEPTy5UrJ82aNVOTHG3evNnv2xQXF2fJddHNGEg//vijDBw40LDeW265RRULEfNgxT7Ibz+cOXPGkI+rjwlwRyAm4vLk4ARyejUZGRkq+iAQUBB/6qmnDOdpHbUUuuxd1SAicqdAG2P3DlpjgZY5tEREREQULFDM++6771Qnpz6L1FywW79+vbz00ksq87Vbt25qUi3y3qJFi6R3796SmZnpOK9z584ye/Zsj7pfAwmTh+XVfewOdyZ9CyQccNBvEzKY//3334Cs+/777zfkCy9ZskR+++23gKybrMECLRGFQMRBhO0jDvTYQUtEREREwQTdmv369VOTFaFDdsqUKdKzZ0/VOevKvHnzVNbrsmXLAr6toWDlypXSvXt3lferadu2rXz77bcSE2OPfGJX9MXM5ORkj69//vx5sRNk7yJuQIMsZkQfBEJsbKzTuthFG9rsXdUgInJrkjB7HkHOs4P2ZOCzq4iIiIiIfKFhw4aqaIViITIyt2zZogq2V155peFy586dk1tvvdXQVUkF27Bhg9xwww2G/Yb4CEwSZbcOU1cTXOknEkMsgCdOnjwpdoPcWf39+vrrr9WBikDARGWYsEyzevVq22T0ku+xQEtEQd1BG1FEJDbK+/ylQLioRKxER+Zu456TuUfCiYiIiIiCubO2QYMGqmCLbtmlS5caumpRwP34448t3cZgsm3bNhVjcPr0acd52L+//vqrynS1u9q1axu6TT2NA0Bx2m5QnEWRVh/nMXr06ICsGxm+5txbdNVmZxsblig0sEBLREGdQRsXHVGogPxAiIwoItXL5MYc7D2VEvAJBoiIiIiI/K19+/YyYcIEw3l//PEHd7wb9u7dK9dee60qamtq1aqlckfLly8fFPuwVatWhmXkproLWbvLly8XO8IBCMQdaNDNvGLFioCsu2/fvnLppZc6ljdt2iSff/55QNZNgcUCLREFleycHEnVFWjtHm/gKuYABeZj59It3R4iIiIiIn9o166dYfnEiRPc0QU4fPiwXHPNNXLgwAHHeVWqVJGFCxeq/weLTp06GZZnzZrl9nWRW2zX5wqiJTBhmBV5sBEREfLcc88Zzhs7dqxh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YLcv7L6Wo+Xd+JifAntnrW4HZ8C3/FicIhdDd8DwLgk1Th8H48zH4Fx1AxRnjXdQdsG54Y8wz3kOfFz/yq4RQggh4SwiShz84Q9/QFVVlSaTlAVYWXbkO++8g7a2NjW78+9//zuefvppPPTQQ+pXFrBjmbIs65YFCY899lhNSQSmsrISV199NcIBq4l76aWXasZYYPJA2cHMt99+i6VLl2oyRy+44IKgzo0FwAPr4XZ0dODJJ5886M+x13RlwzLz5s07aOmFK664QrP84osvor6+/oA/U1dXh5deeumA6yGDF1jegDHpIuIjZNgkdSsBuu7r1aGaCokgiVI98sXtPYKz9fxI7NeNgaTwcHsU1Da7sbfaiU17OtDgiUezkAkbn0ilDUhESze7cNeMzeA5f6PXTY0peH3H6JDOi0QfM0Q1SMsp/t+1Bi4OS7lRFJ6NALwxFaZZjwP6RP+gpx3O9XdCdh74PIEQQgiJBGEfXWElCD7//HNN1iwLQP773//Gd999h/POO++gt5az588991y1ZAAL6CYkeLONujJpv/jiC/z0008IB3fccYcmK5X9/weWPeiOBZivvPJKzdiNN96oZuIeSPc6vSxT92AefPBBzfLjjz9+wP3W29wffvjhg27ntNNOUwO5XRobG9Vga1+ZEW63W32eva7LEUccgZNOOumg2yKDC9CaDWH/ETJsBL0Ia0Afp/bmduxcq21aR4iGoiBdrEC2VKp2sg9smrRfGIV6XS4FX0lMmJbWjCvG+0sUMe/vLcCPVdT4LqKx2iw8B5fkgU10B/XhED1qaYOu8gb9lYkOzFf8PRuYfXwq1nOhv5OOHBxvzYVp5qMA78/IVpx1cG24B4rce21rQgghJFKEfXTlueee833PgqmshAHLGD3UDFEW0GWB3aSkpD63E0ossHr33Xdrxu666y5cc801ahZxF1mW8eGHH6plEQKbg40cORK33nrrkMyNlQxg5Qe6sIApC4KyfWe3233jLGP22WefVV8fGFRl5SSOO+64fm3rqaeeUjOKu3zyySfqttevX6953bp169TxTz/91DcmCEK/sntJEEoc6KnEQRdLorZu6Mbv10GW/Fk6hHSXJlcivVvjGhF6lOkmok048EU2QqLNOYXlOGpk563LnZ7ZPAl726iREwmucUo9JnVrxLiZz8Zujj53I4GQOB7GafcC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yedERERERERERETUFgZos0hsgE+KJ6di1apV7a6vs0aOHKkCyJFd+zdv3pz042XZyLqyTqcTw4cPz+hr0JPPiYiIiIiIiIiIqC0M0GaRvffeO+r+woULk37stm3bsGnTptb7ZrNZDRaVDlJ6Ya+99ur0vn3++edR92VdkeUc0vUaJNpW7Poy8ZyIiIiIiIiIiIjawgBtFjn++OOj7s+fPz/pgcLee++9qPuHHnpoWgetit23999/P+nHxi57wgkntLns3LlzVTZqi7Vr1yad2SoB6nXr1rXez83NVevL9HMiIiIiIiIiIiJqCwO0WWT27NlqsLAWP/zwAxYsWJDUYx977LGo+yeddFJa9+3EE0+Muv/iiy+iqampw8c1NjaqZZPdN5vNhiOPPDJq3uOPP57UPsYud/TRR8NisWT8OREREREREREREbWFAdosYjAYcOGFF0bNu+WWWzrMov3ggw/w6aefRmWOnnHGGWndN+nCv99++7Xel0DmPffc0+HjZBmp79pi//3377D0wkUXXRR1/+9//zuqqqrafUxlZSUeeuihdteTyedERERERERERESUCAO0Webaa6+NKk3w8ccf4+67725z+YqKCvzsZz+LmvfrX/86KhM3EamXGjklk6n7pz/9Ker+XXfdhU8++aTN5RPt+2233dbhdo477jgV9GxRU1Ojgq1+vz/h8j6fT/2/LNfi4IMPxlFHHZU1z4mIiIiIiIiIiCgRBmizjARWr7/++qh51113HS6//HJs3bq1dV4oFMKrr76qyiJEDg42ZMgQ/OY3v+mWfZOSAZHlByRgKkHQBx54AM3Nza3zJbv0/vvvV8tHBlWPPfZYHH744Ult695771UZxS3eeOMNte0lS5ZELffNN9+o+W+++WbrPKPRmFQmbE8/JyIiIiIiIiIiolgM0GZpFm3sAFb/93//hxEjRmDs2LGYPn06ioqKcMopp6CsrKx1GbvdjhdeeAEFBQXdtm//+te/MHr06Nb7Ho8HV155pQos77HHHpg6dar6+6qrrlL/10L2+8knn0x6OwcddBDuvPPOqHmS5bvvvvti6NChmDFjhgpGy61ktUaS4GxkBm62PCciIiIiIiIiIqJYprg5lHGSOSqDUP3kJz/Bf/7zn9b5wWBQDRyWiARs//vf/+LAAw/s1n0bOHAgPvroIzUo1tKlS1vnu91u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EREREREREWUIA7REREREREREREREGcIALREREREREREREVGGMEBLRERERERERERElCEM0BIRERERERERERFlCAO0RERERERERERERBnCAC0RERERERERERFRhjBAS0RERERERERERJQhDNASERERERERERERZQgDtEREREREREREREQZwgAtERERERERERERUYYwQEtERERERERERESUIQzQEhEREREREREREWUIA7REREREREREREREGcIALREREREREREREVGGMEBLRERERERERERElCEM0BIRERERERERERFlCAO0RERERERERERERBnCAC0RERERERERERFRhjBAS0RERERERERERJQhDNASERERERERERERZQgDtERERER9zNy5c6FpWusk94koMzweD8aNG9f6eczLy0NlZSXfjm60Y8cO5Obmtr7m8vp7vV6+5kRElLUYoCUiIiIiIuomd999NzZs2NB6/3e/+x1KS0v5enejgQMH4uqrr269L6//vffey9eciIiylqbrup7pnSAiIiLqbTZt2oTRo0cntazRaITNZlNTUVGRCs6MGjUKkyZNwrRp0zB79mwMGDAgbfsmGbMff/xx6/1DDjkECxYsSNv6iSg5GzduxJQpU1QWrRg8eDDWr18Ph8PBl7CbNTY2qszZlmxlec1XrVqFESNG8LUnIqKswwxaIiIiom4WDAbhcrlQU1ODtWvX4rPPPsPTTz+NG2+8ESeccAKKi4ux77774q677kJFRUWffz9YgoH6ixtuuKE1OCuuueYaBmd7iJQ4uOqqq1rvNzc346abbuqpzRMREaWEAVoiIiKiDJMOTUuWLMF1112nsnLPP/98lJWVZXq3iKgLli9fjv/85z+t9yV7/pJLLuFr2oMuv/xy5Ofnt96XC2OSRUtERJRtGKAlIiIiShOn06lKFiSaxowZowI0JpOp3XX4/X78+9//VuUP7r//fr43RL2UZMhHVpP71a9+pY4R1HNkQLaf//znUb0Zbr75Zr4FRESUdViDloiIiChNNWiTrfUqI4wvXrxYTe+//z4WLVrU5rI//vGP8a9//avDwG5vwhq51NetXLkSe+yxR2uAVj6/khUvNWip5+sAjx07tvW9MBgMqtSMzCMiIsoWzKAlIiIiysAI41J79tZbb8WXX36Jr7/+Gueeey40TYtb9rnnnlMlD4io9/jrX/8alT174oknMjibIXIhbd68ea33Q6EQeycQEVHWYYCWiIiIKMNkgDApa/D666+rMgiJgrR/+ctfMrJvRJQaGQxQap1Guuiii/gyZlDs6//EE0+goaEhY/tDREQUiwFaIiIioixx/PHHq2zakpKShKPBb968OSP7RUTJe/bZZ+HxeFrvFxQU4IgjjuBLmOFjq91ub73vcrnw4osv8j0hIqKs0XeKmRERERH1AaNGjVIjvx955JFqQJsWEvD5wx/+oOrR9hQZsExGPP/+++9RW1urMs6kDIMEOiToNGLECDX4mexzttm5cydWr16NdevWqb+bmprgcDgwYMAAVWJi5syZ6u+e4Ha7VSkL2R/ZF3n9JAgvNUplALlEpS3SYcuWLVi6dCmqq6tVVqfX60Vubq56/jIInUwWiyUt22publa1lLdv346qqioVAJNscHmee++9d1y95nQ9t4qKCtUu5bnJ6yqDcA0ZMkS1yQkTJsBms6GnxX5GpbyB2Wzulm1Jd/0lS5Zg+fLlqKysVG2puLhYfS5nz56dtve3LbJNed+lzmtjY6P6jI0cOVJte9CgQUmvRx4vF6fKy8vV50Weg6xH6nqn4z2UdnH00UfjlVdeiXqfmNlMRERZQyciIiKilG3cuFEKTEZNhxxySNpeySuuuCJu/RaLRd+2bVuHj5X96Mp+ffjhh/qZZ56p2+32uH1INBUXF+vHH3+8/sgjj+hVVVUJ15nMejqa5DVvi9vt1l9++WX9kksu0SdMmNDhujRN06dOnarfd999emNjo94ZN998c9x6I61fv16/8MILdYfD0eZ+DBw4UL/lllv0pqYmPR02bNig2s748eM7fA1kv4455hj90Ucf7dT2/X6//s9//lM/7LDDVNtsb1tjx47Vr7vuOr2mpqbTz62iokKtY9SoUUm1F9mnmTNn6tdff73+7bff6j1B3vPY/XjhhRdSXs9HH30Utx6Z16K+vl6/6aabVPtp6/k7nU7V/srKylLe/gUXXBC1rpEjR0b9/3vvvacfeuihusFgSLhto9Gon3jiifrKlSvb3EYwGNSfeuopfdq0ae0+h0svvVSvrq7Wu+rxxx+POwZs2bKly+slIiJKBwZoiYiIiLIwQPvDDz+oIEfsNh544IFuC9DW1tbqp5xySpeCqBLY7ekA7f3336/n5eV1er2FhYX6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ObhVOsQGvZYvX64Gk+ovJGM4dkClr7/+ulPrkqxBCaSlYubMmXHzYgfVihWbdSoDgMkAXt1BBmWKbXMy4FRnxQ7CJgOBJZIoGCuDUGWr2Mx2ySaXAerS5bDDDsPYsWO7vW215aKLLkpp+X322Sfh/NjM2s6sp7PPQdqUXBSJJAOrERERZZop0ztARERERG2LzZIU6e6qPWvWrKj7LpcLZ511Fp555pmEJRaynXSL/+KLL9So9xJs3rZtm8rGlEmCr8koKyvr1LY7UxoiUdCtvQC5lAWIzSA88cQTu1TqoT2xI91LN/GuZIbGZt5KBmNdXV1ctrgEzqX91dbWts679tprMWHCBBx88MHINrGZ7cl04e/utiWZqrIf8plu0dmLL3PmzEn54pIEqCMzoeU9HTRoUJfLb0h76SwJnEe+BokytYmIiHoaA7REREREWSxRIELqaabTUUcdhcGDB6tAZgupLyrd9aUG5GmnnabqWcZmnmWb7du346677lL1LXfs2NGldXU2ABSbjZuMRIHV9oJoEnxOVDqgu3z++edR96urq7H33nt3en1NTU1x82SdsQFayUSW8hP3339/VB1TCRQeccQRqvv+scceq2qaZoPYshjp/px2pm21tK+uBmjluQwZMiTlx8UGaMeNG9epdcTqSoa/PJfIx0e+NkRERJnCAC0RERFRFtu5c2fcvLy8vLRuQwIWDz74oArE6roMbB4mQYyHHnpITZJ1dsABB6hsW5kkgzFR+YVMeeSRR1Rd1HTV5+1sAKgzGceJAt9+v7/N5RMFn7uj9mxbXfcloLV06dK0bkMCr4mCdzIg1uuvvx43kN3777+vJskmnzp1KmbPno399ttPtctkBrHqDrEDmhmNxrSuv7PZ7LHtq7221ZbOftZjt92Z9aT6+Uhm8MVIyWbVExERdSfWoCUiIiLKUj6fL2GAdvjw4WnflowO//TTT7fZLVuyAz/44APccccdOOmkk1TW4owZM3D33XdnvIajDJx06aWXpnXwtM4GgHoiyziyy3+L7gqWS+3ZdNc8bms7bdUMnT9/fpv1TOWCwvfff68C9BdffDEmTZqkssF//vOfx5Vm6G6xGbMejyet689kBnu6tp0NWfixbU0uPhEREWUaA7REREREWerbb79NmN0lpQe6w9lnn60G37n88ss7HNxIauPKaPC///3vVQ1V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50vpeco+/vWvf1WTrFcGdZK2KduT10DajASjN2/erILvMsieZEjHlt2Q97m7SZaxZGb/85//bJ0n75FcjKHMih3Y7qyzzmo305+IiKgnMUBLRERElCYShO3qoEAWi0V1Xb/11lszWnN2zZo1anr++eeTfozsr5Q7SBRcjfX//t//U/U6JYszkgTW1q1bl/AxiQJ7UgdVAnLPPfdc3Hvxr3/9S03tkZIIUl6iNwZohQQqJdAqAVIJUEaSQK28H7ElKLrirrvuwvDhw1VGa+z7IQHwp556Ct1J6jPL+y1TsiQ4+5e//AVHHHEEeoK0pcgA7bJly7B+/XqVMUyZIWU05HgWqbd+5omIqG9iiQMiIiKiDJMA0owZM1TwS+pySt3NngzODho0qMvrkAChDIgko6InQ7JlJXA4cODALm9bXq+LLroopcfYbDYVJL7tttvQ20mWs2QFS6kCg6Fzp/eSYZssyeqWwdi62j1cMl8li1EyYRORbFmTqWv5JFIC47XXXlNZ4T1FXpc99tgjat4zzzzTY9uneFJWI9I+++yjSqQQERFlCwZoiYiIiLr7hMtgUMGowsJClUUnNULPOecc/OlPf1IDIUnNTOmafe2112Lw4ME9/n7873//U+UG7rvvPhx//PGqJEAy8vPzVbf3jz/+WD2PoUOHphzUlYD0Cy+8oAKsEjCRYLGUd5CgdSrdyqUWqtT1lde2o32+9NJLsWLFim4deK2nSYD13//+t8oUvPjii1WWa0fktZB6vZJ9LIO9pULeKwnSyiRtObL0RHtkOclYlszmbdu2qW23FRyW5aResywj7UMGn0umXcjnTco+/OMf/8Dq1atVO+tpkmEcSepIx2aLU88IBALqIk577w8REVGmaXoy1fyJiIiIqF+R7uobNmxQAVSpISolA6ReowyoJQE1ydqUYHNnMza7044dO1S3f6m1KvsuAVzJ1JUAn2TOdTUrs7eQ4KTUjJULADJJcDM3N1cFSSdNmqQGzZI6vukidWZle1KGQCaptSzbkzYjg4zJNlPJ1E1EatpKCQwZQExq00ptYQl8ynakHq08J2mbEuTPJCn/INm727dvb533xhtvqAsg1LMkU18uRLSQC0kbN25k/VkiIsoqDNASERERERGl2QMPPBBVWkHKenzwwQd8nXuYZFNLpncLGSyuJwdfJCIiSgYDtERERERERGnm9XpVRq9ko7dYtGgRa5/2IMmkj6yVPGrUKDVYmAzGSERElE2yr08aERERERFRLyelNf74xz9Gzbvjjjsytj/90Z133hl1/5ZbbmFwloiIshIzaImIiIiIiLpBKBTC/vvvrwYBbPHFF1+oedSz2bPymi9cuDClAQiJiIh6CgO0RERERERE3eTrr7/GrFmzVLBWSNAwsiYqdY8DDzxQBWSFDGa4ePFi7Lvvvny5iYgoK/WPIWyJiIiIiIgyYMaMGXj88cexcePG1nmVlZUoLS3l+9FN5PU94ogj1CRGjx7N4CwREWU1ZtASERERERERERERZQgHCSMiIiIiIiIiIiLKEAZoiYiIiIiIiIiIiDKEAVoiIiIiIiIiIiKiDGGAloiIiIiIiIiIiChDGKAlIiIiIiIiIiIiyhAGaImIiIiIiIiIiIgyhAFaIiIiIiIiIiIiogxhgJaIiIiIiIiIiIgoQxigJSIiIiIiIiIiIsoQBmiJiIiIiIiIiIiIMoQBWiIiIiIiIiIiIqIMYYCWiIiIiIiIiIiIKEMYoCUiIiIiIiIiIiLKEAZoiYiIiIiIiIiIiDKEAVoiIiIiIiIiIiKiDGGAloiIiIiIiIiIiChDGKAlIiIiIiIiIiIiyhAGaImIiIiIiIiIiIgyhAFaIiIiIiIiIiIiogxhgJaIiIiIiIiIiIgoQxigJSIiIiIiIiIiIsoQBmiJiIiIiIiIiIiIMoQBWiIiIiIiIiIiIqIMYYCWiIiIiIiIiIiIKEMYoCUiIiIiIiIiIiLKEAZoiYiIiIiIiIiIiDKEAVoiIiIiIiIiIiKiDGGAloiIiIiIiIiIiChDGKAlIiIiIiIiIiIiyhAGaImIiIiIiIiIiIgyhAFaIiIiIiIiIiIiImTG/wcdNVYquGqw4wAAAABJRU5ErkJggg==", "text/plain": [ "
" ] @@ -245,36 +245,36 @@ "name": "stderr", "output_type": "stream", "text": [ - "Coarse pass: 97%|█████████▋| 57/59 [00:04<00:00, 11.98it/s]\n", - "Coarse pass: 26%|██▌ | 9/35 [00:04<00:11, 2.23it/s]\n", - "Bootstrapping 1/20; spinning structures: 100%|██████████| 31/31 [00:07<00:00, 4.41it/s]\n", - "Bootstrapping 2/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.56it/s]\n", - "Bootstrapping 3/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.59it/s]\n", - "Bootstrapping 4/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.54it/s]\n", - "Bootstrapping 5/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.57it/s]\n", - "Bootstrapping 6/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.56it/s]\n", - "Bootstrapping 7/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.54it/s]\n", - "Bootstrapping 8/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.57it/s]\n", - "Bootstrapping 9/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.51it/s]\n", - "Bootstrapping 10/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.50it/s]\n", - "Bootstrapping 11/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.45it/s]\n", - "Bootstrapping 12/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.52it/s]\n", - "Bootstrapping 13/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.58it/s]\n", - "Bootstrapping 14/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.59it/s]\n", - "Bootstrapping 15/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.59it/s]\n", - "Bootstrapping 16/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.61it/s]\n", - "Bootstrapping 17/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.59it/s]\n", - "Bootstrapping 18/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.60it/s]\n", - "Bootstrapping 19/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.59it/s]\n", - "Bootstrapping 20/20; spinning structures: 100%|██████████| 31/31 [00:06<00:00, 4.53it/s]" + "Spinning structures | Fine pass | Coarse pass: 97%|█████████▋| 57/59 [00:05<00:00, 11.04it/s]\n", + "Spinning structures | Fine pass | Fine pass: 97%|█████████▋| 34/35 [00:04<00:00, 8.39it/s]\n", + "Bootstrapping 1/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.59it/s]\n", + "Bootstrapping 2/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.64it/s]\n", + "Bootstrapping 3/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.72it/s]\n", + "Bootstrapping 4/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.69it/s]\n", + "Bootstrapping 5/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.60it/s]\n", + "Bootstrapping 6/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.61it/s]\n", + "Bootstrapping 7/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.65it/s]\n", + "Bootstrapping 8/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.56it/s]\n", + "Bootstrapping 9/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.53it/s]\n", + "Bootstrapping 10/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.70it/s]\n", + "Bootstrapping 11/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.72it/s]\n", + "Bootstrapping 12/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.64it/s]\n", + "Bootstrapping 13/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.71it/s]\n", + "Bootstrapping 14/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.67it/s]\n", + "Bootstrapping 15/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.65it/s]\n", + "Bootstrapping 16/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.71it/s]\n", + "Bootstrapping 17/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.67it/s]\n", + "Bootstrapping 18/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.72it/s]\n", + "Bootstrapping 19/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.68it/s]\n", + "Bootstrapping 20/20; spinning structures: 100%|██████████| 29/29 [00:06<00:00, 4.69it/s]" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "Mean stoichiometry (m/d/t): [63.6 9.1 27.3]\n", - "Uncertainty (std) in stoichiometry (m/d/t): [2.3 4.1 2.6]\n" + "Mean stoichiometry (m/d/t): [60.6 12.1 27.3]\n", + "Uncertainty (std) in stoichiometry (m/d/t): [2.4 4.1 2.7]\n" ] }, { @@ -302,6 +302,99 @@ "print(f\"Uncertainty (std) in stoichiometry (m/d/t): {props_sd.round(1)}\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Other functionalities\n", + "\n", + "SPINNA provides framework for finding labeling efficiency values (one has to conduct a very specific experiment for this) as per https://doi.org/10.1038/s41592-024-02242-5. We do not provide a dataset so only the docstring is displayed below.\n", + "\n", + "Additionally, batch analaysis can be conducted via the command window. Simply run `picasso spinna -h` in the command window/terminal for instructions." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on function fit_le in module picasso.spinna:\n", + "\n", + "fit_le(\n", + " target_a: str,\n", + " target_b: str,\n", + " exp_data: dict,\n", + " granularity: int,\n", + " label_unc: dict,\n", + " distances: list[float],\n", + " N_sim: int = 1,\n", + " mask_dict: dict | None = None,\n", + " width: float | None = None,\n", + " height: float | None = None,\n", + " depth: float | None = None,\n", + " random_rot_mode: Literal['2D', '3D'] | None = '2D',\n", + " asynch: bool = True,\n", + " savedir: str = '',\n", + " callback: lib.ProgressDialog | Literal['console'] | None = None,\n", + " fitting_mode: Literal['coarse-to-fine', 'bayesian', 'brute-force'] = 'coarse-to-fine'\n", + ") -> tuple[dict, dict, float, float, lib.FloatArray1D, StructureMixer]\n", + " Fit labeling efficiency (LE) for two molecular target species.\n", + "\n", + " Builds the three required structures internally (monomer A, monomer B,\n", + " and a family of heterodimers AB at the requested distances), forces\n", + " LE to 100% during the fit, and delegates to ``compare_models`` which\n", + " fits label uncertainty and picks the best heterodimer distance. The\n", + " recovered structure proportions are reinterpreted as LE values via\n", + " ``get_le_from_props``.\n", + "\n", + " Parameters\n", + " ----------\n", + " target_a, target_b : str\n", + " Names of the two molecular target species. Both must appear as\n", + " keys in ``exp_data``.\n", + " exp_data : dict\n", + " Dictionary with molecular target names as keys and spatial\n", + " coordinates of the observed molecules as values.\n", + " granularity : int\n", + " Granularity as in ``generate_N_structures``.\n", + " label_unc : dict\n", + " Per-target list of label-uncertainty candidates. When a list has\n", + " a single entry the fit of that target's label_unc is skipped.\n", + " distances : list of float\n", + " Heterodimer distances (nm) to test. When a single entry is\n", + " given the heterodimer distance is fixed.\n", + " N_sim, mask_dict, width, height, depth, random_rot_mode, asynch,\n", + " savedir, callback, fitting_mode\n", + " Forwarded to ``compare_models``.\n", + "\n", + " Returns\n", + " -------\n", + " le_values : dict\n", + " Recovered LE values per target (percent).\n", + " fitted_label_unc : dict\n", + " Label uncertainty value chosen for each target.\n", + " best_distance : float\n", + " Heterodimer distance that produced the best fit.\n", + " best_score : float\n", + " KS2 score of the best fit.\n", + " best_props : lib.FloatArray1D\n", + " Structure proportions at the best fit (monomer A, monomer B,\n", + " heterodimer).\n", + " best_mixer : StructureMixer\n", + " Mixer corresponding to the best fit (its ``structures`` attribute\n", + " holds [monomer_a, monomer_b, heterodimer(best_distance)]).\n", + "\n" + ] + } + ], + "source": [ + "help(spinna.fit_le)" + ] + }, { "cell_type": "code", "execution_count": null, From 3d2efb4677fba88a69739cdee66b53c4b6ab6bb6 Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 28 May 2026 13:38:22 +0200 Subject: [PATCH 41/42] prep for release --- changelog.md | 49 ++++++++++++++---------- picasso/version.py | 2 +- readme.rst | 1 + release/one_click_macos_gui/readme.txt | 1 + release/one_click_windows_gui/readme.txt | 1 + 5 files changed, 32 insertions(+), 22 deletions(-) diff --git a/changelog.md b/changelog.md index fcf974d4..1eab1f68 100644 --- a/changelog.md +++ b/changelog.md @@ -1,38 +1,45 @@ # Changelog -Last change: 27-MAY-2026 CEST +Last change: 28-MAY-2026 CEST ## 0.10.1 -This patch adds a number of new, useful features rather than simply fixing the bugs found in 0.10.0. +#### Localize +- Fixed Gauss-fitting error when spot's sum is zero (zero division error) +#### Render - Faster rendering through improvements for all blur methods and multi-level spatial indexing for quick zoomed-in rendering - Multichannel rendering supports colormaps, not only a single RGB color +- Anisotripic DBSCAN (with faster implementation) [DOI: 10.1021/acs.jpcb.4c02030](https://doi.org/10.1021/acs.jpcb.4c02030) +- Faster cluster centers calculation for SMLM clusterer, DBSCAN and HDBSCAN + `lpz` is saved if applicable +- Load FOV keeps the aspect ratio of the input .txt file +- Files dialog resizing fixed +- Show 3D clustering widgets only when 3D data is loaded - Fixed linking saving `lpz` -- Anisotripic DBSCAN (faster implementation) [DOI: 10.1021/acs.jpcb.4c02030](https://doi.org/10.1021/acs.jpcb.4c02030) -- Improved docstrings for 3D SMLM clusterer -- SPINNA: comparing models uses fitting modes and has cleaner progress dialog -- SPINNA: convenient fitting of LE -- Batch analysis in SPINNA for LE fitting +- Fixed high-resolution display of mask in Render (#666) +- Removed render property cache since it did not provide any significant speed improvement + +#### SPINNA +- Comparing models uses fitting modes (Bayesian, etc) and has a cleaner progress dialog +- Convenient fitting of LE +- Area/volume button removed (deduced automatically from densities and number of molecules in the exp. data) +- Show 3D masking widgets only if 3D mask is selected +- Batch analysis for LE fitting - Batch analysis does not require area input (if found in metadata) -- Batch analysis: clear instructions on what columns are required -- SPINNA: area/volume button removed (deduced automatically from densities and number of molecules in the exp. data) -- Flake8 clean-up -- Efficient Filter: much lower RAM usage + faster filtering by histogram selection (1D/2D), especially for very large datasets -- Fixed Gauss-fitting error when spot's sum is zero (zero division error) +- Batch analysis has clear instructions on what columns are required via CLI and the docs update - Fixed NND plot reindexing after fitting (#665) -- Faster cluster centers calculation for SMLM clusterer, DBSCAN and HDBSCAN + `lpz` is saved if applicable -- Removed render property cache since it did not provide any significant speed improvement + +#### Filter +- Efficient Filter: much lower RAM usage + faster filtering by histogram selection (1D/2D), especially for very large datasets + +#### Others +- Expanded the scope of sample notebooks +- Improved docstrings for 3D SMLM clusterer +- Flake8 clean-up - Installers are distributed with readme.txt files (previously .rst) -- Render: Load FOV keeps the aspect ratio of the input .txt file -- Render: Files dialog resizing fixed -- Render GUI: show 3D clustering widgets only when 3D data is loaded -- SPINNA GUIL show 3D masking widgets only if 3D mask is selected -- Fixed high-resolution display of mask in Render (#666) +- Minor changes to documentation - Fixed subcluster check plot when one of the two populations is empty (#667) - Fixed 2D fitting with console printout and no multiprocessing -- Expanded the scope of sample notebooks -- Minor changes to documentation ## 0.10.0 diff --git a/picasso/version.py b/picasso/version.py index 61fb31ca..1f4c4d43 100644 --- a/picasso/version.py +++ b/picasso/version.py @@ -1 +1 @@ -__version__ = "0.10.0" +__version__ = "0.10.1" diff --git a/readme.rst b/readme.rst index 277c6000..8d51e25e 100644 --- a/readme.rst +++ b/readme.rst @@ -134,6 +134,7 @@ If you use Picasso in your research, please cite our Nature Protocols publicatio - Theoretical axial localization precision (Gauss LQ and MLE). DOI: `10.1038/s41467-026-70198-5 `__ - MLE fitting. DOI: `10.1038/nmeth.1449 `__ - GPU fitting (LQ). DOI: `10.1038/s41598-017-15313-9 `__. License can be found `here `__. +- 3D fitting via astigmatism. DOI: `10.1126/science.1153529 `__. - RCC undrifting: DOI: `10.1364/OE.22.015982 `__ - AIM undrifting. DOI: `10.1126/sciadv.adm776 `__ - SMLM clusterer. DOIs: `10.1038/s41467-021-22606-1 `__ and `10.1038/s41586-023-05925-9 `__ diff --git a/release/one_click_macos_gui/readme.txt b/release/one_click_macos_gui/readme.txt index e7320c77..fc2cf68e 100644 --- a/release/one_click_macos_gui/readme.txt +++ b/release/one_click_macos_gui/readme.txt @@ -59,6 +59,7 @@ If you use some of the functionalities provided by Picasso, please also cite the - Theoretical axial localization precision (Gauss LQ and MLE). DOI: 10.1038/s41467-026-70198-5 (https://doi.org/10.1038/s41467-026-70198-5) - MLE fitting. DOI: 10.1038/nmeth.1449 (https://doi.org/10.1038/nmeth.1449) - GPU fitting (LQ). DOI: 10.1038/s41598-017-15313-9 (https://doi.org/10.1038/s41598-017-15313-9). License can be found here (https://github.com/jungmannlab/picasso/tree/master/picasso/ext/pygpufit). +- 3D fitting via astigmatism. DOI: 10.1126/science.1153529 (https://www.science.org/doi/10.1126/science.1153529). - RCC undrifting: DOI: 10.1364/OE.22.015982 (https://doi.org/10.1364/OE.22.015982) - AIM undrifting. DOI: 10.1126/sciadv.adm776 (https://www.science.org/doi/10.1126/sciadv.adm7765) - SMLM clusterer. DOIs: 10.1038/s41467-021-22606-1 (https://doi.org/10.1038/s41467-021-22606-1) and 10.1038/s41586-023-05925-9 (https://doi.org/10.1038/s41586-023-05925-9) diff --git a/release/one_click_windows_gui/readme.txt b/release/one_click_windows_gui/readme.txt index f00160bc..039f1303 100644 --- a/release/one_click_windows_gui/readme.txt +++ b/release/one_click_windows_gui/readme.txt @@ -64,6 +64,7 @@ If you use some of the functionalities provided by Picasso, please also cite the - Theoretical axial localization precision (Gauss LQ and MLE). DOI: 10.1038/s41467-026-70198-5 (https://doi.org/10.1038/s41467-026-70198-5) - MLE fitting. DOI: 10.1038/nmeth.1449 (https://doi.org/10.1038/nmeth.1449) - GPU fitting (LQ). DOI: 10.1038/s41598-017-15313-9 (https://doi.org/10.1038/s41598-017-15313-9). License can be found here (https://github.com/jungmannlab/picasso/tree/master/picasso/ext/pygpufit). +- 3D fitting via astigmatism. DOI: 10.1126/science.1153529 (https://www.science.org/doi/10.1126/science.1153529). - RCC undrifting: DOI: 10.1364/OE.22.015982 (https://doi.org/10.1364/OE.22.015982) - AIM undrifting. DOI: 10.1126/sciadv.adm776 (https://www.science.org/doi/10.1126/sciadv.adm7765) - SMLM clusterer. DOIs: 10.1038/s41467-021-22606-1 (https://doi.org/10.1038/s41467-021-22606-1) and 10.1038/s41586-023-05925-9 (https://doi.org/10.1038/s41586-023-05925-9) From 0fd4284698b7e3d978673534eb28da04e5903abc Mon Sep 17 00:00:00 2001 From: rafalkowalewski1 Date: Thu, 28 May 2026 13:52:24 +0200 Subject: [PATCH 42/42] typo fix --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 1eab1f68..bb5c30b0 100644 --- a/changelog.md +++ b/changelog.md @@ -33,7 +33,7 @@ Last change: 28-MAY-2026 CEST - Efficient Filter: much lower RAM usage + faster filtering by histogram selection (1D/2D), especially for very large datasets #### Others -- Expanded the scope of sample notebooks +- Expanded the scope of the sample notebooks - Improved docstrings for 3D SMLM clusterer - Flake8 clean-up - Installers are distributed with readme.txt files (previously .rst)