cellfinder 1.3.3__tar.gz → 1.4.0__tar.gz
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-1.3.3 → cellfinder-1.4.0}/.github/workflows/test_and_deploy.yml +48 -7
- {cellfinder-1.3.3 → cellfinder-1.4.0}/PKG-INFO +5 -4
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/classify/classify.py +3 -2
- cellfinder-1.4.0/cellfinder/core/detect/detect.py +236 -0
- cellfinder-1.4.0/cellfinder/core/detect/filters/plane/classical_filter.py +347 -0
- cellfinder-1.4.0/cellfinder/core/detect/filters/plane/plane_filter.py +169 -0
- cellfinder-1.4.0/cellfinder/core/detect/filters/plane/tile_walker.py +154 -0
- cellfinder-1.4.0/cellfinder/core/detect/filters/setup_filters.py +427 -0
- cellfinder-1.4.0/cellfinder/core/detect/filters/volume/ball_filter.py +415 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/detect/filters/volume/structure_detection.py +73 -35
- cellfinder-1.4.0/cellfinder/core/detect/filters/volume/structure_splitting.py +306 -0
- cellfinder-1.4.0/cellfinder/core/detect/filters/volume/volume_filter.py +523 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/main.py +6 -2
- cellfinder-1.4.0/cellfinder/core/tools/IO.py +45 -0
- cellfinder-1.4.0/cellfinder/core/tools/threading.py +380 -0
- cellfinder-1.4.0/cellfinder/core/tools/tools.py +295 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder.egg-info/PKG-INFO +5 -4
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder.egg-info/SOURCES.txt +2 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder.egg-info/requires.txt +3 -2
- {cellfinder-1.3.3 → cellfinder-1.4.0}/pyproject.toml +5 -2
- cellfinder-1.3.3/cellfinder/core/detect/detect.py +0 -301
- cellfinder-1.3.3/cellfinder/core/detect/filters/plane/classical_filter.py +0 -45
- cellfinder-1.3.3/cellfinder/core/detect/filters/plane/plane_filter.py +0 -87
- cellfinder-1.3.3/cellfinder/core/detect/filters/plane/tile_walker.py +0 -88
- cellfinder-1.3.3/cellfinder/core/detect/filters/setup_filters.py +0 -70
- cellfinder-1.3.3/cellfinder/core/detect/filters/volume/ball_filter.py +0 -417
- cellfinder-1.3.3/cellfinder/core/detect/filters/volume/structure_splitting.py +0 -242
- cellfinder-1.3.3/cellfinder/core/detect/filters/volume/volume_filter.py +0 -202
- cellfinder-1.3.3/cellfinder/core/tools/tools.py +0 -173
- {cellfinder-1.3.3 → cellfinder-1.4.0}/.github/workflows/test_include_guard.yaml +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/.gitignore +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/.napari/config.yml +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/CITATION.cff +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/LICENSE +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/MANIFEST.in +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/README.md +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/cli_migration_warning.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/classify/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/classify/augment.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/classify/cube_generator.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/classify/resnet.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/classify/tools.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/config/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/config/cellfinder.conf +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/detect/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/detect/filters/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/detect/filters/plane/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/detect/filters/volume/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/download/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/download/cli.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/download/download.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/tools/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/tools/array_operations.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/tools/geometry.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/tools/image_processing.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/tools/prep.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/tools/source_files.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/tools/system.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/tools/tiff.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/train/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/train/train_yml.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/core/types.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/curation.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/detect/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/detect/detect.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/detect/detect_containers.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/detect/thread_worker.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/input_container.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/napari.yaml +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/sample_data.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/train/__init__.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/train/train.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/train/train_containers.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder/napari/utils.py +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder.egg-info/dependency_links.txt +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder.egg-info/entry_points.txt +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/cellfinder.egg-info/top_level.txt +0 -0
- {cellfinder-1.3.3 → cellfinder-1.4.0}/setup.cfg +0 -0
|
@@ -38,11 +38,14 @@ jobs:
|
|
|
38
38
|
test:
|
|
39
39
|
needs: [linting, manifest]
|
|
40
40
|
name: Run package tests
|
|
41
|
-
timeout-minutes:
|
|
41
|
+
timeout-minutes: 120
|
|
42
42
|
runs-on: ${{ matrix.os }}
|
|
43
43
|
env:
|
|
44
44
|
KERAS_BACKEND: torch
|
|
45
45
|
CELLFINDER_TEST_DEVICE: cpu
|
|
46
|
+
# pooch cache dir
|
|
47
|
+
BRAINGLOBE_TEST_DATA_DIR: "~/.pooch_cache"
|
|
48
|
+
|
|
46
49
|
strategy:
|
|
47
50
|
matrix:
|
|
48
51
|
# Run all supported Python versions on linux
|
|
@@ -56,6 +59,14 @@ jobs:
|
|
|
56
59
|
python-version: "3.12"
|
|
57
60
|
|
|
58
61
|
steps:
|
|
62
|
+
- uses: actions/checkout@v4
|
|
63
|
+
- name: Cache pooch data
|
|
64
|
+
uses: actions/cache@v4
|
|
65
|
+
with:
|
|
66
|
+
path: "~/.pooch_cache"
|
|
67
|
+
# hash on conftest in case url changes
|
|
68
|
+
key: ${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pooch_registry.txt') }}
|
|
69
|
+
# Cache the tensorflow model so we don't have to remake it every time
|
|
59
70
|
- name: Cache brainglobe directory
|
|
60
71
|
uses: actions/cache@v3
|
|
61
72
|
with:
|
|
@@ -67,7 +78,7 @@ jobs:
|
|
|
67
78
|
- name: Setup qtpy libraries
|
|
68
79
|
uses: tlambert03/setup-qt-libs@v1
|
|
69
80
|
# Setup VTK with headless display
|
|
70
|
-
- uses: pyvista/setup-headless-display-action@
|
|
81
|
+
- uses: pyvista/setup-headless-display-action@v3
|
|
71
82
|
# Run all tests
|
|
72
83
|
- uses: neuroinformatics-unit/actions/test@v2
|
|
73
84
|
with:
|
|
@@ -75,15 +86,28 @@ jobs:
|
|
|
75
86
|
secret-codecov-token: ${{ secrets.CODECOV_TOKEN }}
|
|
76
87
|
use-xvfb: true
|
|
77
88
|
|
|
89
|
+
- name: Notify slack on scheduled failure
|
|
90
|
+
if: failure() && github.event_name == 'schedule'
|
|
91
|
+
uses: ravsamhq/notify-slack-action@v2
|
|
92
|
+
with:
|
|
93
|
+
status: ${{ job.status }} # required
|
|
94
|
+
notify_when: 'failure'
|
|
95
|
+
env:
|
|
96
|
+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_NOTIFYBOT_WEBHOOK_URL }} # required
|
|
97
|
+
|
|
78
98
|
test_numba_disabled:
|
|
79
99
|
needs: [linting, manifest]
|
|
80
100
|
name: Run tests with numba disabled
|
|
81
|
-
timeout-minutes:
|
|
101
|
+
timeout-minutes: 120
|
|
82
102
|
runs-on: ubuntu-latest
|
|
83
103
|
env:
|
|
84
|
-
|
|
104
|
+
NUMBA_DISABLE_JIT: "1"
|
|
105
|
+
PYTORCH_JIT: "0"
|
|
106
|
+
# pooch cache dir
|
|
107
|
+
BRAINGLOBE_TEST_DATA_DIR: "~/.pooch_cache"
|
|
85
108
|
|
|
86
109
|
steps:
|
|
110
|
+
- uses: actions/checkout@v4
|
|
87
111
|
- name: Cache brainglobe directory
|
|
88
112
|
uses: actions/cache@v3
|
|
89
113
|
with:
|
|
@@ -91,11 +115,18 @@ jobs:
|
|
|
91
115
|
~/.brainglobe
|
|
92
116
|
!~/.brainglobe/atlas.tar.gz
|
|
93
117
|
key: brainglobe
|
|
118
|
+
|
|
119
|
+
- name: Cache pooch data
|
|
120
|
+
uses: actions/cache@v4
|
|
121
|
+
with:
|
|
122
|
+
path: "~/.pooch_cache"
|
|
123
|
+
key: ${{ runner.os }}-3.10-${{ hashFiles('**/pooch_registry.txt') }}
|
|
124
|
+
|
|
94
125
|
# Setup pyqt libraries
|
|
95
126
|
- name: Setup qtpy libraries
|
|
96
127
|
uses: tlambert03/setup-qt-libs@v1
|
|
97
128
|
# Setup VTK with headless display
|
|
98
|
-
- uses: pyvista/setup-headless-display-action@
|
|
129
|
+
- uses: pyvista/setup-headless-display-action@v3
|
|
99
130
|
# Run test suite with numba disabled
|
|
100
131
|
- uses: neuroinformatics-unit/actions/test@v2
|
|
101
132
|
with:
|
|
@@ -103,12 +134,21 @@ jobs:
|
|
|
103
134
|
secret-codecov-token: ${{ secrets.CODECOV_TOKEN }}
|
|
104
135
|
codecov-flags: "numba"
|
|
105
136
|
|
|
137
|
+
- name: Notify slack on scheduled failure
|
|
138
|
+
if: failure() && github.event_name == 'schedule'
|
|
139
|
+
uses: ravsamhq/notify-slack-action@v2
|
|
140
|
+
with:
|
|
141
|
+
status: ${{ job.status }} # required
|
|
142
|
+
notify_when: 'failure'
|
|
143
|
+
env:
|
|
144
|
+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_NOTIFYBOT_WEBHOOK_URL }} # required
|
|
145
|
+
|
|
106
146
|
# Run brainglobe-workflows brainmapper-CLI tests to check for
|
|
107
147
|
# breakages
|
|
108
148
|
test_brainmapper_cli:
|
|
109
149
|
needs: [linting, manifest]
|
|
110
150
|
name: Run brainmapper tests to check for breakages
|
|
111
|
-
timeout-minutes:
|
|
151
|
+
timeout-minutes: 120
|
|
112
152
|
runs-on: ubuntu-latest
|
|
113
153
|
env:
|
|
114
154
|
KERAS_BACKEND: torch
|
|
@@ -144,8 +184,9 @@ jobs:
|
|
|
144
184
|
run: |
|
|
145
185
|
python -m pytest --color=yes -v tests/brainmapper
|
|
146
186
|
|
|
187
|
+
|
|
147
188
|
build_sdist_wheel:
|
|
148
|
-
name: Build source distribution
|
|
189
|
+
name: Build source distribution
|
|
149
190
|
needs: [test, test_numba_disabled]
|
|
150
191
|
if: github.event_name == 'push' && github.ref_type == 'tag'
|
|
151
192
|
runs-on: ubuntu-latest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: cellfinder
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.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
|
|
@@ -32,8 +32,8 @@ Requires-Dist: numba
|
|
|
32
32
|
Requires-Dist: numpy
|
|
33
33
|
Requires-Dist: scikit-image
|
|
34
34
|
Requires-Dist: scikit-learn
|
|
35
|
-
Requires-Dist: keras
|
|
36
|
-
Requires-Dist: torch
|
|
35
|
+
Requires-Dist: keras>=3.7.0
|
|
36
|
+
Requires-Dist: torch!=2.4,>=2.1.0
|
|
37
37
|
Requires-Dist: tifffile
|
|
38
38
|
Requires-Dist: tqdm
|
|
39
39
|
Provides-Extra: dev
|
|
@@ -46,6 +46,7 @@ Requires-Dist: pytest-qt; extra == "dev"
|
|
|
46
46
|
Requires-Dist: pytest-timeout; extra == "dev"
|
|
47
47
|
Requires-Dist: pytest; extra == "dev"
|
|
48
48
|
Requires-Dist: tox; extra == "dev"
|
|
49
|
+
Requires-Dist: pooch>=1; extra == "dev"
|
|
49
50
|
Provides-Extra: napari
|
|
50
51
|
Requires-Dist: brainglobe-napari-io; extra == "napari"
|
|
51
52
|
Requires-Dist: magicgui; extra == "napari"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from datetime import datetime
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
4
5
|
|
|
5
6
|
import keras
|
|
@@ -30,7 +31,7 @@ def main(
|
|
|
30
31
|
max_workers: int = 3,
|
|
31
32
|
*,
|
|
32
33
|
callback: Optional[Callable[[int], None]] = None,
|
|
33
|
-
) -> List:
|
|
34
|
+
) -> List[Cell]:
|
|
34
35
|
"""
|
|
35
36
|
Parameters
|
|
36
37
|
----------
|
|
@@ -68,7 +69,7 @@ def main(
|
|
|
68
69
|
workers=workers,
|
|
69
70
|
)
|
|
70
71
|
|
|
71
|
-
if trained_model and trained_model.suffix == ".h5":
|
|
72
|
+
if trained_model and Path(trained_model).suffix == ".h5":
|
|
72
73
|
print(
|
|
73
74
|
"Weights provided in place of the model, "
|
|
74
75
|
"loading weights into default model."
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Detection is run in three steps:
|
|
3
|
+
|
|
4
|
+
1. 2D filtering
|
|
5
|
+
2. 3D filtering
|
|
6
|
+
3. Structure detection
|
|
7
|
+
|
|
8
|
+
In steps 1. and 2. filters are applied, and any bright points detected
|
|
9
|
+
post-filter are marked. To avoid using a separate mask array to mark the
|
|
10
|
+
bright points, the input data is clipped to [0, (max_val - 2)]
|
|
11
|
+
(max_val is the maximum value that the image data type can store), and:
|
|
12
|
+
- (max_val - 1) is used to mark bright points during 2D filtering
|
|
13
|
+
- (max_val) is used to mark bright points during 3D filtering
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import dataclasses
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from typing import Callable, List, Optional, Tuple
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
import torch
|
|
22
|
+
from brainglobe_utils.cells.cells import Cell
|
|
23
|
+
|
|
24
|
+
from cellfinder.core import logger, types
|
|
25
|
+
from cellfinder.core.detect.filters.plane import TileProcessor
|
|
26
|
+
from cellfinder.core.detect.filters.setup_filters import DetectionSettings
|
|
27
|
+
from cellfinder.core.detect.filters.volume.volume_filter import VolumeFilter
|
|
28
|
+
from cellfinder.core.tools.tools import inference_wrapper
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@inference_wrapper
|
|
32
|
+
def main(
|
|
33
|
+
signal_array: types.array,
|
|
34
|
+
start_plane: int = 0,
|
|
35
|
+
end_plane: int = -1,
|
|
36
|
+
voxel_sizes: Tuple[float, float, float] = (5, 2, 2),
|
|
37
|
+
soma_diameter: float = 16,
|
|
38
|
+
max_cluster_size: float = 100_000,
|
|
39
|
+
ball_xy_size: float = 6,
|
|
40
|
+
ball_z_size: float = 15,
|
|
41
|
+
ball_overlap_fraction: float = 0.6,
|
|
42
|
+
soma_spread_factor: float = 1.4,
|
|
43
|
+
n_free_cpus: int = 2,
|
|
44
|
+
log_sigma_size: float = 0.2,
|
|
45
|
+
n_sds_above_mean_thresh: float = 10,
|
|
46
|
+
outlier_keep: bool = False,
|
|
47
|
+
artifact_keep: bool = False,
|
|
48
|
+
save_planes: bool = False,
|
|
49
|
+
plane_directory: Optional[str] = None,
|
|
50
|
+
batch_size: Optional[int] = None,
|
|
51
|
+
torch_device: str = "cpu",
|
|
52
|
+
use_scipy: bool = True,
|
|
53
|
+
split_ball_xy_size: int = 3,
|
|
54
|
+
split_ball_z_size: int = 3,
|
|
55
|
+
split_ball_overlap_fraction: float = 0.8,
|
|
56
|
+
split_soma_diameter: int = 7,
|
|
57
|
+
*,
|
|
58
|
+
callback: Optional[Callable[[int], None]] = None,
|
|
59
|
+
) -> List[Cell]:
|
|
60
|
+
"""
|
|
61
|
+
Perform cell candidate detection on a 3D signal array.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
signal_array : numpy.ndarray
|
|
66
|
+
3D array representing the signal data.
|
|
67
|
+
|
|
68
|
+
start_plane : int
|
|
69
|
+
Index of the starting plane for detection.
|
|
70
|
+
|
|
71
|
+
end_plane : int
|
|
72
|
+
Index of the ending plane for detection.
|
|
73
|
+
|
|
74
|
+
voxel_sizes : Tuple[float, float, float]
|
|
75
|
+
Tuple of voxel sizes in each dimension (z, y, x).
|
|
76
|
+
|
|
77
|
+
soma_diameter : float
|
|
78
|
+
Diameter of the soma in physical units.
|
|
79
|
+
|
|
80
|
+
max_cluster_size : float
|
|
81
|
+
Maximum size of a cluster in physical units.
|
|
82
|
+
|
|
83
|
+
ball_xy_size : float
|
|
84
|
+
Size of the XY ball used for filtering in physical units.
|
|
85
|
+
|
|
86
|
+
ball_z_size : float
|
|
87
|
+
Size of the Z ball used for filtering in physical units.
|
|
88
|
+
|
|
89
|
+
ball_overlap_fraction : float
|
|
90
|
+
Fraction of overlap allowed between balls.
|
|
91
|
+
|
|
92
|
+
soma_spread_factor : float
|
|
93
|
+
Spread factor for soma size.
|
|
94
|
+
|
|
95
|
+
n_free_cpus : int
|
|
96
|
+
Number of free CPU cores available for parallel processing.
|
|
97
|
+
|
|
98
|
+
log_sigma_size : float
|
|
99
|
+
Size of the sigma for the log filter.
|
|
100
|
+
|
|
101
|
+
n_sds_above_mean_thresh : float
|
|
102
|
+
Number of standard deviations above the mean threshold.
|
|
103
|
+
|
|
104
|
+
outlier_keep : bool, optional
|
|
105
|
+
Whether to keep outliers during detection. Defaults to False.
|
|
106
|
+
|
|
107
|
+
artifact_keep : bool, optional
|
|
108
|
+
Whether to keep artifacts during detection. Defaults to False.
|
|
109
|
+
|
|
110
|
+
save_planes : bool, optional
|
|
111
|
+
Whether to save the planes during detection. Defaults to False.
|
|
112
|
+
|
|
113
|
+
plane_directory : str, optional
|
|
114
|
+
Directory path to save the planes. Defaults to None.
|
|
115
|
+
|
|
116
|
+
batch_size : int, optional
|
|
117
|
+
The number of planes to process in each batch. Defaults to 1.
|
|
118
|
+
For CPU, there's no benefit for a larger batch size. Only a memory
|
|
119
|
+
usage increase. For CUDA, the larger the batch size the better the
|
|
120
|
+
performance. Until it fills up the GPU memory - after which it
|
|
121
|
+
becomes slower.
|
|
122
|
+
|
|
123
|
+
torch_device : str, optional
|
|
124
|
+
The device on which to run the computation. By default, it's "cpu".
|
|
125
|
+
To run on a gpu, specify the PyTorch device name, such as "cuda" to
|
|
126
|
+
run on the first GPU.
|
|
127
|
+
|
|
128
|
+
callback : Callable[int], optional
|
|
129
|
+
A callback function that is called every time a plane has finished
|
|
130
|
+
being processed. Called with the plane number that has finished.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
List[Cell]
|
|
135
|
+
List of detected cells.
|
|
136
|
+
"""
|
|
137
|
+
start_time = datetime.now()
|
|
138
|
+
if batch_size is None:
|
|
139
|
+
if torch_device == "cpu":
|
|
140
|
+
batch_size = 4
|
|
141
|
+
else:
|
|
142
|
+
batch_size = 1
|
|
143
|
+
|
|
144
|
+
if not np.issubdtype(signal_array.dtype, np.number):
|
|
145
|
+
raise TypeError(
|
|
146
|
+
"signal_array must be a numpy datatype, but has datatype "
|
|
147
|
+
f"{signal_array.dtype}"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if signal_array.ndim != 3:
|
|
151
|
+
raise ValueError("Input data must be 3D")
|
|
152
|
+
|
|
153
|
+
if end_plane < 0:
|
|
154
|
+
end_plane = len(signal_array)
|
|
155
|
+
end_plane = min(len(signal_array), end_plane)
|
|
156
|
+
|
|
157
|
+
torch_device = torch_device.lower()
|
|
158
|
+
batch_size = max(batch_size, 1)
|
|
159
|
+
# brainmapper can pass them in as str
|
|
160
|
+
voxel_sizes = list(map(float, voxel_sizes))
|
|
161
|
+
|
|
162
|
+
settings = DetectionSettings(
|
|
163
|
+
plane_shape=signal_array.shape[1:],
|
|
164
|
+
plane_original_np_dtype=signal_array.dtype,
|
|
165
|
+
voxel_sizes=voxel_sizes,
|
|
166
|
+
soma_spread_factor=soma_spread_factor,
|
|
167
|
+
soma_diameter_um=soma_diameter,
|
|
168
|
+
max_cluster_size_um3=max_cluster_size,
|
|
169
|
+
ball_xy_size_um=ball_xy_size,
|
|
170
|
+
ball_z_size_um=ball_z_size,
|
|
171
|
+
start_plane=start_plane,
|
|
172
|
+
end_plane=end_plane,
|
|
173
|
+
n_free_cpus=n_free_cpus,
|
|
174
|
+
ball_overlap_fraction=ball_overlap_fraction,
|
|
175
|
+
log_sigma_size=log_sigma_size,
|
|
176
|
+
n_sds_above_mean_thresh=n_sds_above_mean_thresh,
|
|
177
|
+
outlier_keep=outlier_keep,
|
|
178
|
+
artifact_keep=artifact_keep,
|
|
179
|
+
save_planes=save_planes,
|
|
180
|
+
plane_directory=plane_directory,
|
|
181
|
+
batch_size=batch_size,
|
|
182
|
+
torch_device=torch_device,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# replicate the settings specific to splitting, before we access anything
|
|
186
|
+
# of the original settings, causing cached properties
|
|
187
|
+
kwargs = dataclasses.asdict(settings)
|
|
188
|
+
kwargs["ball_z_size_um"] = split_ball_z_size * settings.z_pixel_size
|
|
189
|
+
kwargs["ball_xy_size_um"] = (
|
|
190
|
+
split_ball_xy_size * settings.in_plane_pixel_size
|
|
191
|
+
)
|
|
192
|
+
kwargs["ball_overlap_fraction"] = split_ball_overlap_fraction
|
|
193
|
+
kwargs["soma_diameter_um"] = (
|
|
194
|
+
split_soma_diameter * settings.in_plane_pixel_size
|
|
195
|
+
)
|
|
196
|
+
# always run on cpu because copying to gpu overhead is likely slower than
|
|
197
|
+
# any benefit for detection on smallish volumes
|
|
198
|
+
kwargs["torch_device"] = "cpu"
|
|
199
|
+
# for splitting, we only do 3d filtering. Its input is a zero volume
|
|
200
|
+
# with cell voxels marked with threshold_value. So just use float32
|
|
201
|
+
# for input because the filters will also use float(32). So there will
|
|
202
|
+
# not be need to convert the input a different dtype before passing to
|
|
203
|
+
# the filters.
|
|
204
|
+
kwargs["plane_original_np_dtype"] = np.float32
|
|
205
|
+
splitting_settings = DetectionSettings(**kwargs)
|
|
206
|
+
|
|
207
|
+
# Create 3D analysis filter
|
|
208
|
+
mp_3d_filter = VolumeFilter(settings=settings)
|
|
209
|
+
|
|
210
|
+
# Create 2D analysis filter
|
|
211
|
+
mp_tile_processor = TileProcessor(
|
|
212
|
+
plane_shape=settings.plane_shape,
|
|
213
|
+
clipping_value=settings.clipping_value,
|
|
214
|
+
threshold_value=settings.threshold_value,
|
|
215
|
+
n_sds_above_mean_thresh=n_sds_above_mean_thresh,
|
|
216
|
+
log_sigma_size=log_sigma_size,
|
|
217
|
+
soma_diameter=settings.soma_diameter,
|
|
218
|
+
torch_device=torch_device,
|
|
219
|
+
dtype=settings.filtering_dtype.__name__,
|
|
220
|
+
use_scipy=use_scipy,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
orig_n_threads = torch.get_num_threads()
|
|
224
|
+
torch.set_num_threads(settings.n_torch_comp_threads)
|
|
225
|
+
|
|
226
|
+
# process the data
|
|
227
|
+
mp_3d_filter.process(mp_tile_processor, signal_array, callback=callback)
|
|
228
|
+
cells = mp_3d_filter.get_results(splitting_settings)
|
|
229
|
+
|
|
230
|
+
torch.set_num_threads(orig_n_threads)
|
|
231
|
+
|
|
232
|
+
time_elapsed = datetime.now() - start_time
|
|
233
|
+
s = f"Detection complete. Found {len(cells)} cells in {time_elapsed}"
|
|
234
|
+
logger.debug(s)
|
|
235
|
+
print(s)
|
|
236
|
+
return cells
|