Commit 8594ab8dc7129fe1411173e4568b48779e5f2bdd

Authored by Thiago Franco de Moraes
1 parent 932566e7
Exists in fill_holes_auto

Returning true when a hole were filled

invesalius/data/floodfill.pyx
@@ -6,6 +6,7 @@ from collections import deque @@ -6,6 +6,7 @@ from collections import deque
6 6
7 from cython.parallel import prange 7 from cython.parallel import prange
8 from libc.math cimport floor, ceil 8 from libc.math cimport floor, ceil
  9 +from libcpp cimport bool
9 from libcpp.deque cimport deque as cdeque 10 from libcpp.deque cimport deque as cdeque
10 from libcpp.vector cimport vector 11 from libcpp.vector cimport vector
11 12
@@ -237,22 +238,37 @@ def floodfill_auto_threshold(np.ndarray[image_t, ndim=3] data, list seeds, float @@ -237,22 +238,37 @@ def floodfill_auto_threshold(np.ndarray[image_t, ndim=3] data, list seeds, float
237 @cython.wraparound(False) 238 @cython.wraparound(False)
238 @cython.nonecheck(False) 239 @cython.nonecheck(False)
239 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): 240 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):
240 - cdef np.ndarray[np.uint32_t, ndim=1] sizes = np.zeros(shape=(nlabels + 1), dtype=np.uint32)  
241 - cdef int x, y, z  
242 - cdef int dx, dy, dz 241 + """
  242 + Fill mask holes automatically. The hole must <= max_size. Return True if any hole were filled.
  243 + """
  244 + cdef np.ndarray[np.uint32_t, ndim=1] sizes = np.zeros(shape=(nlabels + 1), dtype=np.uint32)
  245 + cdef int x, y, z
  246 + cdef int dx, dy, dz
  247 + cdef int i
  248 +
  249 + cdef bool modified = False
  250 +
  251 + dz = mask.shape[0]
  252 + dy = mask.shape[1]
  253 + dx = mask.shape[2]
  254 +
  255 + for z in xrange(dz):
  256 + for y in xrange(dy):
  257 + for x in xrange(dx):
  258 + sizes[labels[z, y, x]] += 1
243 259
244 - dz = mask.shape[0]  
245 - dy = mask.shape[1]  
246 - dx = mask.shape[2] 260 + #Checking if any hole will be filled
  261 + for i in xrange(nlabels + 1):
  262 + if sizes[i] <= max_size:
  263 + modified = True
247 264
248 - for z in xrange(dz):  
249 - for y in xrange(dy):  
250 - for x in xrange(dx):  
251 - sizes[labels[z, y, x]] += 1 265 + if not modified:
  266 + return 0
252 267
  268 + for z in prange(dz, nogil=True):
  269 + for y in xrange(dy):
  270 + for x in xrange(dx):
  271 + if sizes[labels[z, y, x]] <= max_size:
  272 + mask[z, y, x] = 254
253 273
254 - for z in prange(dz, nogil=True):  
255 - for y in xrange(dy):  
256 - for x in xrange(dx):  
257 - if sizes[labels[z, y, x]] <= max_size:  
258 - mask[z, y, x] = 254 274 + return modified
invesalius/data/mask.py
@@ -353,9 +353,13 @@ class Mask(): @@ -353,9 +353,13 @@ class Mask():
353 353
354 imask = (~(matrix > 127)) 354 imask = (~(matrix > 127))
355 labels, nlabels = ndimage.label(imask, bstruct, output=np.uint16) 355 labels, nlabels = ndimage.label(imask, bstruct, output=np.uint16)
356 - print ">>>>", nlabels  
357 - floodfill.fill_holes_automatically(matrix, labels, nlabels, size)  
358 - self.save_history(index, orientation, self.matrix.copy(), cp_mask) 356 +
  357 + if nlabels == 0:
  358 + return
  359 +
  360 + ret = floodfill.fill_holes_automatically(matrix, labels, nlabels, size)
  361 + if ret:
  362 + self.save_history(index, orientation, self.matrix.copy(), cp_mask)
359 else: 363 else:
360 bstruct = ndimage.generate_binary_structure(2, CON2D[conn]) 364 bstruct = ndimage.generate_binary_structure(2, CON2D[conn])
361 365
@@ -371,11 +375,15 @@ class Mask(): @@ -371,11 +375,15 @@ class Mask():
371 imask = (~(matrix > 127)) 375 imask = (~(matrix > 127))
372 labels, nlabels = ndimage.label(imask, bstruct, output=np.uint16) 376 labels, nlabels = ndimage.label(imask, bstruct, output=np.uint16)
373 377
  378 + if nlabels == 0:
  379 + return
  380 +
374 labels = labels.reshape(1, labels.shape[0], labels.shape[1]) 381 labels = labels.reshape(1, labels.shape[0], labels.shape[1])
375 matrix = matrix.reshape(1, matrix.shape[0], matrix.shape[1]) 382 matrix = matrix.reshape(1, matrix.shape[0], matrix.shape[1])
376 383
377 - floodfill.fill_holes_automatically(matrix, labels, nlabels, size)  
378 - self.save_history(index, orientation, matrix.copy(), cp_mask) 384 + ret = floodfill.fill_holes_automatically(matrix, labels, nlabels, size)
  385 + if ret:
  386 + self.save_history(index, orientation, matrix.copy(), cp_mask)
379 387
380 def __del__(self): 388 def __del__(self):
381 if self.is_shown: 389 if self.is_shown:
invesalius/gui/frame.py
@@ -596,7 +596,6 @@ class Frame(wx.Frame): @@ -596,7 +596,6 @@ class Frame(wx.Frame):
596 Publisher.sendMessage('Enable style', const.SLICE_STATE_MASK_FFILL) 596 Publisher.sendMessage('Enable style', const.SLICE_STATE_MASK_FFILL)
597 597
598 def OnFillHolesAutomatically(self): 598 def OnFillHolesAutomatically(self):
599 - # Publisher.sendMessage('Fill holes automatically')  
600 fdlg = dlg.FillHolesAutoDialog(_(u"Fill holes automatically")) 599 fdlg = dlg.FillHolesAutoDialog(_(u"Fill holes automatically"))
601 fdlg.Show() 600 fdlg.Show()
602 601
@@ -752,20 +751,23 @@ class MenuBar(wx.MenuBar): @@ -752,20 +751,23 @@ class MenuBar(wx.MenuBar):
752 self.clean_mask_menu.Enable(False) 751 self.clean_mask_menu.Enable(False)
753 752
754 mask_menu.AppendSeparator() 753 mask_menu.AppendSeparator()
  754 +
755 self.fill_hole_mask_menu = mask_menu.Append(const.ID_FLOODFILL_MASK, _(u"Fill holes manually")) 755 self.fill_hole_mask_menu = mask_menu.Append(const.ID_FLOODFILL_MASK, _(u"Fill holes manually"))
756 self.fill_hole_mask_menu.Enable(False) 756 self.fill_hole_mask_menu.Enable(False)
757 757
758 self.fill_hole_auto_menu = mask_menu.Append(const.ID_FILL_HOLE_AUTO, _(u"Fill holes automatically")) 758 self.fill_hole_auto_menu = mask_menu.Append(const.ID_FILL_HOLE_AUTO, _(u"Fill holes automatically"))
759 self.fill_hole_mask_menu.Enable(False) 759 self.fill_hole_mask_menu.Enable(False)
760 760
  761 + mask_menu.AppendSeparator()
  762 +
761 self.remove_mask_part_menu = mask_menu.Append(const.ID_REMOVE_MASK_PART, _(u"Remove parts")) 763 self.remove_mask_part_menu = mask_menu.Append(const.ID_REMOVE_MASK_PART, _(u"Remove parts"))
762 self.remove_mask_part_menu.Enable(False) 764 self.remove_mask_part_menu.Enable(False)
763 765
764 self.select_mask_part_menu = mask_menu.Append(const.ID_SELECT_MASK_PART, _(u"Select parts")) 766 self.select_mask_part_menu = mask_menu.Append(const.ID_SELECT_MASK_PART, _(u"Select parts"))
765 self.select_mask_part_menu.Enable(False) 767 self.select_mask_part_menu.Enable(False)
766 - 768 +
767 mask_menu.AppendSeparator() 769 mask_menu.AppendSeparator()
768 - 770 +
769 self.crop_mask_menu = mask_menu.Append(const.ID_CROP_MASK, _("Crop")) 771 self.crop_mask_menu = mask_menu.Append(const.ID_CROP_MASK, _("Crop"))
770 self.crop_mask_menu.Enable(False) 772 self.crop_mask_menu.Enable(False)
771 773