Пример #1
0
    def __init__(self,
                 img,
                 seed=(0, 0, 0),
                 alpha=1000,
                 sigma=5.48,
                 smoothing=1,
                 threshold=0.31,
                 balloon=5):
        import morphsnakes
        self.alpha = alpha
        self.sigma = sigma
        self.smoothing = smoothing
        self.threshold = threshold
        self.balloon = balloon
        self.img = img
        self.seed = seed
        # self.gI = morphsnakes.gborders(img, alpha=alpha, sigma=sigma)
        # self.mgac = morphsnakes.MorphGAC(self.gI, smoothing=smoothing, threshold=threshold, balloon=balloon)
        # self.last_levelset = self.mgac.levelset = circle_levelset(img.shape, seed, balloon)

        self.macwe = morphsnakes.MorphACWE(img,
                                           smoothing=1,
                                           lambda1=1,
                                           lambda2=2)
        self.last_levelset = self.macwe.levelset = circle_levelset(
            img.shape, seed, balloon)

        self.last_crop_levelset = Crop_arr(self.last_levelset)
        self.iter_ind = 0
        self.max_balloon = balloon * 2
Пример #2
0
    def _autolevelset(self, startPoints="min"):
        # Convert the startPoints to a list if necessary
        if not isinstance(startPoints, list):
            startPoints = [startPoints for i in range(len(self.data))]

        self.macwes = list()
        for frame, img in enumerate(self.data):
            # Set up the image
            macwe = morphsnakes.MorphACWE(img, smoothing=self.smoothing ,\
                                               lambda1=self.lambda1     ,\
                                               lambda2=self.lambda2
                                         )
            macwe.frame = frame

            if startPoints[frame] == "min":
                x, y = np.where(img == np.min(img))
            elif startPoints[frame] == "max":
                x, y = np.where(img == np.max(img))
            else:
                raise ValueError(
                    "startPoint %r not understood. It must be either min or max"
                    % startPoints[frame])
            macwe.levelset = circle_levelset(img.shape, (x[0], y[0]), 10)

            self.macwes.append(macwe)
Пример #3
0
def example_confocal3d():

    # Load the image.
    img = np.load("testimages/confocal.npy")

    # Morphological ACWE. Initialization of the level-set.
    macwe = morphsnakes.MorphACWE(img, smoothing=1, lambda1=1, lambda2=2)
    macwe.levelset = circle_levelset(img.shape, (30, 50, 80), 25)
Пример #4
0
def test_confocal3d():

    # Load the image.
    img = np.load("testimages/confocal.npy")

    # Morphological ACWE. Initialization of the level-set.
    macwe = morphsnakes.MorphACWE(img, smoothing=1, lambda1=1, lambda2=2)
    macwe.levelset = circle_levelset(img.shape, (30, 50, 80), 25)

    # Visual evolution.
    morphsnakes.evolve_visual3d(macwe, num_iters=200)
Пример #5
0
    def step(self, iters=1):
        balloon = self.balloon
        img = self.img

        # self.mgac.step()
        # self.last_levelset = self.mgac.levelset

        # self.macwe.step()
        # self.last_levelset = self.macwe.levelset
        # return

        # Coordinates of non-black pixels.
        coords = np.argwhere(self.last_levelset)

        # Bounding box of non-black pixels.
        try:
            x0, y0, z0 = coords.min(axis=0)
            x1, y1, z1 = coords.max(
                axis=0) + 1  # slices are exclusive at the top
        except:
            return
        bounds = [x0, y0, z0, x1, y1, z1]
        crop_bounds = []
        for i, c in enumerate(bounds):
            c += balloon * (iters + 1) if i > 2 else balloon * (-1) * (iters +
                                                                       1)
            if i < 3:
                c = 0 if c < 0 else c
            else:
                s = img.shape[i - 3]
                c = s if c > s else c

            crop_bounds.append(c)
        # print(self.last_crop_levelset.shape)
        (x0, y0, z0, x1, y1, z1) = crop_bounds = tuple(crop_bounds)
        # gI = morphsnakes.gborders(img[x0:x1, y0:y1, z0:z1], alpha=self.alpha, sigma=self.sigma)
        # balloon = self.balloon

        macwe = morphsnakes.MorphACWE(img[x0:x1, y0:y1, z0:z1],
                                      smoothing=1,
                                      lambda1=1,
                                      lambda2=2)
        macwe.levelset = self.last_levelset[x0:x1, y0:y1, z0:z1]
        # balloon = self.balloon * random.uniform(0.5, 1.2)
        # mgac = morphsnakes.MorphGAC(gI, smoothing=self.smoothing, threshold=self.threshold, balloon=balloon)
        # mgac.levelset = self.last_levelset[x0:x1, y0:y1, z0:z1]
        for _ in range(iters):
            macwe.step()
        self.last_levelset[x0:x1, y0:y1, z0:z1] = macwe.levelset
        self.last_crop_levelset = Crop_arr(macwe.levelset, (x0, y0, z0),
                                           (x1, y1, z1))

        self.iter_ind += iters
Пример #6
0
def test_lakes():
    # Load the image.
    imgcolor = imread("testimages/lakes3.jpg") / 255.0
    img = rgb2gray(imgcolor)

    # MorphACWE does not need g(I)

    # Morphological ACWE. Initialization of the level-set.
    macwe = morphsnakes.MorphACWE(img, smoothing=3, lambda1=1, lambda2=1)
    macwe.levelset = circle_levelset(img.shape, (80, 170), 25)

    # Visual evolution.
    ppl.figure()
    morphsnakes.evolve_visual(macwe, num_iters=190, background=imgcolor)
Пример #7
0
def faz_level_set_3d(img, poligono, it):
    gmin, gmax = 0.0, 1.0
    fmin, fmax = img.min(), img.max()

    nimg = ((gmax - gmin) / (fmax - fmin)) * (img - fmin) + gmin

    macwe = morphsnakes.MorphACWE(nimg, smoothing=3, lambda1=1, lambda2=1)
    #macwe= morphsnakes.MorphGAC(nimg,smoothing=1,threshold=0.6, balloon=-2)
    macwe.levelset = poligono

    for i in xrange(it):
        macwe.step()

        yield macwe.levelset
def morph_snake(img, ROI, radius):
    #Input: Image to apply snake to as well as a region of interest and radius for the snake to initialize from and extend to
    #Output: Image of applied snake (including pixel values for mass outline)

    img1 = img / 255.0
    # g(I)
    gI = morphsnakes.gborders(img1, alpha=1000, sigma=11)

    mgac = morphsnakes.MorphACWE(img1, smoothing=3, lambda1=1, lambda2=1)
    mgac.levelset = circle_levelset(img1.shape, ROI, radius)

    pylab.figure(figsize=(8, 6), dpi=400)
    return (morphsnakes.evolve_visual(mgac, num_iters=30,
                                      background=img1))  #110
    pylab.show()
Пример #9
0
def _segment(image,
             lambda2,
             iterations,
             smoothing=1,
             sigma=None,
             threshold=None,
             init=None):
    """Segment a region of the image using morphological active contours"""
    if threshold is not None:
        image[image < threshold] = 0
    if sigma is not None:
        image = ndif.gaussian_filter(image, sigma=sigma)
    if init is None:
        init = np.zeros_like(image, dtype=np.uint8)
        bounds = np.ceil(np.array(image.shape) * 0.1).astype(int)
        init[[slice(b, -b) for b in bounds]] = 1
    macwe = morphsnakes.MorphACWE(image, smoothing=smoothing, lambda2=lambda2)
    macwe.levelset = init
    macwe.run(iterations)
    return macwe.levelset.astype(np.uint8)
Пример #10
0
def test_confocal3d(img):

    if False:
        macwe = morphsnakes.MorphGAC(img,
                                     smoothing=1,
                                     threshold=0.3,
                                     balloon=-1)
    else:
        macwe = morphsnakes.MorphACWE(img, smoothing=1, lambda1=1, lambda2=2)
    macwe.levelset = circle_levelset(img.shape, (30, 50, 80), 25)

    if True:
        # Visual evolution.
        morphsnakes.evolve_visual3d(macwe, num_iters=200)
    else:
        fig = ppl.figure(frameon=False)
        for i in range(100):
            macwe.step()
            fd = macwe.levelset
            ppl.imshow(np.max(img, axis=2), cmap='gray')
            ppl.imshow(np.max(fd[:, :, :], axis=2), cmap='jet', alpha=0.3)
            ppl.show()
            ppl.pause(.1)
            ppl.draw()
def snake(img,
          init_submasks=None,
          init_contours=None,
          lambda1=MORPHSNAKE_LAMBDA1,
          return_masks=True,
          min_size=MIN_SUBMASK_SIZE,
          crop_margin=50):
    """
    Args:
        crop_margin (int): crop the image to fit the extent of all masks plus a margin of `margin`. Set to negative for no cropping.
    """

    if init_contours is None:
        assert init_submasks is not None, "If no init_contours is given, must provide init_submasks."
        init_submasks = [
            m for m in init_submasks
            if np.count_nonzero(init_submasks) <= min_size
        ]
        assert len(
            init_submasks
        ) > 0, "No initial submasks are above the minimal area of %d." % min_size
        assert all([
            (m.shape[0] == img.shape[0]) & (m.shape[1] == img.shape[1])
            for m in init_submasks
        ]), "Not all shapes of initial submasks match the input image."
        bbox_all_submasks = [bbox_2d(m) for m in init_submasks]
        xmin, ymin = np.min(bbox_all_submasks[:, [0, 2]])
        xmax, ymax = np.max(bbox_all_submasks[:, [1, 3]])
    else:
        init_contours = [c.astype(np.int) for c in init_contours]
        xmin, ymin = np.min([np.min(c, axis=0) for c in init_contours], axis=0)
        xmax, ymax = np.max([np.max(c, axis=0) for c in init_contours], axis=0)

    # Crop to fit the extent.
    if crop_margin >= 0:
        crop_xmin = max(0, xmin - crop_margin)
        crop_ymin = max(0, ymin - crop_margin)
        crop_xmax = min(img.shape[1] - 1, xmax + crop_margin)
        crop_ymax = min(img.shape[0] - 1, ymax + crop_margin)
        cropped_img = img[crop_ymin:crop_ymax + 1, crop_xmin:crop_xmax + 1]
    else:
        crop_xmin = 0
        crop_ymin = 0
        crop_xmax = img.shape[1] - 1
        crop_ymax = img.shape[0] - 1
        cropped_img = img

    # Form initial levelsets
    init_levelsets = []
    if init_contours is None:
        for m in init_submasks:
            init_levelset = m[crop_ymin:crop_ymax + 1, crop_xmin:crop_xmax + 1]
            init_levelset[:10, :] = 0
            init_levelset[-10:, :] = 0
            init_levelset[:, :10] = 0
            init_levelset[:, -10:] = 0
            init_levelsets.append(init_levelset)
    else:
        for cnt in init_contours:
            init_contours_on_cropped_img = cnt - (crop_xmin, crop_ymin)
            init_levelset = contours_to_mask([init_contours_on_cropped_img],
                                             cropped_img.shape[:2])
            init_levelset[:10, :] = 0
            init_levelset[-10:, :] = 0
            init_levelset[:, :10] = 0
            init_levelset[:, -10:] = 0
            init_levelsets.append(init_levelset)

    sys.stderr.write('Found %d levelsets.\n' % len(init_levelsets))

    #####################
    # Evolve morphsnake #
    #####################

    final_masks = []

    for levelset_ind, init_levelset in enumerate(init_levelsets):
        sys.stderr.write('\nContour %d\n' % levelset_ind)

        discard = False
        init_area = np.count_nonzero(init_levelset)

        t = time.time()

        msnake = morphsnakes.MorphACWE(cropped_img.astype(np.float),
                                       smoothing=int(MORPHSNAKE_SMOOTHING),
                                       lambda1=lambda1,
                                       lambda2=MORPHSNAKE_LAMBDA2)
        msnake.levelset = init_levelset.copy()

        dq = deque([None, None])
        for i in range(MORPHSNAKE_MAXITER):
            # At stable stage, the levelset (thus contour) will oscilate,
            # so instead of comparing to previous levelset, must compare to the one before the previous
            oneBefore_levelset = dq.popleft()

            # If less than 3 pixels are changed, stop.
            if i > MORPHSNAKE_MINITER:
                if np.count_nonzero(msnake.levelset - oneBefore_levelset
                                    ) < PIXEL_CHANGE_TERMINATE_CRITERIA:
                    break

            # area = np.count_nonzero(msnake.levelset)
            # if area < min_size:
            #     discard = True
            #     sys.stderr.write('Too small, stop iteration.\n')
            #     break

            labeled_mask = label(msnake.levelset.astype(np.bool))
            component_sizes = []
            for l in np.unique(labeled_mask):
                if l != 0:
                    m = labeled_mask == l
                    component_area = np.count_nonzero(m)
                    component_sizes.append(component_area)
                    if component_area / float(
                            init_area) > AREA_CHANGE_RATIO_MAX:
                        msnake.levelset[m] = 0
                        sys.stderr.write(
                            'Component area expands too much - nullified.\n')
                    elif component_area < min_size:
                        msnake.levelset[m] = 0
                        sys.stderr.write(
                            'Component area is too small - nullified.\n')
            print component_sizes

            if np.count_nonzero(msnake.levelset) / float(
                    init_area) < AREA_CHANGE_RATIO_MIN:
                discard = True
                sys.stderr.write('Area shrinks too much, stop iteration.\n')
                break

            dq.append(msnake.levelset)

            #         t = time.time()
            msnake.step()
    #         sys.stderr.write('Step: %f seconds\n' % (time.time()-t)) # 0.6 second/step, roughly 200 steps takes 120s

        sys.stderr.write('Snake finished at iteration %d.\n' % i)
        sys.stderr.write('Snake: %.2f seconds\n' % (time.time() - t))  # 72s

        if discard:
            sys.stderr.write('Discarded.\n')
            continue
        else:
            # Handles the case that a single initial contour morphs into multiple contours
            labeled_mask = label(msnake.levelset.astype(np.bool))
            for l in np.unique(labeled_mask):
                if l != 0:
                    m = labeled_mask == l
                    if np.count_nonzero(m) > min_size:
                        final_masks.append(m)
                        sys.stderr.write('Final masks added.\n')

    if len(final_masks) == 0:
        sys.stderr.write('Snake return no valid submasks.\n')
        return []

    if return_masks:
        final_masks_uncropped = []
        for m in final_masks:
            uncropped_mask = np.zeros(img.shape[:2], np.bool)
            uncropped_mask[crop_ymin:crop_ymax + 1,
                           crop_xmin:crop_xmax + 1] = m
            final_masks_uncropped.append(uncropped_mask)
        return final_masks_uncropped
    else:
        final_contours = []
        for m in final_masks:
            cnts = [
                cnt_on_cropped + (crop_xmin, crop_ymin)
                for cnt_on_cropped in find_contour_points(m)[1]
            ]
            final_contours += cnts
        return final_contours
Пример #12
0
vel = np.zeros((num_vessels, nfrms))

print('coords = ', coords)

fig = ppl.figure(num=32, figsize=(16, 16))
fig.clf()
ax1 = ppl.subplot(2, 2, 1)
ax1.set_xticks([])
ax1.set_yticks([])
ppl.imshow(myimg, cmap=ppl.cm.gray, interpolation='bicubic')

masksum = np.zeros((nrows, ncols))

for ptnum, pt in enumerate(coords):
    # Morphological ACWE. Initialization of the level-set.
    macwe = morphsnakes.MorphACWE(myimg, smoothing=1, lambda1=1, lambda2=2)
    macwe.levelset = circle_levelset(myimg.shape, pt, 2)

    # Visual evolution.
    numiter = 10
    print('Snaking at (%d,%d)...' % (pt))

    # finalset = morphsnakes.evolve_visual(macwe, num_iters=numiter, background=myimg)
    ax1.contour(macwe.levelset, [0.5], colors='r')

    ax2 = ppl.subplot(2, 2, 2)
    ax2.set_xticks([])
    ax2.set_yticks([])
    ax2b = ppl.imshow(macwe.levelset)

    # Iterate.
        #         for _ in range(3):
        #             scoremap_thresholded_padded = binary_erosion(scoremap_thresholded_padded, disk(3))

        scoremap_thresholded_padded = remove_small_holes(
            scoremap_thresholded_padded, 1000)
        #         scoremap_thresholded_padded = remove_small_objects(scoremap_thresholded_padded, 100)
        scoremap_thresholded = scoremap_thresholded_padded[50:-50, 50:-50][:]

        init_levelset = np.zeros((roi_height, roi_width))
        init_levelset[inside_points_inroi[:, 1], inside_points_inroi[:,
                                                                     0]] = 1.

        t = time.time()

        msnake = morphsnakes.MorphACWE(scoremap_thresholded.astype(np.float),
                                       smoothing=smoothing,
                                       lambda1=1.,
                                       lambda2=1.)

        msnake.levelset = init_levelset.copy()
        # levelset values are either 1.0 or 0.0

        dq = deque([None, None])
        for i in range(1000):

            # at stable stage, the levelset (thus contour) will oscilate,
            # so instead of comparing to previous levelset, must compare to the one before the previous
            oneBefore_levelset = dq.popleft()

            if i > 10:
                #                 print np.count_nonzero(msnake.levelset - oneBefore_levelset)
                if np.count_nonzero(msnake.levelset - oneBefore_levelset) < 3:
Пример #14
0
def generate_mask(fn, tb_fmt='png'):

    try:
        submask_dir = create_if_not_exists(os.path.join(output_dir, fn))
        execute_command('rm -f %s/*' % submask_dir)

        img_rgb = imread(
            os.path.join(input_dir,
                         '%(fn)s.%(tb_fmt)s' % dict(fn=fn, tb_fmt=tb_fmt)))

        stds = []
        images = []

        # for c in range(3):
        for c in [0]:

            img = img_rgb[..., c].copy()

            border = np.median(np.r_[img[:10, :].flatten(),
                                     img[-10:, :].flatten(),
                                     img[:, :10].flatten(),
                                     img[:, -10:].flatten()])

            if border < 123:
                # dark background, fluorescent
                img = img.max(
                ) - img  # invert, make tissue dark on bright background

            # Stretch contrast
            img_flattened = img.flatten()

            vmax_perc = VMAX_PERCENTILE
            while vmax_perc > 80:
                vmax = np.percentile(img_flattened, vmax_perc)
                if vmax < 255:
                    break
                else:
                    vmax_perc -= 1

            vmin_perc = VMIN_PERCENTILE
            while vmin_perc < 20:
                vmin = np.percentile(img_flattened, vmin_perc)
                if vmin > 0:
                    break
                else:
                    vmin_perc += 1

            sys.stderr.write('%d(%d percentile), %d(%d percentile)\n' %
                             (vmin, vmin_perc, vmax, vmax_perc))

            img = img_as_ubyte(rescale_intensity(img, in_range=(vmin, vmax)))

            # img[(img <= vmax) & (img >= vmin)] = 255./(vmax-vmin)*(img[(img <= vmax) & (img >= vmin)]-vmin)
            # img[img > vmax] = 255
            # img[img < vmin] = 0
            # img = img.astype(np.uint8)

            images.append(img)

            std = np.std(img)
            stds.append(std)

            sys.stderr.write('std: %.2f\n' % (std))

        best_channel_id = np.argmax(stds)
        sys.stderr.write('Use channel %s.\n' %
                         ['RED', 'GREEN', 'BLUE'][best_channel_id])
        img = images[best_channel_id]

        #############################
        ## Graph cut based method. ##
        #############################

        # Input to slic() must be float
        slic_labels = slic(img.astype(np.float),
                           sigma=SLIC_SIGMA,
                           compactness=SLIC_COMPACTNESS,
                           n_segments=SLIC_N_SEGMENTS,
                           multichannel=False,
                           max_iter=SLIC_MAXITER)

        # sim_graph = rag_mean_color(img, slic_labels, mode='similarity', sigma=SUPERPIXEL_SIMILARITY_SIGMA)

        # Normalized cut - merge superpixels.

        # for _ in range(3):
        #     try:
        #         t = time.time()
        #         ncut_labels = cut_normalized(slic_labels, sim_graph, in_place=False, thresh=SUPERPIXEL_MERGE_SIMILARITY_THRESH,
        #                                     num_cuts=GRAPHCUT_NUM_CUTS)
        #         sys.stderr.write('Normalized Cut: %.2f seconds.\n' % (time.time() - t))
        #         break
        #     except ArpackError as e:
        #         sys.stderr.write('ArpackError encountered.\n')
        #         continue

        # ncut_boundaries_viz = mark_boundaries(img, label_img=ncut_labels, background_label=-1)
        # imsave(os.path.join(submask_dir, '%(fn)s_.png' % dict(fn=fn)), ncut_boundaries_viz)

        ncut_labels = slic_labels

        # Find background superpixels.

        background_labels = np.unique(
            np.concatenate([
                ncut_labels[:, 0], ncut_labels[:, -1], ncut_labels[0, :],
                ncut_labels[-1, :]
            ]))

        # Collect training superpixels.

        border_histos = []
        for b in background_labels:
            histo = np.histogram(img[ncut_labels == b],
                                 bins=np.arange(0, 256, 5))[0].astype(np.float)
            histo = histo / np.sum(histo)
            border_histos.append(histo)

        histos = {}
        for l in np.unique(ncut_labels):
            histo = np.histogram(img[ncut_labels == l],
                                 bins=np.arange(0, 256, 5))[0].astype(np.float)
            histo = histo / np.sum(histo)
            histos[l] = histo

        hist_distances = {}
        for l, h in histos.iteritems():
            hist_distances[l] = np.percentile(
                [chi2(h, th) for th in border_histos],
                BORDER_DISSIMILARITY_PERCENTILE)
            # min is too sensitive if there is a blob at the border

        if FOREGROUND_DISSIMILARITY_THRESHOLD is None:
            # Automatically determine this value

            dist_vals = np.asarray(hist_distances.values())
            ticks = np.linspace(0, dist_vals.max(), 100)
            percentages = [
                np.count_nonzero(dist_vals < th) / float(len(dist_vals))
                for th in ticks
            ]

            def moving_average(interval, window_size):
                window = np.ones(int(window_size)) / float(window_size)
                return np.convolve(interval, window, 'same')

            grad = np.gradient(percentages, 3)
            # smoothed_grad = moving_average(grad, 1)
            hessian = np.gradient(grad, 3)

            # plt.figure();
            # plt.plot(ticks, grad, label='grad');
            # plt.plot(ticks, smoothed_grad, label='smoothed');
            # plt.legend();
            # plt.xlabel('Chi2 distance');
            # plt.savefig(os.path.join(submask_dir, '%(fn)s_spDissimCumDistGradient.png' % dict(fn=fn)));
            # plt.close();

            # plt.figure();
            # plt.plot(ticks, h);
            # plt.title('Hessian - minima is the plateau point of cum. distr.');
            # plt.xlabel('Dissimlarity threshold');
            # plt.savefig(os.path.join(submask_dir, '%(fn)s_spDissimCumDistHessian.png' % dict(fn=fn)));
            # plt.close();

            # ticks_sorted = ticks[np.argsort(grad, kind='mergesort')]
            # ticks_sorted = ticks[np.argsort(smoothed_grad, kind='mergesort')]
            ticks_sorted = ticks[10:][hessian[10:].argsort()]
            ticks_sorted_reduced = ticks_sorted[
                ticks_sorted < FOREGROUND_DISSIMILARITY_THRESHOLD_MAX]

            init_contour_percentages = np.asarray([
                np.sum([
                    np.count_nonzero(ncut_labels == l)
                    for l, d in hist_distances.iteritems() if d > th
                ]) / float(img.size) for th in ticks_sorted_reduced
            ])

            threshold_candidates = ticks_sorted_reduced[(init_contour_percentages < INIT_CONTOUR_COVERAGE_MAX) &\
                                                              (init_contour_percentages > 0)]
            # np.savetxt(os.path.join(submask_dir, '%(fn)s_spThreshCandidates.txt' % dict(fn=fn)), threshold_candidates, fmt='%.3f')
            print threshold_candidates[:10]
            foreground_dissimilarity_threshold = threshold_candidates[0]
        else:
            foreground_dissimilarity_threshold = FOREGROUND_DISSIMILARITY_THRESHOLD

        sys.stderr.write('FOREGROUND_DISSIMILARITY_THRESHOLD: %.2f\n' %
                         foreground_dissimilarity_threshold)

        # Visualize superpixel border distance map
        superpixel_border_distances = np.zeros_like(img, np.float)
        for l, s in hist_distances.iteritems():
            superpixel_border_distances[ncut_labels == l] = s

        # plt.figure();
        # im = plt.imshow(superpixel_border_distances, vmin=0, vmax=2);
        # plt.title('Superpixels distance to border');
        # plt.colorbar(im, fraction=0.025, pad=0.02);
        # plt.savefig(os.path.join(submask_dir, '%(fn)s_spBorderDissim.png' % dict(fn=fn)));
        # plt.close();

        # Generate mask for snake's initial contours.

        superpixel_mask = np.zeros_like(img, np.bool)
        for l, d in hist_distances.iteritems():
            if d > foreground_dissimilarity_threshold:
                superpixel_mask[ncut_labels == l] = 1

        superpixel_mask = remove_small_objects(superpixel_mask,
                                               min_size=MIN_SIZE)

        labelmap, n_submasks = label(superpixel_mask, return_num=True)

        # superpixel_submasks = []
        dilated_superpixel_submasks = []

        for i in range(1, n_submasks + 1):
            m = labelmap == i
            # superpixel_submasks.append(m)
            dilated_m = binary_dilation(m, disk(10))
            dilated_m = remove_small_objects(dilated_m, min_size=MIN_SIZE)
            dilated_superpixel_submasks.append(dilated_m)

        # Visualize

        # viz = img_as_ubyte(ncut_boundaries_viz)
        # for submask in superpixel_submasks:
        #     for cnt in find_contour_points(submask)[1]:
        #         cv2.polylines(viz, [cnt.astype(np.int)], True, (255,0,0), 1) # red
        # for submask in dilated_superpixel_submasks:
        #     for cnt in find_contour_points(submask)[1]:
        #         cv2.polylines(viz, [cnt.astype(np.int)], True, (0,0,255), 1) # blue
        # imsave(os.path.join(submask_dir, '%(fn)s_ncutSubmasks.png' % dict(fn=fn)), viz)

        #####################

        # Find contours from mask.
        init_contours = []
        for submask in dilated_superpixel_submasks:
            cnts = find_contour_points(submask.astype(np.int), sample_every=1)
            if 1 not in cnts or len(cnts[1]) == 0:
                continue
            for cnt in cnts[1]:
                if len(cnt) > INIT_CONTOUR_MINLEN:
                    init_contours.append(cnt)

        # assert len(init_contours) > 0, 'No contour is detected from entropy mask %s' % fn
        sys.stderr.write('Extracted %d contours from mask.\n' %
                         len(init_contours))

        # Create initial levelset
        init_levelsets = []
        for cnt in init_contours:
            init_levelset = np.zeros_like(img, np.float)
            init_levelset[contours_to_mask([cnt], img.shape[:2])] = 1.
            init_levelset[:10, :] = 0
            init_levelset[-10:, :] = 0
            init_levelset[:, :10] = 0
            init_levelset[:, -10:] = 0

            init_levelsets.append(init_levelset)

        #######################
        # Binary Thresholding #
        #######################

        img_enhanced = img

        #####################
        # Evolve morphsnake #
        #####################

        final_masks = []

        for init_levelset in init_levelsets:

            discard = False
            init_area = np.count_nonzero(init_levelset)

            t = time.time()

            msnake = morphsnakes.MorphACWE(img_enhanced.astype(np.float),
                                           smoothing=int(MORPHSNAKE_SMOOTHING),
                                           lambda1=MORPHSNAKE_LAMBDA1,
                                           lambda2=MORPHSNAKE_LAMBDA2)

            msnake.levelset = init_levelset.copy()

            dq = deque([None, None])
            for i in range(MORPHSNAKE_MAXITER):

                # At stable stage, the levelset (thus contour) will oscilate,
                # so instead of comparing to previous levelset, must compare to the one before the previous
                oneBefore_levelset = dq.popleft()

                # If less than 3 pixels are changed, stop.
                if i > MORPHSNAKE_MINITER:
                    if np.count_nonzero(msnake.levelset - oneBefore_levelset
                                        ) < PIXEL_CHANGE_TERMINATE_CRITERIA:
                        break

                area = np.count_nonzero(msnake.levelset)

                if area < MIN_SIZE:
                    discard = True
                    sys.stderr.write('Too small, stop iteration.\n')
                    break

                labeled_mask = label(msnake.levelset.astype(np.bool))
                for l in np.unique(labeled_mask):
                    if l != 0:
                        m = labeled_mask == l
                        if np.count_nonzero(m) / float(
                                init_area) > AREA_CHANGE_RATIO_MAX:
                            msnake.levelset[m] = 0
                            sys.stderr.write('Area nullified.\n')

                if np.count_nonzero(msnake.levelset) / float(
                        init_area) < AREA_CHANGE_RATIO_MIN:
                    discard = True
                    sys.stderr.write(
                        'Area shrinks too much, stop iteration.\n')
                    break

                dq.append(msnake.levelset)

                #         t = time.time()
                msnake.step()
        #         sys.stderr.write('Step: %f seconds\n' % (time.time()-t)) # 0.6 second / step

            sys.stderr.write('Snake finished at iteration %d.\n' % i)
            sys.stderr.write('Snake: %.2f seconds\n' %
                             (time.time() - t))  # 72s

            if discard:
                sys.stderr.write('Discarded.\n')
                continue
            else:
                # Handles the case that a single initial contour morphs into multiple contours
                labeled_mask = label(msnake.levelset.astype(np.bool))
                for l in np.unique(labeled_mask):
                    if l != 0:
                        m = labeled_mask == l
                        if np.count_nonzero(m) > MIN_SIZE:
                            final_masks.append(m)
                            sys.stderr.write('Final masks added.\n')

        ############
        ## Export ##
        ############

        # submask_viz_dir = create_if_not_exists('/home/yuncong/CSHL_data_processed/%(stack)s/%(stack)s_submask_overlayViz/%(fn)s' % dict(stack=stack, fn=fn))
        # execute_command('rm %s/*' % submask_viz_dir)

        # img_rgb = imread(os.path.join(input_dir, fn + '.' + tb_fmt))

        # all_init_cnts = []
        # for i, init_levelset in enumerate(init_levelsets):
        #     all_init_cnts += find_contour_points(init_levelset)[1]

        for i, mask in enumerate(final_masks):
            imsave(
                os.path.join(submask_dir, '%(fn)s_submask_%(i)d.png' % {
                    'fn': fn,
                    'i': i
                }), img_as_ubyte(mask))

            # viz = img_rgb.copy()
            #
            # cnts = find_contour_points(mask)
            # if len(cnts) == 0:
            #     raise
            # for cnt in cnts[1]:
            #     cv2.polylines(viz, [cnt.astype(np.int)], True, (255,0,0), 3) # red
            #
            # for cnt in all_init_cnts:
            #     cv2.polylines(viz, [cnt.astype(np.int)], True, (0,255,0), 3) # green

            # viz_fn = os.path.join(submask_dir, '%(fn)s_submask_%(i)d_overlayViz.png' % dict(fn=fn, i=i+1))
            # sys.stderr.write(viz_fn + '\n')
            # imsave(viz_fn, viz)

        ################################################
        # Automatically judge the goodness of each mask
        ################################################

        submasks = final_masks

        n = len(final_masks)

        rank1 = np.argsort([np.count_nonzero(m) for m in submasks])[::-1]

        image_center = np.r_[submasks[0].shape[1] / 2,
                             submasks[0].shape[0] / 2]

        bbox_to_image_center_distance = []
        for m in submasks:
            xmin, xmax, ymin, ymax = bbox_2d(m)
            dist = np.sqrt(
                np.sum((image_center - ((xmin + xmax) / 2,
                                        (ymin + ymax) / 2))**2))
            bbox_to_image_center_distance.append(dist)

        rank2 = np.argsort(bbox_to_image_center_distance)

        r1 = np.asarray(
            [r for r, i in sorted(enumerate(rank1), key=lambda (r, i): i)])
        r2 = np.asarray(
            [r for r, i in sorted(enumerate(rank2), key=lambda (r, i): i)])
        rank = np.argsort(
            r1 +
            1.01 * r2)  # weight being close to center a bit more to break tie
        best_mask_ind = rank[0]

        decisions = [False for _ in range(n)]
        decisions[best_mask_ind] = True

        np.savetxt(os.path.join(submask_dir,
                                '%(fn)s_submasksAlgReview.txt' % dict(fn=fn)),
                   decisions,
                   fmt='%d')

    except Exception as e:
        sys.stderr.write('%s\n' % e)
        sys.stderr.write('Mask error: %s\n' % fn)
        return