lbm_suite2p_python 3.0.8__tar.gz → 3.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lbm_suite2p_python-3.0.8/lbm_suite2p_python.egg-info → lbm_suite2p_python-3.1.1}/PKG-INFO +2 -2
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/__init__.py +2 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/cli.py +2 -2
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/db_settings.py +114 -26
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/run_lsp.py +43 -22
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/utils.py +10 -1
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/volume.py +2 -2
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/zplane.py +4950 -4950
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1/lbm_suite2p_python.egg-info}/PKG-INFO +2 -2
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python.egg-info/requires.txt +1 -1
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/pyproject.toml +106 -106
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/LICENSE.md +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/MANIFEST.in +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/README.md +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/__main__.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/_benchmarking.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/cellpose.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/conversion.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/default_ops.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/grid_search.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/gui.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/merging.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python/postprocessing.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python.egg-info/SOURCES.txt +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python.egg-info/dependency_links.txt +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python.egg-info/entry_points.txt +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/lbm_suite2p_python.egg-info/top_level.txt +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/setup.cfg +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/tests/test_frame_count_aliases.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/tests/test_pipeline_parameters.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/tests/test_refactored_pipeline.py +0 -0
- {lbm_suite2p_python-3.0.8 → lbm_suite2p_python-3.1.1}/tests/test_run_volume.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lbm_suite2p_python
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.1.1
|
|
4
4
|
Summary: Calcium Imaging Pipeline built with Suite2p, Cellpose and Rastermap
|
|
5
5
|
License-Expression: BSD-3-Clause
|
|
6
6
|
Project-URL: homepage, https://github.com/MillerBrainObservatory/LBM-Suite2p-Python
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
11
11
|
Requires-Python: <3.14,>=3.12.7
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE.md
|
|
14
|
-
Requires-Dist: mbo_utilities>=3.
|
|
14
|
+
Requires-Dist: mbo_utilities>=3.2.1
|
|
15
15
|
Requires-Dist: suite2p>=1.0.0.1
|
|
16
16
|
Requires-Dist: setuptools<81
|
|
17
17
|
Provides-Extra: rastermap
|
|
@@ -70,6 +70,7 @@ from lbm_suite2p_python.postprocessing import (
|
|
|
70
70
|
load_ops,
|
|
71
71
|
load_planar_results,
|
|
72
72
|
dff_rolling_percentile,
|
|
73
|
+
dff_median_filter,
|
|
73
74
|
zscore_trace,
|
|
74
75
|
baseline_percentile_dff,
|
|
75
76
|
dff_shot_noise,
|
|
@@ -133,6 +134,7 @@ __all__ = [
|
|
|
133
134
|
"load_ops",
|
|
134
135
|
"load_planar_results",
|
|
135
136
|
"dff_rolling_percentile",
|
|
137
|
+
"dff_median_filter",
|
|
136
138
|
"zscore_trace",
|
|
137
139
|
"baseline_percentile_dff",
|
|
138
140
|
"dff_shot_noise",
|
|
@@ -208,9 +208,9 @@ Examples:
|
|
|
208
208
|
help="smoothing window for dF/F"
|
|
209
209
|
)
|
|
210
210
|
dff.add_argument(
|
|
211
|
-
"--norm-method", choices=["dff", "zscore"], default="
|
|
211
|
+
"--norm-method", choices=["dff", "zscore"], default="zscore",
|
|
212
212
|
dest="norm_method",
|
|
213
|
-
help="normalization for norm_traces.npy (default:
|
|
213
|
+
help="normalization for norm_traces.npy (default: zscore)"
|
|
214
214
|
)
|
|
215
215
|
dff.add_argument(
|
|
216
216
|
"--correct-neuropil", dest="correct_neuropil",
|
|
@@ -175,20 +175,121 @@ _FORK_TO_UPSTREAM_RENAMES: dict[str, str] = {
|
|
|
175
175
|
"pretrained_model": "cellpose_model",
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
# fork's anatomical_only int
|
|
179
|
-
# upstream
|
|
180
|
-
#
|
|
181
|
-
# 2 -> 'meanImg'
|
|
182
|
-
# 3 -> enhanced_mean_img (REMOVED upstream — no equivalent string;
|
|
183
|
-
# falls through to max_proj branch)
|
|
184
|
-
# 4 -> 'max_proj' (anything not matching the two strings hits else: img=max_proj)
|
|
178
|
+
# fork's anatomical_only int selects which image cellpose runs on;
|
|
179
|
+
# upstream encodes this via cellpose_settings.img. 3 (enhanced mean) was
|
|
180
|
+
# removed from suite2p detection — reconcile_detection_keys maps it to 1.
|
|
185
181
|
_ANATOMICAL_ONLY_TO_IMG: dict[int, str] = {
|
|
186
182
|
1: "max_proj / meanImg",
|
|
187
183
|
2: "meanImg",
|
|
188
184
|
4: "max_proj",
|
|
189
185
|
}
|
|
186
|
+
|
|
187
|
+
# Values suite2p actually accepts. Mirrors suite2p.parameters:
|
|
188
|
+
# detection.algorithm description -> ['sparsery', 'sourcery', 'cellpose'];
|
|
189
|
+
# cellpose_settings.img description -> ['max_proj / meanImg', 'meanImg',
|
|
190
|
+
# 'max_proj']. Hardcoded (the schema lists these only in a description
|
|
191
|
+
# string) and kept in sync with upstream.
|
|
192
|
+
VALID_DETECTION_ALGORITHMS = ("sparsery", "sourcery", "cellpose")
|
|
193
|
+
VALID_CELLPOSE_IMG = ("max_proj / meanImg", "meanImg", "max_proj")
|
|
194
|
+
|
|
195
|
+
# legacy / renamed cellpose image spellings -> current suite2p string. the
|
|
196
|
+
# old enhanced-mean image (meanImgE) was removed from suite2p detection, so
|
|
197
|
+
# it maps to suite2p's default; use spatial_hp_cp (highpass_spatial) > 0 to
|
|
198
|
+
# recover the sharpening.
|
|
199
|
+
_LEGACY_IMG_ALIASES = {
|
|
200
|
+
"meanimge": "max_proj / meanImg",
|
|
201
|
+
"enhanced_meanimg": "max_proj / meanImg",
|
|
202
|
+
"enhanced_mean_img": "max_proj / meanImg",
|
|
203
|
+
"mean_img": "meanImg",
|
|
204
|
+
"maximg": "max_proj",
|
|
205
|
+
"max_img": "max_proj",
|
|
206
|
+
"maxproj": "max_proj",
|
|
207
|
+
}
|
|
190
208
|
_UPSTREAM_TO_FORK_RENAMES = {v: k for k, v in _FORK_TO_UPSTREAM_RENAMES.items()}
|
|
191
209
|
|
|
210
|
+
|
|
211
|
+
def reconcile_detection_keys(ops: dict | None) -> dict | None:
|
|
212
|
+
"""Make the flat detection keys consistent and valid for suite2p.
|
|
213
|
+
|
|
214
|
+
The fork spells detection as `anatomical_only` (int) / `sparse_mode`
|
|
215
|
+
(bool); suite2p uses `algorithm` (str). Users mix both. This keeps them
|
|
216
|
+
mutually consistent and validates them against what suite2p accepts, so
|
|
217
|
+
either spelling engages identical code paths. Mutates and returns `ops`
|
|
218
|
+
(no-op for non-dicts); idempotent.
|
|
219
|
+
|
|
220
|
+
- `algorithm="cellpose"` with no `anatomical_only` -> `anatomical_only=1`
|
|
221
|
+
(suite2p's default cellpose image), so the fork's anatomical paths run.
|
|
222
|
+
- `anatomical_only>0` with no `algorithm` -> `algorithm="cellpose"`.
|
|
223
|
+
- `algorithm` in {sparsery, sourcery} -> `anatomical_only=0`, `sparse_mode`.
|
|
224
|
+
- legacy `anatomical_only=3` (enhanced mean, removed upstream) -> 1 + warn.
|
|
225
|
+
- unknown `algorithm` / `img` -> warn and fall back to a valid value.
|
|
226
|
+
"""
|
|
227
|
+
import warnings
|
|
228
|
+
|
|
229
|
+
if not isinstance(ops, dict):
|
|
230
|
+
return ops
|
|
231
|
+
|
|
232
|
+
algo = ops.get("algorithm")
|
|
233
|
+
if isinstance(algo, str):
|
|
234
|
+
algo = algo.strip()
|
|
235
|
+
if algo in VALID_DETECTION_ALGORITHMS:
|
|
236
|
+
ops["algorithm"] = algo
|
|
237
|
+
else:
|
|
238
|
+
warnings.warn(
|
|
239
|
+
f"Unknown detection algorithm {ops['algorithm']!r}; suite2p "
|
|
240
|
+
f"accepts {VALID_DETECTION_ALGORITHMS}. Ignoring it (falling "
|
|
241
|
+
"back to anatomical_only / suite2p default).",
|
|
242
|
+
stacklevel=2,
|
|
243
|
+
)
|
|
244
|
+
ops.pop("algorithm", None)
|
|
245
|
+
algo = None
|
|
246
|
+
else:
|
|
247
|
+
algo = None
|
|
248
|
+
|
|
249
|
+
anat = ops.get("anatomical_only")
|
|
250
|
+
try:
|
|
251
|
+
anat_int = int(anat) if anat is not None else None
|
|
252
|
+
except (TypeError, ValueError):
|
|
253
|
+
anat_int = None
|
|
254
|
+
|
|
255
|
+
if anat_int == 3:
|
|
256
|
+
warnings.warn(
|
|
257
|
+
"anatomical_only=3 (enhanced mean image) was removed from suite2p "
|
|
258
|
+
"detection; using anatomical_only=1 (suite2p default image). Set "
|
|
259
|
+
"spatial_hp_cp>0 to sharpen the detection image.",
|
|
260
|
+
stacklevel=2,
|
|
261
|
+
)
|
|
262
|
+
anat_int = 1
|
|
263
|
+
ops["anatomical_only"] = 1
|
|
264
|
+
|
|
265
|
+
if algo == "cellpose":
|
|
266
|
+
if not anat_int or anat_int <= 0:
|
|
267
|
+
ops["anatomical_only"] = 1 # suite2p's default cellpose image
|
|
268
|
+
elif algo in ("sparsery", "sourcery"):
|
|
269
|
+
ops["anatomical_only"] = 0
|
|
270
|
+
ops.setdefault("sparse_mode", algo == "sparsery")
|
|
271
|
+
elif algo is None and anat_int and anat_int > 0:
|
|
272
|
+
ops["algorithm"] = "cellpose"
|
|
273
|
+
|
|
274
|
+
img = ops.get("img")
|
|
275
|
+
if isinstance(img, str) and img not in VALID_CELLPOSE_IMG:
|
|
276
|
+
mapped = _LEGACY_IMG_ALIASES.get(img.strip().lower())
|
|
277
|
+
if mapped:
|
|
278
|
+
warnings.warn(
|
|
279
|
+
f"cellpose image {img!r} is legacy/renamed; using {mapped!r}.",
|
|
280
|
+
stacklevel=2,
|
|
281
|
+
)
|
|
282
|
+
ops["img"] = mapped
|
|
283
|
+
else:
|
|
284
|
+
warnings.warn(
|
|
285
|
+
f"Unknown cellpose image {img!r}; suite2p accepts "
|
|
286
|
+
f"{VALID_CELLPOSE_IMG}. Ignoring it (using suite2p default).",
|
|
287
|
+
stacklevel=2,
|
|
288
|
+
)
|
|
289
|
+
ops.pop("img", None)
|
|
290
|
+
|
|
291
|
+
return ops
|
|
292
|
+
|
|
192
293
|
# per-section flat-key disambiguation. Several upstream sections share
|
|
193
294
|
# an upstream key. When flattened to ops, the second iteration overwrites
|
|
194
295
|
# the first; on the reverse derivation, the surviving value gets fanned
|
|
@@ -286,8 +387,9 @@ def ops_to_db_settings(ops: dict) -> tuple[dict, dict]:
|
|
|
286
387
|
settings: dict[str, Any] = {}
|
|
287
388
|
|
|
288
389
|
# resolve fork→upstream renames into a temporary lookup that
|
|
289
|
-
# doesn't mutate the caller's ops
|
|
290
|
-
|
|
390
|
+
# doesn't mutate the caller's ops. reconcile first so algorithm /
|
|
391
|
+
# anatomical_only / img are consistent and valid before translation.
|
|
392
|
+
lookup = reconcile_detection_keys(dict(ops))
|
|
291
393
|
for fork_name, upstream_name in _FORK_TO_UPSTREAM_RENAMES.items():
|
|
292
394
|
if fork_name in lookup and upstream_name not in lookup:
|
|
293
395
|
lookup[upstream_name] = lookup[fork_name]
|
|
@@ -377,11 +479,9 @@ def ops_to_db_settings(ops: dict) -> tuple[dict, dict]:
|
|
|
377
479
|
if key in lookup:
|
|
378
480
|
cellpose[key] = lookup[key]
|
|
379
481
|
|
|
380
|
-
# fork's anatomical_only int picks which image cellpose runs on
|
|
381
|
-
# upstream encodes this via cellpose_settings.img
|
|
382
|
-
#
|
|
383
|
-
# (the upstream default 'max_proj / meanImg' = log ratio image)
|
|
384
|
-
# because the translator only sets algorithm='cellpose' otherwise.
|
|
482
|
+
# fork's anatomical_only int picks which image cellpose runs on;
|
|
483
|
+
# upstream encodes this via cellpose_settings.img. anatomical_only was
|
|
484
|
+
# already validated/normalized (incl. legacy 3) by reconcile above.
|
|
385
485
|
anat = lookup.get("anatomical_only")
|
|
386
486
|
if anat and "img" not in cellpose:
|
|
387
487
|
try:
|
|
@@ -390,18 +490,6 @@ def ops_to_db_settings(ops: dict) -> tuple[dict, dict]:
|
|
|
390
490
|
anat_int = None
|
|
391
491
|
if anat_int in _ANATOMICAL_ONLY_TO_IMG:
|
|
392
492
|
cellpose["img"] = _ANATOMICAL_ONLY_TO_IMG[anat_int]
|
|
393
|
-
elif anat_int == 3:
|
|
394
|
-
# upstream removed enhanced_mean_img — closest fallback is
|
|
395
|
-
# 'meanImg' (same source family, no median-ratio enhancement).
|
|
396
|
-
# warn the caller so they know something changed.
|
|
397
|
-
import warnings
|
|
398
|
-
warnings.warn(
|
|
399
|
-
"anatomical_only=3 (enhanced_mean_img) is no longer supported "
|
|
400
|
-
"upstream; falling back to cellpose_settings.img='meanImg'. "
|
|
401
|
-
"Use anatomical_only in {1, 2, 4} to avoid this fallback.",
|
|
402
|
-
stacklevel=2,
|
|
403
|
-
)
|
|
404
|
-
cellpose["img"] = "max_proj"
|
|
405
493
|
|
|
406
494
|
if cellpose:
|
|
407
495
|
detection["cellpose_settings"] = cellpose
|
|
@@ -14,7 +14,7 @@ import copy
|
|
|
14
14
|
import numpy as np
|
|
15
15
|
|
|
16
16
|
from lbm_suite2p_python.default_ops import default_ops
|
|
17
|
-
from lbm_suite2p_python.db_settings import save_ops_db_settings
|
|
17
|
+
from lbm_suite2p_python.db_settings import save_ops_db_settings, reconcile_detection_keys
|
|
18
18
|
from lbm_suite2p_python.postprocessing import (
|
|
19
19
|
ops_to_json,
|
|
20
20
|
load_ops,
|
|
@@ -751,6 +751,7 @@ def _prepare_plane_ops(*, base_ops, plane_idx, num_planes, input_arr,
|
|
|
751
751
|
"""
|
|
752
752
|
plane_num = plane_idx + 1
|
|
753
753
|
current_ops = copy.deepcopy(load_ops(base_ops)) if base_ops else default_ops()
|
|
754
|
+
reconcile_detection_keys(current_ops)
|
|
754
755
|
current_ops["plane"] = plane_num
|
|
755
756
|
current_ops["num_zplanes"] = num_planes
|
|
756
757
|
|
|
@@ -796,7 +797,7 @@ def pipeline(
|
|
|
796
797
|
dff_window_size: int = None,
|
|
797
798
|
dff_percentile: int = 20,
|
|
798
799
|
dff_smooth_window: int = None,
|
|
799
|
-
norm_method: str = "
|
|
800
|
+
norm_method: str = "zscore",
|
|
800
801
|
correct_neuropil: bool = True,
|
|
801
802
|
cell_filters: list = None,
|
|
802
803
|
accept_all_cells: bool = False,
|
|
@@ -827,8 +828,17 @@ def pipeline(
|
|
|
827
828
|
Output directory for results. If None, saves next to input file.
|
|
828
829
|
Required when input_data is an array without filenames.
|
|
829
830
|
ops : dict, optional
|
|
830
|
-
Suite2p
|
|
831
|
-
defaults with metadata auto-populated from input.
|
|
831
|
+
Flat Suite2p parameter overrides (the fork's ops shape). If None,
|
|
832
|
+
uses suite2p defaults with metadata auto-populated from input.
|
|
833
|
+
Detection is selectable either way and kept consistent: suite2p's
|
|
834
|
+
``"algorithm"`` ("cellpose" / "sparsery" / "sourcery") or the fork's
|
|
835
|
+
``"anatomical_only"`` (0=functional, 1=max_proj/mean, 2=mean,
|
|
836
|
+
4=max_proj). Setting one fills in the other; invalid or legacy
|
|
837
|
+
values (e.g. ``anatomical_only=3``) are warned and mapped to what
|
|
838
|
+
suite2p accepts.
|
|
839
|
+
db, settings : dict, optional
|
|
840
|
+
suite2p's nested ``db`` / ``settings`` dicts (the new upstream
|
|
841
|
+
shape). Flattened and merged into ``ops``; explicit ``ops`` keys win.
|
|
832
842
|
planes : int or list, optional
|
|
833
843
|
Which z-planes to process (1-indexed). None processes all planes.
|
|
834
844
|
roi_mode : int, optional
|
|
@@ -862,7 +872,7 @@ def pipeline(
|
|
|
862
872
|
dff_smooth_window : int, optional
|
|
863
873
|
Temporal smoothing window for dF/F traces (frames).
|
|
864
874
|
If None, auto-calculated. Set to 1 to disable.
|
|
865
|
-
norm_method : str, default "
|
|
875
|
+
norm_method : str, default "zscore"
|
|
866
876
|
Normalization for the saved norm_traces.npy. "dff" uses the rolling
|
|
867
877
|
percentile ΔF/F (dff_window_size / dff_percentile / dff_smooth_window
|
|
868
878
|
apply); "zscore" uses per-ROI (F - mean) / std. Quality metrics
|
|
@@ -1232,7 +1242,7 @@ def run_volume(
|
|
|
1232
1242
|
dff_window_size: int = None,
|
|
1233
1243
|
dff_percentile: int = 20,
|
|
1234
1244
|
dff_smooth_window: int = None,
|
|
1235
|
-
norm_method: str = "
|
|
1245
|
+
norm_method: str = "zscore",
|
|
1236
1246
|
correct_neuropil: bool = True,
|
|
1237
1247
|
accept_all_cells: bool = False,
|
|
1238
1248
|
cell_filters: list = None,
|
|
@@ -1275,7 +1285,7 @@ def run_volume(
|
|
|
1275
1285
|
List of frame indices to process.
|
|
1276
1286
|
dff_window_size, dff_percentile, dff_smooth_window : optional
|
|
1277
1287
|
dF/F calculation parameters (used when norm_method="dff").
|
|
1278
|
-
norm_method : str, default "
|
|
1288
|
+
norm_method : str, default "zscore"
|
|
1279
1289
|
Normalization for norm_traces.npy: "dff" or "zscore". See pipeline().
|
|
1280
1290
|
accept_all_cells : bool, default False
|
|
1281
1291
|
Mark all ROIs as accepted.
|
|
@@ -1381,8 +1391,8 @@ def run_volume(
|
|
|
1381
1391
|
else None
|
|
1382
1392
|
)
|
|
1383
1393
|
_volume_source_shape = (
|
|
1384
|
-
tuple(input_arr.
|
|
1385
|
-
if input_arr is not None and hasattr(input_arr, "
|
|
1394
|
+
tuple(input_arr._shape5d())
|
|
1395
|
+
if input_arr is not None and hasattr(input_arr, "_shape5d")
|
|
1386
1396
|
else None
|
|
1387
1397
|
)
|
|
1388
1398
|
|
|
@@ -1530,8 +1540,10 @@ def run_volume(
|
|
|
1530
1540
|
)
|
|
1531
1541
|
|
|
1532
1542
|
# Download the cellpose model once before fanning out; concurrent
|
|
1533
|
-
# first-time downloads race on the shared cache file.
|
|
1534
|
-
|
|
1543
|
+
# first-time downloads race on the shared cache file. Use a prepared
|
|
1544
|
+
# per-plane ops (already reconciled) so algorithm="cellpose" without
|
|
1545
|
+
# anatomical_only still triggers the warm-up.
|
|
1546
|
+
_prewarm_cellpose_model(prepared[0][1] if prepared else ops)
|
|
1535
1547
|
|
|
1536
1548
|
manager = mp.Manager()
|
|
1537
1549
|
log_queue = manager.Queue()
|
|
@@ -2262,7 +2274,7 @@ def run_plane(
|
|
|
2262
2274
|
dff_window_size: int = None,
|
|
2263
2275
|
dff_percentile: int = 20,
|
|
2264
2276
|
dff_smooth_window: int = None,
|
|
2265
|
-
norm_method: str = "
|
|
2277
|
+
norm_method: str = "zscore",
|
|
2266
2278
|
correct_neuropil: bool = True,
|
|
2267
2279
|
accept_all_cells: bool = False,
|
|
2268
2280
|
cell_filters: list = None,
|
|
@@ -2287,7 +2299,11 @@ def run_plane(
|
|
|
2287
2299
|
Root directory to save the results. A subdirectory will be created based on
|
|
2288
2300
|
the input filename or `plane_name` parameter.
|
|
2289
2301
|
ops : dict, str or Path, optional
|
|
2290
|
-
Path to or dict of user‐supplied ops.
|
|
2302
|
+
Path to or dict of user‐supplied ops (flat fork shape). Detection
|
|
2303
|
+
accepts suite2p's ``"algorithm"`` or the fork's ``"anatomical_only"``
|
|
2304
|
+
interchangeably (kept consistent; invalid/legacy values warned and
|
|
2305
|
+
mapped to suite2p-valid ones). Pass ``db`` / ``settings`` for the
|
|
2306
|
+
nested upstream shape.
|
|
2291
2307
|
chan2_file : str, optional
|
|
2292
2308
|
Path to structural / anatomical data used for registration.
|
|
2293
2309
|
keep_raw : bool, default False
|
|
@@ -2304,7 +2320,7 @@ def run_plane(
|
|
|
2304
2320
|
Percentile for baseline F0.
|
|
2305
2321
|
dff_smooth_window : int, optional
|
|
2306
2322
|
Smoothing window for dF/F. Default: auto-calculated.
|
|
2307
|
-
norm_method : str, default "
|
|
2323
|
+
norm_method : str, default "zscore"
|
|
2308
2324
|
Normalization for norm_traces.npy: "dff" (rolling percentile ΔF/F)
|
|
2309
2325
|
or "zscore" (per-ROI (F - mean) / std). Quality metrics always use
|
|
2310
2326
|
ΔF/F regardless of this setting.
|
|
@@ -2413,6 +2429,9 @@ def run_plane(
|
|
|
2413
2429
|
ops_user = load_ops(ops) if ops else {}
|
|
2414
2430
|
if db is not None or settings is not None:
|
|
2415
2431
|
ops_user = merge_db_settings_into_ops(ops_user, db, settings)
|
|
2432
|
+
# keep the flat (anatomical_only/sparse_mode) and suite2p (algorithm)
|
|
2433
|
+
# detection spellings consistent + valid before they merge into ops.
|
|
2434
|
+
reconcile_detection_keys(ops_user)
|
|
2416
2435
|
ops = {**ops_default, **ops_user, "data_path": str(input_path.resolve())}
|
|
2417
2436
|
|
|
2418
2437
|
# normalize kwargs
|
|
@@ -2735,7 +2754,7 @@ def run_plane(
|
|
|
2735
2754
|
dict(file.metadata) if hasattr(file, "metadata") else None
|
|
2736
2755
|
)
|
|
2737
2756
|
_src_shape = (
|
|
2738
|
-
tuple(file.
|
|
2757
|
+
tuple(file._shape5d()) if hasattr(file, "_shape5d") else None
|
|
2739
2758
|
)
|
|
2740
2759
|
_apply_reactive_metadata(
|
|
2741
2760
|
ops=ops,
|
|
@@ -2868,13 +2887,11 @@ def run_plane(
|
|
|
2868
2887
|
show_progress=False,
|
|
2869
2888
|
)
|
|
2870
2889
|
ops["chan2_file"] = str((plane_dir / "data_chan2.bin").resolve())
|
|
2871
|
-
# Use
|
|
2872
|
-
# (
|
|
2873
|
-
# real T dimension
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
if hasattr(chan2_data, "shape5d"):
|
|
2877
|
-
ops["nframes_chan2"] = int(chan2_data.shape5d[0])
|
|
2890
|
+
# Use _shape5d() (the always-5D contract) so natural-rank
|
|
2891
|
+
# arrays (BinArray, a 4D TZYX _ChannelView) still report the
|
|
2892
|
+
# real T dimension at index 0.
|
|
2893
|
+
if hasattr(chan2_data, "_shape5d"):
|
|
2894
|
+
ops["nframes_chan2"] = int(chan2_data._shape5d()[0])
|
|
2878
2895
|
elif hasattr(chan2_data, "shape"):
|
|
2879
2896
|
ops["nframes_chan2"] = int(chan2_data.shape[0])
|
|
2880
2897
|
else:
|
|
@@ -2950,6 +2967,10 @@ def run_plane(
|
|
|
2950
2967
|
save_ops_db_settings(ops_file, updated_ops)
|
|
2951
2968
|
|
|
2952
2969
|
except Exception as e:
|
|
2970
|
+
if "no ROIs were found" in str(e):
|
|
2971
|
+
print(f"Warning: no ROIs found for {plane_dir.name}, skipping plane.")
|
|
2972
|
+
_cleanup_bin_files(plane_dir, keep_raw, keep_reg)
|
|
2973
|
+
return ops_file
|
|
2953
2974
|
print(f"Error in run_plane_bin: {e}")
|
|
2954
2975
|
traceback.print_exc()
|
|
2955
2976
|
_cleanup_bin_files(plane_dir, keep_raw, keep_reg)
|
|
@@ -3,7 +3,14 @@ import numpy as np
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# mbo_utilities >= 4.0 exposes a single LazyArray base; isinstance covers
|
|
7
|
+
# every built-in and any third-party plugin. The class-name tuple is the
|
|
8
|
+
# fallback for mbo_utilities < 4.0, which has no shared base.
|
|
9
|
+
try:
|
|
10
|
+
from mbo_utilities import LazyArray as _LazyArray
|
|
11
|
+
except ImportError: # mbo_utilities < 4.0
|
|
12
|
+
_LazyArray = None
|
|
13
|
+
|
|
7
14
|
_LAZY_ARRAY_TYPES = (
|
|
8
15
|
"ScanImageArray",
|
|
9
16
|
"LBMArray",
|
|
@@ -22,6 +29,8 @@ _LAZY_ARRAY_TYPES = (
|
|
|
22
29
|
|
|
23
30
|
def _is_lazy_array(obj):
|
|
24
31
|
"""Check if obj is an mbo_utilities lazy array type."""
|
|
32
|
+
if _LazyArray is not None and isinstance(obj, _LazyArray):
|
|
33
|
+
return True
|
|
25
34
|
return type(obj).__name__ in _LAZY_ARRAY_TYPES
|
|
26
35
|
|
|
27
36
|
|
|
@@ -1891,7 +1891,7 @@ def plot_volume_trace_figures(
|
|
|
1891
1891
|
dff_percentile: int = 8,
|
|
1892
1892
|
dff_window_size: int = None,
|
|
1893
1893
|
dff_smooth_window: int = None,
|
|
1894
|
-
norm_method: str = "
|
|
1894
|
+
norm_method: str = "zscore",
|
|
1895
1895
|
correct_neuropil: bool = True,
|
|
1896
1896
|
):
|
|
1897
1897
|
"""
|
|
@@ -1926,7 +1926,7 @@ def plot_volume_trace_figures(
|
|
|
1926
1926
|
model exists, the rastermap heatmap is skipped.
|
|
1927
1927
|
dff_percentile, dff_window_size, dff_smooth_window
|
|
1928
1928
|
Forwarded to :func:`dff_rolling_percentile` (when norm_method="dff").
|
|
1929
|
-
norm_method : str, default "
|
|
1929
|
+
norm_method : str, default "zscore"
|
|
1930
1930
|
Normalization for the norm-trace plots: "dff" or "zscore".
|
|
1931
1931
|
"""
|
|
1932
1932
|
from types import SimpleNamespace
|