senoquant 1.0.0b1__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.
- senoquant/__init__.py +6 -0
- senoquant/_reader.py +7 -0
- senoquant/_widget.py +33 -0
- senoquant/napari.yaml +83 -0
- senoquant/reader/__init__.py +5 -0
- senoquant/reader/core.py +369 -0
- senoquant/tabs/__init__.py +15 -0
- senoquant/tabs/batch/__init__.py +10 -0
- senoquant/tabs/batch/backend.py +641 -0
- senoquant/tabs/batch/config.py +270 -0
- senoquant/tabs/batch/frontend.py +1283 -0
- senoquant/tabs/batch/io.py +326 -0
- senoquant/tabs/batch/layers.py +86 -0
- senoquant/tabs/quantification/__init__.py +1 -0
- senoquant/tabs/quantification/backend.py +228 -0
- senoquant/tabs/quantification/features/__init__.py +80 -0
- senoquant/tabs/quantification/features/base.py +142 -0
- senoquant/tabs/quantification/features/marker/__init__.py +5 -0
- senoquant/tabs/quantification/features/marker/config.py +69 -0
- senoquant/tabs/quantification/features/marker/dialog.py +437 -0
- senoquant/tabs/quantification/features/marker/export.py +879 -0
- senoquant/tabs/quantification/features/marker/feature.py +119 -0
- senoquant/tabs/quantification/features/marker/morphology.py +285 -0
- senoquant/tabs/quantification/features/marker/rows.py +654 -0
- senoquant/tabs/quantification/features/marker/thresholding.py +46 -0
- senoquant/tabs/quantification/features/roi.py +346 -0
- senoquant/tabs/quantification/features/spots/__init__.py +5 -0
- senoquant/tabs/quantification/features/spots/config.py +62 -0
- senoquant/tabs/quantification/features/spots/dialog.py +477 -0
- senoquant/tabs/quantification/features/spots/export.py +1292 -0
- senoquant/tabs/quantification/features/spots/feature.py +112 -0
- senoquant/tabs/quantification/features/spots/morphology.py +279 -0
- senoquant/tabs/quantification/features/spots/rows.py +241 -0
- senoquant/tabs/quantification/frontend.py +815 -0
- senoquant/tabs/segmentation/__init__.py +1 -0
- senoquant/tabs/segmentation/backend.py +131 -0
- senoquant/tabs/segmentation/frontend.py +1009 -0
- senoquant/tabs/segmentation/models/__init__.py +5 -0
- senoquant/tabs/segmentation/models/base.py +146 -0
- senoquant/tabs/segmentation/models/cpsam/details.json +65 -0
- senoquant/tabs/segmentation/models/cpsam/model.py +150 -0
- senoquant/tabs/segmentation/models/default_2d/details.json +69 -0
- senoquant/tabs/segmentation/models/default_2d/model.py +664 -0
- senoquant/tabs/segmentation/models/default_3d/details.json +69 -0
- senoquant/tabs/segmentation/models/default_3d/model.py +682 -0
- senoquant/tabs/segmentation/models/hf.py +71 -0
- senoquant/tabs/segmentation/models/nuclear_dilation/__init__.py +1 -0
- senoquant/tabs/segmentation/models/nuclear_dilation/details.json +26 -0
- senoquant/tabs/segmentation/models/nuclear_dilation/model.py +96 -0
- senoquant/tabs/segmentation/models/perinuclear_rings/__init__.py +1 -0
- senoquant/tabs/segmentation/models/perinuclear_rings/details.json +34 -0
- senoquant/tabs/segmentation/models/perinuclear_rings/model.py +132 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/__init__.py +2 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/__init__.py +3 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/__init__.py +6 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/generate.py +470 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/prepare.py +273 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/rawdata.py +112 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/transform.py +384 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/__init__.py +0 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/blocks.py +184 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/losses.py +79 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/nets.py +165 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/predict.py +467 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/probability.py +67 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/train.py +148 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/io/__init__.py +163 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/__init__.py +52 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/base_model.py +329 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_isotropic.py +160 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_projection.py +178 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_standard.py +446 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_upsampling.py +54 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/config.py +254 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/pretrained.py +119 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/scripts/__init__.py +0 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/scripts/care_predict.py +180 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/__init__.py +5 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/plot_utils.py +159 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/six.py +18 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/tf.py +644 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/utils.py +272 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/version.py +1 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/docs/source/conf.py +368 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/setup.py +68 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_datagen.py +169 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_models.py +462 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_utils.py +166 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tools/create_zip_contents.py +34 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/__init__.py +30 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/big.py +624 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/bioimageio_utils.py +494 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/data/__init__.py +39 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/__init__.py +10 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/geom2d.py +215 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/geom3d.py +349 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/matching.py +483 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/__init__.py +28 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/base.py +1217 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/model2d.py +594 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/model3d.py +696 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/nms.py +384 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/__init__.py +2 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/plot.py +74 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/render.py +298 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/rays3d.py +373 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/sample_patches.py +65 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/__init__.py +0 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/predict2d.py +90 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/predict3d.py +93 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/utils.py +408 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/version.py +1 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/__init__.py +45 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/__init__.py +17 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/cli.py +55 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/core.py +285 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/__init__.py +15 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/cli.py +36 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/divisibility.py +193 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/probe.py +100 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/receptive_field.py +182 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/rf_cli.py +48 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/valid_sizes.py +278 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/post/__init__.py +8 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/post/core.py +157 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/pre/__init__.py +17 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/pre/core.py +226 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/predict/__init__.py +5 -0
- senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/predict/core.py +401 -0
- senoquant/tabs/settings/__init__.py +1 -0
- senoquant/tabs/settings/backend.py +29 -0
- senoquant/tabs/settings/frontend.py +19 -0
- senoquant/tabs/spots/__init__.py +1 -0
- senoquant/tabs/spots/backend.py +139 -0
- senoquant/tabs/spots/frontend.py +800 -0
- senoquant/tabs/spots/models/__init__.py +5 -0
- senoquant/tabs/spots/models/base.py +94 -0
- senoquant/tabs/spots/models/rmp/details.json +61 -0
- senoquant/tabs/spots/models/rmp/model.py +499 -0
- senoquant/tabs/spots/models/udwt/details.json +103 -0
- senoquant/tabs/spots/models/udwt/model.py +482 -0
- senoquant/utils.py +25 -0
- senoquant-1.0.0b1.dist-info/METADATA +193 -0
- senoquant-1.0.0b1.dist-info/RECORD +148 -0
- senoquant-1.0.0b1.dist-info/WHEEL +5 -0
- senoquant-1.0.0b1.dist-info/entry_points.txt +2 -0
- senoquant-1.0.0b1.dist-info/licenses/LICENSE +28 -0
- senoquant-1.0.0b1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
from __future__ import print_function, unicode_literals, absolute_import, division
|
|
2
|
+
import numpy as np
|
|
3
|
+
from time import time
|
|
4
|
+
from .utils import _normalize_grid
|
|
5
|
+
|
|
6
|
+
def _ind_prob_thresh(prob, prob_thresh, b=2):
|
|
7
|
+
if b is not None and np.isscalar(b):
|
|
8
|
+
b = ((b,b),)*prob.ndim
|
|
9
|
+
|
|
10
|
+
ind_thresh = prob > prob_thresh
|
|
11
|
+
if b is not None:
|
|
12
|
+
_ind_thresh = np.zeros_like(ind_thresh)
|
|
13
|
+
ss = tuple(slice(_bs[0] if _bs[0]>0 else None,
|
|
14
|
+
-_bs[1] if _bs[1]>0 else None) for _bs in b)
|
|
15
|
+
_ind_thresh[ss] = True
|
|
16
|
+
ind_thresh &= _ind_thresh
|
|
17
|
+
return ind_thresh
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _non_maximum_suppression_old(coord, prob, grid=(1,1), b=2, nms_thresh=0.5, prob_thresh=0.5, verbose=False, max_bbox_search=True):
|
|
21
|
+
"""2D coordinates of the polys that survive from a given prediction (prob, coord)
|
|
22
|
+
|
|
23
|
+
prob.shape = (Ny,Nx)
|
|
24
|
+
coord.shape = (Ny,Nx,2,n_rays)
|
|
25
|
+
|
|
26
|
+
b: don't use pixel closer than b pixels to the image boundary
|
|
27
|
+
|
|
28
|
+
returns retained points
|
|
29
|
+
"""
|
|
30
|
+
from .lib.stardist2d import c_non_max_suppression_inds_old
|
|
31
|
+
|
|
32
|
+
# TODO: using b>0 with grid>1 can suppress small/cropped objects at the image boundary
|
|
33
|
+
|
|
34
|
+
assert prob.ndim == 2
|
|
35
|
+
assert coord.ndim == 4
|
|
36
|
+
grid = _normalize_grid(grid,2)
|
|
37
|
+
|
|
38
|
+
# mask = prob > prob_thresh
|
|
39
|
+
# if b is not None and b > 0:
|
|
40
|
+
# _mask = np.zeros_like(mask)
|
|
41
|
+
# _mask[b:-b,b:-b] = True
|
|
42
|
+
# mask &= _mask
|
|
43
|
+
|
|
44
|
+
mask = _ind_prob_thresh(prob, prob_thresh, b)
|
|
45
|
+
|
|
46
|
+
polygons = coord[mask]
|
|
47
|
+
scores = prob[mask]
|
|
48
|
+
|
|
49
|
+
# sort scores descendingly
|
|
50
|
+
ind = np.argsort(scores)[::-1]
|
|
51
|
+
survivors = np.zeros(len(ind), bool)
|
|
52
|
+
polygons = polygons[ind]
|
|
53
|
+
scores = scores[ind]
|
|
54
|
+
|
|
55
|
+
if max_bbox_search:
|
|
56
|
+
# map pixel indices to ids of sorted polygons (-1 => polygon at that pixel not a candidate)
|
|
57
|
+
mapping = -np.ones(mask.shape,np.int32)
|
|
58
|
+
mapping.flat[ np.flatnonzero(mask)[ind] ] = range(len(ind))
|
|
59
|
+
else:
|
|
60
|
+
mapping = np.empty((0,0),np.int32)
|
|
61
|
+
|
|
62
|
+
if verbose:
|
|
63
|
+
t = time()
|
|
64
|
+
|
|
65
|
+
survivors[ind] = c_non_max_suppression_inds_old(np.ascontiguousarray(polygons.astype(np.int32)),
|
|
66
|
+
mapping, np.float32(nms_thresh), np.int32(max_bbox_search),
|
|
67
|
+
np.int32(grid[0]), np.int32(grid[1]),np.int32(verbose))
|
|
68
|
+
|
|
69
|
+
if verbose:
|
|
70
|
+
print("keeping %s/%s polygons" % (np.count_nonzero(survivors), len(polygons)))
|
|
71
|
+
print("NMS took %.4f s" % (time() - t))
|
|
72
|
+
|
|
73
|
+
points = np.stack([ii[survivors] for ii in np.nonzero(mask)],axis=-1)
|
|
74
|
+
return points
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def non_maximum_suppression(dist, prob, grid=(1,1), b=2, nms_thresh=0.5, prob_thresh=0.5,
|
|
78
|
+
use_bbox=True, use_kdtree=True, verbose=False):
|
|
79
|
+
"""Non-Maximum-Supression of 2D polygons
|
|
80
|
+
|
|
81
|
+
Retains only polygons whose overlap is smaller than nms_thresh
|
|
82
|
+
|
|
83
|
+
dist.shape = (Ny,Nx, n_rays)
|
|
84
|
+
prob.shape = (Ny,Nx)
|
|
85
|
+
|
|
86
|
+
returns the retained points, probabilities, and distances:
|
|
87
|
+
|
|
88
|
+
points, prob, dist = non_maximum_suppression(dist, prob, ....
|
|
89
|
+
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
# TODO: using b>0 with grid>1 can suppress small/cropped objects at the image boundary
|
|
93
|
+
|
|
94
|
+
assert prob.ndim == 2 and dist.ndim == 3 and prob.shape == dist.shape[:2]
|
|
95
|
+
dist = np.asarray(dist)
|
|
96
|
+
prob = np.asarray(prob)
|
|
97
|
+
n_rays = dist.shape[-1]
|
|
98
|
+
|
|
99
|
+
grid = _normalize_grid(grid,2)
|
|
100
|
+
|
|
101
|
+
# mask = prob > prob_thresh
|
|
102
|
+
# if b is not None and b > 0:
|
|
103
|
+
# _mask = np.zeros_like(mask)
|
|
104
|
+
# _mask[b:-b,b:-b] = True
|
|
105
|
+
# mask &= _mask
|
|
106
|
+
|
|
107
|
+
mask = _ind_prob_thresh(prob, prob_thresh, b)
|
|
108
|
+
points = np.stack(np.where(mask), axis=1)
|
|
109
|
+
|
|
110
|
+
dist = dist[mask]
|
|
111
|
+
scores = prob[mask]
|
|
112
|
+
|
|
113
|
+
# sort scores descendingly
|
|
114
|
+
ind = np.argsort(scores)[::-1]
|
|
115
|
+
dist = dist[ind]
|
|
116
|
+
scores = scores[ind]
|
|
117
|
+
points = points[ind]
|
|
118
|
+
|
|
119
|
+
points = (points * np.array(grid).reshape((1,2)))
|
|
120
|
+
|
|
121
|
+
if verbose:
|
|
122
|
+
t = time()
|
|
123
|
+
|
|
124
|
+
inds = non_maximum_suppression_inds(dist, points.astype(np.int32, copy=False), scores=scores,
|
|
125
|
+
use_bbox=use_bbox, use_kdtree=use_kdtree,
|
|
126
|
+
thresh=nms_thresh, verbose=verbose)
|
|
127
|
+
|
|
128
|
+
if verbose:
|
|
129
|
+
print("keeping %s/%s polygons" % (np.count_nonzero(inds), len(inds)))
|
|
130
|
+
print("NMS took %.4f s" % (time() - t))
|
|
131
|
+
|
|
132
|
+
return points[inds], scores[inds], dist[inds]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def non_maximum_suppression_sparse(dist, prob, points, b=2, nms_thresh=0.5,
|
|
136
|
+
use_bbox=True, use_kdtree = True, verbose=False):
|
|
137
|
+
"""Non-Maximum-Supression of 2D polygons from a list of dists, probs (scores), and points
|
|
138
|
+
|
|
139
|
+
Retains only polyhedra whose overlap is smaller than nms_thresh
|
|
140
|
+
|
|
141
|
+
dist.shape = (n_polys, n_rays)
|
|
142
|
+
prob.shape = (n_polys,)
|
|
143
|
+
points.shape = (n_polys,2)
|
|
144
|
+
|
|
145
|
+
returns the retained instances
|
|
146
|
+
|
|
147
|
+
(pointsi, probi, disti, indsi)
|
|
148
|
+
|
|
149
|
+
with
|
|
150
|
+
pointsi = points[indsi] ...
|
|
151
|
+
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
# TODO: using b>0 with grid>1 can suppress small/cropped objects at the image boundary
|
|
155
|
+
|
|
156
|
+
dist = np.asarray(dist)
|
|
157
|
+
prob = np.asarray(prob)
|
|
158
|
+
points = np.asarray(points)
|
|
159
|
+
n_rays = dist.shape[-1]
|
|
160
|
+
|
|
161
|
+
assert dist.ndim == 2 and prob.ndim == 1 and points.ndim == 2 and \
|
|
162
|
+
points.shape[-1]==2 and len(prob) == len(dist) == len(points)
|
|
163
|
+
|
|
164
|
+
verbose and print("predicting instances with nms_thresh = {nms_thresh}".format(nms_thresh=nms_thresh), flush=True)
|
|
165
|
+
|
|
166
|
+
inds_original = np.arange(len(prob))
|
|
167
|
+
_sorted = np.argsort(prob)[::-1]
|
|
168
|
+
probi = prob[_sorted]
|
|
169
|
+
disti = dist[_sorted]
|
|
170
|
+
pointsi = points[_sorted]
|
|
171
|
+
inds_original = inds_original[_sorted]
|
|
172
|
+
|
|
173
|
+
if verbose:
|
|
174
|
+
print("non-maximum suppression...")
|
|
175
|
+
t = time()
|
|
176
|
+
|
|
177
|
+
inds = non_maximum_suppression_inds(disti, pointsi, scores=probi, thresh=nms_thresh, use_kdtree = use_kdtree, verbose=verbose)
|
|
178
|
+
|
|
179
|
+
if verbose:
|
|
180
|
+
print("keeping %s/%s polyhedra" % (np.count_nonzero(inds), len(inds)))
|
|
181
|
+
print("NMS took %.4f s" % (time() - t))
|
|
182
|
+
|
|
183
|
+
return pointsi[inds], probi[inds], disti[inds], inds_original[inds]
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def non_maximum_suppression_inds(dist, points, scores, thresh=0.5, use_bbox=True, use_kdtree = True, verbose=1):
|
|
187
|
+
"""
|
|
188
|
+
Applies non maximum supression to ray-convex polygons given by dists and points
|
|
189
|
+
sorted by scores and IoU threshold
|
|
190
|
+
|
|
191
|
+
P1 will suppress P2, if IoU(P1,P2) > thresh
|
|
192
|
+
|
|
193
|
+
with IoU(P1,P2) = Ainter(P1,P2) / min(A(P1),A(P2))
|
|
194
|
+
|
|
195
|
+
i.e. the smaller thresh, the more polygons will be supressed
|
|
196
|
+
|
|
197
|
+
dist.shape = (n_poly, n_rays)
|
|
198
|
+
point.shape = (n_poly, 2)
|
|
199
|
+
score.shape = (n_poly,)
|
|
200
|
+
|
|
201
|
+
returns indices of selected polygons
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
from .lib.stardist2d import c_non_max_suppression_inds
|
|
205
|
+
|
|
206
|
+
assert dist.ndim == 2
|
|
207
|
+
assert points.ndim == 2
|
|
208
|
+
|
|
209
|
+
n_poly = dist.shape[0]
|
|
210
|
+
|
|
211
|
+
if scores is None:
|
|
212
|
+
scores = np.ones(n_poly)
|
|
213
|
+
|
|
214
|
+
assert len(scores) == n_poly
|
|
215
|
+
assert points.shape[0] == n_poly
|
|
216
|
+
|
|
217
|
+
def _prep(x, dtype):
|
|
218
|
+
return np.ascontiguousarray(x.astype(dtype, copy=False))
|
|
219
|
+
|
|
220
|
+
inds = c_non_max_suppression_inds(_prep(dist, np.float32),
|
|
221
|
+
_prep(points, np.float32),
|
|
222
|
+
int(use_kdtree),
|
|
223
|
+
int(use_bbox),
|
|
224
|
+
int(verbose),
|
|
225
|
+
np.float32(thresh))
|
|
226
|
+
|
|
227
|
+
return inds
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
#########
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def non_maximum_suppression_3d(dist, prob, rays, grid=(1,1,1), b=2, nms_thresh=0.5, prob_thresh=0.5, use_bbox=True, use_kdtree=True, verbose=False):
|
|
234
|
+
"""Non-Maximum-Supression of 3D polyhedra
|
|
235
|
+
|
|
236
|
+
Retains only polyhedra whose overlap is smaller than nms_thresh
|
|
237
|
+
|
|
238
|
+
dist.shape = (Nz,Ny,Nx, n_rays)
|
|
239
|
+
prob.shape = (Nz,Ny,Nx)
|
|
240
|
+
|
|
241
|
+
returns the retained points, probabilities, and distances:
|
|
242
|
+
|
|
243
|
+
points, prob, dist = non_maximum_suppression_3d(dist, prob, ....
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
# TODO: using b>0 with grid>1 can suppress small/cropped objects at the image boundary
|
|
247
|
+
|
|
248
|
+
dist = np.asarray(dist)
|
|
249
|
+
prob = np.asarray(prob)
|
|
250
|
+
|
|
251
|
+
assert prob.ndim == 3 and dist.ndim == 4 and dist.shape[-1] == len(rays) and prob.shape == dist.shape[:3]
|
|
252
|
+
|
|
253
|
+
grid = _normalize_grid(grid,3)
|
|
254
|
+
|
|
255
|
+
verbose and print("predicting instances with prob_thresh = {prob_thresh} and nms_thresh = {nms_thresh}".format(prob_thresh=prob_thresh, nms_thresh=nms_thresh), flush=True)
|
|
256
|
+
|
|
257
|
+
# ind_thresh = prob > prob_thresh
|
|
258
|
+
# if b is not None and b > 0:
|
|
259
|
+
# _ind_thresh = np.zeros_like(ind_thresh)
|
|
260
|
+
# _ind_thresh[b:-b,b:-b,b:-b] = True
|
|
261
|
+
# ind_thresh &= _ind_thresh
|
|
262
|
+
|
|
263
|
+
ind_thresh = _ind_prob_thresh(prob, prob_thresh, b)
|
|
264
|
+
points = np.stack(np.where(ind_thresh), axis=1)
|
|
265
|
+
verbose and print("found %s candidates"%len(points))
|
|
266
|
+
probi = prob[ind_thresh]
|
|
267
|
+
disti = dist[ind_thresh]
|
|
268
|
+
|
|
269
|
+
_sorted = np.argsort(probi)[::-1]
|
|
270
|
+
probi = probi[_sorted]
|
|
271
|
+
disti = disti[_sorted]
|
|
272
|
+
points = points[_sorted]
|
|
273
|
+
|
|
274
|
+
verbose and print("non-maximum suppression...")
|
|
275
|
+
points = (points * np.array(grid).reshape((1,3)))
|
|
276
|
+
|
|
277
|
+
inds = non_maximum_suppression_3d_inds(disti, points, rays=rays, scores=probi, thresh=nms_thresh,
|
|
278
|
+
use_bbox=use_bbox, use_kdtree = use_kdtree,
|
|
279
|
+
verbose=verbose)
|
|
280
|
+
|
|
281
|
+
verbose and print("keeping %s/%s polyhedra" % (np.count_nonzero(inds), len(inds)))
|
|
282
|
+
return points[inds], probi[inds], disti[inds]
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def non_maximum_suppression_3d_sparse(dist, prob, points, rays, b=2, nms_thresh=0.5, use_kdtree = True, verbose=False):
|
|
286
|
+
"""Non-Maximum-Supression of 3D polyhedra from a list of dists, probs and points
|
|
287
|
+
|
|
288
|
+
Retains only polyhedra whose overlap is smaller than nms_thresh
|
|
289
|
+
dist.shape = (n_polys, n_rays)
|
|
290
|
+
prob.shape = (n_polys,)
|
|
291
|
+
points.shape = (n_polys,3)
|
|
292
|
+
|
|
293
|
+
returns the retained instances
|
|
294
|
+
|
|
295
|
+
(pointsi, probi, disti, indsi)
|
|
296
|
+
|
|
297
|
+
with
|
|
298
|
+
pointsi = points[indsi] ...
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
# TODO: using b>0 with grid>1 can suppress small/cropped objects at the image boundary
|
|
302
|
+
|
|
303
|
+
dist = np.asarray(dist)
|
|
304
|
+
prob = np.asarray(prob)
|
|
305
|
+
points = np.asarray(points)
|
|
306
|
+
|
|
307
|
+
assert dist.ndim == 2 and prob.ndim == 1 and points.ndim == 2 and \
|
|
308
|
+
dist.shape[-1] == len(rays) and points.shape[-1]==3 and len(prob) == len(dist) == len(points)
|
|
309
|
+
|
|
310
|
+
verbose and print("predicting instances with nms_thresh = {nms_thresh}".format(nms_thresh=nms_thresh), flush=True)
|
|
311
|
+
|
|
312
|
+
inds_original = np.arange(len(prob))
|
|
313
|
+
_sorted = np.argsort(prob)[::-1]
|
|
314
|
+
probi = prob[_sorted]
|
|
315
|
+
disti = dist[_sorted]
|
|
316
|
+
pointsi = points[_sorted]
|
|
317
|
+
inds_original = inds_original[_sorted]
|
|
318
|
+
|
|
319
|
+
verbose and print("non-maximum suppression...")
|
|
320
|
+
|
|
321
|
+
inds = non_maximum_suppression_3d_inds(disti, pointsi, rays=rays, scores=probi, thresh=nms_thresh, use_kdtree = use_kdtree, verbose=verbose)
|
|
322
|
+
|
|
323
|
+
verbose and print("keeping %s/%s polyhedra" % (np.count_nonzero(inds), len(inds)))
|
|
324
|
+
return pointsi[inds], probi[inds], disti[inds], inds_original[inds]
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def non_maximum_suppression_3d_inds(dist, points, rays, scores, thresh=0.5, use_bbox=True, use_kdtree = True, verbose=1):
|
|
328
|
+
"""
|
|
329
|
+
Applies non maximum supression to ray-convex polyhedra given by dists and rays
|
|
330
|
+
sorted by scores and IoU threshold
|
|
331
|
+
|
|
332
|
+
P1 will suppress P2, if IoU(P1,P2) > thresh
|
|
333
|
+
|
|
334
|
+
with IoU(P1,P2) = Ainter(P1,P2) / min(A(P1),A(P2))
|
|
335
|
+
|
|
336
|
+
i.e. the smaller thresh, the more polygons will be supressed
|
|
337
|
+
|
|
338
|
+
dist.shape = (n_poly, n_rays)
|
|
339
|
+
point.shape = (n_poly, 3)
|
|
340
|
+
score.shape = (n_poly,)
|
|
341
|
+
|
|
342
|
+
returns indices of selected polygons
|
|
343
|
+
"""
|
|
344
|
+
from .lib.stardist3d import c_non_max_suppression_inds
|
|
345
|
+
|
|
346
|
+
assert dist.ndim == 2
|
|
347
|
+
assert points.ndim == 2
|
|
348
|
+
assert dist.shape[1] == len(rays)
|
|
349
|
+
|
|
350
|
+
n_poly = dist.shape[0]
|
|
351
|
+
|
|
352
|
+
if scores is None:
|
|
353
|
+
scores = np.ones(n_poly)
|
|
354
|
+
|
|
355
|
+
assert len(scores) == n_poly
|
|
356
|
+
assert points.shape[0] == n_poly
|
|
357
|
+
|
|
358
|
+
# sort scores descendingly
|
|
359
|
+
ind = np.argsort(scores)[::-1]
|
|
360
|
+
survivors = np.ones(n_poly, bool)
|
|
361
|
+
dist = dist[ind]
|
|
362
|
+
points = points[ind]
|
|
363
|
+
scores = scores[ind]
|
|
364
|
+
|
|
365
|
+
def _prep(x, dtype):
|
|
366
|
+
return np.ascontiguousarray(x.astype(dtype, copy=False))
|
|
367
|
+
|
|
368
|
+
if verbose:
|
|
369
|
+
t = time()
|
|
370
|
+
|
|
371
|
+
survivors[ind] = c_non_max_suppression_inds(_prep(dist, np.float32),
|
|
372
|
+
_prep(points, np.float32),
|
|
373
|
+
_prep(rays.vertices, np.float32),
|
|
374
|
+
_prep(rays.faces, np.int32),
|
|
375
|
+
_prep(scores, np.float32),
|
|
376
|
+
int(use_bbox),
|
|
377
|
+
int(use_kdtree),
|
|
378
|
+
int(verbose),
|
|
379
|
+
np.float32(thresh))
|
|
380
|
+
|
|
381
|
+
if verbose:
|
|
382
|
+
print("NMS took %.4f s" % (time() - t))
|
|
383
|
+
|
|
384
|
+
return survivors
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import print_function, unicode_literals, absolute_import, division
|
|
2
|
+
import numpy as np
|
|
3
|
+
from warnings import warn
|
|
4
|
+
from csbdeep.utils import normalize
|
|
5
|
+
from ..utils import _normalize_grid
|
|
6
|
+
from ..matching import matching
|
|
7
|
+
|
|
8
|
+
def random_label_cmap(n=2**16, h = (0,1), l = (.4,1), s =(.2,.8), seed=None):
|
|
9
|
+
import matplotlib
|
|
10
|
+
import colorsys
|
|
11
|
+
# cols = np.random.rand(n,3)
|
|
12
|
+
# cols = np.random.uniform(0.1,1.0,(n,3))
|
|
13
|
+
rng = np.random.default_rng(seed)
|
|
14
|
+
|
|
15
|
+
h, l, s = rng.uniform(*h, n), rng.uniform(*l, n), rng.uniform(*s, n)
|
|
16
|
+
cols = np.stack([colorsys.hls_to_rgb(_h,_l,_s) for _h,_l,_s in zip(h,l,s)],axis=0)
|
|
17
|
+
cols[0] = 0
|
|
18
|
+
return matplotlib.colors.ListedColormap(cols)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _plot_polygon(x,y,score,color):
|
|
22
|
+
import matplotlib.pyplot as plt
|
|
23
|
+
a,b = list(x),list(y)
|
|
24
|
+
a += a[:1]
|
|
25
|
+
b += b[:1]
|
|
26
|
+
plt.plot(a,b,'--', alpha=1, linewidth=score, zorder=1, color=color)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def draw_polygons(coord, score, poly_idx, grid=(1,1), cmap=None, show_dist=False):
|
|
30
|
+
"""poly_idx is a N x 2 array with row-col coordinate indices"""
|
|
31
|
+
grid = _normalize_grid(grid,2)
|
|
32
|
+
return _draw_polygons(polygons=coord[poly_idx[:,0],poly_idx[:,1]],
|
|
33
|
+
points=poly_idx*np.array(grid),
|
|
34
|
+
scores=score[poly_idx[:,0],poly_idx[:,1]],
|
|
35
|
+
cmap=cmap, show_dist=show_dist)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _draw_polygons(polygons, points=None, scores=None, grid=None, cmap=None, show_dist=False):
|
|
39
|
+
"""
|
|
40
|
+
polygons is a list/array of x,y coordinate lists/arrays
|
|
41
|
+
points is a list/array of x,y coordinates
|
|
42
|
+
scores is a list/array of scalar values between 0 and 1
|
|
43
|
+
"""
|
|
44
|
+
# TODO: better name for this function?
|
|
45
|
+
import matplotlib.pyplot as plt
|
|
46
|
+
from matplotlib.collections import LineCollection
|
|
47
|
+
|
|
48
|
+
if grid is not None:
|
|
49
|
+
warn("parameter 'grid' has no effect anymore, please remove")
|
|
50
|
+
if points is None:
|
|
51
|
+
points = [None]*len(polygons)
|
|
52
|
+
if scores is None:
|
|
53
|
+
scores = np.ones(len(polygons))
|
|
54
|
+
if cmap is None:
|
|
55
|
+
cmap = random_label_cmap(len(polygons)+1)
|
|
56
|
+
|
|
57
|
+
assert len(polygons) == len(scores)
|
|
58
|
+
assert len(cmap.colors[1:]) >= len(polygons)
|
|
59
|
+
assert not show_dist or all(p is not None for p in points)
|
|
60
|
+
|
|
61
|
+
for point,poly,score,c in zip(points,polygons,scores,cmap.colors[1:]):
|
|
62
|
+
if point is not None:
|
|
63
|
+
plt.plot(point[1], point[0], '.', markersize=8*score, color=c)
|
|
64
|
+
|
|
65
|
+
if show_dist:
|
|
66
|
+
dist_lines = np.empty((poly.shape[-1],2,2))
|
|
67
|
+
dist_lines[:,0,0] = poly[1]
|
|
68
|
+
dist_lines[:,0,1] = poly[0]
|
|
69
|
+
dist_lines[:,1,0] = point[1]
|
|
70
|
+
dist_lines[:,1,1] = point[0]
|
|
71
|
+
plt.gca().add_collection(LineCollection(dist_lines, colors=c, linewidths=0.4))
|
|
72
|
+
|
|
73
|
+
_plot_polygon(poly[1], poly[0], 3*score, color=c)
|
|
74
|
+
|