lbm_suite2p_python 2.0.3__tar.gz → 2.0.5__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-2.0.3/lbm_suite2p_python.egg-info → lbm_suite2p_python-2.0.5}/PKG-INFO +1 -11
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python/run_lsp.py +138 -95
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5/lbm_suite2p_python.egg-info}/PKG-INFO +1 -11
- lbm_suite2p_python-2.0.5/lbm_suite2p_python.egg-info/requires.txt +1 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/pyproject.toml +15 -46
- lbm_suite2p_python-2.0.3/lbm_suite2p_python.egg-info/requires.txt +0 -14
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/LICENSE.md +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/MANIFEST.in +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/README.md +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python/__init__.py +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python/__main__.py +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python/_benchmarking.py +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python/default_ops.py +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python/merging.py +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python/postprocessing.py +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python/utils.py +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python/volume.py +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python/zplane.py +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python.egg-info/SOURCES.txt +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python.egg-info/dependency_links.txt +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python.egg-info/entry_points.txt +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python.egg-info/top_level.txt +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/setup.cfg +0 -0
- {lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/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: 2.0.
|
|
3
|
+
Version: 2.0.5
|
|
4
4
|
Summary: Light Beads Microscopy Pipeline using Suite2p
|
|
5
5
|
License-Expression: BSD-3-Clause
|
|
6
6
|
Project-URL: homepage, https://github.com/MillerBrainObservatory/LBM-Suite2p-Python
|
|
@@ -12,16 +12,6 @@ Requires-Python: <3.12.10,>=3.12.7
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE.md
|
|
14
14
|
Requires-Dist: mbo_utilities>=2.0.4
|
|
15
|
-
Provides-Extra: cpsam
|
|
16
|
-
Requires-Dist: cellpose==4.0.6; extra == "cpsam"
|
|
17
|
-
Requires-Dist: pytorch; extra == "cpsam"
|
|
18
|
-
Requires-Dist: torchaudio; extra == "cpsam"
|
|
19
|
-
Provides-Extra: cpu
|
|
20
|
-
Requires-Dist: torch>=2.7.0; extra == "cpu"
|
|
21
|
-
Requires-Dist: torchvision>=0.22.0; extra == "cpu"
|
|
22
|
-
Provides-Extra: cu126
|
|
23
|
-
Requires-Dist: torch>=2.7.0; extra == "cu126"
|
|
24
|
-
Requires-Dist: torchvision>=0.22.0; extra == "cu126"
|
|
25
15
|
Dynamic: license-file
|
|
26
16
|
|
|
27
17
|
# Light Beads Microscopy (LBM) Pipeline: Suite2p
|
|
@@ -18,10 +18,7 @@ from lbm_suite2p_python.postprocessing import (
|
|
|
18
18
|
)
|
|
19
19
|
from mbo_utilities.log import get as get_logger
|
|
20
20
|
|
|
21
|
-
from lbm_suite2p_python.zplane import
|
|
22
|
-
save_pc_panels_and_metrics,
|
|
23
|
-
plot_zplane_figures
|
|
24
|
-
)
|
|
21
|
+
from lbm_suite2p_python.zplane import save_pc_panels_and_metrics, plot_zplane_figures
|
|
25
22
|
|
|
26
23
|
logger = get_logger("run_lsp")
|
|
27
24
|
|
|
@@ -31,7 +28,9 @@ from lbm_suite2p_python.volume import (
|
|
|
31
28
|
plot_volume_neuron_counts,
|
|
32
29
|
get_volume_stats,
|
|
33
30
|
)
|
|
34
|
-
from mbo_utilities.file_io import
|
|
31
|
+
from mbo_utilities.file_io import (
|
|
32
|
+
get_plane_from_filename,
|
|
33
|
+
) # derive_tag_from_filename, PIPELINE_TAGS
|
|
35
34
|
|
|
36
35
|
PIPELINE_TAGS = ("plane", "roi", "z", "plane_", "roi_", "z_")
|
|
37
36
|
|
|
@@ -160,6 +159,7 @@ def run_volume(
|
|
|
160
159
|
- Optional rastermap clustering results
|
|
161
160
|
"""
|
|
162
161
|
from mbo_utilities.file_io import get_files, get_plane_from_filename
|
|
162
|
+
|
|
163
163
|
start = time.time()
|
|
164
164
|
if save_path is None:
|
|
165
165
|
save_path = Path(input_files[0]).parent
|
|
@@ -204,6 +204,7 @@ def run_volume(
|
|
|
204
204
|
if "roi" in Path(input_files[0]).stem.lower():
|
|
205
205
|
print("Detected mROI data, merging ROIs for each z-plane...")
|
|
206
206
|
from .merging import merge_mrois
|
|
207
|
+
|
|
207
208
|
merged_savepath = save_path.joinpath("merged_mrois")
|
|
208
209
|
merge_mrois(save_path, merged_savepath)
|
|
209
210
|
save_path = merged_savepath
|
|
@@ -269,57 +270,54 @@ def run_volume(
|
|
|
269
270
|
return all_ops
|
|
270
271
|
|
|
271
272
|
|
|
272
|
-
def _should_write_bin(ops_path: Path, force: bool = False) -> bool:
|
|
273
|
-
"""
|
|
274
|
-
Decide whether raw binary export should be performed before registration.
|
|
275
|
-
|
|
276
|
-
Conditions that trigger re-write:
|
|
277
|
-
- force=True
|
|
278
|
-
- bin file missing (data.bin or data_chan2.bin)
|
|
279
|
-
- ops.npy missing
|
|
280
|
-
- mismatch between ops metadata (Ly, Lx, nframes) and bin file size
|
|
281
|
-
- bin file unreadable or truncated
|
|
282
|
-
"""
|
|
273
|
+
def _should_write_bin(ops_path: Path, force: bool = False, *, validate_chan2: bool | None = None, expected_dtype: np.dtype = np.int16) -> bool:
|
|
283
274
|
if force:
|
|
284
275
|
return True
|
|
285
276
|
ops_path = Path(ops_path)
|
|
286
277
|
if not ops_path.is_file():
|
|
287
278
|
return True
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if not
|
|
279
|
+
raw_path = ops_path.parent / "data_raw.bin"
|
|
280
|
+
chan2_path = ops_path.parent / "data_chan2.bin"
|
|
281
|
+
if not raw_path.is_file():
|
|
282
|
+
return True
|
|
283
|
+
try:
|
|
284
|
+
ops = np.load(ops_path, allow_pickle=True).item()
|
|
285
|
+
if validate_chan2 is None:
|
|
286
|
+
validate_chan2 = (ops.get("align_by_chan", 1) == 2)
|
|
287
|
+
Ly = ops.get("Ly")
|
|
288
|
+
Lx = ops.get("Lx")
|
|
289
|
+
nframes_raw = ops.get("nframes_chan1") or ops.get("nframes") or ops.get("num_frames")
|
|
290
|
+
if (not raw_path.is_file()) or (None in (nframes_raw, Ly, Lx)) or (nframes_raw <= 0 or Ly <= 0 or Lx <= 0):
|
|
291
|
+
return True
|
|
292
|
+
expected_size_raw = int(nframes_raw) * int(Ly) * int(Lx) * np.dtype(expected_dtype).itemsize
|
|
293
|
+
actual_size_raw = raw_path.stat().st_size
|
|
294
|
+
if actual_size_raw != expected_size_raw or actual_size_raw == 0:
|
|
300
295
|
return True
|
|
301
296
|
try:
|
|
302
|
-
|
|
303
|
-
nframes = (
|
|
304
|
-
ops.get("nframes_chan2")
|
|
305
|
-
if "chan2" in bin_path.name
|
|
306
|
-
else ops.get("nframes_chan1", ops.get("nframes"))
|
|
307
|
-
)
|
|
308
|
-
if None in (Ly, Lx, nframes):
|
|
309
|
-
return True
|
|
310
|
-
|
|
311
|
-
expected_size = nframes * Ly * Lx * np.dtype(np.int16).itemsize
|
|
312
|
-
actual_size = bin_path.stat().st_size
|
|
313
|
-
if actual_size != expected_size:
|
|
314
|
-
return True
|
|
315
|
-
|
|
316
|
-
arr = np.memmap(bin_path, dtype=np.int16, mode="r", shape=(nframes, Ly, Lx))
|
|
297
|
+
arr = np.memmap(raw_path, dtype=expected_dtype, mode="r", shape=(int(nframes_raw), int(Ly), int(Lx)))
|
|
317
298
|
_ = arr[0, 0, 0]
|
|
318
299
|
del arr
|
|
319
|
-
except Exception
|
|
320
|
-
print(f"Bin validation failed for {bin_path}: {e}")
|
|
300
|
+
except Exception:
|
|
321
301
|
return True
|
|
322
|
-
|
|
302
|
+
if validate_chan2:
|
|
303
|
+
nframes_chan2 = ops.get("nframes_chan2")
|
|
304
|
+
if (not chan2_path.is_file()) or (nframes_chan2 is None) or (nframes_chan2 <= 0):
|
|
305
|
+
return True
|
|
306
|
+
expected_size_chan2 = int(nframes_chan2) * int(Ly) * int(Lx) * np.dtype(expected_dtype).itemsize
|
|
307
|
+
actual_size_chan2 = chan2_path.stat().st_size
|
|
308
|
+
if actual_size_chan2 != expected_size_chan2 or actual_size_chan2 == 0:
|
|
309
|
+
return True
|
|
310
|
+
try:
|
|
311
|
+
arr2 = np.memmap(chan2_path, dtype=expected_dtype, mode="r", shape=(int(nframes_chan2), int(Ly), int(Lx)))
|
|
312
|
+
_ = arr2[0, 0, 0]
|
|
313
|
+
del arr2
|
|
314
|
+
except Exception:
|
|
315
|
+
return True
|
|
316
|
+
return False
|
|
317
|
+
except Exception as e:
|
|
318
|
+
print(f"Bin validation failed for {ops_path.parent}: {e}")
|
|
319
|
+
return True
|
|
320
|
+
|
|
323
321
|
|
|
324
322
|
def _should_register(ops_path: str | Path) -> bool:
|
|
325
323
|
"""
|
|
@@ -336,9 +334,8 @@ def _should_register(ops_path: str | Path) -> bool:
|
|
|
336
334
|
|
|
337
335
|
has_ref = isinstance(ops.get("refImg"), np.ndarray)
|
|
338
336
|
has_mean = isinstance(ops.get("meanImg"), np.ndarray)
|
|
339
|
-
has_offsets = (
|
|
340
|
-
|
|
341
|
-
("yoff" in ops and np.any(np.isfinite(ops["yoff"])))
|
|
337
|
+
has_offsets = ("xoff" in ops and np.any(np.isfinite(ops["xoff"]))) or (
|
|
338
|
+
"yoff" in ops and np.any(np.isfinite(ops["yoff"]))
|
|
342
339
|
)
|
|
343
340
|
has_metrics = any(k in ops for k in ("regDX", "regPC", "regPC1", "regDX1"))
|
|
344
341
|
|
|
@@ -348,55 +345,73 @@ def _should_register(ops_path: str | Path) -> bool:
|
|
|
348
345
|
|
|
349
346
|
|
|
350
347
|
def run_plane_bin(ops) -> bool:
|
|
351
|
-
from mbo_utilities._binary import BinaryFile
|
|
352
|
-
from suite2p.run_s2p import pipeline
|
|
353
348
|
from contextlib import nullcontext
|
|
349
|
+
from suite2p.io.binary import BinaryFile
|
|
350
|
+
from suite2p.run_s2p import pipeline
|
|
354
351
|
|
|
355
352
|
ops = load_ops(ops)
|
|
356
|
-
Ly, Lx = ops["Ly"], ops["Lx"]
|
|
353
|
+
Ly, Lx = int(ops["Ly"]), int(ops["Lx"])
|
|
357
354
|
|
|
358
|
-
# input functional channel (unregistered)
|
|
359
355
|
raw_file = ops.get("raw_file")
|
|
360
|
-
|
|
361
|
-
if raw_file is None or
|
|
356
|
+
n_func = ops.get("nframes_chan1") or ops.get("nframes") or ops.get("n_frames")
|
|
357
|
+
if raw_file is None or n_func is None:
|
|
362
358
|
raise KeyError("Missing raw_file or nframes_chan1")
|
|
359
|
+
n_func = int(n_func)
|
|
363
360
|
|
|
364
|
-
|
|
365
|
-
chan2_file = ops.get("chan2_file", "")
|
|
366
|
-
nframes_chan2 = ops.get("nframes_chan2", 0)
|
|
367
|
-
|
|
368
|
-
ops_parent = Path(ops.get("ops_path")).parent
|
|
361
|
+
ops_parent = Path(ops["ops_path"]).parent
|
|
369
362
|
ops["save_path"] = ops_parent
|
|
370
363
|
|
|
371
|
-
align_structural = ops.get("align_by_chan", 1) == 2
|
|
372
|
-
ops["align_structural"] = align_structural
|
|
373
|
-
|
|
374
364
|
reg_file = ops_parent / "data.bin"
|
|
375
365
|
ops["reg_file"] = str(reg_file)
|
|
376
366
|
|
|
377
|
-
|
|
367
|
+
chan2_file = ops.get("chan2_file", "")
|
|
368
|
+
use_chan2 = bool(chan2_file) and Path(chan2_file).exists()
|
|
369
|
+
n_chan2 = int(ops.get("nframes_chan2", 0)) if use_chan2 else 0
|
|
370
|
+
|
|
371
|
+
n_align = n_func if not use_chan2 else min(n_func, n_chan2)
|
|
372
|
+
if n_align <= 0:
|
|
373
|
+
raise ValueError("Non-positive frame count after alignment selection.")
|
|
374
|
+
if use_chan2 and (n_func != n_chan2):
|
|
375
|
+
print(f"[run_plane_bin] Trimming to {n_align} frames (func={n_func}, chan2={n_chan2}).")
|
|
376
|
+
|
|
377
|
+
ops["functional_chan"] = 1
|
|
378
|
+
ops["align_by_chan"] = 2 if use_chan2 else 1
|
|
379
|
+
ops["nchannels"] = 2 if use_chan2 else 1
|
|
380
|
+
ops["nframes"] = n_align
|
|
381
|
+
ops["nframes_chan1"] = n_align
|
|
382
|
+
if use_chan2:
|
|
383
|
+
ops["nframes_chan2"] = n_align
|
|
384
|
+
|
|
378
385
|
if "diameter" in ops:
|
|
379
386
|
if ops["diameter"] is not None and np.isnan(ops["diameter"]):
|
|
380
387
|
ops["diameter"] = 8
|
|
381
|
-
if (ops["diameter"]
|
|
388
|
+
if (ops["diameter"] in (None, 0)) and ops.get("anatomical_only", 0) > 0:
|
|
382
389
|
ops["diameter"] = 8
|
|
383
390
|
print("Warning: diameter was not set, defaulting to 8.")
|
|
384
391
|
|
|
392
|
+
reg_file_chan2 = ops_parent / "data_chan2_reg.bin" if use_chan2 else None
|
|
393
|
+
|
|
394
|
+
ops["anatomical_red"] = False
|
|
395
|
+
ops["chan2_thres"] = 0.1
|
|
396
|
+
|
|
385
397
|
with (
|
|
386
|
-
BinaryFile(Ly=Ly, Lx=Lx, filename=str(reg_file), n_frames=
|
|
387
|
-
BinaryFile(Ly=Ly, Lx=Lx, filename=raw_file, n_frames=
|
|
388
|
-
(BinaryFile(Ly=Ly, Lx=Lx, filename=
|
|
389
|
-
if
|
|
398
|
+
BinaryFile(Ly=Ly, Lx=Lx, filename=str(reg_file), n_frames=n_align) as f_reg,
|
|
399
|
+
BinaryFile(Ly=Ly, Lx=Lx, filename=str(raw_file), n_frames=n_align) as f_raw,
|
|
400
|
+
(BinaryFile(Ly=Ly, Lx=Lx, filename=str(reg_file_chan2), n_frames=n_align) if use_chan2 else nullcontext()) as f_reg_chan2,
|
|
401
|
+
(BinaryFile(Ly=Ly, Lx=Lx, filename=str(chan2_file), n_frames=n_align) if use_chan2 else nullcontext()) as f_raw_chan2,
|
|
390
402
|
):
|
|
391
403
|
ops = pipeline(
|
|
392
404
|
f_reg=f_reg,
|
|
393
405
|
f_raw=f_raw,
|
|
394
|
-
f_reg_chan2=f_reg_chan2,
|
|
395
|
-
f_raw_chan2=
|
|
396
|
-
run_registration=ops.get("do_registration", True),
|
|
406
|
+
f_reg_chan2=f_reg_chan2 if use_chan2 else None,
|
|
407
|
+
f_raw_chan2=f_raw_chan2 if use_chan2 else None,
|
|
408
|
+
run_registration=bool(ops.get("do_registration", True)),
|
|
397
409
|
ops=ops,
|
|
398
410
|
stat=None,
|
|
399
411
|
)
|
|
412
|
+
|
|
413
|
+
if use_chan2:
|
|
414
|
+
ops["reg_file_chan2"] = str(reg_file_chan2)
|
|
400
415
|
np.save(ops["ops_path"], ops)
|
|
401
416
|
return True
|
|
402
417
|
|
|
@@ -405,6 +420,7 @@ def run_plane(
|
|
|
405
420
|
input_path: str | Path,
|
|
406
421
|
save_path: str | Path | None = None,
|
|
407
422
|
ops: dict | str | Path = None,
|
|
423
|
+
chan2_file: str | Path | None = None,
|
|
408
424
|
keep_raw: bool = False,
|
|
409
425
|
keep_reg: bool = True,
|
|
410
426
|
force_reg: bool = False,
|
|
@@ -426,6 +442,8 @@ def run_plane(
|
|
|
426
442
|
Directory to save the results.
|
|
427
443
|
ops : dict, str or Path, optional
|
|
428
444
|
Path to or dict of user‐supplied ops.npy. If given, it overrides any existing or generated ops.
|
|
445
|
+
chan2_file : str, optional
|
|
446
|
+
Path to structural / anatomical data used for registration.
|
|
429
447
|
keep_raw : bool, default false
|
|
430
448
|
if true, do not delete the raw binary (`data_raw.bin`) after processing.
|
|
431
449
|
keep_reg : bool, default false
|
|
@@ -484,18 +502,15 @@ def run_plane(
|
|
|
484
502
|
logger.setLevel(logging.DEBUG)
|
|
485
503
|
logger.info("Debug mode enabled.")
|
|
486
504
|
|
|
487
|
-
assert isinstance(
|
|
488
|
-
|
|
489
|
-
)
|
|
505
|
+
assert isinstance(
|
|
506
|
+
input_path, (Path, str)
|
|
507
|
+
), f"input_path should be a pathlib.Path or string, not: {type(input_path)}"
|
|
490
508
|
input_path = Path(input_path)
|
|
491
|
-
if not input_path.is_file():
|
|
492
|
-
if input_path.suffix != ".zarr":
|
|
493
|
-
raise ValueError(f"Input file does not exist: {input_path}")
|
|
494
509
|
input_parent = input_path.parent
|
|
495
510
|
|
|
496
|
-
assert isinstance(
|
|
497
|
-
|
|
498
|
-
)
|
|
511
|
+
assert isinstance(
|
|
512
|
+
save_path, (Path, str, type(None))
|
|
513
|
+
), f"save_path should be a pathlib.Path or string, not: {type(save_path)}"
|
|
499
514
|
if save_path is None:
|
|
500
515
|
logger.debug(f"save_path is None, using parent of input file: {input_parent}")
|
|
501
516
|
save_path = input_parent
|
|
@@ -512,8 +527,11 @@ def run_plane(
|
|
|
512
527
|
ops = {**ops_default, **ops_user, "data_path": str(input_path.resolve())}
|
|
513
528
|
|
|
514
529
|
# suite2p diameter handling
|
|
515
|
-
if
|
|
516
|
-
|
|
530
|
+
if (
|
|
531
|
+
isinstance(ops["diameter"], list)
|
|
532
|
+
and len(ops["diameter"]) > 1
|
|
533
|
+
and ops["aspect"] == 1.0
|
|
534
|
+
):
|
|
517
535
|
ops["aspect"] = ops["diameter"][0] / ops["diameter"][1] # noqa
|
|
518
536
|
|
|
519
537
|
file = imread(input_path)
|
|
@@ -562,7 +580,8 @@ def run_plane(
|
|
|
562
580
|
else:
|
|
563
581
|
print(
|
|
564
582
|
f"ops['roidetect'] is True with no stat.npy file present, "
|
|
565
|
-
f"proceeding with segmentation/detection for plane {plane}."
|
|
583
|
+
f"proceeding with segmentation/detection for plane {plane}."
|
|
584
|
+
)
|
|
566
585
|
needs_detect = True
|
|
567
586
|
elif (plane_dir / "stat.npy").is_file():
|
|
568
587
|
# check contents of stat.npy
|
|
@@ -576,7 +595,7 @@ def run_plane(
|
|
|
576
595
|
|
|
577
596
|
ops_file = plane_dir / "ops.npy"
|
|
578
597
|
|
|
579
|
-
if _should_write_bin(ops_file, force=
|
|
598
|
+
if _should_write_bin(ops_file, force=force_reg):
|
|
580
599
|
md_combined = {**metadata, **ops}
|
|
581
600
|
imwrite(file, plane_dir, ext=".bin", metadata=md_combined, register_z=False)
|
|
582
601
|
else:
|
|
@@ -609,6 +628,23 @@ def run_plane(
|
|
|
609
628
|
"reg_file": str((plane_dir / "data.bin").resolve()),
|
|
610
629
|
}
|
|
611
630
|
|
|
631
|
+
# optional structural (channel 2) input
|
|
632
|
+
if chan2_file is not None:
|
|
633
|
+
chan2_file = Path(chan2_file)
|
|
634
|
+
if not chan2_file.exists():
|
|
635
|
+
raise FileNotFoundError(f"chan2_path not found: {chan2_file}")
|
|
636
|
+
|
|
637
|
+
chan2_data = imread(chan2_file)
|
|
638
|
+
chan2_md = chan2_data.metadata if hasattr(chan2_data, "metadata") else {}
|
|
639
|
+
chan2_frames = chan2_md.get("num_frames") or chan2_md.get("nframes") or chan2_data.shape[0]
|
|
640
|
+
|
|
641
|
+
# write channel 2 binary automatically
|
|
642
|
+
imwrite(chan2_data, plane_dir, ext=".bin", metadata=chan2_md, register_z=False, structural=True)
|
|
643
|
+
ops["chan2_file"] = str((plane_dir / "data_chan2.bin").resolve())
|
|
644
|
+
ops["nframes_chan2"] = int(chan2_frames)
|
|
645
|
+
ops["nchannels"] = 2
|
|
646
|
+
ops["align_by_chan"] = 2
|
|
647
|
+
|
|
612
648
|
if "nframes" not in ops:
|
|
613
649
|
if "metadata" in ops and "shape" in ops["metadata"]:
|
|
614
650
|
ops["nframes"] = ops["metadata"]["shape"][0]
|
|
@@ -625,23 +661,30 @@ def run_plane(
|
|
|
625
661
|
"missing frame count (nframes) in ops or metadata, and cannot infer from data"
|
|
626
662
|
)
|
|
627
663
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
664
|
+
try:
|
|
665
|
+
processed = run_plane_bin(ops)
|
|
666
|
+
except Exception as e:
|
|
667
|
+
print(e)
|
|
668
|
+
processed = False
|
|
633
669
|
|
|
634
670
|
if not processed:
|
|
635
671
|
print(f"Skipping {ops_file.name}, processing was not completed.")
|
|
636
672
|
return ops_file
|
|
637
673
|
|
|
674
|
+
if save_json:
|
|
675
|
+
# convert ops dict to JSON serializable and save as ops.json
|
|
676
|
+
ops_to_json(ops_file)
|
|
677
|
+
|
|
638
678
|
raw_file = Path(ops.get("raw_file", plane_dir / "data_raw.bin"))
|
|
639
679
|
reg_file = Path(ops.get("reg_file", plane_dir / "data.bin"))
|
|
640
680
|
|
|
641
|
-
|
|
642
|
-
raw_file.
|
|
643
|
-
|
|
644
|
-
reg_file.
|
|
681
|
+
try:
|
|
682
|
+
if not keep_raw and raw_file.exists():
|
|
683
|
+
raw_file.unlink(missing_ok=True)
|
|
684
|
+
if not keep_reg and reg_file.exists():
|
|
685
|
+
reg_file.unlink(missing_ok=True)
|
|
686
|
+
except Exception as e:
|
|
687
|
+
print(e)
|
|
645
688
|
|
|
646
689
|
save_pc_panels_and_metrics(ops_file, plane_dir / "pc_metrics")
|
|
647
690
|
|
|
@@ -760,4 +803,4 @@ def run_grid_search(
|
|
|
760
803
|
keep_raw=True,
|
|
761
804
|
force_reg=force_reg,
|
|
762
805
|
force_detect=force_detect,
|
|
763
|
-
)
|
|
806
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lbm_suite2p_python
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.5
|
|
4
4
|
Summary: Light Beads Microscopy Pipeline using Suite2p
|
|
5
5
|
License-Expression: BSD-3-Clause
|
|
6
6
|
Project-URL: homepage, https://github.com/MillerBrainObservatory/LBM-Suite2p-Python
|
|
@@ -12,16 +12,6 @@ Requires-Python: <3.12.10,>=3.12.7
|
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE.md
|
|
14
14
|
Requires-Dist: mbo_utilities>=2.0.4
|
|
15
|
-
Provides-Extra: cpsam
|
|
16
|
-
Requires-Dist: cellpose==4.0.6; extra == "cpsam"
|
|
17
|
-
Requires-Dist: pytorch; extra == "cpsam"
|
|
18
|
-
Requires-Dist: torchaudio; extra == "cpsam"
|
|
19
|
-
Provides-Extra: cpu
|
|
20
|
-
Requires-Dist: torch>=2.7.0; extra == "cpu"
|
|
21
|
-
Requires-Dist: torchvision>=0.22.0; extra == "cpu"
|
|
22
|
-
Provides-Extra: cu126
|
|
23
|
-
Requires-Dist: torch>=2.7.0; extra == "cu126"
|
|
24
|
-
Requires-Dist: torchvision>=0.22.0; extra == "cu126"
|
|
25
15
|
Dynamic: license-file
|
|
26
16
|
|
|
27
17
|
# Light Beads Microscopy (LBM) Pipeline: Suite2p
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mbo_utilities>=2.0.4
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "lbm_suite2p_python"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.5"
|
|
8
8
|
description = "Light Beads Microscopy Pipeline using Suite2p"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
@@ -21,51 +21,6 @@ dependencies = [
|
|
|
21
21
|
"mbo_utilities>=2.0.4",
|
|
22
22
|
]
|
|
23
23
|
|
|
24
|
-
[project.optional-dependencies]
|
|
25
|
-
cpsam = ["cellpose==4.0.6", "pytorch", "torchaudio"]
|
|
26
|
-
cpu = [
|
|
27
|
-
"torch>=2.7.0",
|
|
28
|
-
"torchvision>=0.22.0",
|
|
29
|
-
]
|
|
30
|
-
cu126 = [
|
|
31
|
-
"torch>=2.7.0",
|
|
32
|
-
"torchvision>=0.22.0",
|
|
33
|
-
]
|
|
34
|
-
|
|
35
|
-
[tool.uv.sources]
|
|
36
|
-
torch = [
|
|
37
|
-
{ index = "pytorch-cu126", extra = "cu126" }, # default
|
|
38
|
-
{ index = "pytorch-cpu", extra = "cpu" },
|
|
39
|
-
]
|
|
40
|
-
torchvision = [
|
|
41
|
-
{ index = "pytorch-cu126", extra = "cu126" }, # default
|
|
42
|
-
{ index = "pytorch-cpu", extra = "cpu" },
|
|
43
|
-
]
|
|
44
|
-
|
|
45
|
-
[[tool.uv.index]]
|
|
46
|
-
name = "pytorch-cpu"
|
|
47
|
-
url = "https://download.pytorch.org/whl/cpu"
|
|
48
|
-
explicit = true
|
|
49
|
-
|
|
50
|
-
[[tool.uv.index]]
|
|
51
|
-
name = "pytorch-cu126"
|
|
52
|
-
url = "https://download.pytorch.org/whl/cu126"
|
|
53
|
-
explicit = true
|
|
54
|
-
|
|
55
|
-
[[tool.uv.index]]
|
|
56
|
-
name = "pytorch-cu128"
|
|
57
|
-
url = "https://download.pytorch.org/whl/cu128"
|
|
58
|
-
explicit = true
|
|
59
|
-
|
|
60
|
-
[tool.uv]
|
|
61
|
-
default-groups = ["all"]
|
|
62
|
-
conflicts = [
|
|
63
|
-
[
|
|
64
|
-
{ extra = "cpu" },
|
|
65
|
-
{ extra = "cu126" },
|
|
66
|
-
],
|
|
67
|
-
]
|
|
68
|
-
|
|
69
24
|
[dependency-groups]
|
|
70
25
|
all = [
|
|
71
26
|
"rastermap",
|
|
@@ -95,6 +50,20 @@ docs = [
|
|
|
95
50
|
"suite2p_mbo",
|
|
96
51
|
]
|
|
97
52
|
|
|
53
|
+
[[tool.uv.index]]
|
|
54
|
+
name = "pytorch-cu126"
|
|
55
|
+
url = "https://download.pytorch.org/whl/cu126"
|
|
56
|
+
explicit = true
|
|
57
|
+
|
|
58
|
+
[tool.uv.sources]
|
|
59
|
+
torch = [
|
|
60
|
+
{ index = "pytorch-cu126", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
|
61
|
+
]
|
|
62
|
+
torchvision = [
|
|
63
|
+
{ index = "pytorch-cu126", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
|
|
98
67
|
# https://github.com/charliermarsh/ruff
|
|
99
68
|
[tool.ruff]
|
|
100
69
|
line-length = 88
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{lbm_suite2p_python-2.0.3 → lbm_suite2p_python-2.0.5}/lbm_suite2p_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|