cellfinder 1.4.1__py3-none-any.whl → 1.9.0__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/cli_migration_warning.py +3 -1
- cellfinder/core/classify/classify.py +51 -5
- cellfinder/core/classify/tools.py +13 -3
- cellfinder/core/detect/detect.py +94 -59
- cellfinder/core/detect/filters/plane/plane_filter.py +107 -10
- cellfinder/core/detect/filters/setup_filters.py +51 -12
- cellfinder/core/detect/filters/volume/ball_filter.py +5 -5
- cellfinder/core/detect/filters/volume/structure_detection.py +5 -0
- cellfinder/core/detect/filters/volume/structure_splitting.py +3 -2
- cellfinder/core/detect/filters/volume/volume_filter.py +1 -1
- cellfinder/core/download/download.py +2 -1
- cellfinder/core/main.py +162 -30
- cellfinder/core/tools/threading.py +4 -3
- cellfinder/core/tools/tools.py +1 -1
- cellfinder/core/train/{train_yml.py → train_yaml.py} +6 -15
- cellfinder/napari/curation.py +72 -21
- cellfinder/napari/detect/detect.py +87 -28
- cellfinder/napari/detect/detect_containers.py +41 -9
- cellfinder/napari/detect/thread_worker.py +26 -16
- cellfinder/napari/input_container.py +14 -4
- cellfinder/napari/train/train.py +5 -9
- cellfinder/napari/train/train_containers.py +2 -4
- cellfinder/napari/utils.py +6 -1
- {cellfinder-1.4.1.dist-info → cellfinder-1.9.0.dist-info}/METADATA +16 -12
- {cellfinder-1.4.1.dist-info → cellfinder-1.9.0.dist-info}/RECORD +29 -29
- {cellfinder-1.4.1.dist-info → cellfinder-1.9.0.dist-info}/WHEEL +1 -1
- {cellfinder-1.4.1.dist-info → cellfinder-1.9.0.dist-info}/entry_points.txt +1 -1
- {cellfinder-1.4.1.dist-info → cellfinder-1.9.0.dist-info/licenses}/LICENSE +0 -0
- {cellfinder-1.4.1.dist-info → cellfinder-1.9.0.dist-info}/top_level.txt +0 -0
|
@@ -148,7 +148,7 @@ def restore_options_defaults(widget: FunctionGui) -> None:
|
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
def get_results_callback(
|
|
151
|
-
skip_classification: bool, viewer: napari.Viewer
|
|
151
|
+
skip_classification: bool, viewer: napari.Viewer, scale
|
|
152
152
|
) -> Callable:
|
|
153
153
|
"""
|
|
154
154
|
Returns the callback that is connected to output of the pipeline.
|
|
@@ -162,6 +162,7 @@ def get_results_callback(
|
|
|
162
162
|
viewer=viewer,
|
|
163
163
|
name="Cell candidates",
|
|
164
164
|
cell_type=Cell.UNKNOWN,
|
|
165
|
+
scale=scale,
|
|
165
166
|
)
|
|
166
167
|
|
|
167
168
|
else:
|
|
@@ -172,6 +173,7 @@ def get_results_callback(
|
|
|
172
173
|
viewer=viewer,
|
|
173
174
|
unknown_name="Rejected",
|
|
174
175
|
cell_name="Detected",
|
|
176
|
+
scale=scale,
|
|
175
177
|
)
|
|
176
178
|
|
|
177
179
|
return done_func
|
|
@@ -244,23 +246,28 @@ def detect_widget() -> FunctionGui:
|
|
|
244
246
|
detection_options,
|
|
245
247
|
skip_detection: bool,
|
|
246
248
|
soma_diameter: float,
|
|
249
|
+
log_sigma_size: float,
|
|
250
|
+
n_sds_above_mean_thresh: float,
|
|
251
|
+
n_sds_above_mean_tiled_thresh: float,
|
|
252
|
+
tiled_thresh_tile_size: float,
|
|
247
253
|
ball_xy_size: float,
|
|
248
254
|
ball_z_size: float,
|
|
249
255
|
ball_overlap_fraction: float,
|
|
250
|
-
|
|
251
|
-
n_sds_above_mean_thresh: int,
|
|
256
|
+
detection_batch_size: int,
|
|
252
257
|
soma_spread_factor: float,
|
|
253
|
-
max_cluster_size:
|
|
258
|
+
max_cluster_size: float,
|
|
254
259
|
classification_options,
|
|
255
260
|
skip_classification: bool,
|
|
256
261
|
use_pre_trained_weights: bool,
|
|
257
262
|
trained_model: Optional[Path],
|
|
258
|
-
|
|
263
|
+
classification_batch_size: int,
|
|
259
264
|
misc_options,
|
|
260
265
|
start_plane: int,
|
|
261
266
|
end_plane: int,
|
|
262
267
|
n_free_cpus: int,
|
|
263
268
|
analyse_local: bool,
|
|
269
|
+
use_gpu: bool,
|
|
270
|
+
pin_memory: bool,
|
|
264
271
|
debug: bool,
|
|
265
272
|
reset_button,
|
|
266
273
|
) -> None:
|
|
@@ -270,43 +277,74 @@ def detect_widget() -> FunctionGui:
|
|
|
270
277
|
Parameters
|
|
271
278
|
----------
|
|
272
279
|
voxel_size_z : float
|
|
273
|
-
Size of your voxels in the axial dimension
|
|
280
|
+
Size of your voxels in the axial dimension (microns)
|
|
274
281
|
voxel_size_y : float
|
|
275
|
-
Size of your voxels in the y direction (top to bottom)
|
|
282
|
+
Size of your voxels in the y direction (top to bottom) (microns)
|
|
276
283
|
voxel_size_x : float
|
|
277
|
-
Size of your voxels in the x direction (left to right)
|
|
284
|
+
Size of your voxels in the x direction (left to right) (microns)
|
|
278
285
|
skip_detection : bool
|
|
279
286
|
If selected, the detection step is skipped and instead we get the
|
|
280
287
|
detected cells from the cell layer below (from a previous
|
|
281
288
|
detection run or import)
|
|
282
289
|
soma_diameter : float
|
|
283
|
-
The expected in-plane soma diameter (microns)
|
|
290
|
+
The expected in-plane (xy) soma diameter (microns)
|
|
291
|
+
log_sigma_size : float
|
|
292
|
+
Gaussian filter width (as a fraction of soma diameter) used during
|
|
293
|
+
2d in-plane Laplacian of Gaussian filtering
|
|
294
|
+
n_sds_above_mean_thresh : float
|
|
295
|
+
Per-plane intensity threshold (the number of standard deviations
|
|
296
|
+
above the mean) of the filtered 2d planes used to mark pixels as
|
|
297
|
+
foreground or background
|
|
298
|
+
n_sds_above_mean_tiled_thresh : float
|
|
299
|
+
Per-plane, per-tile intensity threshold (the number of standard
|
|
300
|
+
deviations above the mean) for the filtered 2d planes used to mark
|
|
301
|
+
pixels as foreground or background. When used, (tile size is not
|
|
302
|
+
zero) a pixel is marked as foreground if its intensity is above
|
|
303
|
+
both the per-plane and per-tile threshold. I.e. it's above the set
|
|
304
|
+
number of standard deviations of the per-plane average and of the
|
|
305
|
+
per-plane per-tile average for the tile that contains it.
|
|
306
|
+
tiled_thresh_tile_size : float
|
|
307
|
+
The tile size used to tile the x, y plane to calculate the local
|
|
308
|
+
average intensity for the tiled threshold. The value is multiplied
|
|
309
|
+
by soma diameter (i.e. 1 means one soma diameter). If zero, the
|
|
310
|
+
tiled threshold is disabled and only the per-plane threshold is
|
|
311
|
+
used. Tiling is done with 50% overlap when striding.
|
|
284
312
|
ball_xy_size : float
|
|
285
|
-
|
|
313
|
+
3d filter's in-plane (xy) filter ball size (microns)
|
|
286
314
|
ball_z_size : float
|
|
287
|
-
|
|
315
|
+
3d filter's axial (z) filter ball size (microns)
|
|
288
316
|
ball_overlap_fraction : float
|
|
289
|
-
|
|
290
|
-
to retain
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
317
|
+
3d filter's fraction of the ball filter needed to be filled by
|
|
318
|
+
foreground voxels, centered on a voxel, to retain the voxel
|
|
319
|
+
detection_batch_size: int
|
|
320
|
+
The number of planes of the original data volume to process at
|
|
321
|
+
once. The GPU/CPU memory must be able to contain this many planes
|
|
322
|
+
for all the filters. For performance-critical applications, tune
|
|
323
|
+
to maximize memory usage without
|
|
324
|
+
running out. Check your GPU/CPU memory to verify it's not full
|
|
295
325
|
soma_spread_factor : float
|
|
296
|
-
Cell spread factor
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
326
|
+
Cell spread factor for determining the largest cell volume before
|
|
327
|
+
splitting up cell clusters. Structures with spherical volume of
|
|
328
|
+
diameter `soma_spread_factor * soma_diameter` or less will not be
|
|
329
|
+
split
|
|
330
|
+
max_cluster_size : float
|
|
331
|
+
Largest detected cell cluster (in cubic um) where splitting
|
|
332
|
+
should be attempted. Clusters above this size will be labeled
|
|
333
|
+
as artifacts
|
|
304
334
|
skip_classification : bool
|
|
305
335
|
If selected, the classification step is skipped and all cells from
|
|
306
336
|
the detection stage are added
|
|
337
|
+
use_pre_trained_weights : bool
|
|
338
|
+
Select to use pre-trained model weights
|
|
307
339
|
trained_model : Optional[Path]
|
|
308
340
|
Trained model file path (home directory (default) -> pretrained
|
|
309
341
|
weights)
|
|
342
|
+
classification_batch_size : int
|
|
343
|
+
How many potential cells to classify at one time. The GPU/CPU
|
|
344
|
+
memory must be able to contain at once this many data cubes for
|
|
345
|
+
the models. For performance-critical applications, tune to
|
|
346
|
+
maximize memory usage without running
|
|
347
|
+
out. Check your GPU/CPU memory to verify it's not full
|
|
310
348
|
start_plane : int
|
|
311
349
|
First plane to process (to process a subset of the data)
|
|
312
350
|
end_plane : int
|
|
@@ -315,6 +353,14 @@ def detect_widget() -> FunctionGui:
|
|
|
315
353
|
How many CPU cores to leave free
|
|
316
354
|
analyse_local : bool
|
|
317
355
|
Only analyse planes around the current position
|
|
356
|
+
use_gpu : bool
|
|
357
|
+
If True, use GPU for processing (if available); otherwise, use CPU.
|
|
358
|
+
pin_memory: bool
|
|
359
|
+
Pins data to be sent to the GPU to the CPU memory. This allows
|
|
360
|
+
faster GPU data speeds, but can only be used if the data used by
|
|
361
|
+
the GPU can stay in the CPU RAM while the GPU uses it. I.e. there's
|
|
362
|
+
enough RAM. Otherwise, if there's a risk of the RAM being paged, it
|
|
363
|
+
shouldn't be used. Defaults to False.
|
|
318
364
|
debug : bool
|
|
319
365
|
Increase logging
|
|
320
366
|
reset_button :
|
|
@@ -368,8 +414,11 @@ def detect_widget() -> FunctionGui:
|
|
|
368
414
|
ball_overlap_fraction,
|
|
369
415
|
log_sigma_size,
|
|
370
416
|
n_sds_above_mean_thresh,
|
|
417
|
+
n_sds_above_mean_tiled_thresh,
|
|
418
|
+
tiled_thresh_tile_size,
|
|
371
419
|
soma_spread_factor,
|
|
372
420
|
max_cluster_size,
|
|
421
|
+
detection_batch_size,
|
|
373
422
|
)
|
|
374
423
|
|
|
375
424
|
if use_pre_trained_weights:
|
|
@@ -378,7 +427,7 @@ def detect_widget() -> FunctionGui:
|
|
|
378
427
|
skip_classification,
|
|
379
428
|
use_pre_trained_weights,
|
|
380
429
|
trained_model,
|
|
381
|
-
|
|
430
|
+
classification_batch_size,
|
|
382
431
|
)
|
|
383
432
|
|
|
384
433
|
if analyse_local:
|
|
@@ -389,7 +438,13 @@ def detect_widget() -> FunctionGui:
|
|
|
389
438
|
end_plane = len(signal_image.data)
|
|
390
439
|
|
|
391
440
|
misc_inputs = MiscInputs(
|
|
392
|
-
start_plane,
|
|
441
|
+
start_plane,
|
|
442
|
+
end_plane,
|
|
443
|
+
n_free_cpus,
|
|
444
|
+
analyse_local,
|
|
445
|
+
use_gpu,
|
|
446
|
+
pin_memory,
|
|
447
|
+
debug,
|
|
393
448
|
)
|
|
394
449
|
|
|
395
450
|
worker = Worker(
|
|
@@ -400,7 +455,11 @@ def detect_widget() -> FunctionGui:
|
|
|
400
455
|
)
|
|
401
456
|
|
|
402
457
|
worker.returned.connect(
|
|
403
|
-
get_results_callback(
|
|
458
|
+
get_results_callback(
|
|
459
|
+
skip_classification,
|
|
460
|
+
options["viewer"],
|
|
461
|
+
options["signal_image"].scale,
|
|
462
|
+
)
|
|
404
463
|
)
|
|
405
464
|
# Make sure if the worker emits an error, it is propagated to this
|
|
406
465
|
# thread
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from typing import List, Optional
|
|
4
4
|
|
|
5
5
|
import numpy
|
|
6
|
+
import torch
|
|
6
7
|
from brainglobe_utils.cells.cells import Cell
|
|
7
8
|
|
|
8
9
|
from cellfinder.napari.input_container import InputContainer
|
|
@@ -30,7 +31,7 @@ class DataInputs(InputContainer):
|
|
|
30
31
|
self.voxel_size_x,
|
|
31
32
|
)
|
|
32
33
|
# del operator doesn't affect self, because asdict creates a copy of
|
|
33
|
-
#
|
|
34
|
+
# the dict.
|
|
34
35
|
del data_input_dict["voxel_size_z"]
|
|
35
36
|
del data_input_dict["voxel_size_y"]
|
|
36
37
|
del data_input_dict["voxel_size_x"]
|
|
@@ -67,9 +68,12 @@ class DetectionInputs(InputContainer):
|
|
|
67
68
|
ball_z_size: float = 15
|
|
68
69
|
ball_overlap_fraction: float = 0.6
|
|
69
70
|
log_sigma_size: float = 0.2
|
|
70
|
-
n_sds_above_mean_thresh:
|
|
71
|
+
n_sds_above_mean_thresh: float = 10
|
|
72
|
+
n_sds_above_mean_tiled_thresh: float = 10
|
|
73
|
+
tiled_thresh_tile_size: float = 0
|
|
71
74
|
soma_spread_factor: float = 1.4
|
|
72
|
-
max_cluster_size:
|
|
75
|
+
max_cluster_size: float = 100000
|
|
76
|
+
detection_batch_size: int = 1
|
|
73
77
|
|
|
74
78
|
def as_core_arguments(self) -> dict:
|
|
75
79
|
return super().as_core_arguments()
|
|
@@ -93,17 +97,27 @@ class DetectionInputs(InputContainer):
|
|
|
93
97
|
"log_sigma_size", custom_label="Filter width"
|
|
94
98
|
),
|
|
95
99
|
n_sds_above_mean_thresh=cls._custom_widget(
|
|
96
|
-
"n_sds_above_mean_thresh", custom_label="
|
|
100
|
+
"n_sds_above_mean_thresh", custom_label="Plane threshold"
|
|
101
|
+
),
|
|
102
|
+
n_sds_above_mean_tiled_thresh=cls._custom_widget(
|
|
103
|
+
"n_sds_above_mean_tiled_thresh", custom_label="Tiled threshold"
|
|
104
|
+
),
|
|
105
|
+
tiled_thresh_tile_size=cls._custom_widget(
|
|
106
|
+
"tiled_thresh_tile_size",
|
|
107
|
+
custom_label="Thresholding tile size",
|
|
97
108
|
),
|
|
98
109
|
soma_spread_factor=cls._custom_widget(
|
|
99
|
-
"soma_spread_factor", custom_label="
|
|
110
|
+
"soma_spread_factor", custom_label="Split cell spread"
|
|
100
111
|
),
|
|
101
112
|
max_cluster_size=cls._custom_widget(
|
|
102
113
|
"max_cluster_size",
|
|
103
|
-
custom_label="
|
|
114
|
+
custom_label="Split max cluster",
|
|
104
115
|
min=0,
|
|
105
116
|
max=10000000,
|
|
106
117
|
),
|
|
118
|
+
detection_batch_size=cls._custom_widget(
|
|
119
|
+
"detection_batch_size", custom_label="Batch size (detection)"
|
|
120
|
+
),
|
|
107
121
|
)
|
|
108
122
|
|
|
109
123
|
|
|
@@ -114,7 +128,7 @@ class ClassificationInputs(InputContainer):
|
|
|
114
128
|
skip_classification: bool = False
|
|
115
129
|
use_pre_trained_weights: bool = True
|
|
116
130
|
trained_model: Optional[Path] = Path.home()
|
|
117
|
-
|
|
131
|
+
classification_batch_size: int = 64
|
|
118
132
|
|
|
119
133
|
def as_core_arguments(self) -> dict:
|
|
120
134
|
args = super().as_core_arguments()
|
|
@@ -132,7 +146,10 @@ class ClassificationInputs(InputContainer):
|
|
|
132
146
|
skip_classification=dict(
|
|
133
147
|
value=cls.defaults()["skip_classification"]
|
|
134
148
|
),
|
|
135
|
-
|
|
149
|
+
classification_batch_size=dict(
|
|
150
|
+
value=cls.defaults()["classification_batch_size"],
|
|
151
|
+
label="Batch size (classification)",
|
|
152
|
+
),
|
|
136
153
|
)
|
|
137
154
|
|
|
138
155
|
|
|
@@ -144,10 +161,14 @@ class MiscInputs(InputContainer):
|
|
|
144
161
|
end_plane: int = 0
|
|
145
162
|
n_free_cpus: int = 2
|
|
146
163
|
analyse_local: bool = False
|
|
164
|
+
use_gpu: bool = field(default_factory=lambda: torch.cuda.is_available())
|
|
165
|
+
pin_memory: bool = False
|
|
147
166
|
debug: bool = False
|
|
148
167
|
|
|
149
168
|
def as_core_arguments(self) -> dict:
|
|
150
169
|
misc_input_dict = super().as_core_arguments()
|
|
170
|
+
misc_input_dict["torch_device"] = "cuda" if self.use_gpu else "cpu"
|
|
171
|
+
del misc_input_dict["use_gpu"]
|
|
151
172
|
del misc_input_dict["debug"]
|
|
152
173
|
del misc_input_dict["analyse_local"]
|
|
153
174
|
return misc_input_dict
|
|
@@ -162,5 +183,16 @@ class MiscInputs(InputContainer):
|
|
|
162
183
|
"n_free_cpus", custom_label="Number of free CPUs"
|
|
163
184
|
),
|
|
164
185
|
analyse_local=dict(value=cls.defaults()["analyse_local"]),
|
|
186
|
+
use_gpu=dict(
|
|
187
|
+
widget_type="CheckBox",
|
|
188
|
+
label="Use GPU",
|
|
189
|
+
value=cls.defaults()["use_gpu"],
|
|
190
|
+
enabled=torch.cuda.is_available(),
|
|
191
|
+
),
|
|
192
|
+
pin_memory=dict(
|
|
193
|
+
widget_type="CheckBox",
|
|
194
|
+
label="Pin data to memory",
|
|
195
|
+
value=cls.defaults()["pin_memory"],
|
|
196
|
+
),
|
|
165
197
|
debug=dict(value=cls.defaults()["debug"]),
|
|
166
198
|
)
|
|
@@ -56,28 +56,35 @@ class Worker(WorkerBase):
|
|
|
56
56
|
self.update_progress_bar.connect(update_progress_bar)
|
|
57
57
|
|
|
58
58
|
def work(self) -> list:
|
|
59
|
-
self.
|
|
59
|
+
if not self.detection_inputs.skip_detection:
|
|
60
|
+
self.update_progress_bar.emit("Setting up detection...", 1, 0)
|
|
60
61
|
|
|
61
62
|
def detect_callback(plane: int) -> None:
|
|
62
|
-
self.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
if not self.detection_inputs.skip_detection:
|
|
64
|
+
self.update_progress_bar.emit(
|
|
65
|
+
"Detecting cells",
|
|
66
|
+
self.data_inputs.nplanes,
|
|
67
|
+
plane + 1,
|
|
68
|
+
)
|
|
67
69
|
|
|
68
70
|
def detect_finished_callback(points: list) -> None:
|
|
69
71
|
self.npoints_detected = len(points)
|
|
70
|
-
self.
|
|
72
|
+
if not self.classification_inputs.skip_classification:
|
|
73
|
+
self.update_progress_bar.emit(
|
|
74
|
+
"Setting up classification...", 1, 0
|
|
75
|
+
)
|
|
71
76
|
|
|
72
77
|
def classify_callback(batch: int) -> None:
|
|
73
|
-
self.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
if not self.classification_inputs.skip_classification:
|
|
79
|
+
self.update_progress_bar.emit(
|
|
80
|
+
"Classifying cells",
|
|
81
|
+
# Default cellfinder-core batch size is 64.
|
|
82
|
+
# This seems to give a slight
|
|
83
|
+
# underestimate of the number of batches though,
|
|
84
|
+
# so allow for batch number to go over this
|
|
85
|
+
max(self.npoints_detected // 64 + 1, batch + 1),
|
|
86
|
+
batch + 1,
|
|
87
|
+
)
|
|
81
88
|
|
|
82
89
|
result = cellfinder_run(
|
|
83
90
|
**self.data_inputs.as_core_arguments(),
|
|
@@ -88,5 +95,8 @@ class Worker(WorkerBase):
|
|
|
88
95
|
classify_callback=classify_callback,
|
|
89
96
|
detect_finished_callback=detect_finished_callback,
|
|
90
97
|
)
|
|
91
|
-
self.
|
|
98
|
+
if not self.classification_inputs.skip_classification:
|
|
99
|
+
self.update_progress_bar.emit("Finished classification", 1, 1)
|
|
100
|
+
else:
|
|
101
|
+
self.update_progress_bar.emit("Finished detection", 1, 1)
|
|
92
102
|
return result
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
-
from dataclasses import
|
|
2
|
+
from dataclasses import dataclass, fields
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
def asdict_no_copy(obj: dataclass) -> dict:
|
|
7
|
+
"""
|
|
8
|
+
Similar to `asdict`, except it makes no copies of the field values.
|
|
9
|
+
asdict will do a deep copy of field values that are non-basic objects.
|
|
10
|
+
|
|
11
|
+
It still creates a new dict to return, though.
|
|
12
|
+
"""
|
|
13
|
+
return {field.name: getattr(obj, field.name) for field in fields(obj)}
|
|
14
|
+
|
|
15
|
+
|
|
6
16
|
@dataclass
|
|
7
17
|
class InputContainer:
|
|
8
18
|
"""Base for classes that contain inputs
|
|
@@ -23,7 +33,7 @@ class InputContainer:
|
|
|
23
33
|
# Derived classes are not expected to be particularly
|
|
24
34
|
# slow to instantiate, so use the default constructor
|
|
25
35
|
# to avoid code repetition.
|
|
26
|
-
return
|
|
36
|
+
return asdict_no_copy(cls())
|
|
27
37
|
|
|
28
38
|
@abstractmethod
|
|
29
39
|
def as_core_arguments(self) -> dict:
|
|
@@ -32,10 +42,10 @@ class InputContainer:
|
|
|
32
42
|
The implementation provided here can be re-used in derived classes, if
|
|
33
43
|
convenient.
|
|
34
44
|
"""
|
|
35
|
-
# note that
|
|
45
|
+
# note that asdict_no_copy returns a new instance of a dict,
|
|
36
46
|
# so any subsequent modifications of this dict won't affect the class
|
|
37
47
|
# instance
|
|
38
|
-
return
|
|
48
|
+
return asdict_no_copy(self)
|
|
39
49
|
|
|
40
50
|
@classmethod
|
|
41
51
|
def _custom_widget(
|
cellfinder/napari/train/train.py
CHANGED
|
@@ -7,7 +7,7 @@ from napari.qt.threading import thread_worker
|
|
|
7
7
|
from napari.utils.notifications import show_info
|
|
8
8
|
from qtpy.QtWidgets import QScrollArea
|
|
9
9
|
|
|
10
|
-
from cellfinder.core.train.
|
|
10
|
+
from cellfinder.core.train.train_yaml import run as train_yaml
|
|
11
11
|
from cellfinder.napari.utils import cellfinder_header, html_label_widget
|
|
12
12
|
|
|
13
13
|
from .train_containers import (
|
|
@@ -25,14 +25,14 @@ def run_training(
|
|
|
25
25
|
optional_training_inputs: OptionalTrainingInputs,
|
|
26
26
|
misc_training_inputs: MiscTrainingInputs,
|
|
27
27
|
):
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
show_info("Running training...")
|
|
29
|
+
train_yaml(
|
|
30
30
|
**training_data_inputs.as_core_arguments(),
|
|
31
31
|
**optional_network_inputs.as_core_arguments(),
|
|
32
32
|
**optional_training_inputs.as_core_arguments(),
|
|
33
33
|
**misc_training_inputs.as_core_arguments(),
|
|
34
34
|
)
|
|
35
|
-
|
|
35
|
+
show_info("Training finished!")
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def training_widget() -> FunctionGui:
|
|
@@ -60,7 +60,6 @@ def training_widget() -> FunctionGui:
|
|
|
60
60
|
continue_training: bool,
|
|
61
61
|
augment: bool,
|
|
62
62
|
tensorboard: bool,
|
|
63
|
-
save_weights: bool,
|
|
64
63
|
save_checkpoints: bool,
|
|
65
64
|
save_progress: bool,
|
|
66
65
|
epochs: int,
|
|
@@ -96,9 +95,6 @@ def training_widget() -> FunctionGui:
|
|
|
96
95
|
Augment the training data to improve generalisation
|
|
97
96
|
tensorboard : bool
|
|
98
97
|
Log to output_directory/tensorboard
|
|
99
|
-
save_weights : bool
|
|
100
|
-
Only store the model weights, and not the full model
|
|
101
|
-
Useful to save storage space
|
|
102
98
|
save_checkpoints : bool
|
|
103
99
|
Store the model at intermediate points during training
|
|
104
100
|
save_progress : bool
|
|
@@ -133,7 +129,6 @@ def training_widget() -> FunctionGui:
|
|
|
133
129
|
continue_training,
|
|
134
130
|
augment,
|
|
135
131
|
tensorboard,
|
|
136
|
-
save_weights,
|
|
137
132
|
save_checkpoints,
|
|
138
133
|
save_progress,
|
|
139
134
|
epochs,
|
|
@@ -147,6 +142,7 @@ def training_widget() -> FunctionGui:
|
|
|
147
142
|
if yaml_files[0] == Path.home(): # type: ignore
|
|
148
143
|
show_info("Please select a YAML file for training")
|
|
149
144
|
else:
|
|
145
|
+
show_info("Starting training process...")
|
|
150
146
|
worker = run_training(
|
|
151
147
|
training_data_inputs,
|
|
152
148
|
optional_network_inputs,
|
|
@@ -5,7 +5,7 @@ from typing import Optional
|
|
|
5
5
|
from magicgui.types import FileDialogMode
|
|
6
6
|
|
|
7
7
|
from cellfinder.core.download.download import model_filenames
|
|
8
|
-
from cellfinder.core.train.
|
|
8
|
+
from cellfinder.core.train.train_yaml import models
|
|
9
9
|
from cellfinder.napari.input_container import InputContainer
|
|
10
10
|
from cellfinder.napari.utils import html_label_widget
|
|
11
11
|
|
|
@@ -31,7 +31,7 @@ class TrainingDataInputs(InputContainer):
|
|
|
31
31
|
"yaml_files",
|
|
32
32
|
custom_label="YAML files",
|
|
33
33
|
mode=FileDialogMode.EXISTING_FILES,
|
|
34
|
-
filter="*.
|
|
34
|
+
filter="*.yaml",
|
|
35
35
|
),
|
|
36
36
|
output_directory=cls._custom_widget(
|
|
37
37
|
"output_directory", mode=FileDialogMode.EXISTING_DIRECTORY
|
|
@@ -75,7 +75,6 @@ class OptionalTrainingInputs(InputContainer):
|
|
|
75
75
|
continue_training: bool = False
|
|
76
76
|
augment: bool = True
|
|
77
77
|
tensorboard: bool = False
|
|
78
|
-
save_weights: bool = False
|
|
79
78
|
save_checkpoints: bool = True
|
|
80
79
|
save_progress: bool = True
|
|
81
80
|
epochs: int = 100
|
|
@@ -98,7 +97,6 @@ class OptionalTrainingInputs(InputContainer):
|
|
|
98
97
|
continue_training=cls._custom_widget("continue_training"),
|
|
99
98
|
augment=cls._custom_widget("augment"),
|
|
100
99
|
tensorboard=cls._custom_widget("tensorboard"),
|
|
101
|
-
save_weights=cls._custom_widget("save_weights"),
|
|
102
100
|
save_checkpoints=cls._custom_widget("save_checkpoints"),
|
|
103
101
|
save_progress=cls._custom_widget("save_progress"),
|
|
104
102
|
epochs=cls._custom_widget("epochs"),
|
cellfinder/napari/utils.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import List, Tuple
|
|
1
|
+
from typing import List, Optional, Tuple
|
|
2
2
|
|
|
3
3
|
import napari
|
|
4
4
|
import napari.layers
|
|
@@ -44,6 +44,7 @@ def add_classified_layers(
|
|
|
44
44
|
viewer: napari.Viewer,
|
|
45
45
|
unknown_name: str = "Rejected",
|
|
46
46
|
cell_name: str = "Detected",
|
|
47
|
+
scale: Optional[Tuple[float, float, float]] = None,
|
|
47
48
|
) -> None:
|
|
48
49
|
"""
|
|
49
50
|
Adds cell candidates as two separate point layers - unknowns and cells, to
|
|
@@ -60,6 +61,7 @@ def add_classified_layers(
|
|
|
60
61
|
face_color="lightskyblue",
|
|
61
62
|
visible=False,
|
|
62
63
|
metadata=dict(point_type=Cell.UNKNOWN),
|
|
64
|
+
scale=scale,
|
|
63
65
|
)
|
|
64
66
|
viewer.add_points(
|
|
65
67
|
cells_to_array(points, Cell.CELL, napari_order=True),
|
|
@@ -70,6 +72,7 @@ def add_classified_layers(
|
|
|
70
72
|
symbol="ring",
|
|
71
73
|
face_color="lightgoldenrodyellow",
|
|
72
74
|
metadata=dict(point_type=Cell.CELL),
|
|
75
|
+
scale=scale,
|
|
73
76
|
)
|
|
74
77
|
|
|
75
78
|
|
|
@@ -78,6 +81,7 @@ def add_single_layer(
|
|
|
78
81
|
viewer: napari.Viewer,
|
|
79
82
|
name: str,
|
|
80
83
|
cell_type: int,
|
|
84
|
+
scale: Optional[Tuple[float, float, float]] = None,
|
|
81
85
|
) -> None:
|
|
82
86
|
"""
|
|
83
87
|
Adds all cells of cell_type Cell.TYPE to a new point layer in the napari
|
|
@@ -93,6 +97,7 @@ def add_single_layer(
|
|
|
93
97
|
face_color="lightskyblue",
|
|
94
98
|
visible=True,
|
|
95
99
|
metadata=dict(point_type=cell_type),
|
|
100
|
+
scale=scale,
|
|
96
101
|
)
|
|
97
102
|
|
|
98
103
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: cellfinder
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.0
|
|
4
4
|
Summary: Automated 3D cell detection in large microscopy images
|
|
5
5
|
Author-email: "Adam Tyson, Christian Niedworok, Charly Rousseau" <code@adamltyson.com>
|
|
6
6
|
License: BSD-3-Clause
|
|
@@ -16,26 +16,27 @@ Classifier: Intended Audience :: Science/Research
|
|
|
16
16
|
Classifier: Operating System :: OS Independent
|
|
17
17
|
Classifier: Programming Language :: Python
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
23
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
26
|
Requires-Dist: brainglobe-utils>=0.5.0
|
|
27
27
|
Requires-Dist: brainglobe-napari-io>=0.3.4
|
|
28
28
|
Requires-Dist: dask[array]
|
|
29
|
-
Requires-Dist: fancylog>=0.0
|
|
29
|
+
Requires-Dist: fancylog>=0.6.0
|
|
30
30
|
Requires-Dist: natsort
|
|
31
31
|
Requires-Dist: numba
|
|
32
32
|
Requires-Dist: numpy
|
|
33
33
|
Requires-Dist: scikit-image
|
|
34
34
|
Requires-Dist: scikit-learn
|
|
35
35
|
Requires-Dist: keras>=3.7.0
|
|
36
|
-
Requires-Dist: torch
|
|
36
|
+
Requires-Dist: torch>=2.4.1
|
|
37
37
|
Requires-Dist: tifffile
|
|
38
38
|
Requires-Dist: tqdm
|
|
39
|
+
Requires-Dist: qt-niu
|
|
39
40
|
Provides-Extra: dev
|
|
40
41
|
Requires-Dist: black; extra == "dev"
|
|
41
42
|
Requires-Dist: pre-commit; extra == "dev"
|
|
@@ -52,23 +53,26 @@ Requires-Dist: brainglobe-napari-io; extra == "napari"
|
|
|
52
53
|
Requires-Dist: magicgui; extra == "napari"
|
|
53
54
|
Requires-Dist: napari-ndtiffs; extra == "napari"
|
|
54
55
|
Requires-Dist: napari-plugin-engine>=0.1.4; extra == "napari"
|
|
55
|
-
Requires-Dist: napari[pyqt5]; extra == "napari"
|
|
56
|
+
Requires-Dist: napari[pyqt5]>=0.6.5; extra == "napari"
|
|
56
57
|
Requires-Dist: pooch>=1; extra == "napari"
|
|
57
58
|
Requires-Dist: qtpy; extra == "napari"
|
|
59
|
+
Dynamic: license-file
|
|
58
60
|
|
|
59
61
|
[](https://pypi.org/project/cellfinder)
|
|
60
62
|
[](https://pypi.org/project/cellfinder)
|
|
61
|
-
[](https://anaconda.org/conda-forge/cellfinder)
|
|
64
|
+
[](https://napari-hub.org/plugins/cellfinder.html)
|
|
65
|
+
[](https://pepy.tech/project/cellfinder)
|
|
62
66
|
[](https://pypi.org/project/cellfinder)
|
|
63
67
|
[](https://github.com/brainglobe/cellfinder)
|
|
64
68
|
[](https://github.com/brainglobe/cellfinder/actions)
|
|
65
69
|
[](https://codecov.io/gh/brainglobe/cellfinder)
|
|
66
|
-
[](https://pycqa.github.io/isort/)
|
|
70
|
+
[](https://github.com/astral-sh/ruff)[](https://pycqa.github.io/isort/)
|
|
68
71
|
[](https://github.com/pre-commit/pre-commit)
|
|
69
72
|
[](https://brainglobe.info/community/developers/index.html)
|
|
70
|
-
[](https://forum.image.sc/tag/brainglobe)
|
|
74
|
+
[](https://bsky.app/profile/brainglobe.info)
|
|
75
|
+
[](https://mastodon.online/@brainglobe)
|
|
72
76
|
# cellfinder
|
|
73
77
|
|
|
74
78
|
cellfinder is software for automated 3D cell detection in very large 3D images (e.g., serial two-photon or lightsheet volumes of whole mouse brains).
|