Example #1
0
def degrade_line(im, eta=0, alpha=1.7, beta=1.7, alpha_0=1, beta_0=1):
    """
    Degrades a line image by adding noise

    Args:
        im (PIL.Image): Input image

    Returns:
        PIL.Image in mode 'L'
    """
    im = pil2array(im)
    im = np.amax(im) - im
    im = im * 1.0 / np.amax(im)

    # foreground distance transform and flipping to white probability
    fg_dist = distance_transform_cdt(im, metric='taxicab')
    fg_prob = alpha_0 * np.exp(-alpha * (fg_dist**2)) + eta
    fg_prob[im == 0] = 0
    fg_flip = np.random.binomial(1, fg_prob)

    # background distance transform and flipping to black probability
    bg_dist = distance_transform_cdt(1 - im, metric='taxicab')
    bg_prob = beta_0 * np.exp(-beta * (bg_dist**2)) + eta
    bg_prob[im == 1] = 0
    bg_flip = np.random.binomial(1, bg_prob)

    # flip
    im -= fg_flip
    im += bg_flip
    # use a circular kernel of size 3
    sel = np.array([[1, 1], [1, 1]])
    im = binary_closing(im, sel)
    return array2pil(255 - im.astype('B') * 255)
Example #2
0
 def compute_error(self,
                   true_mask: Mask,
                   pred_mask: Mask,
                   c: int = 5,
                   p: float = 2):
     """ Compute the Baddeley Error for binary images.
     
     Note:
         A.J. Baddeley - "An Error Metric for Binary Images"
     """
     # Ensure that masks have binary values
     true_mask = true_mask > 0
     pred_mask = pred_mask > 0
     # Handle masks filled with the same value
     xor_mask = true_mask ^ pred_mask
     if (~xor_mask).all():  # Masks equal
         return 1.0
     elif xor_mask.all():  # Masks completely different
         return 0.0
     # Compute metric
     true_edt = distance_transform_cdt(true_mask,
                                       metric='taxicab').astype(float)
     true_edt = np.minimum(true_edt, c)
     true_edt[true_edt <
              0] = c  # where a distance cannot be computed, set to maximum
     pred_edt = distance_transform_cdt(pred_mask,
                                       metric='taxicab').astype(float)
     pred_edt = np.minimum(pred_edt, c)
     pred_edt[pred_edt < 0] = c
     dist = np.abs(true_edt - pred_edt)
     dist /= c  # c is the maximum possible distance
     dist = (dist**p).mean()**(1 / p)
     return 1.0 - dist
Example #3
0
    def __init__(self,
                 img_mask_name,
                 crop_sz=(64, 64),
                 num_data=10000,
                 transform=None):
        """
        Args:
        img_mask_name: list of name pairs of image and mask data, each pair is a tuple of (img_name, mask_name)
            For mask, 1-positive samples, 0-negative samples
        crop_sz: cropping size 
        num_data: generated sample size
        transform: data augmentation 
        """
        self.img_all = {}
        self.mask_all = {}
        self.seg_all = {
        }  # segmented image, used as a reference to generate training samples by random cropping
        self.num_pairs = len(img_mask_name)
        for i in range(self.num_pairs):
            curr_name = img_mask_name[i]
            assert os.path.exists(curr_name[0]) and os.path.exists(curr_name[1]), \
                'Image or mask does not exist!'
            img = imread(curr_name[0])
            thres = threshold_triangle(img)
            seg_img = np.zeros(img.shape, dtype=img.dtype)
            seg_img[img > thres] = 1
            self.seg_all[str(i)] = seg_img

            img = img.astype(float)
            mu = img.mean(axis=(1, 2))
            sigma = img.std(axis=(1, 2))
            img = (img - mu.reshape(len(mu), 1, 1)) / sigma.reshape(
                len(sigma), 1, 1)
            self.img_all[str(i)] = img

            msk_data = np.load(curr_name[1], allow_pickle=True).tolist()
            mask = msk_data['masks']
            mask[mask != -1] = 1
            mask[mask == -1] = 0
            obj_dist = distance_transform_cdt(mask, metric='chessboard')
            bg = np.zeros(mask.shape, dtype=mask.dtype)
            bg[mask == 0] = 1
            bg_dist = distance_transform_cdt(bg, metric='chessboard')
            bg_dist[bg_dist > 4] = 4
            mask_dist = (obj_dist - bg_dist).astype(float)

            self.mask_all[str(i)] = mask_dist

        self.crop_sz = crop_sz
        self.num_data = num_data
        self.transform = transform
    def create_dt_mask(self, pnum, grid_size):
        batch_mask = np.zeros(
            [pnum, grid_size * grid_size],
            dtype=np.float32)  # Initialize your array of zeros

        for i in range(pnum):
            mask = np.zeros([grid_size, grid_size],
                            dtype=np.float32)  # Initialize your array of zeros

            thera = 1.0 * i / pnum * 2 * 3.1416
            x = np.cos(thera)
            y = -np.sin(thera)

            x = (0.7 * x + 1) / 2
            y = (0.7 * y + 1) / 2

            mask[np.clip(np.floor(y * (grid_size - 1)), 0, grid_size -
                         1).astype(int),
                 np.clip(np.floor(x * (grid_size - 1)), 0, grid_size -
                         1).astype(int)] = 1

            dt_mask = grid_size - distance_transform_cdt(
                1 - mask, metric='taxicab').astype(np.float32)

            dt_mask[dt_mask < grid_size - 15] = 0

            batch_mask[i] = dt_mask.reshape(-1)

        return batch_mask
Example #5
0
        def get_weight(polygon_targets):
            batch_targets = []

            for b in range(polygon_targets.shape[0]):
                target_ins = []
                for p in polygon_targets[b]:
                    t = np.zeros(h * w + 1)
                    t[p] = 1
                    if p != h * w:
                        spatial_part = t[:-1].reshape(h, w)
                        spatial_part = -1 * (spatial_part - 1)
                        spatial_part = distance_transform_cdt(
                            spatial_part, metric='taxicab').astype(np.float32)
                        spatial_part = np.clip(spatial_part, 0, dt_threshold)
                        spatial_part /= dt_threshold
                        spatial_part = -1. * (spatial_part - 1.)
                        spatial_part /= np.sum(spatial_part)
                        spatial_part = spatial_part.flatten()
                        t = np.concatenate([spatial_part, [0.]], axis=-1)
                    target_ins.append(t.astype(np.float32))
                batch_targets.append(target_ins)
            batch_targets = np.array(batch_targets).astype(np.float32)
            batch_targets = torch.from_numpy(batch_targets).reshape(
                batch_size, -1, h * w + 1).to(polygon_targets.device)
            return batch_targets
Example #6
0
 def load(self, index):
     if index < len(self.images) and index < len(
             self.gt_segmentations) and index < len(self.gt_boundaries):
         image = np.copy(self.images[index])
         gt_segmentation = np.copy(self.gt_segmentations[index])
         gt_boundary = np.copy(self.gt_boundaries[index])
     else:
         image = scipy.misc.imread(self.image_filenames[index],
                                   flatten=False).astype(float)
         if self.equalize_histogram:
             histmin, histmax = get_isbi_dataset_intensity_min_max(
                 self.image_filenames[index])
             image = do_equalize_histogram(image,
                                           histmin=histmin,
                                           histmax=histmax)
         gt_segmentation = None
         gt_boundary = None
         if len(self.gt_filenames) > index:
             gt_segmentation = scipy.misc.imread(self.gt_filenames[index],
                                                 flatten=False)
             boundary_pixels = np.ones(gt_segmentation.shape, dtype=bool)
             boundary_pixels[0:-1, :] &= (
                 gt_segmentation[0:-1, :] == gt_segmentation[1:, :])
             boundary_pixels[1:, :] &= (
                 gt_segmentation[1:, :] == gt_segmentation[0:-1, :])
             boundary_pixels[:, 0:-1] &= (
                 gt_segmentation[:, 0:-1] == gt_segmentation[:, 1:])
             boundary_pixels[:, 1:] &= (
                 gt_segmentation[:, 1:] == gt_segmentation[:, 0:-1])
             gt_boundary = distance_transform_cdt(
                 boundary_pixels, metric='taxicab').astype(float)
             gt_boundary[gt_boundary > self.dt_bound] = self.dt_bound
             gt_segmentation = (gt_segmentation > 0.5)
             gt_segmentation = gt_segmentation.astype(float)
     return (image, gt_segmentation, gt_boundary)
Example #7
0
def skeleton2graph(dendrite_id, dendrite_folder, seg, res, shrink=False):
    print('generate graph from skeleton ..')
    skel = ReadSkeletons(dendrite_folder,
                         skeleton_algorithm='thinning',
                         downsample_resolution=res,
                         read_edges=True)[1]

    sz = seg.shape
    #     bb = GetBbox(seg>0)
    bb = GetBbox(seg == int(dendrite_id))
    seg_b = seg[bb[0]:bb[1] + 1, bb[2]:bb[3] + 1, bb[4]:bb[5] + 1]
    dt = distance_transform_cdt(seg_b, return_distances=True)
    new_graph, wt_dict, th_dict, ph_dict = GetGraphFromSkeleton(
        skel,
        dt=dt,
        dt_bb=[bb[x] for x in [0, 2, 4]],
        modified_bfs=modified_bfs)

    edge_list = GetEdgeList(new_graph, wt_dict, th_dict, ph_dict)
    G = nx.Graph(shape=sz)
    # add edge attributes
    G.add_edges_from(edge_list)

    if shrink == True:  #shrink graph size
        edgTh = [40, 1]  # threshold
        n0 = len(G.nodes())
        G = ShrinkGraph_v2(G, threshold=edgTh)
        n1 = len(G.nodes())
        print('#nodes: %d -> %d' % (n0, n1))

    return G, skel
 def getEdgeDistByMask(self, mask3D, setID, sigma=4.5):
     result = Preprocessor.loadThresholdMask(setID)
     #result = generic_gradient_magnitude(result, sobel).astype(np.float32)
     #result = nd.filters.gaussian_filter(result, sigma)
     result = morph.distance_transform_cdt(result, metric='taxicab').astype(
         np.float32)
     return result[mask3D]
Example #9
0
def get_indeces(image_path):

    # Load image from path
    Image = io.loadmat(image_path)
    Label = np.zeros(Image['scanROI'].shape)

    Label[Image['goldROI'] == 2] = 1
    Scan = Image["scanROI"]
    Dims = np.prod(Scan.shape)

    #    units = np.divide(Image['scanROI'].shape, 100.0)

    #   max = np.array([0.9854,    0.7775,    0.6922])
    #   min = np.array([0.4995,    0.1120,   0.0538])

    #    xl, yl, zl = np.around(np.multiply(Scan.shape, max))
    #    xu, yu, zu = np.int32(np.floor(np.multiply(Scan.shape, min)))
    #
    #    cropped = np.squeeze(np.where(Label[xu:xl, yu:yl, zu:zl] >= 0))
    #    X = np.asarray([cropped[0] +xu, cropped[1]+yu , cropped[2] + zu])
    B = morphology.distance_transform_cdt(np.absolute(Label - 1))
    B = B.reshape(Dims)
    X = np.squeeze(np.where(B < 6))
    Inds = np.asarray(np.unravel_index(X, Scan.shape, order='C'))

    return Inds.T, Label, Image
def CompactLabel(Label, Compaction=3):
    """Re-labels objects that have multiple non-contiguous portions to create
    a new label image where each object is contiguous.

    Parameters:
    -----------
    Label : array_like
        A uint32 type label image generated by segmentation methods.
    Compaction : int
        Factor used in compacting objects to remove thin spurs. Refered to as
        'd' in the reference below. Default value = 3.

    Notes:
    ------
    Implemented from the reference below.

    Returns:
    --------
    Split : array_like
        A uint32 label where discontiguous objects are split and relabeled.

    See Also:
    ---------
    AreaOpenLabel, CondenseLabel, ShuffleLabel, SplitLabel, WidthOpenLabel

    References:
    -----------
    .. [1] S. Weinert et al "Detection and Segmentation of Cell Nuclei in
    Virtual Microscopy Images: A Minimum-Model Approach" in Nature Scientific
    Reports,vol.2,no.503, doi:10.1038/srep00503, 2012.
    """

    # copy input image
    Compact = Label.copy()

    # generate distance map of label image
    D = mp.distance_transform_cdt(Compact > 0, metric='taxicab')

    # define 4-neighbors filtering kernel
    Kernel = np.zeros((3, 3), dtype=np.bool)
    Kernel[1, :] = True
    Kernel[:, 1] = True

    # sweep over distance values from d-1 to 1
    for i in np.arange(Compaction-1, 0, -1):

        # four-neighbor maxima of distance transform
        MaxD = ft.maximum_filter(D, footprint=Kernel)

        # identify pixels whose max 4-neighbor is less than i+1
        Decrement = (D == i) & (MaxD < i+1)

        # decrement non-compact pixels
        D[Decrement] -= 1

    # zero label pixels where D == 0
    Compact[D == 0] = 0

    return Compact
Example #11
0
def Sampling(image_path):
    """
    :param image_path: path to image generated by get_paths
    :param patch_sizes: a list or array of patch sizes desired for convolution. Must be odd > 1.
    :param total_sample_size: sampled patches for convolution, and corresponding labels
    :return: sampled patches for convolution, and corresponding labels
    """

    # Load image from path
    Image = io.loadmat(image_path)
    # Extract total number of voxels
    Dims = np.prod(Image['scan'].shape)
    # Create 1 mask image
    Label = (Image['Tibia'] + 2 * Image['Femur'])
    np.place(Label, Label > 2, [2, 1])

    if Image['isright'] == 0:

        Scan = np.fliplr(Image["scan"])
        Label = np.fliplr(Label)

    else:
        Scan = Image["scan"]

    Label2 = (Image['Tibia'] + Image['Femur'])

    B = morphology.distance_transform_cdt(np.absolute(Label2 - 1))
    B = B.reshape(Dims)
    X = np.float32(range(np.max(B), 3, -1)) / range(4, np.max(B) + 1, 1)

    No_samples = 18000
    Index = np.array([])
    samples = np.divide(X, sum(X)) * No_samples

    for i in range(4, np.max(B) + 1):

        Index = np.int32(
            np.append(
                Index,
                np.random.choice(np.squeeze(np.where(B == i)),
                                 (samples[i - 4]),
                                 replace=False)))

    Label = Label.reshape(Dims)

    # Find indexes of voxels to sample
    Gold_ind = np.squeeze(np.where(Label == 1))
    Silv_ind = np.squeeze(np.where(Label == 2))
    Edge_ind = np.squeeze(np.where((B > 0) & (B <= 2)))

    Index = np.hstack((Gold_ind, Silv_ind, Edge_ind, Index))

    np.random.shuffle(Index)

    Inds = np.asarray(np.unravel_index(Index, Scan.shape, order='C'))

    Label_Sample = Label[Index]

    return Scan, Label_Sample, Inds.T
Example #12
0
def degrade_line(im, eta=0.0, alpha=1.5, beta=1.5, alpha_0=1.0, beta_0=1.0):
    """
    Degrades a line image by adding noise.

    For parameter meanings consult [1].

    Args:
        im (PIL.Image): Input image
        eta (float):
        alpha (float):
        beta (float):
        alpha_0 (float):
        beta_0 (float):

    Returns:
        PIL.Image in mode '1'
    """
    logger.debug('Inverting and normalizing input image')
    im = pil2array(im)
    im = np.amax(im) - im
    im = im * 1.0 / np.amax(im)

    logger.debug('Calculating foreground distance transform')
    fg_dist = distance_transform_cdt(1 - im, metric='taxicab')
    logger.debug('Calculating flip to white probability')
    fg_prob = alpha_0 * np.exp(-alpha * (fg_dist**2)) + eta
    fg_prob[im == 1] = 0
    fg_flip = np.random.binomial(1, fg_prob)

    logger.debug('Calculating background distance transform')
    bg_dist = distance_transform_cdt(im, metric='taxicab')
    logger.debug('Calculating flip to black probability')
    bg_prob = beta_0 * np.exp(-beta * (bg_dist**2)) + eta
    bg_prob[im == 0] = 0
    bg_flip = np.random.binomial(1, bg_prob)

    # flip
    logger.debug('Flipping')
    im -= bg_flip
    im += fg_flip

    logger.debug('Binary closing')
    sel = np.array([[1, 1], [1, 1]])
    im = binary_closing(im, sel)
    logger.debug('Converting to image')
    return array2pil(255 - im.astype('B') * 255)
Example #13
0
def degrade_line(im, eta=0.0, alpha=1.5, beta=1.5, alpha_0=1.0, beta_0=1.0):
    """
    Degrades a line image by adding noise.

    For parameter meanings consult [1].

    Args:
        im (PIL.Image): Input image
        eta (float):
        alpha (float):
        beta (float):
        alpha_0 (float):
        beta_0 (float):

    Returns:
        PIL.Image in mode '1'
    """
    logger.debug(u'Inverting and normalizing input image')
    im = pil2array(im)
    im = np.amax(im)-im
    im = im*1.0/np.amax(im)

    logger.debug(u'Calculating foreground distance transform')
    fg_dist = distance_transform_cdt(1-im, metric='taxicab')
    logger.debug(u'Calculating flip to white probability')
    fg_prob = alpha_0 * np.exp(-alpha * (fg_dist**2)) + eta
    fg_prob[im == 1] = 0
    fg_flip = np.random.binomial(1, fg_prob)

    logger.debug(u'Calculating background distance transform')
    bg_dist = distance_transform_cdt(im, metric='taxicab')
    logger.debug(u'Calculating flip to black probability')
    bg_prob = beta_0 * np.exp(-beta * (bg_dist**2)) + eta
    bg_prob[im == 0] = 0
    bg_flip = np.random.binomial(1, bg_prob)

    # flip
    logger.debug(u'Flipping')
    im -= bg_flip
    im += fg_flip

    logger.debug(u'Binary closing')
    sel = np.array([[1, 1], [1, 1]])
    im = binary_closing(im, sel)
    logger.debug(u'Converting to image')
    return array2pil(255-im.astype('B')*255)
Example #14
0
def Sampling(image_path):
    """
    :param image_path: path to image generated by get_paths
    :param patch_sizes: a list or array of patch sizes desired for convolution. Must be odd > 1.
    :param total_sample_size: sampled patches for convolution, and corresponding labels
    :return: sampled patches for convolution, and corresponding labels
    """

    # Load image from path
    Image = io.loadmat(image_path)
    # Extract total number of voxels
    Dims = np.prod(Image['scanROI'].shape)

    # Create 1 mask image
    Label = np.zeros(Image['scanROI'].shape)
    Label[Image['goldROI'] == 2] = 1

    Scan = Image["scanROI"]

    #max = np.array([0.9854,    0.7775,    0.6922])
    #min = np.array([0.4995,    0.1120,   0.0538])

    #xl, yl, zl =np.int32(np.around(np.multiply(Scan.shape, max)))
    #xu, yu, zu = np.int32(np.floor(np.multiply(Scan.shape, min)))

    #cropped = np.squeeze(np.where(Label[xu:xl, yu:yl, zu:zl] >= 0))
    #X = np.asarray([cropped[0] +xu, cropped[1]+yu , cropped[2] + zu])

    #   MM = np.ravel_multi_index(X, np.asarray(Scan.shape))
    B = morphology.distance_transform_cdt(np.absolute(Label - 1))
    B = B.reshape(Dims)
    #  Gold_ind = np.squeeze(np.where(Label == 1 ))

    #    X =  np.float32(range(np.max(B), 1, -1))/range(2, np.max(B)+1, 1)
    #    No_samples = np.shape(Gold_ind)[1]
    #    A = B[MM]
    #    Index = np.array([])
    #    samples = np.divide(X, sum(X))*No_samples
    #    for i in range(2, np.max(A)):
    #        Index = np.int32(np.append(Index, np.random.choice(np.squeeze(np.where(A == i)),(np.floor(samples)[i-1]), replace=False)))

    Label = Label.reshape(Dims)

    # Find indexes of voxels to sample
    Gold_ind = np.squeeze(np.where(Label == 1))
    Indeg = np.int32(
        np.random.choice(np.squeeze(Gold_ind), 1000, replace=False))

    P_ind = np.squeeze(np.where(np.logical_and(B > 0, B < 6)))
    Index = np.int32(np.random.choice(np.squeeze(P_ind), 1000, replace=False))
    Index = np.hstack((Index, Indeg))
    np.random.shuffle(Index)

    Inds = np.asarray(np.unravel_index(Index, Scan.shape, order='C'))
    Indie = Inds.reshape(3, 1, Inds.shape[1]).T
    Label_Sample = Label[Index]

    return Scan, Label_Sample, Inds.T
Example #15
0
def roi_track_counts(fdpy,fref,fatlas,roi_no,dist_transf=True,fres=None):
    
    dpr=Dpy(fdpy,'r')
    T=dpr.read_tracks()
    dpr.close()
    
    img=nib.load(fref)
    affine=img.get_affine()
    zooms = img.get_header().get_zooms()
    iaffine=np.linalg.inv(affine)
    T2=[]
    #go back to volume space
    for t in T:
        T2.append(np.dot(t,iaffine[:3,:3].T)+iaffine[:3,3])
    del T
    
    tcs,tes=track_counts(T2,img.get_shape(),zooms,True)
    
    atlas_img=nib.load(fatlas)
    atlas=atlas_img.get_data()
    roi=atlas.copy()
    roi[atlas!=roi_no]=0
    
    if dist_transf:
        roi2=distance_transform_cdt(roi)
        roi[roi2!=roi2.max()]=0
        I=np.array(np.where(roi==roi_no)).T
    else:
        I=np.array(np.where(roi==roi_no)).T    
    
    """    
    if erosion_level>0:
        roi2=binary_erosion(roi,cross,erosion_level)                 
        I=np.array(np.where(roi2==True)).T
    else:        
        roi2=distance_transform_cdt(roi)        
        I=np.array(np.where(roi==roi_no)).T        
    """
    
    #print I.shape    
    #nib.save(nib.Nifti1Image(roi2,affine),'/tmp/test.nii.gz')
    
    Ttes=[]
    for iroi in I:
        try:
            Ttes.append(tes[tuple(iroi)])
        except KeyError:
            pass
    
    Ttes=list(set(list(chain.from_iterable(Ttes))))    
    T2n=np.array(T2,dtype=np.object)
    res=list(T2n[Ttes])
    
    #back to world space
    res2=[]
    for t in res:
        res2.append(np.dot(t,affine[:3,:3].T)+affine[:3,3])
    np.save(fres,np.array(res2,dtype=np.object))
Example #16
0
def main():
    H, W = MAP()
    A = [INPUT() for _ in range(H)]

    # A : 2 次元 numpy 配列. 白マス -> True, 黒マス -> False
    A = np.array([[(Ai[j] != "#") for j in range(W)] for Ai in A])
    # distance_transform_cdt(X) : 2 次元 numpy 配列 X の要素 0 からの距離を要素に持つ 2 次元 numpy 配列を返す.
    # [metric] taxicab : マンハッタン距離 (4 近傍), chessboard : チェビシェフ距離 (8 近傍)
    print(distance_transform_cdt(A, metric="taxicab").max())
Example #17
0
def Sampling(image_path):
    """
    :param image_path: path to image generated by get_paths
    :param patch_sizes: a list or array of patch sizes desired for convolution. Must be odd > 1.
    :param total_sample_size: sampled patches for convolution, and corresponding labels
    :return: sampled patches for convolution, and corresponding labels
    """

    # Load image from path
    Image = io.loadmat(image_path)
    # Extract total number of voxels
    Dims = np.prod(Image['scanROI'].shape)

    # Create 1 mask image
    Label = np.zeros(Image['scanROI'].shape)
    #   Label[Image['classIm'] == 1] = 1
    Label[Image['classIm'] == 1] = 1

    if Image['isright'] == 0:

        Scan = np.fliplr(Image["scanROI"])
        Label = np.fliplr(Label)

    else:
        Scan = Image["scanROI"]

    Gold_ind = np.squeeze(np.where(Label == 1))

    B = morphology.distance_transform_cdt(np.absolute(Label - 1))
    B = B.reshape(Dims)
    X = np.float32(range(np.max(B), 1, -1)) / range(2, np.max(B) + 1, 1)
    No_samples = Gold_ind.shape[1]
    Index = np.array([])
    samples = np.divide(X, sum(X)) * No_samples

    for i in range(2, np.max(B)):

        Index = np.int32(
            np.append(
                Index,
                np.random.choice(np.squeeze(np.where(B == i)),
                                 (samples[i - 1]),
                                 replace=False)))

    Label = Label.reshape(Dims)

    # Find indexes of voxels to sample
    Gold_ind = np.squeeze(np.where(Label == 1))
    P_ind = np.squeeze(np.where(B > 1) & (B < 5))

    Index = np.hstack((Gold_ind, Index, P_ind))
    print Index.shape
    np.random.shuffle(Index)
    Inds = np.asarray(np.unravel_index(Index, Scan.shape, order='C'))

    Label_Sample = Label[Index]
    return Scan, Label_Sample, Inds.T
Example #18
0
def DistanceWithoutNormalise(bin_image):
    res = np.zeros_like(bin_image)
    for j in range(1, bin_image.max() + 1):
        one_cell = np.zeros_like(bin_image)
        one_cell[bin_image == j] = 1
        one_cell = distance_transform_cdt(one_cell)
        res[bin_image == j] = one_cell[bin_image == j]
    res = res.astype('uint8')
    return res
def DistanceWithoutNormalise(bin_image):
    res = np.zeros_like(bin_image)
    for j in range(1, bin_image.max() + 1):
        one_cell = np.zeros_like(bin_image)
        one_cell[bin_image == j] = 1
        one_cell = distance_transform_cdt(one_cell)
        res[bin_image == j] = one_cell[bin_image == j]
    res = res.astype('uint8')
    return res
Example #20
0
def process(inp):
    (indir, outdir, basename) = inp
    print(inp)
    labelmap = np.array(Image.open(osp.join(indir, basename)
                                   ).convert("P")).astype(np.int16)
    labelmap = _encode_label(labelmap)
    depth_map = np.zeros(labelmap.shape, dtype=np.float32)
    dir_map = np.zeros((*labelmap.shape, 2), dtype=np.float32)

    for id in range(255):
        labelmap_i = (labelmap == id).astype(np.uint8)

        if labelmap_i.sum() < 100:
            continue

        if args.metric == 'euc':
            depth_i = distance_transform_edt(labelmap_i)
        elif args.metric == 'taxicab':
            depth_i = distance_transform_cdt(labelmap_i, metric='taxicab')
        else:
            raise RuntimeError
        depth_map += depth_i

        dir_i_before = dir_i = np.zeros_like(dir_map)
        dir_i = torch.nn.functional.conv2d(torch.from_numpy(depth_i).float().view(
            1, 1, *depth_i.shape), sobel_ker, padding=ksize//2).squeeze().permute(1, 2, 0).numpy()

        # The following line is necessary
        dir_i[(labelmap_i == 0), :] = 0

        dir_map += dir_i

    depth_map[depth_map > 250] = 250
    depth_map = depth_map.astype(np.uint8)
    deg_reduce = 2
    dir_deg_map = np.degrees(np.arctan2(
        dir_map[:, :, 0], dir_map[:, :, 1])) + 180
    dir_deg_map = (dir_deg_map / deg_reduce)
    print(dir_deg_map.min(), dir_deg_map.max())
    dir_deg_map = dir_deg_map.astype(np.uint8)

    io.savemat(
        osp.join(outdir, basename.replace("png", "mat")),
        {"dir_deg": dir_deg_map, "depth": depth_map, 'deg_reduce': deg_reduce},
        do_compression=True,
    )

    try:
        io.loadmat(osp.join(outdir, basename.replace("png", "mat")),)
    except Exception as e:
        print(e)
        io.savemat(
            osp.join(outdir, basename.replace("png", "mat")),
            {"dir_deg": dir_deg_map, "depth": depth_map, 'deg_reduce': deg_reduce},
            do_compression=False,
        )
Example #21
0
def process(inp):
    (indir, outdir, basename) = inp
    print(inp)
    labelmap = np.array(Image.open(osp.join(indir, basename)))
    depth_map = np.ones(labelmap.shape) * 0
    dir_map = np.zeros((*labelmap.shape, 2))

    ignore_id_list = set(range(256)) - label_list

    for id in ignore_id_list:
        labelmap[labelmap == id] = 255

    labelmap_flattened = np.unique(labelmap)
    print(labelmap_flattened)

    for id in labelmap_flattened:
        labelmap_i = labelmap.copy()
        labelmap_i[labelmap != id] = 0
        labelmap_i[labelmap == id] = 1

        # if labelmap_i.sum() < 100:
        #     continue

        if args.metric == 'euc':
            depth_i = distance_transform_edt(labelmap_i)
        elif args.metric == 'taxicab':
            depth_i = distance_transform_cdt(labelmap_i, metric='taxicab')
        else:
            raise RuntimeError
        depth_map[labelmap_i == 1] = depth_i[labelmap_i == 1]

        dir_i_before = dir_i = np.zeros_like(dir_map)
        dir_i = torch.nn.functional.conv2d(
            torch.from_numpy(depth_i).float().view(1, 1, *depth_i.shape),
            sobel_ker,
            padding=ksize // 2).squeeze().permute(1, 2, 0).numpy()

        # The following line is necessary
        dir_i[(labelmap_i == 0), :] = 0

        dir_map += dir_i
    depth_map[depth_map > 250] = 250
    depth_map = depth_map.astype(np.uint8)
    # print(np.unique(depth_map))
    deg_reduce = 2
    dir_deg_map = np.degrees(np.arctan2(dir_map[:, :, 0], dir_map[:, :,
                                                                  1])) + 180
    dir_deg_map = (dir_deg_map / deg_reduce)
    print(dir_deg_map.min(), dir_deg_map.max())
    dir_deg_map = dir_deg_map.astype(np.uint8)
    dct = {
        "dir_deg": dir_deg_map,
        "depth": depth_map,
        'deg_reduce': deg_reduce
    }
    safe_savemat(osp.join(outdir, basename.replace("png", "mat")), dct)
Example #22
0
def get_skeleton(seg_fn, out_folder, bfs, res, edgTh, modified_bfs, opt):
    if opt == '0':  # mesh -> skeleton
        seg = ReadH5(seg_fn, 'main')
        CreateSkeleton(seg, out_folder, res, res)

    elif opt == '1':  # skeleton -> dense graph
        import networkx as nx
        print('read skel')
        skel = ReadSkeletons(out_folder,
                             skeleton_algorithm='thinning',
                             downsample_resolution=res,
                             read_edges=True)[1]

        print('save node positions')
        node_pos = np.stack(skel.get_nodes()).astype(int)
        WriteH5(out_folder + 'node_pos.h5', node_pos)

        print('generate dt for edge width')
        seg = ReadH5(seg_fn, 'main')
        sz = seg.shape
        bb = GetBbox(seg > 0)
        seg_b = seg[bb[0]:bb[1] + 1, bb[2]:bb[3] + 1, bb[4]:bb[5] + 1]
        dt = distance_transform_cdt(seg_b, return_distances=True)

        print('generate graph')
        new_graph, wt_dict, th_dict, ph_dict = GetGraphFromSkeleton(skel, dt=dt, dt_bb=[bb[x] for x in [0,2,4]],\
                                                       modified_bfs=modified_bfs)

        print('save as a networkx object')
        edge_list = GetEdgeList(new_graph, wt_dict, th_dict, ph_dict)
        G = nx.Graph(shape=sz)
        # add edge attributes
        G.add_edges_from(edge_list)
        nx.write_gpickle(G, out_folder + 'graph-%s.obj' % (bfs))

    elif opt == '2':  # reduced graph
        import networkx as nx
        G = nx.read_gpickle(out_folder + 'graph-%s.obj' % (bfs))

        n0 = len(G.nodes())
        G = ShrinkGraph_v2(G, threshold=edgTh)
        n1 = len(G.nodes())
        print('#nodes: %d -> %d' % (n0, n1))
        nx.write_gpickle(
            G,
            out_folder + 'graph-%s-%d-%d.obj' % (bfs, edgTh[0], 10 * edgTh[1]))
    elif opt == '3':  # generate h5 for visualization
        import networkx as nx
        G = nx.read_gpickle(out_folder + 'graph-%s-%d-%d.obj' %
                            (bfs, edgTh[0], 10 * edgTh[1]))
        pos = ReadH5(out_folder + 'node_pos.h5', 'main')
        vis = Graph2H5(G, pos)
        WriteH5(
            out_folder + 'graph-%s-%d-%d.h5' % (bfs, edgTh[0], 10 * edgTh[1]),
            vis)
def Symlink_Mask(r, dst_rgb, dst_mask):
    rgb = r['path_to_image']
    #print rgb
    mask_name = r['path_to_label']
    symlink(rgb, join(dst_rgb, basename(rgb)))
    mask = imread(mask_name)
    line = generate_wsl(mask)
    mask[mask > 0] = 1
    mask[line > 0] = 0
    mask = distance_transform_cdt(mask)
    imsave(join(dst_mask, basename(mask_name)), mask)
Example #24
0
def dt_image(shape, vertices, thresh=5):
    """
    Create a distance transform image given the size of
    the canvas and vertices of the polygon. Any distance
    less than the threshold is set to 0
    """
    mask = create_polygon(shape, vertices, perimeter=True)
    # Invert
    mask = 1 - mask
    dt_mask = distance_transform_cdt(mask, metric='taxicab').astype(np.float32)

    dt_mask[dt_mask < thresh] = 0
    return dt_mask
def DistanceBinNormalise(bin_image):
    bin_image = label(bin_image)
    result = np.zeros_like(bin_image, dtype="float")

    for k in range(1, bin_image.max() + 1):
        tmp = np.zeros_like(result, dtype="float")
        tmp[bin_image == k] = 1
        dist = distance_transform_cdt(tmp)
        MAX = dist.max()
        dist = dist.astype(float) / MAX
        result[bin_image == k] = dist[bin_image == k]
    result = result * 255
    result = result.astype('uint8')
    return result
Example #26
0
def DistanceBinNormalise(bin_image):
    bin_image = label(bin_image)
    result = np.zeros_like(bin_image, dtype="float")

    for k in range(1, bin_image.max() + 1):
        tmp = np.zeros_like(result, dtype="float")
        tmp[bin_image == k] = 1
        dist = distance_transform_cdt(tmp)
        MAX = dist.max()
        dist = dist.astype(float) / MAX
        result[bin_image == k] = dist[bin_image == k]
    result = result * 255
    result = result.astype('uint8')
    return result
Example #27
0
def dist_image(imf):
    '''
    Return the distance image 'imd' of 'imf'
    imd[r,c] is the distance  of pixel (r,c) to the closest edge pixel.
    '''
    imf_inv = 1 - (skimage.img_as_ubyte(imf) != 0)
    imd = distance_transform_cdt(imf_inv)
#    imd = cv2.distanceTransform(imf_inv ,cv2.DIST_L2,5)
#    plt.imshow(imf_inv)
#    plt.title('imf_inv')
#    plt.figure()
#    plt.imshow(imd)
#    plt.title('imd')
#    plt.colorbar()
    return imd
Example #28
0
def stringify_board(board):
    t = {0: '*', 1: '.', 2: '.', 3: 'c'}
    stringified_board = ['']

    dt = distance_transform_cdt(board)
    try:
        candidate = np.argwhere(dt > 1)[0]
        board[candidate[0], candidate[1]] = 3
    except IndexError:
        board[0, 0] = 3

    for l in board:
        stringified_board.append("".join([t[e] for e in l]))

    return "\n".join(stringified_board)
Example #29
0
    def _augment(self, img, _):
        img = np.copy(img)
        orig_ann = img[..., 0]  # instance ID map
        fixed_ann = self._fix_mirror_padding(orig_ann)
        # re-cropping with fixed instance id map
        crop_ann = cropping_center(fixed_ann, self.crop_shape)

        orig_dst = np.zeros(orig_ann.shape, dtype=np.float32)

        inst_list = list(np.unique(crop_ann))
        inst_list.remove(0)  # 0 is background
        for inst_id in inst_list:
            inst_map = np.array(fixed_ann == inst_id, np.uint8)
            inst_box = bounding_box(inst_map)

            # expand the box by 2px
            inst_box[0] -= 2
            inst_box[2] -= 2
            inst_box[1] += 2
            inst_box[3] += 2

            inst_map = inst_map[inst_box[0]:inst_box[1],
                                inst_box[2]:inst_box[3]]

            if inst_map.shape[0] < 2 or \
                inst_map.shape[1] < 2:
                continue

            # chessboard distance map generation
            # normalize distance to 0-1
            inst_dst = distance_transform_cdt(inst_map)
            inst_dst = inst_dst.astype('float32')
            if self.inst_norm:
                max_value = np.amax(inst_dst)
                if max_value <= 0:
                    continue  # HACK: temporay patch for divide 0 i.e no nuclei (how?)
                inst_dst = (inst_dst / np.amax(inst_dst))

            ####
            dst_map_box = orig_dst[inst_box[0]:inst_box[1],
                                   inst_box[2]:inst_box[3]]
            dst_map_box[inst_map > 0] = inst_dst[inst_map > 0]

        #
        img = img.astype('float32')
        img = np.dstack([img, orig_dst])

        return img
Example #30
0
def dt_targets_from_class(poly, grid_size, dt_threshold):
    #  grid_size: 28
    #  poly: 即Ground Truth, [bs, seq_len], 每行一个poly, seq_len个值, 每个值是在grid*gird中的index
    """
    NOTE: numpy function!
    poly: [bs, time_steps], each value in [0, grid*size**2+1)
    grid_size: size of the grid the polygon is in
    dt_threshold: threshold for smoothing in dt targets

    returns:
    full_targets: [bs, time_steps, grid_size**2+1] array containing
    dt smoothed targets to be used for the polygon loss function
    """
    full_targets = []
    for b in range(poly.shape[0]):
        targets = []
        for p in poly[b]:
            t = np.zeros(grid_size**2 + 1, dtype=np.int32)
            t[p] += 1

            if p != grid_size**2:  #EOS
                spatial_part = t[:-1]
                spatial_part = np.reshape(spatial_part,
                                          [grid_size, grid_size, 1])

                # Invert image
                spatial_part = -1 * (spatial_part - 1)
                # Compute distance transform
                spatial_part = distance_transform_cdt(spatial_part,
                                                      metric='taxicab').astype(
                                                          np.float32)
                # Threshold
                spatial_part = np.clip(spatial_part, 0, dt_threshold)
                # Normalize
                spatial_part /= dt_threshold
                # Invert back
                spatial_part = -1. * (spatial_part - 1.)

                spatial_part /= np.sum(spatial_part)
                spatial_part = spatial_part.flatten()

                t = np.concatenate([spatial_part, [0.]], axis=-1)

            targets.append(t.astype(np.float32))
        full_targets.append(targets)

    return np.array(full_targets, dtype=np.float32)
Example #31
0
    def from_t2(t2, mask, data_img, split, edge_n=None, grp_target=None):
        """ scales effect with observations

        Args:
            t2 (float): ratio of effect to population variance
            mask (Mask): effect location
            data_img (DataImage):
            grp_target: grp that effect is applied to
            split (dict): keys are grp labels, values are sbj_list
            edge_n (int): number of voxels on edge of mask (taxicab erosion)
                          which have a 'scaled' effect.  For example, if edge_n
                          = 1, then the outermost layer of voxels has only half
                          the delta applied.  see Effect.scale
        """
        # get grp_tuple, grp_tuple[1] has effect applied
        assert len(split) == 2, 'invalid split'
        grp_tuple = sorted(split.keys())
        if grp_target is not None:
            assert grp_target in grp_tuple
            if grp_target is not grp_tuple[1]:
                grp_tuple = reversed(grp_tuple)

        # get fs_dict
        fs_dict = dict()
        for grp, sbj_list in split.items():
            fs_dict[grp] = data_img.get_fs(sbj_list=sbj_list, mask=mask)

        delta, cov_pool, _, _ = get_t2_stats(fs_dict, grp_tuple=grp_tuple)

        # compute scale
        if edge_n is None:
            scale = mask
        else:
            scale = distance_transform_cdt(mask,
                                           metric='taxicab') / (edge_n + 1)
            scale[scale >= 1] = 1

        # build effect with proper direction
        # todo: refactor all pool_cov -> cov_pool
        eff = EffectConstant(mask=mask,
                             delta=delta,
                             pool_cov=cov_pool,
                             scale=scale)

        # scale to given t2
        return eff.to_t2(t2)
def fitAndDivide(roi_mask, brain_mask, t_map, nii, workdir, iteration):
    if not os.path.exists(workdir + str(iteration)):
        os.mkdir(workdir + str(iteration))
        
    flat_data = t_map[roi_mask].ravel().squeeze()
    
    (flat_active, selected_model) = fitMixture(flat_data)
    
    print "winning model: %s"%selected_model    
    
    active_map = np.zeros(t_map.shape) == 1
    active_map[roi_mask] = flat_active

    new_img = nb.Nifti1Image(active_map, nii.get_affine(), nii.get_header())
    nb.save(new_img, os.path.join(workdir + str(iteration), 'thr_map.nii')) 
    
    plt.show()
    
    #26 connectivity
    labeled_map, n_regions = label(active_map, generate_binary_structure(3,3))
    
    print "%d disconnected regions"%n_regions
    
    distances, indices = distance_transform_cdt(np.logical_not(active_map), metric='chessboard', return_distances=True, return_indices=True)
    
    new_img = nb.Nifti1Image(distances, nii.get_affine(), nii.get_header())
    nb.save(new_img, os.path.join(workdir+ str(iteration), 'distances.nii')) 
    
    labels = labeled_map[indices[0,:], indices[1,:], indices[2,:]]
    
    for region_id in range(1,n_regions+1):
        sub_roi_mask = np.zeros(t_map.shape) == 1     
        sub_roi_mask[np.logical_and(labels == region_id, roi_mask)] = True
        
        activity_within_roi = (labeled_map == region_id)
        
        new_img = nb.Nifti1Image(sub_roi_mask, nii.get_affine(), nii.get_header())
        nb.save(new_img, os.path.join(workdir+ str(iteration), 'sub%d_roi_mask.nii'%region_id))
        
        new_sub_roi_mask = extendROIMask(sub_roi_mask, activity_within_roi, brain_mask)
        new_img = nb.Nifti1Image(new_sub_roi_mask, nii.get_affine(), nii.get_header())
        nb.save(new_img, os.path.join(workdir+ str(iteration), 'new_sub%d_roi_mask.nii'%region_id))
        
        if new_sub_roi_mask != sub_roi_mask:
            fitAndDivide(new_sub_roi_mask, brain_mask, t_map, nii, workdir += , iteration+1)
Example #33
0
def find_seed_centroids(stack):
    """Find seeds in given binary stack, returning dictionary containing seed
    centroids."""

    distances = distance_transform_cdt(stack)
    seeds = distances > 10
    seed_label_array = label(seeds)

    seed_labels = {}
    for seed_label in np.unique(seed_label_array):
        float_center = map(np.mean, np.where(seed_label_array==seed_label))
        int_center = map(int, float_center)

        seed_name = 'seed{}'.format(seed_label)

        seed_labels[seed_name] = int_center

    return seed_labels
Example #34
0
def get_roi_new(roi_no,dist_transf=False): 

    atlas_img=nib.load(fatlas)
    atlas=atlas_img.get_data()
    roiaff=atlas_img.get_affine()
    roi=atlas.copy()
    roi[atlas!=roi_no]=0
    
    if dist_transf:
        roi2=distance_transform_cdt(roi)
        roi[roi2!=roi2.max()]=0
        I=np.array(np.where(roi==roi_no)).T
    else:
        I=np.array(np.where(roi==roi_no)).T
        
    wI=np.dot(roiaff[:3,:3],I.T).T+roiaff[:3,3]
    wI=wI.astype('f4')
    
    return wI
Example #35
0
def spike_dt(data_file):

    pm = PathManager(data_file, working_base='/localscratch/ct_analysis')
    stack_path = pm.spath('raw_stack.stack')

    z = 273
    filename = "z{}.png".format(z)
    full_path = os.path.join(stack_path, filename)

    plane = Image.from_file(full_path)

    threshold = threshold_otsu(plane)
    thresholded = plane > threshold
    imsave('t.png', thresholded)
    dt = distance_transform_cdt(thresholded)
    imsave('dt.png', dt)
    print dt.max()
    tdt = dt > 70

    imsave('tdt.png', tdt)
Example #36
0
def Sampling(image_path):
    """
    :param image_path: path to image generated by get_paths
    :param patch_sizes: a list or array of patch sizes desired for convolution. Must be odd > 1.
    :param total_sample_size: sampled patches for convolution, and corresponding labels
    :return: sampled patches for convolution, and corresponding labels
    """
    Image = io.loadmat(image_path)
    Scan = Image['Scan']
    Label = np.zeros(Image['Label'].shape)
    Label[Image['Label'] > 0] = 1
    Gold_ind = np.squeeze(np.where(Label >= 1))
    Dims = np.prod(Scan.shape)
    B = morphology.distance_transform_cdt(np.absolute(Label - 1))
    B = B.reshape(Dims)
    X = np.float32(range(np.max(B), 1, -1)) / range(2, np.max(B) + 1, 1)
    No_samples = Gold_ind.shape[1]

    Index = np.array([])
    samples = np.divide(X, sum(X)) * No_samples

    for i in range(2, np.max(B)):
        Index = np.int32(
            np.append(
                Index,
                np.random.choice(np.squeeze(np.where(B == i)),
                                 (samples[i - 1]),
                                 replace=False)))

    Label = Label.reshape(Dims)
    Gold_ind = np.squeeze(np.where(Label >= 1))
    # Find indexes of voxels to sample
    P_ind = np.squeeze(np.where(B == 1))
    Index = np.hstack((Gold_ind, Index, P_ind))
    np.random.shuffle(Index)
    Inds = np.asarray(np.unravel_index(Index, Scan.shape, order='C'))
    Label_Sample = Label[Index]
    return Scan, Label_Sample, Inds.T
Example #37
0
    def _get_surface_distance(self, y_pred_edges, y_edges):
        """
        Calculate the surface distances from `y_pred_edges` to `y_edges`.

         Args:
            y_pred_edges (np.ndarray): the edge of the predictions.
            y_edges (np.ndarray): the edge of the ground truth.
        """

        if not np.any(y_pred_edges):
            return np.array([])

        if not np.any(y_edges):
            dis = np.inf * np.ones_like(y_edges)
        else:
            if self.distance_metric == "euclidean":
                dis = morphology.distance_transform_edt(~y_edges)
            elif self.distance_metric == "chessboard" or self.distance_metric == "taxicab":
                dis = morphology.distance_transform_cdt(~y_edges, metric=self.distance_metric)

        surface_distance = dis[y_pred_edges]

        return surface_distance
    def _get_surface_distance(self, y_pred_edges, y_edges):
        """
        Calculate the surface distances from `y_pred_edges` to `y_edges`.

         Args:
            y_pred_edges (np.ndarray): the edge of the predictions.
            y_edges (np.ndarray): the edge of the ground truth.
        """

        if not np.any(y_pred_edges):
            return np.array([])

        if not np.any(y_edges):
            dis = np.full(y_edges.shape, np.inf)
        else:
            if self.distance_metric == "euclidean":
                dis = morphology.distance_transform_edt(~y_edges)
            elif self.distance_metric in self.distance_metric_list[-2:]:
                dis = morphology.distance_transform_cdt(~y_edges, metric=self.distance_metric)

        surface_distance = dis[y_pred_edges]

        return surface_distance
Example #39
0
    print('read skel')
    skel = ReadSkeletons(out_folder,
                         skeleton_algorithm='thinning',
                         downsample_resolution=res,
                         read_edges=True)[1]

    print('save node positions')
    node_pos = np.stack(skel.get_nodes()).astype(int)
    WriteH5(out_folder + 'node_pos.h5', node_pos)

    print('generate dt for edge width')
    seg = ReadH5(seg_fn, 'main')
    sz = seg.shape
    bb = GetBbox(seg > 0)
    seg_b = seg[bb[0]:bb[1] + 1, bb[2]:bb[3] + 1, bb[4]:bb[5] + 1]
    dt = distance_transform_cdt(seg_b, return_distances=True)

    print('generate graph')
    new_graph, wt_dict, th_dict, ph_dict = GetGraphFromSkeleton(skel, dt=dt, dt_bb=[bb[x] for x in [0,2,4]],\
                                                   modified_bfs=modified_bfs)

    print('save as a networkx object')
    edge_list = GetEdgeList(new_graph, wt_dict, th_dict, ph_dict)
    G = nx.Graph(shape=sz)
    # add edge attributes
    G.add_edges_from(edge_list)
    nx.write_gpickle(G, out_folder + 'graph-%s.obj' % (bfs))

elif opt == '2':  # reduced graph
    import networkx as nx
    G = nx.read_gpickle(out_folder + 'graph-%s.obj' % (bfs))
Example #40
0
 def dthresh(image):
     thresh = threshold_otsu(image)
     thresholded = image > thresh
     dt = distance_transform_cdt(thresholded)
     return dt > 18
Example #41
0
def dt_and_threshold(input_plane, threshold=75):
    t_otsu = threshold_otsu(input_plane)
    thresholded = input_plane > t_otsu
    dt = distance_transform_cdt(thresholded)

    return dt > threshold
def compact(im_label, compaction=3):
    """Performs a thinning operation on a label image to remove thin
    protrusions from objects that are in contact with the background. Applies a
    distance transform and sequentially removes pixels with small distances to
    background that are not connected to higher distance pixels.

    Parameters
    ----------
    im_label : array_like
        A labeled segmentation mask
    compaction : int
        Factor used in compacting objects to remove thin protrusions. Refered
        to as d in the reference below. Default value = 3.

    Notes
    -----
    Implemented from the reference below.

    Returns
    -------
    im_compact : array_like
        A labeled segmentation mask with thin protrusions removed.

    See Also
    --------
    histomicstk.segmentation.label.area_open,
    histomicstk.segmentation.label.condense,
    histomicstk.segmentation.label.shuffle,
    histomicstk.segmentation.label.split,
    histomicstk.segmentation.label.width_open

    References
    ----------
    .. [1] S. Weinert et al "Detection and Segmentation of Cell Nuclei in
           Virtual Microscopy Images: A Minimum-Model Approach" in Nature
           Scientific Reports,vol.2,no.503, doi:10.1038/srep00503, 2012.
    """

    # copy input image
    im_compact = im_label.copy()

    # generate distance map of label image
    D = mp.distance_transform_cdt(im_compact > 0, metric='taxicab')

    # define 4-neighbors filtering kernel
    Kernel = np.zeros((3, 3), dtype=np.bool)
    Kernel[1, :] = True
    Kernel[:, 1] = True

    # sweep over distance values from d-1 to 1
    for i in np.arange(compaction-1, 0, -1):

        # four-neighbor maxima of distance transform
        MaxD = ft.maximum_filter(D, footprint=Kernel)

        # identify pixels whose max 4-neighbor is less than i+1
        Decrement = (D == i) & (MaxD < i+1)

        # decrement non-compact pixels
        D[Decrement] -= 1

    # zero label pixels where D == 0
    im_compact[D == 0] = 0

    return im_compact
def width_open(im_label, width):
    """Removes thin objects from label image using maximum of distance
    transform values within each object.

    Parameters
    ----------
    im_label : array_like
        A uint32 type label image generated by segmentation methods.
    width : int
        width threshold for objects. Objects with fewer than 'Area' pixels will
        be zeroed to merge with background.

    Notes
    -----
    Objects are assumed to have positive nonzero values. A binary mask is
    generated for each object setting all other objects to the background value
    (0). The maximum chamfered distance transform value of this mask is used
    to represent the object width.

    Returns
    -------
    im_thinned : array_like
        A uint32 label where objects with pixels < Area are removed.

    See Also
    --------
    histomicstk.segmentation.label.condense,
    histomicstk.segmentation.label.shuffle,
    histomicstk.segmentation.label.split,
    histomicstk.segmentation.label.area_open
    """

    # copy input image
    im_thinned = im_label.copy()

    # condense label image
    if np.unique(im_thinned).size-1 != im_thinned.max():
        im_thinned = condense(im_thinned)

    # get locations of objects in initial label image
    Locations = ms.find_objects(im_thinned)

    # iterate through objects, calculating distances where needed
    for i in np.arange(1, len(Locations)+1):

        # extract object from label image
        W = im_thinned[Locations[i-1]]

        # embed into mask with boundary
        Mask = np.zeros((W.shape[0]+2, W.shape[1]+2), dtype=np.bool)
        Mask[1:-1, 1:-1] = W == i

        # calculate distance transform of mask
        D = mp.distance_transform_cdt(Mask, metric='taxicab')

        # get max distance
        Max = D.max()

        # zero label mask of object 'i'
        if Max < width:
            W[W == i] = 0

    # condense to fill gaps
    im_thinned = condense(im_thinned)

    return im_thinned