def _carve_segs(roi, blobs): # carve out background from segmented area carved = roi if blobs is None: # clean up by using simple threshold to remove all background carved, _ = cv_nd.carve(carved) else: # use blobs as ellipsoids to identify background to remove; # TODO: consider setting spacing in config since depends on # microscopy characteristics, such as elongation from # thick lightsheet thresholded = plot_3d.build_ground_truth( np.zeros(carved.shape, dtype=bool), blobs, ellipsoid=True) #thresholded = thresholded.astype(bool) carved[~thresholded] = 0 return carved
def segment_from_labels(edges, markers, labels_img, atlas_img=None, exclude_labels=None, mask_filt=config.SmoothingModes.opening, mask_filt_size=2): """Segment an image using markers from a labels image. Labels images may have been generally manually and thus may not perfectly match the underlying image. As a way to check or augment the label image, segment the underlying image using the labels as the seeds to prescribe the number and initial location of each label. Args: edges (:obj:`np.ndarray`): Image as a Numpy array to segment, typically an edge-detected image of the main atlas. markers (:obj:`np.ndarray`): Image as an integer Numpy array of same shape as ``img`` to use as seeds for the watershed segmentation. This array is generally constructed from an array similar to ``labels_img``. labels_img (:obj:`np.ndarray`): Labels image as Numpy array of same shape as ``img``, used to generate a mask for the watershed. If None, a mask will be generated from a thresholded version of ``atlas_img``, so should only be None if ``atlas_img`` is not None. atlas_img (:obj:`np.ndarray`): Atlas image as a Numpy array to use for finding foreground; defaults to None. If both ``labels_img`` and ``atlas_img`` are not None, their combined volume will be used as a mask. exclude_labels (List[int]): Sequence of labels to exclude from the segmentation; defaults to None. mask_filt (:obj:`config.SmoothingModes`): Enumeration for a filter mode to use for the watershed mask; defaults to :obj:`config.SmoothingModes.opening`. Ignored if ``atlas_img`` or both ``atlas_img`` and ``labels_img`` are given to generate the mask. mask_filt_size (int): Size of structuring element for the filter specified by ``mask_filt``; defaults to 2. Returns: :obj:`np.ndarray`: Segmented image of the same shape as ``img`` with the same number of labels as in ``markers``. """ # generate mask for watershed if atlas_img is not None and labels_img is not None: # broad mask from both atlas and labels mask = mask_atlas(atlas_img, labels_img) elif atlas_img is not None: # otsu seems to give more inclusive threshold for these atlases _, mask = cv_nd.carve(atlas_img, thresh=filters.threshold_otsu(atlas_img), holes_area=5000) else: # default to using label foreground mask = labels_img != 0 fn_mask = None if mask_filt is config.SmoothingModes.opening: # default filter opens the mask to prevent spillover across # artifacts that may bridge otherwise separate structures fn_mask = morphology.binary_opening elif mask_filt is config.SmoothingModes.closing: fn_mask = morphology.binary_closing if fn_mask and mask_filt_size: print("Filtering watershed mask with {}, size {}".format( fn_mask, mask_filt_size)) mask = fn_mask(mask, cv_nd.get_selem(labels_img.ndim)(mask_filt_size)) exclude = None if exclude_labels is not None: # remove excluded labels from mask exclude = np.isin(labels_img, exclude_labels) mask[exclude] = False # WORKAROUND: remove excluded markers from marker image itself for # apparent Scikit-image bug (see PR 3809, fixed in 0.15) markers[np.isin(markers, exclude_labels)] = 0 watershed = watershed_distance(edges == 0, markers, compactness=0.005, mask=mask) if exclude is not None: # add excluded labels directly to watershed image watershed[exclude] = labels_img[exclude] return watershed
def segment_from_labels(edges, markers, labels_img, atlas_img=None, exclude_labels=None): """Segment an image using markers from a labels image. Labels images may have been generally manually and thus may not perfectly match the underlying image. As a way to check or augment the label image, segment the underlying image using the labels as the seeds to prescribe the number and initial location of each label. Args: edges (:obj:`np.ndarray`): Image as a Numpy array to segment, typically an edge-detected image of the main atlas. markers (:obj:`np.ndarray`): Image as an integer Numpy array of same shape as ``img`` to use as seeds for the watershed segmentation. This array is generally constructed from an array similar to ``labels_img``. labels_img (:obj:`np.ndarray`): Labels image as Numpy array of same shape as ``img``, used to generate a mask for the watershed. If None, a mask will be generated from a thresholded version of ``atlas_img``, so should only be None if ``atlas_img`` is not None. atlas_img (:obj:`np.ndarray`): Atlas image as a Numpy array to use for finding foreground; defaults to None. If both ``labels_img`` and ``atlas_img`` are not None, their combined volume will be used as a mask. exclude_labels (List[int]): Sequence of labels to exclude from the segmentation; defaults to None. Returns: :obj:`np.ndarray`: Segmented image of the same shape as ``img`` with the same number of labels as in ``markers``. """ if atlas_img is not None and labels_img is not None: # broad mask from both atlas and labels mask = mask_atlas(atlas_img, labels_img) elif atlas_img is not None: # otsu seems to give more inclusive threshold for these atlases _, mask = cv_nd.carve( atlas_img, thresh=filters.threshold_otsu(atlas_img), holes_area=5000) else: # default to using labels, opening them up small holes to prevent # spillover across artifacts that may bridge them fn_selem = cv_nd.get_selem(labels_img.ndim) mask = morphology.binary_opening(labels_img != 0, fn_selem(2)) exclude = None if exclude_labels is not None: # remove excluded labels from mask exclude = np.isin(labels_img, exclude_labels) mask[exclude] = False # WORKAROUND: remove excluded markers from marker image itself for # apparent Scikit-image bug (see PR 3809, fixed in 0.15) markers[np.isin(markers, exclude_labels)] = 0 watershed = watershed_distance( edges == 0, markers, compactness=0.005, mask=mask) if exclude is not None: # add excluded labels directly to watershed image watershed[exclude] = labels_img[exclude] return watershed