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,215 @@
|
|
|
1
|
+
from __future__ import print_function, unicode_literals, absolute_import, division
|
|
2
|
+
import numpy as np
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
from skimage.measure import regionprops
|
|
6
|
+
from skimage.draw import polygon
|
|
7
|
+
from csbdeep.utils import _raise
|
|
8
|
+
|
|
9
|
+
from ..utils import path_absolute, _is_power_of_2, _normalize_grid
|
|
10
|
+
from ..matching import _check_label_array
|
|
11
|
+
from ..lib.stardist2d import c_star_dist
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _ocl_star_dist(lbl, n_rays=32, grid=(1,1)):
|
|
16
|
+
from gputools import OCLProgram, OCLArray, OCLImage
|
|
17
|
+
(np.isscalar(n_rays) and 0 < int(n_rays)) or _raise(ValueError())
|
|
18
|
+
n_rays = int(n_rays)
|
|
19
|
+
# slicing with grid is done with tuple(slice(0, None, g) for g in grid)
|
|
20
|
+
res_shape = tuple((s-1)//g+1 for s, g in zip(lbl.shape, grid))
|
|
21
|
+
|
|
22
|
+
src = OCLImage.from_array(lbl.astype(np.uint16,copy=False))
|
|
23
|
+
dst = OCLArray.empty(res_shape+(n_rays,), dtype=np.float32)
|
|
24
|
+
program = OCLProgram(path_absolute("kernels/stardist2d.cl"), build_options=['-D', 'N_RAYS=%d' % n_rays])
|
|
25
|
+
program.run_kernel('star_dist', res_shape[::-1], None, dst.data, src, np.int32(grid[0]),np.int32(grid[1]))
|
|
26
|
+
return dst.get()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _cpp_star_dist(lbl, n_rays=32, grid=(1,1)):
|
|
30
|
+
(np.isscalar(n_rays) and 0 < int(n_rays)) or _raise(ValueError())
|
|
31
|
+
return c_star_dist(lbl.astype(np.uint16,copy=False), np.int32(n_rays), np.int32(grid[0]),np.int32(grid[1]))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _py_star_dist(a, n_rays=32, grid=(1,1)):
|
|
35
|
+
(np.isscalar(n_rays) and 0 < int(n_rays)) or _raise(ValueError())
|
|
36
|
+
if grid != (1,1):
|
|
37
|
+
raise NotImplementedError(grid)
|
|
38
|
+
|
|
39
|
+
n_rays = int(n_rays)
|
|
40
|
+
a = a.astype(np.uint16,copy=False)
|
|
41
|
+
dst = np.empty(a.shape+(n_rays,),np.float32)
|
|
42
|
+
|
|
43
|
+
for i in range(a.shape[0]):
|
|
44
|
+
for j in range(a.shape[1]):
|
|
45
|
+
value = a[i,j]
|
|
46
|
+
if value == 0:
|
|
47
|
+
dst[i,j] = 0
|
|
48
|
+
else:
|
|
49
|
+
st_rays = np.float32((2*np.pi) / n_rays)
|
|
50
|
+
for k in range(n_rays):
|
|
51
|
+
phi = np.float32(k*st_rays)
|
|
52
|
+
dy = np.cos(phi)
|
|
53
|
+
dx = np.sin(phi)
|
|
54
|
+
x, y = np.float32(0), np.float32(0)
|
|
55
|
+
while True:
|
|
56
|
+
x += dx
|
|
57
|
+
y += dy
|
|
58
|
+
ii = int(round(i+x))
|
|
59
|
+
jj = int(round(j+y))
|
|
60
|
+
if (ii < 0 or ii >= a.shape[0] or
|
|
61
|
+
jj < 0 or jj >= a.shape[1] or
|
|
62
|
+
value != a[ii,jj]):
|
|
63
|
+
# small correction as we overshoot the boundary
|
|
64
|
+
t_corr = 1-.5/max(np.abs(dx),np.abs(dy))
|
|
65
|
+
x -= t_corr*dx
|
|
66
|
+
y -= t_corr*dy
|
|
67
|
+
dist = np.sqrt(x**2+y**2)
|
|
68
|
+
dst[i,j,k] = dist
|
|
69
|
+
break
|
|
70
|
+
return dst
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def star_dist(a, n_rays=32, grid=(1,1), mode='cpp'):
|
|
74
|
+
"""'a' assumbed to be a label image with integer values that encode object ids. id 0 denotes background."""
|
|
75
|
+
|
|
76
|
+
n_rays >= 3 or _raise(ValueError("need 'n_rays' >= 3"))
|
|
77
|
+
|
|
78
|
+
if mode == 'python':
|
|
79
|
+
return _py_star_dist(a, n_rays, grid=grid)
|
|
80
|
+
elif mode == 'cpp':
|
|
81
|
+
return _cpp_star_dist(a, n_rays, grid=grid)
|
|
82
|
+
elif mode == 'opencl':
|
|
83
|
+
return _ocl_star_dist(a, n_rays, grid=grid)
|
|
84
|
+
else:
|
|
85
|
+
_raise(ValueError("Unknown mode %s" % mode))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _dist_to_coord_old(rhos, grid=(1,1)):
|
|
89
|
+
"""convert from polar to cartesian coordinates for a single image (3-D array) or multiple images (4-D array)"""
|
|
90
|
+
|
|
91
|
+
grid = _normalize_grid(grid,2)
|
|
92
|
+
is_single_image = rhos.ndim == 3
|
|
93
|
+
if is_single_image:
|
|
94
|
+
rhos = np.expand_dims(rhos,0)
|
|
95
|
+
assert rhos.ndim == 4
|
|
96
|
+
|
|
97
|
+
n_images,h,w,n_rays = rhos.shape
|
|
98
|
+
coord = np.empty((n_images,h,w,2,n_rays),dtype=rhos.dtype)
|
|
99
|
+
|
|
100
|
+
start = np.indices((h,w))
|
|
101
|
+
for i in range(2):
|
|
102
|
+
coord[...,i,:] = grid[i] * np.broadcast_to(start[i].reshape(1,h,w,1), (n_images,h,w,n_rays))
|
|
103
|
+
|
|
104
|
+
phis = ray_angles(n_rays).reshape(1,1,1,n_rays)
|
|
105
|
+
|
|
106
|
+
coord[...,0,:] += rhos * np.sin(phis) # row coordinate
|
|
107
|
+
coord[...,1,:] += rhos * np.cos(phis) # col coordinate
|
|
108
|
+
|
|
109
|
+
return coord[0] if is_single_image else coord
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _polygons_to_label_old(coord, prob, points, shape=None, thr=-np.inf):
|
|
113
|
+
sh = coord.shape[:2] if shape is None else shape
|
|
114
|
+
lbl = np.zeros(sh,np.int32)
|
|
115
|
+
# sort points with increasing probability
|
|
116
|
+
ind = np.argsort([ prob[p[0],p[1]] for p in points ])
|
|
117
|
+
points = points[ind]
|
|
118
|
+
|
|
119
|
+
i = 1
|
|
120
|
+
for p in points:
|
|
121
|
+
if prob[p[0],p[1]] < thr:
|
|
122
|
+
continue
|
|
123
|
+
rr,cc = polygon(coord[p[0],p[1],0], coord[p[0],p[1],1], sh)
|
|
124
|
+
lbl[rr,cc] = i
|
|
125
|
+
i += 1
|
|
126
|
+
|
|
127
|
+
return lbl
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def dist_to_coord(dist, points, scale_dist=(1,1)):
|
|
131
|
+
"""convert from polar to cartesian coordinates for a list of distances and center points
|
|
132
|
+
dist.shape = (n_polys, n_rays)
|
|
133
|
+
points.shape = (n_polys, 2)
|
|
134
|
+
len(scale_dist) = 2
|
|
135
|
+
return coord.shape = (n_polys,2,n_rays)
|
|
136
|
+
"""
|
|
137
|
+
dist = np.asarray(dist)
|
|
138
|
+
points = np.asarray(points)
|
|
139
|
+
assert dist.ndim==2 and points.ndim==2 and len(dist)==len(points) \
|
|
140
|
+
and points.shape[1]==2 and len(scale_dist)==2
|
|
141
|
+
n_rays = dist.shape[1]
|
|
142
|
+
phis = ray_angles(n_rays)
|
|
143
|
+
coord = (dist[:,np.newaxis]*np.array([np.sin(phis),np.cos(phis)])).astype(np.float32)
|
|
144
|
+
coord *= np.asarray(scale_dist).reshape(1,2,1)
|
|
145
|
+
coord += points[...,np.newaxis]
|
|
146
|
+
return coord
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def polygons_to_label_coord(coord, shape, labels=None):
|
|
150
|
+
"""renders polygons to image of given shape
|
|
151
|
+
|
|
152
|
+
coord.shape = (n_polys, n_rays)
|
|
153
|
+
"""
|
|
154
|
+
coord = np.asarray(coord)
|
|
155
|
+
if labels is None: labels = np.arange(len(coord))
|
|
156
|
+
|
|
157
|
+
_check_label_array(labels, "labels")
|
|
158
|
+
assert coord.ndim==3 and coord.shape[1]==2 and len(coord)==len(labels)
|
|
159
|
+
|
|
160
|
+
lbl = np.zeros(shape,np.int32)
|
|
161
|
+
|
|
162
|
+
for i,c in zip(labels,coord):
|
|
163
|
+
rr,cc = polygon(*c, shape)
|
|
164
|
+
lbl[rr,cc] = i+1
|
|
165
|
+
|
|
166
|
+
return lbl
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def polygons_to_label(dist, points, shape, prob=None, thr=-np.inf, scale_dist=(1,1)):
|
|
170
|
+
"""converts distances and center points to label image
|
|
171
|
+
|
|
172
|
+
dist.shape = (n_polys, n_rays)
|
|
173
|
+
points.shape = (n_polys, 2)
|
|
174
|
+
|
|
175
|
+
label ids will be consecutive and adhere to the order given
|
|
176
|
+
"""
|
|
177
|
+
dist = np.asarray(dist)
|
|
178
|
+
points = np.asarray(points)
|
|
179
|
+
prob = np.inf*np.ones(len(points)) if prob is None else np.asarray(prob)
|
|
180
|
+
|
|
181
|
+
assert dist.ndim==2 and points.ndim==2 and len(dist)==len(points)
|
|
182
|
+
assert len(points)==len(prob) and points.shape[1]==2 and prob.ndim==1
|
|
183
|
+
|
|
184
|
+
n_rays = dist.shape[1]
|
|
185
|
+
|
|
186
|
+
ind = prob>thr
|
|
187
|
+
points = points[ind]
|
|
188
|
+
dist = dist[ind]
|
|
189
|
+
prob = prob[ind]
|
|
190
|
+
|
|
191
|
+
ind = np.argsort(prob, kind='stable')
|
|
192
|
+
points = points[ind]
|
|
193
|
+
dist = dist[ind]
|
|
194
|
+
|
|
195
|
+
coord = dist_to_coord(dist, points, scale_dist=scale_dist)
|
|
196
|
+
|
|
197
|
+
return polygons_to_label_coord(coord, shape=shape, labels=ind)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def relabel_image_stardist(lbl, n_rays, **kwargs):
|
|
201
|
+
"""relabel each label region in `lbl` with its star representation"""
|
|
202
|
+
_check_label_array(lbl, "lbl")
|
|
203
|
+
if not lbl.ndim==2:
|
|
204
|
+
raise ValueError("lbl image should be 2 dimensional")
|
|
205
|
+
dist = star_dist(lbl, n_rays, **kwargs)
|
|
206
|
+
points = np.array(tuple(np.array(r.centroid).astype(int) for r in regionprops(lbl)))
|
|
207
|
+
if len(points) == 0:
|
|
208
|
+
dist, points = np.zeros((0,n_rays),np.float32), np.zeros((0,2),int)
|
|
209
|
+
else:
|
|
210
|
+
dist = dist[tuple(points.T)]
|
|
211
|
+
return polygons_to_label(dist, points, shape=lbl.shape)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def ray_angles(n_rays=32):
|
|
215
|
+
return np.linspace(0,2*np.pi,n_rays,endpoint=False)
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
from __future__ import print_function, unicode_literals, absolute_import, division
|
|
2
|
+
import numpy as np
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from skimage.measure import regionprops
|
|
6
|
+
from csbdeep.utils import _raise
|
|
7
|
+
from tqdm import tqdm
|
|
8
|
+
|
|
9
|
+
from ..utils import path_absolute, _normalize_grid
|
|
10
|
+
from ..matching import _check_label_array
|
|
11
|
+
# from ..lib.stardist3d import c_star_dist3d, c_polyhedron_to_label, c_dist_to_volume, c_dist_to_centroid
|
|
12
|
+
from ..lib.stardist3d import c_star_dist3d, c_polyhedron_to_label
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _cpp_star_dist3D(lbl, rays, grid=(1,1,1)):
|
|
17
|
+
dz, dy, dx = rays.vertices.T
|
|
18
|
+
grid = _normalize_grid(grid,3)
|
|
19
|
+
|
|
20
|
+
return c_star_dist3d(lbl.astype(np.uint16, copy=False),
|
|
21
|
+
dz.astype(np.float32, copy=False),
|
|
22
|
+
dy.astype(np.float32, copy=False),
|
|
23
|
+
dx.astype(np.float32, copy=False),
|
|
24
|
+
int(len(rays)), *tuple(int(a) for a in grid))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _py_star_dist3D(img, rays, grid=(1,1,1)):
|
|
28
|
+
grid = _normalize_grid(grid,3)
|
|
29
|
+
img = img.astype(np.uint16, copy=False)
|
|
30
|
+
dst_shape = tuple(s // a for a, s in zip(grid, img.shape)) + (len(rays),)
|
|
31
|
+
dst = np.empty(dst_shape, np.float32)
|
|
32
|
+
|
|
33
|
+
dzs, dys, dxs = rays.vertices.T
|
|
34
|
+
|
|
35
|
+
for i in range(dst_shape[0]):
|
|
36
|
+
for j in range(dst_shape[1]):
|
|
37
|
+
for k in range(dst_shape[2]):
|
|
38
|
+
value = img[i * grid[0], j * grid[1], k * grid[2]]
|
|
39
|
+
if value == 0:
|
|
40
|
+
dst[i, j, k] = 0
|
|
41
|
+
else:
|
|
42
|
+
|
|
43
|
+
for n, (dz, dy, dx) in enumerate(zip(dzs, dys, dxs)):
|
|
44
|
+
x, y, z = np.float32(0), np.float32(0), np.float32(0)
|
|
45
|
+
while True:
|
|
46
|
+
x += dx
|
|
47
|
+
y += dy
|
|
48
|
+
z += dz
|
|
49
|
+
ii = int(round(i * grid[0] + z))
|
|
50
|
+
jj = int(round(j * grid[1] + y))
|
|
51
|
+
kk = int(round(k * grid[2] + x))
|
|
52
|
+
if (ii < 0 or ii >= img.shape[0] or
|
|
53
|
+
jj < 0 or jj >= img.shape[1] or
|
|
54
|
+
kk < 0 or kk >= img.shape[2] or
|
|
55
|
+
value != img[ii, jj, kk]):
|
|
56
|
+
dist = np.sqrt(x * x + y * y + z * z)
|
|
57
|
+
dst[i, j, k, n] = dist
|
|
58
|
+
break
|
|
59
|
+
|
|
60
|
+
return dst
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _ocl_star_dist3D(lbl, rays, grid=(1,1,1)):
|
|
64
|
+
from gputools import OCLProgram, OCLArray, OCLImage
|
|
65
|
+
|
|
66
|
+
grid = _normalize_grid(grid,3)
|
|
67
|
+
|
|
68
|
+
# if not all(g==1 for g in grid):
|
|
69
|
+
# raise NotImplementedError("grid not yet implemented for OpenCL version of star_dist3D()...")
|
|
70
|
+
|
|
71
|
+
# slicing with grid is done with tuple(slice(0, None, g) for g in grid)
|
|
72
|
+
res_shape = tuple((s-1)//g+1 for s, g in zip(lbl.shape, grid))
|
|
73
|
+
|
|
74
|
+
lbl_g = OCLImage.from_array(lbl.astype(np.uint16, copy=False))
|
|
75
|
+
dist_g = OCLArray.empty(res_shape + (len(rays),), dtype=np.float32)
|
|
76
|
+
rays_g = OCLArray.from_array(rays.vertices.astype(np.float32, copy=False))
|
|
77
|
+
|
|
78
|
+
program = OCLProgram(path_absolute("kernels/stardist3d.cl"), build_options=['-D', 'N_RAYS=%d' % len(rays)])
|
|
79
|
+
program.run_kernel('stardist3d', res_shape[::-1], None,
|
|
80
|
+
lbl_g, rays_g.data, dist_g.data,
|
|
81
|
+
np.int32(grid[0]),np.int32(grid[1]),np.int32(grid[2]))
|
|
82
|
+
|
|
83
|
+
return dist_g.get()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def star_dist3D(lbl, rays, grid=(1,1,1), mode='cpp'):
|
|
87
|
+
"""lbl assumbed to be a label image with integer values that encode object ids. id 0 denotes background."""
|
|
88
|
+
|
|
89
|
+
grid = _normalize_grid(grid,3)
|
|
90
|
+
if mode == 'python':
|
|
91
|
+
return _py_star_dist3D(lbl, rays, grid=grid)
|
|
92
|
+
elif mode == 'cpp':
|
|
93
|
+
return _cpp_star_dist3D(lbl, rays, grid=grid)
|
|
94
|
+
elif mode == 'opencl':
|
|
95
|
+
return _ocl_star_dist3D(lbl, rays, grid=grid)
|
|
96
|
+
else:
|
|
97
|
+
_raise(ValueError("Unknown mode %s" % mode))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def polyhedron_to_label(dist, points, rays, shape, prob=None, thr=-np.inf, labels=None, mode="full", verbose=True, overlap_label=None):
|
|
101
|
+
"""
|
|
102
|
+
creates labeled image from stardist representations
|
|
103
|
+
|
|
104
|
+
:param dist: array of shape (n_points,n_rays)
|
|
105
|
+
the list of distances for each point and ray
|
|
106
|
+
:param points: array of shape (n_points, 3)
|
|
107
|
+
the list of center points
|
|
108
|
+
:param rays: Rays object
|
|
109
|
+
Ray object (e.g. `stardist.Rays_GoldenSpiral`) defining
|
|
110
|
+
vertices and faces
|
|
111
|
+
:param shape: (nz,ny,nx)
|
|
112
|
+
output shape of the image
|
|
113
|
+
:param prob: array of length/shape (n_points,) or None
|
|
114
|
+
probability per polyhedron
|
|
115
|
+
:param thr: scalar
|
|
116
|
+
probability threshold (only polyhedra with prob>thr are labeled)
|
|
117
|
+
:param labels: array of length/shape (n_points,) or None
|
|
118
|
+
labels to use
|
|
119
|
+
:param mode: str
|
|
120
|
+
labeling mode, can be "full", "kernel", "hull", "bbox" or "debug"
|
|
121
|
+
:param verbose: bool
|
|
122
|
+
enable to print some debug messages
|
|
123
|
+
:param overlap_label: scalar or None
|
|
124
|
+
if given, will label each pixel that belongs ot more than one polyhedron with that label
|
|
125
|
+
:return: array of given shape
|
|
126
|
+
labeled image
|
|
127
|
+
"""
|
|
128
|
+
if len(points) == 0:
|
|
129
|
+
if verbose:
|
|
130
|
+
print("warning: empty list of points (returning background-only image)")
|
|
131
|
+
return np.zeros(shape, np.uint16)
|
|
132
|
+
|
|
133
|
+
dist = np.asanyarray(dist)
|
|
134
|
+
points = np.asanyarray(points)
|
|
135
|
+
|
|
136
|
+
if dist.ndim == 1:
|
|
137
|
+
dist = dist.reshape(1, -1)
|
|
138
|
+
if points.ndim == 1:
|
|
139
|
+
points = points.reshape(1, -1)
|
|
140
|
+
if labels is None:
|
|
141
|
+
labels = np.arange(1, len(points) + 1)
|
|
142
|
+
|
|
143
|
+
if np.amin(dist) <= 0:
|
|
144
|
+
raise ValueError("distance array should be positive!")
|
|
145
|
+
|
|
146
|
+
prob = np.ones(len(points)) if prob is None else np.asanyarray(prob)
|
|
147
|
+
|
|
148
|
+
if dist.ndim != 2:
|
|
149
|
+
raise ValueError("dist should be 2 dimensional but has shape %s" % str(dist.shape))
|
|
150
|
+
|
|
151
|
+
if dist.shape[1] != len(rays):
|
|
152
|
+
raise ValueError("inconsistent number of rays!")
|
|
153
|
+
|
|
154
|
+
if len(prob) != len(points):
|
|
155
|
+
raise ValueError("len(prob) != len(points)")
|
|
156
|
+
|
|
157
|
+
if len(labels) != len(points):
|
|
158
|
+
raise ValueError("len(labels) != len(points)")
|
|
159
|
+
|
|
160
|
+
modes = {"full": 0, "kernel": 1, "hull": 2, "bbox": 3, "debug": 4}
|
|
161
|
+
|
|
162
|
+
if not mode in modes:
|
|
163
|
+
raise KeyError("Unknown render mode '%s' , allowed: %s" % (mode, tuple(modes.keys())))
|
|
164
|
+
|
|
165
|
+
lbl = np.zeros(shape, np.uint16)
|
|
166
|
+
|
|
167
|
+
# filter points
|
|
168
|
+
ind = np.where(prob >= thr)[0]
|
|
169
|
+
if len(ind) == 0:
|
|
170
|
+
if verbose:
|
|
171
|
+
print("warning: no points found with probability>= {thr:.4f} (returning background-only image)".format(thr=thr))
|
|
172
|
+
return lbl
|
|
173
|
+
|
|
174
|
+
prob = prob[ind]
|
|
175
|
+
points = points[ind]
|
|
176
|
+
dist = dist[ind]
|
|
177
|
+
labels = labels[ind]
|
|
178
|
+
|
|
179
|
+
# sort points with decreasing probability
|
|
180
|
+
ind = np.argsort(prob)[::-1]
|
|
181
|
+
points = points[ind]
|
|
182
|
+
dist = dist[ind]
|
|
183
|
+
labels = labels[ind]
|
|
184
|
+
|
|
185
|
+
def _prep(x, dtype):
|
|
186
|
+
return np.ascontiguousarray(x.astype(dtype, copy=False))
|
|
187
|
+
|
|
188
|
+
return c_polyhedron_to_label(_prep(dist, np.float32),
|
|
189
|
+
_prep(points, np.float32),
|
|
190
|
+
_prep(rays.vertices, np.float32),
|
|
191
|
+
_prep(rays.faces, np.int32),
|
|
192
|
+
_prep(labels, np.int32),
|
|
193
|
+
np.int32(modes[mode]),
|
|
194
|
+
np.int32(verbose),
|
|
195
|
+
np.int32(overlap_label is not None),
|
|
196
|
+
np.int32(0 if overlap_label is None else overlap_label),
|
|
197
|
+
shape
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def relabel_image_stardist3D(lbl, rays, verbose=False, **kwargs):
|
|
202
|
+
"""relabel each label region in `lbl` with its star representation"""
|
|
203
|
+
_check_label_array(lbl, "lbl")
|
|
204
|
+
if not lbl.ndim==3:
|
|
205
|
+
raise ValueError("lbl image should be 3 dimensional")
|
|
206
|
+
|
|
207
|
+
dist_all = star_dist3D(lbl, rays, **kwargs)
|
|
208
|
+
|
|
209
|
+
regs = regionprops(lbl)
|
|
210
|
+
|
|
211
|
+
points = np.array(tuple(np.array(r.centroid).astype(int) for r in regs))
|
|
212
|
+
labs = np.array(tuple(r.label for r in regs))
|
|
213
|
+
dist = np.array(tuple(dist_all[p[0], p[1], p[2]] for p in points))
|
|
214
|
+
dist = np.maximum(dist, 1e-3)
|
|
215
|
+
|
|
216
|
+
lbl_new = polyhedron_to_label(dist, points, rays, shape=lbl.shape, labels=labs, verbose=verbose)
|
|
217
|
+
return lbl_new
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def dist_to_volume(dist, rays):
|
|
221
|
+
""" returns areas of polyhedra
|
|
222
|
+
dist.shape = (nz,ny,nx,nrays)
|
|
223
|
+
"""
|
|
224
|
+
dist = np.asanyarray(dist)
|
|
225
|
+
dist.ndim == 4 or _raise(ValueError("dist.ndim = {dist.ndim} but should be 4".format(dist = dist)))
|
|
226
|
+
dist.shape[-1]== len(rays) or _raise(ValueError("dist.shape[-1] = {d} but should be {l}".format(d = dist.shape[-1], l = len(rays))))
|
|
227
|
+
|
|
228
|
+
dist = np.ascontiguousarray(dist.astype(np.float32, copy=False))
|
|
229
|
+
|
|
230
|
+
def _prep(x, dtype):
|
|
231
|
+
return np.ascontiguousarray(x.astype(dtype, copy=False))
|
|
232
|
+
|
|
233
|
+
return c_dist_to_volume(_prep(dist, np.float32),
|
|
234
|
+
_prep(rays.vertices, np.float32),
|
|
235
|
+
_prep(rays.faces, np.int32))
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def dist_to_centroid(dist, rays, mode='absolute'):
|
|
239
|
+
""" returns centroids of polyhedra
|
|
240
|
+
|
|
241
|
+
dist.shape = (nz,ny,nx,nrays)
|
|
242
|
+
mode = 'absolute' or 'relative'
|
|
243
|
+
|
|
244
|
+
"""
|
|
245
|
+
dist.ndim == 4 or _raise(ValueError("dist.ndim = {dist.ndim} but should be 4".format(dist = dist)))
|
|
246
|
+
dist.shape[-1]== len(rays) or _raise(ValueError("dist.shape[-1] = {d} but should be {l}".format(d = dist.shape[-1], l = len(rays))))
|
|
247
|
+
dist = np.ascontiguousarray(dist.astype(np.float32, copy=False))
|
|
248
|
+
|
|
249
|
+
mode in ('absolute', 'relative') or _raise(ValueError("mode should be either 'absolute' or 'relative'"))
|
|
250
|
+
|
|
251
|
+
def _prep(x, dtype):
|
|
252
|
+
return np.ascontiguousarray(x.astype(dtype, copy=False))
|
|
253
|
+
|
|
254
|
+
return c_dist_to_centroid(_prep(dist, np.float32),
|
|
255
|
+
_prep(rays.vertices, np.float32),
|
|
256
|
+
_prep(rays.faces, np.int32),
|
|
257
|
+
np.int32(mode=='absolute'))
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def dist_to_coord3D(dist, points, rays_vertices):
|
|
262
|
+
""" converts dist/points/rays_vertices to list of coords """
|
|
263
|
+
|
|
264
|
+
dist = np.asarray(dist)
|
|
265
|
+
points = np.asarray(points)
|
|
266
|
+
rays_vertices = np.asarray(rays_vertices)
|
|
267
|
+
|
|
268
|
+
if not all((len(dist)==len(points), dist.ndim==2, points.ndim==2,
|
|
269
|
+
points.shape[-1]==3, rays_vertices.shape[-1]==3, dist.shape[-1]==len(rays_vertices))):
|
|
270
|
+
raise ValueError(f"Wrong shapes! dist -> (m,n) points -> (m,3) rays_vertices -> (m,)")
|
|
271
|
+
|
|
272
|
+
# return points[:,np.newaxis]+dist[...,np.newaxis]*rays_vertices
|
|
273
|
+
|
|
274
|
+
return points[:,np.newaxis]+dist[...,np.newaxis]*rays_vertices
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def export_to_obj_file3D(polys, fname=None, scale=1, single_mesh=True, uv_map=False, name="poly"):
|
|
278
|
+
""" exports 3D mesh result to obj file format """
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
dist = polys["dist"]
|
|
282
|
+
points = polys["points"]
|
|
283
|
+
rays_vertices = polys["rays_vertices"]
|
|
284
|
+
rays_faces = polys["rays_faces"]
|
|
285
|
+
except KeyError as e:
|
|
286
|
+
print(e)
|
|
287
|
+
raise ValueError("polys should be a dict with keys 'dist', 'points', 'rays_vertices', 'rays_faces' (such as generated by StarDist3D.predict_instances) ")
|
|
288
|
+
|
|
289
|
+
coord = dist_to_coord3D(dist, points, rays_vertices)
|
|
290
|
+
|
|
291
|
+
if not all((coord.ndim==3, coord.shape[-1]==3, rays_faces.shape[-1]==3)):
|
|
292
|
+
raise ValueError(f"Wrong shapes! coord -> (m,n,3) rays_faces -> (k,3)")
|
|
293
|
+
|
|
294
|
+
if np.isscalar(scale):
|
|
295
|
+
scale = (scale,)*3
|
|
296
|
+
|
|
297
|
+
scale = np.asarray(scale)
|
|
298
|
+
assert len(scale)==3
|
|
299
|
+
|
|
300
|
+
coord *= scale
|
|
301
|
+
|
|
302
|
+
obj_str = ""
|
|
303
|
+
vert_count = 0
|
|
304
|
+
|
|
305
|
+
decimals = int(max(1,1-np.log10(np.min(scale))))
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
scaled_verts = scale*rays_vertices
|
|
309
|
+
scaled_verts /= np.linalg.norm(scaled_verts,axis = 1, keepdims=True)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
vertex_line = f"v {{x:.{decimals}f}} {{y:.{decimals}f}} {{z:.{decimals}f}}\n"
|
|
313
|
+
|
|
314
|
+
rays_faces = rays_faces.copy()+1
|
|
315
|
+
|
|
316
|
+
for i, xs in enumerate(tqdm(coord)):
|
|
317
|
+
# reorder to xyz
|
|
318
|
+
xs = xs[:,[2,1,0]]
|
|
319
|
+
|
|
320
|
+
# print(xs)
|
|
321
|
+
|
|
322
|
+
# new object
|
|
323
|
+
if i==0 or not single_mesh:
|
|
324
|
+
obj_str += f"o {name}_{i:d}\n"
|
|
325
|
+
|
|
326
|
+
# vertex coords
|
|
327
|
+
for x,y,z in xs:
|
|
328
|
+
obj_str += vertex_line.format(x=x,y=y,z=z)
|
|
329
|
+
|
|
330
|
+
if uv_map:
|
|
331
|
+
# UV coords
|
|
332
|
+
for vz,vy,vx in scaled_verts:
|
|
333
|
+
u = 1-(.5 + .5*np.arctan2(vz,vx)/np.pi)
|
|
334
|
+
v = 1-(.5 - np.arcsin(vy)/np.pi)
|
|
335
|
+
obj_str += f"vt {u:.4f} {v:.4f}\n"
|
|
336
|
+
|
|
337
|
+
# face indices
|
|
338
|
+
for face in rays_faces:
|
|
339
|
+
obj_str += f"f {face[0]}/{face[0]} {face[1]}/{face[1]} {face[2]}/{face[2]}\n"
|
|
340
|
+
|
|
341
|
+
rays_faces += len(xs)
|
|
342
|
+
|
|
343
|
+
if fname is not None:
|
|
344
|
+
with open(fname,"w") as f:
|
|
345
|
+
f.write(obj_str)
|
|
346
|
+
|
|
347
|
+
return obj_str
|
|
348
|
+
|
|
349
|
+
|