mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-12 23:29:44 +08:00
Short tutorial on segmentation for the user guide +
example on the same subject used to generate the figures
This commit is contained in:
@@ -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()
|
||||
@@ -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 |
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user