Commit f13a4b79d6e6cc3f3ac885ccd20527fde9106719
Committed by
GitHub
1 parent
339908de
Exists in
master
and in
13 other branches
Casmoothing implemented in cython (#61)
Implements casmoothing in Cython. - Implements in cython - Better handling of border vertices
Showing
6 changed files
with
516 additions
and
9 deletions
Show diff stats
.gitignore
... | ... | @@ -0,0 +1,466 @@ |
1 | +# distutils: language = c++ | |
2 | +#cython: boundscheck=False | |
3 | +#cython: wraparound=False | |
4 | +#cython: initializedcheck=False | |
5 | +#cython: cdivision=True | |
6 | +#cython: nonecheck=False | |
7 | + | |
8 | +import sys | |
9 | +import time | |
10 | +cimport numpy as np | |
11 | + | |
12 | +from libc.math cimport sin, cos, acos, exp, sqrt, fabs, M_PI | |
13 | +from libc.stdlib cimport abs as cabs | |
14 | +from cython.operator cimport dereference as deref, preincrement as inc | |
15 | +from libcpp.map cimport map | |
16 | +from libcpp.unordered_map cimport unordered_map | |
17 | +from libcpp.set cimport set | |
18 | +from libcpp.vector cimport vector | |
19 | +from libcpp.pair cimport pair | |
20 | +from libcpp cimport bool | |
21 | +from libcpp.deque cimport deque as cdeque | |
22 | +from cython.parallel import prange | |
23 | + | |
24 | +from cy_my_types cimport vertex_t, normal_t, vertex_id_t | |
25 | + | |
26 | +import numpy as np | |
27 | +import vtk | |
28 | + | |
29 | +from vtk.util import numpy_support | |
30 | + | |
31 | +ctypedef float weight_t | |
32 | + | |
33 | +cdef struct Point: | |
34 | + vertex_t x | |
35 | + vertex_t y | |
36 | + vertex_t z | |
37 | + | |
38 | +ctypedef pair[vertex_id_t, vertex_id_t] key | |
39 | + | |
40 | + | |
41 | +cdef class Mesh: | |
42 | + cdef vertex_t[:, :] vertices | |
43 | + cdef vertex_id_t[:, :] faces | |
44 | + cdef normal_t[:, :] normals | |
45 | + | |
46 | + cdef unordered_map[int, vector[vertex_id_t]] map_vface | |
47 | + cdef unordered_map[vertex_id_t, int] border_vertices | |
48 | + | |
49 | + cdef bool _initialized | |
50 | + | |
51 | + def __cinit__(self, pd=None, other=None): | |
52 | + cdef int i | |
53 | + cdef map[key, int] edge_nfaces | |
54 | + cdef map[key, int].iterator it | |
55 | + if pd: | |
56 | + self._initialized = True | |
57 | + _vertices = numpy_support.vtk_to_numpy(pd.GetPoints().GetData()) | |
58 | + _vertices.shape = -1, 3 | |
59 | + | |
60 | + _faces = numpy_support.vtk_to_numpy(pd.GetPolys().GetData()) | |
61 | + _faces.shape = -1, 4 | |
62 | + | |
63 | + _normals = numpy_support.vtk_to_numpy(pd.GetCellData().GetArray("Normals")) | |
64 | + _normals.shape = -1, 3 | |
65 | + | |
66 | + self.vertices = _vertices | |
67 | + self.faces = _faces | |
68 | + self.normals = _normals | |
69 | + | |
70 | + for i in xrange(_faces.shape[0]): | |
71 | + self.map_vface[self.faces[i, 1]].push_back(i) | |
72 | + self.map_vface[self.faces[i, 2]].push_back(i) | |
73 | + self.map_vface[self.faces[i, 3]].push_back(i) | |
74 | + | |
75 | + edge_nfaces[key(min(self.faces[i, 1], self.faces[i, 2]), max(self.faces[i, 1], self.faces[i, 2]))] += 1 | |
76 | + edge_nfaces[key(min(self.faces[i, 2], self.faces[i, 3]), max(self.faces[i, 2], self.faces[i, 3]))] += 1 | |
77 | + edge_nfaces[key(min(self.faces[i, 1], self.faces[i, 3]), max(self.faces[i, 1], self.faces[i, 3]))] += 1 | |
78 | + | |
79 | + it = edge_nfaces.begin() | |
80 | + | |
81 | + while it != edge_nfaces.end(): | |
82 | + if deref(it).second == 1: | |
83 | + self.border_vertices[deref(it).first.first] = 1 | |
84 | + self.border_vertices[deref(it).first.second] = 1 | |
85 | + | |
86 | + inc(it) | |
87 | + | |
88 | + elif other: | |
89 | + _other = <Mesh>other | |
90 | + self._initialized = True | |
91 | + self.vertices = _other.vertices.copy() | |
92 | + self.faces = _other.faces.copy() | |
93 | + self.normals = _other.normals.copy() | |
94 | + self.map_vface = unordered_map[int, vector[vertex_id_t]](_other.map_vface) | |
95 | + self.border_vertices = unordered_map[vertex_id_t, int](_other.border_vertices) | |
96 | + else: | |
97 | + self._initialized = False | |
98 | + | |
99 | + cdef void copy_to(self, Mesh other): | |
100 | + """ | |
101 | + Copies self content to other. | |
102 | + """ | |
103 | + if self._initialized: | |
104 | + other.vertices[:] = self.vertices | |
105 | + other.faces[:] = self.faces | |
106 | + other.normals[:] = self.normals | |
107 | + other.map_vface = unordered_map[int, vector[vertex_id_t]](self.map_vface) | |
108 | + other.border_vertices = unordered_map[vertex_id_t, int](self.border_vertices) | |
109 | + else: | |
110 | + other.vertices = self.vertices.copy() | |
111 | + other.faces = self.faces.copy() | |
112 | + other.normals = self.normals.copy() | |
113 | + | |
114 | + other.map_vface = self.map_vface | |
115 | + other.border_vertices = self.border_vertices | |
116 | + | |
117 | + def to_vtk(self): | |
118 | + """ | |
119 | + Converts Mesh to vtkPolyData. | |
120 | + """ | |
121 | + vertices = np.asarray(self.vertices) | |
122 | + faces = np.asarray(self.faces) | |
123 | + normals = np.asarray(self.normals) | |
124 | + | |
125 | + points = vtk.vtkPoints() | |
126 | + points.SetData(numpy_support.numpy_to_vtk(vertices)) | |
127 | + | |
128 | + id_triangles = numpy_support.numpy_to_vtkIdTypeArray(faces) | |
129 | + triangles = vtk.vtkCellArray() | |
130 | + triangles.SetCells(faces.shape[0], id_triangles) | |
131 | + | |
132 | + pd = vtk.vtkPolyData() | |
133 | + pd.SetPoints(points) | |
134 | + pd.SetPolys(triangles) | |
135 | + | |
136 | + return pd | |
137 | + | |
138 | + cdef vector[vertex_id_t]* get_faces_by_vertex(self, int v_id) nogil: | |
139 | + """ | |
140 | + Returns the faces whose vertex `v_id' is part. | |
141 | + """ | |
142 | + return &self.map_vface[v_id] | |
143 | + | |
144 | + cdef set[vertex_id_t]* get_ring1(self, vertex_id_t v_id) nogil: | |
145 | + """ | |
146 | + Returns the ring1 of vertex `v_id' | |
147 | + """ | |
148 | + cdef vertex_id_t f_id | |
149 | + cdef set[vertex_id_t]* ring1 = new set[vertex_id_t]() | |
150 | + cdef vector[vertex_id_t].iterator it = self.map_vface[v_id].begin() | |
151 | + | |
152 | + while it != self.map_vface[v_id].end(): | |
153 | + f_id = deref(it) | |
154 | + inc(it) | |
155 | + if self.faces[f_id, 1] != v_id: | |
156 | + ring1.insert(self.faces[f_id, 1]) | |
157 | + if self.faces[f_id, 2] != v_id: | |
158 | + ring1.insert(self.faces[f_id, 2]) | |
159 | + if self.faces[f_id, 3] != v_id: | |
160 | + ring1.insert(self.faces[f_id, 3]) | |
161 | + | |
162 | + return ring1 | |
163 | + | |
164 | + cdef bool is_border(self, vertex_id_t v_id) nogil: | |
165 | + """ | |
166 | + Check if vertex `v_id' is a vertex border. | |
167 | + """ | |
168 | + return self.border_vertices.find(v_id) != self.border_vertices.end() | |
169 | + | |
170 | + cdef vector[vertex_id_t]* get_near_vertices_to_v(self, vertex_id_t v_id, float dmax) nogil: | |
171 | + """ | |
172 | + Returns all vertices with distance at most `d' to the vertex `v_id' | |
173 | + | |
174 | + Params: | |
175 | + v_id: id of the vertex | |
176 | + dmax: the maximun distance. | |
177 | + """ | |
178 | + cdef vector[vertex_id_t]* idfaces | |
179 | + cdef vector[vertex_id_t]* near_vertices = new vector[vertex_id_t]() | |
180 | + | |
181 | + cdef cdeque[vertex_id_t] to_visit | |
182 | + cdef unordered_map[vertex_id_t, bool] status_v | |
183 | + cdef unordered_map[vertex_id_t, bool] status_f | |
184 | + | |
185 | + cdef vertex_t *vip | |
186 | + cdef vertex_t *vjp | |
187 | + | |
188 | + cdef float distance | |
189 | + cdef int nf, nid, j | |
190 | + cdef vertex_id_t f_id, vj | |
191 | + | |
192 | + vip = &self.vertices[v_id, 0] | |
193 | + to_visit.push_back(v_id) | |
194 | + while(not to_visit.empty()): | |
195 | + v_id = to_visit.front() | |
196 | + to_visit.pop_front() | |
197 | + | |
198 | + status_v[v_id] = True | |
199 | + | |
200 | + idfaces = self.get_faces_by_vertex(v_id) | |
201 | + nf = idfaces.size() | |
202 | + | |
203 | + for nid in xrange(nf): | |
204 | + f_id = deref(idfaces)[nid] | |
205 | + if status_f.find(f_id) == status_f.end(): | |
206 | + status_f[f_id] = True | |
207 | + | |
208 | + for j in xrange(3): | |
209 | + vj = self.faces[f_id, j+1] | |
210 | + if status_v.find(vj) == status_v.end(): | |
211 | + status_v[vj] = True | |
212 | + vjp = &self.vertices[vj, 0] | |
213 | + distance = sqrt((vip[0] - vjp[0]) * (vip[0] - vjp[0]) \ | |
214 | + + (vip[1] - vjp[1]) * (vip[1] - vjp[1]) \ | |
215 | + + (vip[2] - vjp[2]) * (vip[2] - vjp[2])) | |
216 | + if distance <= dmax: | |
217 | + near_vertices.push_back(vj) | |
218 | + to_visit.push_back(vj) | |
219 | + | |
220 | + return near_vertices | |
221 | + | |
222 | + | |
223 | +cdef vector[weight_t]* calc_artifacts_weight(Mesh mesh, vector[vertex_id_t]& vertices_staircase, float tmax, float bmin) nogil: | |
224 | + """ | |
225 | + Calculate the artifact weight based on distance of each vertex to its | |
226 | + nearest staircase artifact vertex. | |
227 | + | |
228 | + Params: | |
229 | + mesh: Mesh | |
230 | + vertices_staircase: the identified staircase artifact vertices | |
231 | + tmax: max distance the vertex must be to its nearest artifact vertex | |
232 | + to considered to calculate the weight | |
233 | + bmin: The minimun weight. | |
234 | + """ | |
235 | + cdef int vi_id, vj_id, nnv, n_ids, i, j | |
236 | + cdef vector[vertex_id_t]* near_vertices | |
237 | + cdef weight_t value | |
238 | + cdef float d | |
239 | + n_ids = vertices_staircase.size() | |
240 | + | |
241 | + cdef vertex_t* vi | |
242 | + cdef vertex_t* vj | |
243 | + cdef size_t msize | |
244 | + | |
245 | + msize = mesh.vertices.shape[0] | |
246 | + cdef vector[weight_t]* weights = new vector[weight_t](msize) | |
247 | + weights.assign(msize, bmin) | |
248 | + | |
249 | + for i in prange(n_ids, nogil=True): | |
250 | + vi_id = vertices_staircase[i] | |
251 | + deref(weights)[vi_id] = 1.0 | |
252 | + | |
253 | + vi = &mesh.vertices[vi_id, 0] | |
254 | + near_vertices = mesh.get_near_vertices_to_v(vi_id, tmax) | |
255 | + nnv = near_vertices.size() | |
256 | + | |
257 | + for j in xrange(nnv): | |
258 | + vj_id = deref(near_vertices)[j] | |
259 | + vj = &mesh.vertices[vj_id, 0] | |
260 | + | |
261 | + d = sqrt((vi[0] - vj[0]) * (vi[0] - vj[0])\ | |
262 | + + (vi[1] - vj[1]) * (vi[1] - vj[1])\ | |
263 | + + (vi[2] - vj[2]) * (vi[2] - vj[2])) | |
264 | + value = (1.0 - d/tmax) * (1.0 - bmin) + bmin | |
265 | + | |
266 | + if value > deref(weights)[vj_id]: | |
267 | + deref(weights)[vj_id] = value | |
268 | + | |
269 | + del near_vertices | |
270 | + | |
271 | + # for i in xrange(msize): | |
272 | + # if mesh.is_border(i): | |
273 | + # deref(weights)[i] = 0.0 | |
274 | + | |
275 | + # cdef vertex_id_t v0, v1, v2 | |
276 | + # for i in xrange(mesh.faces.shape[0]): | |
277 | + # for j in xrange(1, 4): | |
278 | + # v0 = mesh.faces[i, j] | |
279 | + # vi = &mesh.vertices[v0, 0] | |
280 | + # if mesh.is_border(v0): | |
281 | + # deref(weights)[v0] = 0.0 | |
282 | + # v1 = mesh.faces[i, (j + 1) % 3 + 1] | |
283 | + # if mesh.is_border(v1): | |
284 | + # vi = &mesh.vertices[v1, 0] | |
285 | + # deref(weights)[v0] = 0.0 | |
286 | + | |
287 | + return weights | |
288 | + | |
289 | + | |
290 | +cdef inline Point calc_d(Mesh mesh, vertex_id_t v_id) nogil: | |
291 | + cdef Point D | |
292 | + cdef int nf, f_id, nid | |
293 | + cdef float n=0 | |
294 | + cdef int i | |
295 | + cdef vertex_t* vi | |
296 | + cdef vertex_t* vj | |
297 | + cdef set[vertex_id_t]* vertices | |
298 | + cdef set[vertex_id_t].iterator it | |
299 | + cdef vertex_id_t vj_id | |
300 | + | |
301 | + D.x = 0.0 | |
302 | + D.y = 0.0 | |
303 | + D.z = 0.0 | |
304 | + | |
305 | + vertices = mesh.get_ring1(v_id) | |
306 | + vi = &mesh.vertices[v_id, 0] | |
307 | + | |
308 | + if mesh.is_border(v_id): | |
309 | + it = vertices.begin() | |
310 | + while it != vertices.end(): | |
311 | + vj_id = deref(it) | |
312 | + if mesh.is_border(vj_id): | |
313 | + vj = &mesh.vertices[vj_id, 0] | |
314 | + | |
315 | + D.x = D.x + (vi[0] - vj[0]) | |
316 | + D.y = D.y + (vi[1] - vj[1]) | |
317 | + D.z = D.z + (vi[2] - vj[2]) | |
318 | + n += 1.0 | |
319 | + | |
320 | + inc(it) | |
321 | + else: | |
322 | + it = vertices.begin() | |
323 | + while it != vertices.end(): | |
324 | + vj_id = deref(it) | |
325 | + vj = &mesh.vertices[vj_id, 0] | |
326 | + | |
327 | + D.x = D.x + (vi[0] - vj[0]) | |
328 | + D.y = D.y + (vi[1] - vj[1]) | |
329 | + D.z = D.z + (vi[2] - vj[2]) | |
330 | + n += 1.0 | |
331 | + | |
332 | + inc(it) | |
333 | + | |
334 | + del vertices | |
335 | + | |
336 | + D.x = D.x / n | |
337 | + D.y = D.y / n | |
338 | + D.z = D.z / n | |
339 | + return D | |
340 | + | |
341 | +cdef vector[vertex_id_t]* find_staircase_artifacts(Mesh mesh, double[3] stack_orientation, double T) nogil: | |
342 | + """ | |
343 | + This function is used to find vertices at staircase artifacts, which are | |
344 | + those vertices whose incident faces' orientation differences are | |
345 | + greater than T. | |
346 | + | |
347 | + Params: | |
348 | + mesh: Mesh | |
349 | + stack_orientation: orientation of slice stacking | |
350 | + T: Min angle (between vertex faces and stack_orientation) to consider a | |
351 | + vertex a staircase artifact. | |
352 | + """ | |
353 | + cdef int nv, nf, f_id, v_id | |
354 | + cdef double of_z, of_y, of_x, min_z, max_z, min_y, max_y, min_x, max_x; | |
355 | + cdef vector[vertex_id_t]* f_ids | |
356 | + cdef normal_t* normal | |
357 | + | |
358 | + cdef vector[vertex_id_t]* output = new vector[vertex_id_t]() | |
359 | + cdef int i | |
360 | + | |
361 | + nv = mesh.vertices.shape[0] | |
362 | + | |
363 | + for v_id in xrange(nv): | |
364 | + max_z = -10000 | |
365 | + min_z = 10000 | |
366 | + max_y = -10000 | |
367 | + min_y = 10000 | |
368 | + max_x = -10000 | |
369 | + min_x = 10000 | |
370 | + | |
371 | + f_ids = mesh.get_faces_by_vertex(v_id) | |
372 | + nf = deref(f_ids).size() | |
373 | + | |
374 | + for i in xrange(nf): | |
375 | + f_id = deref(f_ids)[i] | |
376 | + normal = &mesh.normals[f_id, 0] | |
377 | + | |
378 | + of_z = 1 - fabs(normal[0]*stack_orientation[0] + normal[1]*stack_orientation[1] + normal[2]*stack_orientation[2]); | |
379 | + of_y = 1 - fabs(normal[0]*0 + normal[1]*1 + normal[2]*0); | |
380 | + of_x = 1 - fabs(normal[0]*1 + normal[1]*0 + normal[2]*0); | |
381 | + | |
382 | + if (of_z > max_z): | |
383 | + max_z = of_z | |
384 | + | |
385 | + if (of_z < min_z): | |
386 | + min_z = of_z | |
387 | + | |
388 | + if (of_y > max_y): | |
389 | + max_y = of_y | |
390 | + | |
391 | + if (of_y < min_y): | |
392 | + min_y = of_y | |
393 | + | |
394 | + if (of_x > max_x): | |
395 | + max_x = of_x | |
396 | + | |
397 | + if (of_x < min_x): | |
398 | + min_x = of_x | |
399 | + | |
400 | + | |
401 | + if ((fabs(max_z - min_z) >= T) or (fabs(max_y - min_y) >= T) or (fabs(max_x - min_x) >= T)): | |
402 | + output.push_back(v_id) | |
403 | + break | |
404 | + return output | |
405 | + | |
406 | + | |
407 | +cdef void taubin_smooth(Mesh mesh, vector[weight_t]& weights, float l, float m, int steps): | |
408 | + """ | |
409 | + Implementation of Taubin's smooth algorithm described in the paper "A | |
410 | + Signal Processing Approach To Fair Surface Design". His benefeat is it | |
411 | + avoids surface shrinking. | |
412 | + """ | |
413 | + cdef int s, i, nvertices | |
414 | + nvertices = mesh.vertices.shape[0] | |
415 | + cdef vector[Point] D = vector[Point](nvertices) | |
416 | + cdef vertex_t* vi | |
417 | + for s in xrange(steps): | |
418 | + for i in prange(nvertices, nogil=True): | |
419 | + D[i] = calc_d(mesh, i) | |
420 | + | |
421 | + for i in prange(nvertices, nogil=True): | |
422 | + mesh.vertices[i, 0] += weights[i]*l*D[i].x; | |
423 | + mesh.vertices[i, 1] += weights[i]*l*D[i].y; | |
424 | + mesh.vertices[i, 2] += weights[i]*l*D[i].z; | |
425 | + | |
426 | + for i in prange(nvertices, nogil=True): | |
427 | + D[i] = calc_d(mesh, i) | |
428 | + | |
429 | + for i in prange(nvertices, nogil=True): | |
430 | + mesh.vertices[i, 0] += weights[i]*m*D[i].x; | |
431 | + mesh.vertices[i, 1] += weights[i]*m*D[i].y; | |
432 | + mesh.vertices[i, 2] += weights[i]*m*D[i].z; | |
433 | + | |
434 | + | |
435 | +def ca_smoothing(Mesh mesh, double T, double tmax, double bmin, int n_iters): | |
436 | + """ | |
437 | + This is a implementation of the paper "Context-aware mesh smoothing for | |
438 | + biomedical applications". It can be used to smooth meshes generated by | |
439 | + binary images to remove its staircase artifacts and keep the fine features. | |
440 | + | |
441 | + Params: | |
442 | + mesh: Mesh | |
443 | + T: Min angle (between vertex faces and stack_orientation) to consider a | |
444 | + vertex a staircase artifact | |
445 | + tmax: max distance the vertex must be to its nearest artifact vertex | |
446 | + to considered to calculate the weight | |
447 | + bmin: The minimun weight | |
448 | + n_iters: Number of iterations. | |
449 | + """ | |
450 | + cdef double[3] stack_orientation = [0.0, 0.0, 1.0] | |
451 | + | |
452 | + t0 = time.time() | |
453 | + cdef vector[vertex_id_t]* vertices_staircase = find_staircase_artifacts(mesh, stack_orientation, T) | |
454 | + print "vertices staircase", time.time() - t0 | |
455 | + | |
456 | + t0 = time.time() | |
457 | + cdef vector[weight_t]* weights = calc_artifacts_weight(mesh, deref(vertices_staircase), tmax, bmin) | |
458 | + print "Weights", time.time() - t0 | |
459 | + | |
460 | + del vertices_staircase | |
461 | + | |
462 | + t0 = time.time() | |
463 | + taubin_smooth(mesh, deref(weights), 0.5, -0.53, n_iters) | |
464 | + print "taubin", time.time() - t0 | |
465 | + | |
466 | + del weights | ... | ... |
invesalius/data/cy_my_types.pxd
invesalius/data/surface.py
... | ... | @@ -42,6 +42,7 @@ try: |
42 | 42 | except ImportError: |
43 | 43 | import data.ca_smoothing as ca_smoothing |
44 | 44 | |
45 | +import cy_mesh | |
45 | 46 | # TODO: Verificar ReleaseDataFlagOn and SetSource |
46 | 47 | |
47 | 48 | class Surface(): |
... | ... | @@ -564,16 +565,28 @@ class SurfaceManager(): |
564 | 565 | # polydata.SetSource(None) |
565 | 566 | del clean |
566 | 567 | |
567 | - try: | |
568 | - polydata.BuildLinks() | |
569 | - except TypeError: | |
570 | - polydata.BuildLinks(0) | |
571 | - polydata = ca_smoothing.ca_smoothing(polydata, options['angle'], | |
572 | - options['max distance'], | |
573 | - options['min weight'], | |
574 | - options['steps']) | |
568 | + # try: | |
569 | + # polydata.BuildLinks() | |
570 | + # except TypeError: | |
571 | + # polydata.BuildLinks(0) | |
572 | + # polydata = ca_smoothing.ca_smoothing(polydata, options['angle'], | |
573 | + # options['max distance'], | |
574 | + # options['min weight'], | |
575 | + # options['steps']) | |
576 | + | |
577 | + mesh = cy_mesh.Mesh(polydata) | |
578 | + cy_mesh.ca_smoothing(mesh, options['angle'], | |
579 | + options['max distance'], | |
580 | + options['min weight'], | |
581 | + options['steps']) | |
582 | + # polydata = mesh.to_vtk() | |
583 | + | |
575 | 584 | # polydata.SetSource(None) |
576 | 585 | # polydata.DebugOn() |
586 | + w = vtk.vtkPLYWriter() | |
587 | + w.SetInputData(polydata) | |
588 | + w.SetFileName('/tmp/ca_smoothing_inv.ply') | |
589 | + w.Write() | |
577 | 590 | |
578 | 591 | else: |
579 | 592 | #smoother = vtk.vtkWindowedSincPolyDataFilter() | ... | ... |
invesalius/gui/dialogs.py
... | ... | @@ -1276,7 +1276,7 @@ class CAOptions(wx.Panel): |
1276 | 1276 | max_val=100.0, increment=0.1, |
1277 | 1277 | digits=2) |
1278 | 1278 | |
1279 | - self.min_weight = floatspin.FloatSpin(self, -1, value=0.2, min_val=0.0, | |
1279 | + self.min_weight = floatspin.FloatSpin(self, -1, value=0.5, min_val=0.0, | |
1280 | 1280 | max_val=1.0, increment=0.1, |
1281 | 1281 | digits=1) |
1282 | 1282 | ... | ... |
setup.py
... | ... | @@ -29,6 +29,12 @@ if sys.platform == 'linux2': |
29 | 29 | Extension("invesalius.data.floodfill", ["invesalius/data/floodfill.pyx"], |
30 | 30 | include_dirs=[numpy.get_include()], |
31 | 31 | language='c++',), |
32 | + | |
33 | + Extension("invesalius.data.cy_mesh", ["invesalius/data/cy_mesh.pyx"], | |
34 | + include_dirs=[numpy.get_include()], | |
35 | + extra_compile_args=['-fopenmp', '-std=c++11'], | |
36 | + extra_link_args=['-fopenmp', '-std=c++11'], | |
37 | + language='c++',), | |
32 | 38 | ]) |
33 | 39 | ) |
34 | 40 | |
... | ... | @@ -50,6 +56,11 @@ elif sys.platform == 'win32': |
50 | 56 | Extension("invesalius.data.floodfill", ["invesalius/data/floodfill.pyx"], |
51 | 57 | include_dirs=[numpy.get_include()], |
52 | 58 | language='c++',), |
59 | + | |
60 | + Extension("invesalius.data.cy_mesh", ["invesalius/data/cy_mesh.pyx"], | |
61 | + include_dirs=[numpy.get_include()], | |
62 | + extra_compile_args=['/openmp',], | |
63 | + language='c++',), | |
53 | 64 | ]) |
54 | 65 | ) |
55 | 66 | |
... | ... | @@ -75,5 +86,12 @@ else: |
75 | 86 | Extension("invesalius.data.floodfill", ["invesalius/data/floodfill.pyx"], |
76 | 87 | include_dirs=[numpy.get_include()], |
77 | 88 | language='c++',), |
89 | + | |
90 | + Extension("invesalius.data.cy_mesh", ["invesalius/data/cy_mesh.pyx"], | |
91 | + include_dirs=[numpy.get_include()], | |
92 | + extra_compile_args=['-fopenmp', '-std=c++11'], | |
93 | + extra_link_args=['-fopenmp', '-std=c++11'], | |
94 | + language='c++',), | |
95 | + | |
78 | 96 | ]) |
79 | 97 | ) | ... | ... |