Source code for ClearMap.ImageProcessing.Binary.Filling

"""
Filling
=======

Parallel binary filling on arbitraily sized images.
"""
__author__    = 'Christoph Kirst <christoph.kirst.ck@gmail.com>'
__license__   = 'GPLv3 - GNU General Pulic License v3 (see LICENSE.txt)'
__copyright__ = 'Copyright © 2020 by Christoph Kirst'
__webpage__   = 'http://idisco.info'
__download__  = 'http://www.github.com/ChristophKirst/ClearMap2'

import os
import numpy as np
import gc

import multiprocessing as mp

import pyximport;

old_get_distutils_extension = pyximport.pyximport.get_distutils_extension

[docs]def new_get_distutils_extension(modname, pyxfilename, language_level=None): extension_mod, setup_args = old_get_distutils_extension(modname, pyxfilename, language_level) extension_mod.language='c++' return extension_mod,setup_args
pyximport.pyximport.get_distutils_extension = new_get_distutils_extension pyximport.install(setup_args = {"include_dirs" : [np.get_include(), os.path.dirname(os.path.abspath(__file__))]}, reload_support=True) from . import FillingCode as code import ClearMap.IO.IO as io; import ClearMap.IO.SMA as sma import ClearMap.Utils.Timer as tmr #%%############################################################################ ### Binarization ###############################################################################
[docs]def fill(source, sink = None, seeds = None, processes = None, verbose = False): """Fill binary holes. Arguments --------- source : array Input source. sink : array or None If None, a new array is allocated. Returns ------- sink : array Binary image with filled holes. """ if source is sink: raise NotImplementedError("Cannot perform operation in place.") if verbose: print('Binary filling: initialized!') timer = tmr.Timer() #create temporary shared array order = io.order(source); #temp = sma.empty(source.shape, dtype='int8', order=order); temp = np.empty(source.shape, dtype='int8', order=order); source_flat = source.reshape(-1, order='A'); temp_flat = temp.reshape(-1, order='A'); if source_flat.dtype == bool: source_flat = source_flat.view(dtype = 'uint8') if processes is None: processes = mp.cpu_count(); if not isinstance(processes, int): processes = 1; #prepare flood fill code.prepare_temp(source_flat, temp_flat, processes=processes); #flood fill in parallel using mp if seeds is None: seeds = border_indices(source); else: seeds = np.where(seeds.reshape(-1, order=order))[0]; strides = np.array(io.element_strides(source)); code.label_temp(temp_flat, strides, seeds, processes=processes) if sink is None: #sink = sma.empty(source.shape, dtype=bool, order=order); sink = np.empty(source.shape, dtype=bool, order=order); sink_flat = sink.reshape(-1, order=order); if sink_flat.dtype == 'bool': sink_flat = sink_flat.view(dtype = 'uint8') code.fill(source_flat, temp_flat, sink_flat, processes=processes); if verbose: timer.print_elapsed_time('Binary filling') del temp, temp_flat gc.collect(); return sink;
[docs]def border_indices(source): """Returns the flat indices of the border pixels in source""" ndim = source.ndim; shape = source.shape; strides = io.element_strides(source); border = []; for d in range(ndim): offsets = tuple(0 if i>d else 1 for i in range(ndim)); for c in [0, shape[d]-1]: sl = tuple(slice(o, None if o==0 else -o) if i != d else c for i,o in enumerate(offsets)); where = np.where(np.logical_not(source[sl])); n = len(where[0]); if n > 0: indices = np.zeros(n, dtype = int); l = 0; for k in range(ndim): if k == d: indices += strides[k] * c; else: indices += strides[k] * (where[l] + offsets[k]); l += 1; border.append(indices); return np.concatenate(border);
def _test(): """Tests.""" import numpy as np import ClearMap.Visualization.Plot3d as p3d import ClearMap.ImageProcessing.Binary.Filling as bf from importlib import reload reload(bf); test = np.zeros((50,50,50), dtype = bool); test[20:30, 30:40, 25:35] = True test[25:27, 34: 38, 27: 32] = False; test[5:15, 5:15, 23:35] = True test[8:12, 8:12, 27:32] = False; filled = bf.fill(test, sink=None, processes=10) p3d.plot([test, filled])