Coder Social home page Coder Social logo

morphsnakes's Introduction

Morphological Snakes

Build Status codecov

morphsnakes is an implementation of the Morphological Snakes for image segmentation. morphsnakes supports 2D images and 3D volumes.

anim_nodule anim_dendrite

Morphological Snakes [1][2] are a family of methods for image segmentation. Their behavior is similar to that of Active Contours like Geodesic Active Contours [3] or Active Contours without Edges [4]. However, traditional approaches for Active Contours require solving PDEs over floating points arrays, which is slow and might have numerical stability issues. Instead of PDEs, Morphological Snakes use morphological operators -such as dilation or erosion- over a binary array. This makes Morphological Snakes faster and numerically more stable than their traditional counterparts.

Two Morphological Snakes methods are available in morphsnakes:

  • Morphological Geodesic Active Contours, or MorphGAC, implemented in the function morphological_geodesic_active_contour,
  • Morphological Active Contours without Edges, or MorphACWE, implemented in the function morphological_chan_vese.

Installation

Option 1: Copy morphsnakes.py to your project

All the required code is contained in morphsnakes.py. You can copy this file into your own project with

wget https://raw.githubusercontent.com/pmneila/morphsnakes/master/morphsnakes.py

Option 2: pip install

It is also possible to install morphsnakes with pip

pip install morphsnakes

Getting started

The file examples.py contains multiple examples of MorphGAC and MorphACWE. You can take any example from that file as a starting point for your project.

What are the differences between MorphGAC and MorphACWE?

MorphACWE

MorphACWE works well when pixel values of the inside and the outside regions of the object to segment have different averages. Unlike MorphGAC, MorphACWE does not require that the contours of the object are well defined, and it can work over the original image without any preprocessing.

MorphACWE is much easier to setup and use than MorphGAC, and much more robust to noise. You should try using MorphACWE first, and only switch to MorphGAC when it is clear to you that MorphACWE is not suitable for the kind of images you are working with.

anim_lakes anim_europe

MorphGAC

MorphGAC is suitable for images with visible contours, even when these contours might be noisy, cluttered, or partially unclear. It requires, however, that the image is preprocessed to highlight the contours. This can be done using the function inverse_gaussian_gradient, although you might want to define your own version. The quality of the MorphGAC segmentation depends greatly on this preprocessing step.

anim_starfish anim_nodule

References

[1]: A Morphological Approach to Curvature-based Evolution of Curves and Surfaces, Pablo Márquez-Neila, Luis Baumela and Luis Álvarez. In IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI), 2014, DOI 10.1109/TPAMI.2013.106

[2]: Morphological Snakes. Luis Álvarez, Luis Baumela, Pablo Márquez-Neila. In Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition 2010 (CVPR10).

[3]: Geodesic Active Contours, Vicent Caselles, Ron Kimmel and Guillermo Sapiro. In International Journal of Computer Vision (IJCV), 1997, DOI:10.1023/A:1007979827043

[4]: Active Contours without Edges, Tony Chan and Luminita Vese. In IEEE Transactions on Image Processing, 2001, DOI:10.1109/83.902291

morphsnakes's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

morphsnakes's Issues

Merging morphological snakes into `scikit-image`

Hello!

First of all, very nice work! Thanks a lot for the implementation, and for the examples you prepared!

I was wondering if you would consider merging these algorithms (i.e. the full repo) into scikit-image (scikit-image.org, https://github.com/scikit-image/scikit-image). It is one of the most popular libraries for image processing, and is fully written in Python/Cython.
If you find this proposal interesting, I'd be happy to assist you with the Pull Request.

In any case, thanks again, and keep up the great work! ;)

a curve with endpoints

Can you please post an example of how to do this in python for an image where I know the start and end position?

Thanks.

========

Hi,

In general, no, it cannot be done. However, if your open curve has fixed end-points (i.e., you know where the curve starts and ends) then it might be possible to simulate this behavior using a closed contour that passes through the end-points and updating only the fragment of the contour between the end-points. Whether this will work or not depends on your particular problem.

Originally posted by @pmneila in #8 (comment)

Saving segmentation

Hi,

I do not have much experience with python, could you please help me how can I save segmentation into nii binary file?

Thank you

Mark

What‘s the role of ISoSI

Dear @pmneila ,

Thanks for sharing the great work.

In the PAMI 2014 paper, both GAC and ACWE use SIoIS operator.

image

image

However, in the code, why ISoSI is also used?

morphsnakes/morphsnakes.py

Lines 136 to 138 in aab66e7

_curvop = _fcycle([lambda u: sup_inf(inf_sup(u)), # SIoIS
lambda u: inf_sup(sup_inf(u))]) # ISoSI

Best,
Jun

License

Please release morphsnakes under a permissive open source license!

Can I extract a signed distance function from the output of morphsnakes ?

Hi, I want to use morphsnakes as a tool for extracting the boundary of a CT/MRI scan of head.

image

I want to use the red line on the left figure as a function handler in python, denoted by fd. For all pixels p = (x, y), it will be fd(p) = 0 on the boundary, fd(p) < 0 interior and fd(p) > 0 at the exterior. I do not know whether morphsnakes support this feature (in both 2D and 3D!!), and is there any post processing techniques I need to achieve this goal ?

extension on superpixels

I have tried it for some medical images which are a bit larger than standard computer vision images and then the processing takes significantly longer time and on the other hand, setting larger step may skip the boundary. Have you consider and you think that it could be easier extendable to run in on superpixels (which usually preserve the edges), similar as we did DOI: 10.1117/1.JEI.26.6.061611?

Could you please a demo about the function evolve_visual3d?

Thanks for your great work, I met a problem on this library.
I can't figure out how to make evolve_visual3d work, could you please provide a demo on that.

def evolve_visual3d(msnake, levelset=None, num_iters=20):
    """
    Visual evolution of a three-dimensional morphological snake.

    Parameters
    ----------
    msnake : MorphGAC or MorphACWE instance
        The morphological snake solver.
    levelset : array-like, optional
        If given, the levelset of the solver is initialized to this. If not
        given, the evolution will use the levelset already set in msnake.
    num_iters : int, optional
        The number of iterations.
    """
    from enthought.mayavi import mlab

    if levelset is not None:
        msnake.levelset = levelset

    fig = mlab.gcf()
    mlab.clf()
    src = mlab.pipeline.scalar_field(msnake.data)
    mlab.pipeline.image_plane_widget(src, plane_orientation='x_axes', colormap='gray')
    cnt = mlab.contour3d(msnake.levelset, contours=[0.5])

    for i in xrange(num_iters):
        msnake.step()
        cnt.mlab_source.scalars = msnake.levelset

    # Return the last levelset.
    return msnake.levelset

getting black images instead of results

Hi all,
I am trying to implement the code, I didn't make any change. However, I can't get the correct outputs as shown in the example repository. Can anyone help me, please?
Thank you

here is the result I get:
image

Buildings Image segmentation

I am working on your code a little bit. I am applying it for segmentation of rooftop area of buildings. So every time I had to change the centre level dataset is there any way to automate that part on the image?

Thanks for your code and help.

Installation with `pip`

Thanks a lot for the very useful library!

I have faced an issue with the installation of morphsnakes using pip as described in the README file.

The issue is related to the dependencies of morphsnakes: numpy and scipy are not automatically installed when running pip install morphsnakes:

$ pip install morphsnakes
Collecting morphsnakes
  Using cached morphsnakes-2.0.1-py3-none-any.whl (7.8 kB). 
Installing collected packages: morphsnakes 
Successfully installed morphsnakes-2.0.1

so that when I try to import morphsnakes:

import morphsnakes

the following import error is raised:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/fnattino/Projects/Eratosthenes/Repos/morphsnakes/morphsnakes.py", line 56, in <module>
    import numpy as np
ModuleNotFoundError: No module named 'numpy'

I would be happy to help fixing this in order to use this tool as a dependency in a project package I am working on, so if you don't mind I will open a PR to address the issue!

Perform pylint on the repository and update dependencies of mayavi in Readme.md

Hi, I saw that you are developing a branch with C++ support and refactoring the folder structure, I think

  1. add pylint check (or enable travis or coverage.)
  2. the 3D demo depend on mayavi, which currently is not supported in Anaconda with python-3.5 (which will be support in the next release of mayavi), I think this dependency should be written in the Readme.md

not showing correct output

Hi,

Thanks for your code. I have been trying to play around it a bit. In that process, I cropped the image. However, the algo doesn't seem to give correct output.

Original

mini

Cropped:

world_out

Do you know what could be the issue?

PS: cropping the image like this

imgcolor = imread("testimages/seastar2.png")/255.0
imgcolor = imgcolor[50:250, 50:250]

NameError: name 'time' is not defined

I copied the examples code into a Jupyter Notebook and ran it. The exact code sitting in Jupyter (Python 3) is below:

import os
import logging
import time
import numpy as np
from imageio import imread
import matplotlib
from matplotlib import pyplot as plt

import morphsnakes as ms

# in case you are running on machine without display, e.g. server
if os.environ.get('DISPLAY', '') == '':
    logging.warning('No display found. Using non-interactive Agg backend.')
    matplotlib.use('Agg')

PATH_IMG_NODULE = 'morphsnakes-master/images/mama07ORI.bmp'
PATH_IMG_STARFISH = 'morphsnakes-master/images/seastar2.png'
PATH_IMG_LAKES = 'morphsnakes-master/images/lakes3.jpg'
PATH_IMG_CAMERA = 'morphsnakes-master/images/camera.png'
PATH_IMG_COINS = 'morphsnakes-master/images/coins.png'
PATH_ARRAY_CONFOCAL = 'morphsnakes-master/images/confocal.npy'


def visual_callback_2d(background, fig=None):
    """
    Returns a callback than can be passed as the argument `iter_callback`
    of `morphological_geodesic_active_contour` and
    `morphological_chan_vese` for visualizing the evolution
    of the levelsets. Only works for 2D images.
    
    Parameters
    ----------
    background : (M, N) array
        Image to be plotted as the background of the visual evolution.
    fig : matplotlib.figure.Figure
        Figure where results will be drawn. If not given, a new figure
        will be created.
    
    Returns
    -------
    callback : Python function
        A function that receives a levelset and updates the current plot
        accordingly. This can be passed as the `iter_callback` argument of
        `morphological_geodesic_active_contour` and
        `morphological_chan_vese`.
    
    """
    
    # Prepare the visual environment.
    if fig is None:
        fig = plt.figure()
    fig.clf()
    ax1 = fig.add_subplot(1, 2, 1)
    ax1.imshow(background, cmap=plt.cm.gray)

    ax2 = fig.add_subplot(1, 2, 2)
    ax_u = ax2.imshow(np.zeros_like(background), vmin=0, vmax=1)
    plt.pause(0.001)

    def callback(levelset):
        
        if ax1.collections:
            del ax1.collections[0]
        ax1.contour(levelset, [0.5], colors='r')
        ax_u.set_data(levelset)
        fig.canvas.draw()
        plt.pause(0.001)

    return callback


def visual_callback_3d(fig=None, plot_each=1):
    """
    Returns a callback than can be passed as the argument `iter_callback`
    of `morphological_geodesic_active_contour` and
    `morphological_chan_vese` for visualizing the evolution
    of the levelsets. Only works for 3D images.
    
    Parameters
    ----------
    fig : matplotlib.figure.Figure
        Figure where results will be drawn. If not given, a new figure
        will be created.
    plot_each : positive integer
        The plot will be updated once every `plot_each` calls to the callback
        function.
    
    Returns
    -------
    callback : Python function
        A function that receives a levelset and updates the current plot
        accordingly. This can be passed as the `iter_callback` argument of
        `morphological_geodesic_active_contour` and
        `morphological_chan_vese`.
    
    """
    
    from mpl_toolkits.mplot3d import Axes3D
    # PyMCubes package is required for `visual_callback_3d`
    try:
        import mcubes
    except ImportError:
        raise ImportError("PyMCubes is required for 3D `visual_callback_3d`")
    
    # Prepare the visual environment.
    if fig is None:
        fig = plt.figure()
    fig.clf()
    ax = fig.add_subplot(111, projection='3d')
    plt.pause(0.001)
    
    counter = [-1]

    def callback(levelset):

        counter[0] += 1
        if (counter[0] % plot_each) != 0:
            return

        if ax.collections:
            del ax.collections[0]

        coords, triangles = mcubes.marching_cubes(levelset, 0.5)
        ax.plot_trisurf(coords[:, 0], coords[:, 1], coords[:, 2],
                        triangles=triangles)
        plt.pause(0.1)

    return callback


def rgb2gray(img):
    """Convert a RGB image to gray scale."""
    return 0.2989 * img[..., 0] + 0.587 * img[..., 1] + 0.114 * img[..., 2]


def example_nodule():
    logging.info('Running: example_nodule (MorphGAC)...')
    
    # Load the image.
    img = imread(PATH_IMG_NODULE)[..., 0] / 255.0
    
    # g(I)
    gimg = ms.inverse_gaussian_gradient(img, alpha=1000, sigma=5.48)
    
    # Initialization of the level-set.
    init_ls = ms.circle_level_set(img.shape, (100, 126), 20)
    
    # Callback for visual plotting
    callback = visual_callback_2d(img)
    
    # MorphGAC. 
    ms.morphological_geodesic_active_contour(gimg, iterations=45, 
                                             init_level_set=init_ls,
                                             smoothing=1, threshold=0.31,
                                             balloon=1, iter_callback=callback)


def example_starfish():
    logging.info('Running: example_starfish (MorphGAC)...')
    
    # Load the image.
    imgcolor = imread(PATH_IMG_STARFISH) / 255.0
    img = rgb2gray(imgcolor)
    
    # g(I)
    gimg = ms.inverse_gaussian_gradient(img, alpha=1000, sigma=2)
    
    # Initialization of the level-set.
    init_ls = ms.circle_level_set(img.shape, (163, 137), 135)
    
    # Callback for visual plotting
    callback = visual_callback_2d(imgcolor)
    
    # MorphGAC. 
    ms.morphological_geodesic_active_contour(gimg, iterations=100, 
                                             init_level_set=init_ls,
                                             smoothing=2, threshold=0.3,
                                             balloon=-1, iter_callback=callback)


def example_coins():
    logging.info('Running: example_coins (MorphGAC)...')
    
    # Load the image.
    img = imread(PATH_IMG_COINS) / 255.0
    
    # g(I)
    gimg = ms.inverse_gaussian_gradient(img)
    
    # Manual initialization of the level set
    init_ls = np.zeros(img.shape, dtype=np.int8)
    init_ls[10:-10, 10:-10] = 1
    
    # Callback for visual plotting
    callback = visual_callback_2d(img)
    
    # MorphGAC. 
    ms.morphological_geodesic_active_contour(gimg, 230, init_ls,
                                             smoothing=1, threshold=0.69,
                                             balloon=-1, iter_callback=callback)


def example_lakes():
    logging.info('Running: example_lakes (MorphACWE)...')
    
    # Load the image.
    imgcolor = imread(PATH_IMG_LAKES)/255.0
    img = rgb2gray(imgcolor)
    
    # MorphACWE does not need g(I)
    
    # Initialization of the level-set.
    init_ls = ms.circle_level_set(img.shape, (80, 170), 25)
    
    # Callback for visual plotting
    callback = visual_callback_2d(imgcolor)

    # Morphological Chan-Vese (or ACWE)
    ms.morphological_chan_vese(img, iterations=200,
                               init_level_set=init_ls,
                               smoothing=3, lambda1=1, lambda2=1,
                               iter_callback=callback)


def example_camera():
    """
    Example with `morphological_chan_vese` with using the default
    initialization of the level-set.
    """
    
    logging.info('Running: example_camera (MorphACWE)...')

    # Load the image.
    img = imread(PATH_IMG_CAMERA)/255.0

    # Callback for visual plotting
    callback = visual_callback_2d(img)

    # Morphological Chan-Vese (or ACWE)
    ms.morphological_chan_vese(img, 35,
                               smoothing=3, lambda1=1, lambda2=1,
                               iter_callback=callback)


def example_confocal3d():
    logging.info('Running: example_confocal3d (MorphACWE)...')
    
    # Load the image.
    img = np.load(PATH_ARRAY_CONFOCAL)
    
    # Initialization of the level-set.
    init_ls = ms.circle_level_set(img.shape, (30, 50, 80), 25)

    # Callback for visual plotting
    callback = visual_callback_3d(plot_each=20)

    # Morphological Chan-Vese (or ACWE)
    ms.morphological_chan_vese(img, iterations=150,
                               init_level_set=init_ls,
                               smoothing=1, lambda1=1, lambda2=2,
                               iter_callback=callback)

if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
    example_nodule()
    example_starfish()
    example_coins()
    example_lakes()
    example_camera()
    
    # Uncomment the following line to see a 3D example
    # This is skipped by default since mplot3d is VERY slow plotting 3d meshes
    # example_confocal3d()
    
    logging.info("Done.")
    plt.show()

After running this (and yes, I put the images in the correct place) it produced the following error:

NameError                                 Traceback (most recent call last)
<ipython-input-7-e6b7bfe1611c> in <module>()
    263 if __name__ == '__main__':
    264     logging.basicConfig(level=logging.DEBUG)
--> 265     example_nodule()
    266     example_starfish()
    267     example_coins()

<ipython-input-7-e6b7bfe1611c> in example_nodule()
    153                                              init_level_set=init_ls,
    154                                              smoothing=1, threshold=0.31,
--> 155                                              balloon=1, iter_callback=callback)
    156 
    157 

C:\ProgramData\Anaconda3\lib\site-packages\morphsnakes.py in morphological_geodesic_active_contour(gimage, iterations, init_level_set, smoothing, threshold, balloon, iter_callback)
    468     u = np.int8(init_level_set > 0)
    469 
--> 470     iter_callback(u)
    471 
    472     for _ in range(iterations):

<ipython-input-7-e6b7bfe1611c> in callback(levelset)
     65         ax_u.set_data(levelset)
     66         fig.canvas.draw()
---> 67         plt.pause(0.001)
     68 
     69     return callback

C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\pyplot.py in pause(interval)
    292         canvas.start_event_loop(interval)
    293     else:
--> 294         time.sleep(interval)
    295 
    296 

NameError: name 'time' is not defined

So, of course, first I check C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\pyplot.py because:

--> 294 time.sleep(interval)

So, I discover that yes, the time module is in fact imported in this script. It makes no sense why this error is being thrown. And, it prevents me from testing any of the examples. What can I do to solve it?

Open contours

Can this code/algorithm be modified to work with open contours? E.g. a curve rather than a closed shape.

implementing on 1. multiple objects and 2. donuts, and 3. a question about precision

Hi. Congratulations on such an impressive method.

I am very new to coding (and only know a little Python) and relatively new to image processing.

Here is one of my results of running ACWE on an image:

folio_041r_cis_acwe_sm

I am trying to segment the ink -- the handwritten letters -- on images with text like this, and wondering three things:

  1. How could I capture multiple letters (even allowing for connected-letters, like the "ic" above) as separate objects at the same time? Like in the image above, how could I capture "s" separately from "ci"?

    Or imagine a word next to these letters (which there is in the original). How could I run morphsnakes to also do its work on those letters and words as well?

  2. How would one isolate a donut-shaped object (like "ci" is, almost, in effect; or like a letter "o" or "d" would be)? I mean, how could I segment the ink without the inside as part of the result? Or even get the letter "s" to hug its borders better?

  3. Lastly, how could I improve the precision of these results, so that tips aren't cropped (like the right side of the "i", top and bottom; or like the upper-right tip of the "s")?

(For one thing, I was wondering if donut-shapes hurt precision -- if the entire interior is being weighed against the exterior. That is, using my example image, a significant area of "not ink" in the donut hole of "ic" is being factored into results.)

Thank you for your consideration.

got AttributeError: 'numpy.ndarray' object has no attribute 'clf' for 3d input

Hi,

I have got this error when dealing with 3d input data.

Traceback (most recent call last):

File "", line 4, in
iter_callback=visual_callback_3d)

File "/Users/renne/anaconda3/lib/python3.7/site-packages/morphsnakes.py", line 346, in morphological_chan_vese
iter_callback(u)

File "", line 37, in visual_callback_3d
fig.clf()

AttributeError: 'numpy.ndarray' object has no attribute 'clf'

Could you please help answer it?

Many thanks,

Renne

Morphsnake cannot be imported after installation

I get morphsnake installation for my Docker image in the requirements.txt file. You can see it. The download is successful:
Successfully built morphsnakes
Installing collected packages: morphsnakes
Attempting uninstall: morphsnakes
Found existing installation: morphsnakes 0.0.9
Uninstalling morphsnakes-0.0.9:
Successfully uninstalled morphsnakes-0.0.9
Successfully installed morphsnakes-0.0.6

but i can't use the library. i'm trying with thos code to try the library:

try:
import morphsnakes
print("morphsnakes not yes intalled")
except ImportError:
print("morphsnakes cannot load please check your installed")

the output on the terminal: morphsnakes cannot load please check your installed

mean i get problem right from this library. can you help me? i mean i tried another way with save morpsnake.py file in my folder. but long time for make the outline of image when i just save the file and not the library. please corect me if my thinking is wrong.I will look forward to your reply

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.