Commit 0d8808847e10d4128b91b388a0ec58fbe550218a
1 parent
071dff96
Exists in
floodfill
Added floodfill code to InVesalius
Showing
2 changed files
with
194 additions
and
0 deletions
Show diff stats
... | ... | @@ -0,0 +1,183 @@ |
1 | +import numpy as np | |
2 | +cimport numpy as np | |
3 | +cimport cython | |
4 | + | |
5 | +from collections import deque | |
6 | + | |
7 | +from libc.math cimport floor, ceil | |
8 | + | |
9 | +DTYPE = np.uint8 | |
10 | +ctypedef np.uint8_t DTYPE_t | |
11 | + | |
12 | +DTYPE16 = np.int16 | |
13 | +ctypedef np.int16_t DTYPE16_t | |
14 | + | |
15 | +@cython.boundscheck(False) # turn of bounds-checking for entire function | |
16 | +def floodfill(np.ndarray[DTYPE_t, ndim=3] data, int i, int j, int k, int v, int fill, np.ndarray[DTYPE_t, ndim=3] out): | |
17 | + | |
18 | + cdef int to_return = 0 | |
19 | + if out is None: | |
20 | + out = np.zeros_like(data) | |
21 | + to_return = 1 | |
22 | + | |
23 | + cdef int x, y, z | |
24 | + cdef int w, h, d | |
25 | + | |
26 | + d = data.shape[0] | |
27 | + h = data.shape[1] | |
28 | + w = data.shape[2] | |
29 | + | |
30 | + stack = [(i, j, k), ] | |
31 | + out[k, j, i] = fill | |
32 | + | |
33 | + while stack: | |
34 | + x, y, z = stack.pop() | |
35 | + | |
36 | + if z + 1 < d and data[z + 1, y, x] == v and out[z + 1, y, x] != fill: | |
37 | + out[z + 1, y, x] = fill | |
38 | + stack.append((x, y, z + 1)) | |
39 | + | |
40 | + if z - 1 >= 0 and data[z - 1, y, x] == v and out[z - 1, y, x] != fill: | |
41 | + out[z - 1, y, x] = fill | |
42 | + stack.append((x, y, z - 1)) | |
43 | + | |
44 | + if y + 1 < h and data[z, y + 1, x] == v and out[z, y + 1, x] != fill: | |
45 | + out[z, y + 1, x] = fill | |
46 | + stack.append((x, y + 1, z)) | |
47 | + | |
48 | + if y - 1 >= 0 and data[z, y - 1, x] == v and out[z, y - 1, x] != fill: | |
49 | + out[z, y - 1, x] = fill | |
50 | + stack.append((x, y - 1, z)) | |
51 | + | |
52 | + if x + 1 < w and data[z, y, x + 1] == v and out[z, y, x + 1] != fill: | |
53 | + out[z, y, x + 1] = fill | |
54 | + stack.append((x + 1, y, z)) | |
55 | + | |
56 | + if x - 1 >= 0 and data[z, y, x - 1] == v and out[z, y, x - 1] != fill: | |
57 | + out[z, y, x - 1] = fill | |
58 | + stack.append((x - 1, y, z)) | |
59 | + | |
60 | + if to_return: | |
61 | + return out | |
62 | + | |
63 | + | |
64 | +@cython.boundscheck(False) # turn of bounds-checking for entire function | |
65 | +@cython.wraparound(False) | |
66 | +@cython.nonecheck(False) | |
67 | +def floodfill_threshold(np.ndarray[DTYPE16_t, ndim=3] data, int i, int j, int k, int t0, int t1, int fill, np.ndarray[DTYPE_t, ndim=3] out): | |
68 | + | |
69 | + cdef int to_return = 0 | |
70 | + if out is None: | |
71 | + out = np.zeros_like(data) | |
72 | + to_return = 1 | |
73 | + | |
74 | + cdef int x, y, z | |
75 | + cdef int w, h, d | |
76 | + cdef int xo, yo, zo | |
77 | + | |
78 | + d = data.shape[0] | |
79 | + h = data.shape[1] | |
80 | + w = data.shape[2] | |
81 | + | |
82 | + stack = deque() | |
83 | + if data[k, j, i] >= t0 and data[k, j, i] <= t1: | |
84 | + stack.append((i, j, k)) | |
85 | + out[k, j, i] = fill | |
86 | + | |
87 | + while stack: | |
88 | + x, y, z = stack.pop() | |
89 | + | |
90 | + xo = x + 1 | |
91 | + yo = y + 1 | |
92 | + zo = z + 1 | |
93 | + | |
94 | + if z + 1 < d and data[z + 1, y, x] >= t0 and data[z + 1, y, x] <= t1 and out[zo + 1, yo, xo] != fill: | |
95 | + out[zo + 1, yo, xo] = fill | |
96 | + stack.append((x, y, z + 1)) | |
97 | + | |
98 | + if z - 1 >= 0 and data[z - 1, y, x] >= t0 and data[z - 1, y, x] <= t1 and out[zo - 1, yo, xo] != fill: | |
99 | + out[zo - 1, yo, xo] = fill | |
100 | + stack.append((x, y, z - 1)) | |
101 | + | |
102 | + if y + 1 < h and data[z, y + 1, x] >= t0 and data[z, y + 1, x] <= t1 and out[zo, yo + 1, xo] != fill: | |
103 | + out[zo, yo + 1, xo] = fill | |
104 | + stack.append((x, y + 1, z)) | |
105 | + | |
106 | + if y - 1 >= 0 and data[z, y - 1, x] >= t0 and data[z, y - 1, x] <= t1 and out[zo, yo - 1, xo] != fill: | |
107 | + out[zo, yo - 1, xo] = fill | |
108 | + stack.append((x, y - 1, z)) | |
109 | + | |
110 | + if x + 1 < w and data[z, y, x + 1] >= t0 and data[z, y, x + 1] <= t1 and out[zo, yo, xo + 1] != fill: | |
111 | + out[zo, yo, xo + 1] = fill | |
112 | + stack.append((x + 1, y, z)) | |
113 | + | |
114 | + if x - 1 >= 0 and data[z, y, x - 1] >= t0 and data[z, y, x - 1] <= t1 and out[zo, yo, xo - 1] != fill: | |
115 | + out[zo, yo, xo - 1] = fill | |
116 | + stack.append((x - 1, y, z)) | |
117 | + | |
118 | + if to_return: | |
119 | + return out | |
120 | + | |
121 | + | |
122 | +@cython.boundscheck(False) # turn of bounds-checking for entire function | |
123 | +@cython.wraparound(False) | |
124 | +@cython.nonecheck(False) | |
125 | +def floodfill_auto_threshold(np.ndarray[DTYPE16_t, ndim=3] data, int i, int j, int k, float p, int fill, np.ndarray[DTYPE_t, ndim=3] out): | |
126 | + | |
127 | + cdef int to_return = 0 | |
128 | + if out is None: | |
129 | + out = np.zeros_like(data) | |
130 | + to_return = 1 | |
131 | + | |
132 | + cdef int x, y, z | |
133 | + cdef int w, h, d | |
134 | + cdef int xo, yo, zo | |
135 | + cdef int t0, t1 | |
136 | + | |
137 | + d = data.shape[0] | |
138 | + h = data.shape[1] | |
139 | + w = data.shape[2] | |
140 | + | |
141 | + stack = deque() | |
142 | + t0 = <int>floor(data[k, j, i] * (1 - p)) | |
143 | + t1 = <int>floor(data[k, j, i] * (1 + p)) | |
144 | + #if data[k, j, i] >= t0 and data[k, j, i] <= t1: | |
145 | + stack.append((i, j, k)) | |
146 | + out[k, j, i] = fill | |
147 | + | |
148 | + while stack: | |
149 | + x, y, z = stack.pop() | |
150 | + | |
151 | + xo = x + 1 | |
152 | + yo = y + 1 | |
153 | + zo = z + 1 | |
154 | + | |
155 | + t0 = <int>floor(data[z, y, x] * (1 - p)) | |
156 | + t1 = <int>floor(data[z, y, x] * (1 + p)) | |
157 | + | |
158 | + if z + 1 < d and data[z + 1, y, x] >= t0 and data[z + 1, y, x] <= t1 and out[zo + 1, yo, xo] != fill: | |
159 | + out[zo + 1, yo, xo] = fill | |
160 | + stack.append((x, y, z + 1)) | |
161 | + | |
162 | + if z - 1 >= 0 and data[z - 1, y, x] >= t0 and data[z - 1, y, x] <= t1 and out[zo - 1, yo, xo] != fill: | |
163 | + out[zo - 1, yo, xo] = fill | |
164 | + stack.append((x, y, z - 1)) | |
165 | + | |
166 | + if y + 1 < h and data[z, y + 1, x] >= t0 and data[z, y + 1, x] <= t1 and out[zo, yo + 1, xo] != fill: | |
167 | + out[zo, yo + 1, xo] = fill | |
168 | + stack.append((x, y + 1, z)) | |
169 | + | |
170 | + if y - 1 >= 0 and data[z, y - 1, x] >= t0 and data[z, y - 1, x] <= t1 and out[zo, yo - 1, xo] != fill: | |
171 | + out[zo, yo - 1, xo] = fill | |
172 | + stack.append((x, y - 1, z)) | |
173 | + | |
174 | + if x + 1 < w and data[z, y, x + 1] >= t0 and data[z, y, x + 1] <= t1 and out[zo, yo, xo + 1] != fill: | |
175 | + out[zo, yo, xo + 1] = fill | |
176 | + stack.append((x + 1, y, z)) | |
177 | + | |
178 | + if x - 1 >= 0 and data[z, y, x - 1] >= t0 and data[z, y, x - 1] <= t1 and out[zo, yo, xo - 1] != fill: | |
179 | + out[zo, yo, xo - 1] = fill | |
180 | + stack.append((x - 1, y, z)) | |
181 | + | |
182 | + if to_return: | |
183 | + return out | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +from distutils.core import setup | |
2 | +from distutils.extension import Extension | |
3 | +from Cython.Distutils import build_ext | |
4 | + | |
5 | +import numpy | |
6 | + | |
7 | +setup( | |
8 | + cmdclass = {'build_ext': build_ext}, | |
9 | + ext_modules = [Extension("invesalius/data/floodfill", ["invesalius/data/floodfill.pyx"], | |
10 | + include_dirs = [numpy.get_include()])] | |
11 | +) | ... | ... |