cellfinder 1.1.3__py3-none-any.whl → 1.3.0rc0__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.
- cellfinder/__init__.py +20 -11
- cellfinder/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/core/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/core/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/core/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/core/__pycache__/main.cpython-310.pyc +0 -0
- cellfinder/core/__pycache__/main.cpython-311.pyc +0 -0
- cellfinder/core/__pycache__/main.cpython-312.pyc +0 -0
- cellfinder/core/__pycache__/types.cpython-310.pyc +0 -0
- cellfinder/core/__pycache__/types.cpython-311.pyc +0 -0
- cellfinder/core/__pycache__/types.cpython-312.pyc +0 -0
- cellfinder/core/classify/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/core/classify/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/core/classify/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/core/classify/__pycache__/augment.cpython-310.pyc +0 -0
- cellfinder/core/classify/__pycache__/augment.cpython-311.pyc +0 -0
- cellfinder/core/classify/__pycache__/augment.cpython-312.pyc +0 -0
- cellfinder/core/classify/__pycache__/classify.cpython-310.pyc +0 -0
- cellfinder/core/classify/__pycache__/classify.cpython-311.pyc +0 -0
- cellfinder/core/classify/__pycache__/classify.cpython-312.pyc +0 -0
- cellfinder/core/classify/__pycache__/cube_generator.cpython-310.pyc +0 -0
- cellfinder/core/classify/__pycache__/cube_generator.cpython-311.pyc +0 -0
- cellfinder/core/classify/__pycache__/cube_generator.cpython-312.pyc +0 -0
- cellfinder/core/classify/__pycache__/resnet.cpython-310.pyc +0 -0
- cellfinder/core/classify/__pycache__/resnet.cpython-311.pyc +0 -0
- cellfinder/core/classify/__pycache__/resnet.cpython-312.pyc +0 -0
- cellfinder/core/classify/__pycache__/tools.cpython-310.pyc +0 -0
- cellfinder/core/classify/__pycache__/tools.cpython-311.pyc +0 -0
- cellfinder/core/classify/__pycache__/tools.cpython-312.pyc +0 -0
- cellfinder/core/classify/classify.py +5 -6
- cellfinder/core/classify/cube_generator.py +25 -9
- cellfinder/core/classify/resnet.py +9 -6
- cellfinder/core/classify/tools.py +13 -11
- cellfinder/core/config/cellfinder.conf.custom +3 -0
- cellfinder/core/detect/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/core/detect/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/core/detect/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/core/detect/__pycache__/detect.cpython-310.pyc +0 -0
- cellfinder/core/detect/__pycache__/detect.cpython-311.pyc +0 -0
- cellfinder/core/detect/__pycache__/detect.cpython-312.pyc +0 -0
- cellfinder/core/detect/detect.py +12 -1
- cellfinder/core/detect/filters/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/core/detect/filters/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/core/detect/filters/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/core/detect/filters/__pycache__/setup_filters.cpython-310.pyc +0 -0
- cellfinder/core/detect/filters/__pycache__/setup_filters.cpython-311.pyc +0 -0
- cellfinder/core/detect/filters/__pycache__/setup_filters.cpython-312.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/classical_filter.cpython-310.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/classical_filter.cpython-311.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/classical_filter.cpython-312.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/plane_filter.cpython-310.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/plane_filter.cpython-311.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/plane_filter.cpython-312.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/tile_walker.cpython-310.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/tile_walker.cpython-311.pyc +0 -0
- cellfinder/core/detect/filters/plane/__pycache__/tile_walker.cpython-312.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter._cube_overlaps-199.py310.1.nbc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter._cube_overlaps-199.py310.nbi +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter._cube_overlaps-263.py310.1.nbc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter._cube_overlaps-263.py310.2.nbc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter._cube_overlaps-263.py310.nbi +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter._cube_overlaps-263.py311.1.nbc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter._cube_overlaps-263.py311.2.nbc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter._cube_overlaps-263.py311.nbi +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter._cube_overlaps-263.py312.1.nbc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter._cube_overlaps-263.py312.2.nbc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter._cube_overlaps-263.py312.nbi +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter.cpython-310.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter.cpython-311.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/ball_filter.cpython-312.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/structure_detection.cpython-310.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/structure_detection.cpython-311.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/structure_detection.cpython-312.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/structure_splitting.cpython-310.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/structure_splitting.cpython-311.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/structure_splitting.cpython-312.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/volume_filter.cpython-310.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/volume_filter.cpython-311.pyc +0 -0
- cellfinder/core/detect/filters/volume/__pycache__/volume_filter.cpython-312.pyc +0 -0
- cellfinder/core/detect/filters/volume/ball_filter.py +198 -113
- cellfinder/core/detect/filters/volume/structure_detection.py +105 -41
- cellfinder/core/detect/filters/volume/structure_splitting.py +1 -1
- cellfinder/core/detect/filters/volume/volume_filter.py +48 -49
- cellfinder/core/download/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/core/download/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/core/download/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/core/download/__pycache__/cli.cpython-310.pyc +0 -0
- cellfinder/core/download/__pycache__/cli.cpython-312.pyc +0 -0
- cellfinder/core/download/__pycache__/download.cpython-310.pyc +0 -0
- cellfinder/core/download/__pycache__/download.cpython-311.pyc +0 -0
- cellfinder/core/download/__pycache__/download.cpython-312.pyc +0 -0
- cellfinder/core/download/cli.py +39 -32
- cellfinder/core/download/download.py +44 -56
- cellfinder/core/main.py +52 -67
- cellfinder/core/tools/__pycache__/IO.cpython-310.pyc +0 -0
- cellfinder/core/tools/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/core/tools/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/core/tools/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/core/tools/__pycache__/array_operations.cpython-310.pyc +0 -0
- cellfinder/core/tools/__pycache__/array_operations.cpython-311.pyc +0 -0
- cellfinder/core/tools/__pycache__/array_operations.cpython-312.pyc +0 -0
- cellfinder/core/tools/__pycache__/geometry.cpython-310.pyc +0 -0
- cellfinder/core/tools/__pycache__/geometry.cpython-311.pyc +0 -0
- cellfinder/core/tools/__pycache__/geometry.cpython-312.pyc +0 -0
- cellfinder/core/tools/__pycache__/image_processing.cpython-310.pyc +0 -0
- cellfinder/core/tools/__pycache__/image_processing.cpython-312.pyc +0 -0
- cellfinder/core/tools/__pycache__/prep.cpython-310.pyc +0 -0
- cellfinder/core/tools/__pycache__/prep.cpython-311.pyc +0 -0
- cellfinder/core/tools/__pycache__/prep.cpython-312.pyc +0 -0
- cellfinder/core/tools/__pycache__/source_files.cpython-310.pyc +0 -0
- cellfinder/core/tools/__pycache__/source_files.cpython-311.pyc +0 -0
- cellfinder/core/tools/__pycache__/source_files.cpython-312.pyc +0 -0
- cellfinder/core/tools/__pycache__/system.cpython-310.pyc +0 -0
- cellfinder/core/tools/__pycache__/system.cpython-312.pyc +0 -0
- cellfinder/core/tools/__pycache__/tf.cpython-310.pyc +0 -0
- cellfinder/core/tools/__pycache__/tiff.cpython-310.pyc +0 -0
- cellfinder/core/tools/__pycache__/tiff.cpython-312.pyc +0 -0
- cellfinder/core/tools/__pycache__/tools.cpython-310.pyc +0 -0
- cellfinder/core/tools/__pycache__/tools.cpython-311.pyc +0 -0
- cellfinder/core/tools/__pycache__/tools.cpython-312.pyc +0 -0
- cellfinder/core/tools/prep.py +12 -20
- cellfinder/core/tools/source_files.py +5 -3
- cellfinder/core/train/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/core/train/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/core/train/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/core/train/__pycache__/train_yml.cpython-310.pyc +0 -0
- cellfinder/core/train/__pycache__/train_yml.cpython-311.pyc +0 -0
- cellfinder/core/train/__pycache__/train_yml.cpython-312.pyc +0 -0
- cellfinder/core/train/train_yml.py +29 -27
- cellfinder/napari/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/napari/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/napari/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/napari/__pycache__/curation.cpython-310.pyc +0 -0
- cellfinder/napari/__pycache__/curation.cpython-312.pyc +0 -0
- cellfinder/napari/__pycache__/input_container.cpython-310.pyc +0 -0
- cellfinder/napari/__pycache__/input_container.cpython-311.pyc +0 -0
- cellfinder/napari/__pycache__/input_container.cpython-312.pyc +0 -0
- cellfinder/napari/__pycache__/sample_data.cpython-310.pyc +0 -0
- cellfinder/napari/__pycache__/sample_data.cpython-312.pyc +0 -0
- cellfinder/napari/__pycache__/utils.cpython-310.pyc +0 -0
- cellfinder/napari/__pycache__/utils.cpython-311.pyc +0 -0
- cellfinder/napari/__pycache__/utils.cpython-312.pyc +0 -0
- cellfinder/napari/detect/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/napari/detect/__pycache__/__init__.cpython-311.pyc +0 -0
- cellfinder/napari/detect/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/napari/detect/__pycache__/detect.cpython-310.pyc +0 -0
- cellfinder/napari/detect/__pycache__/detect.cpython-311.pyc +0 -0
- cellfinder/napari/detect/__pycache__/detect.cpython-312.pyc +0 -0
- cellfinder/napari/detect/__pycache__/detect_containers.cpython-310.pyc +0 -0
- cellfinder/napari/detect/__pycache__/detect_containers.cpython-311.pyc +0 -0
- cellfinder/napari/detect/__pycache__/detect_containers.cpython-312.pyc +0 -0
- cellfinder/napari/detect/__pycache__/thread_worker.cpython-310.pyc +0 -0
- cellfinder/napari/detect/__pycache__/thread_worker.cpython-311.pyc +0 -0
- cellfinder/napari/detect/__pycache__/thread_worker.cpython-312.pyc +0 -0
- cellfinder/napari/detect/detect.py +252 -57
- cellfinder/napari/detect/detect_containers.py +9 -1
- cellfinder/napari/detect/thread_worker.py +14 -0
- cellfinder/napari/train/__pycache__/__init__.cpython-310.pyc +0 -0
- cellfinder/napari/train/__pycache__/__init__.cpython-312.pyc +0 -0
- cellfinder/napari/train/__pycache__/train.cpython-310.pyc +0 -0
- cellfinder/napari/train/__pycache__/train.cpython-312.pyc +0 -0
- cellfinder/napari/train/__pycache__/train_containers.cpython-310.pyc +0 -0
- cellfinder/napari/train/__pycache__/train_containers.cpython-312.pyc +0 -0
- cellfinder/napari/train/train.py +2 -9
- cellfinder/napari/train/train_containers.py +3 -3
- cellfinder/napari/utils.py +88 -47
- {cellfinder-1.1.3.dist-info → cellfinder-1.3.0rc0.dist-info}/METADATA +11 -11
- cellfinder-1.3.0rc0.dist-info/RECORD +211 -0
- cellfinder/core/download/models.py +0 -49
- cellfinder/core/tools/IO.py +0 -48
- cellfinder/core/tools/tf.py +0 -46
- cellfinder/napari/images/brainglobe.png +0 -0
- cellfinder-1.1.3.dist-info/RECORD +0 -63
- {cellfinder-1.1.3.dist-info → cellfinder-1.3.0rc0.dist-info}/LICENSE +0 -0
- {cellfinder-1.1.3.dist-info → cellfinder-1.3.0rc0.dist-info}/WHEEL +0 -0
- {cellfinder-1.1.3.dist-info → cellfinder-1.3.0rc0.dist-info}/entry_points.txt +0 -0
- {cellfinder-1.1.3.dist-info → cellfinder-1.3.0rc0.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
from functools import partial
|
|
1
2
|
from math import ceil
|
|
2
3
|
from pathlib import Path
|
|
3
|
-
from typing import Optional
|
|
4
|
+
from typing import Any, Callable, Dict, Optional, Tuple
|
|
4
5
|
|
|
5
6
|
import napari
|
|
7
|
+
import napari.layers
|
|
8
|
+
from brainglobe_utils.cells.cells import Cell
|
|
6
9
|
from magicgui import magicgui
|
|
7
10
|
from magicgui.widgets import FunctionGui, ProgressBar
|
|
8
11
|
from napari.utils.notifications import show_info
|
|
@@ -10,10 +13,11 @@ from qtpy.QtWidgets import QScrollArea
|
|
|
10
13
|
|
|
11
14
|
from cellfinder.core.classify.cube_generator import get_cube_depth_min_max
|
|
12
15
|
from cellfinder.napari.utils import (
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
add_classified_layers,
|
|
17
|
+
add_single_layer,
|
|
18
|
+
cellfinder_header,
|
|
15
19
|
html_label_widget,
|
|
16
|
-
|
|
20
|
+
napari_array_to_cells,
|
|
17
21
|
)
|
|
18
22
|
|
|
19
23
|
from .detect_containers import (
|
|
@@ -33,14 +37,194 @@ CUBE_DEPTH = 20
|
|
|
33
37
|
MIN_PLANES_ANALYSE = 0
|
|
34
38
|
|
|
35
39
|
|
|
40
|
+
def get_heavy_widgets(
|
|
41
|
+
options: Dict[str, Any]
|
|
42
|
+
) -> Tuple[Callable, Callable, Callable]:
|
|
43
|
+
# signal and other input are separated out from the main magicgui
|
|
44
|
+
# parameter selections and are inserted as widget children in their own
|
|
45
|
+
# sub-containers of the root. Because if these image parameters are
|
|
46
|
+
# included in the root widget, every time *any* parameter updates, the gui
|
|
47
|
+
# freezes for a bit likely because magicgui is processing something for
|
|
48
|
+
# all the parameters when any parameter changes. And this processing takes
|
|
49
|
+
# particularly long for image parameters. Placing them as sub-containers
|
|
50
|
+
# alleviates this
|
|
51
|
+
@magicgui(
|
|
52
|
+
call_button=False,
|
|
53
|
+
persist=False,
|
|
54
|
+
scrollable=False,
|
|
55
|
+
labels=False,
|
|
56
|
+
auto_call=True,
|
|
57
|
+
)
|
|
58
|
+
def signal_image_opt(
|
|
59
|
+
viewer: napari.Viewer,
|
|
60
|
+
signal_image: napari.layers.Image,
|
|
61
|
+
):
|
|
62
|
+
"""
|
|
63
|
+
magicgui widget for setting the signal_image parameter.
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
signal_image : napari.layers.Image
|
|
68
|
+
Image layer containing the labelled cells
|
|
69
|
+
"""
|
|
70
|
+
options["signal_image"] = signal_image
|
|
71
|
+
options["viewer"] = viewer
|
|
72
|
+
|
|
73
|
+
@magicgui(
|
|
74
|
+
call_button=False,
|
|
75
|
+
persist=False,
|
|
76
|
+
scrollable=False,
|
|
77
|
+
labels=False,
|
|
78
|
+
auto_call=True,
|
|
79
|
+
)
|
|
80
|
+
def background_image_opt(
|
|
81
|
+
background_image: napari.layers.Image,
|
|
82
|
+
):
|
|
83
|
+
"""
|
|
84
|
+
magicgui widget for setting the background image parameter.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
background_image : napari.layers.Image
|
|
89
|
+
Image layer without labelled cells
|
|
90
|
+
"""
|
|
91
|
+
options["background_image"] = background_image
|
|
92
|
+
|
|
93
|
+
@magicgui(
|
|
94
|
+
call_button=False,
|
|
95
|
+
persist=False,
|
|
96
|
+
scrollable=False,
|
|
97
|
+
labels=False,
|
|
98
|
+
auto_call=True,
|
|
99
|
+
)
|
|
100
|
+
def cell_layer_opt(
|
|
101
|
+
cell_layer: napari.layers.Points,
|
|
102
|
+
):
|
|
103
|
+
"""
|
|
104
|
+
magicgui widget for setting the cell layer input when detection is
|
|
105
|
+
skipped.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
cell_layer : napari.layers.Points
|
|
110
|
+
If detection is skipped, select the cell layer containing the
|
|
111
|
+
detected cells to use for classification
|
|
112
|
+
"""
|
|
113
|
+
options["cell_layer"] = cell_layer
|
|
114
|
+
|
|
115
|
+
return signal_image_opt, background_image_opt, cell_layer_opt
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def add_heavy_widgets(
|
|
119
|
+
root: FunctionGui,
|
|
120
|
+
widgets: Tuple[FunctionGui, ...],
|
|
121
|
+
new_names: Tuple[str, ...],
|
|
122
|
+
insertions: Tuple[str, ...],
|
|
123
|
+
) -> None:
|
|
124
|
+
for widget, new_name, insertion in zip(widgets, new_names, insertions):
|
|
125
|
+
# make it look as if it's directly in the root container
|
|
126
|
+
widget.margins = 0, 0, 0, 0
|
|
127
|
+
# the parameters of these widgets are updated using `auto_call` only.
|
|
128
|
+
# If False, magicgui passes these as args to root() when the root's
|
|
129
|
+
# function runs. But that doesn't list them as args of its function
|
|
130
|
+
widget.gui_only = True
|
|
131
|
+
root.insert(root.index(insertion) + 1, widget)
|
|
132
|
+
getattr(root, widget.name).label = new_name
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def restore_options_defaults(widget: FunctionGui) -> None:
|
|
136
|
+
"""
|
|
137
|
+
Restore default widget values.
|
|
138
|
+
"""
|
|
139
|
+
defaults = {
|
|
140
|
+
**DataInputs.defaults(),
|
|
141
|
+
**DetectionInputs.defaults(),
|
|
142
|
+
**ClassificationInputs.defaults(),
|
|
143
|
+
**MiscInputs.defaults(),
|
|
144
|
+
}
|
|
145
|
+
for name, value in defaults.items():
|
|
146
|
+
if value is not None: # ignore fields with no default
|
|
147
|
+
getattr(widget, name).value = value
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def get_results_callback(
|
|
151
|
+
skip_classification: bool, viewer: napari.Viewer
|
|
152
|
+
) -> Callable:
|
|
153
|
+
"""
|
|
154
|
+
Returns the callback that is connected to output of the pipeline.
|
|
155
|
+
It returns the detected points that we have to visualize.
|
|
156
|
+
"""
|
|
157
|
+
if skip_classification:
|
|
158
|
+
# after detection w/o classification, everything is unknown
|
|
159
|
+
def done_func(points):
|
|
160
|
+
add_single_layer(
|
|
161
|
+
points,
|
|
162
|
+
viewer=viewer,
|
|
163
|
+
name="Cell candidates",
|
|
164
|
+
cell_type=Cell.UNKNOWN,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
else:
|
|
168
|
+
# after classification we have either cell or unknown
|
|
169
|
+
def done_func(points):
|
|
170
|
+
add_classified_layers(
|
|
171
|
+
points,
|
|
172
|
+
viewer=viewer,
|
|
173
|
+
unknown_name="Rejected",
|
|
174
|
+
cell_name="Detected",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
return done_func
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def find_local_planes(
|
|
181
|
+
viewer: napari.Viewer,
|
|
182
|
+
voxel_size_z: float,
|
|
183
|
+
signal_image: napari.layers.Image,
|
|
184
|
+
) -> Tuple[int, int]:
|
|
185
|
+
"""
|
|
186
|
+
When detecting only locally, it returns the start and end planes to use.
|
|
187
|
+
"""
|
|
188
|
+
current_plane = viewer.dims.current_step[0]
|
|
189
|
+
|
|
190
|
+
# so a reasonable number of cells in the plane are detected
|
|
191
|
+
planes_needed = MIN_PLANES_ANALYSE + int(
|
|
192
|
+
ceil((CUBE_DEPTH * NETWORK_VOXEL_SIZES[0]) / voxel_size_z)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
start_plane, end_plane = get_cube_depth_min_max(
|
|
196
|
+
current_plane, planes_needed
|
|
197
|
+
)
|
|
198
|
+
start_plane = max(0, start_plane)
|
|
199
|
+
end_plane = min(len(signal_image.data), end_plane)
|
|
200
|
+
|
|
201
|
+
return start_plane, end_plane
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def reraise(e: Exception) -> None:
|
|
205
|
+
"""Re-raises the exception."""
|
|
206
|
+
raise Exception from e
|
|
207
|
+
|
|
208
|
+
|
|
36
209
|
def detect_widget() -> FunctionGui:
|
|
37
210
|
"""
|
|
38
211
|
Create a detection plugin GUI.
|
|
39
212
|
"""
|
|
40
213
|
progress_bar = ProgressBar()
|
|
41
214
|
|
|
215
|
+
# options that is filled in from the gui
|
|
216
|
+
options = {
|
|
217
|
+
"signal_image": None,
|
|
218
|
+
"background_image": None,
|
|
219
|
+
"viewer": None,
|
|
220
|
+
"cell_layer": None,
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
signal_image_opt, background_image_opt, cell_layer_opt = get_heavy_widgets(
|
|
224
|
+
options
|
|
225
|
+
)
|
|
226
|
+
|
|
42
227
|
@magicgui(
|
|
43
|
-
header=header_label_widget,
|
|
44
228
|
detection_label=html_label_widget("Cell detection", tag="h3"),
|
|
45
229
|
**DataInputs.widget_representation(),
|
|
46
230
|
**DetectionInputs.widget_representation(),
|
|
@@ -52,16 +236,13 @@ def detect_widget() -> FunctionGui:
|
|
|
52
236
|
scrollable=True,
|
|
53
237
|
)
|
|
54
238
|
def widget(
|
|
55
|
-
header,
|
|
56
239
|
detection_label,
|
|
57
240
|
data_options,
|
|
58
|
-
viewer: napari.Viewer,
|
|
59
|
-
signal_image: napari.layers.Image,
|
|
60
|
-
background_image: napari.layers.Image,
|
|
61
241
|
voxel_size_z: float,
|
|
62
242
|
voxel_size_y: float,
|
|
63
243
|
voxel_size_x: float,
|
|
64
244
|
detection_options,
|
|
245
|
+
skip_detection: bool,
|
|
65
246
|
soma_diameter: float,
|
|
66
247
|
ball_xy_size: float,
|
|
67
248
|
ball_z_size: float,
|
|
@@ -71,6 +252,7 @@ def detect_widget() -> FunctionGui:
|
|
|
71
252
|
soma_spread_factor: float,
|
|
72
253
|
max_cluster_size: int,
|
|
73
254
|
classification_options,
|
|
255
|
+
skip_classification: bool,
|
|
74
256
|
trained_model: Optional[Path],
|
|
75
257
|
use_pre_trained_weights: bool,
|
|
76
258
|
misc_options,
|
|
@@ -86,16 +268,16 @@ def detect_widget() -> FunctionGui:
|
|
|
86
268
|
|
|
87
269
|
Parameters
|
|
88
270
|
----------
|
|
89
|
-
signal_image : napari.layers.Image
|
|
90
|
-
Image layer containing the labelled cells
|
|
91
|
-
background_image : napari.layers.Image
|
|
92
|
-
Image layer without labelled cells
|
|
93
271
|
voxel_size_z : float
|
|
94
272
|
Size of your voxels in the axial dimension
|
|
95
273
|
voxel_size_y : float
|
|
96
274
|
Size of your voxels in the y direction (top to bottom)
|
|
97
275
|
voxel_size_x : float
|
|
98
276
|
Size of your voxels in the x direction (left to right)
|
|
277
|
+
skip_detection : bool
|
|
278
|
+
If selected, the detection step is skipped and instead we get the
|
|
279
|
+
detected cells from the cell layer below (from a previous
|
|
280
|
+
detection run or import)
|
|
99
281
|
soma_diameter : float
|
|
100
282
|
The expected in-plane soma diameter (microns)
|
|
101
283
|
ball_xy_size : float
|
|
@@ -116,6 +298,9 @@ def detect_widget() -> FunctionGui:
|
|
|
116
298
|
should be attempted
|
|
117
299
|
use_pre_trained_weights : bool
|
|
118
300
|
Select to use pre-trained model weights
|
|
301
|
+
skip_classification : bool
|
|
302
|
+
If selected, the classification step is skipped and all cells from
|
|
303
|
+
the detection stage are added
|
|
119
304
|
trained_model : Optional[Path]
|
|
120
305
|
Trained model file path (home directory (default) -> pretrained
|
|
121
306
|
weights)
|
|
@@ -132,18 +317,48 @@ def detect_widget() -> FunctionGui:
|
|
|
132
317
|
reset_button :
|
|
133
318
|
Reset parameters to default
|
|
134
319
|
"""
|
|
135
|
-
|
|
320
|
+
# we must manually call so that the parameters of these functions are
|
|
321
|
+
# initialized and updated. Because, if the images are open in napari
|
|
322
|
+
# before we open cellfinder, then these functions may never be called,
|
|
323
|
+
# even though the image filenames are shown properly in the parameters
|
|
324
|
+
# in the gui. Likely auto_call doesn't make magicgui call the functions
|
|
325
|
+
# in this circumstance, only if the parameters are updated once
|
|
326
|
+
# cellfinder plugin is fully open and initialized
|
|
327
|
+
signal_image_opt()
|
|
328
|
+
background_image_opt()
|
|
329
|
+
cell_layer_opt()
|
|
330
|
+
|
|
331
|
+
signal_image = options["signal_image"]
|
|
332
|
+
|
|
333
|
+
if signal_image is None or options["background_image"] is None:
|
|
136
334
|
show_info("Both signal and background images must be specified.")
|
|
137
335
|
return
|
|
336
|
+
|
|
337
|
+
detected_cells = []
|
|
338
|
+
if skip_detection:
|
|
339
|
+
if options["cell_layer"] is None:
|
|
340
|
+
show_info(
|
|
341
|
+
"Skip detection selected, but no existing cell layer "
|
|
342
|
+
"is selected."
|
|
343
|
+
)
|
|
344
|
+
return
|
|
345
|
+
|
|
346
|
+
# set cells as unknown so that classification will process them
|
|
347
|
+
detected_cells = napari_array_to_cells(
|
|
348
|
+
options["cell_layer"], Cell.UNKNOWN
|
|
349
|
+
)
|
|
350
|
+
|
|
138
351
|
data_inputs = DataInputs(
|
|
139
352
|
signal_image.data,
|
|
140
|
-
background_image.data,
|
|
353
|
+
options["background_image"].data,
|
|
141
354
|
voxel_size_z,
|
|
142
355
|
voxel_size_y,
|
|
143
356
|
voxel_size_x,
|
|
144
357
|
)
|
|
145
358
|
|
|
146
359
|
detection_inputs = DetectionInputs(
|
|
360
|
+
skip_detection,
|
|
361
|
+
detected_cells,
|
|
147
362
|
soma_diameter,
|
|
148
363
|
ball_xy_size,
|
|
149
364
|
ball_z_size,
|
|
@@ -157,24 +372,15 @@ def detect_widget() -> FunctionGui:
|
|
|
157
372
|
if use_pre_trained_weights:
|
|
158
373
|
trained_model = None
|
|
159
374
|
classification_inputs = ClassificationInputs(
|
|
160
|
-
use_pre_trained_weights, trained_model
|
|
375
|
+
skip_classification, use_pre_trained_weights, trained_model
|
|
161
376
|
)
|
|
162
377
|
|
|
163
|
-
end_plane = len(signal_image.data) if end_plane == 0 else end_plane
|
|
164
|
-
|
|
165
378
|
if analyse_local:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
# so a reasonable number of cells in the plane are detected
|
|
169
|
-
planes_needed = MIN_PLANES_ANALYSE + int(
|
|
170
|
-
ceil((CUBE_DEPTH * NETWORK_VOXEL_SIZES[0]) / voxel_size_z)
|
|
379
|
+
start_plane, end_plane = find_local_planes(
|
|
380
|
+
options["viewer"], voxel_size_z, signal_image
|
|
171
381
|
)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
current_plane, planes_needed
|
|
175
|
-
)
|
|
176
|
-
start_plane = max(0, start_plane)
|
|
177
|
-
end_plane = min(len(signal_image.data), end_plane)
|
|
382
|
+
elif not end_plane:
|
|
383
|
+
end_plane = len(signal_image.data)
|
|
178
384
|
|
|
179
385
|
misc_inputs = MiscInputs(
|
|
180
386
|
start_plane, end_plane, n_free_cpus, analyse_local, debug
|
|
@@ -186,45 +392,34 @@ def detect_widget() -> FunctionGui:
|
|
|
186
392
|
classification_inputs,
|
|
187
393
|
misc_inputs,
|
|
188
394
|
)
|
|
395
|
+
|
|
189
396
|
worker.returned.connect(
|
|
190
|
-
|
|
397
|
+
get_results_callback(skip_classification, options["viewer"])
|
|
191
398
|
)
|
|
192
|
-
|
|
193
399
|
# Make sure if the worker emits an error, it is propagated to this
|
|
194
400
|
# thread
|
|
195
|
-
def reraise(e):
|
|
196
|
-
raise Exception from e
|
|
197
|
-
|
|
198
401
|
worker.errored.connect(reraise)
|
|
402
|
+
worker.connect_progress_bar_callback(progress_bar)
|
|
199
403
|
|
|
200
|
-
def update_progress_bar(label: str, max: int, value: int):
|
|
201
|
-
progress_bar.label = label
|
|
202
|
-
progress_bar.max = max
|
|
203
|
-
progress_bar.value = value
|
|
204
|
-
|
|
205
|
-
worker.update_progress_bar.connect(update_progress_bar)
|
|
206
404
|
worker.start()
|
|
207
405
|
|
|
208
|
-
widget.
|
|
209
|
-
widget.header.native.setOpenExternalLinks(True)
|
|
406
|
+
widget.native.layout().insertWidget(0, cellfinder_header())
|
|
210
407
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
"""
|
|
216
|
-
defaults = {
|
|
217
|
-
**DataInputs.defaults(),
|
|
218
|
-
**DetectionInputs.defaults(),
|
|
219
|
-
**ClassificationInputs.defaults(),
|
|
220
|
-
**MiscInputs.defaults(),
|
|
221
|
-
}
|
|
222
|
-
for name, value in defaults.items():
|
|
223
|
-
if value is not None: # ignore fields with no default
|
|
224
|
-
getattr(widget, name).value = value
|
|
408
|
+
# reset restores defaults
|
|
409
|
+
widget.reset_button.changed.connect(
|
|
410
|
+
partial(restore_options_defaults, widget)
|
|
411
|
+
)
|
|
225
412
|
|
|
226
413
|
# Insert progress bar before the run and reset buttons
|
|
227
|
-
widget.insert(
|
|
414
|
+
widget.insert(widget.index("debug") + 1, progress_bar)
|
|
415
|
+
|
|
416
|
+
# add the signal and background image etc.
|
|
417
|
+
add_heavy_widgets(
|
|
418
|
+
widget,
|
|
419
|
+
(background_image_opt, signal_image_opt, cell_layer_opt),
|
|
420
|
+
("Background image", "Signal image", "Candidate cell layer"),
|
|
421
|
+
("voxel_size_z", "voxel_size_z", "soma_diameter"),
|
|
422
|
+
)
|
|
228
423
|
|
|
229
424
|
scroll = QScrollArea()
|
|
230
425
|
scroll.setWidget(widget._widget._qwidget)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import List, Optional
|
|
4
4
|
|
|
5
5
|
import numpy
|
|
6
|
+
from brainglobe_utils.cells.cells import Cell
|
|
6
7
|
|
|
7
8
|
from cellfinder.napari.input_container import InputContainer
|
|
8
9
|
from cellfinder.napari.utils import html_label_widget
|
|
@@ -59,6 +60,8 @@ class DataInputs(InputContainer):
|
|
|
59
60
|
class DetectionInputs(InputContainer):
|
|
60
61
|
"""Container for cell candidate detection inputs."""
|
|
61
62
|
|
|
63
|
+
skip_detection: bool = False
|
|
64
|
+
detected_cells: Optional[List[Cell]] = None
|
|
62
65
|
soma_diameter: float = 16.0
|
|
63
66
|
ball_xy_size: float = 6
|
|
64
67
|
ball_z_size: float = 15
|
|
@@ -75,6 +78,7 @@ class DetectionInputs(InputContainer):
|
|
|
75
78
|
def widget_representation(cls) -> dict:
|
|
76
79
|
return dict(
|
|
77
80
|
detection_options=html_label_widget("Detection:"),
|
|
81
|
+
skip_detection=dict(value=cls.defaults()["skip_detection"]),
|
|
78
82
|
soma_diameter=cls._custom_widget("soma_diameter"),
|
|
79
83
|
ball_xy_size=cls._custom_widget(
|
|
80
84
|
"ball_xy_size", custom_label="Ball filter (xy)"
|
|
@@ -107,6 +111,7 @@ class DetectionInputs(InputContainer):
|
|
|
107
111
|
class ClassificationInputs(InputContainer):
|
|
108
112
|
"""Container for classification inputs."""
|
|
109
113
|
|
|
114
|
+
skip_classification: bool = False
|
|
110
115
|
use_pre_trained_weights: bool = True
|
|
111
116
|
trained_model: Optional[Path] = Path.home()
|
|
112
117
|
|
|
@@ -123,6 +128,9 @@ class ClassificationInputs(InputContainer):
|
|
|
123
128
|
value=cls.defaults()["use_pre_trained_weights"]
|
|
124
129
|
),
|
|
125
130
|
trained_model=dict(value=cls.defaults()["trained_model"]),
|
|
131
|
+
skip_classification=dict(
|
|
132
|
+
value=cls.defaults()["skip_classification"]
|
|
133
|
+
),
|
|
126
134
|
)
|
|
127
135
|
|
|
128
136
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from magicgui.widgets import ProgressBar
|
|
1
2
|
from napari.qt.threading import WorkerBase, WorkerBaseSignals
|
|
2
3
|
from qtpy.QtCore import Signal
|
|
3
4
|
|
|
@@ -41,6 +42,19 @@ class Worker(WorkerBase):
|
|
|
41
42
|
self.classification_inputs = classification_inputs
|
|
42
43
|
self.misc_inputs = misc_inputs
|
|
43
44
|
|
|
45
|
+
def connect_progress_bar_callback(self, progress_bar: ProgressBar):
|
|
46
|
+
"""
|
|
47
|
+
Connects the progress bar to the work so that updates are shown on
|
|
48
|
+
the bar.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def update_progress_bar(label: str, max: int, value: int):
|
|
52
|
+
progress_bar.label = label
|
|
53
|
+
progress_bar.max = max
|
|
54
|
+
progress_bar.value = value
|
|
55
|
+
|
|
56
|
+
self.update_progress_bar.connect(update_progress_bar)
|
|
57
|
+
|
|
44
58
|
def work(self) -> list:
|
|
45
59
|
self.update_progress_bar.emit("Setting up detection...", 1, 0)
|
|
46
60
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
cellfinder/napari/train/train.py
CHANGED
|
@@ -8,11 +8,7 @@ from napari.utils.notifications import show_info
|
|
|
8
8
|
from qtpy.QtWidgets import QScrollArea
|
|
9
9
|
|
|
10
10
|
from cellfinder.core.train.train_yml import run as train_yml
|
|
11
|
-
from cellfinder.napari.utils import
|
|
12
|
-
header_label_widget,
|
|
13
|
-
html_label_widget,
|
|
14
|
-
widget_header,
|
|
15
|
-
)
|
|
11
|
+
from cellfinder.napari.utils import cellfinder_header, html_label_widget
|
|
16
12
|
|
|
17
13
|
from .train_containers import (
|
|
18
14
|
MiscTrainingInputs,
|
|
@@ -41,7 +37,6 @@ def run_training(
|
|
|
41
37
|
|
|
42
38
|
def training_widget() -> FunctionGui:
|
|
43
39
|
@magicgui(
|
|
44
|
-
header=header_label_widget,
|
|
45
40
|
training_label=html_label_widget("Network training", tag="h3"),
|
|
46
41
|
**TrainingDataInputs.widget_representation(),
|
|
47
42
|
**OptionalNetworkInputs.widget_representation(),
|
|
@@ -52,7 +47,6 @@ def training_widget() -> FunctionGui:
|
|
|
52
47
|
scrollable=True,
|
|
53
48
|
)
|
|
54
49
|
def widget(
|
|
55
|
-
header: dict,
|
|
56
50
|
training_label: dict,
|
|
57
51
|
data_options: dict,
|
|
58
52
|
yaml_files: Path,
|
|
@@ -161,8 +155,7 @@ def training_widget() -> FunctionGui:
|
|
|
161
155
|
)
|
|
162
156
|
worker.start()
|
|
163
157
|
|
|
164
|
-
widget.
|
|
165
|
-
widget.header.native.setOpenExternalLinks(True)
|
|
158
|
+
widget.native.layout().insertWidget(0, cellfinder_header())
|
|
166
159
|
|
|
167
160
|
@widget.reset_button.changed.connect
|
|
168
161
|
def restore_defaults():
|
|
@@ -4,7 +4,7 @@ from typing import Optional
|
|
|
4
4
|
|
|
5
5
|
from magicgui.types import FileDialogMode
|
|
6
6
|
|
|
7
|
-
from cellfinder.core.download.
|
|
7
|
+
from cellfinder.core.download.download import model_filenames
|
|
8
8
|
from cellfinder.core.train.train_yml import models
|
|
9
9
|
from cellfinder.napari.input_container import InputContainer
|
|
10
10
|
from cellfinder.napari.utils import html_label_widget
|
|
@@ -46,7 +46,7 @@ class OptionalNetworkInputs(InputContainer):
|
|
|
46
46
|
trained_model: Optional[Path] = Path.home()
|
|
47
47
|
model_weights: Optional[Path] = Path.home()
|
|
48
48
|
model_depth: str = list(models.keys())[2]
|
|
49
|
-
pretrained_model: str = str(list(
|
|
49
|
+
pretrained_model: str = str(list(model_filenames.keys())[0])
|
|
50
50
|
|
|
51
51
|
def as_core_arguments(self) -> dict:
|
|
52
52
|
arguments = super().as_core_arguments()
|
|
@@ -65,7 +65,7 @@ class OptionalNetworkInputs(InputContainer):
|
|
|
65
65
|
),
|
|
66
66
|
pretrained_model=cls._custom_widget(
|
|
67
67
|
"pretrained_model",
|
|
68
|
-
choices=list(
|
|
68
|
+
choices=list(model_filenames.keys()),
|
|
69
69
|
),
|
|
70
70
|
)
|
|
71
71
|
|