napari-piscis 0.1.20__py3-none-any.whl

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.

Potentially problematic release.


This version of napari-piscis might be problematic. Click here for more details.

@@ -0,0 +1 @@
1
+ from ._version import __version__
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.1.20'
32
+ __version_tuple__ = version_tuple = (0, 1, 20)
33
+
34
+ __commit_id__ = commit_id = None
@@ -0,0 +1,261 @@
1
+ from __future__ import annotations
2
+
3
+ import warnings
4
+ from typing import TYPE_CHECKING, Tuple, List, Union, Dict, Any, Optional
5
+
6
+ import numpy as np
7
+ import napari
8
+ from napari.qt.threading import thread_worker
9
+ from napari.utils.notifications import show_info, show_warning, show_error
10
+ from magicgui import magic_factory
11
+ from loguru import logger
12
+
13
+ try:
14
+ from skimage.color import rgb2gray
15
+ from piscis import Piscis
16
+ from piscis.utils import pad_and_stack
17
+ DEPENDENCIES_INSTALLED = True
18
+ except ImportError as e:
19
+ DEPENDENCIES_INSTALLED = False
20
+ Piscis = None
21
+ pad_and_stack = None
22
+ rgb2gray = None
23
+
24
+ if TYPE_CHECKING:
25
+ import napari.layers
26
+ import napari.types
27
+
28
+
29
+ # --- 1. Pure Scientific Logic ---
30
+
31
+ def infer_img_axes(shape: tuple) -> str:
32
+ """Infers axes (e.g. 'yx', 'zyx') from shape."""
33
+ if len(shape) == 2:
34
+ return 'yx'
35
+ elif len(shape) == 3:
36
+ if shape[-1] in (3, 4):
37
+ return 'yxc'
38
+ min_dim_idx = shape.index(min(shape))
39
+ low_dim_shape = list(shape)
40
+ low_dim_shape.pop(min_dim_idx)
41
+ low_dim_axes = infer_img_axes(tuple(low_dim_shape))
42
+ return low_dim_axes[:min_dim_idx] + 'z' + low_dim_axes[min_dim_idx:]
43
+ else:
44
+ raise ValueError(f"Image shape {shape} is not supported.")
45
+
46
+ def run_inference_logic(
47
+ raw_image: np.ndarray,
48
+ model_name: str,
49
+ threshold: float,
50
+ min_distance: int,
51
+ intermediates: bool
52
+ ) -> Dict[str, Any]:
53
+ """Pure python function to run PISCIS inference."""
54
+ if not DEPENDENCIES_INSTALLED:
55
+ raise ImportError("PISCIS or skimage not installed.")
56
+
57
+ # 1. Analyze Input
58
+ axes = infer_img_axes(raw_image.shape)
59
+ logger.info(f"Input Shape {raw_image.shape}, Inferred axes {axes}")
60
+
61
+ is_3d_stack = 'z' in axes
62
+
63
+ # 2. Initialize Model
64
+ model = Piscis(model_name=model_name)
65
+
66
+ # 3. Preprocess
67
+ images_batch = None
68
+ processed_image_for_display = None
69
+ was_color_converted = False
70
+
71
+ if is_3d_stack:
72
+ if axes == 'zyx':
73
+ images_batch = raw_image
74
+ elif axes == 'yxz':
75
+ images_batch = np.moveaxis(raw_image, -1, 0)
76
+ else:
77
+ raise ValueError(f"Unsupported 3D axis pattern: {axes}")
78
+ else:
79
+ if axes == 'yx':
80
+ images_batch = np.expand_dims(raw_image, axis=0)
81
+ elif axes == 'yxc':
82
+ gray_img = rgb2gray(raw_image)
83
+ images_batch = gray_img
84
+ processed_image_for_display = gray_img
85
+ was_color_converted = True
86
+ else:
87
+ raise ValueError(f"Unsupported 2D axis pattern: {axes}")
88
+
89
+ # 4. Pad and Stack
90
+ images_padded = pad_and_stack(images_batch)
91
+
92
+ # 5. Predict
93
+ logger.info("PISCIS: Predicting...")
94
+ coords_pred, features = model.predict(
95
+ images_padded,
96
+ threshold=threshold,
97
+ intermediates=intermediates,
98
+ min_distance=min_distance,
99
+ stack=is_3d_stack
100
+ )
101
+
102
+ return {
103
+ 'coords': coords_pred,
104
+ 'features': features,
105
+ 'is_3d_stack': is_3d_stack,
106
+ 'padded_shape': images_padded.shape,
107
+ 'processed_image': processed_image_for_display,
108
+ 'was_color_converted': was_color_converted
109
+ }
110
+
111
+
112
+ # --- 2. Worker Thread ---
113
+
114
+ @thread_worker
115
+ def piscis_worker(
116
+ raw_image: np.ndarray,
117
+ model_name: str,
118
+ threshold: float,
119
+ min_distance: int,
120
+ intermediates: bool
121
+ ):
122
+ try:
123
+ return run_inference_logic(
124
+ raw_image, model_name, threshold, min_distance, intermediates
125
+ )
126
+ except Exception as e:
127
+ raise e
128
+
129
+
130
+ # --- 3. Visualization Helper ---
131
+
132
+ def _display_features(viewer: napari.Viewer, features: Any, is_3d_stack: bool, layer_name: str):
133
+ """Parses PISCIS feature maps and adds them to the Napari viewer."""
134
+ features_np = np.array(features)
135
+
136
+ try:
137
+ if is_3d_stack:
138
+ # Handle 3D Stacks
139
+ if features_np.ndim == 5:
140
+ feats = features_np[0]
141
+ elif features_np.ndim == 4:
142
+ feats = features_np
143
+ else:
144
+ raise ValueError(f"Unexpected 3D shape: {features_np.shape}")
145
+
146
+ if feats.shape[0] >= 2:
147
+ disp_y = features_np[:, 0, :, :]
148
+ disp_x = features_np[:, 1, :, :]
149
+
150
+ viewer.add_image(disp_y, name=f"Disp Y ({layer_name})", visible=False)
151
+ viewer.add_image(disp_x, name=f"Disp X ({layer_name})", visible=False)
152
+
153
+ if feats.shape[0] > 2:
154
+ labels = features_np[:, 2, :, :]
155
+ viewer.add_image(labels, name=f"Labels ({layer_name})", visible=False)
156
+ pooled = features_np[:, 3, :, :]
157
+ viewer.add_image(pooled, name=f"Pooled Labels ({layer_name})", visible=False)
158
+
159
+ else:
160
+ # Handle 2D Images
161
+ if features_np.ndim == 4:
162
+ feats = features_np[0]
163
+ else:
164
+ feats = features_np
165
+
166
+ if feats.shape[0] >= 2:
167
+
168
+ viewer.add_image(feats[0], name=f"Disp Y ({layer_name})", visible=False)
169
+ viewer.add_image(feats[1], name=f"Disp X ({layer_name})", visible=False)
170
+
171
+ if feats.shape[0] > 2:
172
+ viewer.add_image(feats[2], name=f"Labels ({layer_name})", visible=False)
173
+ viewer.add_image(feats[3], name=f"Pooled Labels ({layer_name})", visible=False)
174
+
175
+ except Exception as e:
176
+ logger.error(f"Error parsing features: {e}")
177
+ show_error(f"Error displaying features: {e}")
178
+ viewer.add_image(features_np, name=f"Raw Features ({layer_name})")
179
+
180
+
181
+ # --- 4. Main Widget ---
182
+
183
+ @magic_factory(
184
+ call_button="Run PISCIS",
185
+ layout="vertical",
186
+ image_layer={"label": "Select Input"},
187
+ model_name={"label": "Model Name", "choices": ["20230905"]},
188
+ threshold={"label": "Threshold", "min": 0.0, "max": 1.0, "step": 0.1, "value": 1.0, "tooltip": "Minimum pixels for a spot."},
189
+ min_distance={"label": "Min Distance", "min": 0, "max": 20, "step": 1, "value": 1},
190
+ intermediates={"label": "Return Feature Maps", "value": False},
191
+ )
192
+ def piscis_inference(
193
+ viewer: napari.Viewer,
194
+ image_layer: "napari.layers.Image",
195
+ model_name: str = "20230905",
196
+ threshold: float = 1.0,
197
+ min_distance: int = 1,
198
+ intermediates: bool = False,
199
+ ):
200
+ if not DEPENDENCIES_INSTALLED:
201
+ show_error("PISCIS dependencies not found. Please install: pip install piscis scikit-image")
202
+ return
203
+ if image_layer is None:
204
+ show_warning("Please select an image layer.")
205
+ return
206
+
207
+ raw_image = np.asarray(image_layer.data)
208
+ show_info(f"PISCIS: Preparing input...")
209
+
210
+ worker = piscis_worker(
211
+ raw_image,
212
+ model_name,
213
+ threshold,
214
+ int(min_distance),
215
+ intermediates
216
+ )
217
+
218
+ def on_success(result):
219
+ coords_pred = result['coords']
220
+ features = result['features']
221
+ is_3d_stack = result['is_3d_stack']
222
+ was_color_converted = result['was_color_converted']
223
+ processed_image = result['processed_image']
224
+
225
+ # 1. Handle Color Conversion
226
+ if was_color_converted and processed_image is not None:
227
+ show_warning(f"Color input detected. Converted to grayscale for processing.")
228
+
229
+ viewer.add_image(
230
+ processed_image,
231
+ name=f"Grayscale Input ({image_layer.name})",
232
+ colormap='gray'
233
+ )
234
+ # Hide the original RGB layer
235
+ image_layer.visible = False
236
+
237
+ # 2. Handle Coordinates
238
+ if len(coords_pred) > 0:
239
+ viewer.add_points(
240
+ np.array(coords_pred),
241
+ name=f"Spots ({image_layer.name})",
242
+ size=3,
243
+ face_color='lime',
244
+ symbol='disc',
245
+ )
246
+ show_info(f"PISCIS: Detected {len(coords_pred)} spots.")
247
+ else:
248
+ show_info("PISCIS: No spots detected.")
249
+
250
+ # 3. Handle Feature Maps
251
+ if intermediates and features is not None:
252
+ _display_features(viewer, features, is_3d_stack, image_layer.name)
253
+
254
+ def on_error(e):
255
+ show_error(f"PISCIS failed: {str(e)}")
256
+ # Loguru syntax to attach the exception object 'e' for a traceback
257
+ logger.opt(exception=e).error("Worker failed")
258
+
259
+ worker.returned.connect(on_success)
260
+ worker.errored.connect(on_error)
261
+ worker.start()
@@ -0,0 +1,10 @@
1
+ name: napari-piscis
2
+ display_name: Piscis
3
+ contributions:
4
+ commands:
5
+ - id: napari-piscis.inference
6
+ title: "Piscis: Inference"
7
+ python_name: napari_piscis._widget:piscis_inference
8
+ widgets:
9
+ - command: napari-piscis.inference
10
+ display_name: "Piscis Inference"
@@ -0,0 +1,98 @@
1
+ Metadata-Version: 2.4
2
+ Name: napari-piscis
3
+ Version: 0.1.20
4
+ Summary: Napari plugin for Piscis spot detection (smFISH)
5
+ Author-email: Michael Blodgett <michaelablodgett@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2023-2024 William Niu
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Classifier: Development Status :: 2 - Pre-Alpha
28
+ Classifier: Framework :: napari
29
+ Classifier: Intended Audience :: Developers
30
+ Classifier: License :: OSI Approved :: MIT License
31
+ Classifier: Operating System :: OS Independent
32
+ Classifier: Programming Language :: Python
33
+ Classifier: Programming Language :: Python :: 3
34
+ Classifier: Programming Language :: Python :: 3 :: Only
35
+ Classifier: Programming Language :: Python :: 3.10
36
+ Classifier: Programming Language :: Python :: 3.11
37
+ Classifier: Programming Language :: Python :: 3.12
38
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
39
+ Requires-Python: <3.14,>=3.10
40
+ Description-Content-Type: text/markdown
41
+ License-File: LICENSE
42
+ Requires-Dist: napari>=0.4.19
43
+ Requires-Dist: magicgui>=0.8
44
+ Requires-Dist: numpy
45
+ Requires-Dist: scikit-image
46
+ Requires-Dist: piscis>=0.2.7
47
+ Requires-Dist: jax<=0.4.30
48
+ Requires-Dist: loguru
49
+ Provides-Extra: all
50
+ Requires-Dist: napari[all]; extra == "all"
51
+ Dynamic: license-file
52
+
53
+ # napari-piscis
54
+
55
+ [![License MIT](https://img.shields.io/pypi/l/napari-piscis.svg?color=green)](https://github.com/p5ithurism/napari-piscis/raw/main/LICENSE)
56
+ [![PyPI](https://img.shields.io/pypi/v/napari-piscis.svg?color=green)](https://pypi.org/project/napari-piscis)
57
+ [![Python Version](https://img.shields.io/pypi/pyversions/napari-piscis.svg?color=green)](https://python.org)
58
+ [![tests](https://github.com/p5ithurism/napari-piscis/workflows/tests/badge.svg)](https://github.com/p5ithurism/napari-piscis/actions)
59
+ [![codecov](https://codecov.io/gh/p5ithurism/napari-piscis/branch/main/graph/badge.svg)](https://codecov.io/gh/p5ithurism/napari-piscis)
60
+ [![napari hub](https://img.shields.io/endpoint?url=https://api.napari-hub.org/shields/napari-piscis)](https://napari-hub.org/plugins/napari-piscis)
61
+ [![npe2](https://img.shields.io/badge/plugin-npe2-blue?link=https://napari.org/stable/plugins/index.html)](https://napari.org/stable/plugins/index.html)
62
+
63
+ Unofficial napari plugin for [Piscis](https://github.com/zjniu/Piscis), a deep learning algorithm for spot detection.
64
+
65
+ ----------------------------------
66
+
67
+ This [napari] plugin was based off of the [napari-plugin-template].
68
+
69
+ ## Installation
70
+
71
+ You can install `napari-piscis` via [pip]:
72
+
73
+ ```
74
+ pip install napari-piscis
75
+ ```
76
+
77
+ ## License
78
+
79
+ Distributed under the terms of the [MIT] license,
80
+ "napari-piscis" is free and open source software
81
+
82
+ ## Issues
83
+
84
+ If you encounter any problems, please [file an issue] along with a detailed description.
85
+
86
+ [napari]: https://github.com/napari/napari
87
+ [@napari]: https://github.com/napari
88
+ [MIT]: http://opensource.org/licenses/MIT
89
+ [BSD-3]: http://opensource.org/licenses/BSD-3-Clause
90
+ [GNU GPL v3.0]: http://www.gnu.org/licenses/gpl-3.0.txt
91
+ [GNU LGPL v3.0]: http://www.gnu.org/licenses/lgpl-3.0.txt
92
+ [Apache Software License 2.0]: http://www.apache.org/licenses/LICENSE-2.0
93
+ [Mozilla Public License 2.0]: https://www.mozilla.org/media/MPL/2.0/index.txt
94
+ [napari-plugin-template]: https://github.com/napari/napari-plugin-template
95
+
96
+ [napari]: https://github.com/napari/napari
97
+ [pip]: https://pypi.org/project/pip/
98
+ [PyPI]: https://pypi.org/
@@ -0,0 +1,10 @@
1
+ napari_piscis/__init__.py,sha256=WjDmvecyDIyJLYp4rCV9vsSYbQDc4L1EpYqORvEXliI,33
2
+ napari_piscis/_version.py,sha256=P88Jfo9OvOr8LB0vHFqLUwFR7A0eE281KxEewbcCSBc,706
3
+ napari_piscis/_widget.py,sha256=T-V5eskIFJghOHGW526QTedMyAle0xjaOM3g_DktG9Q,8447
4
+ napari_piscis/napari.yaml,sha256=6X-K-Y_dbNdn9TGjA1YzGKCpBbdA9_dmB9HdEABE5lQ,281
5
+ napari_piscis-0.1.20.dist-info/licenses/LICENSE,sha256=83k71GgrPZSt4JsOf6KiAyi3_XL3kdmiexDLLYWvCTI,1072
6
+ napari_piscis-0.1.20.dist-info/METADATA,sha256=8_iuvEH3QD5PVoIJYFOe6m4mXDIYl2lPcDJxzCKRWUg,4476
7
+ napari_piscis-0.1.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ napari_piscis-0.1.20.dist-info/entry_points.txt,sha256=rXFKdNsiSm17cVGs3vJzP6SnKPllUkk3EaWrQNQlMQo,60
9
+ napari_piscis-0.1.20.dist-info/top_level.txt,sha256=vKkzMwrBPY9PlqL5WNKJB-V1lNXwTioDpIh_p2bSjEk,14
10
+ napari_piscis-0.1.20.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [napari.manifest]
2
+ napari-piscis = napari_piscis:napari.yaml
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023-2024 William Niu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ napari_piscis