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.
- senoquant/__init__.py +6 -0
- senoquant/_reader.py +7 -0
- senoquant/_widget.py +33 -0
- senoquant/napari.yaml +83 -0
- senoquant/reader/__init__.py +5 -0
- senoquant/reader/core.py +369 -0
- senoquant/tabs/__init__.py +15 -0
- senoquant/tabs/batch/__init__.py +10 -0
- senoquant/tabs/batch/backend.py +641 -0
- senoquant/tabs/batch/config.py +270 -0
- senoquant/tabs/batch/frontend.py +1283 -0
- senoquant/tabs/batch/io.py +326 -0
- senoquant/tabs/batch/layers.py +86 -0
- senoquant/tabs/quantification/__init__.py +1 -0
- senoquant/tabs/quantification/backend.py +228 -0
- senoquant/tabs/quantification/features/__init__.py +80 -0
- senoquant/tabs/quantification/features/base.py +142 -0
- senoquant/tabs/quantification/features/marker/__init__.py +5 -0
- senoquant/tabs/quantification/features/marker/config.py +69 -0
- senoquant/tabs/quantification/features/marker/dialog.py +437 -0
- senoquant/tabs/quantification/features/marker/export.py +879 -0
- senoquant/tabs/quantification/features/marker/feature.py +119 -0
- senoquant/tabs/quantification/features/marker/morphology.py +285 -0
- senoquant/tabs/quantification/features/marker/rows.py +654 -0
- senoquant/tabs/quantification/features/marker/thresholding.py +46 -0
- senoquant/tabs/quantification/features/roi.py +346 -0
- senoquant/tabs/quantification/features/spots/__init__.py +5 -0
- senoquant/tabs/quantification/features/spots/config.py +62 -0
- senoquant/tabs/quantification/features/spots/dialog.py +477 -0
- senoquant/tabs/quantification/features/spots/export.py +1292 -0
- senoquant/tabs/quantification/features/spots/feature.py +112 -0
- senoquant/tabs/quantification/features/spots/morphology.py +279 -0
- senoquant/tabs/quantification/features/spots/rows.py +241 -0
- senoquant/tabs/quantification/frontend.py +815 -0
- senoquant/tabs/segmentation/__init__.py +1 -0
- senoquant/tabs/segmentation/backend.py +131 -0
- senoquant/tabs/segmentation/frontend.py +1009 -0
- senoquant/tabs/segmentation/models/__init__.py +5 -0
- senoquant/tabs/segmentation/models/base.py +146 -0
- senoquant/tabs/segmentation/models/cpsam/details.json +65 -0
- senoquant/tabs/segmentation/models/cpsam/model.py +150 -0
- senoquant/tabs/segmentation/models/default_2d/details.json +69 -0
- senoquant/tabs/segmentation/models/default_2d/model.py +664 -0
- senoquant/tabs/segmentation/models/default_3d/details.json +69 -0
- senoquant/tabs/segmentation/models/default_3d/model.py +682 -0
- senoquant/tabs/segmentation/models/hf.py +71 -0
- senoquant/tabs/segmentation/models/nuclear_dilation/__init__.py +1 -0
- senoquant/tabs/segmentation/models/nuclear_dilation/details.json +26 -0
- senoquant/tabs/segmentation/models/nuclear_dilation/model.py +96 -0
- senoquant/tabs/segmentation/models/perinuclear_rings/__init__.py +1 -0
- senoquant/tabs/segmentation/models/perinuclear_rings/details.json +34 -0
- senoquant/tabs/segmentation/models/perinuclear_rings/model.py +132 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/__init__.py +2 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/__init__.py +3 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/__init__.py +6 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/generate.py +470 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/prepare.py +273 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/rawdata.py +112 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/transform.py +384 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/__init__.py +0 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/blocks.py +184 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/losses.py +79 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/nets.py +165 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/predict.py +467 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/probability.py +67 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/train.py +148 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/io/__init__.py +163 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/__init__.py +52 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/base_model.py +329 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_isotropic.py +160 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_projection.py +178 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_standard.py +446 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_upsampling.py +54 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/config.py +254 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/pretrained.py +119 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/scripts/__init__.py +0 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/scripts/care_predict.py +180 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/__init__.py +5 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/plot_utils.py +159 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/six.py +18 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/tf.py +644 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/utils.py +272 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/version.py +1 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/docs/source/conf.py +368 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/setup.py +68 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_datagen.py +169 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_models.py +462 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_utils.py +166 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tools/create_zip_contents.py +34 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/__init__.py +30 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/big.py +624 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/bioimageio_utils.py +494 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/data/__init__.py +39 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/__init__.py +10 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/geom2d.py +215 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/geom3d.py +349 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/matching.py +483 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/__init__.py +28 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/base.py +1217 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/model2d.py +594 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/model3d.py +696 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/nms.py +384 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/__init__.py +2 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/plot.py +74 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/render.py +298 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/rays3d.py +373 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/sample_patches.py +65 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/__init__.py +0 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/predict2d.py +90 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/predict3d.py +93 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/utils.py +408 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/version.py +1 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/__init__.py +45 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/__init__.py +17 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/cli.py +55 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/core.py +285 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/__init__.py +15 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/cli.py +36 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/divisibility.py +193 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/probe.py +100 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/receptive_field.py +182 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/rf_cli.py +48 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/valid_sizes.py +278 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/post/__init__.py +8 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/post/core.py +157 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/pre/__init__.py +17 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/pre/core.py +226 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/predict/__init__.py +5 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/predict/core.py +401 -0
- senoquant/tabs/settings/__init__.py +1 -0
- senoquant/tabs/settings/backend.py +29 -0
- senoquant/tabs/settings/frontend.py +19 -0
- senoquant/tabs/spots/__init__.py +1 -0
- senoquant/tabs/spots/backend.py +139 -0
- senoquant/tabs/spots/frontend.py +800 -0
- senoquant/tabs/spots/models/__init__.py +5 -0
- senoquant/tabs/spots/models/base.py +94 -0
- senoquant/tabs/spots/models/rmp/details.json +61 -0
- senoquant/tabs/spots/models/rmp/model.py +499 -0
- senoquant/tabs/spots/models/udwt/details.json +103 -0
- senoquant/tabs/spots/models/udwt/model.py +482 -0
- senoquant/utils.py +25 -0
- senoquant-1.0.0b1.dist-info/METADATA +193 -0
- senoquant-1.0.0b1.dist-info/RECORD +148 -0
- senoquant-1.0.0b1.dist-info/WHEEL +5 -0
- senoquant-1.0.0b1.dist-info/entry_points.txt +2 -0
- senoquant-1.0.0b1.dist-info/licenses/LICENSE +28 -0
- senoquant-1.0.0b1.dist-info/top_level.txt +1 -0
senoquant/__init__.py
ADDED
senoquant/_reader.py
ADDED
senoquant/_widget.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Example QtPy widget for napari."""
|
|
2
|
+
|
|
3
|
+
from qtpy.QtWidgets import QTabWidget, QVBoxLayout, QWidget
|
|
4
|
+
|
|
5
|
+
from .tabs import BatchTab, QuantificationTab, SegmentationTab, SettingsTab, SpotsTab
|
|
6
|
+
from .tabs.settings.backend import SettingsBackend
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SenoQuantWidget(QWidget):
|
|
10
|
+
"""Main SenoQuant widget with tabbed UI."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, napari_viewer):
|
|
13
|
+
super().__init__()
|
|
14
|
+
self._viewer = napari_viewer
|
|
15
|
+
self._settings_backend = SettingsBackend()
|
|
16
|
+
|
|
17
|
+
layout = QVBoxLayout()
|
|
18
|
+
|
|
19
|
+
tabs = QTabWidget()
|
|
20
|
+
tabs.addTab(
|
|
21
|
+
SegmentationTab(
|
|
22
|
+
napari_viewer=napari_viewer,
|
|
23
|
+
settings_backend=self._settings_backend,
|
|
24
|
+
),
|
|
25
|
+
"Segmentation",
|
|
26
|
+
)
|
|
27
|
+
tabs.addTab(SpotsTab(napari_viewer=napari_viewer), "Spots")
|
|
28
|
+
tabs.addTab(QuantificationTab(napari_viewer=napari_viewer), "Quantification")
|
|
29
|
+
tabs.addTab(BatchTab(napari_viewer=napari_viewer), "Batch")
|
|
30
|
+
tabs.addTab(SettingsTab(backend=self._settings_backend), "Settings")
|
|
31
|
+
|
|
32
|
+
layout.addWidget(tabs)
|
|
33
|
+
self.setLayout(layout)
|
senoquant/napari.yaml
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: senoquant
|
|
2
|
+
contributions:
|
|
3
|
+
commands:
|
|
4
|
+
- id: senoquant.make_widget
|
|
5
|
+
python_name: senoquant._widget:SenoQuantWidget
|
|
6
|
+
title: SenoQuant
|
|
7
|
+
- id: senoquant.reader
|
|
8
|
+
python_name: senoquant._reader:get_reader
|
|
9
|
+
title: SenoQuant reader
|
|
10
|
+
widgets:
|
|
11
|
+
- command: senoquant.make_widget
|
|
12
|
+
display_name: SenoQuant
|
|
13
|
+
readers:
|
|
14
|
+
- command: senoquant.reader
|
|
15
|
+
filename_patterns:
|
|
16
|
+
- "*.czi"
|
|
17
|
+
- "*.dv"
|
|
18
|
+
- "*.r3d"
|
|
19
|
+
- "*.264"
|
|
20
|
+
- "*.265"
|
|
21
|
+
- "*.3fr"
|
|
22
|
+
- "*.3g2"
|
|
23
|
+
- "*.a64"
|
|
24
|
+
- "*.imt"
|
|
25
|
+
- "*.mcidas"
|
|
26
|
+
- "*.pcx"
|
|
27
|
+
- "*.spider"
|
|
28
|
+
- "*.xvthumb"
|
|
29
|
+
- "*.adp"
|
|
30
|
+
- "*.amr"
|
|
31
|
+
- "*.amv"
|
|
32
|
+
- "*.apng"
|
|
33
|
+
- "*.arw"
|
|
34
|
+
- "*.asf"
|
|
35
|
+
- "*.avc"
|
|
36
|
+
- "*.avi"
|
|
37
|
+
- "*.avs"
|
|
38
|
+
- "*.avs2"
|
|
39
|
+
- "*.bay"
|
|
40
|
+
- "*.bif"
|
|
41
|
+
- "*.bmp"
|
|
42
|
+
- "*.cdg"
|
|
43
|
+
- "*.cgi"
|
|
44
|
+
- "*.cif"
|
|
45
|
+
- "*.ct"
|
|
46
|
+
- "*.dcr"
|
|
47
|
+
- "*.dib"
|
|
48
|
+
- "*.dip"
|
|
49
|
+
- "*.dng"
|
|
50
|
+
- "*.dnxhd"
|
|
51
|
+
- "*.dvd"
|
|
52
|
+
- "*.erf"
|
|
53
|
+
- "*.exr"
|
|
54
|
+
- "*.fff"
|
|
55
|
+
- "*.gif"
|
|
56
|
+
- "*.icb"
|
|
57
|
+
- "*.if"
|
|
58
|
+
- "*.iiq"
|
|
59
|
+
- "*.ism"
|
|
60
|
+
- "*.jif"
|
|
61
|
+
- "*.jfif"
|
|
62
|
+
- "*.jng"
|
|
63
|
+
- "*.jp2"
|
|
64
|
+
- "*.jpg"
|
|
65
|
+
- "*.mov"
|
|
66
|
+
- "*.mp4"
|
|
67
|
+
- "*.mpo"
|
|
68
|
+
- "*.msp"
|
|
69
|
+
- "*.pdf"
|
|
70
|
+
- "*.png"
|
|
71
|
+
- "*.ppm"
|
|
72
|
+
- "*.ps"
|
|
73
|
+
- "*.zif"
|
|
74
|
+
- "*.lif"
|
|
75
|
+
- "*.nd2"
|
|
76
|
+
- "*.ome.tiff"
|
|
77
|
+
- "*.tiff"
|
|
78
|
+
- "*.ome.tif"
|
|
79
|
+
- "*.tif"
|
|
80
|
+
- "*.zarr"
|
|
81
|
+
- "*.sldy"
|
|
82
|
+
- "*.dir"
|
|
83
|
+
accepts_directories: false
|
senoquant/reader/core.py
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
"""Core BioIO reader implementation for SenoQuant."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import itertools
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Callable, Iterable
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from bioio_base.exceptions import UnsupportedFileFormatError
|
|
11
|
+
except Exception: # pragma: no cover - optional dependency
|
|
12
|
+
UnsupportedFileFormatError = Exception
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_reader(path: str | list[str]) -> Callable | None:
|
|
16
|
+
"""Return a reader callable for the given path.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
path : str or list of str
|
|
21
|
+
Path(s) selected in the napari reader dialog.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
callable or None
|
|
26
|
+
Reader callable that returns napari layer data, or ``None`` if the
|
|
27
|
+
path is not supported.
|
|
28
|
+
|
|
29
|
+
Notes
|
|
30
|
+
-----
|
|
31
|
+
This uses ``bioio.BioImage.determine_plugin`` to ensure the file can be
|
|
32
|
+
handled by BioIO. If the file is unsupported or BioIO is unavailable,
|
|
33
|
+
``None`` is returned so napari can try other readers.
|
|
34
|
+
"""
|
|
35
|
+
if isinstance(path, (list, tuple)):
|
|
36
|
+
if not path:
|
|
37
|
+
return None
|
|
38
|
+
path = path[0]
|
|
39
|
+
if not isinstance(path, str) or not path:
|
|
40
|
+
return None
|
|
41
|
+
if not Path(path).is_file():
|
|
42
|
+
return None
|
|
43
|
+
try:
|
|
44
|
+
import bioio
|
|
45
|
+
except ImportError:
|
|
46
|
+
return None
|
|
47
|
+
if not hasattr(bioio.BioImage, "determine_plugin"):
|
|
48
|
+
return None
|
|
49
|
+
try:
|
|
50
|
+
plugin = bioio.BioImage.determine_plugin(path)
|
|
51
|
+
except (
|
|
52
|
+
AttributeError,
|
|
53
|
+
ImportError,
|
|
54
|
+
ValueError,
|
|
55
|
+
RuntimeError,
|
|
56
|
+
FileNotFoundError,
|
|
57
|
+
OSError,
|
|
58
|
+
UnsupportedFileFormatError,
|
|
59
|
+
Exception,
|
|
60
|
+
):
|
|
61
|
+
return None
|
|
62
|
+
if plugin is None:
|
|
63
|
+
return None
|
|
64
|
+
return _read_senoquant
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _read_senoquant(path: str) -> Iterable[tuple]:
|
|
68
|
+
"""Read image data using BioIO and return napari layer tuples.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
path : str
|
|
73
|
+
File path to read.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
iterable of tuple
|
|
78
|
+
Napari layer tuples of the form ``(data, metadata, layer_type)``.
|
|
79
|
+
|
|
80
|
+
Notes
|
|
81
|
+
-----
|
|
82
|
+
When multiple scenes are present, each scene becomes a separate layer
|
|
83
|
+
with metadata describing the scene index and name.
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
from bioio import BioImage
|
|
87
|
+
except Exception as exc: # pragma: no cover - dependency dependent
|
|
88
|
+
raise ImportError(
|
|
89
|
+
"BioIO is required for the SenoQuant reader."
|
|
90
|
+
) from exc
|
|
91
|
+
|
|
92
|
+
base_name = Path(path).name
|
|
93
|
+
image = _open_bioimage(path)
|
|
94
|
+
layers: list[tuple] = []
|
|
95
|
+
colormap_cycle = _colormap_cycle()
|
|
96
|
+
scenes = image.scenes
|
|
97
|
+
|
|
98
|
+
for scene_idx, scene_id in enumerate(scenes):
|
|
99
|
+
image.set_scene(scene_id)
|
|
100
|
+
layers.extend(
|
|
101
|
+
_iter_channel_layers(
|
|
102
|
+
image,
|
|
103
|
+
base_name=base_name,
|
|
104
|
+
scene_id=scene_id,
|
|
105
|
+
scene_idx=scene_idx,
|
|
106
|
+
total_scenes=len(scenes),
|
|
107
|
+
path=path,
|
|
108
|
+
colormap_cycle=colormap_cycle,
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
return layers
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _open_bioimage(path: str):
|
|
116
|
+
"""Open a BioImage using bioio.
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
path : str
|
|
121
|
+
File path to read.
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
bioio.BioImage
|
|
126
|
+
BioIO image instance for the requested file.
|
|
127
|
+
"""
|
|
128
|
+
import bioio
|
|
129
|
+
|
|
130
|
+
plugin = None
|
|
131
|
+
try:
|
|
132
|
+
plugin = bioio.BioImage.determine_plugin(path)
|
|
133
|
+
except Exception:
|
|
134
|
+
plugin = None
|
|
135
|
+
|
|
136
|
+
if _should_force_tifffile(plugin, path):
|
|
137
|
+
image = _try_bioimage_readers(
|
|
138
|
+
bioio,
|
|
139
|
+
path,
|
|
140
|
+
reader_names=("bioio_tifffile", "bioio_ome_tiff"),
|
|
141
|
+
)
|
|
142
|
+
if image is not None:
|
|
143
|
+
return image
|
|
144
|
+
|
|
145
|
+
return bioio.BioImage(path)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _should_force_tifffile(plugin, path: str) -> bool:
|
|
149
|
+
"""Return True when tiff_glob should be bypassed for single-file TIFFs."""
|
|
150
|
+
if "*" in path or "?" in path:
|
|
151
|
+
return False
|
|
152
|
+
if not path.lower().endswith((".tif", ".tiff")):
|
|
153
|
+
return False
|
|
154
|
+
names = set()
|
|
155
|
+
if isinstance(plugin, str):
|
|
156
|
+
names.add(plugin)
|
|
157
|
+
else:
|
|
158
|
+
for attr in ("name", "value", "__name__", "__module__"):
|
|
159
|
+
value = getattr(plugin, attr, None)
|
|
160
|
+
if value:
|
|
161
|
+
names.add(str(value))
|
|
162
|
+
entrypoint = getattr(plugin, "entrypoint", None)
|
|
163
|
+
if entrypoint is not None:
|
|
164
|
+
for attr in ("name", "value", "__name__", "__module__"):
|
|
165
|
+
value = getattr(entrypoint, attr, None)
|
|
166
|
+
if value:
|
|
167
|
+
names.add(str(value))
|
|
168
|
+
return any("tiff_glob" in name or "tiff-glob" in name for name in names)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _try_bioimage_readers(bioio, path: str, reader_names: tuple[str, ...]):
|
|
172
|
+
"""Try opening a BioImage with explicit reader plugins."""
|
|
173
|
+
import importlib
|
|
174
|
+
|
|
175
|
+
for reader_name in reader_names:
|
|
176
|
+
module = None
|
|
177
|
+
try:
|
|
178
|
+
module = importlib.import_module(reader_name)
|
|
179
|
+
except Exception:
|
|
180
|
+
module = None
|
|
181
|
+
if module is not None:
|
|
182
|
+
reader_cls = getattr(module, "Reader", None)
|
|
183
|
+
if reader_cls is not None:
|
|
184
|
+
try:
|
|
185
|
+
return bioio.BioImage(path, reader=reader_cls)
|
|
186
|
+
except Exception:
|
|
187
|
+
continue
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _colormap_cycle() -> Iterable[str]:
|
|
192
|
+
"""Return an iterator cycling through approved colormap names.
|
|
193
|
+
|
|
194
|
+
Returns
|
|
195
|
+
-------
|
|
196
|
+
iterable of str
|
|
197
|
+
Cycle of colormap names to assign to reader layers.
|
|
198
|
+
"""
|
|
199
|
+
names = [
|
|
200
|
+
"blue",
|
|
201
|
+
"bop blue",
|
|
202
|
+
"bop orange",
|
|
203
|
+
"bop purple",
|
|
204
|
+
"cyan",
|
|
205
|
+
# "fire",
|
|
206
|
+
# "gist_earth",
|
|
207
|
+
# "gray",
|
|
208
|
+
# "gray_r",
|
|
209
|
+
"green",
|
|
210
|
+
# "HiLo",
|
|
211
|
+
# "hsv",
|
|
212
|
+
# "I Blue",
|
|
213
|
+
# "I Bordeaux",
|
|
214
|
+
# "I Forest",
|
|
215
|
+
# "I Orange",
|
|
216
|
+
# "I Purple",
|
|
217
|
+
# "ice",
|
|
218
|
+
# "inferno",
|
|
219
|
+
# "magenta",
|
|
220
|
+
# "magma",
|
|
221
|
+
# "nan",
|
|
222
|
+
# "PiYG",
|
|
223
|
+
# "plasma",
|
|
224
|
+
"red",
|
|
225
|
+
# "turbo",
|
|
226
|
+
# "twilight",
|
|
227
|
+
# "twilight_shifted",
|
|
228
|
+
# "viridis",
|
|
229
|
+
"yellow",
|
|
230
|
+
]
|
|
231
|
+
return itertools.cycle(names)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _physical_pixel_sizes(image) -> dict[str, float | None]:
|
|
235
|
+
"""Return physical pixel sizes (um) for the active scene."""
|
|
236
|
+
try:
|
|
237
|
+
sizes = image.physical_pixel_sizes
|
|
238
|
+
except Exception:
|
|
239
|
+
return {"Z": None, "Y": None, "X": None}
|
|
240
|
+
return {
|
|
241
|
+
"Z": sizes.Z,
|
|
242
|
+
"Y": sizes.Y,
|
|
243
|
+
"X": sizes.X,
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _axes_present(image) -> set[str]:
|
|
248
|
+
"""Return the set of axis labels present in the BioIO image."""
|
|
249
|
+
dims = getattr(image, "dims", None)
|
|
250
|
+
if dims is None:
|
|
251
|
+
return set()
|
|
252
|
+
if isinstance(dims, str):
|
|
253
|
+
return set(dims)
|
|
254
|
+
order = getattr(dims, "order", None)
|
|
255
|
+
if isinstance(order, str):
|
|
256
|
+
return set(order)
|
|
257
|
+
axes = getattr(dims, "axes", None)
|
|
258
|
+
if not axes:
|
|
259
|
+
return set()
|
|
260
|
+
result: set[str] = set()
|
|
261
|
+
for axis in axes:
|
|
262
|
+
if isinstance(axis, str):
|
|
263
|
+
result.add(axis)
|
|
264
|
+
continue
|
|
265
|
+
name = (
|
|
266
|
+
getattr(axis, "name", None)
|
|
267
|
+
or getattr(axis, "value", None)
|
|
268
|
+
or getattr(axis, "axis", None)
|
|
269
|
+
)
|
|
270
|
+
if name:
|
|
271
|
+
result.add(str(name))
|
|
272
|
+
return result
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _iter_channel_layers(
|
|
276
|
+
image,
|
|
277
|
+
*,
|
|
278
|
+
base_name: str,
|
|
279
|
+
scene_id: str,
|
|
280
|
+
scene_idx: int,
|
|
281
|
+
total_scenes: int,
|
|
282
|
+
path: str,
|
|
283
|
+
colormap_cycle: Iterable[str] | None = None,
|
|
284
|
+
) -> list[tuple]:
|
|
285
|
+
"""Split BioIO data into single-channel (Z)YX napari layers.
|
|
286
|
+
|
|
287
|
+
Parameters
|
|
288
|
+
----------
|
|
289
|
+
image : bioio.BioImage
|
|
290
|
+
BioIO image with the current scene selected.
|
|
291
|
+
base_name : str
|
|
292
|
+
Base filename for layer naming.
|
|
293
|
+
scene_id : str
|
|
294
|
+
Scene identifier string.
|
|
295
|
+
scene_idx : int
|
|
296
|
+
Scene index within the file.
|
|
297
|
+
total_scenes : int
|
|
298
|
+
Total number of scenes in the file.
|
|
299
|
+
path : str
|
|
300
|
+
Original image path to store in the metadata.
|
|
301
|
+
colormap_cycle : iterable of str or None, optional
|
|
302
|
+
Iterator that provides colormap names to assign to each layer.
|
|
303
|
+
|
|
304
|
+
Returns
|
|
305
|
+
-------
|
|
306
|
+
list of tuple
|
|
307
|
+
Napari layer tuples for each channel.
|
|
308
|
+
"""
|
|
309
|
+
dims = getattr(image, "dims", None)
|
|
310
|
+
axes_present = _axes_present(image)
|
|
311
|
+
t_size = getattr(dims, "T", 1) if "T" in axes_present else 1
|
|
312
|
+
c_size = getattr(dims, "C", 1) if "C" in axes_present else 1
|
|
313
|
+
z_size = getattr(dims, "Z", 1) if "Z" in axes_present else 1
|
|
314
|
+
|
|
315
|
+
scene_name = scene_id or f"Scene {scene_idx}"
|
|
316
|
+
scene_meta = {
|
|
317
|
+
"scene_id": scene_id,
|
|
318
|
+
"scene_index": scene_idx,
|
|
319
|
+
"scene_name": scene_name,
|
|
320
|
+
"total_scenes": total_scenes,
|
|
321
|
+
}
|
|
322
|
+
layers: list[tuple] = []
|
|
323
|
+
t_index = 0
|
|
324
|
+
|
|
325
|
+
if c_size > 1:
|
|
326
|
+
order = "CZYX" if z_size > 1 else "CYX"
|
|
327
|
+
kwargs = {}
|
|
328
|
+
if "T" in axes_present and "T" not in order:
|
|
329
|
+
kwargs["T"] = t_index
|
|
330
|
+
if "Z" in axes_present and "Z" not in order:
|
|
331
|
+
kwargs["Z"] = 0
|
|
332
|
+
data = image.get_image_data(order, **kwargs)
|
|
333
|
+
channel_iter = range(c_size)
|
|
334
|
+
else:
|
|
335
|
+
order = "ZYX" if z_size > 1 else "YX"
|
|
336
|
+
kwargs = {}
|
|
337
|
+
if "T" in axes_present and "T" not in order:
|
|
338
|
+
kwargs["T"] = t_index
|
|
339
|
+
if "C" in axes_present and "C" not in order:
|
|
340
|
+
kwargs["C"] = 0
|
|
341
|
+
if "Z" in axes_present and "Z" not in order:
|
|
342
|
+
kwargs["Z"] = 0
|
|
343
|
+
data = image.get_image_data(order, **kwargs)
|
|
344
|
+
channel_iter = [0]
|
|
345
|
+
|
|
346
|
+
for channel_index in channel_iter:
|
|
347
|
+
layer_data = data[channel_index] if c_size > 1 else data
|
|
348
|
+
|
|
349
|
+
layer_name = f"{base_name} - {scene_name}" if total_scenes > 1 else base_name
|
|
350
|
+
if c_size > 1:
|
|
351
|
+
layer_name = f"{layer_name} - Channel {channel_index}"
|
|
352
|
+
|
|
353
|
+
physical_sizes = _physical_pixel_sizes(image)
|
|
354
|
+
meta = {
|
|
355
|
+
"name": layer_name,
|
|
356
|
+
"blending": "additive",
|
|
357
|
+
"metadata": {
|
|
358
|
+
"bioio_metadata": image.metadata,
|
|
359
|
+
"scene_info": scene_meta,
|
|
360
|
+
"path": path,
|
|
361
|
+
"channel_index": channel_index,
|
|
362
|
+
"physical_pixel_sizes": physical_sizes,
|
|
363
|
+
},
|
|
364
|
+
}
|
|
365
|
+
if colormap_cycle is not None:
|
|
366
|
+
meta["colormap"] = next(colormap_cycle)
|
|
367
|
+
layers.append((layer_data, meta, "image"))
|
|
368
|
+
|
|
369
|
+
return layers
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Tab widgets for SenoQuant."""
|
|
2
|
+
|
|
3
|
+
from .segmentation.frontend import SegmentationTab
|
|
4
|
+
from .spots.frontend import SpotsTab
|
|
5
|
+
from .quantification.frontend import QuantificationTab
|
|
6
|
+
from .settings.frontend import SettingsTab
|
|
7
|
+
from .batch.frontend import BatchTab
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"SegmentationTab",
|
|
11
|
+
"SpotsTab",
|
|
12
|
+
"QuantificationTab",
|
|
13
|
+
"SettingsTab",
|
|
14
|
+
"BatchTab",
|
|
15
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""Batch tab modules.
|
|
2
|
+
|
|
3
|
+
Exports the Batch UI widget and configuration/backend helpers.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .backend import BatchBackend
|
|
7
|
+
from .config import BatchChannelConfig, BatchJobConfig
|
|
8
|
+
from .frontend import BatchTab
|
|
9
|
+
|
|
10
|
+
__all__ = ["BatchBackend", "BatchChannelConfig", "BatchJobConfig", "BatchTab"]
|