def median_otsu(input_volume, median_radius=4, numpass=4, autocrop=False, b0Slices=None): """ Simple brain extraction tool method for images from DWI data. It uses a median filter smoothing of the input_volumes b0Slices and an automatic histogram Otsu thresholding technique, hence the name medain_otsu. It mimics the MRtrix bet from the documentation. (mrconvert dwi.nii -coord 3 0 - | threshold - - | median3D - - | median3D - mask.nii) MRtrix uses default mean_radius=3 and numpass=2 However, from tests on multiple 1.5T and 3T data from GE, Philips, Siemens, the most robust choice is median_radius=4, numpass=4 Parameters ---------- input_volume : ndarray ndarray of the brain volume median_radius : int Radius (in voxels) of the applied median filter(default 4) numpass: int Number of pass of the median filter (default 4) autocrop: bool, optional if True, the masked input_volume will also be cropped using the bounding box defined by the masked data. Should be on if DWI is upsampled to 1x1x1 resolution. (default False) b0Slices : array 1D array representing indexes of the volume where b=0 Returns ------- maskedvolume : ndarray Masked input_volume mask : 3D ndarray The binary brain mask """ if len(input_volume.shape) == 4: if b0Slices is not None: b0vol = np.mean(input_volume[..., tuple(b0Slices)], axis=3) else: b0vol = input_volume[..., 0].copy() else: b0vol = input_volume.copy() # Make a mask using a multiple pass median filter and histogram thresholding. mask = multi_median(b0vol, median_radius, numpass) thresh = otsu(mask) mask = binary_threshold(mask, thresh) # Auto crop the volumes using the mask as input_volume for bounding box computing. if autocrop: mins, maxs = bounding_box(mask) mask = crop(mask, mins, maxs) croppedvolume = crop(input_volume, mins, maxs) maskedvolume = applymask(croppedvolume, mask) else: maskedvolume = applymask(input_volume, mask) return maskedvolume, mask
def nuclear_dots(file, t=0, c='g'): ''' t: threshold c: color, 'r', 'g', 'b' ''' im = imread(file)[:, :, RGB[c]] if t == 0: t = otsu(im) return label(im >= t)
def edited_to_segmentation(tivfn, brfn, s0fn): outfn = tivfn.replace('.nii', '_segmentation.nii') brnii = nib.load(brfn) brain = brnii.get_data() tiv = nib.load(tivfn).get_data() seg = np.zeros_like(tiv) csf = tiv.copy() csf[brain > 0] = 0 s0 = nib.load(s0fn).get_data() s0[s0 < 0.01] = 0.01 csf_other_thresh = np.exp(otsu(np.log(s0[csf > 0]))) other = csf.copy() csf[s0 < csf_other_thresh] = 0 other[csf > 0] = 0 seg[brain > 0] = 1 seg[csf > 0] = 2 seg[other > 0] = 3 nib.save(nib.Nifti1Image(seg, brnii.affine), outfn) return outfn
def remove_disconnected_components(mask, aff=None, dilrad=0, inplace=True, verbose=False, nkeep=1, weight=None): """ Return a version of mask with all but the largest nkeep regions removed. Parameters ---------- mask: array-like 3D array which is true where there is a component, and 0 elsewhere. aff: None or 4x4 float array The voxel-to-world coordinate affine array of the mask. Must be given if dilrad > 0. dilrad: float Radius in aff's units (mm by the Nifti standard) to dilate by before determining how mask's voxels are connected. This does not affect mask (even if inplace is True), just a copy used in finding connected components. A moderate radius will ensure that tenuously tethered components are considered to be connected, but too large a radius will make everything appear connected. inplace: bool Whether to operate on mask in place or return a new array. verbose: bool Chattiness controller nkeep: None or int > 0 Keep the nkeep largest components. If None it will be determined by Otsu thresholding the region sizes, optionally weighted by weight. weight: None or array-like An optional image with the same shape as mask which can be used to weight regions if nkeep is None. It is useful for preferring bright medium regions over large regions just above 0. Output ------ mask: array-like 3D array which is true for the largest component, and 0 elsewhere. """ if verbose: print("Removing disconnected components") cmask = mask.copy() if aff is None: aff = np.eye(4) if dilrad: ball = make_structural_sphere(aff, dilrad) # Closing isn't really necessary since mask itself is not dilated. cmask = ndi.morphology.binary_dilation(cmask, ball) labelled, nb_labels = ndi.label(cmask) if verbose: print("Found %d components" % nb_labels) del cmask labels = np.arange(1, nb_labels + 1) if weight is None: weight = np.ones(mask.shape) sizes = np.array([weight[labelled == label].sum() for label in labels]) if nkeep is None: if len(sizes) > 1: thresh = otsu(sizes) nkeep = max(1, len(sizes[sizes > thresh])) else: nkeep = 1 keep_indices = np.argpartition( sizes, -nkeep)[-nkeep:] # O(n), requires numpy >= 1.8 if inplace: mymask = mask else: mymask = mask.copy() mymask[mask > 0] = 0 for ki in keep_indices: mymask[labelled == labels[ki]] = 1 return mymask
def median_otsu(input_volume, median_radius=4, numpass=4, autocrop=False, vol_idx=None, dilate=None): """ Simple brain extraction tool method for images from DWI data It uses a median filter smoothing of the input_volumes `vol_idx` and an automatic histogram Otsu thresholding technique, hence the name *median_otsu*. This function is inspired from Mrtrix's bet which has default values ``median_radius=3``, ``numpass=2``. However, from tests on multiple 1.5T and 3T data from GE, Philips, Siemens, the most robust choice is ``median_radius=4``, ``numpass=4``. Parameters ---------- input_volume : ndarray ndarray of the brain volume median_radius : int Radius (in voxels) of the applied median filter(default 4) numpass: int Number of pass of the median filter (default 4) autocrop: bool, optional if True, the masked input_volume will also be cropped using the bounding box defined by the masked data. Should be on if DWI is upsampled to 1x1x1 resolution. (default False) vol_idx : None or array, optional 1D array representing indices of ``axis=3`` of a 4D `input_volume` None (the default) corresponds to ``(0,)`` (assumes first volume in 4D array) dilate : None or int, optional number of iterations for binary dilation Returns ------- maskedvolume : ndarray Masked input_volume mask : 3D ndarray The binary brain mask """ if len(input_volume.shape) == 4: if vol_idx is not None: b0vol = np.mean(input_volume[..., tuple(vol_idx)], axis=3) else: b0vol = input_volume[..., 0].copy() else: b0vol = input_volume.copy() # Make a mask using a multiple pass median filter and histogram thresholding. mask = multi_median(b0vol, median_radius, numpass) thresh = otsu(mask) mask = mask > thresh if dilate is not None: cross = generate_binary_structure(3, 1) mask = binary_dilation(mask, cross, iterations=dilate) # Auto crop the volumes using the mask as input_volume for bounding box computing. if autocrop: mins, maxs = bounding_box(mask) mask = crop(mask, mins, maxs) croppedvolume = crop(input_volume, mins, maxs) maskedvolume = applymask(croppedvolume, mask) else: maskedvolume = applymask(input_volume, mask) return maskedvolume, mask
def guessFLAIRity(self): """ Guess at whether data's contrast suppressed free water or not, i.e. return True for FLAIR DTI and False otherwise. It operates by doing a rough segmentation of brain tissue and CSF, and checking whether the tissue is brighter than CSF for b ~ 0. Sets self.mask as a side-effect. """ b = np.asarray(self.bvals) b0 = utils.calc_average_s0(self.data, b, self.relbthresh, estimator=np.median) embDt = np.exp(-b * self.Dt) embDCSF = np.exp(-b * self.DCSF) w = (embDt * (1.0 - embDCSF))**2 tisssig = np.zeros(b0.shape) embb0s = {} sw = 0.0 for v, emb in enumerate(embDCSF): if emb not in embb0s: embb0s[emb] = emb * b0 tisssig += w[v] * (self.data[..., v] - embb0s[emb]) sw += w[v] del embb0s tisssig /= sw # Don't use median_otsu because it assumes isotropic voxels. if self.nmed > 0: if self.medrad >= 1: ball = utils.make_structural_sphere( self.aff, self.medrad * self.maxscale) if self.verbose: print("Median filtering %d times with radius %f." % (self.nmed, self.medrad * self.maxscale)) for i in range(self.nmed): tisssig = median_filter(tisssig, footprint=ball) elif self.medrad > 0: print("Warning: not median filtering since medrad < 1.") if self.verbose: print("Getting the Otsu threshold.") thresh = otsu(tisssig) self._mask = np.zeros(tisssig.shape, np.bool) self._mask[tisssig >= thresh] = 1 ball = utils.make_structural_sphere(self.aff, max(10.0, self.maxscale)) self._csfmask = utils.binary_closing(self._mask, ball) gaprad = max(self.closerad, 2 * self.maxscale) self._csfmask, success = utils.fill_holes(self._csfmask, self.aff, gaprad, self.verbose) self._csfmask = utils.binary_opening(self._csfmask, ball) self._csfmask[self._mask > 0] = 0 csfmed = np.median(b0[self._csfmask > 0]) b0tiss = b0[self._mask > 0] # Now we have an approximate brain, and we know it is surrounded by CSF # (in vivo) or solution (ex vivo), which we'll call CSF. Figure out # whether CSF is brighter or darker than tissue in the b0. tissmed = np.median(b0tiss) return tissmed > 2.0 * csfmed
def median_otsu(input_volume, median_radius=4, numpass=4, autocrop=False, vol_idx=None, dilate=None): """Simple brain extraction tool method for images from DWI data. It uses a median filter smoothing of the input_volumes `vol_idx` and an automatic histogram Otsu thresholding technique, hence the name *median_otsu*. This function is inspired from Mrtrix's bet which has default values ``median_radius=3``, ``numpass=2``. However, from tests on multiple 1.5T and 3T data from GE, Philips, Siemens, the most robust choice is ``median_radius=4``, ``numpass=4``. Parameters ---------- input_volume : ndarray ndarray of the brain volume median_radius : int Radius (in voxels) of the applied median filter (default: 4). numpass: int Number of pass of the median filter (default: 4). autocrop: bool, optional if True, the masked input_volume will also be cropped using the bounding box defined by the masked data. Should be on if DWI is upsampled to 1x1x1 resolution. (default: False). vol_idx : None or array, optional 1D array representing indices of ``axis=3`` of a 4D `input_volume` None (the default) corresponds to ``(0,)`` (assumes first volume in 4D array). dilate : None or int, optional number of iterations for binary dilation Returns ------- maskedvolume : ndarray Masked input_volume mask : 3D ndarray The binary brain mask Notes ----- Copyright (C) 2011, the scikit-image team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of skimage nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ if len(input_volume.shape) == 4: if vol_idx is not None: b0vol = np.mean(input_volume[..., tuple(vol_idx)], axis=3) else: b0vol = input_volume[..., 0].copy() else: b0vol = input_volume.copy() # Make a mask using a multiple pass median filter and histogram # thresholding. mask = multi_median(b0vol, median_radius, numpass) thresh = otsu(mask) mask = mask > thresh if dilate is not None: cross = generate_binary_structure(3, 1) mask = binary_dilation(mask, cross, iterations=dilate) # Auto crop the volumes using the mask as input_volume for bounding box # computing. if autocrop: mins, maxs = bounding_box(mask) mask = crop(mask, mins, maxs) croppedvolume = crop(input_volume, mins, maxs) maskedvolume = applymask(croppedvolume, mask) else: maskedvolume = applymask(input_volume, mask) return maskedvolume, mask
def median_otsu(input_volume, median_radius=4, numpass=4, autocrop=False, b0Slices=None): """ Simple brain extraction tool method for images from DWI data It uses a median filter smoothing of the input_volumes `b0Slices` and an automatic histogram Otsu thresholding technique, hence the name *median_otsu*. It mimics the ``MRtrix`` bet from the documentation:: mrconvert dwi.nii -coord 3 0 - | threshold - - | median3D - - | \ median3D - mask.nii ``MRtrix`` uses default ``mean_radius=3`` and ``numpass=2`` However, from tests on multiple 1.5T and 3T data from GE, Philips, Siemens, the most robust choice is ``median_radius=4``, ``numpass=4`` Parameters ---------- input_volume : ndarray ndarray of the brain volume median_radius : int Radius (in voxels) of the applied median filter(default 4) numpass: int Number of pass of the median filter (default 4) autocrop: bool, optional if True, the masked input_volume will also be cropped using the bounding box defined by the masked data. Should be on if DWI is upsampled to 1x1x1 resolution. (default False) b0Slices : None or array, optional 1D array representing indices of ``axis=3`` of a 4D `input_volume` where the acquisition b value == 0. None (the default) corresponds to ``(0,)`` (assumes first volume in 4D array is b == 0) Returns ------- maskedvolume : ndarray Masked input_volume mask : 3D ndarray The binary brain mask """ if len(input_volume.shape) == 4: if b0Slices is not None: b0vol = np.mean(input_volume[..., tuple(b0Slices)], axis=3) else: b0vol = input_volume[..., 0].copy() else: b0vol = input_volume.copy() # Make a mask using a multiple pass median filter and histogram thresholding. mask = multi_median(b0vol, median_radius, numpass) thresh = otsu(mask) mask = mask > thresh # Auto crop the volumes using the mask as input_volume for bounding box computing. if autocrop: mins, maxs = bounding_box(mask) mask = crop(mask, mins, maxs) croppedvolume = crop(input_volume, mins, maxs) maskedvolume = applymask(croppedvolume, mask) else: maskedvolume = applymask(input_volume, mask) return maskedvolume, mask