senoquant 1.0.0b1__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.
Files changed (148) hide show
  1. senoquant/__init__.py +6 -0
  2. senoquant/_reader.py +7 -0
  3. senoquant/_widget.py +33 -0
  4. senoquant/napari.yaml +83 -0
  5. senoquant/reader/__init__.py +5 -0
  6. senoquant/reader/core.py +369 -0
  7. senoquant/tabs/__init__.py +15 -0
  8. senoquant/tabs/batch/__init__.py +10 -0
  9. senoquant/tabs/batch/backend.py +641 -0
  10. senoquant/tabs/batch/config.py +270 -0
  11. senoquant/tabs/batch/frontend.py +1283 -0
  12. senoquant/tabs/batch/io.py +326 -0
  13. senoquant/tabs/batch/layers.py +86 -0
  14. senoquant/tabs/quantification/__init__.py +1 -0
  15. senoquant/tabs/quantification/backend.py +228 -0
  16. senoquant/tabs/quantification/features/__init__.py +80 -0
  17. senoquant/tabs/quantification/features/base.py +142 -0
  18. senoquant/tabs/quantification/features/marker/__init__.py +5 -0
  19. senoquant/tabs/quantification/features/marker/config.py +69 -0
  20. senoquant/tabs/quantification/features/marker/dialog.py +437 -0
  21. senoquant/tabs/quantification/features/marker/export.py +879 -0
  22. senoquant/tabs/quantification/features/marker/feature.py +119 -0
  23. senoquant/tabs/quantification/features/marker/morphology.py +285 -0
  24. senoquant/tabs/quantification/features/marker/rows.py +654 -0
  25. senoquant/tabs/quantification/features/marker/thresholding.py +46 -0
  26. senoquant/tabs/quantification/features/roi.py +346 -0
  27. senoquant/tabs/quantification/features/spots/__init__.py +5 -0
  28. senoquant/tabs/quantification/features/spots/config.py +62 -0
  29. senoquant/tabs/quantification/features/spots/dialog.py +477 -0
  30. senoquant/tabs/quantification/features/spots/export.py +1292 -0
  31. senoquant/tabs/quantification/features/spots/feature.py +112 -0
  32. senoquant/tabs/quantification/features/spots/morphology.py +279 -0
  33. senoquant/tabs/quantification/features/spots/rows.py +241 -0
  34. senoquant/tabs/quantification/frontend.py +815 -0
  35. senoquant/tabs/segmentation/__init__.py +1 -0
  36. senoquant/tabs/segmentation/backend.py +131 -0
  37. senoquant/tabs/segmentation/frontend.py +1009 -0
  38. senoquant/tabs/segmentation/models/__init__.py +5 -0
  39. senoquant/tabs/segmentation/models/base.py +146 -0
  40. senoquant/tabs/segmentation/models/cpsam/details.json +65 -0
  41. senoquant/tabs/segmentation/models/cpsam/model.py +150 -0
  42. senoquant/tabs/segmentation/models/default_2d/details.json +69 -0
  43. senoquant/tabs/segmentation/models/default_2d/model.py +664 -0
  44. senoquant/tabs/segmentation/models/default_3d/details.json +69 -0
  45. senoquant/tabs/segmentation/models/default_3d/model.py +682 -0
  46. senoquant/tabs/segmentation/models/hf.py +71 -0
  47. senoquant/tabs/segmentation/models/nuclear_dilation/__init__.py +1 -0
  48. senoquant/tabs/segmentation/models/nuclear_dilation/details.json +26 -0
  49. senoquant/tabs/segmentation/models/nuclear_dilation/model.py +96 -0
  50. senoquant/tabs/segmentation/models/perinuclear_rings/__init__.py +1 -0
  51. senoquant/tabs/segmentation/models/perinuclear_rings/details.json +34 -0
  52. senoquant/tabs/segmentation/models/perinuclear_rings/model.py +132 -0
  53. senoquant/tabs/segmentation/stardist_onnx_utils/__init__.py +2 -0
  54. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/__init__.py +3 -0
  55. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/__init__.py +6 -0
  56. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/generate.py +470 -0
  57. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/prepare.py +273 -0
  58. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/rawdata.py +112 -0
  59. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/transform.py +384 -0
  60. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/__init__.py +0 -0
  61. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/blocks.py +184 -0
  62. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/losses.py +79 -0
  63. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/nets.py +165 -0
  64. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/predict.py +467 -0
  65. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/probability.py +67 -0
  66. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/train.py +148 -0
  67. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/io/__init__.py +163 -0
  68. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/__init__.py +52 -0
  69. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/base_model.py +329 -0
  70. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_isotropic.py +160 -0
  71. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_projection.py +178 -0
  72. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_standard.py +446 -0
  73. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_upsampling.py +54 -0
  74. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/config.py +254 -0
  75. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/pretrained.py +119 -0
  76. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/scripts/__init__.py +0 -0
  77. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/scripts/care_predict.py +180 -0
  78. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/__init__.py +5 -0
  79. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/plot_utils.py +159 -0
  80. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/six.py +18 -0
  81. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/tf.py +644 -0
  82. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/utils.py +272 -0
  83. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/version.py +1 -0
  84. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/docs/source/conf.py +368 -0
  85. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/setup.py +68 -0
  86. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_datagen.py +169 -0
  87. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_models.py +462 -0
  88. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_utils.py +166 -0
  89. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tools/create_zip_contents.py +34 -0
  90. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/__init__.py +30 -0
  91. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/big.py +624 -0
  92. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/bioimageio_utils.py +494 -0
  93. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/data/__init__.py +39 -0
  94. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/__init__.py +10 -0
  95. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/geom2d.py +215 -0
  96. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/geom3d.py +349 -0
  97. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/matching.py +483 -0
  98. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/__init__.py +28 -0
  99. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/base.py +1217 -0
  100. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/model2d.py +594 -0
  101. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/model3d.py +696 -0
  102. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/nms.py +384 -0
  103. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/__init__.py +2 -0
  104. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/plot.py +74 -0
  105. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/render.py +298 -0
  106. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/rays3d.py +373 -0
  107. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/sample_patches.py +65 -0
  108. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/__init__.py +0 -0
  109. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/predict2d.py +90 -0
  110. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/predict3d.py +93 -0
  111. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/utils.py +408 -0
  112. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/version.py +1 -0
  113. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/__init__.py +45 -0
  114. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/__init__.py +17 -0
  115. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/cli.py +55 -0
  116. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/core.py +285 -0
  117. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/__init__.py +15 -0
  118. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/cli.py +36 -0
  119. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/divisibility.py +193 -0
  120. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/probe.py +100 -0
  121. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/receptive_field.py +182 -0
  122. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/rf_cli.py +48 -0
  123. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/valid_sizes.py +278 -0
  124. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/post/__init__.py +8 -0
  125. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/post/core.py +157 -0
  126. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/pre/__init__.py +17 -0
  127. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/pre/core.py +226 -0
  128. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/predict/__init__.py +5 -0
  129. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/predict/core.py +401 -0
  130. senoquant/tabs/settings/__init__.py +1 -0
  131. senoquant/tabs/settings/backend.py +29 -0
  132. senoquant/tabs/settings/frontend.py +19 -0
  133. senoquant/tabs/spots/__init__.py +1 -0
  134. senoquant/tabs/spots/backend.py +139 -0
  135. senoquant/tabs/spots/frontend.py +800 -0
  136. senoquant/tabs/spots/models/__init__.py +5 -0
  137. senoquant/tabs/spots/models/base.py +94 -0
  138. senoquant/tabs/spots/models/rmp/details.json +61 -0
  139. senoquant/tabs/spots/models/rmp/model.py +499 -0
  140. senoquant/tabs/spots/models/udwt/details.json +103 -0
  141. senoquant/tabs/spots/models/udwt/model.py +482 -0
  142. senoquant/utils.py +25 -0
  143. senoquant-1.0.0b1.dist-info/METADATA +193 -0
  144. senoquant-1.0.0b1.dist-info/RECORD +148 -0
  145. senoquant-1.0.0b1.dist-info/WHEEL +5 -0
  146. senoquant-1.0.0b1.dist-info/entry_points.txt +2 -0
  147. senoquant-1.0.0b1.dist-info/licenses/LICENSE +28 -0
  148. senoquant-1.0.0b1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,5 @@
1
+ """Segmentation model definitions and storage."""
2
+
3
+ from .base import SenoQuantSegmentationModel
4
+
5
+ __all__ = ["SenoQuantSegmentationModel"]
@@ -0,0 +1,146 @@
1
+ """Model wrapper for segmentation resources."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+
8
+
9
+ class SenoQuantSegmentationModel:
10
+ """Handle per-model storage and metadata paths.
11
+
12
+ Parameters
13
+ ----------
14
+ name : str
15
+ Model identifier used for folder creation.
16
+ models_root : pathlib.Path or None
17
+ Optional root folder for model storage.
18
+ """
19
+
20
+ def __init__(self, name: str, models_root: Path | None = None) -> None:
21
+ """Initialize the model wrapper and ensure its folder exists."""
22
+ if not name:
23
+ raise ValueError("Model name must be non-empty.")
24
+
25
+ self.name = name
26
+ self.models_root = models_root or Path(__file__).parent
27
+ self.model_dir = self.models_root / name
28
+ self.model_dir.mkdir(parents=True, exist_ok=True)
29
+
30
+ @property
31
+ def details_path(self) -> Path:
32
+ """Return the path to the details JSON file."""
33
+ return self.model_dir / "details.json"
34
+
35
+ @property
36
+ def class_path(self) -> Path:
37
+ """Return the path to the model class file."""
38
+ return self.model_dir / "model.py"
39
+
40
+ def load_details(self) -> dict:
41
+ """Load model metadata from the details file.
42
+
43
+ Returns
44
+ -------
45
+ dict
46
+ Parsed model metadata dictionary.
47
+ """
48
+ if not self.details_path.exists():
49
+ return {}
50
+ with self.details_path.open("r", encoding="utf-8") as handle:
51
+ return json.load(handle)
52
+
53
+ def run(self, **kwargs) -> dict | None:
54
+ """Run the model with the provided inputs and settings.
55
+
56
+ Parameters
57
+ ----------
58
+ **kwargs
59
+ Model inputs and settings passed from the UI.
60
+
61
+ Returns
62
+ -------
63
+ dict or None
64
+ Result dictionary from the model, or None if not implemented.
65
+ """
66
+ raise NotImplementedError("Model run not implemented.")
67
+
68
+ def list_settings(self) -> list[dict]:
69
+ """Return the settings definitions for this model.
70
+
71
+ Returns
72
+ -------
73
+ list[dict]
74
+ Settings definitions for building the UI.
75
+ """
76
+ details = self.load_details()
77
+ settings = details.get("settings", [])
78
+ if isinstance(settings, list):
79
+ return settings
80
+ return []
81
+
82
+ def display_order(self) -> float | None:
83
+ """Return the optional display ordering for this model.
84
+
85
+ Returns
86
+ -------
87
+ float or None
88
+ Numeric ordering value if specified in details.json.
89
+ """
90
+ details = self.load_details()
91
+ order = details.get("order")
92
+ if isinstance(order, (int, float)):
93
+ return float(order)
94
+ if isinstance(order, str):
95
+ try:
96
+ return float(order)
97
+ except ValueError:
98
+ return None
99
+ return None
100
+
101
+ def supports_task(self, task: str) -> bool:
102
+ """Return whether the model supports a given task.
103
+
104
+ Parameters
105
+ ----------
106
+ task : str
107
+ Task name, such as "nuclear" or "cytoplasmic".
108
+
109
+ Returns
110
+ -------
111
+ bool
112
+ True if the task is supported.
113
+ """
114
+ details = self.load_details()
115
+ tasks = details.get("tasks", {})
116
+ task_info = tasks.get(task, {})
117
+ return bool(task_info.get("supported", False))
118
+
119
+ def cytoplasmic_input_modes(self) -> list[str]:
120
+ """Return supported input modes for cytoplasmic segmentation.
121
+
122
+ Returns
123
+ -------
124
+ list[str]
125
+ Input modes, e.g., "cytoplasmic" or "nuclear+cytoplasmic".
126
+ """
127
+ details = self.load_details()
128
+ tasks = details.get("tasks", {})
129
+ task_info = tasks.get("cytoplasmic", {})
130
+ modes = task_info.get("input_modes", [])
131
+ if isinstance(modes, list):
132
+ return modes
133
+ return []
134
+
135
+ def cytoplasmic_nuclear_optional(self) -> bool:
136
+ """Return whether the nuclear channel is optional for cytoplasmic mode.
137
+
138
+ Returns
139
+ -------
140
+ bool
141
+ True when the nuclear channel is optional.
142
+ """
143
+ details = self.load_details()
144
+ tasks = details.get("tasks", {})
145
+ task_info = tasks.get("cytoplasmic", {})
146
+ return bool(task_info.get("nuclear_channel_optional", False))
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "cpsam",
3
+ "description": "Cellpose SAM model",
4
+ "version": "0.1.0",
5
+ "order": 3,
6
+ "tasks": {
7
+ "nuclear": {
8
+ "supported": true
9
+ },
10
+ "cytoplasmic": {
11
+ "supported": true,
12
+ "input_modes": ["cytoplasmic", "nuclear+cytoplasmic"],
13
+ "nuclear_channel_optional": true
14
+ }
15
+ },
16
+ "settings": [
17
+ {
18
+ "key": "diameter",
19
+ "label": "Diameter",
20
+ "type": "float",
21
+ "decimals": 1,
22
+ "min": 0.1,
23
+ "max": 1000.0,
24
+ "default": 30.0
25
+ },
26
+ {
27
+ "key": "flow_threshold",
28
+ "label": "Flow threshold",
29
+ "type": "float",
30
+ "decimals": 1,
31
+ "min": 0.0,
32
+ "max": 2.0,
33
+ "default": 0.4
34
+ },
35
+ {
36
+ "key": "cellprob_threshold",
37
+ "label": "Cellprob threshold",
38
+ "type": "float",
39
+ "decimals": 1,
40
+ "min": -6.0,
41
+ "max": 6.0,
42
+ "default": 0.0
43
+ },
44
+ {
45
+ "key": "n_iterations",
46
+ "label": "Number of iterations",
47
+ "type": "int",
48
+ "min": 0,
49
+ "max": 9999,
50
+ "default": 0
51
+ },
52
+ {
53
+ "key": "use_3d",
54
+ "label": "3D",
55
+ "type": "bool",
56
+ "default": false
57
+ },
58
+ {
59
+ "key": "normalize",
60
+ "label": "Normalize",
61
+ "type": "bool",
62
+ "default": true
63
+ }
64
+ ]
65
+ }
@@ -0,0 +1,150 @@
1
+ """CPSAM segmentation model implementation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ import numpy as np
7
+
8
+ from senoquant.utils import layer_data_asarray
9
+ from ..hf import DEFAULT_REPO_ID, ensure_hf_model
10
+ from ..base import SenoQuantSegmentationModel
11
+
12
+
13
+ class CPSAMModel(SenoQuantSegmentationModel):
14
+ """CPSAM segmentation model implementation."""
15
+
16
+ def __init__(self, models_root=None) -> None:
17
+ """Initialize the CPSAM model wrapper."""
18
+ super().__init__("cpsam", models_root=models_root)
19
+ from cellpose.models import CellposeModel
20
+
21
+ model_path = Path(self.model_dir) / "cpsam"
22
+ if not model_path.exists():
23
+ try:
24
+ model_path = ensure_hf_model(
25
+ "cpsam",
26
+ self.model_dir,
27
+ repo_id=DEFAULT_REPO_ID,
28
+ )
29
+ except RuntimeError:
30
+ pass
31
+ # Always request GPU; Cellpose will fall back if unavailable.
32
+ self._model = CellposeModel(gpu=True, pretrained_model=str(model_path))
33
+
34
+ def run(self, **kwargs) -> dict:
35
+ """Run CPSAM using the Cellpose API.
36
+
37
+ Parameters
38
+ ----------
39
+ **kwargs
40
+ task : str
41
+ Segmentation task ("nuclear" or "cytoplasmic").
42
+ layer : napari.layers.Image or None
43
+ Nuclear image layer for nuclear task.
44
+ cytoplasmic_layer : napari.layers.Image or None
45
+ Cytoplasmic image layer for cytoplasmic task.
46
+ nuclear_layer : napari.layers.Image or None
47
+ Nuclear image layer for cytoplasmic task.
48
+ settings : dict
49
+ Model settings keyed by the details.json schema.
50
+
51
+ Returns
52
+ -------
53
+ dict
54
+ Dictionary containing masks, flows, and styles from Cellpose.
55
+ """
56
+ task = kwargs.get("task")
57
+ settings = kwargs.get("settings", {})
58
+
59
+ do_3d = bool(settings.get("use_3d", False))
60
+ normalize = bool(settings.get("normalize", True))
61
+ diameter = settings.get("diameter")
62
+ flow_threshold = settings.get("flow_threshold", 0.4)
63
+ cellprob_threshold = settings.get("cellprob_threshold", 0.0)
64
+ n_iterations = settings.get("n_iterations", 0)
65
+
66
+ if task == "nuclear":
67
+ layer = kwargs.get("layer")
68
+ image = self._extract_layer_data(layer, required=True)
69
+ input_data = self._prepare_input(image)
70
+ elif task == "cytoplasmic":
71
+ cyto_layer = kwargs.get("cytoplasmic_layer")
72
+ nuclear_layer = kwargs.get("nuclear_layer")
73
+ cyto_image = self._extract_layer_data(cyto_layer, required=True)
74
+ nuclear_image = self._extract_layer_data(
75
+ nuclear_layer, required=False
76
+ )
77
+ if nuclear_image is None:
78
+ input_data = self._prepare_input(cyto_image)
79
+ else:
80
+ input_data = self._prepare_input(
81
+ nuclear_image,
82
+ cyto_image,
83
+ )
84
+ else:
85
+ raise ValueError("Unknown task for CPSAM.")
86
+
87
+ masks, flows, styles = self._model.eval(
88
+ input_data,
89
+ normalize=normalize,
90
+ diameter=diameter,
91
+ flow_threshold=flow_threshold,
92
+ cellprob_threshold=cellprob_threshold,
93
+ do_3D=do_3d,
94
+ z_axis=0 if do_3d else None,
95
+ niter=n_iterations,
96
+ )
97
+
98
+ return {"masks": masks, "flows": flows, "styles": styles}
99
+
100
+ def _extract_layer_data(self, layer, required: bool) -> np.ndarray | None:
101
+ """Return numpy data for the given napari layer.
102
+
103
+ Parameters
104
+ ----------
105
+ layer : object or None
106
+ Napari layer to convert.
107
+ required : bool
108
+ Whether a missing layer should raise an error.
109
+
110
+ Returns
111
+ -------
112
+ numpy.ndarray or None
113
+ Layer data or None if not required and missing.
114
+ """
115
+ if layer is None:
116
+ if required:
117
+ raise ValueError("Layer is required for CPSAM.")
118
+ return None
119
+ return layer_data_asarray(layer)
120
+
121
+ def _prepare_input(
122
+ self,
123
+ nuclear: np.ndarray,
124
+ cytoplasmic: np.ndarray | None = None,
125
+ ) -> np.ndarray:
126
+ """Prepare CPSAM input as YX, ZYX, CYX, or ZCYX.
127
+
128
+ Parameters
129
+ ----------
130
+ nuclear : numpy.ndarray
131
+ Nuclear image data.
132
+ cytoplasmic : numpy.ndarray or None
133
+ Cytoplasmic image data, when provided.
134
+
135
+ Returns
136
+ -------
137
+ numpy.ndarray
138
+ Input array suitable for Cellpose eval.
139
+ """
140
+ if nuclear.ndim not in (2, 3):
141
+ raise ValueError("Input image must be 2D or 3D.")
142
+ if cytoplasmic is not None and cytoplasmic.shape != nuclear.shape:
143
+ raise ValueError("Nuclear and cytoplasmic images must match in shape.")
144
+
145
+ if cytoplasmic is None:
146
+ return nuclear.astype(np.float32)
147
+
148
+ axis = 0 if nuclear.ndim == 2 else 1
149
+ stacked = np.stack([nuclear, cytoplasmic], axis=axis)
150
+ return stacked.astype(np.float32)
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "default_2d",
3
+ "description": "Fine-tuned StarDist 2D model for nuclear segmentation.",
4
+ "version": "0.1.0",
5
+ "order": 1,
6
+ "tasks": {
7
+ "nuclear": {
8
+ "supported": true
9
+ },
10
+ "cytoplasmic": {
11
+ "supported": false
12
+ }
13
+ },
14
+ "settings": [
15
+ {
16
+ "key": "object_diameter_px",
17
+ "label": "Object diameter (px)",
18
+ "type": "float",
19
+ "decimals": 1,
20
+ "min": 1.0,
21
+ "max": 500.0,
22
+ "default": 30.0
23
+ },
24
+ {
25
+ "key": "prob_thresh",
26
+ "label": "Prob threshold",
27
+ "type": "float",
28
+ "decimals": 3,
29
+ "min": 0.0,
30
+ "max": 1.0,
31
+ "default": 0.496187
32
+ },
33
+ {
34
+ "key": "nms_thresh",
35
+ "label": "NMS threshold",
36
+ "type": "float",
37
+ "decimals": 3,
38
+ "min": 0.0,
39
+ "max": 1.0,
40
+ "default": 0.3
41
+ },
42
+ {
43
+ "key": "normalize",
44
+ "label": "Normalize",
45
+ "type": "bool",
46
+ "default": true
47
+ },
48
+ {
49
+ "key": "pmin",
50
+ "label": "Percentile min",
51
+ "type": "float",
52
+ "decimals": 1,
53
+ "min": 0.0,
54
+ "max": 100.0,
55
+ "default": 1.0,
56
+ "enabled_by": "normalize"
57
+ },
58
+ {
59
+ "key": "pmax",
60
+ "label": "Percentile max",
61
+ "type": "float",
62
+ "decimals": 1,
63
+ "min": 0.0,
64
+ "max": 100.0,
65
+ "default": 99.8,
66
+ "enabled_by": "normalize"
67
+ }
68
+ ]
69
+ }