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.
Files changed (148) hide show
  1. senoquant/__init__.py +6 -0
  2. senoquant/_reader.py +7 -0
  3. senoquant/_widget.py +33 -0
  4. senoquant/napari.yaml +83 -0
  5. senoquant/reader/__init__.py +5 -0
  6. senoquant/reader/core.py +369 -0
  7. senoquant/tabs/__init__.py +15 -0
  8. senoquant/tabs/batch/__init__.py +10 -0
  9. senoquant/tabs/batch/backend.py +641 -0
  10. senoquant/tabs/batch/config.py +270 -0
  11. senoquant/tabs/batch/frontend.py +1283 -0
  12. senoquant/tabs/batch/io.py +326 -0
  13. senoquant/tabs/batch/layers.py +86 -0
  14. senoquant/tabs/quantification/__init__.py +1 -0
  15. senoquant/tabs/quantification/backend.py +228 -0
  16. senoquant/tabs/quantification/features/__init__.py +80 -0
  17. senoquant/tabs/quantification/features/base.py +142 -0
  18. senoquant/tabs/quantification/features/marker/__init__.py +5 -0
  19. senoquant/tabs/quantification/features/marker/config.py +69 -0
  20. senoquant/tabs/quantification/features/marker/dialog.py +437 -0
  21. senoquant/tabs/quantification/features/marker/export.py +879 -0
  22. senoquant/tabs/quantification/features/marker/feature.py +119 -0
  23. senoquant/tabs/quantification/features/marker/morphology.py +285 -0
  24. senoquant/tabs/quantification/features/marker/rows.py +654 -0
  25. senoquant/tabs/quantification/features/marker/thresholding.py +46 -0
  26. senoquant/tabs/quantification/features/roi.py +346 -0
  27. senoquant/tabs/quantification/features/spots/__init__.py +5 -0
  28. senoquant/tabs/quantification/features/spots/config.py +62 -0
  29. senoquant/tabs/quantification/features/spots/dialog.py +477 -0
  30. senoquant/tabs/quantification/features/spots/export.py +1292 -0
  31. senoquant/tabs/quantification/features/spots/feature.py +112 -0
  32. senoquant/tabs/quantification/features/spots/morphology.py +279 -0
  33. senoquant/tabs/quantification/features/spots/rows.py +241 -0
  34. senoquant/tabs/quantification/frontend.py +815 -0
  35. senoquant/tabs/segmentation/__init__.py +1 -0
  36. senoquant/tabs/segmentation/backend.py +131 -0
  37. senoquant/tabs/segmentation/frontend.py +1009 -0
  38. senoquant/tabs/segmentation/models/__init__.py +5 -0
  39. senoquant/tabs/segmentation/models/base.py +146 -0
  40. senoquant/tabs/segmentation/models/cpsam/details.json +65 -0
  41. senoquant/tabs/segmentation/models/cpsam/model.py +150 -0
  42. senoquant/tabs/segmentation/models/default_2d/details.json +69 -0
  43. senoquant/tabs/segmentation/models/default_2d/model.py +664 -0
  44. senoquant/tabs/segmentation/models/default_3d/details.json +69 -0
  45. senoquant/tabs/segmentation/models/default_3d/model.py +682 -0
  46. senoquant/tabs/segmentation/models/hf.py +71 -0
  47. senoquant/tabs/segmentation/models/nuclear_dilation/__init__.py +1 -0
  48. senoquant/tabs/segmentation/models/nuclear_dilation/details.json +26 -0
  49. senoquant/tabs/segmentation/models/nuclear_dilation/model.py +96 -0
  50. senoquant/tabs/segmentation/models/perinuclear_rings/__init__.py +1 -0
  51. senoquant/tabs/segmentation/models/perinuclear_rings/details.json +34 -0
  52. senoquant/tabs/segmentation/models/perinuclear_rings/model.py +132 -0
  53. senoquant/tabs/segmentation/stardist_onnx_utils/__init__.py +2 -0
  54. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/__init__.py +3 -0
  55. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/__init__.py +6 -0
  56. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/generate.py +470 -0
  57. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/prepare.py +273 -0
  58. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/rawdata.py +112 -0
  59. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/data/transform.py +384 -0
  60. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/__init__.py +0 -0
  61. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/blocks.py +184 -0
  62. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/losses.py +79 -0
  63. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/nets.py +165 -0
  64. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/predict.py +467 -0
  65. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/probability.py +67 -0
  66. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/internals/train.py +148 -0
  67. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/io/__init__.py +163 -0
  68. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/__init__.py +52 -0
  69. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/base_model.py +329 -0
  70. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_isotropic.py +160 -0
  71. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_projection.py +178 -0
  72. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_standard.py +446 -0
  73. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/care_upsampling.py +54 -0
  74. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/config.py +254 -0
  75. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/models/pretrained.py +119 -0
  76. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/scripts/__init__.py +0 -0
  77. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/scripts/care_predict.py +180 -0
  78. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/__init__.py +5 -0
  79. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/plot_utils.py +159 -0
  80. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/six.py +18 -0
  81. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/tf.py +644 -0
  82. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/utils/utils.py +272 -0
  83. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/csbdeep/version.py +1 -0
  84. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/docs/source/conf.py +368 -0
  85. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/setup.py +68 -0
  86. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_datagen.py +169 -0
  87. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_models.py +462 -0
  88. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tests/test_utils.py +166 -0
  89. senoquant/tabs/segmentation/stardist_onnx_utils/_csbdeep/tools/create_zip_contents.py +34 -0
  90. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/__init__.py +30 -0
  91. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/big.py +624 -0
  92. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/bioimageio_utils.py +494 -0
  93. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/data/__init__.py +39 -0
  94. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/__init__.py +10 -0
  95. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/geom2d.py +215 -0
  96. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/geometry/geom3d.py +349 -0
  97. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/matching.py +483 -0
  98. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/__init__.py +28 -0
  99. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/base.py +1217 -0
  100. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/model2d.py +594 -0
  101. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/models/model3d.py +696 -0
  102. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/nms.py +384 -0
  103. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/__init__.py +2 -0
  104. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/plot.py +74 -0
  105. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/plot/render.py +298 -0
  106. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/rays3d.py +373 -0
  107. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/sample_patches.py +65 -0
  108. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/__init__.py +0 -0
  109. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/predict2d.py +90 -0
  110. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/scripts/predict3d.py +93 -0
  111. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/utils.py +408 -0
  112. senoquant/tabs/segmentation/stardist_onnx_utils/_stardist/version.py +1 -0
  113. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/__init__.py +45 -0
  114. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/__init__.py +17 -0
  115. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/cli.py +55 -0
  116. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/convert/core.py +285 -0
  117. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/__init__.py +15 -0
  118. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/cli.py +36 -0
  119. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/divisibility.py +193 -0
  120. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/probe.py +100 -0
  121. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/receptive_field.py +182 -0
  122. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/rf_cli.py +48 -0
  123. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/inspect/valid_sizes.py +278 -0
  124. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/post/__init__.py +8 -0
  125. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/post/core.py +157 -0
  126. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/pre/__init__.py +17 -0
  127. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/pre/core.py +226 -0
  128. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/predict/__init__.py +5 -0
  129. senoquant/tabs/segmentation/stardist_onnx_utils/onnx_framework/predict/core.py +401 -0
  130. senoquant/tabs/settings/__init__.py +1 -0
  131. senoquant/tabs/settings/backend.py +29 -0
  132. senoquant/tabs/settings/frontend.py +19 -0
  133. senoquant/tabs/spots/__init__.py +1 -0
  134. senoquant/tabs/spots/backend.py +139 -0
  135. senoquant/tabs/spots/frontend.py +800 -0
  136. senoquant/tabs/spots/models/__init__.py +5 -0
  137. senoquant/tabs/spots/models/base.py +94 -0
  138. senoquant/tabs/spots/models/rmp/details.json +61 -0
  139. senoquant/tabs/spots/models/rmp/model.py +499 -0
  140. senoquant/tabs/spots/models/udwt/details.json +103 -0
  141. senoquant/tabs/spots/models/udwt/model.py +482 -0
  142. senoquant/utils.py +25 -0
  143. senoquant-1.0.0b1.dist-info/METADATA +193 -0
  144. senoquant-1.0.0b1.dist-info/RECORD +148 -0
  145. senoquant-1.0.0b1.dist-info/WHEEL +5 -0
  146. senoquant-1.0.0b1.dist-info/entry_points.txt +2 -0
  147. senoquant-1.0.0b1.dist-info/licenses/LICENSE +28 -0
  148. 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
+