Source code for ClearMap.ImageProcessing.Experts.Vasculature

# -*- coding: utf-8 -*-
"""
Vasculature
===========

Expert vasculature image processing pipeline.

This module provides the basic routines for processing vasculature data.
The routines are used in the :mod:`ClearMap.Scripts.TubeMap` pipeline.
"""
__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__ = 'https://idisco.info'
__download__ = 'https://www.github.com/ChristophKirst/ClearMap2'
  
import gc
import tempfile as tmpf

import numpy as np
import scipy.ndimage as ndi
import skimage.filters as skif

import ClearMap.IO.IO as io
from ClearMap.Utils.exceptions import MissingRequirementException

import ClearMap.ParallelProcessing.BlockProcessing as bp
import ClearMap.ParallelProcessing.DataProcessing.ArrayProcessing as ap

import ClearMap.ImageProcessing.Filter.Rank as rnk
import ClearMap.ImageProcessing.LocalStatistics as ls
import ClearMap.ImageProcessing.LightsheetCorrection as lc
import ClearMap.ImageProcessing.Differentiation.Hessian as hes
import ClearMap.ImageProcessing.Binary.Filling as bf
import ClearMap.ImageProcessing.Binary.Smoothing as bs

import ClearMap.Utils.Timer as tmr
from ClearMap.ImageProcessing.Experts.utils import initialize_sinks, run_step, print_params
from ClearMap.Utils.utilities import check_enough_temp_space, get_free_temp_space

###############################################################################
# ## Generic parameter
###############################################################################

MAX_BIN = 2**12
"""Number of intensity levels to use for the data after preprocessing.

Note
----
      * Higher values will increase the intensity resolution but slow down
        processing. 
      * 2**12 is a good choice for the vasculature data.
"""

DTYPE = 'uint16'
"""Data type for the data after preprocessing.

Note
----
      * The data type should fit numbers as big as the :const:`MAX_BIN` parameter. 
      * 'uint16' is a good choice for the vasculature data.
"""

BINARY_NAMES = ['High', 'Equalized', 'Deconvolved', 'Adaptive', 'Tube', 'Fill', 'Tracing']
"""Names for the multi-path binarization steps."""

BINARY_STATUS = {n: 2**k for k, n in enumerate(BINARY_NAMES)}
"""Binary representation for the multi-path binarization steps."""
     
###############################################################################
# ## Default parameter
###############################################################################

default_binarization_parameter = dict(
    # initial clipping and mask generation
    clip=dict(clip_range=(400, 60000),
              save=None),

    # lightsheet correction
    lightsheet=dict(percentile=0.25,
                    lightsheet=dict(selem=(150, 1, 1)),
                    background=dict(selem=(200, 200, 1),
                                    spacing=(25, 25, 1),
                                    step=(2, 2, 1),
                                    interpolate=1),
                    lightsheet_vs_background=2,
                    save=None),

    # median
    median=dict(selem=((3,)*3),
                save=None),

    # deconvolution
    deconvolve=dict(sigma=10,
                    threshold=750,
                    save=None),

    # equalization
    equalize=dict(percentile=(0.4, 0.975),
                  selem=(200, 200, 5),
                  spacing=(50, 50, 5),
                  interpolate=1,
                  threshold=1.1,
                  save=None),

    # adaptive threshold
    adaptive=dict(selem=(250, 250, 3),
                  spacing=(50, 50, 3),
                  interpolate=1,
                  save=None),

    # tubeness
    vesselize=dict(background=dict(selem=('disk', (30, 30, 1)),
                                   percentile=0.5),
                   tubeness=dict(sigma=1.0,
                                 gamma12=0.0),
                   threshold=120,
                   save=None),

    # fill
    fill=None,

    # smooth
    smooth=None,

    # controls
    binary_status=None,
    max_bin=MAX_BIN
)
"""Parameter for the vasculature binarization pipeline. 
See :func:`binarize` for details."""


default_binarization_processing_parameter = dict(
    size_max=40,
    size_min=5,
    overlap=0,
    axes=[2],
    optimization=True,
    optimization_fix='all',
    verbose=None,
    processes=None
)
"""Parallel processing parameter for the vasculature binarization pipeline. 
See :func:`ClearMap.ParallelProcessing.BlockProcessing.process`. for details."""       

                   
default_postprocessing_parameter = dict(
    # binary smoothing
    smooth=dict(iterations=6),

    # binary filling
    fill=True,

    # temporary file
    temporary_filename=None
)                   
"""Parameter for the postprocessing step of the binarized data.
See :func:`postprocess` for details."""


default_postprocessing_processing_parameter = dict(
    overlap=None,
    size_min=None,
    optimization=True,
    optimization_fix='all',
    as_memory=True
)
"""Parallel processing parameter for the vasculature postprocessing pipeline. 
See :func:`ClearMap.ParallelProcessing.BlockProcessing.process`. for details."""     


###############################################################################
# ## Binarization
###############################################################################
                   
[docs] def binarize(source, sink=None, binarization_parameter=default_binarization_parameter, processing_parameter=default_binarization_processing_parameter): """ Multi-path binarization of iDISCO+ cleared vasculature data. Arguments --------- source : source specification The source of the stitched raw data. sink : sink specification or None The sink to write the result to. If None, an array is returned. binarization_parameter : dict Parameter for the binarization. See below for details. processing_parameter : dict Parameter for the parallel processing. See :func:`ClearMap.ParallelProcessing.BlockProcesing.process` for description of all the parameter. Returns ------- sink : Source The result of the binarization. Notes ----- * The binarization pipeline is composed of several steps. The parameters for each step are passed as sub-dictionaries to the binarization_parameter dictionary. * If None is passed for one of the steps this step is skipped. * Each step also has an additional parameter 'save' that enables saving of the result of that step to a file to inspect the pipeline. General parameter ----------------- binary_status : str or None File name to save the information about which part of the multi-path binarization contributed to the final result. max_bin : int Number of intensity levels to use for the data after preprocessing. Higher values will increase the intensity resolution but slow down processing. For the vasculature a typical value is 2**12. Clipping -------- clip : dict or None Clipping and mask generation step parameter. clip_range : tuple The range to clip the raw data as (lowest, highest) Voxels above lowest define the foreground mask used in the following steps. For the vasculature a typical value is (400,60000). save : str or None Save the result of this step to the specified file if not None. See also :mod:`ClearMap.ImageProcessing.Clipping.Clipping` Lightsheet correction --------------------- lightsheet : dict or None Lightsheet correction step parameter. percentile : float Percentile in [0,1] used to estimate the lightsheet artifact. For the vasculature a typical value is 0.25. lightsheet : dict Parameter for the ligthsheet artifact percentile estimation. See :func:`ClearMap.ImageProcessing.LightsheetCorrection.correct_lightsheet` for list of all parameters. The crucial parameter is selem : tuple The structural element shape used to estimate the stripe artifact. It should match the typical length, width, and depth of the artifact in the data. For the vasculature a typical value is (150,1,1). background : dict Parameter for the background estimation in the light sheet correction. See :func:`ClearMap.ImageProcessing.LightsheetCorrection.correct_lightsheet` for list of all parameters. The crucial parameters are selem : tuple The structural element shape used to estimate the background. It should be bigger than the largest vessels, For the vasculature a typical value is (200,200,1). spacing : tuple The spacing to use to estimate the background. Larger spacings speed up processing but become less local estimates. For the vasculature a typical value is (25,25,1) step : tuple This parameter enables to subsample from the entire array defined by the structural element using larger than single voxel steps. For the vasculature a typical value is (2,2,1). interpolate : int The order of the interpolation used in constructing the full background estimate in case a non-trivial spacing is used. For the vasculature a typical value is 1. lightsheet_vs_background : float The background is multiplied by this weight before comparing to the lightsheet artifact estimate. For the vasculature a typical value is 2. save : str or None Save the result of this step to the specified file if not None. Median filter ------------- median : dict or None Median correction step parameter. See :func:`ClearMap.ImageProcessing.Filter.Rank.median` for all parameter. The important parameters are selem : tuple The structural element size for the median filter. For the vasculature a typical value is (3,3,3). save : str or None Save the result of this step to the specified file if not None. Pseudo Deconvolution -------------------- deconvolve : dict The deconvolution step parameter. sigma : float The std of a Gaussian filter applied to the high intensity pixel image. The number should reflect the scale of the halo effect seen around high intensity structures. For the vasculature a typical value is 10. save : str or None Save the result of this step to the specified file if not None. threshold : float Voxels above this threshold will be added to the binarization result in the multi-path binarization. For the vasculature a typical value is 750. Adaptive Thresholding --------------------- adaptive : dict or None Adaptive thresholding step parameter. A local ISODATA threshold is estimated. See also :mod:`ClearMap.ImageProcessing.LocalStatistics`. selem : tuple The structural element size to estimate the percentiles. Should be larger than the larger vessels. For the vasculature a typical value is (200,200,5). spacing : tuple The spacing used to move the structural elements. Larger spacings speed up processing but become locally less precise. For the vasculature a typical value is (50,50,5) interpolate : int The order of the interpolation used in constructing the full background estimate in case a non-trivial spacing is used. For the vasculature a typical value is 1. save : str or None Save the result of this step to the specified file if not None. Equalization ------------ equalize : dict or None Equalization step parameter. See also :func:`ClearMap.ImageProcessing.LocalStatistics.local_percentile` precentile : tuple The lower and upper percentiles used to estimate the equalization. The lower percentile is used for normalization, the upper to limit the maximal boost to a maximal intensity above this percentile. For the vasculature a typical value is (0.4, 0.975). max_value : float The maximal intensity value in the equalized image. For the vasculature a typical value is 1.5. selem : tuple The structural element size to estimate the percentiles. Should be larger than the larger vessels. For the vasculature a typical value is (200,200,5). spacing : tuple The spacing used to move the structural elements. Larger spacings speed up processing but become locally less precise. For the vasculature a typical value is (50,50,5) interpolate : int The order of the interpolation used in constructing the full background estimate in case a non-trivial spacing is used. For the vasculature a typical value is 1. save : str or None Save the result of this step to the specified file if not None. threshold : float Voxels above this threshold will be added to the binarization result in the multi-path binarization. For the vasculature a typical value is 1.1. Tube filter ----------- vesselize : dict The tube filter step parameter. background : dict or None Parameters to correct for local background. See :func:`ClearMap.ImageProcessing.Filter.Rank.percentile`. If None, no background correction is done before the tube filter. selem : tuple The structural element specification to estimate the percentiles. Should be larger than the largest vessels intended to be boosted by the tube filter. For the vasculature a typical value is ('disk', (30,30,1)). percentile : float Percentile in [0,1] used to estimate the background. For the vasculature a typical value is 0.5. tubness : dict Parameters used for the tube filter. See :func:`ClearMap.ImageProcessing.Differentiation.Hessian.lambda123`. sigma : float The scale of the vessels to boos in the filter. For the vasculature a typical value is 1.0. save : str or None Save the result of this step to the specified file if not None. threshold : float Voxels above this threshold will be added to the binarization result in the multi-path binarization. For the vasculature a typical value is 120. Binary filling -------------- fill : dict or None If not None, apply a binary filling the binarized result. For the vasculature this step is set to None and done globally in the postprocessing step. Binary smoothing ---------------- smooth : dict or None The smoothing parameter passed to :func:`ClearMap.ImageProcessing.Binary.Smoothing.smooth_by_configuration`. For the vasculature this step is set to None and done globally in the postprocessing step. References ---------- [1] C. Kirst et al., "Mapping the Fine-Scale Organization and Plasticity of the Brain Vasculature", Cell 180, 780 (2020) """ # initialize sink shape = io.shape(source) order = io.order(source) sink, sink_buffer = ap.initialize_sink(sink=sink, shape=shape, order=order, dtype=bool) # , memory='shared') # initialize addition output sinks binary_status = binarization_parameter.get('binary_status') if binary_status: ap.initialize_sink(binary_status, source=sink, shape=shape, order=order, dtype='uint16') initialize_sinks(binarization_parameter, shape, order) binarization_parameter.update(verbose=processing_parameter.get('verbose', False)) bp.process(binarize_block, source, sink, function_type='block', parameter=binarization_parameter, **processing_parameter) return sink
[docs] def binarize_block(source, sink, parameter=default_binarization_parameter): """Binarize a Block.""" # initialize parameter and slicings verbose = parameter.get('verbose', False) if verbose: prefix = 'Block %s: ' % (source.info(),) total_time = tmr.Timer(prefix) else: prefix = '' max_bin = parameter.get('max_bin', MAX_BIN) base_slicing = sink.valid.base_slicing valid_slicing = source.valid.slicing # initialize binary status for inspection binary_status = parameter.get('binary_status') if binary_status: binary_status = io.as_source(binary_status) binary_status = binary_status[base_slicing] default_step_params = {'parameter': parameter, 'steps_to_measure': {}, 'prefix': prefix, 'base_slicing': base_slicing, 'valid_slicing': valid_slicing} # clipping parameter_clip = parameter.get('clip') if parameter_clip: parameter_clip, timer = print_params(parameter_clip, 'clip', prefix, verbose) parameter_clip.update(norm=max_bin, dtype=DTYPE) save = parameter_clip.pop('save', None) clipped, mask, high, low = clip(source, **parameter_clip) not_low = np.logical_not(low) if save: save = io.as_source(save) save[base_slicing] = clipped[valid_slicing] if binary_status is not None: binary_status[high[valid_slicing]] += BINARY_STATUS['High'] else: sink[valid_slicing] = high[valid_slicing] del high, low if verbose: timer.print_elapsed_time('Clipping') else: clipped = source mask = not_low = np.ones(source.shape, dtype=bool) # active arrays: clipped, mask, not_low # lightsheet correction corrected = run_step('lightsheet', clipped, lc.correct_lightsheet, remove_previous_result=True, extra_kwargs={'mask': mask, 'max_bin': max_bin}, **default_step_params) # active arrays: corrected, mask, not_low # median filter median = run_step('median', corrected, rnk.median, remove_previous_result=True, extra_kwargs={'max_bin': max_bin, 'mask': not_low}, **default_step_params) del not_low # active arrays: median, mask # pseudo deconvolution parameter_deconvolution = parameter.get('deconvolve') if parameter_deconvolution: parameter_deconvolution, timer = print_params(parameter_deconvolution, 'deconvolve', prefix, verbose) save = parameter_deconvolution.pop('save', None) threshold = parameter_deconvolution.pop('threshold', None) if binary_status is not None: binarized = binary_status > 0 else: binarized = sink[:] deconvolved = deconvolve(median, binarized[:], **parameter_deconvolution) del binarized if save: save = io.as_source(save) save[base_slicing] = deconvolved[valid_slicing] if verbose: timer.print_elapsed_time('Deconvolution') if threshold: binary_deconvolved = deconvolved > threshold if binary_status is not None: binary_status[binary_deconvolved[valid_slicing]] += BINARY_STATUS['Deconvolved'] else: sink[valid_slicing] += binary_deconvolved[valid_slicing] del binary_deconvolved if verbose: timer.print_elapsed_time('Deconvolution: binarization') else: deconvolved = median # active arrays: median, mask, deconvolved # adaptive parameter_adaptive = parameter.get('adaptive') adaptive = run_step('adaptive', deconvolved, threshold_adaptive, remove_previous_result=False, **default_step_params) if parameter_adaptive: binary_adaptive = deconvolved > adaptive if binary_status is not None: binary_status[binary_adaptive[valid_slicing]] += BINARY_STATUS['Adaptive'] else: sink[valid_slicing] += binary_adaptive[valid_slicing] del binary_adaptive, adaptive # if verbose: # timer.print_elapsed_time('Adaptive') del deconvolved # active arrays: median, mask # equalize parameter_equalize = parameter.get('equalize') if parameter_equalize: parameter_equalize, timer = print_params(parameter_equalize, 'equalize', prefix, verbose) save = parameter_equalize.pop('save', None) threshold = parameter_equalize.pop('threshold', None) equalized = equalize(median, mask=mask, **parameter_equalize) if save: save = io.as_source(save) save[base_slicing] = equalized[valid_slicing] if verbose: timer.print_elapsed_time('Equalization') if threshold: binary_equalized = equalized > threshold if binary_status is not None: binary_status[binary_equalized[valid_slicing]] += BINARY_STATUS['Equalized'] else: sink[valid_slicing] += binary_equalized[valid_slicing] # prepare equalized for use in vesselization parameter_vesselization = parameter.get('vesselize') if parameter_vesselization and parameter_vesselization.get('background'): equalized[binary_equalized] = threshold equalized = float(max_bin-1) / threshold * equalized del binary_equalized if verbose: timer.print_elapsed_time('Equalization: binarization') else: equalized = median del median # active arrays: mask, equalized # smaller vessels /capillaries parameter_vesselization = parameter.get('vesselize') if parameter_vesselization: parameter_vesselization, timer = print_params(parameter_vesselization, 'vesselize', prefix, verbose) parameter_background = parameter_vesselization.get('background') parameter_background = parameter_background.copy() if parameter_background: save = parameter_background.pop('save', None) equalized = np.array(equalized, dtype='uint16') background = rnk.percentile(equalized, max_bin=max_bin, mask=mask, **parameter_background) tubeness = equalized - np.minimum(equalized, background) del background if save: save = io.as_source(save) save[base_slicing] = tubeness[valid_slicing] else: tubeness = equalized parameter_tubeness = parameter_vesselization.get('tubeness', {}) tubeness = tubify(tubeness, **parameter_tubeness) save = parameter_vesselization.get('save') if save: save = io.as_source(save) save[base_slicing] = tubeness[valid_slicing] if verbose: timer.print_elapsed_time('Vesselization') threshold = parameter_vesselization.get('threshold') if threshold: binary_vesselized = tubeness > threshold if binary_status is not None: binary_status[binary_vesselized[valid_slicing]] += BINARY_STATUS['Tube'] else: sink[valid_slicing] += binary_vesselized[valid_slicing] del binary_vesselized if verbose: timer.print_elapsed_time('Vesselization: binarization') del tubeness del equalized, mask # active arrays: None # fill holes parameter_fill = parameter.get('fill') if parameter_fill: step_param, timer = print_params(parameter_fill, 'fill', prefix, verbose) if binary_status is not None: foreground = binary_status > 0 filled = ndi.morphology.binary_fill_holes(foreground) binary_status[np.logical_and(filled, np.logical_not(foreground))] += BINARY_STATUS['Fill'] del foreground, filled else: filled = ndi.morphology.binary_fill_holes(sink[:]) sink[valid_slicing] += filled[valid_slicing] del filled if verbose: timer.print_elapsed_time('Filling') if binary_status is not None: sink[valid_slicing] = binary_status[valid_slicing] > 0 # smooth binary if parameter.get('smooth'): # WARNING: otherwise removes sink if no smoothing parameter['smooth']['save'] = False smoothed = run_step('smooth', sink, bs.smooth_by_configuration, remove_previous_result=False, extra_kwargs={'sink': None, 'processes': 1}, **default_step_params) sink[valid_slicing] = smoothed[valid_slicing] del smoothed if verbose: total_time.print_elapsed_time('Binarization') gc.collect()
############################################################################### # ## Postprocessing ###############################################################################
[docs] def postprocess(source, sink=None, postprocessing_parameter=default_postprocessing_parameter, processing_parameter=default_postprocessing_processing_parameter, processes=None, verbose=True): """ Postprocess a binarized image. Arguments --------- source : source specification The binary source. sink : sink specification or None The sink to write the postprocessed result to. If None, an array is returned. postprocessing_parameter : dict Parameter for the postprocessing. processing_parameter : dict Parameter for the parallel processing. processes: int or None Number of parallel processes to run. Defaults to max if None verbose : bool If True, print progress output. Returns ------- sink : Source The result of the binarization. Notes ----- * The postprocessing pipeline is composed of several steps. The parameters for each step are passed as sub-dictionaries to the postprocessing_parameter dictionary. * If None is passed for one of the steps the step is skipped. Smoothing --------- smooth : dict or None Smoothing step parameter. See :func:`ClearMap.ImageProcessing.Binary.Smoothing.smooth_by_configuration` iterations : int Number of smoothing iterations. For the vasculature a typical value is 6. Filling ------- fill : bool or None If True, fill holes in the binary data. """ source = io.as_source(source) sink = ap.initialize_sink(sink, shape=source.shape, dtype=source.dtype, order=source.order, return_buffer=False) if verbose: timer = tmr.Timer() print('Binary post processing: initialized.') postprocessing_parameter = postprocessing_parameter.copy() run_binary_filling = postprocessing_parameter.get('fill', False) parameter_smooth = postprocessing_parameter.get('smooth') # smoothing if parameter_smooth: fill_source, tmp_f_path, save = apply_smoothing(source, sink, processing_parameter, postprocessing_parameter, processes, verbose) else: fill_source = source save = False if run_binary_filling: bf.fill(fill_source, sink=sink, processes=processes, verbose=verbose) if parameter_smooth and not save: io.delete_file(tmp_f_path) if verbose: timer.print_elapsed_time('Binary post processing') gc.collect()
[docs] def apply_smoothing(source, sink, processing_parameter, postprocessing_parameter, processes=None, verbose=True): parameter_smooth = postprocessing_parameter.pop('smooth', None) if postprocessing_parameter.get('fill'): save = parameter_smooth.pop('save', False) # initialize temporary files if needed if not check_enough_temp_space(): raise MissingRequirementException(f'Free space in temporary directory is insufficient, required 200 GB, ' f'got {get_free_temp_space() // (2**30)} GB' f'Please free some space or use a different temporary directory.' f'You can set the "TMP" environment variable to a directory of your choice') tmp_f_path = save if save else postprocessing_parameter['temporary_filename'] tmp_f_path = tmp_f_path if tmp_f_path else tmpf.mktemp(prefix='TubeMap_Vasculature_postprocessing', suffix='.npy') sink_smooth = ap.initialize_sink(tmp_f_path, shape=source.shape, dtype=source.dtype, order=source.order, return_buffer=False) else: sink_smooth = sink save = False tmp_f_path = '' # run smoothing fill_source = bs.smooth_by_configuration(source, sink=sink_smooth, processing_parameter=processing_parameter, processes=processes, verbose=verbose, **parameter_smooth) return fill_source, tmp_f_path, save
############################################################################### # ## Binarization processing steps ###############################################################################
[docs] def clip(source, clip_range=(300, 60000), norm=MAX_BIN, dtype=DTYPE): clip_low, clip_high = clip_range clipped = np.array(source[:], dtype=float) low = clipped < clip_low clipped[low] = clip_low high = clipped >= clip_high clipped[high] = clip_high mask = np.logical_not(np.logical_or(low, high)) clipped -= clip_low clipped *= float(norm-1) / (clip_high - clip_low) clipped = np.asarray(clipped, dtype=dtype) return clipped, mask, high, low
[docs] def deconvolve(source, binarized, sigma=10): convolved = np.zeros(source.shape, dtype=float) convolved[binarized] = source[binarized] for z in range(convolved.shape[2]): convolved[:, :, z] = ndi.gaussian_filter(convolved[:, :, z], sigma=sigma) deconvolved = source - np.minimum(source, convolved) deconvolved[binarized] = source[binarized] return deconvolved
[docs] def threshold_isodata(source): try: thresholds = skif.threshold_isodata(source, return_all=True) if len(thresholds) > 0: return thresholds[-1] else: return 1 except: # FIXME: too broad return 1
[docs] def threshold_adaptive(source, function=threshold_isodata, selem=(100, 100, 3), spacing=(25, 25, 3), interpolate=1, mask=None, step=None): source = io.as_source(source)[:] threshold = ls.apply_local_function(source, function=function, mask=mask, dtype=float, selem=selem, spacing=spacing, interpolate=interpolate, step=step) return threshold
[docs] def equalize(source, percentile=(0.5, 0.95), max_value=1.5, selem=(200, 200, 5), spacing=(50, 50, 5), interpolate=1, mask=None): equalized = ls.local_percentile(source, percentile=percentile, mask=mask, dtype=float, selem=selem, spacing=spacing, interpolate=interpolate) normalize = 1/np.maximum(equalized[..., 0], 1) maxima = equalized[..., 1] ids = maxima * normalize > max_value normalize[ids] = max_value / maxima[ids] equalized = np.array(source, dtype=float) * normalize return equalized
[docs] def tubify(source, sigma=1.0, gamma12=1.0, gamma23=1.0, alpha=0.25): return hes.lambda123(source=source, sink=None, sigma=sigma, gamma12=gamma12, gamma23=gamma23, alpha=alpha)
############################################################################### # ## Helper ###############################################################################
[docs] def status_to_description(status): """ Converts a status int to its description. Arguments --------- status : int The status. Returns ------- description : str The description corresponding to the status. """ description = '' for k in range(len(BINARY_NAMES)-1, -1, -1): if status / 2**k == 1: description = BINARY_NAMES[k] + ',' + description status -= 2**k if len(description) == 0: description = 'Background' else: description = description[:-1] return description
[docs] def binary_statistics(source): """ Counts the binarization types. Arguments --------- source : array The status array of the binarization process. Returns ------- statistics : dict A dict with entires {description : count}. """ status, counts = np.unique(io.as_source(source)[:], return_counts=True) return {status_to_description(s): c for s, c in zip(status, counts)}
############################################################################### # ## Tests ############################################################################### def _test(): """Tests.""" import numpy as np import ClearMap.Visualization.Plot3d as p3d import ClearMap.Tests.Files as tsf import ClearMap.ImageProcessing.Experts.Vasculature as vasc source = np.array(tsf.source('vls')[:300, :300, 80:120]) source[:, :, [0, -1]] = 0 source[:, [0, -1], :] = 0 source[[0, -1], :, :] = 0 bpar = vasc.default_binarization_parameter.copy() bpar['clip']['clip_range'] = (150, 7000) bpar['as_memory'] = True # bpar['binary_status'] = 'binary_status.npy' ppar = vasc.default_processing_parameter.copy() ppar['processes'] = 10 ppar['size_max'] = 10 sink = 'binary.npy' # sink=None; binary = vasc.binarize(source, sink=sink, binarization_parameter=bpar, processing_parameter=ppar) p3d.plot([source, binary]) import ClearMap.IO.IO as io io.delete_file(sink) pppar = vasc.default_postprocessing_parameter.copy() pppar['smooth']['iterations'] = 3 smoothed = vasc.postprocess(binary, postprocessing_parameter=pppar) p3d.plot([binary, smoothed])