lbm_suite2p_python 3.0.7__tar.gz → 3.1.0__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.7/lbm_suite2p_python.egg-info → lbm_suite2p_python-3.1.0}/PKG-INFO +2 -2
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/__init__.py +6 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/cli.py +6 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/conversion.py +2 -2
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/db_settings.py +114 -26
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/postprocessing.py +78 -7
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/run_lsp.py +111 -35
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/utils.py +10 -1
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/volume.py +35 -24
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/zplane.py +97 -85
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0/lbm_suite2p_python.egg-info}/PKG-INFO +2 -2
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python.egg-info/requires.txt +1 -1
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/pyproject.toml +106 -106
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/tests/test_pipeline_parameters.py +29 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/tests/test_refactored_pipeline.py +4 -2
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/LICENSE.md +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/MANIFEST.in +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/README.md +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/__main__.py +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/_benchmarking.py +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/cellpose.py +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/default_ops.py +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/grid_search.py +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/gui.py +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python/merging.py +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python.egg-info/SOURCES.txt +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python.egg-info/dependency_links.txt +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python.egg-info/entry_points.txt +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/lbm_suite2p_python.egg-info/top_level.txt +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/setup.cfg +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/tests/test_frame_count_aliases.py +0 -0
- {lbm_suite2p_python-3.0.7 → lbm_suite2p_python-3.1.0}/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.0
|
|
3
|
+
Version: 3.1.0
|
|
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.0
|
|
14
|
+
Requires-Dist: mbo_utilities>=3.1.0
|
|
15
15
|
Requires-Dist: suite2p>=1.0.0.1
|
|
16
16
|
Requires-Dist: setuptools<81
|
|
17
17
|
Provides-Extra: rastermap
|
|
@@ -70,6 +70,9 @@ from lbm_suite2p_python.postprocessing import (
|
|
|
70
70
|
load_ops,
|
|
71
71
|
load_planar_results,
|
|
72
72
|
dff_rolling_percentile,
|
|
73
|
+
dff_median_filter,
|
|
74
|
+
zscore_trace,
|
|
75
|
+
baseline_percentile_dff,
|
|
73
76
|
dff_shot_noise,
|
|
74
77
|
compute_roi_stats,
|
|
75
78
|
)
|
|
@@ -131,6 +134,9 @@ __all__ = [
|
|
|
131
134
|
"load_ops",
|
|
132
135
|
"load_planar_results",
|
|
133
136
|
"dff_rolling_percentile",
|
|
137
|
+
"dff_median_filter",
|
|
138
|
+
"zscore_trace",
|
|
139
|
+
"baseline_percentile_dff",
|
|
134
140
|
"dff_shot_noise",
|
|
135
141
|
"compute_roi_stats",
|
|
136
142
|
|
|
@@ -207,6 +207,11 @@ Examples:
|
|
|
207
207
|
"--dff-smooth", type=int, dest="dff_smooth_window",
|
|
208
208
|
help="smoothing window for dF/F"
|
|
209
209
|
)
|
|
210
|
+
dff.add_argument(
|
|
211
|
+
"--norm-method", choices=["dff", "zscore"], default="zscore",
|
|
212
|
+
dest="norm_method",
|
|
213
|
+
help="normalization for norm_traces.npy (default: zscore)"
|
|
214
|
+
)
|
|
210
215
|
dff.add_argument(
|
|
211
216
|
"--correct-neuropil", dest="correct_neuropil",
|
|
212
217
|
action=argparse.BooleanOptionalAction, default=True,
|
|
@@ -570,6 +575,7 @@ def main():
|
|
|
570
575
|
dff_window_size=args.dff_window_size,
|
|
571
576
|
dff_percentile=args.dff_percentile,
|
|
572
577
|
dff_smooth_window=args.dff_smooth_window,
|
|
578
|
+
norm_method=args.norm_method,
|
|
573
579
|
correct_neuropil=args.correct_neuropil,
|
|
574
580
|
cell_filters=cell_filters,
|
|
575
581
|
accept_all_cells=args.accept_all_cells,
|
|
@@ -871,7 +871,7 @@ def get_results(path, include_traces=True):
|
|
|
871
871
|
"F": None,
|
|
872
872
|
"Fneu": None,
|
|
873
873
|
"spks": None,
|
|
874
|
-
"
|
|
874
|
+
"norm_traces": None,
|
|
875
875
|
"ops": None,
|
|
876
876
|
"seg_file": None,
|
|
877
877
|
}
|
|
@@ -902,7 +902,7 @@ def get_results(path, include_traces=True):
|
|
|
902
902
|
|
|
903
903
|
# Load traces if requested
|
|
904
904
|
if include_traces and fmt == "suite2p":
|
|
905
|
-
for name in ["F", "Fneu", "spks", "
|
|
905
|
+
for name in ["F", "Fneu", "spks", "norm_traces"]:
|
|
906
906
|
trace_file = path / f"{name}.npy"
|
|
907
907
|
if trace_file.exists():
|
|
908
908
|
result[name] = np.load(trace_file)
|
|
@@ -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
|
|
@@ -1162,6 +1162,80 @@ def dff_median_filter(f_trace):
|
|
|
1162
1162
|
return (f_trace - f0) / (f0 + 1e-6) # 1e-6 to avoid division by zero
|
|
1163
1163
|
|
|
1164
1164
|
|
|
1165
|
+
def zscore_trace(f_trace, smooth_window: int = None, fs: float = None, tau: float = None):
|
|
1166
|
+
"""
|
|
1167
|
+
Z-score fluorescence traces per ROI: ``(f - mean) / std`` over time.
|
|
1168
|
+
|
|
1169
|
+
One of the ``norm_method`` options for ``norm_traces.npy``. Unlike ΔF/F,
|
|
1170
|
+
output is unitless, centered on 0, and can be negative.
|
|
1171
|
+
|
|
1172
|
+
Parameters
|
|
1173
|
+
----------
|
|
1174
|
+
f_trace : np.ndarray
|
|
1175
|
+
(N_neurons, N_frames) fluorescence traces.
|
|
1176
|
+
smooth_window : int, optional
|
|
1177
|
+
Temporal smoothing window (frames) applied after z-scoring. If None
|
|
1178
|
+
and both ``tau`` and ``fs`` are given, set to ~0.5 * tau * fs;
|
|
1179
|
+
otherwise no smoothing. Set to 0 or 1 to disable.
|
|
1180
|
+
fs : float, optional
|
|
1181
|
+
Frame rate in Hz. Used with ``tau`` to auto-size ``smooth_window``.
|
|
1182
|
+
tau : float, optional
|
|
1183
|
+
Calcium indicator decay time constant in seconds. Used with ``fs``
|
|
1184
|
+
to auto-size ``smooth_window``.
|
|
1185
|
+
|
|
1186
|
+
Returns
|
|
1187
|
+
-------
|
|
1188
|
+
z : np.ndarray
|
|
1189
|
+
(N_neurons, N_frames) z-scored traces.
|
|
1190
|
+
"""
|
|
1191
|
+
from scipy.ndimage import uniform_filter1d
|
|
1192
|
+
|
|
1193
|
+
if not isinstance(f_trace, np.ndarray):
|
|
1194
|
+
raise TypeError("f_trace must be a numpy array")
|
|
1195
|
+
if f_trace.ndim != 2:
|
|
1196
|
+
raise ValueError("f_trace must be a 2D array with shape (N_neurons, N_frames)")
|
|
1197
|
+
if f_trace.shape[0] == 0 or f_trace.shape[1] == 0:
|
|
1198
|
+
raise ValueError("f_trace must not be empty")
|
|
1199
|
+
|
|
1200
|
+
mean = np.mean(f_trace, axis=1, keepdims=True)
|
|
1201
|
+
std = np.std(f_trace, axis=1, keepdims=True)
|
|
1202
|
+
z = (f_trace - mean) / (std + 1e-6) # 1e-6 to avoid division by zero
|
|
1203
|
+
|
|
1204
|
+
if smooth_window is None and tau is not None and fs is not None:
|
|
1205
|
+
smooth_window = max(1, int(0.5 * tau * fs))
|
|
1206
|
+
if smooth_window is not None and smooth_window > 1:
|
|
1207
|
+
z = uniform_filter1d(z, size=smooth_window, axis=1, mode="nearest")
|
|
1208
|
+
|
|
1209
|
+
return z
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
def baseline_percentile_dff(f_corr, percentile: int = 20):
|
|
1213
|
+
"""
|
|
1214
|
+
ΔF/F using a static percentile baseline.
|
|
1215
|
+
|
|
1216
|
+
Used by the SNR, skew, and shot-noise metrics, which always require ΔF/F
|
|
1217
|
+
units regardless of the ``norm_method`` chosen for ``norm_traces.npy``.
|
|
1218
|
+
Kept separate from :func:`dff_rolling_percentile` and :func:`zscore_trace`
|
|
1219
|
+
so the quality metrics never follow the user-selected normalization.
|
|
1220
|
+
|
|
1221
|
+
Parameters
|
|
1222
|
+
----------
|
|
1223
|
+
f_corr : np.ndarray
|
|
1224
|
+
(N_neurons, N_frames) fluorescence, already neuropil-corrected and
|
|
1225
|
+
rectified as needed by the caller.
|
|
1226
|
+
percentile : int, default 20
|
|
1227
|
+
Percentile for the static baseline F0.
|
|
1228
|
+
|
|
1229
|
+
Returns
|
|
1230
|
+
-------
|
|
1231
|
+
dff : np.ndarray
|
|
1232
|
+
(N_neurons, N_frames) ΔF/F traces.
|
|
1233
|
+
"""
|
|
1234
|
+
baseline = np.percentile(f_corr, percentile, axis=1, keepdims=True)
|
|
1235
|
+
baseline = np.maximum(baseline, 1e-6)
|
|
1236
|
+
return (f_corr - baseline) / baseline
|
|
1237
|
+
|
|
1238
|
+
|
|
1165
1239
|
def dff_shot_noise(dff, fr):
|
|
1166
1240
|
"""
|
|
1167
1241
|
Estimate the shot noise level of calcium imaging traces.
|
|
@@ -1279,10 +1353,8 @@ def compute_trace_quality_score(
|
|
|
1279
1353
|
F_corr = F
|
|
1280
1354
|
F_corr = np.maximum(F_corr, 0)
|
|
1281
1355
|
|
|
1282
|
-
#
|
|
1283
|
-
|
|
1284
|
-
baseline = np.maximum(baseline, 1e-6)
|
|
1285
|
-
dff = (F_corr - baseline) / baseline
|
|
1356
|
+
# static-baseline ΔF/F for quality metrics only (see baseline_percentile_dff)
|
|
1357
|
+
dff = baseline_percentile_dff(F_corr)
|
|
1286
1358
|
|
|
1287
1359
|
# SNR
|
|
1288
1360
|
signal = np.std(dff, axis=1)
|
|
@@ -1448,9 +1520,8 @@ def compute_roi_stats(plane_dir, fs=None):
|
|
|
1448
1520
|
# neuropil correction, rectify negatives, and dF/F
|
|
1449
1521
|
F_corr = F - 0.7 * Fneu
|
|
1450
1522
|
F_corr = np.maximum(F_corr, 0)
|
|
1451
|
-
baseline
|
|
1452
|
-
|
|
1453
|
-
dff = (F_corr - baseline) / baseline
|
|
1523
|
+
# static-baseline ΔF/F for quality metrics only (see baseline_percentile_dff)
|
|
1524
|
+
dff = baseline_percentile_dff(F_corr)
|
|
1454
1525
|
|
|
1455
1526
|
# compute metrics
|
|
1456
1527
|
signal = np.std(dff, axis=1)
|