Source code for pyfibre.model.tools.convertors

"""
PyFibre
Image Segmentation Library

Created by: Frank Longford
Created on: 26/11/2019

Last Modified: 26/11/2019
"""

import logging
import numpy as np

from scipy.ndimage.filters import gaussian_filter
from scipy.ndimage.morphology import binary_dilation

from skimage import measure
from skimage.morphology import remove_small_holes
from skimage.measure import regionprops

from pyfibre.model.tools.figures import draw_network
from pyfibre.utilities import label_set

from .utilities import region_check, bbox_indices

logger = logging.getLogger(__name__)


[docs]def sort_by_area(segments): """Sort a list of skimage regionprops segments by thier filled_area attribute""" areas = [segment.filled_area for segment in segments] indices = np.argsort(areas)[::-1] sorted_segments = [segments[i] for i in indices] return sorted_segments
[docs]def binary_to_stack(binary): """Create a segment stack from a global binary""" label_image = measure.label(binary.astype(int)) labels = label_set(label_image) binary_stack = np.zeros((labels.size,) + binary.shape, dtype=int) for index, label in enumerate(labels): binary_stack[index] += np.where(label_image == label, 1, 0) return np.where(binary_stack, 1, 0)
[docs]def stack_to_binary(stack): """Converts a stack to a binary image""" binary = np.sum(stack, axis=0) binary = np.where(binary, 1, 0) return binary
[docs]def regions_to_stack(regions, shape): """Convert a list of scikit-image segments to a single binary mask""" stack = np.zeros( (len(regions),) + shape, dtype=int) for index, region in enumerate(regions): binary_image = np.zeros(shape, dtype=int) indices = bbox_indices(region) binary_image[indices] += region.image stack[index] += binary_image stack = np.where(stack, 1, 0) return stack
[docs]def stack_to_regions(stack, intensity_image=None, min_size=0, min_frac=0): """Convert a binary mask image to a set of scikit-image regionprops objects""" regions = [] for binary in stack: labels = measure.label(binary.astype(np.int)) regions += [ region for region in regionprops( labels, intensity_image=intensity_image) if region_check(region, min_size, min_frac) ] regions = sort_by_area(regions) return regions
[docs]def regions_to_binary(regions, shape): """Convert a list of scikit-image segments to a single binary mask""" binary_image = np.zeros(shape, dtype=int) for region in regions: indices = bbox_indices(region) binary_image[indices] += region.image binary_image = np.where(binary_image, 1, 0) return binary_image
[docs]def binary_to_regions(binary, intensity_image=None, min_size=0, min_frac=0.0): """Convert a binary mask image to a set of scikit-image segment objects""" if binary.ndim > 2: binary = binary.sum(axis=0) labels = measure.label(binary.astype(np.int)) regions = [ region for region in regionprops( labels, intensity_image=intensity_image) if region_check(region, min_size, min_frac) ] regions = sort_by_area(regions) return regions
[docs]def networks_to_binary(networks, shape, area_threshold=200, iterations=9, sigma=None): """Return a global binary representing areas of an image containing networks""" binary = np.zeros(shape, dtype=int) # Create skeleton image based on connected components in network for index, network in enumerate(networks): draw_network(network, binary, index=1) # Dilate skeleton image if iterations > 0: binary = binary_dilation(binary, iterations=iterations) # Smooth dilated image if sigma is not None: smoothed = gaussian_filter( binary.astype(float), sigma=sigma ) # Convert float image back to binary binary = np.where(smoothed, 1, 0) # Remove smooth holes with area less than threshold binary = remove_small_holes( binary.astype(bool), area_threshold=area_threshold) return binary.astype(int)
[docs]def networks_to_regions(networks, image=None, shape=None, area_threshold=200, iterations=9, sigma=None): """Transform fibre networks into a set of scikit-image segments""" # If no intensity image is provided, make sure binary # shape is provided if image is None: assert shape else: shape = image.shape binary = networks_to_binary(networks, shape, area_threshold=area_threshold, iterations=iterations, sigma=sigma) regions = binary_to_regions(binary, intensity_image=image) return regions
[docs]def binary_to_segments(binary, segment_klass, intensity_image=None, min_size=100, min_frac=0.1): """Transform binary array into a BaseSegment instance""" # Create a new set of segments for each region in binary regions = binary_to_regions( binary, intensity_image=intensity_image, min_size=min_size, min_frac=min_frac) segments = [ segment_klass(region=region) for region in regions ] return segments
[docs]def segments_to_binary(segments, shape): """Transform list of BaseSegment instances into a binary array""" stack = [ segment.to_array(shape=shape) for segment in segments ] binary = stack_to_binary(stack) return binary