From 84074871f2b78eb8290e24b30aeec72b90de2ccc Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Tue, 15 May 2012 22:24:54 -0400 Subject: [PATCH] DOC: convert segmentation example to tutorial-style example. --- .../applications/plot_coins_segmentation.py | 183 +++++++++++------- 1 file changed, 111 insertions(+), 72 deletions(-) diff --git a/doc/examples/applications/plot_coins_segmentation.py b/doc/examples/applications/plot_coins_segmentation.py index f2d65658..f46bbd33 100644 --- a/doc/examples/applications/plot_coins_segmentation.py +++ b/doc/examples/applications/plot_coins_segmentation.py @@ -3,57 +3,40 @@ 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 merging parts -of the background 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. - +In this example, we will see how to segment objects from a background. We use +the ``coins`` image from ``skimage.data``, which shows several coins outlined +against a darker background. """ - 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)) +coins = data.coins() +hist = 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.plot(hist[1][:-1], hist[0], lw=2) plt.title('histogram of grey values') -#------------------ Tentative thresholding -------------------------------- +""" +.. image:: PLOT2RST.current_figure + +Thresholding +============ + +A simple way to segment the coins is to choose a threshold based on the +histogram of grey values. Unfortunately, thresholding this image gives a binary +image that either misses significant parts of the coins or merges parts of the +background with the coins: + +""" + plt.figure(figsize=(6, 3)) plt.subplot(121) plt.imshow(coins > 100, cmap=plt.cm.gray, interpolation='nearest') @@ -63,73 +46,126 @@ plt.subplot(122) plt.imshow(coins > 150, cmap=plt.cm.gray, interpolation='nearest') plt.title('coins > 150') plt.axis('off') +margins = dict(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0, right=1) +plt.subplots_adjust(**margins) -plt.subplots_adjust(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0, - right=1) +""" +.. image:: PLOT2RST.current_figure -#------------------ Edge-based segmentation -------------------------------- +Edge-based segmentation +======================= + +Next, we try to delineate the contours of the coins using edge-based +segmentation. To do this, we first get the edges of features using the Canny +edge-detector. +""" + +from skimage.filter import canny edges = canny(coins/255.) +plt.figure(figsize=(4, 3)) +plt.imshow(edges, cmap=plt.cm.gray, interpolation='nearest') +plt.axis('off') +plt.title('Canny detector') + +""" +.. image:: PLOT2RST.current_figure + +These contours are then filled using mathematical morphology. +""" + +from scipy import ndimage + fill_coins = ndimage.binary_fill_holes(edges) +plt.figure(figsize=(4, 3)) +plt.imshow(fill_coins, cmap=plt.cm.gray, interpolation='nearest') +plt.axis('off') +plt.title('Filling the holes') + +""" +.. image:: PLOT2RST.current_figure + +Small spurious objects are easily removed by setting a minimum size for valid +objects. +""" + 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.figure(figsize=(4, 3)) 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) + +""" +.. image:: PLOT2RST.current_figure + +However, this method is not very robust, since contours that are not perfectly +closed are not filled correctly, as is the case for one unfilled coin above. -#------------------ Region-based segmentation -------------------------------- +Region-based segmentation +========================= + +We therefore try a region-based method using the +watershed transform. First, we find an elevation map using the Sobel gradient of the +image. + +""" + +from skimage.filter import sobel + +elevation_map = sobel(coins) + +plt.figure(figsize=(4, 3)) +plt.imshow(elevation_map, cmap=plt.cm.jet, interpolation='nearest') +plt.axis('off') +plt.title('elevation_map') + +""" +.. image:: PLOT2RST.current_figure + +Next we find markers of the background and the coins based on the extreme parts +of the histogram of grey values. +""" 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.figure(figsize=(4, 3)) 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) + +""" +.. image:: PLOT2RST.current_figure + +Finally, we use the watershed transform to fill regions of the elevation map starting from the markers determined above: + +""" +from skimage.morphology import watershed +segmentation = watershed(elevation_map, markers) + +plt.figure(figsize=(4, 3)) 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) +""" +.. image:: PLOT2RST.current_figure -# ------------------- Removing a few small holes --------------------- +This last method works even better, and the coins can be segmented and +labeled individually. + +""" segmentation = ndimage.binary_fill_holes(segmentation - 1) - -#------------------ Labeling the coins -------------------------------- labeled_coins, _ = ndimage.label(segmentation) plt.figure(figsize=(6, 3)) @@ -141,8 +177,11 @@ plt.subplot(122) plt.imshow(labeled_coins, cmap=plt.cm.spectral, interpolation='nearest') plt.axis('off') -plt.subplots_adjust(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0, - right=1) +plt.subplots_adjust(**margins) +""" +.. image:: PLOT2RST.current_figure + +""" plt.show()