Ejemplo n.º 1
0
def apply_ACWE(img):
    '''
    '''
    image = img_as_float(img)
    init_ls = checkerboard_level_set(image.shape, 6)
    # List with intermediate results for plotting the evolution
    evolution = []
    callback = store_evolution_in(evolution)
    ls = morphological_chan_vese(image,
                                 35,
                                 init_level_set=init_ls,
                                 smoothing=3,
                                 iter_callback=callback)

    fig, axes = plt.subplots(2, 2, figsize=(8, 8))
    ax = axes.flatten()
    ax[1].imshow(ls, cmap="gray")
    ax[1].set_axis_off()
    contour = ax[1].contour(evolution[2], [0.5], colors='g')
    contour.collections[0].set_label("Iteration 2")
    contour = ax[1].contour(evolution[7], [0.5], colors='y')
    contour.collections[0].set_label("Iteration 7")
    contour = ax[1].contour(evolution[-1], [0.5], colors='r')
    contour.collections[0].set_label("Iteration 35")
    ax[1].legend(loc="upper right")
    title = "Morphological ACWE evolution"
    ax[1].set_title(title, fontsize=12)
def process_file(filename):
    """Process contour method"""
    image = io.imread(filename)
    init_ls = checkerboard_level_set(image.shape, 5)
    evolution = []
    callback = store_evolution_in(evolution)
    ls = morphological_chan_vese(image, iterations, init_level_set=init_ls, smoothing=1, iter_callback=callback)
    return evolution[-1]
Ejemplo n.º 3
0
def calculate_profile(image):
    # Initial level set
    init_ls = checkerboard_level_set(image.shape, 5)
    # List with intermediate results for plotting the evolution

    ls = morphological_chan_vese(image,
                                 10,
                                 init_level_set=init_ls,
                                 smoothing=5)
    return ls
Ejemplo n.º 4
0
def morphological_snakes(img, verts, SHOW_RESULTS=False):
    from skimage import data, img_as_float
    from skimage.segmentation import (morphological_chan_vese,
                                      morphological_geodesic_active_contour,
                                      inverse_gaussian_gradient,
                                      checkerboard_level_set)
    img = rgb2gray(img.copy())
    image = img_as_float(img)

    # Initial level set
    init_ls = checkerboard_level_set(img.shape, 6)

    # verts = np.array(verts).reshape((-1,1,2)).astype(np.int32)
    # init_ls = np.zeros(image.shape, dtype=np.int8)
    # cv2.drawContours(init_ls, [verts], -1, 1, 2)

    plt.imshow(init_ls)
    plt.show()
    # List with intermediate results for plotting the evolution
    evolution = []
    callback = store_evolution_in(evolution)
    ls = morphological_chan_vese(image,
                                 32,
                                 init_level_set=init_ls,
                                 smoothing=3,
                                 iter_callback=callback)

    fig, axes = plt.subplots(2, 2, figsize=(8, 8))
    ax = axes.flatten()

    ax[0].imshow(image, cmap="gray")
    ax[0].set_axis_off()
    ax[0].contour(ls, [0.5], colors='r')
    ax[0].set_title("Morphological ACWE segmentation", fontsize=12)

    ax[1].imshow(ls, cmap="gray")
    ax[1].set_axis_off()
    contour = ax[1].contour(evolution[2], [0.5], colors='g')
    contour.collections[0].set_label("Iteration 2")
    contour = ax[1].contour(evolution[7], [0.5], colors='y')
    contour.collections[0].set_label("Iteration 7")
    contour = ax[1].contour(evolution[-1], [0.5], colors='r')
    contour.collections[0].set_label("Iteration 32")
    ax[1].legend(loc="upper right")
    title = "Morphological ACWE evolution"
    ax[1].set_title(title, fontsize=12)

    return ls
Ejemplo n.º 5
0
    def store_evolution_in(lst):
        def _store(x):
            lst.append(np.copy(x))

        return _store
        max = np.amax(image)
        eimage = (image * 255 / max)**5
        init_ls = checkerboard_level_set(image.shape, 6)
        evolution = []
        callback = store_evolution_in(evolution)
        ls = morphological_chan_vese(eimage,
                                     35,
                                     init_level_set=init_ls,
                                     smoothing=3,
                                     iter_callback=callback)
        l = ls
Ejemplo n.º 6
0
def apply_ACWE(img):
    '''
    Segments largest region using ACWE.
    '''
    init_ls = checkerboard_level_set(img.shape, 6)
    evolution = []
    callback = store_evolution_in(evolution)
    l = morphological_chan_vese(img,
                                3,
                                init_level_set=init_ls,
                                smoothing=1,
                                iter_callback=callback)

    #plt.figure(figsize=(9, 3))
    #plt.imshow(ls, cmap="gray")
    #plt.contour(evolution[3], [0.5], colors='y')

    return l
def morphChaneVese(image, itr=120, p=3):
    """
        calculate the segmentation mask of the image using morphological_chan_vese built in function.
        Args:
            img: RGB image to calculate the segmentation mask over it.
            itr: number of iterations used to get the segmentation mask, default = 120.
            p: square width of the initial segmentation mask, default = 3.
        Returns:
            Binary image represents the filled object.
    """
    image = copy.deepcopy(image)
    #turn RGB image to gray scaled one.
    image = rgb2gray(image)
    #apply hestogram equalization to enhance the image.
    image = equalize_hist(image)
    #set the initial segmentation mask base on p value.
    init_ls = checkerboard_level_set(image.shape, p)
    #calculate the mask using morphological_chan_vese algorithm.
    mask = morphological_chan_vese(image, iterations=itr, init_level_set=init_ls, smoothing=2)
    #return the calculated mask
    return mask
Ejemplo n.º 8
0
def segment_with_level_sets(img):
    """ Function to perform segmentation with level sets. img_gray should not contain hairs nor black zones

    Parameters
    ----------
    binary          Binary mask with result of level sets

    Returns
    -------
    output_image    Binary image with segmentation result

    """
    #First, find out if it is necessary to appy inpainting to remove black zones

    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

    r1 = img_gray[:5, :5]
    r2 = img_gray[:5, -5:]
    r3 = img_gray[-5:, :5]
    r4 = img_gray[-5:, -5:]

    if (np.mean(r1) < 50 or np.mean(r2) < 50 or np.mean(r3) < 50
            or np.mean(r4) < 50
        ):  #If zones of the corners are too dark, apply ellipse inpainting
        elliptical_mask = 1 - mask_eliptical(img_gray)
        to_process = cv.inpaint(img_gray, elliptical_mask, 21, cv.INPAINT_NS)
    else:
        to_process = img_gray

    init_ls = checkerboard_level_set(img_gray.shape, 6)

    ls = morphological_chan_vese(to_process,
                                 35,
                                 init_level_set=init_ls,
                                 smoothing=3)

    segmented = find_segmented(ls)

    return segmented
Ejemplo n.º 9
0
def get_snake(file, plot=False):
    def store_evolution_in(lst):
        def _store(x):
            lst.append(np.copy(x))

        return _store

    evolution = []
    callback = store_evolution_in(evolution)
    raster_filepath = os.path.dirname(file) + "/"
    raster_filename = os.path.basename(file)
    with rasterio.open(file, driver='GTiff') as src:
        kwargs = src.meta
        kwargs.update(count=1, dtype=rasterio.uint8)
        input = src.read(1).astype(rasterio.uint8)
    if plot:
        plt.imshow(input, cmap='gray')
        plt.show()
    init_lvl_set = checkerboard_level_set(input.shape)
    lvl_set = morphological_chan_vese(input,
                                      100,
                                      init_level_set=init_lvl_set,
                                      iter_callback=callback,
                                      smoothing=1)
    if plot:
        plt.imshow(lvl_set, cmap='gray')
        plt.show()
    noise_reduced = morph_transform(lvl_set.astype(rasterio.uint8), 9, 9)
    if plot:
        plt.imshow(noise_reduced)
        plt.show()
    out_filename = raster_filepath + raster_filename.split(
        sep=".")[0] + "_chan_vese.tif"
    with rasterio.open(out_filename, 'w', **kwargs) as dst:
        dst.write_band(1, noise_reduced.astype(rasterio.uint8))
    return lvl_set
Ejemplo n.º 10
0

I = pydicom.dcmread('000001.dcm')
I = I.pixel_array
x = I.copy()
y = x.resize((256, 256))
max = np.amax(I)
sigma = 0.08
sigma_est = np.mean(estimate_sigma(I, multichannel=False))
patch_kw = dict(patch_size=5, patch_distance=6, multichannel=False)
I = denoise_nl_means(I, h=1.15 * sigma_est, fast_mode=False, **patch_kw)
image = np.copy(I)
print(image[10, 10])
print(image[100, 150])
print(np.amax(image[50, 190:210]))
print(np.amax(image))
max = np.amax(image)
eimage = (image * 255 / max)**5
#eimage = image ** (3)
init_ls = checkerboard_level_set(image.shape, 6)
evolution = []
callback = store_evolution_in(evolution)
ls = morphological_chan_vese(eimage,
                             35,
                             init_level_set=init_ls,
                             smoothing=3,
                             iter_callback=callback)

plt.imshow(image, cmap=plt.cm.bone)
plt.contour(ls, [0.5], colors='r')
plt.show()
Ejemplo n.º 11
0
    def execute(self, arg, trans):
        # Number of Chan-Vese iterations
        nIter = 20
        std = 1.5  # [mm], original
        squareSize = 1.5  # [mm]

        saveMetaImage = False
        savePNGImage = False
        # Actual work - now done using SciPy

        # Gaussian smoothing
        dx, dy, dz = arg.GetSpacing()
        sx, sy = std / dx, std / dy
        smoother = vtk.vtkImageGaussianSmooth()
        smoother.SetStandardDeviations(sx, sy)
        smoother.SetDimensionality(2)
        smoother.SetInputData(arg)
        smoother.Update()

        if savePNGImage:
            writer = vtk.vtkPNGWriter()
            writer.SetFileName('./output.png')
            writer.SetInputConnection(smoother.GetOutputPort())
            writer.Write()

        if saveMetaImage:
            # Save to disk
            writer = vtk.vtkMetaImageWriter()
            writer.SetFileName('./output.mhd')
            writer.SetInputConnection(smoother.GetOutputPort())
            writer.Write()

        smoothedData = smoother.GetOutput()

        # Convert VTK to NumPy image
        dims = arg.GetDimensions()
        vtk_array = smoothedData.GetPointData().GetScalars()
        nComponents = vtk_array.GetNumberOfComponents()
        npData = vtk_to_numpy(vtk_array).reshape(
            dims[2], dims[1], dims[0],
            nComponents)[:, :, :, 0].reshape(dims[1], dims[0])

        # Seed for active contours
        iSquareSize = int(squareSize / dx)
        init_ls = checkerboard_level_set(npData.shape, iSquareSize)

        contours = morphological_chan_vese(npData,
                                           nIter,
                                           init_level_set=init_ls,
                                           smoothing=2)
        # Add singleton to get 3-dimensional data
        data = contours[None, :]

        # Convert Numpy to VTK data
        importer = vtk.vtkImageImport()
        importer.SetDataScalarType(vtk.VTK_SIGNED_CHAR)
        importer.SetDataExtent(0, data.shape[2] - 1, 0, data.shape[1] - 1, 0,
                               data.shape[0] - 1)
        importer.SetWholeExtent(0, data.shape[2] - 1, 0, data.shape[1] - 1, 0,
                                data.shape[0] - 1)
        importer.SetImportVoidPointer(data.data)
        importer.Update()
        vtkData = importer.GetOutput()
        vtkData.SetSpacing(smoothedData.GetSpacing())
        vtkData.SetOrigin(0, 0, 0)

        # Contour filter
        contourFilter = vtk.vtkContourFilter()
        iso_value = 0.5
        contourFilter.SetInputData(vtkData)
        contourFilter.SetValue(0, iso_value)
        contourFilter.Update()
        contourFilter.ReleaseDataFlagOn()

        # Compute normals
        normals = vtk.vtkPolyDataNormals()
        normals.SetInputConnection(contourFilter.GetOutputPort())
        normals.SetFeatureAngle(60.0)
        normals.ReleaseDataFlagOn()

        # Join line segments
        stripper = vtk.vtkStripper()
        stripper.SetInputConnection(normals.GetOutputPort())
        stripper.ReleaseDataFlagOn()
        stripper.Update()

        # Transform data from scaled screen to world coordinates
        transformFilter = vtk.vtkTransformPolyDataFilter()
        transformFilter.SetInputConnection(stripper.GetOutputPort())
        transformFilter.SetTransform(trans)
        transformFilter.Update()
        result = transformFilter.GetOutput()

        # Emit done with output
        self.done.emit(result)
Ejemplo n.º 12
0
def segment_lesion(image, mode="KMeans"):
    ## Unsupervised Segmentation
    # K-Means Clustering
    if (mode == "KMeans"):
        # K-Means Clustering
        deim = denoise(cv2.cvtColor(image, cv2.COLOR_RGB2GRAY), weight=150)
        kmeans = KMeans(n_clusters=2, random_state=0,
                        n_jobs=-1).fit(deim.reshape(-1, 1))
        mask = (kmeans.labels_).reshape(deim.shape[0],
                                        deim.shape[1]).astype('uint8')

    # Expectation-Maximization Gaussian Mixture Model
    elif (mode == "EM"):
        # K-Means Clustering
        feature_vector = (denoise(cv2.cvtColor(image, cv2.COLOR_RGB2GRAY),
                                  weight=150)).reshape(-1, 1)
        kmeans = KMeans(n_clusters=2, random_state=0,
                        n_jobs=-1).fit(feature_vector)
        KMpredict = kmeans.predict(feature_vector)

        # Expectation Step: Initialization with K-Means
        KM_BG = feature_vector[KMpredict == 0]
        KM_FG = feature_vector[KMpredict == 1]

        # Expectation Step: Mean and Covariance
        mean_BG = np.mean(KM_BG, axis=0)
        mean_FG = np.mean(KM_FG, axis=0)
        covar_BG = np.cov(KM_BG, rowvar=False)
        covar_FG = np.cov(KM_FG, rowvar=False)

        # Expectation Step: Prior Probabilities
        prob_BG = KM_BG.shape[0] / feature_vector.shape[0]
        prob_FG = KM_FG.shape[0] / feature_vector.shape[0]

        # Iterative Update
        min_change = 0.01
        max_steps = 5

        for i in range(max_steps):
            # Expectation Step: Probability Density Function
            PDF_BG = multivariate_normal.pdf(feature_vector,
                                             mean=mean_BG,
                                             cov=covar_BG)
            PDF_FG = multivariate_normal.pdf(feature_vector,
                                             mean=mean_FG,
                                             cov=covar_FG)
            weights_BG = (prob_BG * PDF_BG) / ((prob_FG * PDF_FG) +
                                               (prob_BG * PDF_BG))
            weights_FG = (prob_FG * PDF_FG) / ((prob_FG * PDF_FG) +
                                               (prob_BG * PDF_BG))
            weights = np.concatenate(
                (weights_BG.reshape(-1, 1), weights_FG.reshape(-1, 1)), axis=1)
            log_B = sum((np.log(sum(weights))))

            # Maximization Step: New Probabilities
            _, counts = np.unique(np.argmax(weights, axis=1),
                                  return_counts=True)
            prob_BG = counts[0] / feature_vector.shape[0]
            prob_FG = counts[1] / feature_vector.shape[0]

            # Maximization Step: New Mean and Covariance
            mean_BG = (1 / counts[0]) * (weights[:, 0] @ feature_vector)
            mean_FG = (1 / counts[1]) * (weights[:, 1] @ feature_vector)
            covar_BG = (1 / counts[0]) * (
                weights[:, 0] * np.transpose(feature_vector - mean_BG)) @ (
                    feature_vector - mean_BG)
            covar_FG = (1 / counts[1]) * (
                weights[:, 1] * np.transpose(feature_vector - mean_FG)) @ (
                    feature_vector - mean_FG)

            # Maximization Step: Probability Density Function
            PDF_BG = multivariate_normal.pdf(feature_vector,
                                             mean=mean_BG,
                                             cov=covar_BG)
            PDF_FG = multivariate_normal.pdf(feature_vector,
                                             mean=mean_FG,
                                             cov=covar_FG)
            weights_BG = (prob_BG * PDF_BG) / ((prob_FG * PDF_FG) +
                                               (prob_BG * PDF_BG))
            weights_FG = (prob_FG * PDF_FG) / ((prob_FG * PDF_FG) +
                                               (prob_BG * PDF_BG))
            weights = np.concatenate(
                (weights_BG.reshape(-1, 1), weights_FG.reshape(-1, 1)), axis=1)
            log_N = sum((np.log(sum(weights))))

            # Update Trackers, Verify Conditions
            change_log = np.linalg.norm(log_N - log_B)
            if (change_log <= min_change):
                continue
            else:
                break

        # Output Image Reconstruction
        mask = (np.argmax(weights, axis=1)).reshape(-1, 1)
        mask = np.reshape(mask,
                          (image.shape[0], image.shape[1])).astype('uint8')

    # Chan-Vese Active Contours
    elif (mode == "active_contours"):
        # Morphological ACWE
        image = img_as_float(cv2.cvtColor(image, cv2.COLOR_RGB2GRAY))

        # Initial level Set
        init_ls = checkerboard_level_set(image.shape, 6)

        # List with Intermediate Results for Plotting the Evolution
        evolution = []
        callback = store_evolution_in(evolution)
        mask = morphological_chan_vese(image,
                                       5,
                                       init_level_set=init_ls,
                                       smoothing=10,
                                       iter_callback=callback).astype(np.uint8)

    # Watershed
    elif (mode == "mod_watershed"):

        ret, thresh = cv2.threshold(cv2.cvtColor(image,
                                                 cv2.COLOR_RGB2GRAY), 0, 255,
                                    cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
        kernel = np.ones((3, 3), np.uint8)
        opening = cv2.morphologyEx(thresh,
                                   cv2.MORPH_OPEN,
                                   kernel,
                                   iterations=2)

        sure_bg = cv2.dilate(opening, kernel, iterations=3)
        dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
        ret, sure_fg = cv2.threshold(dist_transform,
                                     0.01 * dist_transform.max(), 255, 0)

        ls = np.uint8(sure_fg) / 255
        circle_mask = create_circular_mask(image.shape[0],
                                           image.shape[1],
                                           radius=220)
        ls = ls * circle_mask

        # Discard Edge-to-Edge Connected Component
        large = getLargestCC(ls).astype(np.uint8)
        if (corner_mean(large) > 1):
            large = getLargestCC(ls - large).astype(np.uint8)

        # Post-Process Masks
        post = ndimage.binary_fill_holes(large, structure=np.ones(
            (5, 5))).astype(bool)

    else:
        print("ERROR: Undefined Segmentation Mode")

    ## Segmentation Label Selection
    # Define Target Scope
    candidate_mask_1 = mask.copy() * create_circular_mask(
        image.shape[0], image.shape[1], radius=230)
    candidate_mask_2 = (np.invert(mask.copy()) + 2) * create_circular_mask(
        image.shape[0], image.shape[1], radius=230)

    # Compute Area of Convex Hulls
    convex_hull_1 = convex_hull_image(candidate_mask_1)
    convex_hull_2 = convex_hull_image(candidate_mask_2)

    # Set Segmentation Component Labels
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10, 10))
    if (np.sum(convex_hull_1) < np.sum(convex_hull_2)):
        dilation = cv2.dilate(candidate_mask_1, kernel, iterations=1)
        mask = ndimage.binary_fill_holes(dilation, structure=np.ones((5, 5)))
    else:
        dilation = cv2.dilate(candidate_mask_2, kernel, iterations=1)
        mask = ndimage.binary_fill_holes(dilation, structure=np.ones((5, 5)))
    if (np.sum(mask) < 5000):
        mask = create_circular_mask(image.shape[0], image.shape[1], radius=230)
    return mask
Ejemplo n.º 13
0
def store_evolution_in(lst):
    """Returns a callback function to store the evolution of the level sets in
    the given list.
    """

    def _store(x):
        lst.append(np.copy(x))

    return _store


# Morphological ACWE
image = img_as_float(data.camera())

# Initial level set
init_ls = checkerboard_level_set(image.shape, 6)
# List with intermediate results for plotting the evolution
evolution = []
callback = store_evolution_in(evolution)
ls = morphological_chan_vese(image, 35, init_level_set=init_ls, smoothing=3,
                             iter_callback=callback)

fig, axes = plt.subplots(2, 2, figsize=(8, 8))
ax = axes.flatten()

ax[0].imshow(image, cmap="gray")
ax[0].set_axis_off()
ax[0].contour(ls, [0.5], colors='r')
ax[0].set_title("Morphological ACWE segmentation", fontsize=12)

ax[1].imshow(ls, cmap="gray")
Ejemplo n.º 14
0
def active_contour(image):
    def store_evolution_in(lst):
        
        def _store(x):
            lst.append(np.copy(x))
    
        return _store
    
    image = img_as_float(data.camera())
    
    init_ls = checkerboard_level_set(image.shape, 6)
    
    evolution = []
    callback = store_evolution_in(evolution)
    ls = morphological_chan_vese(image, 35, init_level_set=init_ls, smoothing=3,
                                 iter_callback=callback)
    
    fig, axes = plt.subplots(2, 2, figsize=(8, 8))
    ax = axes.flatten()
    
    ax[0].imshow(image, cmap="gray")
    ax[0].set_axis_off()
    ax[0].contour(ls, [0.5], colors='r')
    ax[0].set_title("Morphological ACWE segmentation", fontsize=12)
    
    ax[1].imshow(ls, cmap="gray")
    ax[1].set_axis_off()
    contour = ax[1].contour(evolution[2], [0.5], colors='g')
    contour.collections[0].set_label("Iteration 2")
    contour = ax[1].contour(evolution[7], [0.5], colors='y')
    contour.collections[0].set_label("Iteration 7")
    contour = ax[1].contour(evolution[-1], [0.5], colors='r')
    contour.collections[0].set_label("Iteration 35")
    ax[1].legend(loc="upper right")
    title = "Morphological ACWE evolution"
    ax[1].set_title(title, fontsize=12)
    
    
    
    image = img_as_float(data.coins())
    gimage = inverse_gaussian_gradient(image)
    
    
    init_ls = np.zeros(image.shape, dtype=np.int8)
    init_ls[10:-10, 10:-10] = 1
    
    evolution = []
    callback = store_evolution_in(evolution)
    ls = morphological_geodesic_active_contour(gimage, 230, init_ls,
                                               smoothing=1, balloon=-1,
                                               threshold=0.69,
                                               iter_callback=callback)
    
    ax[2].imshow(image, cmap="gray")
    ax[2].set_axis_off()
    ax[2].contour(ls, [0.5], colors='r')
    ax[2].set_title("Morphological GAC segmentation", fontsize=12)
    
    ax[3].imshow(ls, cmap="gray")
    ax[3].set_axis_off()
    contour = ax[3].contour(evolution[0], [0.5], colors='g')
    contour.collections[0].set_label("Iteration 0")
    contour = ax[3].contour(evolution[100], [0.5], colors='y')
    contour.collections[0].set_label("Iteration 100")
    contour = ax[3].contour(evolution[-1], [0.5], colors='r')
    contour.collections[0].set_label("Iteration 230")
    ax[3].legend(loc="upper right")
    title = "Morphological GAC evolution"
    ax[3].set_title(title, fontsize=12)
    
    fig.tight_layout()
    plt.show()
Ejemplo n.º 15
0
def morphological(image):

    # Morphological ACWE
    img = img_as_float(skimage.color.rgb2gray(image))
    # Initial level set
    init_ls = checkerboard_level_set(img.shape, 6)
    # List with intermediate results for plotting the evolution
    evolution = []
    callback = store_evolution_in(evolution)
    ls = morphological_chan_vese(img,
                                 200,
                                 init_level_set=init_ls,
                                 smoothing=4,
                                 iter_callback=callback)

    fig, axes = plt.subplots(2, 2, figsize=(8, 8))
    ax = axes.flatten()

    ax[0].imshow(img, cmap="gray")
    ax[0].set_axis_off()
    ax[0].contour(ls, [0.5], colors='r')
    ax[0].set_title("Morphological ACWE segmentation", fontsize=12)

    ax[1].imshow(ls, cmap="gray")
    ax[1].set_axis_off()
    contour = ax[1].contour(evolution[2], [0.5], colors='g')
    contour.collections[0].set_label("Iteration 2")
    contour = ax[1].contour(evolution[7], [0.5], colors='y')
    contour.collections[0].set_label("Iteration 7")
    contour = ax[1].contour(evolution[-1], [0.5], colors='r')
    contour.collections[0].set_label("Iteration 35")
    ax[1].legend(loc="upper right")
    title = "Morphological ACWE evolution"
    ax[1].set_title(title, fontsize=12)

    # Morphological GAC
    img = img_as_float(skimage.color.rgb2gray(image))
    gimage = inverse_gaussian_gradient(img)

    # Initial level set
    init_ls = np.zeros(img.shape, dtype=np.int8)
    init_ls[10:-10, 10:-10] = 1
    # List with intermediate results for plotting the evolution
    evolution = []
    callback = store_evolution_in(evolution)
    ls = morphological_geodesic_active_contour(gimage,
                                               230,
                                               init_ls,
                                               smoothing=5,
                                               balloon=-1,
                                               threshold=0.5,
                                               iter_callback=callback)

    ax[2].imshow(img, cmap="gray")
    ax[2].set_axis_off()
    ax[2].contour(ls, [0.5], colors='r')
    ax[2].set_title("Morphological GAC segmentation", fontsize=12)

    ax[3].imshow(ls, cmap="gray")
    ax[3].set_axis_off()
    contour = ax[3].contour(evolution[0], [0.5], colors='g')
    contour.collections[0].set_label("Iteration 0")
    contour = ax[3].contour(evolution[100], [0.5], colors='y')
    contour.collections[0].set_label("Iteration 100")
    contour = ax[3].contour(evolution[-1], [0.5], colors='r')
    contour.collections[0].set_label("Iteration 230")
    ax[3].legend(loc="upper right")
    title = "Morphological GAC evolution"
    ax[3].set_title(img, fontsize=12)

    fig.tight_layout()
    plt.show()