diff --git a/invesalius/data/floodfill.pyx b/invesalius/data/floodfill.pyx new file mode 100644 index 0000000..389c345 --- /dev/null +++ b/invesalius/data/floodfill.pyx @@ -0,0 +1,183 @@ +import numpy as np +cimport numpy as np +cimport cython + +from collections import deque + +from libc.math cimport floor, ceil + +DTYPE = np.uint8 +ctypedef np.uint8_t DTYPE_t + +DTYPE16 = np.int16 +ctypedef np.int16_t DTYPE16_t + +@cython.boundscheck(False) # turn of bounds-checking for entire function +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): + + cdef int to_return = 0 + if out is None: + out = np.zeros_like(data) + to_return = 1 + + cdef int x, y, z + cdef int w, h, d + + d = data.shape[0] + h = data.shape[1] + w = data.shape[2] + + stack = [(i, j, k), ] + out[k, j, i] = fill + + while stack: + x, y, z = stack.pop() + + if z + 1 < d and data[z + 1, y, x] == v and out[z + 1, y, x] != fill: + out[z + 1, y, x] = fill + stack.append((x, y, z + 1)) + + if z - 1 >= 0 and data[z - 1, y, x] == v and out[z - 1, y, x] != fill: + out[z - 1, y, x] = fill + stack.append((x, y, z - 1)) + + if y + 1 < h and data[z, y + 1, x] == v and out[z, y + 1, x] != fill: + out[z, y + 1, x] = fill + stack.append((x, y + 1, z)) + + if y - 1 >= 0 and data[z, y - 1, x] == v and out[z, y - 1, x] != fill: + out[z, y - 1, x] = fill + stack.append((x, y - 1, z)) + + if x + 1 < w and data[z, y, x + 1] == v and out[z, y, x + 1] != fill: + out[z, y, x + 1] = fill + stack.append((x + 1, y, z)) + + if x - 1 >= 0 and data[z, y, x - 1] == v and out[z, y, x - 1] != fill: + out[z, y, x - 1] = fill + stack.append((x - 1, y, z)) + + if to_return: + return out + + +@cython.boundscheck(False) # turn of bounds-checking for entire function +@cython.wraparound(False) +@cython.nonecheck(False) +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): + + cdef int to_return = 0 + if out is None: + out = np.zeros_like(data) + to_return = 1 + + cdef int x, y, z + cdef int w, h, d + cdef int xo, yo, zo + + d = data.shape[0] + h = data.shape[1] + w = data.shape[2] + + stack = deque() + if data[k, j, i] >= t0 and data[k, j, i] <= t1: + stack.append((i, j, k)) + out[k, j, i] = fill + + while stack: + x, y, z = stack.pop() + + xo = x + 1 + yo = y + 1 + zo = z + 1 + + 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: + out[zo + 1, yo, xo] = fill + stack.append((x, y, z + 1)) + + 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: + out[zo - 1, yo, xo] = fill + stack.append((x, y, z - 1)) + + 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: + out[zo, yo + 1, xo] = fill + stack.append((x, y + 1, z)) + + 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: + out[zo, yo - 1, xo] = fill + stack.append((x, y - 1, z)) + + 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: + out[zo, yo, xo + 1] = fill + stack.append((x + 1, y, z)) + + 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: + out[zo, yo, xo - 1] = fill + stack.append((x - 1, y, z)) + + if to_return: + return out + + +@cython.boundscheck(False) # turn of bounds-checking for entire function +@cython.wraparound(False) +@cython.nonecheck(False) +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): + + cdef int to_return = 0 + if out is None: + out = np.zeros_like(data) + to_return = 1 + + cdef int x, y, z + cdef int w, h, d + cdef int xo, yo, zo + cdef int t0, t1 + + d = data.shape[0] + h = data.shape[1] + w = data.shape[2] + + stack = deque() + t0 = floor(data[k, j, i] * (1 - p)) + t1 = floor(data[k, j, i] * (1 + p)) + #if data[k, j, i] >= t0 and data[k, j, i] <= t1: + stack.append((i, j, k)) + out[k, j, i] = fill + + while stack: + x, y, z = stack.pop() + + xo = x + 1 + yo = y + 1 + zo = z + 1 + + t0 = floor(data[z, y, x] * (1 - p)) + t1 = floor(data[z, y, x] * (1 + p)) + + 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: + out[zo + 1, yo, xo] = fill + stack.append((x, y, z + 1)) + + 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: + out[zo - 1, yo, xo] = fill + stack.append((x, y, z - 1)) + + 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: + out[zo, yo + 1, xo] = fill + stack.append((x, y + 1, z)) + + 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: + out[zo, yo - 1, xo] = fill + stack.append((x, y - 1, z)) + + 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: + out[zo, yo, xo + 1] = fill + stack.append((x + 1, y, z)) + + 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: + out[zo, yo, xo - 1] = fill + stack.append((x - 1, y, z)) + + if to_return: + return out diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..dd2c623 --- /dev/null +++ b/setup.py @@ -0,0 +1,11 @@ +from distutils.core import setup +from distutils.extension import Extension +from Cython.Distutils import build_ext + +import numpy + +setup( + cmdclass = {'build_ext': build_ext}, + ext_modules = [Extension("invesalius/data/floodfill", ["invesalius/data/floodfill.pyx"], + include_dirs = [numpy.get_include()])] +) -- libgit2 0.21.2