5
5
import trimesh
6
6
from numba import jit
7
7
8
+
8
9
def sphere (shape : list , radius : float , position : list ):
9
10
"""
10
- Creates a binary sphere
11
+ Creates a binary sphere
11
12
"""
12
13
# assume shape and position are both a 3-tuple of int or float
13
14
# the units are pixels / voxels (px for short)
@@ -30,90 +31,22 @@ def sphere(shape: list, radius: float, position: list):
30
31
# the inner part of the sphere will have distance below 1
31
32
return arr <= 1.0
32
33
33
- def cartesian (arrays , out = None ):
34
- """
35
- Generate a cartesian product of input arrays
36
- """
37
- arrays = [np .asarray (x ) for x in arrays ]
38
- dtype = arrays [0 ].dtype
39
-
40
- n = np .prod ([x .size for x in arrays ])
41
- if out is None :
42
- out = np .zeros ([n , len (arrays )], dtype = dtype )
43
-
44
- m = int (n / arrays [0 ].size )
45
- out [:,0 ] = np .repeat (arrays [0 ], m )
46
- if arrays [1 :]:
47
- cartesian (arrays [1 :], out = out [0 :m , 1 :])
48
- for j in range (1 , arrays [0 ].size ):
49
- out [j * m :(j + 1 )* m , 1 :] = out [0 :m , 1 :]
50
- return out
51
-
52
- def find_enclosure (surface : trimesh .Trimesh , data_shape : tuple ):
53
- """
54
- Finds all voxels inside of a surface
55
-
56
- This function stores all the surface vertices in a k-d tree
57
- and uses it to quickly look up the closest vertex to each
58
- volume voxel in the image.
59
-
60
- Once the closest vertex is found, a vector is created between
61
- the voxel location and the vertex. The resulting vector is dot
62
- product with the corresponding vertex normal. Negative values
63
- indicate that the voxel lies exterior to the surface (since it
64
- is anti-parallel to the vertex normal), while positive values
65
- indicate that they are interior to the surface (parallel to
66
- the vertex normal).
67
- """
68
- # get vertex normals for each vertex on the surface
69
- normals = surface .vertex_normals
70
-
71
- # get KDTree over surface vertices
72
- searcher = surface .kdtree
73
-
74
- # get bounding box around surface
75
- max_loc = np .ceil (np .max (surface .vertices , axis = 0 )).astype (np .int64 )
76
- min_loc = np .floor (np .min (surface .vertices , axis = 0 )).astype (np .int64 )
77
-
78
- # build a list of locations representing the volume grid
79
- # within the bounding box
80
- locs = cartesian ([
81
- np .arange (min_loc [0 ], max_loc [0 ]),
82
- np .arange (min_loc [1 ], max_loc [1 ]),
83
- np .arange (min_loc [2 ], max_loc [2 ])])
84
-
85
- # find the nearest vertex to each voxel
86
- # searcher.query returns a list of vertices corresponding
87
- # to the closest vertex to the given voxel location
88
- _ , nearest_idx = searcher .query (locs , n_jobs = 6 )
89
- nearest_vertices = surface .vertices [nearest_idx ]
90
-
91
- # get the directional vector from each voxel location to it's nearest vertex
92
- direction_vectors = nearest_vertices - locs
93
-
94
- # find it's direction by taking the dot product with vertex normal
95
- # this is done row-by-row between directional vectors and the vertex normals
96
- dot_products = np .einsum ('ij,ij->i' , direction_vectors , normals [nearest_idx ])
97
-
98
- # get the interior (where dot product is > 0)
99
- interior = (dot_products > 0 ).reshape ((max_loc - min_loc ).astype (np .int64 ))
100
-
101
- # create mask
102
- mask = np .zeros (data_shape )
103
- mask [min_loc [0 ]:max_loc [0 ],min_loc [1 ]:max_loc [1 ],min_loc [2 ]:max_loc [2 ]] = interior
104
-
105
- # return the mask
106
- return mask
107
34
108
35
@jit (nopython = True , cache = True )
109
36
def closest_integer_point (vertex : np .ndarray ):
110
37
"""
111
- Gives the closest integer point based on euclidean distance
38
+ Gives the closest integer point based on euclidean distance
112
39
"""
113
40
# get neighboring grid points to search
114
- x = vertex [0 ]; y = vertex [1 ]; z = vertex [2 ]
115
- x0 = np .floor (x ); y0 = np .floor (y ); z0 = np .floor (z )
116
- x1 = x0 + 1 ; y1 = y0 + 1 ; z1 = z0 + 1
41
+ x = vertex [0 ]
42
+ y = vertex [1 ]
43
+ z = vertex [2 ]
44
+ x0 = np .floor (x )
45
+ y0 = np .floor (y )
46
+ z0 = np .floor (z )
47
+ x1 = x0 + 1
48
+ y1 = y0 + 1
49
+ z1 = z0 + 1
117
50
118
51
# initialize min euclidean distance
119
52
min_euclid = 99
@@ -132,12 +65,13 @@ def closest_integer_point(vertex: np.ndarray):
132
65
# return the final coords
133
66
return final_coords .astype (np .int64 )
134
67
68
+
135
69
@jit (nopython = True , cache = True )
136
70
def bresenham3d (v0 : np .ndarray , v1 : np .ndarray ):
137
71
"""
138
- Bresenham's algorithm for a 3-D line
72
+ Bresenham's algorithm for a 3-D line
139
73
140
- https://www.geeksforgeeks.org/bresenhams-algorithm-for-3-d-line-drawing/
74
+ https://www.geeksforgeeks.org/bresenhams-algorithm-for-3-d-line-drawing/
141
75
"""
142
76
# initialize axis differences
143
77
@@ -150,32 +84,50 @@ def bresenham3d(v0: np.ndarray, v1: np.ndarray):
150
84
151
85
# determine the driving axis
152
86
if dx >= dy and dx >= dz :
153
- d0 = dx ; d1 = dy ; d2 = dz
154
- s0 = xs ; s1 = ys ; s2 = zs
155
- a0 = 0 ; a1 = 1 ; a2 = 2
87
+ d0 = dx
88
+ d1 = dy
89
+ d2 = dz
90
+ s0 = xs
91
+ s1 = ys
92
+ s2 = zs
93
+ a0 = 0
94
+ a1 = 1
95
+ a2 = 2
156
96
elif dy >= dx and dy >= dz :
157
- d0 = dy ; d1 = dx ; d2 = dz
158
- s0 = ys ; s1 = xs ; s2 = zs
159
- a0 = 1 ; a1 = 0 ; a2 = 2
97
+ d0 = dy
98
+ d1 = dx
99
+ d2 = dz
100
+ s0 = ys
101
+ s1 = xs
102
+ s2 = zs
103
+ a0 = 1
104
+ a1 = 0
105
+ a2 = 2
160
106
elif dz >= dx and dz >= dy :
161
- d0 = dz ; d1 = dx ; d2 = dy
162
- s0 = zs ; s1 = xs ; s2 = ys
163
- a0 = 2 ; a1 = 0 ; a2 = 1
107
+ d0 = dz
108
+ d1 = dx
109
+ d2 = dy
110
+ s0 = zs
111
+ s1 = xs
112
+ s2 = ys
113
+ a0 = 2
114
+ a1 = 0
115
+ a2 = 1
164
116
165
117
# create line array
166
118
line = np .zeros ((d0 + 1 , 3 ), dtype = np .int64 )
167
119
line [0 ] = v0
168
120
169
121
# get points
170
- p1 = 2 * d1 - d0
171
- p2 = 2 * d2 - d0
122
+ p1 = 2 * d1 - d0
123
+ p2 = 2 * d2 - d0
172
124
for i in range (d0 ):
173
125
c = line [i ].copy ()
174
126
c [a0 ] += s0
175
- if ( p1 >= 0 ) :
127
+ if p1 >= 0 :
176
128
c [a1 ] += s1
177
129
p1 -= 2 * d0
178
- if ( p2 >= 0 ) :
130
+ if p2 >= 0 :
179
131
c [a2 ] += s2
180
132
p2 -= 2 * d0
181
133
p1 += 2 * d1
@@ -185,27 +137,30 @@ def bresenham3d(v0: np.ndarray, v1: np.ndarray):
185
137
# return list
186
138
return line
187
139
140
+
188
141
@jit (nopython = True , cache = True )
189
142
def l2norm (vec : np .ndarray ):
190
143
"""
191
- Computes the l2 norm for 3d vector
144
+ Computes the l2 norm for 3d vector
192
145
"""
193
- return np .sqrt (vec [0 ]** 2 + vec [1 ]** 2 + vec [2 ]** 2 )
146
+ return np .sqrt (vec [0 ] ** 2 + vec [1 ] ** 2 + vec [2 ] ** 2 )
147
+
194
148
195
149
@jit (nopython = True , cache = True )
196
150
def l2normarray (array : np .ndarray ):
197
151
"""
198
- Computes the l2 norm for several 3d vectors
152
+ Computes the l2 norm for several 3d vectors
199
153
"""
200
- return np .sqrt (array [:, 0 ]** 2 + array [:, 1 ]** 2 + array [:, 2 ]** 2 )
154
+ return np .sqrt (array [:, 0 ] ** 2 + array [:, 1 ] ** 2 + array [:, 2 ] ** 2 )
155
+
201
156
202
157
def diagonal_dot (a : np .ndarray , b : np .ndarray ):
203
158
"""
204
- Dot product by row of a and b.
205
- There are a lot of ways to do this though
206
- performance varies very widely. This method
207
- uses a dot product to sum the row and avoids
208
- function calls if at all possible.
159
+ Dot product by row of a and b.
160
+ There are a lot of ways to do this though
161
+ performance varies very widely. This method
162
+ uses a dot product to sum the row and avoids
163
+ function calls if at all possible.
209
164
"""
210
165
a = np .asanyarray (a )
211
- return np .dot (a * b , [1.0 ] * a .shape [1 ])
166
+ return np .dot (a * b , [1.0 ] * a .shape [1 ])
0 commit comments