diff --git a/invesalius/data/floodfill.pyx b/invesalius/data/floodfill.pyx index 5b7ac6e..6636cc9 100644 --- a/invesalius/data/floodfill.pyx +++ b/invesalius/data/floodfill.pyx @@ -6,6 +6,7 @@ from collections import deque from cython.parallel import prange from libc.math cimport floor, ceil +from libcpp cimport bool from libcpp.deque cimport deque as cdeque from libcpp.vector cimport vector @@ -237,22 +238,37 @@ def floodfill_auto_threshold(np.ndarray[image_t, ndim=3] data, list seeds, float @cython.wraparound(False) @cython.nonecheck(False) def fill_holes_automatically(np.ndarray[mask_t, ndim=3] mask, np.ndarray[np.uint16_t, ndim=3] labels, unsigned int nlabels, unsigned int max_size): - cdef np.ndarray[np.uint32_t, ndim=1] sizes = np.zeros(shape=(nlabels + 1), dtype=np.uint32) - cdef int x, y, z - cdef int dx, dy, dz + """ + Fill mask holes automatically. The hole must <= max_size. Return True if any hole were filled. + """ + cdef np.ndarray[np.uint32_t, ndim=1] sizes = np.zeros(shape=(nlabels + 1), dtype=np.uint32) + cdef int x, y, z + cdef int dx, dy, dz + cdef int i + + cdef bool modified = False + + dz = mask.shape[0] + dy = mask.shape[1] + dx = mask.shape[2] + + for z in xrange(dz): + for y in xrange(dy): + for x in xrange(dx): + sizes[labels[z, y, x]] += 1 - dz = mask.shape[0] - dy = mask.shape[1] - dx = mask.shape[2] + #Checking if any hole will be filled + for i in xrange(nlabels + 1): + if sizes[i] <= max_size: + modified = True - for z in xrange(dz): - for y in xrange(dy): - for x in xrange(dx): - sizes[labels[z, y, x]] += 1 + if not modified: + return 0 + for z in prange(dz, nogil=True): + for y in xrange(dy): + for x in xrange(dx): + if sizes[labels[z, y, x]] <= max_size: + mask[z, y, x] = 254 - for z in prange(dz, nogil=True): - for y in xrange(dy): - for x in xrange(dx): - if sizes[labels[z, y, x]] <= max_size: - mask[z, y, x] = 254 + return modified diff --git a/invesalius/data/mask.py b/invesalius/data/mask.py index 07c172d..d00d22e 100644 --- a/invesalius/data/mask.py +++ b/invesalius/data/mask.py @@ -353,9 +353,13 @@ class Mask(): imask = (~(matrix > 127)) labels, nlabels = ndimage.label(imask, bstruct, output=np.uint16) - print ">>>>", nlabels - floodfill.fill_holes_automatically(matrix, labels, nlabels, size) - self.save_history(index, orientation, self.matrix.copy(), cp_mask) + + if nlabels == 0: + return + + ret = floodfill.fill_holes_automatically(matrix, labels, nlabels, size) + if ret: + self.save_history(index, orientation, self.matrix.copy(), cp_mask) else: bstruct = ndimage.generate_binary_structure(2, CON2D[conn]) @@ -371,11 +375,15 @@ class Mask(): imask = (~(matrix > 127)) labels, nlabels = ndimage.label(imask, bstruct, output=np.uint16) + if nlabels == 0: + return + labels = labels.reshape(1, labels.shape[0], labels.shape[1]) matrix = matrix.reshape(1, matrix.shape[0], matrix.shape[1]) - floodfill.fill_holes_automatically(matrix, labels, nlabels, size) - self.save_history(index, orientation, matrix.copy(), cp_mask) + ret = floodfill.fill_holes_automatically(matrix, labels, nlabels, size) + if ret: + self.save_history(index, orientation, matrix.copy(), cp_mask) def __del__(self): if self.is_shown: diff --git a/invesalius/gui/frame.py b/invesalius/gui/frame.py index 1c97e67..b6c4bab 100644 --- a/invesalius/gui/frame.py +++ b/invesalius/gui/frame.py @@ -596,7 +596,6 @@ class Frame(wx.Frame): Publisher.sendMessage('Enable style', const.SLICE_STATE_MASK_FFILL) def OnFillHolesAutomatically(self): - # Publisher.sendMessage('Fill holes automatically') fdlg = dlg.FillHolesAutoDialog(_(u"Fill holes automatically")) fdlg.Show() @@ -752,20 +751,23 @@ class MenuBar(wx.MenuBar): self.clean_mask_menu.Enable(False) mask_menu.AppendSeparator() + self.fill_hole_mask_menu = mask_menu.Append(const.ID_FLOODFILL_MASK, _(u"Fill holes manually")) self.fill_hole_mask_menu.Enable(False) self.fill_hole_auto_menu = mask_menu.Append(const.ID_FILL_HOLE_AUTO, _(u"Fill holes automatically")) self.fill_hole_mask_menu.Enable(False) + mask_menu.AppendSeparator() + self.remove_mask_part_menu = mask_menu.Append(const.ID_REMOVE_MASK_PART, _(u"Remove parts")) self.remove_mask_part_menu.Enable(False) self.select_mask_part_menu = mask_menu.Append(const.ID_SELECT_MASK_PART, _(u"Select parts")) self.select_mask_part_menu.Enable(False) - + mask_menu.AppendSeparator() - + self.crop_mask_menu = mask_menu.Append(const.ID_CROP_MASK, _("Crop")) self.crop_mask_menu.Enable(False) -- libgit2 0.21.2