Short tutorial on segmentation for the user guide +

example on the same subject used to generate the figures
This commit is contained in:
emmanuelle
2011-12-07 20:57:29 +01:00
parent 0fa504f320
commit d5994c6715
5 changed files with 279 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
Longer examples and demonstrations
----------------------------------
@@ -0,0 +1,135 @@
"""
===============================================================
Comparing edge-based segmentation and region-based segmentation
===============================================================
In this example, we will see how to segment objects from a background.
We use the ``coins`` image from ``skimage.data``. This image shows
several coins outlined against a darker background. The segmentation of
the coins cannot be done directly from the histogram of grey values,
because the background shares enough grey levels with the coins that a
thresholding segmentation is not sufficient. Simply thresholding the image
leads either to missing significant parts of the coins, or to getting parts
of the background together with the coins.
We first try an edge-based segmentation. We use the Canny detector to
delineate the contours of the coins. These contours are filled using
mathematical morphology (``scipy.ndimage.binary_fill_holes``). Small spurious
objects are easily removed by applying a threshold on the size of
unconnected objects. However, this method is not very robust, since contours
that are not perfectly closed are not filled correctly. This happens for one
of the coins.
We therefore try a second method, that is region-based. Here we use the
watershed transform. An elevation map is provided by the Sobel gradient
of the image. Markers of the background and the coins are determined from
the extreme parts of the histogram of grey values.
This second method works even better, and the coins can be segmented and
labeled individually.
"""
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt
import skimage
from skimage.filter import canny, sobel
from skimage.morphology import watershed
#------------------ Loading data --------------------------------
from skimage import data
coins = data.coins()
#------------ Histogram of grey values ---------------------------
histo = np.histogram(coins, bins=np.arange(0, 256))
plt.figure(figsize=(8, 3))
plt.subplot(121)
plt.imshow(coins, cmap=plt.cm.gray, interpolation='nearest')
plt.axis('off')
plt.subplot(122)
plt.plot(histo[1][:-1], histo[0], lw=2)
plt.title('histogram of grey values')
#------------------ Tentative thresholding --------------------------------
plt.figure(figsize=(6, 3))
plt.subplot(121)
plt.imshow(coins > 100, cmap=plt.cm.gray, interpolation='nearest')
plt.title('coins > 100')
plt.axis('off')
plt.subplot(122)
plt.imshow(coins > 150, cmap=plt.cm.gray, interpolation='nearest')
plt.title('coins > 150')
plt.axis('off')
plt.subplots_adjust(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0,
right=1)
#------------------ Edge-based segmentation --------------------------------
edges = canny(coins/255.)
fill_coins = ndimage.binary_fill_holes(edges)
label_objects, nb_labels = ndimage.label(fill_coins)
sizes = np.bincount(label_objects.ravel())
mask_sizes = sizes > 20
mask_sizes[0] = 0
coins_cleaned = mask_sizes[label_objects]
plt.figure(figsize=(7, 3.))
plt.subplot(131)
plt.imshow(edges, cmap=plt.cm.gray, interpolation='nearest')
plt.axis('off')
plt.title('Canny detector')
plt.subplot(132)
plt.imshow(fill_coins, cmap=plt.cm.gray, interpolation='nearest')
plt.axis('off')
plt.title('Filling the holes')
plt.subplot(133)
plt.imshow(coins_cleaned, cmap=plt.cm.gray, interpolation='nearest')
plt.axis('off')
plt.title('Removing small objects')
plt.subplots_adjust(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0,
right=1)
#------------------ Region-based segmentation --------------------------------
markers = np.zeros_like(coins)
markers[coins < 30] = 1
markers[coins > 150] = 2
elevation_map = sobel(coins)
segmentation = watershed(elevation_map, markers)
plt.figure(figsize=(7, 3))
plt.subplot(131)
plt.imshow(markers, cmap=plt.cm.spectral, interpolation='nearest')
plt.axis('off')
plt.title('markers')
plt.subplot(132)
plt.imshow(elevation_map, cmap=plt.cm.jet, interpolation='nearest')
plt.axis('off')
plt.title('elevation_map')
plt.subplot(133)
plt.imshow(segmentation, cmap=plt.cm.gray, interpolation='nearest')
plt.axis('off')
plt.title('segmentation')
plt.subplots_adjust(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0,
right=1)
#------------------ Labeling the coins --------------------------------
labeled_coins, _ = ndimage.label(segmentation - 1)
plt.figure(figsize=(6, 4))
plt.imshow(labeled_coins, cmap=plt.cm.spectral, interpolation='nearest')
plt.axis('off')
plt.show()
+1
View File
@@ -5,4 +5,5 @@ User Guide
:maxdepth: 1
user_guide/data_types
user_guide/segmentation
user_guide/plugins
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

+141
View File
@@ -0,0 +1,141 @@
Image Segmentation
------------------
Image segmentation is the task of labeling the pixels of objects of
interest in an image.
In this tutorial, we will see how to segment objects from a background.
We use the ``coins`` image from ``skimage.data``. This image shows
several coins outlined against a darker background. The segmentation of
the coins cannot be done directly from the histogram of grey values,
because the background shares enough grey levels with the coins that a
thresholding segmentation is not sufficient.
.. image:: ../../_images/plot_coins_segmentation_1.png
:target: ../auto_examples/applications/plot_coins_segmentation.html
:align: center
::
>>> import numpy as np
>>> from skimage import data
>>> coins = data.coins()
>>> histo = np.histogram(coins, bins=np.arange(0, 256))
Simply thresholding the image leads either to missing significant parts
of the coins, or to getting parts of the background together with the
coins. This is due to the inhomogeneous lighting of the image.
.. image:: ../../_images/plot_coins_segmentation_2.png
:target: ../auto_examples/applications/plot_coins_segmentation.html
:align: center
A first idea is to take advantage of the local contrast, that is, to
use the gradients rather than the grey values.
Edge-based segmentation
~~~~~~~~~~~~~~~~~~~~~~~
Let us first try to detect edges than enclose the coins. For edge
detection, we use the `Canny detector
<http://en.wikipedia.org/wiki/Canny_edge_detector>`_ of ``skimage.filter.canny``
::
>>> from skimage.filter import canny
>>> edges = canny(coins/255.)
As the background is very smooth, almost all edges are found at the
boundary of the coins, or inside the coins.
Now that we have contours that delineate the outer boundary of the coins,
we fill the inner part of the coins using the
``ndimage.binary_fill_holes`` function, that uses mathematical morphology
to fill the holes.
::
>>> from scipy import ndimage
>>> fill_coins = ndimage.binary_fill_holes(edges)
.. image:: ../../_images/plot_coins_segmentation_3.png
:target: ../auto_examples/applications/plot_coins_segmentation.html
:align: center
Most coins are well segmented out of the background. Small objects from
the background can be easily removed using the ``ndimage.label``
function, and removing objects smaller than a small threshold.
::
>>> label_objects, nb_labels = ndimage.label(fill_coins)
>>> sizes = np.bincount(label_objects.ravel())
>>> mask_sizes = sizes > 20
>>> mask_sizes[0] = 0
>>> coins_cleaned = mask_sizes[label_objects]
However, the segmentation is not very satisfying, since one of the coins
has not been segmented correctly at all. The reason is that the contour
that we got from the Canny detector was not completely closed, therefore
the filling function did not fill the inner part of the coin.
Therefore, this segmentation method is not very robust: if we miss a
single pixel of the contour of the object, we will not be able to fill
it. Of course, we could try first to dilate the contours in order to
close them. However, it is preferable to try a more robust method.
Region-based segmentation
~~~~~~~~~~~~~~~~~~~~~~~~~
Let us first determine markers of the coins and the background. These
markers are pixels that we can label unambiguously with one of the
phases. Here, the markers are found at the two extreme parts of the
histogram of grey values:
::
>>> markers = np.zeros_like(coins)
>>> markers[coins < 30] = 1
>>> markers[coins > 150] = 2
We will use these markers in a watershed segmentation. The name watershed
comes from an analogy with hydrology. The `watershed transform
<http://en.wikipedia.org/wiki/Watershed_%28image_processing%29>`_ floods
an image of elevation from markers, in order to determine the catchment
basins of these markers. Watershed lines separate these catchment basins,
and correspond to the segmentation that is looked for.
The choice of the elevation map is critical for a good segmentation.
Here, the amplitude of the gradient provides a good elevation map. We
use the Sobel operator for computing the amplitude of the gradient::
>>> from skimage.filter import sobel
>>> elevation_map = sobel(coins)
From the 3-D surface plot shown below, we see that high barriers separate
well the coins from the background.
.. image:: elevation_map.jpg
:align: center
Let us now compute the watershed transform::
>>> from skimage.morphology import watershed
>>> segmentation = watershed(elevation_map, markers)
.. image:: ../../_images/plot_coins_segmentation_4.png
:target: ../auto_examples/applications/plot_coins_segmentation.html
:align: center
With this method, the result is satisfying for all coins. Even if the
markers for the background were not well distributed, the barriers in the
elevation map were high enough for these markers to flood the entire
background.
We can now label all the coins one by one using ``ndimage.label``::
>>> labeled_coins, _ = ndimage.label(segmentation - 1)
.. image:: ../../_images/plot_coins_segmentation_5.png
:target: ../auto_examples/applications/plot_coins_segmentation.html
:align: center