cellfinder 1.8.0__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/core/detect/detect.py +23 -3
- cellfinder/core/detect/filters/plane/plane_filter.py +106 -9
- cellfinder/core/detect/filters/setup_filters.py +22 -2
- cellfinder/core/detect/filters/volume/structure_detection.py +5 -0
- cellfinder/core/detect/filters/volume/structure_splitting.py +1 -2
- cellfinder/core/main.py +33 -15
- cellfinder/core/tools/tools.py +1 -1
- cellfinder/core/train/train_yaml.py +2 -2
- cellfinder/napari/curation.py +52 -17
- cellfinder/napari/detect/detect.py +28 -4
- cellfinder/napari/detect/detect_containers.py +10 -1
- cellfinder/napari/detect/thread_worker.py +26 -16
- cellfinder/napari/utils.py +6 -1
- {cellfinder-1.8.0.dist-info → cellfinder-1.9.0.dist-info}/METADATA +10 -8
- {cellfinder-1.8.0.dist-info → cellfinder-1.9.0.dist-info}/RECORD +19 -19
- {cellfinder-1.8.0.dist-info → cellfinder-1.9.0.dist-info}/WHEEL +1 -1
- {cellfinder-1.8.0.dist-info → cellfinder-1.9.0.dist-info}/entry_points.txt +0 -0
- {cellfinder-1.8.0.dist-info → cellfinder-1.9.0.dist-info}/licenses/LICENSE +0 -0
- {cellfinder-1.8.0.dist-info → cellfinder-1.9.0.dist-info}/top_level.txt +0 -0
cellfinder/core/detect/detect.py
CHANGED
|
@@ -54,6 +54,8 @@ def main(
|
|
|
54
54
|
split_ball_z_size: float = 15,
|
|
55
55
|
split_ball_overlap_fraction: float = 0.8,
|
|
56
56
|
n_splitting_iter: int = 10,
|
|
57
|
+
n_sds_above_mean_tiled_thresh: float = 10,
|
|
58
|
+
tiled_thresh_tile_size: float | None = None,
|
|
57
59
|
*,
|
|
58
60
|
callback: Optional[Callable[[int], None]] = None,
|
|
59
61
|
) -> List[Cell]:
|
|
@@ -96,8 +98,8 @@ def main(
|
|
|
96
98
|
Gaussian filter width (as a fraction of soma diameter) used during
|
|
97
99
|
2d in-plane Laplacian of Gaussian filtering.
|
|
98
100
|
n_sds_above_mean_thresh : float
|
|
99
|
-
|
|
100
|
-
the mean) of the filtered 2d planes used to mark pixels as
|
|
101
|
+
Per-plane intensity threshold (the number of standard deviations
|
|
102
|
+
above the mean) of the filtered 2d planes used to mark pixels as
|
|
101
103
|
foreground or background.
|
|
102
104
|
outlier_keep : bool, optional
|
|
103
105
|
Whether to keep outliers during detection. Defaults to False.
|
|
@@ -136,6 +138,20 @@ def main(
|
|
|
136
138
|
The number of iterations to run the 3d filtering on a cluster. Each
|
|
137
139
|
iteration reduces the cluster size by the voxels not retained in
|
|
138
140
|
the previous iteration.
|
|
141
|
+
n_sds_above_mean_tiled_thresh : float
|
|
142
|
+
Per-plane, per-tile intensity threshold (the number of standard
|
|
143
|
+
deviations above the mean) for the filtered 2d planes used to mark
|
|
144
|
+
pixels as foreground or background. When used, (tile size is not zero)
|
|
145
|
+
a pixel is marked as foreground if its intensity is above both the
|
|
146
|
+
per-plane and per-tile threshold. I.e. it's above the set number of
|
|
147
|
+
standard deviations of the per-plane average and of the per-plane
|
|
148
|
+
per-tile average for the tile that contains it.
|
|
149
|
+
tiled_thresh_tile_size : float
|
|
150
|
+
The tile size used to tile the x, y plane to calculate the local
|
|
151
|
+
average intensity for the tiled threshold. The value is multiplied
|
|
152
|
+
by soma diameter (i.e. 1 means one soma diameter). If zero or None, the
|
|
153
|
+
tiled threshold is disabled and only the per-plane threshold is used.
|
|
154
|
+
Tiling is done with 50% overlap when striding.
|
|
139
155
|
callback : Callable[int], optional
|
|
140
156
|
A callback function that is called every time a plane has finished
|
|
141
157
|
being processed. Called with the plane number that has finished.
|
|
@@ -193,6 +209,8 @@ def main(
|
|
|
193
209
|
ball_overlap_fraction=ball_overlap_fraction,
|
|
194
210
|
log_sigma_size=log_sigma_size,
|
|
195
211
|
n_sds_above_mean_thresh=n_sds_above_mean_thresh,
|
|
212
|
+
n_sds_above_mean_tiled_thresh=n_sds_above_mean_tiled_thresh,
|
|
213
|
+
tiled_thresh_tile_size=tiled_thresh_tile_size,
|
|
196
214
|
outlier_keep=outlier_keep,
|
|
197
215
|
artifact_keep=artifact_keep,
|
|
198
216
|
save_planes=save_planes,
|
|
@@ -228,7 +246,9 @@ def main(
|
|
|
228
246
|
plane_shape=settings.plane_shape,
|
|
229
247
|
clipping_value=settings.clipping_value,
|
|
230
248
|
threshold_value=settings.threshold_value,
|
|
231
|
-
n_sds_above_mean_thresh=n_sds_above_mean_thresh,
|
|
249
|
+
n_sds_above_mean_thresh=settings.n_sds_above_mean_thresh,
|
|
250
|
+
n_sds_above_mean_tiled_thresh=settings.n_sds_above_mean_tiled_thresh,
|
|
251
|
+
tiled_thresh_tile_size=settings.tiled_thresh_tile_size,
|
|
232
252
|
log_sigma_size=log_sigma_size,
|
|
233
253
|
soma_diameter=settings.soma_diameter,
|
|
234
254
|
torch_device=torch_device,
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
from dataclasses import dataclass, field
|
|
2
1
|
from typing import Tuple
|
|
3
2
|
|
|
4
3
|
import torch
|
|
4
|
+
import torch.nn.functional as F
|
|
5
5
|
|
|
6
6
|
from cellfinder.core.detect.filters.plane.classical_filter import PeakEnhancer
|
|
7
7
|
from cellfinder.core.detect.filters.plane.tile_walker import TileWalker
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
@dataclass
|
|
11
10
|
class TileProcessor:
|
|
12
11
|
"""
|
|
13
12
|
Processor that filters each plane to highlight the peaks and also
|
|
@@ -63,12 +62,22 @@ class TileProcessor:
|
|
|
63
62
|
# voxels who are this many std above mean or more are set to
|
|
64
63
|
# threshold_value
|
|
65
64
|
n_sds_above_mean_thresh: float
|
|
65
|
+
# If used, voxels who are this many or more std above mean of the
|
|
66
|
+
# containing tile as well as above n_sds_above_mean_thresh for the plane
|
|
67
|
+
# average are set to threshold_value.
|
|
68
|
+
n_sds_above_mean_tiled_thresh: float
|
|
69
|
+
# the tile size, in pixels, that will be used to tile the x, y plane when
|
|
70
|
+
# we calculate the per-tile mean / std for use with
|
|
71
|
+
# n_sds_above_mean_tiled_thresh. We use 50% overlap when tiling.
|
|
72
|
+
local_threshold_tile_size_px: int = 0
|
|
73
|
+
# the torch device name
|
|
74
|
+
torch_device: str = ""
|
|
66
75
|
|
|
67
76
|
# filter that finds the peaks in the planes
|
|
68
|
-
peak_enhancer: PeakEnhancer =
|
|
77
|
+
peak_enhancer: PeakEnhancer = None
|
|
69
78
|
# generates tiles of the planes, with each tile marked as being inside
|
|
70
79
|
# or outside the brain based on brightness
|
|
71
|
-
tile_walker: TileWalker =
|
|
80
|
+
tile_walker: TileWalker = None
|
|
72
81
|
|
|
73
82
|
def __init__(
|
|
74
83
|
self,
|
|
@@ -76,6 +85,8 @@ class TileProcessor:
|
|
|
76
85
|
clipping_value: int,
|
|
77
86
|
threshold_value: int,
|
|
78
87
|
n_sds_above_mean_thresh: float,
|
|
88
|
+
n_sds_above_mean_tiled_thresh: float,
|
|
89
|
+
tiled_thresh_tile_size: float | None,
|
|
79
90
|
log_sigma_size: float,
|
|
80
91
|
soma_diameter: int,
|
|
81
92
|
torch_device: str,
|
|
@@ -85,6 +96,12 @@ class TileProcessor:
|
|
|
85
96
|
self.clipping_value = clipping_value
|
|
86
97
|
self.threshold_value = threshold_value
|
|
87
98
|
self.n_sds_above_mean_thresh = n_sds_above_mean_thresh
|
|
99
|
+
self.n_sds_above_mean_tiled_thresh = n_sds_above_mean_tiled_thresh
|
|
100
|
+
if tiled_thresh_tile_size:
|
|
101
|
+
self.local_threshold_tile_size_px = int(
|
|
102
|
+
round(soma_diameter * tiled_thresh_tile_size)
|
|
103
|
+
)
|
|
104
|
+
self.torch_device = torch_device
|
|
88
105
|
|
|
89
106
|
laplace_gaussian_sigma = log_sigma_size * soma_diameter
|
|
90
107
|
self.peak_enhancer = PeakEnhancer(
|
|
@@ -131,7 +148,10 @@ class TileProcessor:
|
|
|
131
148
|
planes,
|
|
132
149
|
enhanced_planes,
|
|
133
150
|
self.n_sds_above_mean_thresh,
|
|
151
|
+
self.n_sds_above_mean_tiled_thresh,
|
|
152
|
+
self.local_threshold_tile_size_px,
|
|
134
153
|
self.threshold_value,
|
|
154
|
+
self.torch_device,
|
|
135
155
|
)
|
|
136
156
|
|
|
137
157
|
return planes, inside_brain_tiles
|
|
@@ -145,21 +165,98 @@ def _threshold_planes(
|
|
|
145
165
|
planes: torch.Tensor,
|
|
146
166
|
enhanced_planes: torch.Tensor,
|
|
147
167
|
n_sds_above_mean_thresh: float,
|
|
168
|
+
n_sds_above_mean_tiled_thresh: float,
|
|
169
|
+
local_threshold_tile_size_px: int,
|
|
148
170
|
threshold_value: int,
|
|
171
|
+
torch_device: str,
|
|
149
172
|
) -> None:
|
|
150
173
|
"""
|
|
151
174
|
Sets each plane (in-place) to threshold_value, where the corresponding
|
|
152
175
|
enhanced_plane > mean + n_sds_above_mean_thresh*std. Each plane will be
|
|
153
176
|
set to zero elsewhere.
|
|
154
177
|
"""
|
|
155
|
-
|
|
178
|
+
z, y, x = enhanced_planes.shape
|
|
156
179
|
|
|
180
|
+
# ---- get per-plane global threshold ----
|
|
181
|
+
planes_1d = enhanced_planes.view(z, -1)
|
|
157
182
|
# add back last dim
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
183
|
+
std, mean = torch.std_mean(planes_1d, dim=1, keepdim=True)
|
|
184
|
+
threshold = mean.unsqueeze(2) + n_sds_above_mean_thresh * std.unsqueeze(2)
|
|
185
|
+
above_global = enhanced_planes > threshold
|
|
186
|
+
|
|
187
|
+
# ---- calculate the local tiled threshold ----
|
|
188
|
+
# we do 50% overlap so there's no jumps at boundaries
|
|
189
|
+
stride = local_threshold_tile_size_px // 2
|
|
190
|
+
# make tile even for ease of computation
|
|
191
|
+
tile_size = stride * 2
|
|
192
|
+
# Due to 50% overlap, to get tiles we move the tile by half tile (stride).
|
|
193
|
+
# Total moves will be y // stride - 2 (we start already with mask on first
|
|
194
|
+
# tile). So add back 1 for the first tile. Partial tiles are dropped
|
|
195
|
+
n_y_tiles = max(y // stride - 1, 1) if stride else 1
|
|
196
|
+
n_x_tiles = max(x // stride - 1, 1) if stride else 1
|
|
197
|
+
do_tile_y = n_y_tiles >= 2
|
|
198
|
+
do_tile_x = n_x_tiles >= 2
|
|
199
|
+
# we want at least one axis to have at least two tiles
|
|
200
|
+
if local_threshold_tile_size_px >= 2 and (do_tile_y or do_tile_x):
|
|
201
|
+
# num edge pixels dropped b/c moving by stride would move tile off edge
|
|
202
|
+
y_rem = y % stride
|
|
203
|
+
x_rem = x % stride
|
|
204
|
+
enhanced_planes_raw = enhanced_planes
|
|
205
|
+
if do_tile_y:
|
|
206
|
+
enhanced_planes = enhanced_planes[:, y_rem // 2 :, :]
|
|
207
|
+
if do_tile_x:
|
|
208
|
+
enhanced_planes = enhanced_planes[:, :, x_rem // 2 :]
|
|
209
|
+
|
|
210
|
+
# add empty channel dim after z "batch" dim -> zcyx
|
|
211
|
+
enhanced_planes = enhanced_planes.unsqueeze(1)
|
|
212
|
+
# unfold makes it 3 dim, z, M, L. L is number of tiles, M is tile area
|
|
213
|
+
unfolded = F.unfold(
|
|
214
|
+
enhanced_planes,
|
|
215
|
+
(tile_size if do_tile_y else y, tile_size if do_tile_x else x),
|
|
216
|
+
stride=stride,
|
|
217
|
+
)
|
|
218
|
+
# average the tile areas, for each tile
|
|
219
|
+
std, mean = torch.std_mean(unfolded, dim=1, keepdim=True)
|
|
220
|
+
threshold = mean + n_sds_above_mean_tiled_thresh * std
|
|
221
|
+
|
|
222
|
+
# reshape it back into Y by X tiles, instead of YX being one dim
|
|
223
|
+
threshold = threshold.reshape((z, n_y_tiles, n_x_tiles))
|
|
224
|
+
|
|
225
|
+
# we need total size of n_tiles * stride + stride + rem for the
|
|
226
|
+
# original size. So we add 2 strides and then chop off the excess above
|
|
227
|
+
# rem. We center it because of 50% overlap, the first tile is actually
|
|
228
|
+
# centered in between the first two strides
|
|
229
|
+
offsets = [(0, y), (0, x)]
|
|
230
|
+
for dim, do_tile, n_tiles, n, rem in [
|
|
231
|
+
(1, do_tile_y, n_y_tiles, y, y_rem),
|
|
232
|
+
(2, do_tile_x, n_x_tiles, x, x_rem),
|
|
233
|
+
]:
|
|
234
|
+
if do_tile:
|
|
235
|
+
repeats = (
|
|
236
|
+
torch.ones(n_tiles, dtype=torch.int, device=torch_device)
|
|
237
|
+
* stride
|
|
238
|
+
)
|
|
239
|
+
# add total of 2 additional strides
|
|
240
|
+
repeats[0] = 2 * stride
|
|
241
|
+
repeats[-1] = 2 * stride
|
|
242
|
+
output_size = (n_tiles + 2) * stride
|
|
243
|
+
|
|
244
|
+
threshold = threshold.repeat_interleave(
|
|
245
|
+
repeats, dim=dim, output_size=output_size
|
|
246
|
+
)
|
|
247
|
+
# drop the excess we gained from padding rem to whole stride
|
|
248
|
+
offset = (stride - rem) // 2
|
|
249
|
+
offsets[dim - 1] = offset, n + offset
|
|
250
|
+
|
|
251
|
+
# can't use slice(...) objects in jit code so use actual indices
|
|
252
|
+
(a, b), (c, d) = offsets
|
|
253
|
+
threshold = threshold[:, a:b, c:d]
|
|
254
|
+
|
|
255
|
+
above_local = enhanced_planes_raw > threshold
|
|
256
|
+
above = torch.logical_and(above_global, above_local)
|
|
257
|
+
else:
|
|
258
|
+
above = above_global
|
|
161
259
|
|
|
162
|
-
above = enhanced_planes > threshold
|
|
163
260
|
planes[above] = threshold_value
|
|
164
261
|
# subsequent steps only care about the values that are set to threshold or
|
|
165
262
|
# above in planes. We set values in *planes* to threshold based on the
|
|
@@ -133,11 +133,31 @@ class DetectionSettings:
|
|
|
133
133
|
|
|
134
134
|
n_sds_above_mean_thresh: float = 10
|
|
135
135
|
"""
|
|
136
|
-
|
|
137
|
-
the mean) of the filtered
|
|
136
|
+
Per-plane intensity threshold (the number of standard deviations
|
|
137
|
+
above the mean) of the 2d filtered planes used to mark pixels as
|
|
138
138
|
foreground or background.
|
|
139
139
|
"""
|
|
140
140
|
|
|
141
|
+
n_sds_above_mean_tiled_thresh: float = 10
|
|
142
|
+
"""
|
|
143
|
+
Per-plane, per-tile intensity threshold (the number of standard deviations
|
|
144
|
+
above the mean) for the filtered 2d planes used to mark pixels as
|
|
145
|
+
foreground or background. When used, (tile size is not zero) a pixel is
|
|
146
|
+
marked as foreground if its intensity is above both the per-plane and
|
|
147
|
+
per-tile threshold. I.e. it's above the set number of standard deviations
|
|
148
|
+
of the per-plane average and of the per-plane per-tile average for the tile
|
|
149
|
+
that contains it.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
tiled_thresh_tile_size: float | None = None
|
|
153
|
+
"""
|
|
154
|
+
The tile size used to tile the x, y plane to calculate the local average
|
|
155
|
+
intensity for the tiled threshold. The value is multiplied by soma
|
|
156
|
+
diameter (i.e. 1 means one soma diameter). If zero or None, the tiled
|
|
157
|
+
threshold is disabled and only the per-plane threshold is used. Tiling is
|
|
158
|
+
done with 50% overlap when striding.
|
|
159
|
+
"""
|
|
160
|
+
|
|
141
161
|
outlier_keep: bool = False
|
|
142
162
|
"""Whether to keep outlier structures during detection."""
|
|
143
163
|
|
|
@@ -222,6 +222,11 @@ class CellDetector:
|
|
|
222
222
|
neighbour_ids[2] = previous_plane[y, x]
|
|
223
223
|
|
|
224
224
|
if is_new_structure(neighbour_ids):
|
|
225
|
+
if self.next_structure_id > self.soma_centre_value:
|
|
226
|
+
raise ValueError(
|
|
227
|
+
"label overflow: number of connected "
|
|
228
|
+
"components exceeds label capacity"
|
|
229
|
+
)
|
|
225
230
|
neighbour_ids[0] = self.next_structure_id
|
|
226
231
|
self.next_structure_id += 1
|
|
227
232
|
struct_id = self.add(x, y, self.z, neighbour_ids)
|
|
@@ -66,8 +66,7 @@ def coords_to_volume(
|
|
|
66
66
|
relative_zs = np.array((zs - z_min + ball_radius), dtype=np.int64)
|
|
67
67
|
|
|
68
68
|
# set each point as the center with a value of threshold
|
|
69
|
-
|
|
70
|
-
volume[rel_x, rel_y, rel_z] = threshold_value
|
|
69
|
+
volume[relative_xs, relative_ys, relative_zs] = threshold_value
|
|
71
70
|
|
|
72
71
|
volume = volume.swapaxes(0, 2)
|
|
73
72
|
return torch.from_numpy(volume)
|
cellfinder/core/main.py
CHANGED
|
@@ -42,6 +42,8 @@ def main(
|
|
|
42
42
|
split_ball_z_size: float = 15,
|
|
43
43
|
split_ball_overlap_fraction: float = 0.8,
|
|
44
44
|
n_splitting_iter: int = 10,
|
|
45
|
+
n_sds_above_mean_tiled_thresh: float = 10,
|
|
46
|
+
tiled_thresh_tile_size: float | None = None,
|
|
45
47
|
*,
|
|
46
48
|
detect_callback: Optional[Callable[[int], None]] = None,
|
|
47
49
|
classify_callback: Optional[Callable[[int], None]] = None,
|
|
@@ -94,8 +96,8 @@ def main(
|
|
|
94
96
|
Gaussian filter width (as a fraction of soma diameter) used during
|
|
95
97
|
2d in-plane Laplacian of Gaussian filtering.
|
|
96
98
|
n_sds_above_mean_thresh : float
|
|
97
|
-
|
|
98
|
-
the mean) of the filtered 2d planes used to mark pixels as
|
|
99
|
+
Per-plane intensity threshold (the number of standard deviations
|
|
100
|
+
above the mean) of the filtered 2d planes used to mark pixels as
|
|
99
101
|
foreground or background.
|
|
100
102
|
soma_spread_factor : float
|
|
101
103
|
Cell spread factor for determining the largest cell volume before
|
|
@@ -155,6 +157,20 @@ def main(
|
|
|
155
157
|
The number of iterations to run the 3d filtering on a cluster. Each
|
|
156
158
|
iteration reduces the cluster size by the voxels not retained in
|
|
157
159
|
the previous iteration.
|
|
160
|
+
n_sds_above_mean_tiled_thresh : float
|
|
161
|
+
Per-plane, per-tile intensity threshold (the number of standard
|
|
162
|
+
deviations above the mean) for the filtered 2d planes used to mark
|
|
163
|
+
pixels as foreground or background. When used, (tile size is not zero)
|
|
164
|
+
a pixel is marked as foreground if its intensity is above both the
|
|
165
|
+
per-plane and per-tile threshold. I.e. it's above the set number of
|
|
166
|
+
standard deviations of the per-plane average and of the per-plane
|
|
167
|
+
per-tile average for the tile that contains it.
|
|
168
|
+
tiled_thresh_tile_size : float
|
|
169
|
+
The tile size used to tile the x, y plane to calculate the local
|
|
170
|
+
average intensity for the tiled threshold. The value is multiplied
|
|
171
|
+
by soma diameter (i.e. 1 means one soma diameter). If zero or None, the
|
|
172
|
+
tiled threshold is disabled and only the per-plane threshold is used.
|
|
173
|
+
Tiling is done with 50% overlap when striding.
|
|
158
174
|
detect_callback : Callable[int], optional
|
|
159
175
|
Called every time a plane has finished being processed during the
|
|
160
176
|
detection stage. Called with the plane number that has finished.
|
|
@@ -172,19 +188,21 @@ def main(
|
|
|
172
188
|
logger.info("Detecting cell candidates")
|
|
173
189
|
|
|
174
190
|
points = detect.main(
|
|
175
|
-
signal_array,
|
|
176
|
-
start_plane,
|
|
177
|
-
end_plane,
|
|
178
|
-
voxel_sizes,
|
|
179
|
-
soma_diameter,
|
|
180
|
-
max_cluster_size,
|
|
181
|
-
ball_xy_size,
|
|
182
|
-
ball_z_size,
|
|
183
|
-
ball_overlap_fraction,
|
|
184
|
-
soma_spread_factor,
|
|
185
|
-
n_free_cpus,
|
|
186
|
-
log_sigma_size,
|
|
187
|
-
n_sds_above_mean_thresh,
|
|
191
|
+
signal_array=signal_array,
|
|
192
|
+
start_plane=start_plane,
|
|
193
|
+
end_plane=end_plane,
|
|
194
|
+
voxel_sizes=voxel_sizes,
|
|
195
|
+
soma_diameter=soma_diameter,
|
|
196
|
+
max_cluster_size=max_cluster_size,
|
|
197
|
+
ball_xy_size=ball_xy_size,
|
|
198
|
+
ball_z_size=ball_z_size,
|
|
199
|
+
ball_overlap_fraction=ball_overlap_fraction,
|
|
200
|
+
soma_spread_factor=soma_spread_factor,
|
|
201
|
+
n_free_cpus=n_free_cpus,
|
|
202
|
+
log_sigma_size=log_sigma_size,
|
|
203
|
+
n_sds_above_mean_thresh=n_sds_above_mean_thresh,
|
|
204
|
+
n_sds_above_mean_tiled_thresh=n_sds_above_mean_tiled_thresh,
|
|
205
|
+
tiled_thresh_tile_size=tiled_thresh_tile_size,
|
|
188
206
|
batch_size=detection_batch_size,
|
|
189
207
|
torch_device=torch_device,
|
|
190
208
|
pin_memory=pin_memory,
|
cellfinder/core/tools/tools.py
CHANGED
|
@@ -29,7 +29,7 @@ from fancylog import fancylog
|
|
|
29
29
|
from keras.callbacks import CSVLogger, ModelCheckpoint, TensorBoard
|
|
30
30
|
from sklearn.model_selection import train_test_split
|
|
31
31
|
|
|
32
|
-
import cellfinder.core as
|
|
32
|
+
import cellfinder.core as package_for_log
|
|
33
33
|
from cellfinder.core import logger
|
|
34
34
|
from cellfinder.core.classify.cube_generator import CubeGeneratorFromDisk
|
|
35
35
|
from cellfinder.core.classify.resnet import layer_type
|
|
@@ -269,7 +269,7 @@ def cli():
|
|
|
269
269
|
|
|
270
270
|
fancylog.start_logging(
|
|
271
271
|
args.output_dir,
|
|
272
|
-
|
|
272
|
+
package=package_for_log,
|
|
273
273
|
variables=[args],
|
|
274
274
|
log_header="CELLFINDER TRAINING LOG",
|
|
275
275
|
)
|
cellfinder/napari/curation.py
CHANGED
|
@@ -11,7 +11,7 @@ from brainglobe_utils.IO.yaml import save_yaml
|
|
|
11
11
|
from magicgui.widgets import ProgressBar
|
|
12
12
|
from napari.qt.threading import thread_worker
|
|
13
13
|
from napari.utils.notifications import show_info
|
|
14
|
-
from qt_niu.dialog import display_warning
|
|
14
|
+
from qt_niu.dialog import display_info, display_warning
|
|
15
15
|
from qt_niu.interaction import add_button, add_combobox
|
|
16
16
|
from qtpy import QtCore
|
|
17
17
|
from qtpy.QtWidgets import (
|
|
@@ -495,26 +495,61 @@ class CurationWidget(QWidget):
|
|
|
495
495
|
return False
|
|
496
496
|
|
|
497
497
|
def check_training_data_exists(self) -> bool:
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
498
|
+
"""
|
|
499
|
+
Checks that
|
|
500
|
+
- both training data layers exists
|
|
501
|
+
- at least one of them is not empty.
|
|
502
|
+
|
|
503
|
+
Will display a popup dialog and return False if these conditions
|
|
504
|
+
are not both fulfilled.
|
|
505
|
+
|
|
506
|
+
Will show a notification if only one layer is non-empty, but this
|
|
507
|
+
is considered valid.
|
|
508
|
+
|
|
509
|
+
Returns
|
|
510
|
+
-------
|
|
511
|
+
bool
|
|
512
|
+
True if both training layers exists and at least one
|
|
513
|
+
of them contains some data. False otherwise.
|
|
514
|
+
"""
|
|
515
|
+
both_training_layers_exist = (
|
|
516
|
+
self.training_data_cell_layer and self.training_data_non_cell_layer
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
if not both_training_layers_exist:
|
|
520
|
+
display_info(
|
|
521
|
+
self,
|
|
522
|
+
"No training data layers have been added.",
|
|
523
|
+
"Please add layers for both cells and non-cells,"
|
|
524
|
+
"and annotate some points.",
|
|
504
525
|
)
|
|
505
526
|
return False
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
527
|
+
|
|
528
|
+
at_least_one_training_layer_contains_data = (
|
|
529
|
+
len(self.training_data_cell_layer.data) > 0
|
|
530
|
+
or len(self.training_data_non_cell_layer.data) > 0
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
both_training_layers_contain_data = (
|
|
534
|
+
len(self.training_data_cell_layer.data) > 0
|
|
535
|
+
and len(self.training_data_non_cell_layer.data) > 0
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
if at_least_one_training_layer_contains_data:
|
|
539
|
+
if not both_training_layers_contain_data:
|
|
513
540
|
show_info(
|
|
514
|
-
"
|
|
515
|
-
"
|
|
541
|
+
"One of the training layers is empty. This is OK, but"
|
|
542
|
+
"For optimal (re-)training ensure you have roughly equal "
|
|
543
|
+
"number of points in each of your training points layers."
|
|
516
544
|
)
|
|
517
|
-
|
|
545
|
+
return True
|
|
546
|
+
else:
|
|
547
|
+
display_info(
|
|
548
|
+
self,
|
|
549
|
+
"No training data points have been added.",
|
|
550
|
+
"Please annotate points in the training data layers.",
|
|
551
|
+
)
|
|
552
|
+
return False
|
|
518
553
|
|
|
519
554
|
def get_output_directory(self):
|
|
520
555
|
"""
|
|
@@ -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
|
|
@@ -246,6 +248,8 @@ def detect_widget() -> FunctionGui:
|
|
|
246
248
|
soma_diameter: float,
|
|
247
249
|
log_sigma_size: float,
|
|
248
250
|
n_sds_above_mean_thresh: float,
|
|
251
|
+
n_sds_above_mean_tiled_thresh: float,
|
|
252
|
+
tiled_thresh_tile_size: float,
|
|
249
253
|
ball_xy_size: float,
|
|
250
254
|
ball_z_size: float,
|
|
251
255
|
ball_overlap_fraction: float,
|
|
@@ -288,9 +292,23 @@ def detect_widget() -> FunctionGui:
|
|
|
288
292
|
Gaussian filter width (as a fraction of soma diameter) used during
|
|
289
293
|
2d in-plane Laplacian of Gaussian filtering
|
|
290
294
|
n_sds_above_mean_thresh : float
|
|
291
|
-
|
|
292
|
-
the mean) of the filtered 2d planes used to mark pixels as
|
|
295
|
+
Per-plane intensity threshold (the number of standard deviations
|
|
296
|
+
above the mean) of the filtered 2d planes used to mark pixels as
|
|
293
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.
|
|
294
312
|
ball_xy_size : float
|
|
295
313
|
3d filter's in-plane (xy) filter ball size (microns)
|
|
296
314
|
ball_z_size : float
|
|
@@ -396,6 +414,8 @@ def detect_widget() -> FunctionGui:
|
|
|
396
414
|
ball_overlap_fraction,
|
|
397
415
|
log_sigma_size,
|
|
398
416
|
n_sds_above_mean_thresh,
|
|
417
|
+
n_sds_above_mean_tiled_thresh,
|
|
418
|
+
tiled_thresh_tile_size,
|
|
399
419
|
soma_spread_factor,
|
|
400
420
|
max_cluster_size,
|
|
401
421
|
detection_batch_size,
|
|
@@ -435,7 +455,11 @@ def detect_widget() -> FunctionGui:
|
|
|
435
455
|
)
|
|
436
456
|
|
|
437
457
|
worker.returned.connect(
|
|
438
|
-
get_results_callback(
|
|
458
|
+
get_results_callback(
|
|
459
|
+
skip_classification,
|
|
460
|
+
options["viewer"],
|
|
461
|
+
options["signal_image"].scale,
|
|
462
|
+
)
|
|
439
463
|
)
|
|
440
464
|
# Make sure if the worker emits an error, it is propagated to this
|
|
441
465
|
# thread
|
|
@@ -69,6 +69,8 @@ class DetectionInputs(InputContainer):
|
|
|
69
69
|
ball_overlap_fraction: float = 0.6
|
|
70
70
|
log_sigma_size: float = 0.2
|
|
71
71
|
n_sds_above_mean_thresh: float = 10
|
|
72
|
+
n_sds_above_mean_tiled_thresh: float = 10
|
|
73
|
+
tiled_thresh_tile_size: float = 0
|
|
72
74
|
soma_spread_factor: float = 1.4
|
|
73
75
|
max_cluster_size: float = 100000
|
|
74
76
|
detection_batch_size: int = 1
|
|
@@ -95,7 +97,14 @@ class DetectionInputs(InputContainer):
|
|
|
95
97
|
"log_sigma_size", custom_label="Filter width"
|
|
96
98
|
),
|
|
97
99
|
n_sds_above_mean_thresh=cls._custom_widget(
|
|
98
|
-
"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",
|
|
99
108
|
),
|
|
100
109
|
soma_spread_factor=cls._custom_widget(
|
|
101
110
|
"soma_spread_factor", custom_label="Split cell spread"
|
|
@@ -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
|
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
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
|
|
@@ -26,7 +26,7 @@ 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
|
|
@@ -53,24 +53,26 @@ Requires-Dist: brainglobe-napari-io; extra == "napari"
|
|
|
53
53
|
Requires-Dist: magicgui; extra == "napari"
|
|
54
54
|
Requires-Dist: napari-ndtiffs; extra == "napari"
|
|
55
55
|
Requires-Dist: napari-plugin-engine>=0.1.4; extra == "napari"
|
|
56
|
-
Requires-Dist: napari[pyqt5]>=0.6.
|
|
56
|
+
Requires-Dist: napari[pyqt5]>=0.6.5; extra == "napari"
|
|
57
57
|
Requires-Dist: pooch>=1; extra == "napari"
|
|
58
58
|
Requires-Dist: qtpy; extra == "napari"
|
|
59
59
|
Dynamic: license-file
|
|
60
60
|
|
|
61
61
|
[](https://pypi.org/project/cellfinder)
|
|
62
62
|
[](https://pypi.org/project/cellfinder)
|
|
63
|
-
[](https://anaconda.org/conda-forge/cellfinder)
|
|
64
|
+
[](https://napari-hub.org/plugins/cellfinder.html)
|
|
65
|
+
[](https://pepy.tech/project/cellfinder)
|
|
64
66
|
[](https://pypi.org/project/cellfinder)
|
|
65
67
|
[](https://github.com/brainglobe/cellfinder)
|
|
66
68
|
[](https://github.com/brainglobe/cellfinder/actions)
|
|
67
69
|
[](https://codecov.io/gh/brainglobe/cellfinder)
|
|
68
|
-
[](https://pycqa.github.io/isort/)
|
|
70
|
+
[](https://github.com/astral-sh/ruff)[](https://pycqa.github.io/isort/)
|
|
70
71
|
[](https://github.com/pre-commit/pre-commit)
|
|
71
72
|
[](https://brainglobe.info/community/developers/index.html)
|
|
72
|
-
[](https://forum.image.sc/tag/brainglobe)
|
|
74
|
+
[](https://bsky.app/profile/brainglobe.info)
|
|
75
|
+
[](https://mastodon.online/@brainglobe)
|
|
74
76
|
# cellfinder
|
|
75
77
|
|
|
76
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).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
cellfinder/__init__.py,sha256=S5oQ3EORuyQTMYC4uUuzGKZ23J3Ya6q-1DOBib1KfiA,1166
|
|
2
2
|
cellfinder/cli_migration_warning.py,sha256=u4nKQiPYmpx0HRqm0PI8wBx78rNiiBSQSGciDoXEq78,1623
|
|
3
3
|
cellfinder/core/__init__.py,sha256=pRFuQsl78HEK0S6gvhJw70QLbjjSBzP-GFO0AtVaGtk,62
|
|
4
|
-
cellfinder/core/main.py,sha256=
|
|
4
|
+
cellfinder/core/main.py,sha256=8VRM05-FvUPLjcBFFYruV_0Yct-UQOW2a0u5ubjBDnw,10875
|
|
5
5
|
cellfinder/core/types.py,sha256=lTqWE4v0SMM0qLAZJdyAzqV1nLgDtobEpglNJcXt160,106
|
|
6
6
|
cellfinder/core/classify/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
cellfinder/core/classify/augment.py,sha256=8dMbM7KhimM6NMgdMC53oHoCfYj1CIB-h3Yk8CZAxPw,6321
|
|
@@ -12,17 +12,17 @@ cellfinder/core/classify/tools.py,sha256=gdWE8cBMlT1pqxBKt6j2az7i7FOMR4N0ds4w9Yn
|
|
|
12
12
|
cellfinder/core/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
cellfinder/core/config/cellfinder.conf,sha256=5i8axif7ekMutKDiVnZRs-LiJrgVQljg_beltidqtNk,56
|
|
14
14
|
cellfinder/core/detect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
cellfinder/core/detect/detect.py,sha256=
|
|
15
|
+
cellfinder/core/detect/detect.py,sha256=DZxots88sG7GgKjXT-35TecQtRXGVEB-tRx12fTZjXU,11245
|
|
16
16
|
cellfinder/core/detect/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
cellfinder/core/detect/filters/setup_filters.py,sha256
|
|
17
|
+
cellfinder/core/detect/filters/setup_filters.py,sha256=-OFQsvMhxRhzHHATghQWjTUNudvo64uY27CHd7FSIk4,16210
|
|
18
18
|
cellfinder/core/detect/filters/plane/__init__.py,sha256=lybcPbVDnurEQeq2FiLq0zR8p_ztarQOlajhdh1Q2-4,40
|
|
19
19
|
cellfinder/core/detect/filters/plane/classical_filter.py,sha256=X5k266tbl9EHRVY5dls53B5IZlmP7U0UB9BsZ1ey_pc,13250
|
|
20
|
-
cellfinder/core/detect/filters/plane/plane_filter.py,sha256=
|
|
20
|
+
cellfinder/core/detect/filters/plane/plane_filter.py,sha256=G22c48ecK7cCQWOutqmy0o5Y06rKTm0dM66-c7qUndk,10503
|
|
21
21
|
cellfinder/core/detect/filters/plane/tile_walker.py,sha256=IiQibvWKnYlgl9h414fRklV7C2xZ0vXNmJ9t89DhYuI,4863
|
|
22
22
|
cellfinder/core/detect/filters/volume/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
cellfinder/core/detect/filters/volume/ball_filter.py,sha256=DSBaHXtJ8TM71xT4fgzwL9E1MTE1euNv0vZ6Ozcx5cQ,14491
|
|
24
|
-
cellfinder/core/detect/filters/volume/structure_detection.py,sha256=
|
|
25
|
-
cellfinder/core/detect/filters/volume/structure_splitting.py,sha256=
|
|
24
|
+
cellfinder/core/detect/filters/volume/structure_detection.py,sha256=jibzhm5WDKDQMO2vMRiqDwxoMKsjNYzUVfp5Dla2sBU,13207
|
|
25
|
+
cellfinder/core/detect/filters/volume/structure_splitting.py,sha256=C4YFamJtJuHZJwC5IJfrFG6TBIUh20kvSyvVR67SEOo,10129
|
|
26
26
|
cellfinder/core/detect/filters/volume/volume_filter.py,sha256=G9uK6rALRTZufrihzz5TVtE1oGpkXNktrFA4nnOIEaM,20573
|
|
27
27
|
cellfinder/core/download/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
cellfinder/core/download/cli.py,sha256=X9L9ZIkWqs58hX8G8q_0AKN4gk8BhydGxI9nLpdHQQE,1764
|
|
@@ -37,25 +37,25 @@ cellfinder/core/tools/source_files.py,sha256=vvwsIMe1ULKvXg_x22L75iqvCyMjEbUjJsD
|
|
|
37
37
|
cellfinder/core/tools/system.py,sha256=WvEzPr7v-ohLDnzf4n13TMcN5OYIAXOEkaSmrHzdnwc,2438
|
|
38
38
|
cellfinder/core/tools/threading.py,sha256=mc-XVEfLHj3AFWYcK1Qh4TKmVuW4mNaxgLBlQmBVViU,14154
|
|
39
39
|
cellfinder/core/tools/tiff.py,sha256=NzIz6wq2GzxmcIhawFMwZADe-uQO2rIG46H7xkpGKLs,2899
|
|
40
|
-
cellfinder/core/tools/tools.py,sha256=
|
|
40
|
+
cellfinder/core/tools/tools.py,sha256=fh8PAcoIGh9U5ChUUPl5a8JY2orR1DqZ3qPGHNxi-zI,9390
|
|
41
41
|
cellfinder/core/train/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
-
cellfinder/core/train/train_yaml.py,sha256=
|
|
42
|
+
cellfinder/core/train/train_yaml.py,sha256=_6XsW82npFRcwHTJK3nUUlQfRbY7IVVAgoXYR2-_IBc,13112
|
|
43
43
|
cellfinder/napari/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
|
-
cellfinder/napari/curation.py,sha256=
|
|
44
|
+
cellfinder/napari/curation.py,sha256=XxrsdK6gHmxgOtmxAJaq-JmheeB4nFJNMrl-0yH--Dc,23464
|
|
45
45
|
cellfinder/napari/input_container.py,sha256=upTkufWF3aOr9sPPK13C2YwqQ6tygULym8oDXrRLXJs,2510
|
|
46
46
|
cellfinder/napari/napari.yaml,sha256=WMR1CIAmYIVyQngbdbomTRZLvlDbb6LxsXsvTRClQnE,921
|
|
47
47
|
cellfinder/napari/sample_data.py,sha256=oUST23q09MM8dxHbUCmO0AjtXG6OlR_32LLqP0EU2UA,732
|
|
48
|
-
cellfinder/napari/utils.py,sha256=
|
|
48
|
+
cellfinder/napari/utils.py,sha256=pXuEoDIUeqgDtEsQg1U7OM3ie0_9rCmxAOZgJOwU-mQ,4009
|
|
49
49
|
cellfinder/napari/detect/__init__.py,sha256=BD9Bg9NTAr6yRTq2A_p58U6j4w5wbY0sdXwhPJ3MSMY,34
|
|
50
|
-
cellfinder/napari/detect/detect.py,sha256=
|
|
51
|
-
cellfinder/napari/detect/detect_containers.py,sha256=
|
|
52
|
-
cellfinder/napari/detect/thread_worker.py,sha256=
|
|
50
|
+
cellfinder/napari/detect/detect.py,sha256=A0FLHnWSEx1h4Lrkx5kmQY4fvgBVYoyla51kWdXcg98,17049
|
|
51
|
+
cellfinder/napari/detect/detect_containers.py,sha256=K4TO7KFac6jzjTt6Mvh6ey4zpO-RHzzs6a8SOfByAfY,6843
|
|
52
|
+
cellfinder/napari/detect/thread_worker.py,sha256=dQitk83sbY8MSx0iSROub3Wnp6nR0HlPOkFvjESm1dQ,3593
|
|
53
53
|
cellfinder/napari/train/__init__.py,sha256=xo4CK-DvSecInGEc2ohcTgQYlH3iylFnGvKTCoq2WkI,35
|
|
54
54
|
cellfinder/napari/train/train.py,sha256=tXrB8j_c293mzhWX5iEs4FvIwV1FpZuaX1rsSP4dgDQ,5830
|
|
55
55
|
cellfinder/napari/train/train_containers.py,sha256=ovPl4ZiH6DUr-CGIYu-iju05z3_rtomyYnCWI6fURKc,4296
|
|
56
|
-
cellfinder-1.
|
|
57
|
-
cellfinder-1.
|
|
58
|
-
cellfinder-1.
|
|
59
|
-
cellfinder-1.
|
|
60
|
-
cellfinder-1.
|
|
61
|
-
cellfinder-1.
|
|
56
|
+
cellfinder-1.9.0.dist-info/licenses/LICENSE,sha256=Tw8iMytIDXLSmcIUsbQmRWojstl9yOWsPCx6ZT6dZLY,1564
|
|
57
|
+
cellfinder-1.9.0.dist-info/METADATA,sha256=EfJdieyhSLKELsEcDv6Go1w7sLfmxZ0n18fQESBQm88,8452
|
|
58
|
+
cellfinder-1.9.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
59
|
+
cellfinder-1.9.0.dist-info/entry_points.txt,sha256=n2n3muDgifENJtTRz1JqYStxvuprROCwmKLt-VxvEHk,248
|
|
60
|
+
cellfinder-1.9.0.dist-info/top_level.txt,sha256=jyTQzX-tDjbsMr6s-E71Oy0IKQzmHTXSk4ZhpG5EDSE,11
|
|
61
|
+
cellfinder-1.9.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|