mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-23 13:10:18 +08:00
Merge pull request #215 from tonysyu/morph-reconstruction
ENH: Add morphological reconstruction.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
"""
|
||||
===============================
|
||||
Filling holes and finding peaks
|
||||
===============================
|
||||
|
||||
In this example, we fill holes (i.e. isolated, dark spots) in an image using
|
||||
morphological reconstruction by erosion. Erosion expands the minimal values of
|
||||
the seed image until it encounters a mask image. Thus, the seed image and mask
|
||||
image represent the maximum and minimum possible values of the reconstructed
|
||||
image.
|
||||
|
||||
We start with an image containing both peaks and holes:
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage.exposure import rescale_intensity
|
||||
|
||||
image = data.moon()
|
||||
# Rescale image intensity so that we can see dim features.
|
||||
image = rescale_intensity(image, in_range=(50, 200))
|
||||
|
||||
# convenience function for plotting images
|
||||
def imshow(image, **kwargs):
|
||||
plt.figure(figsize=(5, 4))
|
||||
plt.imshow(image, **kwargs)
|
||||
plt.axis('off')
|
||||
|
||||
imshow(image)
|
||||
plt.title('original image')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Now we need to create the seed image, where the minima represent the starting
|
||||
points for erosion. To fill holes, we initialize the seed image to the maximum
|
||||
value of the original image. Along the borders, however, we use the original
|
||||
values of the image. These border pixels will be the starting points for the
|
||||
erosion process. We then limit the erosion by setting the mask to the values
|
||||
of the original image.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from skimage.morphology import reconstruction
|
||||
|
||||
seed = np.copy(image)
|
||||
seed[1:-1, 1:-1] = image.max()
|
||||
mask = image
|
||||
|
||||
filled = reconstruction(seed, mask, method='erosion')
|
||||
|
||||
imshow(filled, vmin=image.min(), vmax=image.max())
|
||||
plt.title('after filling holes')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
As shown above, eroding inward from the edges removes holes, since (by
|
||||
definition) holes are surrounded by pixels of brighter value. Finally, we can
|
||||
isolate the dark regions by subtracting the reconstructed image from the
|
||||
original image.
|
||||
"""
|
||||
|
||||
imshow(image - filled)
|
||||
plt.title('holes')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Alternatively, we can find bright spots in an image using morphological
|
||||
reconstruction by dilation. Dilation is the inverse of erosion and expands the
|
||||
*maximal* values of the seed image until it encounters a mask image. Since this
|
||||
is an inverse operation, we initialize the seed image to the minimum image
|
||||
intensity instead of the maximum. The remainder of the process is the same.
|
||||
"""
|
||||
|
||||
seed = np.copy(image)
|
||||
seed[1:-1, 1:-1] = image.min()
|
||||
rec = reconstruction(seed, mask, method='dilation')
|
||||
imshow(image - rec)
|
||||
plt.title('peaks')
|
||||
plt.show()
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
"""
|
||||
@@ -0,0 +1,113 @@
|
||||
"""
|
||||
=========================
|
||||
Filtering regional maxima
|
||||
=========================
|
||||
|
||||
Here, we use morphological reconstruction to create a background image, which
|
||||
we can subtract from the original image to isolate bright features (regional
|
||||
maxima).
|
||||
|
||||
First we try reconstruction by dilation starting at the edges of the image. We
|
||||
initialize a seed image to the minimum intensity of the image, and set its
|
||||
border to be the pixel values in the original image. These maximal pixels will
|
||||
get dilated in order to reconstruct the background image.
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
from skimage import data
|
||||
from skimage import img_as_float
|
||||
from skimage.morphology import reconstruction
|
||||
from scipy.ndimage import gaussian_filter
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Convert to float: Important for subtraction later which won't work with uint8
|
||||
image = img_as_float(data.coins())
|
||||
image = gaussian_filter(image, 1)
|
||||
|
||||
seed = np.copy(image)
|
||||
seed[1:-1, 1:-1] = image.min()
|
||||
mask = image
|
||||
|
||||
dilated = reconstruction(seed, mask, method='dilation')
|
||||
|
||||
"""
|
||||
Subtracting the dilated image leaves an image with just the coins and a flat,
|
||||
black background, as shown below.
|
||||
"""
|
||||
|
||||
fig, (ax1, ax2, ax3) = plt.subplots(ncols=3, figsize=(8, 2.5))
|
||||
|
||||
ax1.imshow(image)
|
||||
ax1.set_title('original image')
|
||||
ax1.axis('off')
|
||||
|
||||
ax2.imshow(dilated, vmin=image.min(), vmax=image.max())
|
||||
ax2.set_title('dilated')
|
||||
ax2.axis('off')
|
||||
|
||||
ax3.imshow(image - dilated)
|
||||
ax3.set_title('image - dilated')
|
||||
ax3.axis('off')
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
"""
|
||||
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Although the features (i.e. the coins) are clearly isolated, the coins
|
||||
surrounded by a bright background in the original image are dimmer in the
|
||||
subtracted image. We can attempt to correct this using a different seed image.
|
||||
|
||||
Instead of creating a seed image with maxima along the image border, we can use
|
||||
the features of the image itself to seed the reconstruction process. Here, the
|
||||
seed image is the original image minus a fixed value, ``h``.
|
||||
"""
|
||||
|
||||
h = 0.4
|
||||
seed = image - h
|
||||
dilated = reconstruction(seed, mask, method='dilation')
|
||||
hdome = image - dilated
|
||||
|
||||
"""
|
||||
To get a feel for the reconstruction process, we plot the intensity of the
|
||||
mask, seed, and dilated images along a slice of the image (indicated by red
|
||||
line).
|
||||
"""
|
||||
|
||||
fig, (ax1, ax2, ax3) = plt.subplots(ncols=3, figsize=(8, 2.5))
|
||||
|
||||
yslice = 197
|
||||
|
||||
ax1.plot(mask[yslice], '0.5', label='mask')
|
||||
ax1.plot(seed[yslice], 'k', label='seed')
|
||||
ax1.plot(dilated[yslice], 'r', label='dilated')
|
||||
ax1.set_ylim(-0.2, 2)
|
||||
ax1.set_title('image slice')
|
||||
ax1.set_xticks([])
|
||||
ax1.legend()
|
||||
|
||||
ax2.imshow(dilated, vmin=image.min(), vmax=image.max())
|
||||
ax2.axhline(yslice, color='r', alpha=0.4)
|
||||
ax2.set_title('dilated')
|
||||
ax2.axis('off')
|
||||
|
||||
ax3.imshow(hdome)
|
||||
ax3.axhline(yslice, color='r', alpha=0.4)
|
||||
ax3.set_title('image - dilated')
|
||||
ax3.axis('off')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
As you can see in the image slice, each coin is given a different baseline
|
||||
intensity in the reconstructed image; this is because we used the local
|
||||
intensity (shifted by ``h``) as a seed value. As a result, the coins in the
|
||||
subtracted image have similar pixel intensities. The final result is known as
|
||||
the h-dome of an image since this tends to isolate regional maxima of height
|
||||
``h``. This operation is particularly useful when your images are unevenly
|
||||
illuminated.
|
||||
"""
|
||||
Reference in New Issue
Block a user