cellfinder 1.1.0__py3-none-any.whl → 1.1.2__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.
Potentially problematic release.
This version of cellfinder might be problematic. Click here for more details.
- cellfinder/core/classify/cube_generator.py +2 -6
- cellfinder/core/detect/detect.py +73 -4
- cellfinder/core/detect/filters/plane/classical_filter.py +25 -0
- cellfinder/core/detect/filters/volume/ball_filter.py +5 -5
- cellfinder/core/detect/filters/volume/structure_splitting.py +54 -6
- cellfinder/core/detect/filters/volume/volume_filter.py +5 -0
- cellfinder/core/download/cli.py +2 -2
- cellfinder/core/download/download.py +39 -10
- cellfinder/core/main.py +1 -0
- cellfinder/core/tools/prep.py +6 -5
- cellfinder/core/tools/source_files.py +21 -3
- cellfinder/core/train/train_yml.py +4 -1
- cellfinder/napari/__init__.py +0 -3
- cellfinder/napari/curation.py +66 -22
- cellfinder/napari/train/train.py +6 -0
- cellfinder/napari/utils.py +1 -89
- {cellfinder-1.1.0.dist-info → cellfinder-1.1.2.dist-info}/METADATA +6 -6
- {cellfinder-1.1.0.dist-info → cellfinder-1.1.2.dist-info}/RECORD +22 -22
- {cellfinder-1.1.0.dist-info → cellfinder-1.1.2.dist-info}/WHEEL +1 -1
- {cellfinder-1.1.0.dist-info → cellfinder-1.1.2.dist-info}/LICENSE +0 -0
- {cellfinder-1.1.0.dist-info → cellfinder-1.1.2.dist-info}/entry_points.txt +0 -0
- {cellfinder-1.1.0.dist-info → cellfinder-1.1.2.dist-info}/top_level.txt +0 -0
|
@@ -197,9 +197,7 @@ class CubeGeneratorFromFile(Sequence):
|
|
|
197
197
|
"""
|
|
198
198
|
return len(self.batches)
|
|
199
199
|
|
|
200
|
-
def __getitem__(
|
|
201
|
-
self, index: int
|
|
202
|
-
) -> Union[
|
|
200
|
+
def __getitem__(self, index: int) -> Union[
|
|
203
201
|
np.ndarray,
|
|
204
202
|
Tuple[np.ndarray, List[Dict[str, float]]],
|
|
205
203
|
Tuple[np.ndarray, Dict],
|
|
@@ -389,9 +387,7 @@ class CubeGeneratorFromDisk(Sequence):
|
|
|
389
387
|
"""
|
|
390
388
|
return int(np.ceil(len(self.signal_list) / self.batch_size))
|
|
391
389
|
|
|
392
|
-
def __getitem__(
|
|
393
|
-
self, index: int
|
|
394
|
-
) -> Union[
|
|
390
|
+
def __getitem__(self, index: int) -> Union[
|
|
395
391
|
np.ndarray,
|
|
396
392
|
Tuple[np.ndarray, List[Dict[str, float]]],
|
|
397
393
|
Tuple[np.ndarray, Dict],
|
cellfinder/core/detect/detect.py
CHANGED
|
@@ -51,6 +51,16 @@ def calculate_parameters_in_pixels(
|
|
|
51
51
|
ball_xy_size = int(round(ball_xy_size_um / mean_in_plane_pixel_size))
|
|
52
52
|
ball_z_size = int(round(ball_z_size_um / float(voxel_sizes[0])))
|
|
53
53
|
|
|
54
|
+
if ball_z_size == 0:
|
|
55
|
+
raise ValueError(
|
|
56
|
+
"Ball z size has been calculated to be 0 voxels."
|
|
57
|
+
" This may be due to large axial spacing of your data or the "
|
|
58
|
+
"ball_z_size_um parameter being too small. "
|
|
59
|
+
"Please check input parameters are correct. "
|
|
60
|
+
"Note that cellfinder requires high resolution data in all "
|
|
61
|
+
"dimensions, so that cells can be detected in multiple "
|
|
62
|
+
"image planes."
|
|
63
|
+
)
|
|
54
64
|
return soma_diameter, max_cluster_size, ball_xy_size, ball_z_size
|
|
55
65
|
|
|
56
66
|
|
|
@@ -76,11 +86,69 @@ def main(
|
|
|
76
86
|
callback: Optional[Callable[[int], None]] = None,
|
|
77
87
|
) -> List[Cell]:
|
|
78
88
|
"""
|
|
89
|
+
Perform cell candidate detection on a 3D signal array.
|
|
90
|
+
|
|
79
91
|
Parameters
|
|
80
92
|
----------
|
|
93
|
+
signal_array : numpy.ndarray
|
|
94
|
+
3D array representing the signal data.
|
|
95
|
+
|
|
96
|
+
start_plane : int
|
|
97
|
+
Index of the starting plane for detection.
|
|
98
|
+
|
|
99
|
+
end_plane : int
|
|
100
|
+
Index of the ending plane for detection.
|
|
101
|
+
|
|
102
|
+
voxel_sizes : Tuple[float, float, float]
|
|
103
|
+
Tuple of voxel sizes in each dimension (x, y, z).
|
|
104
|
+
|
|
105
|
+
soma_diameter : float
|
|
106
|
+
Diameter of the soma in physical units.
|
|
107
|
+
|
|
108
|
+
max_cluster_size : float
|
|
109
|
+
Maximum size of a cluster in physical units.
|
|
110
|
+
|
|
111
|
+
ball_xy_size : float
|
|
112
|
+
Size of the XY ball used for filtering in physical units.
|
|
113
|
+
|
|
114
|
+
ball_z_size : float
|
|
115
|
+
Size of the Z ball used for filtering in physical units.
|
|
116
|
+
|
|
117
|
+
ball_overlap_fraction : float
|
|
118
|
+
Fraction of overlap allowed between balls.
|
|
119
|
+
|
|
120
|
+
soma_spread_factor : float
|
|
121
|
+
Spread factor for soma size.
|
|
122
|
+
|
|
123
|
+
n_free_cpus : int
|
|
124
|
+
Number of free CPU cores available for parallel processing.
|
|
125
|
+
|
|
126
|
+
log_sigma_size : float
|
|
127
|
+
Size of the sigma for the log filter.
|
|
128
|
+
|
|
129
|
+
n_sds_above_mean_thresh : float
|
|
130
|
+
Number of standard deviations above the mean threshold.
|
|
131
|
+
|
|
132
|
+
outlier_keep : bool, optional
|
|
133
|
+
Whether to keep outliers during detection. Defaults to False.
|
|
134
|
+
|
|
135
|
+
artifact_keep : bool, optional
|
|
136
|
+
Whether to keep artifacts during detection. Defaults to False.
|
|
137
|
+
|
|
138
|
+
save_planes : bool, optional
|
|
139
|
+
Whether to save the planes during detection. Defaults to False.
|
|
140
|
+
|
|
141
|
+
plane_directory : str, optional
|
|
142
|
+
Directory path to save the planes. Defaults to None.
|
|
143
|
+
|
|
81
144
|
callback : Callable[int], optional
|
|
82
145
|
A callback function that is called every time a plane has finished
|
|
83
146
|
being processed. Called with the plane number that has finished.
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
List[Cell]
|
|
151
|
+
List of detected cells.
|
|
84
152
|
"""
|
|
85
153
|
if not np.issubdtype(signal_array.dtype, np.integer):
|
|
86
154
|
raise ValueError(
|
|
@@ -107,6 +175,7 @@ def main(
|
|
|
107
175
|
if end_plane == -1:
|
|
108
176
|
end_plane = len(signal_array)
|
|
109
177
|
signal_array = signal_array[start_plane:end_plane]
|
|
178
|
+
signal_array = signal_array.astype(np.uint32)
|
|
110
179
|
|
|
111
180
|
callback = callback or (lambda *args, **kwargs: None)
|
|
112
181
|
|
|
@@ -169,11 +238,11 @@ def main(
|
|
|
169
238
|
# processes.
|
|
170
239
|
cells = mp_3d_filter.process(async_results, locks, callback=callback)
|
|
171
240
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
)
|
|
241
|
+
time_elapsed = datetime.now() - start_time
|
|
242
|
+
logger.debug(
|
|
243
|
+
f"All Planes done. Found {len(cells)} cells in {format(time_elapsed)}"
|
|
176
244
|
)
|
|
245
|
+
print("Detection complete - all planes done in : {}".format(time_elapsed))
|
|
177
246
|
return cells
|
|
178
247
|
|
|
179
248
|
|
|
@@ -6,6 +6,31 @@ from scipy.signal import medfilt2d
|
|
|
6
6
|
def enhance_peaks(
|
|
7
7
|
img: np.ndarray, clipping_value: float, gaussian_sigma: float = 2.5
|
|
8
8
|
) -> np.ndarray:
|
|
9
|
+
"""
|
|
10
|
+
Enhances the peaks (bright pixels) in an input image.
|
|
11
|
+
|
|
12
|
+
Parameters:
|
|
13
|
+
----------
|
|
14
|
+
img : np.ndarray
|
|
15
|
+
Input image.
|
|
16
|
+
clipping_value : float
|
|
17
|
+
Maximum value for the enhanced image.
|
|
18
|
+
gaussian_sigma : float, optional
|
|
19
|
+
Standard deviation for the Gaussian filter. Default is 2.5.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
-------
|
|
23
|
+
np.ndarray
|
|
24
|
+
Enhanced image with peaks.
|
|
25
|
+
|
|
26
|
+
Notes:
|
|
27
|
+
------
|
|
28
|
+
The enhancement process includes the following steps:
|
|
29
|
+
1. Applying a 2D median filter.
|
|
30
|
+
2. Applying a Laplacian of Gaussian filter (LoG).
|
|
31
|
+
3. Multiplying by -1 (bright spots respond negative in a LoG).
|
|
32
|
+
4. Rescaling image values to range from 0 to clipping value.
|
|
33
|
+
"""
|
|
9
34
|
type_in = img.dtype
|
|
10
35
|
filtered_img = medfilt2d(img.astype(np.float64))
|
|
11
36
|
filtered_img = gaussian_filter(filtered_img, gaussian_sigma)
|
|
@@ -104,7 +104,7 @@ class BallFilter:
|
|
|
104
104
|
|
|
105
105
|
# Stores the current planes that are being filtered
|
|
106
106
|
self.volume = np.empty(
|
|
107
|
-
(plane_width, plane_height, ball_z_size), dtype=np.
|
|
107
|
+
(plane_width, plane_height, ball_z_size), dtype=np.uint32
|
|
108
108
|
)
|
|
109
109
|
# Index of the middle plane in the volume
|
|
110
110
|
self.middle_z_idx = int(np.floor(ball_z_size / 2))
|
|
@@ -165,7 +165,7 @@ class BallFilter:
|
|
|
165
165
|
Get the plane in the middle of self.volume.
|
|
166
166
|
"""
|
|
167
167
|
z = self.middle_z_idx
|
|
168
|
-
return np.array(self.volume[:, :, z], dtype=np.
|
|
168
|
+
return np.array(self.volume[:, :, z], dtype=np.uint32)
|
|
169
169
|
|
|
170
170
|
def walk(self) -> None: # Highly optimised because most time critical
|
|
171
171
|
ball_radius = self.ball_xy_size // 2
|
|
@@ -327,6 +327,6 @@ def _walk(
|
|
|
327
327
|
THRESHOLD_VALUE,
|
|
328
328
|
kernel,
|
|
329
329
|
):
|
|
330
|
-
volume[
|
|
331
|
-
|
|
332
|
-
|
|
330
|
+
volume[ball_centre_x, ball_centre_y, middle_z] = (
|
|
331
|
+
SOMA_CENTRE_VALUE
|
|
332
|
+
)
|
|
@@ -28,7 +28,7 @@ def coords_to_volume(
|
|
|
28
28
|
expanded_shape = [
|
|
29
29
|
dim_size + ball_diameter for dim_size in get_shape(xs, ys, zs)
|
|
30
30
|
]
|
|
31
|
-
volume = np.zeros(expanded_shape, dtype=np.
|
|
31
|
+
volume = np.zeros(expanded_shape, dtype=np.uint32)
|
|
32
32
|
|
|
33
33
|
x_min, y_min, z_min = xs.min(), ys.min(), zs.min()
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ def coords_to_volume(
|
|
|
38
38
|
|
|
39
39
|
# OPTIMISE: vectorize
|
|
40
40
|
for rel_x, rel_y, rel_z in zip(relative_xs, relative_ys, relative_zs):
|
|
41
|
-
volume[rel_x, rel_y, rel_z] =
|
|
41
|
+
volume[rel_x, rel_y, rel_z] = np.iinfo(volume.dtype).max - 1
|
|
42
42
|
return volume
|
|
43
43
|
|
|
44
44
|
|
|
@@ -49,6 +49,26 @@ def ball_filter_imgs(
|
|
|
49
49
|
ball_xy_size: int = 3,
|
|
50
50
|
ball_z_size: int = 3,
|
|
51
51
|
) -> Tuple[np.ndarray, np.ndarray]:
|
|
52
|
+
"""
|
|
53
|
+
Apply ball filtering to a 3D volume and detect cell centres.
|
|
54
|
+
|
|
55
|
+
Uses the `BallFilter` class to perform ball filtering on the volume
|
|
56
|
+
and the `CellDetector` class to detect cell centres.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
volume (np.ndarray): The 3D volume to be filtered.
|
|
60
|
+
threshold_value (int): The threshold value for ball filtering.
|
|
61
|
+
soma_centre_value (int): The value representing the soma centre.
|
|
62
|
+
ball_xy_size (int, optional):
|
|
63
|
+
The size of the ball filter in the XY plane. Defaults to 3.
|
|
64
|
+
ball_z_size (int, optional):
|
|
65
|
+
The size of the ball filter in the Z plane. Defaults to 3.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Tuple[np.ndarray, np.ndarray]:
|
|
69
|
+
A tuple containing the filtered volume and the cell centres.
|
|
70
|
+
|
|
71
|
+
"""
|
|
52
72
|
# OPTIMISE: reuse ball filter instance
|
|
53
73
|
|
|
54
74
|
good_tiles_mask = np.ones((1, 1, volume.shape[2]), dtype=bool)
|
|
@@ -71,10 +91,10 @@ def ball_filter_imgs(
|
|
|
71
91
|
)
|
|
72
92
|
|
|
73
93
|
# FIXME: hard coded type
|
|
74
|
-
ball_filtered_volume = np.zeros(volume.shape, dtype=np.
|
|
94
|
+
ball_filtered_volume = np.zeros(volume.shape, dtype=np.uint32)
|
|
75
95
|
previous_plane = None
|
|
76
96
|
for z in range(volume.shape[2]):
|
|
77
|
-
bf.append(volume[:, :, z].astype(np.
|
|
97
|
+
bf.append(volume[:, :, z].astype(np.uint32), good_tiles_mask[:, :, z])
|
|
78
98
|
if bf.ready:
|
|
79
99
|
bf.walk()
|
|
80
100
|
middle_plane = bf.get_middle_plane()
|
|
@@ -89,11 +109,24 @@ def ball_filter_imgs(
|
|
|
89
109
|
def iterative_ball_filter(
|
|
90
110
|
volume: np.ndarray, n_iter: int = 10
|
|
91
111
|
) -> Tuple[List[int], List[np.ndarray]]:
|
|
112
|
+
"""
|
|
113
|
+
Apply iterative ball filtering to the given volume.
|
|
114
|
+
The volume is eroded at each iteration, by subtracting 1 from the volume.
|
|
115
|
+
|
|
116
|
+
Parameters:
|
|
117
|
+
volume (np.ndarray): The input volume.
|
|
118
|
+
n_iter (int): The number of iterations to perform. Default is 10.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Tuple[List[int], List[np.ndarray]]: A tuple containing two lists:
|
|
122
|
+
The structures found in each iteration.
|
|
123
|
+
The cell centres found in each iteration.
|
|
124
|
+
"""
|
|
92
125
|
ns = []
|
|
93
126
|
centres = []
|
|
94
127
|
|
|
95
|
-
threshold_value =
|
|
96
|
-
soma_centre_value =
|
|
128
|
+
threshold_value = np.iinfo(volume.dtype).max - 1
|
|
129
|
+
soma_centre_value = np.iinfo(volume.dtype).max
|
|
97
130
|
|
|
98
131
|
vol = volume.copy() # TODO: check if required
|
|
99
132
|
|
|
@@ -131,6 +164,21 @@ def check_centre_in_cuboid(centre: np.ndarray, max_coords: np.ndarray) -> bool:
|
|
|
131
164
|
def split_cells(
|
|
132
165
|
cell_points: np.ndarray, outlier_keep: bool = False
|
|
133
166
|
) -> np.ndarray:
|
|
167
|
+
"""
|
|
168
|
+
Split the given cell points into individual cell centres.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
cell_points (np.ndarray): Array of cell points with shape (N, 3),
|
|
172
|
+
where N is the number of cell points and each point is represented
|
|
173
|
+
by its x, y, and z coordinates.
|
|
174
|
+
outlier_keep (bool, optional): Flag indicating whether to keep outliers
|
|
175
|
+
during the splitting process. Defaults to False.
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
np.ndarray: Array of absolute cell centres with shape (M, 3),
|
|
179
|
+
where M is the number of individual cells and each centre is
|
|
180
|
+
represented by its x, y, and z coordinates.
|
|
181
|
+
"""
|
|
134
182
|
orig_centre = get_structure_centre(cell_points)
|
|
135
183
|
|
|
136
184
|
xs = cell_points[:, 0]
|
|
@@ -142,6 +142,10 @@ class VolumeFilter(object):
|
|
|
142
142
|
)
|
|
143
143
|
|
|
144
144
|
cells = []
|
|
145
|
+
|
|
146
|
+
logger.debug(
|
|
147
|
+
f"Processing {len(self.cell_detector.coords_maps.items())} cells"
|
|
148
|
+
)
|
|
145
149
|
for cell_id, cell_points in self.cell_detector.coords_maps.items():
|
|
146
150
|
cell_volume = len(cell_points)
|
|
147
151
|
|
|
@@ -191,6 +195,7 @@ class VolumeFilter(object):
|
|
|
191
195
|
)
|
|
192
196
|
)
|
|
193
197
|
|
|
198
|
+
logger.debug("Finished splitting cell clusters.")
|
|
194
199
|
return cells
|
|
195
200
|
|
|
196
201
|
|
cellfinder/core/download/cli.py
CHANGED
|
@@ -3,7 +3,7 @@ from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from cellfinder.core.download import models
|
|
6
|
-
from cellfinder.core.download.download import
|
|
6
|
+
from cellfinder.core.download.download import amend_user_configuration
|
|
7
7
|
|
|
8
8
|
home = Path.home()
|
|
9
9
|
DEFAULT_DOWNLOAD_DIRECTORY = home / ".cellfinder"
|
|
@@ -65,7 +65,7 @@ def main():
|
|
|
65
65
|
model_path = models.main(args.model, args.install_path)
|
|
66
66
|
|
|
67
67
|
if not args.no_amend_config:
|
|
68
|
-
|
|
68
|
+
amend_user_configuration(new_model_path=model_path)
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
if __name__ == "__main__":
|
|
@@ -7,8 +7,8 @@ from brainglobe_utils.general.config import get_config_obj
|
|
|
7
7
|
from brainglobe_utils.general.system import disk_free_gb
|
|
8
8
|
|
|
9
9
|
from cellfinder.core.tools.source_files import (
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
default_configuration_path,
|
|
11
|
+
user_specific_configuration_path,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
|
|
@@ -75,16 +75,45 @@ def download(
|
|
|
75
75
|
os.remove(download_path)
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
new_config = source_custom_config_cellfinder()
|
|
83
|
-
if new_model_path is not None:
|
|
84
|
-
write_model_to_cfg(new_model_path, original_config, new_config)
|
|
78
|
+
def amend_user_configuration(new_model_path=None) -> None:
|
|
79
|
+
"""
|
|
80
|
+
Amends the user configuration to contain the configuration
|
|
81
|
+
in new_model_path, if specified.
|
|
85
82
|
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
new_model_path : str, optional
|
|
86
|
+
The path to the new model configuration.
|
|
87
|
+
"""
|
|
88
|
+
print("(Over-)writing custom user configuration")
|
|
86
89
|
|
|
87
|
-
|
|
90
|
+
original_config = default_configuration_path()
|
|
91
|
+
new_config = user_specific_configuration_path()
|
|
92
|
+
if new_model_path is not None:
|
|
93
|
+
write_model_to_config(new_model_path, original_config, new_config)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def write_model_to_config(new_model_path, orig_config, custom_config):
|
|
97
|
+
"""
|
|
98
|
+
Update the model path in the custom configuration file, by
|
|
99
|
+
reading the lines in the original configuration file, replacing
|
|
100
|
+
the line starting with "model_path =" and writing these
|
|
101
|
+
lines to the custom file.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
new_model_path : str
|
|
106
|
+
The new path to the model.
|
|
107
|
+
orig_config : str
|
|
108
|
+
The path to the original configuration file.
|
|
109
|
+
custom_config : str
|
|
110
|
+
The path to the custom configuration file to be created.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
None
|
|
115
|
+
|
|
116
|
+
"""
|
|
88
117
|
config_obj = get_config_obj(orig_config)
|
|
89
118
|
model_conf = config_obj["model"]
|
|
90
119
|
orig_path = model_conf["model_path"]
|
cellfinder/core/main.py
CHANGED
cellfinder/core/tools/prep.py
CHANGED
|
@@ -3,6 +3,7 @@ prep
|
|
|
3
3
|
==================
|
|
4
4
|
Functions to prepare files and directories needed for other functions
|
|
5
5
|
"""
|
|
6
|
+
|
|
6
7
|
import os
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from typing import Optional
|
|
@@ -13,8 +14,8 @@ from brainglobe_utils.general.system import get_num_processes
|
|
|
13
14
|
import cellfinder.core.tools.tf as tf_tools
|
|
14
15
|
from cellfinder.core import logger
|
|
15
16
|
from cellfinder.core.download import models as model_download
|
|
16
|
-
from cellfinder.core.download.download import
|
|
17
|
-
from cellfinder.core.tools.source_files import
|
|
17
|
+
from cellfinder.core.download.download import amend_user_configuration
|
|
18
|
+
from cellfinder.core.tools.source_files import user_specific_configuration_path
|
|
18
19
|
|
|
19
20
|
home = Path.home()
|
|
20
21
|
DEFAULT_INSTALL_PATH = home / ".cellfinder"
|
|
@@ -48,18 +49,18 @@ def prep_models(
|
|
|
48
49
|
if model_weights_path is None:
|
|
49
50
|
logger.debug("No model supplied, so using the default")
|
|
50
51
|
|
|
51
|
-
config_file =
|
|
52
|
+
config_file = user_specific_configuration_path()
|
|
52
53
|
|
|
53
54
|
if not Path(config_file).exists():
|
|
54
55
|
logger.debug("Custom config does not exist, downloading models")
|
|
55
56
|
model_path = model_download.main(model_name, install_path)
|
|
56
|
-
|
|
57
|
+
amend_user_configuration(new_model_path=model_path)
|
|
57
58
|
|
|
58
59
|
model_weights = get_model_weights(config_file)
|
|
59
60
|
if not model_weights.exists():
|
|
60
61
|
logger.debug("Model weights do not exist, downloading")
|
|
61
62
|
model_path = model_download.main(model_name, install_path)
|
|
62
|
-
|
|
63
|
+
amend_user_configuration(new_model_path=model_path)
|
|
63
64
|
model_weights = get_model_weights(config_file)
|
|
64
65
|
else:
|
|
65
66
|
model_weights = Path(model_weights_path)
|
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def
|
|
4
|
+
def default_configuration_path():
|
|
5
|
+
"""
|
|
6
|
+
Returns the default configuration path for cellfinder.
|
|
7
|
+
|
|
8
|
+
Returns:
|
|
9
|
+
Path: The default configuration path.
|
|
10
|
+
"""
|
|
5
11
|
return Path(__file__).parent.parent / "config" / "cellfinder.conf"
|
|
6
12
|
|
|
7
13
|
|
|
8
|
-
def
|
|
9
|
-
|
|
14
|
+
def user_specific_configuration_path():
|
|
15
|
+
"""
|
|
16
|
+
Returns the path to the user-specific configuration file for cellfinder.
|
|
17
|
+
|
|
18
|
+
This function returns the path to the user-specific configuration file
|
|
19
|
+
for cellfinder. The user-specific configuration file is located in the
|
|
20
|
+
user's home directory under the ".cellfinder" folder and is named
|
|
21
|
+
"cellfinder.conf.custom".
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Path: The path to the custom configuration file.
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
return Path.home() / ".cellfinder" / "cellfinder.conf.custom"
|
|
@@ -338,7 +338,10 @@ def run(
|
|
|
338
338
|
|
|
339
339
|
ensure_directory_exists(output_dir)
|
|
340
340
|
model_weights = prep_model_weights(
|
|
341
|
-
|
|
341
|
+
model_weights=model_weights,
|
|
342
|
+
install_path=install_path,
|
|
343
|
+
model_name=model,
|
|
344
|
+
n_free_cpus=n_free_cpus,
|
|
342
345
|
)
|
|
343
346
|
|
|
344
347
|
yaml_contents = parse_yaml(yaml_file)
|
cellfinder/napari/__init__.py
CHANGED
cellfinder/napari/curation.py
CHANGED
|
@@ -6,7 +6,10 @@ import numpy as np
|
|
|
6
6
|
import tifffile
|
|
7
7
|
from brainglobe_napari_io.cellfinder.utils import convert_layer_to_cells
|
|
8
8
|
from brainglobe_utils.cells.cells import Cell
|
|
9
|
+
from brainglobe_utils.general.system import delete_directory_contents
|
|
9
10
|
from brainglobe_utils.IO.yaml import save_yaml
|
|
11
|
+
from brainglobe_utils.qtpy.dialog import display_warning
|
|
12
|
+
from brainglobe_utils.qtpy.interaction import add_button, add_combobox
|
|
10
13
|
from magicgui.widgets import ProgressBar
|
|
11
14
|
from napari.qt.threading import thread_worker
|
|
12
15
|
from napari.utils.notifications import show_info
|
|
@@ -20,8 +23,6 @@ from qtpy.QtWidgets import (
|
|
|
20
23
|
QWidget,
|
|
21
24
|
)
|
|
22
25
|
|
|
23
|
-
from .utils import add_button, add_combobox, display_question
|
|
24
|
-
|
|
25
26
|
# Constants used throughout
|
|
26
27
|
WINDOW_HEIGHT = 750
|
|
27
28
|
WINDOW_WIDTH = 1500
|
|
@@ -69,6 +70,7 @@ class CurationWidget(QWidget):
|
|
|
69
70
|
self.output_directory: Optional[Path] = None
|
|
70
71
|
|
|
71
72
|
self.setup_main_layout()
|
|
73
|
+
self.setup_keybindings()
|
|
72
74
|
|
|
73
75
|
@self.viewer.layers.events.connect
|
|
74
76
|
def update_layer_list(v: napari.viewer.Viewer):
|
|
@@ -173,33 +175,35 @@ class CurationWidget(QWidget):
|
|
|
173
175
|
self.load_data_layout,
|
|
174
176
|
"Training_data (non_cells)",
|
|
175
177
|
self.point_layer_names,
|
|
176
|
-
4,
|
|
178
|
+
row=4,
|
|
177
179
|
callback=self.set_training_data_non_cell,
|
|
178
180
|
)
|
|
179
181
|
self.mark_as_cell_button = add_button(
|
|
180
182
|
"Mark as cell(s)",
|
|
181
183
|
self.load_data_layout,
|
|
182
184
|
self.mark_as_cell,
|
|
183
|
-
5,
|
|
185
|
+
row=5,
|
|
186
|
+
tooltip="Mark all selected points as non cell. Shortcut: 'c'",
|
|
184
187
|
)
|
|
185
188
|
self.mark_as_non_cell_button = add_button(
|
|
186
189
|
"Mark as non cell(s)",
|
|
187
190
|
self.load_data_layout,
|
|
188
191
|
self.mark_as_non_cell,
|
|
189
|
-
5,
|
|
192
|
+
row=5,
|
|
190
193
|
column=1,
|
|
194
|
+
tooltip="Mark all selected points as non cell. Shortcut: 'x'",
|
|
191
195
|
)
|
|
192
196
|
self.add_training_data_button = add_button(
|
|
193
197
|
"Add training data layers",
|
|
194
198
|
self.load_data_layout,
|
|
195
199
|
self.add_training_data,
|
|
196
|
-
6,
|
|
200
|
+
row=6,
|
|
197
201
|
)
|
|
198
202
|
self.save_training_data_button = add_button(
|
|
199
203
|
"Save training data",
|
|
200
204
|
self.load_data_layout,
|
|
201
205
|
self.save_training_data,
|
|
202
|
-
6,
|
|
206
|
+
row=6,
|
|
203
207
|
column=1,
|
|
204
208
|
)
|
|
205
209
|
self.load_data_layout.setColumnMinimumWidth(0, COLUMN_WIDTH)
|
|
@@ -207,6 +211,10 @@ class CurationWidget(QWidget):
|
|
|
207
211
|
self.load_data_panel.setVisible(True)
|
|
208
212
|
self.layout.addWidget(self.load_data_panel, row, column, 1, 1)
|
|
209
213
|
|
|
214
|
+
def setup_keybindings(self):
|
|
215
|
+
self.viewer.bind_key("c", self.mark_as_cell)
|
|
216
|
+
self.viewer.bind_key("x", self.mark_as_non_cell)
|
|
217
|
+
|
|
210
218
|
def set_signal_image(self):
|
|
211
219
|
"""
|
|
212
220
|
Set signal layer from current signal text box selection.
|
|
@@ -245,9 +253,9 @@ class CurationWidget(QWidget):
|
|
|
245
253
|
self.training_data_non_cell_layer = self.viewer.layers[
|
|
246
254
|
self.training_data_non_cell_choice.currentText()
|
|
247
255
|
]
|
|
248
|
-
self.training_data_non_cell_layer.metadata[
|
|
249
|
-
|
|
250
|
-
|
|
256
|
+
self.training_data_non_cell_layer.metadata["point_type"] = (
|
|
257
|
+
Cell.UNKNOWN
|
|
258
|
+
)
|
|
251
259
|
self.training_data_non_cell_layer.metadata["training_data"] = True
|
|
252
260
|
|
|
253
261
|
def add_training_data(self):
|
|
@@ -256,7 +264,7 @@ class CurationWidget(QWidget):
|
|
|
256
264
|
|
|
257
265
|
overwrite = False
|
|
258
266
|
if self.training_data_cell_layer or self.training_data_non_cell_layer:
|
|
259
|
-
overwrite =
|
|
267
|
+
overwrite = display_warning(
|
|
260
268
|
self,
|
|
261
269
|
"Training data layers exist",
|
|
262
270
|
"Training data layers already exist, "
|
|
@@ -303,10 +311,10 @@ class CurationWidget(QWidget):
|
|
|
303
311
|
)
|
|
304
312
|
self.training_data_non_cell_choice.setCurrentText(non_cell_name)
|
|
305
313
|
|
|
306
|
-
def mark_as_cell(self):
|
|
314
|
+
def mark_as_cell(self, viewer=None):
|
|
307
315
|
self.mark_point_as_type("cell")
|
|
308
316
|
|
|
309
|
-
def mark_as_non_cell(self):
|
|
317
|
+
def mark_as_non_cell(self, viewer=None):
|
|
310
318
|
self.mark_point_as_type("non-cell")
|
|
311
319
|
|
|
312
320
|
def mark_point_as_type(self, point_type: str):
|
|
@@ -363,7 +371,10 @@ class CurationWidget(QWidget):
|
|
|
363
371
|
)
|
|
364
372
|
|
|
365
373
|
def save_training_data(
|
|
366
|
-
self,
|
|
374
|
+
self,
|
|
375
|
+
*,
|
|
376
|
+
block: bool = False,
|
|
377
|
+
prompt_for_directory: bool = True,
|
|
367
378
|
) -> None:
|
|
368
379
|
"""
|
|
369
380
|
Parameters
|
|
@@ -373,16 +384,45 @@ class CurationWidget(QWidget):
|
|
|
373
384
|
prompt_for_directory :
|
|
374
385
|
If `True` show a file dialog for the user to select a directory.
|
|
375
386
|
"""
|
|
387
|
+
|
|
376
388
|
if self.is_data_extractable():
|
|
377
389
|
if prompt_for_directory:
|
|
378
390
|
self.get_output_directory()
|
|
391
|
+
# if the directory is not empty
|
|
392
|
+
if any(self.output_directory.iterdir()):
|
|
393
|
+
choice = display_warning(
|
|
394
|
+
self,
|
|
395
|
+
"About to save training data",
|
|
396
|
+
"Existing files will be will be deleted. Proceed?",
|
|
397
|
+
)
|
|
398
|
+
if not choice:
|
|
399
|
+
return
|
|
379
400
|
if self.output_directory is not None:
|
|
401
|
+
self.__prep_directories_for_save()
|
|
380
402
|
self.__extract_cubes(block=block)
|
|
381
403
|
self.__save_yaml_file()
|
|
382
404
|
show_info("Done")
|
|
383
405
|
|
|
384
406
|
self.update_status_label("Ready")
|
|
385
407
|
|
|
408
|
+
def __prep_directories_for_save(self):
|
|
409
|
+
self.yaml_filename = self.output_directory / "training.yml"
|
|
410
|
+
self.cell_cube_dir = self.output_directory / "cells"
|
|
411
|
+
self.no_cell_cube_dir = self.output_directory / "non_cells"
|
|
412
|
+
|
|
413
|
+
self.__delete_existing_saved_training_data()
|
|
414
|
+
|
|
415
|
+
def __delete_existing_saved_training_data(self):
|
|
416
|
+
self.yaml_filename.unlink(missing_ok=True)
|
|
417
|
+
for directory in (
|
|
418
|
+
self.cell_cube_dir,
|
|
419
|
+
self.no_cell_cube_dir,
|
|
420
|
+
):
|
|
421
|
+
if directory.exists():
|
|
422
|
+
delete_directory_contents(directory)
|
|
423
|
+
else:
|
|
424
|
+
directory.mkdir(exist_ok=True, parents=True)
|
|
425
|
+
|
|
386
426
|
def __extract_cubes(self, *, block=False):
|
|
387
427
|
"""
|
|
388
428
|
Parameters
|
|
@@ -489,18 +529,16 @@ class CurationWidget(QWidget):
|
|
|
489
529
|
self.non_cells_to_extract = list(set(self.non_cells_to_extract))
|
|
490
530
|
|
|
491
531
|
def __save_yaml_file(self):
|
|
492
|
-
# TODO: implement this in a portable way
|
|
493
|
-
yaml_filename = self.output_directory / "training.yml"
|
|
494
532
|
yaml_section = [
|
|
495
533
|
{
|
|
496
|
-
"cube_dir": str(self.
|
|
534
|
+
"cube_dir": str(self.cell_cube_dir),
|
|
497
535
|
"cell_def": "",
|
|
498
536
|
"type": "cell",
|
|
499
537
|
"signal_channel": 0,
|
|
500
538
|
"bg_channel": 1,
|
|
501
539
|
},
|
|
502
540
|
{
|
|
503
|
-
"cube_dir": str(self.
|
|
541
|
+
"cube_dir": str(self.no_cell_cube_dir),
|
|
504
542
|
"cell_def": "",
|
|
505
543
|
"type": "no_cell",
|
|
506
544
|
"signal_channel": 0,
|
|
@@ -509,7 +547,7 @@ class CurationWidget(QWidget):
|
|
|
509
547
|
]
|
|
510
548
|
|
|
511
549
|
yaml_contents = {"data": yaml_section}
|
|
512
|
-
save_yaml(yaml_contents, yaml_filename)
|
|
550
|
+
save_yaml(yaml_contents, self.yaml_filename)
|
|
513
551
|
|
|
514
552
|
def update_progress(self, attributes: dict):
|
|
515
553
|
"""
|
|
@@ -538,9 +576,15 @@ class CurationWidget(QWidget):
|
|
|
538
576
|
"non_cells": self.non_cells_to_extract,
|
|
539
577
|
}
|
|
540
578
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
579
|
+
directories = {
|
|
580
|
+
"cells": self.cell_cube_dir,
|
|
581
|
+
"non_cells": self.no_cell_cube_dir,
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
for cell_type in ["cells", "non_cells"]:
|
|
585
|
+
cell_type_output_directory = directories[cell_type]
|
|
586
|
+
cell_list = to_extract[cell_type]
|
|
587
|
+
|
|
544
588
|
self.update_status_label(f"Saving {cell_type}...")
|
|
545
589
|
|
|
546
590
|
cube_generator = CubeGeneratorFromFile(
|
cellfinder/napari/train/train.py
CHANGED
|
@@ -5,6 +5,7 @@ from magicgui import magicgui
|
|
|
5
5
|
from magicgui.widgets import FunctionGui, PushButton
|
|
6
6
|
from napari.qt.threading import thread_worker
|
|
7
7
|
from napari.utils.notifications import show_info
|
|
8
|
+
from qtpy.QtWidgets import QScrollArea
|
|
8
9
|
|
|
9
10
|
from cellfinder.core.train.train_yml import run as train_yml
|
|
10
11
|
from cellfinder.napari.utils import (
|
|
@@ -48,6 +49,7 @@ def training_widget() -> FunctionGui:
|
|
|
48
49
|
**MiscTrainingInputs.widget_representation(),
|
|
49
50
|
call_button=True,
|
|
50
51
|
reset_button=dict(widget_type="PushButton", text="Reset defaults"),
|
|
52
|
+
scrollable=True,
|
|
51
53
|
)
|
|
52
54
|
def widget(
|
|
53
55
|
header: dict,
|
|
@@ -175,4 +177,8 @@ def training_widget() -> FunctionGui:
|
|
|
175
177
|
if value is not None:
|
|
176
178
|
getattr(widget, name).value = value
|
|
177
179
|
|
|
180
|
+
scroll = QScrollArea()
|
|
181
|
+
scroll.setWidget(widget._widget._qwidget)
|
|
182
|
+
widget._widget._qwidget = scroll
|
|
183
|
+
|
|
178
184
|
return widget
|
cellfinder/napari/utils.py
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import List, Tuple
|
|
2
2
|
|
|
3
3
|
import napari
|
|
4
4
|
import numpy as np
|
|
5
5
|
import pandas as pd
|
|
6
6
|
from brainglobe_utils.cells.cells import Cell
|
|
7
7
|
from pkg_resources import resource_filename
|
|
8
|
-
from qtpy.QtWidgets import (
|
|
9
|
-
QComboBox,
|
|
10
|
-
QLabel,
|
|
11
|
-
QLayout,
|
|
12
|
-
QMessageBox,
|
|
13
|
-
QPushButton,
|
|
14
|
-
QWidget,
|
|
15
|
-
)
|
|
16
8
|
|
|
17
9
|
brainglobe_logo = resource_filename(
|
|
18
10
|
"cellfinder", "napari/images/brainglobe.png"
|
|
@@ -98,83 +90,3 @@ def cells_to_array(cells: List[Cell]) -> Tuple[np.ndarray, np.ndarray]:
|
|
|
98
90
|
points = cells_df_as_np(df[df["type"] == Cell.CELL])
|
|
99
91
|
rejected = cells_df_as_np(df[df["type"] == Cell.UNKNOWN])
|
|
100
92
|
return points, rejected
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def add_combobox(
|
|
104
|
-
layout: QLayout,
|
|
105
|
-
label: str,
|
|
106
|
-
items: List[str],
|
|
107
|
-
row: int,
|
|
108
|
-
column: int = 0,
|
|
109
|
-
label_stack: bool = False,
|
|
110
|
-
callback=None,
|
|
111
|
-
width: int = 150,
|
|
112
|
-
) -> Tuple[QComboBox, Optional[QLabel]]:
|
|
113
|
-
"""
|
|
114
|
-
Add a selection box to *layout*.
|
|
115
|
-
"""
|
|
116
|
-
if label_stack:
|
|
117
|
-
combobox_row = row + 1
|
|
118
|
-
combobox_column = column
|
|
119
|
-
else:
|
|
120
|
-
combobox_row = row
|
|
121
|
-
combobox_column = column + 1
|
|
122
|
-
combobox = QComboBox()
|
|
123
|
-
combobox.addItems(items)
|
|
124
|
-
if callback:
|
|
125
|
-
combobox.currentIndexChanged.connect(callback)
|
|
126
|
-
combobox.setMaximumWidth = width
|
|
127
|
-
|
|
128
|
-
if label is not None:
|
|
129
|
-
combobox_label = QLabel(label)
|
|
130
|
-
combobox_label.setMaximumWidth = width
|
|
131
|
-
layout.addWidget(combobox_label, row, column)
|
|
132
|
-
else:
|
|
133
|
-
combobox_label = None
|
|
134
|
-
|
|
135
|
-
layout.addWidget(combobox, combobox_row, combobox_column)
|
|
136
|
-
return combobox, combobox_label
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def add_button(
|
|
140
|
-
label: str,
|
|
141
|
-
layout: QLayout,
|
|
142
|
-
connected_function: Callable,
|
|
143
|
-
row: int,
|
|
144
|
-
column: int = 0,
|
|
145
|
-
visibility: bool = True,
|
|
146
|
-
minimum_width: int = 0,
|
|
147
|
-
alignment: str = "center",
|
|
148
|
-
) -> QPushButton:
|
|
149
|
-
"""
|
|
150
|
-
Add a button to *layout*.
|
|
151
|
-
"""
|
|
152
|
-
button = QPushButton(label)
|
|
153
|
-
if alignment == "center":
|
|
154
|
-
pass
|
|
155
|
-
elif alignment == "left":
|
|
156
|
-
button.setStyleSheet("QPushButton { text-align: left; }")
|
|
157
|
-
elif alignment == "right":
|
|
158
|
-
button.setStyleSheet("QPushButton { text-align: right; }")
|
|
159
|
-
|
|
160
|
-
button.setVisible(visibility)
|
|
161
|
-
button.setMinimumWidth(minimum_width)
|
|
162
|
-
layout.addWidget(button, row, column)
|
|
163
|
-
button.clicked.connect(connected_function)
|
|
164
|
-
return button
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
def display_question(widget: QWidget, title: str, message: str) -> bool:
|
|
168
|
-
"""
|
|
169
|
-
Display a warning in a pop up that informs about overwriting files.
|
|
170
|
-
"""
|
|
171
|
-
message_reply = QMessageBox.question(
|
|
172
|
-
widget,
|
|
173
|
-
title,
|
|
174
|
-
message,
|
|
175
|
-
QMessageBox.Yes | QMessageBox.Cancel,
|
|
176
|
-
)
|
|
177
|
-
if message_reply == QMessageBox.Yes:
|
|
178
|
-
return True
|
|
179
|
-
else:
|
|
180
|
-
return False
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cellfinder
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
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
|
|
7
7
|
Project-URL: Homepage, https://brainglobe.info/documentation/cellfinder/index.html
|
|
8
|
-
Project-URL: Source Code, https://github.com/brainglobe/cellfinder
|
|
9
|
-
Project-URL: Bug Tracker, https://github.com/brainglobe/cellfinder
|
|
8
|
+
Project-URL: Source Code, https://github.com/brainglobe/cellfinder
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/brainglobe/cellfinder/issues
|
|
10
10
|
Project-URL: Documentation, https://brainglobe.info/documentation/cellfinder/index.html
|
|
11
11
|
Project-URL: User Support, https://forum.image.sc/tag/brainglobe
|
|
12
12
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -22,7 +22,8 @@ Classifier: Topic :: Scientific/Engineering :: Image Recognition
|
|
|
22
22
|
Requires-Python: >=3.9
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
|
-
Requires-Dist: brainglobe-utils
|
|
25
|
+
Requires-Dist: brainglobe-utils >=0.4.2
|
|
26
|
+
Requires-Dist: brainglobe-napari-io >=0.3.4
|
|
26
27
|
Requires-Dist: dask[array]
|
|
27
28
|
Requires-Dist: fancylog >=0.0.7
|
|
28
29
|
Requires-Dist: natsort
|
|
@@ -39,7 +40,6 @@ Requires-Dist: black ; extra == 'dev'
|
|
|
39
40
|
Requires-Dist: pre-commit ; extra == 'dev'
|
|
40
41
|
Requires-Dist: pyinstrument ; extra == 'dev'
|
|
41
42
|
Requires-Dist: pytest-cov ; extra == 'dev'
|
|
42
|
-
Requires-Dist: pytest-lazy-fixture ; extra == 'dev'
|
|
43
43
|
Requires-Dist: pytest-mock ; extra == 'dev'
|
|
44
44
|
Requires-Dist: pytest-qt ; extra == 'dev'
|
|
45
45
|
Requires-Dist: pytest-timeout ; extra == 'dev'
|
|
@@ -59,7 +59,7 @@ Requires-Dist: qtpy ; extra == 'napari'
|
|
|
59
59
|
[](https://pepy.tech/project/cellfinder)
|
|
60
60
|
[](https://pypi.org/project/cellfinder)
|
|
61
61
|
[](https://github.com/brainglobe/cellfinder)
|
|
62
|
-
[](https://github.com/brainglobe/cellfinder/actions)
|
|
63
63
|
[](https://codecov.io/gh/brainglobe/cellfinder)
|
|
64
64
|
[](https://github.com/python/black)
|
|
65
65
|
[](https://pycqa.github.io/isort/)
|
|
@@ -1,63 +1,63 @@
|
|
|
1
1
|
cellfinder/__init__.py,sha256=XwXxMZJgw62dOHhrAKMQk0CE3378WRQWoC-kpf5LLpE,935
|
|
2
2
|
cellfinder/cli_migration_warning.py,sha256=gPtNrtnXvWpl5q0k_EGAQZg0DwcpCmuBTgpg56n5NfA,1578
|
|
3
3
|
cellfinder/core/__init__.py,sha256=pRFuQsl78HEK0S6gvhJw70QLbjjSBzP-GFO0AtVaGtk,62
|
|
4
|
-
cellfinder/core/main.py,sha256=
|
|
4
|
+
cellfinder/core/main.py,sha256=3Wg4QvBUlaHq04Yltjg9FSiePAhtGeFL1C-6Z1SI0Gk,3900
|
|
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
|
|
8
8
|
cellfinder/core/classify/classify.py,sha256=y4EnHX3IpAUAfhGScoJGv2hLLpy6OtmGvkqxAow3_Kw,3046
|
|
9
|
-
cellfinder/core/classify/cube_generator.py,sha256=
|
|
9
|
+
cellfinder/core/classify/cube_generator.py,sha256=4zMaa7bb7vr1QxBFpzqqitnWaNdzgEzZ2gt2xFeN37A,16586
|
|
10
10
|
cellfinder/core/classify/resnet.py,sha256=976Qt0li7ySYx_w2m9QfRMaDAc0rEkA8rO9SYP5a6uw,10057
|
|
11
11
|
cellfinder/core/classify/tools.py,sha256=UqABKS9KnkwR3PIhGRl7xHJbADP0mgVRSxXI5dybpqA,2547
|
|
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=bsiOPyxM3D_YZiYxqyauTxe7C4C2-rb_9yYiJiYaylM,9102
|
|
16
16
|
cellfinder/core/detect/filters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
cellfinder/core/detect/filters/setup_filters.py,sha256=sUyZ2dKbfwhn4YTq3kLVTHkckZZ460AVozLfoz7hJK8,2084
|
|
18
18
|
cellfinder/core/detect/filters/plane/__init__.py,sha256=lybcPbVDnurEQeq2FiLq0zR8p_ztarQOlajhdh1Q2-4,40
|
|
19
|
-
cellfinder/core/detect/filters/plane/classical_filter.py,sha256=
|
|
19
|
+
cellfinder/core/detect/filters/plane/classical_filter.py,sha256=Nton_nrvv6LhNlK10HnS0TIHsjapBiMJPURgzYh2myY,1331
|
|
20
20
|
cellfinder/core/detect/filters/plane/plane_filter.py,sha256=PWcAHDKB3qn3i9aSmQzUTEi3tgmccrf24DipjnYbOg0,2746
|
|
21
21
|
cellfinder/core/detect/filters/plane/tile_walker.py,sha256=lhKrq-MB35bR0_JYgj7WMR4otuaZ6XRDDhJhKyH1yS8,3001
|
|
22
22
|
cellfinder/core/detect/filters/volume/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
cellfinder/core/detect/filters/volume/ball_filter.py,sha256=
|
|
23
|
+
cellfinder/core/detect/filters/volume/ball_filter.py,sha256=nNGYAjDWst4Ufh6o6LAb5CVi5hFqYi2ObyrwgChbkQw,11175
|
|
24
24
|
cellfinder/core/detect/filters/volume/structure_detection.py,sha256=8lSyDFiqn5lYoE_mDLpXTUOsWGVm2-suGttZBdDAqmA,9152
|
|
25
|
-
cellfinder/core/detect/filters/volume/structure_splitting.py,sha256=
|
|
26
|
-
cellfinder/core/detect/filters/volume/volume_filter.py,sha256=
|
|
25
|
+
cellfinder/core/detect/filters/volume/structure_splitting.py,sha256=VuXKAOHG6f63ommPxZC08JRoegbQgZUReZAmvY5nm_U,7656
|
|
26
|
+
cellfinder/core/detect/filters/volume/volume_filter.py,sha256=nBc9kZhsZ2jsyuzizXcx6s0i5-Vc7BfUfDqtIxhGUSo,6873
|
|
27
27
|
cellfinder/core/download/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
cellfinder/core/download/cli.py,sha256=
|
|
29
|
-
cellfinder/core/download/download.py,sha256=
|
|
28
|
+
cellfinder/core/download/cli.py,sha256=uejrDBoveybeyMq-tcn8460r2Hf3iC1FhpEC2sXs7EI,1807
|
|
29
|
+
cellfinder/core/download/download.py,sha256=k7DZwox6UWhL4Efz1CK0VqG6jNVkotcomKHMZIlunTQ,3797
|
|
30
30
|
cellfinder/core/download/models.py,sha256=mLb6wAUn-tH1FtgvQU6ooLXqXetTP4D5Jd4_gPN-kJo,1359
|
|
31
31
|
cellfinder/core/tools/IO.py,sha256=q-euCP1s3iuWYvD2V72sPSBfwr5A1ETcL6Z_K41mLV0,1235
|
|
32
32
|
cellfinder/core/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
33
|
cellfinder/core/tools/array_operations.py,sha256=LDbqWA_N2YtxeKS877QYdJRL2FCMC1R1ExJtdb6-vEA,3371
|
|
34
34
|
cellfinder/core/tools/geometry.py,sha256=xlEQQmVQ9jcXqRzUqU554P8VxkaWkc5J_YhjkkKlI0Q,1124
|
|
35
35
|
cellfinder/core/tools/image_processing.py,sha256=z27bGjf3Iv3G4Nt1OYzpEnIYQNc4nNomj_QitqvZB78,2269
|
|
36
|
-
cellfinder/core/tools/prep.py,sha256=
|
|
37
|
-
cellfinder/core/tools/source_files.py,sha256=
|
|
36
|
+
cellfinder/core/tools/prep.py,sha256=A1tfHZmkGFpbb7cjLdfP8KavZNqRwzdum92NW8Et01o,2553
|
|
37
|
+
cellfinder/core/tools/source_files.py,sha256=NCPeA_kvnPWgS_fxWarc-9nBNjxkgL1xMFg-Co_kPlA,791
|
|
38
38
|
cellfinder/core/tools/system.py,sha256=6iSOLrz2mtKgOIdwjp_VS0-74XQCuaQYCvWAViJgEus,2232
|
|
39
39
|
cellfinder/core/tools/tf.py,sha256=jNU97-gIB51rLube9EoZ9bREkbqWRk-xPZhDTXG_jE4,1713
|
|
40
40
|
cellfinder/core/tools/tiff.py,sha256=NzIz6wq2GzxmcIhawFMwZADe-uQO2rIG46H7xkpGKLs,2899
|
|
41
41
|
cellfinder/core/tools/tools.py,sha256=G8oDGNRuWkzEJDnnC4r3SNGgpVbqbelCZR5ODk9JRzs,4867
|
|
42
42
|
cellfinder/core/train/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
|
-
cellfinder/core/train/train_yml.py,sha256=
|
|
44
|
-
cellfinder/napari/__init__.py,sha256=
|
|
45
|
-
cellfinder/napari/curation.py,sha256=
|
|
43
|
+
cellfinder/core/train/train_yml.py,sha256=TO_8e6Uz1S-P49o53gB_4i97Afz6RCHqmyyIrA70TZ8,13214
|
|
44
|
+
cellfinder/napari/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
+
cellfinder/napari/curation.py,sha256=jz7GekuuzOdwiEFLarvvO5Bho68k8eL7AjObMdEoPXw,21447
|
|
46
46
|
cellfinder/napari/input_container.py,sha256=tkm0dkPt7kSL8Xkvs1fh8M6vKWw57QLIt_wv74HFkGk,2150
|
|
47
47
|
cellfinder/napari/napari.yaml,sha256=WMR1CIAmYIVyQngbdbomTRZLvlDbb6LxsXsvTRClQnE,921
|
|
48
48
|
cellfinder/napari/sample_data.py,sha256=oUST23q09MM8dxHbUCmO0AjtXG6OlR_32LLqP0EU2UA,732
|
|
49
|
-
cellfinder/napari/utils.py,sha256=
|
|
49
|
+
cellfinder/napari/utils.py,sha256=mCy4Lr_gE2wWgRUSEiApNUjgLLAz0SSahTZnmUnRbgQ,2592
|
|
50
50
|
cellfinder/napari/detect/__init__.py,sha256=BD9Bg9NTAr6yRTq2A_p58U6j4w5wbY0sdXwhPJ3MSMY,34
|
|
51
51
|
cellfinder/napari/detect/detect.py,sha256=Jr4-lDGIntwcxSQ1Ne4xoA0SaKd4kf4zUT8MPyyO5ic,7443
|
|
52
52
|
cellfinder/napari/detect/detect_containers.py,sha256=C_tN7qwWNHhYFRRn_2yQ7QDyxpj3pQXLZalO0UQ_cbw,5020
|
|
53
53
|
cellfinder/napari/detect/thread_worker.py,sha256=BchLapCyML4SBDfihsqNLvqGM8FVcXEejaGg8MSkMCs,2604
|
|
54
54
|
cellfinder/napari/images/brainglobe.png,sha256=IT2zfikD2vKTOg--e6bMRHr5mC2x3s-eOt_t0eblvQ0,21216
|
|
55
55
|
cellfinder/napari/train/__init__.py,sha256=xo4CK-DvSecInGEc2ohcTgQYlH3iylFnGvKTCoq2WkI,35
|
|
56
|
-
cellfinder/napari/train/train.py,sha256=
|
|
56
|
+
cellfinder/napari/train/train.py,sha256=Cv8ivPWZoJJ-Iq1vW1JUmViaSnBPeqs07jBvZfOZQLc,6061
|
|
57
57
|
cellfinder/napari/train/train_containers.py,sha256=mYI_M1QssWgPBpkqViqgj1Ls32lVRUAjt6OGTqcrKI4,4390
|
|
58
|
-
cellfinder-1.1.
|
|
59
|
-
cellfinder-1.1.
|
|
60
|
-
cellfinder-1.1.
|
|
61
|
-
cellfinder-1.1.
|
|
62
|
-
cellfinder-1.1.
|
|
63
|
-
cellfinder-1.1.
|
|
58
|
+
cellfinder-1.1.2.dist-info/LICENSE,sha256=Tw8iMytIDXLSmcIUsbQmRWojstl9yOWsPCx6ZT6dZLY,1564
|
|
59
|
+
cellfinder-1.1.2.dist-info/METADATA,sha256=uP0C6fbKBWInZTmOxAfR24SIgOomzbvZZW82CYjXYXI,6652
|
|
60
|
+
cellfinder-1.1.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
61
|
+
cellfinder-1.1.2.dist-info/entry_points.txt,sha256=cKKjU8GPiN-TRelG2sT2JCKAcB9XDCjP6g9atE9pSoY,247
|
|
62
|
+
cellfinder-1.1.2.dist-info/top_level.txt,sha256=jyTQzX-tDjbsMr6s-E71Oy0IKQzmHTXSk4ZhpG5EDSE,11
|
|
63
|
+
cellfinder-1.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|