lbm_suite2p_python 2.3.0__tar.gz → 2.3.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.
Files changed (24) hide show
  1. {lbm_suite2p_python-2.3.0/lbm_suite2p_python.egg-info → lbm_suite2p_python-2.3.1}/PKG-INFO +9 -6
  2. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python/__init__.py +19 -0
  3. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python/default_ops.py +1 -2
  4. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python/postprocessing.py +115 -0
  5. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python/run_lsp.py +257 -33
  6. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python/zplane.py +772 -0
  7. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1/lbm_suite2p_python.egg-info}/PKG-INFO +9 -6
  8. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python.egg-info/requires.txt +10 -5
  9. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/pyproject.toml +13 -11
  10. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/LICENSE.md +0 -0
  11. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/MANIFEST.in +0 -0
  12. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/README.md +0 -0
  13. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python/__main__.py +0 -0
  14. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python/_benchmarking.py +0 -0
  15. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python/merging.py +0 -0
  16. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python/utils.py +0 -0
  17. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python/volume.py +0 -0
  18. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python.egg-info/SOURCES.txt +0 -0
  19. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python.egg-info/dependency_links.txt +0 -0
  20. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python.egg-info/entry_points.txt +0 -0
  21. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/lbm_suite2p_python.egg-info/top_level.txt +0 -0
  22. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/setup.cfg +0 -0
  23. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.1}/tests/test_pipeline_parameters.py +0 -0
  24. {lbm_suite2p_python-2.3.0 → lbm_suite2p_python-2.3.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: 2.3.0
3
+ Version: 2.3.1
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,15 +12,18 @@ 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.3.0
15
- Requires-Dist: suite2p_mbo>=2.0.0
15
+ Requires-Dist: fastplotlib
16
+ Provides-Extra: suite2p
17
+ Requires-Dist: suite2p_mbo>=2.0.0; extra == "suite2p"
16
18
  Provides-Extra: rastermap
17
19
  Requires-Dist: rastermap; extra == "rastermap"
18
20
  Provides-Extra: cellpose
19
- Requires-Dist: cellpose==4.0.6; extra == "cellpose"
20
- Requires-Dist: torch>=2.7.0; extra == "cellpose"
21
- Requires-Dist: torchvision>=0.22.0; extra == "cellpose"
21
+ Requires-Dist: cellpose>=4.0.6; extra == "cellpose"
22
+ Provides-Extra: torch
23
+ Requires-Dist: torch>=2.7.0; extra == "torch"
24
+ Requires-Dist: torchvision>=0.22.0; extra == "torch"
22
25
  Provides-Extra: all
23
- Requires-Dist: lbm_suite2p_python[cellpose,rastermap]; extra == "all"
26
+ Requires-Dist: lbm_suite2p_python[cellpose,rastermap,suite2p,torch]; extra == "all"
24
27
  Dynamic: license-file
25
28
 
26
29
  # LBM-Suite2p-Python
@@ -19,6 +19,7 @@ __all__ = [
19
19
  "run_plane",
20
20
  "grid_search",
21
21
  "consolidate_volume",
22
+ "add_processing_step",
22
23
  "plot_traces",
23
24
  "plot_masks",
24
25
  "plot_rastermap",
@@ -36,6 +37,7 @@ __all__ = [
36
37
  "plot_plane_diagnostics",
37
38
  "plot_trace_analysis",
38
39
  "plot_zplane_figures",
40
+ "plot_mask_comparison",
39
41
  "create_volume_summary_table",
40
42
  "dff_rolling_percentile",
41
43
  "dff_median_filter",
@@ -45,4 +47,21 @@ __all__ = [
45
47
  "load_ops",
46
48
  "load_planar_results",
47
49
  "default_ops",
50
+ # Image processing utilities
51
+ "normalize99",
52
+ "apply_hp_filter",
53
+ "random_colors_for_mask",
54
+ "mask_overlay",
55
+ "stat_to_mask",
56
+ # Filtering utilities
57
+ "filter_by_max_diameter",
58
+ "filter_by_diameter",
59
+ "filter_by_area",
60
+ "filter_by_eccentricity",
61
+ "plot_regional_zoom",
62
+ "plot_filtered_cells",
63
+ "plot_diameter_histogram",
48
64
  ]
65
+
66
+ # Re-export with public name
67
+ from lbm_suite2p_python.run_lsp import _add_processing_step as add_processing_step
@@ -130,8 +130,7 @@ def s2p_ops():
130
130
  "cellprob_threshold": -6, # cellprob_threshold for cellpose
131
131
  "flow_threshold": 0, # flow_threshold for cellpose
132
132
  "spatial_hp_cp": 0.5, # high-pass image spatially by a multiple of the diameter
133
- "pretrained_model":
134
- "cpsam", # path to pretrained model or model type string in Cellpose (can be user model)
133
+ "pretrained_model": "cpsam", # cellpose model to use for anatomical detection
135
134
 
136
135
  # classification parameters
137
136
  "soma_crop":
@@ -39,6 +39,121 @@ def filter_by_diameter(iscell, stat, ops, min_mult=0.3, max_mult=3.0):
39
39
  return iscell
40
40
 
41
41
 
42
+ def filter_by_max_diameter(
43
+ iscell,
44
+ stat,
45
+ max_diameter_um: float = None,
46
+ max_diameter_px: float = None,
47
+ pixel_size_um: float = None,
48
+ ops: dict = None,
49
+ ):
50
+ """
51
+ Filter cells by maximum diameter in microns or pixels.
52
+
53
+ Sets iscell=False for ROIs whose diameter exceeds the specified threshold.
54
+ Diameter is computed as 2 * radius from the ellipse fit.
55
+
56
+ Parameters
57
+ ----------
58
+ iscell : np.ndarray
59
+ Cell classification array (n_rois,) or (n_rois, 2).
60
+ stat : np.ndarray or list
61
+ Suite2p stat array with ROI statistics.
62
+ max_diameter_um : float, optional
63
+ Maximum allowed diameter in microns. Requires pixel_size_um or ops
64
+ with pixel resolution info.
65
+ max_diameter_px : float, optional
66
+ Maximum allowed diameter in pixels. Used if max_diameter_um not given.
67
+ pixel_size_um : float, optional
68
+ Pixel size in microns. If None, attempts to read from ops.
69
+ ops : dict, optional
70
+ Suite2p ops dictionary. Used to get pixel resolution if pixel_size_um
71
+ not provided. Looks for 'umPerPix' or 'diameter' keys.
72
+
73
+ Returns
74
+ -------
75
+ iscell_filtered : np.ndarray
76
+ Updated iscell array with large ROIs marked as rejected.
77
+ removed_mask : np.ndarray
78
+ Boolean mask of ROIs that were removed (True = removed).
79
+ diameters_px : np.ndarray
80
+ Diameter of each ROI in pixels.
81
+ threshold_px : float
82
+ The threshold used in pixels.
83
+
84
+ Examples
85
+ --------
86
+ >>> # Filter by max 22 microns diameter
87
+ >>> iscell_filtered, removed, diameters, thresh = filter_by_max_diameter(
88
+ ... iscell, stat, max_diameter_um=22, ops=ops
89
+ ... )
90
+
91
+ >>> # Filter by max 15 pixels diameter
92
+ >>> iscell_filtered, removed, diameters, thresh = filter_by_max_diameter(
93
+ ... iscell, stat, max_diameter_px=15
94
+ ... )
95
+
96
+ >>> # Plot the removed cells
97
+ >>> from lbm_suite2p_python import plot_filtered_cells
98
+ >>> fig = plot_filtered_cells(stat, iscell_filtered, removed, ops=ops)
99
+ """
100
+ iscell = _normalize_iscell(iscell)
101
+
102
+ if max_diameter_um is None and max_diameter_px is None:
103
+ raise ValueError("Must specify either max_diameter_um or max_diameter_px")
104
+
105
+ # Get radii from stat
106
+ if "radius" not in stat[0]:
107
+ # Compute radius if not present
108
+ radii = []
109
+ for s in stat:
110
+ npix = len(s["xpix"])
111
+ # Approximate radius from area: r = sqrt(npix / pi)
112
+ radii.append(np.sqrt(npix / np.pi))
113
+ radii = np.array(radii)
114
+ else:
115
+ radii = np.array([s["radius"] for s in stat])
116
+
117
+ diameters_px = 2 * radii
118
+
119
+ # Convert to pixels if given in microns
120
+ if max_diameter_um is not None:
121
+ # Get pixel size
122
+ if pixel_size_um is None and ops is not None:
123
+ # Try various keys for pixel resolution
124
+ if "umPerPix" in ops:
125
+ pixel_size_um = ops["umPerPix"]
126
+ elif "um_per_pixel" in ops:
127
+ pixel_size_um = ops["um_per_pixel"]
128
+ elif "pixel_resolution" in ops and ops["pixel_resolution"]:
129
+ pr = ops["pixel_resolution"]
130
+ if isinstance(pr, (list, tuple)) and len(pr) >= 2:
131
+ pixel_size_um = np.mean([pr[0], pr[1]])
132
+ else:
133
+ pixel_size_um = float(pr)
134
+ elif "umPerPixX" in ops and "umPerPixY" in ops:
135
+ pixel_size_um = np.mean([ops["umPerPixX"], ops["umPerPixY"]])
136
+
137
+ if pixel_size_um is None:
138
+ raise ValueError(
139
+ "Cannot convert microns to pixels: pixel_size_um not provided "
140
+ "and could not be found in ops. Use max_diameter_px instead."
141
+ )
142
+
143
+ max_diameter_px = max_diameter_um / pixel_size_um
144
+
145
+ # Apply filter
146
+ valid = diameters_px <= max_diameter_px
147
+ removed_mask = ~valid & iscell # ROIs that were cells but got filtered out
148
+ n_rejected = removed_mask.sum()
149
+
150
+ if n_rejected > 0:
151
+ print(f"Filtered {n_rejected} ROIs with diameter > {max_diameter_px:.1f} px")
152
+
153
+ iscell_filtered = iscell & valid
154
+ return iscell_filtered, removed_mask, diameters_px, max_diameter_px
155
+
156
+
42
157
  def filter_by_area(iscell, stat, min_mult=0.25, max_mult=4.0):
43
158
  """
44
159
  Filter cells by total area (in pixels).