Source code for nighres.microscopy.mgdm_cells

import numpy as np
import nibabel as nb
import os
import sys
import nighresjava
from ..io import load_volume, save_volume
from ..utils import _output_dir_4saving, _fname_4saving, \
                    _check_topology_lut_dir, _check_available_memory


[docs]def mgdm_cells(contrast_image1, contrast_type1, contrast_image2=None, contrast_type2=None, contrast_image3=None, contrast_type3=None, stack_dimension='2D', force_weight=0.6, curvature_weight=0.3, cell_threshold=0.1, max_iterations=200, min_change=0.0001, topology='wcs', topology_lut_dir=None, save_data=False, overwrite=False, output_dir=None, file_name=None): """ MGDM cell segmentation Estimates cell structures using a Multiple Object Geometric Deformable Model (MGDM) Parameters ---------- contrast_image1: niimg First input image to perform segmentation on contrast_type1: {"centroid-proba", "local-maxima","foreground-proba", "image-intensities"} Contrast type of first input image contrast_image2: niimg, optional Additional input image to inform segmentation, must be in the same space as constrast_image1, requires contrast_type2 contrast_type2: str, {"centroid-proba", "local-maxima","foreground-proba", "image-intensities"}, optional Contrast type of second input image contrast_image3: niimg, optional Additional input image to inform segmentation, must be in the same space as constrast_image1, requires contrast_type3 contrast_type3: {"centroid-proba", "local-maxima","foreground-proba", "image-intensities"}, optional Contrast type of third input image stack_dimension: {'2D','3D'}, optional Dimension of the data for processing, either a stack of independent 2D slices or a fully 3D stack max_iterations: int, optional Maximum number of iterations per step for MGDM (default is 800, set to 1 for quick testing of registration of priors, which does not perform true segmentation) min_change: float, optional Minimum amount of change in the segmentation for MGDM to stop (default is 0.001) force_weight: float, optional Forces to drive MGDM to cell boundaries (default is 0.5) curvature_weight: float, optional Curvature regularization forces (default is 0.1) cell_threshold: float, optional Ratio of lower intensities from the local maximum to be included in a given cell (default is 0.1) topology: {'wcs', 'no'}, optional Topology setting, choose 'wcs' (well-composed surfaces) for strongest topology constraint, 'no' for no topology constraint (default is 'wcs') topology_lut_dir: str, optional Path to directory in which topology files are stored (default is stored in TOPOLOGY_LUT_DIR) save_data: bool, optional Save output data to file (default is False) overwrite: bool, optional Overwrite existing results (default is False) output_dir: str, optional Path to desired output directory, will be created if it doesn't exist file_name: str, optional Desired base name for output files with file extension (suffixes will be added) Returns ---------- dict Dictionary collecting outputs under the following keys (suffix of output files in brackets) * segmentation (niimg): Hard brain segmentation with topological constraints (if chosen) (_mgdmc-seg) * distance (niimg): Minimum distance to a segmentation boundary (_mgdmc-dist) Notes ---------- Original Java module by Pierre-Louis Bazin. Algorithm details can be found in [1]_ and [2]_ References ---------- .. [1] Bogovic, Prince and Bazin (2013). A multiple object geometric deformable model for image segmentation. doi:10.1016/j.cviu.2012.10.006.A .. [2] Fan, Bazin and Prince (2008). A multi-compartment segmentation framework with homeomorphic level sets. DOI: 10.1109/CVPR.2008.4587475 """ print('\nMGDM Cell Segmentation') # check topology_lut_dir and set default if not given topology_lut_dir = _check_topology_lut_dir(topology_lut_dir) # sanity check contrast types contrasts = [contrast_image1, contrast_image2, contrast_image3] ctypes = [contrast_type1, contrast_type2, contrast_type3] for idx, ctype in enumerate(ctypes): if ctype is None and contrasts[idx] is not None: raise ValueError(("If specifying contrast_image{0}, please also " "specify contrast_type{0}".format(idx+1, idx+1))) # make sure that saving related parameters are correct if save_data: output_dir = _output_dir_4saving(output_dir, contrast_image1) seg_file = os.path.join(output_dir, _fname_4saving(module=__name__,file_name=file_name, rootfile=contrast_image1, suffix='mgdmc-seg', )) dist_file = os.path.join(output_dir, _fname_4saving(module=__name__,file_name=file_name, rootfile=contrast_image1, suffix='mgdmc-dist')) if overwrite is False \ and os.path.isfile(seg_file) \ and os.path.isfile(dist_file) : print("skip computation (use existing results)") output = {'segmentation': seg_file, 'distance': dist_file} return output # start virtual machine, if not already running try: mem = _check_available_memory() nighresjava.initVM(initialheap=mem['init'], maxheap=mem['max']) except ValueError: pass # create mgdm instance mgdm = nighresjava.SegmentationCellMgdm() # set mgdm parameters mgdm.setDataStackDimension(stack_dimension) mgdm.setTopologyLUTdirectory(topology_lut_dir) mgdm.setMaxIterations(max_iterations) mgdm.setMinChange(min_change) mgdm.setDataWeight(force_weight) mgdm.setCurvatureWeight(curvature_weight) mgdm.setCellThreshold(cell_threshold) mgdm.setTopology(topology) # load contrast image 1 and use it to set dimensions and resolution img = load_volume(contrast_image1) data = img.get_data() affine = img.affine header = img.header resolution = [x.item() for x in header.get_zooms()] dimensions = data.shape mgdm.setDimensions(dimensions[0], dimensions[1], dimensions[2]) mgdm.setResolutions(resolution[0], resolution[1], resolution[2]) # input image 1 mgdm.setContrastImage1(nighresjava.JArray('float')( (data.flatten('F')).astype(float))) mgdm.setContrastType1(contrast_type1) # if further contrast are specified, input them if contrast_image2 is not None: data = load_volume(contrast_image2).get_data() mgdm.setContrastImage2(nighresjava.JArray('float')( (data.flatten('F')).astype(float))) mgdm.setContrastType2(contrast_type2) if contrast_image3 is not None: data = load_volume(contrast_image3).get_data() mgdm.setContrastImage3(nighresjava.JArray('float')( (data.flatten('F')).astype(float))) mgdm.setContrastType3(contrast_type3) # execute MGDM try: mgdm.execute() except: # if the Java module fails, reraise the error it throws print("\n The underlying Java code did not execute cleanly: ") print(sys.exc_info()[0]) raise return # reshape output to what nibabel likes seg_data = np.reshape(np.array(mgdm.getSegmentedImage(), dtype=np.int32), dimensions, 'F') dist_data = np.reshape(np.array(mgdm.getLevelsetBoundaryImage(), dtype=np.float32), dimensions, 'F') # adapt header max for each image so that correct max is displayed # and create nifiti objects header['cal_max'] = np.nanmax(seg_data) seg = nb.Nifti1Image(seg_data, affine, header) header['cal_max'] = np.nanmax(dist_data) dist = nb.Nifti1Image(dist_data, affine, header) if save_data: save_volume(seg_file, seg) save_volume(dist_file, dist) return {'segmentation': seg_file, 'distance': dist_file} else: return {'segmentation': seg, 'distance': dist}