mirror of
https://github.com/wassname/scikit-image.git
synced 2026-09-10 12:35:06 +08:00
Added sections to gallery of examples
Modified travis_script.sh to account for the new structure of the gallery Added README.txt files in directories of gallery examples Fixed references to gallery images in user guide pages Fixed broken links
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
Edges and lines
|
||||
---------------
|
||||
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
===================
|
||||
Canny edge detector
|
||||
===================
|
||||
|
||||
The Canny filter is a multi-stage edge detector. It uses a filter based on the
|
||||
derivative of a Gaussian in order to compute the intensity of the gradients.The
|
||||
Gaussian reduces the effect of noise present in the image. Then, potential
|
||||
edges are thinned down to 1-pixel curves by removing non-maximum pixels of the
|
||||
gradient magnitude. Finally, edge pixels are kept or removed using hysteresis
|
||||
thresholding on the gradient magnitude.
|
||||
|
||||
The Canny has three adjustable parameters: the width of the Gaussian (the
|
||||
noisier the image, the greater the width), and the low and high threshold for
|
||||
the hysteresis thresholding.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy import ndimage as ndi
|
||||
|
||||
from skimage import feature
|
||||
|
||||
|
||||
# Generate noisy image of a square
|
||||
im = np.zeros((128, 128))
|
||||
im[32:-32, 32:-32] = 1
|
||||
|
||||
im = ndi.rotate(im, 15, mode='constant')
|
||||
im = ndi.gaussian_filter(im, 4)
|
||||
im += 0.2 * np.random.random(im.shape)
|
||||
|
||||
# Compute the Canny filter for two values of sigma
|
||||
edges1 = feature.canny(im)
|
||||
edges2 = feature.canny(im, sigma=3)
|
||||
|
||||
# display results
|
||||
fig, (ax1, ax2, ax3) = plt.subplots(nrows=1, ncols=3, figsize=(8, 3), sharex=True, sharey=True)
|
||||
|
||||
ax1.imshow(im, cmap=plt.cm.jet)
|
||||
ax1.axis('off')
|
||||
ax1.set_title('noisy image', fontsize=20)
|
||||
|
||||
ax2.imshow(edges1, cmap=plt.cm.gray)
|
||||
ax2.axis('off')
|
||||
ax2.set_title('Canny filter, $\sigma=1$', fontsize=20)
|
||||
|
||||
ax3.imshow(edges2, cmap=plt.cm.gray)
|
||||
ax3.axis('off')
|
||||
ax3.set_title('Canny filter, $\sigma=3$', fontsize=20)
|
||||
|
||||
fig.subplots_adjust(wspace=0.02, hspace=0.02, top=0.9,
|
||||
bottom=0.02, left=0.02, right=0.98)
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,154 @@
|
||||
"""
|
||||
========================================
|
||||
Circular and Elliptical Hough Transforms
|
||||
========================================
|
||||
|
||||
The Hough transform in its simplest form is a `method to detect
|
||||
straight lines <http://en.wikipedia.org/wiki/Hough_transform>`__
|
||||
but it can also be used to detect circles or ellipses.
|
||||
The algorithm assumes that the edge is detected and it is robust against
|
||||
noise or missing points.
|
||||
|
||||
Circle detection
|
||||
================
|
||||
|
||||
In the following example, the Hough transform is used to detect
|
||||
coin positions and match their edges. We provide a range of
|
||||
plausible radii. For each radius, two circles are extracted and
|
||||
we finally keep the five most prominent candidates.
|
||||
The result shows that coin positions are well-detected.
|
||||
|
||||
|
||||
Algorithm overview
|
||||
------------------
|
||||
|
||||
Given a black circle on a white background, we first guess its
|
||||
radius (or a range of radii) to construct a new circle.
|
||||
This circle is applied on each black pixel of the original picture
|
||||
and the coordinates of this circle are voting in an accumulator.
|
||||
From this geometrical construction, the original circle center
|
||||
position receives the highest score.
|
||||
|
||||
Note that the accumulator size is built to be larger than the
|
||||
original picture in order to detect centers outside the frame.
|
||||
Its size is extended by two times the larger radius.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data, color
|
||||
from skimage.transform import hough_circle
|
||||
from skimage.feature import peak_local_max, canny
|
||||
from skimage.draw import circle_perimeter
|
||||
from skimage.util import img_as_ubyte
|
||||
|
||||
|
||||
# Load picture and detect edges
|
||||
image = img_as_ubyte(data.coins()[0:95, 70:370])
|
||||
edges = canny(image, sigma=3, low_threshold=10, high_threshold=50)
|
||||
|
||||
fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(5, 2))
|
||||
|
||||
# Detect two radii
|
||||
hough_radii = np.arange(15, 30, 2)
|
||||
hough_res = hough_circle(edges, hough_radii)
|
||||
|
||||
centers = []
|
||||
accums = []
|
||||
radii = []
|
||||
|
||||
for radius, h in zip(hough_radii, hough_res):
|
||||
# For each radius, extract two circles
|
||||
num_peaks = 2
|
||||
peaks = peak_local_max(h, num_peaks=num_peaks)
|
||||
centers.extend(peaks)
|
||||
accums.extend(h[peaks[:, 0], peaks[:, 1]])
|
||||
radii.extend([radius] * num_peaks)
|
||||
|
||||
# Draw the most prominent 5 circles
|
||||
image = color.gray2rgb(image)
|
||||
for idx in np.argsort(accums)[::-1][:5]:
|
||||
center_x, center_y = centers[idx]
|
||||
radius = radii[idx]
|
||||
cx, cy = circle_perimeter(center_y, center_x, radius)
|
||||
image[cy, cx] = (220, 20, 20)
|
||||
|
||||
ax.imshow(image, cmap=plt.cm.gray)
|
||||
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Ellipse detection
|
||||
=================
|
||||
|
||||
In this second example, the aim is to detect the edge of a coffee cup.
|
||||
Basically, this is a projection of a circle, i.e. an ellipse.
|
||||
The problem to solve is much more difficult because five parameters have to be
|
||||
determined, instead of three for circles.
|
||||
|
||||
|
||||
Algorithm overview
|
||||
------------------
|
||||
|
||||
The algorithm takes two different points belonging to the ellipse. It assumes
|
||||
that it is the main axis. A loop on all the other points determines how much
|
||||
an ellipse passes to them. A good match corresponds to high accumulator values.
|
||||
|
||||
A full description of the algorithm can be found in reference [1]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Xie, Yonghong, and Qiang Ji. "A new efficient ellipse detection
|
||||
method." Pattern Recognition, 2002. Proceedings. 16th International
|
||||
Conference on. Vol. 2. IEEE, 2002
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data, color
|
||||
from skimage.feature import canny
|
||||
from skimage.transform import hough_ellipse
|
||||
from skimage.draw import ellipse_perimeter
|
||||
|
||||
# Load picture, convert to grayscale and detect edges
|
||||
image_rgb = data.coffee()[0:220, 160:420]
|
||||
image_gray = color.rgb2gray(image_rgb)
|
||||
edges = canny(image_gray, sigma=2.0,
|
||||
low_threshold=0.55, high_threshold=0.8)
|
||||
|
||||
# Perform a Hough Transform
|
||||
# The accuracy corresponds to the bin size of a major axis.
|
||||
# The value is chosen in order to get a single high accumulator.
|
||||
# The threshold eliminates low accumulators
|
||||
result = hough_ellipse(edges, accuracy=20, threshold=250,
|
||||
min_size=100, max_size=120)
|
||||
result.sort(order='accumulator')
|
||||
|
||||
# Estimated parameters for the ellipse
|
||||
best = list(result[-1])
|
||||
yc, xc, a, b = [int(round(x)) for x in best[1:5]]
|
||||
orientation = best[5]
|
||||
|
||||
# Draw the ellipse on the original image
|
||||
cy, cx = ellipse_perimeter(yc, xc, a, b, orientation)
|
||||
image_rgb[cy, cx] = (0, 0, 255)
|
||||
# Draw the edge (white) and the resulting ellipse (red)
|
||||
edges = color.gray2rgb(edges)
|
||||
edges[cy, cx] = (250, 0, 0)
|
||||
|
||||
fig2, (ax1, ax2) = plt.subplots(ncols=2, nrows=1, figsize=(8, 4), sharex=True, sharey=True, subplot_kw={'adjustable':'box-forced'})
|
||||
|
||||
ax1.set_title('Original picture')
|
||||
ax1.imshow(image_rgb)
|
||||
|
||||
ax2.set_title('Edge (white) and result (red)')
|
||||
ax2.imshow(edges)
|
||||
|
||||
plt.show()
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
"""
|
||||
@@ -0,0 +1,41 @@
|
||||
"""
|
||||
===============
|
||||
Contour finding
|
||||
===============
|
||||
|
||||
``skimage.measure.find_contours`` uses a marching squares method to find
|
||||
constant valued contours in an image. Array values are linearly interpolated
|
||||
to provide better precision of the output contours. Contours which intersect
|
||||
the image edge are open; all others are closed.
|
||||
|
||||
The `marching squares algorithm
|
||||
<http://www.essi.fr/~lingrand/MarchingCubes/algo.html>`__ is a special case of
|
||||
the marching cubes algorithm (Lorensen, William and Harvey E. Cline. Marching
|
||||
Cubes: A High Resolution 3D Surface Construction Algorithm. Computer Graphics
|
||||
(SIGGRAPH 87 Proceedings) 21(4) July 1987, p. 163-170).
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import measure
|
||||
|
||||
|
||||
# Construct some test data
|
||||
x, y = np.ogrid[-np.pi:np.pi:100j, -np.pi:np.pi:100j]
|
||||
r = np.sin(np.exp((np.sin(x)**3 + np.cos(y)**2)))
|
||||
|
||||
# Find contours at a constant value of 0.8
|
||||
contours = measure.find_contours(r, 0.8)
|
||||
|
||||
# Display the image and plot all contours found
|
||||
fig, ax = plt.subplots()
|
||||
ax.imshow(r, interpolation='nearest', cmap=plt.cm.gray)
|
||||
|
||||
for n, contour in enumerate(contours):
|
||||
ax.plot(contour[:, 1], contour[:, 0], linewidth=2)
|
||||
|
||||
ax.axis('image')
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
plt.show()
|
||||
@@ -0,0 +1,53 @@
|
||||
"""
|
||||
===========
|
||||
Convex Hull
|
||||
===========
|
||||
|
||||
The convex hull of a binary image is the set of pixels included in the
|
||||
smallest convex polygon that surround all white pixels in the input.
|
||||
|
||||
In this example, we show how the input pixels (white) get filled in by the
|
||||
convex hull (white and grey).
|
||||
|
||||
A good overview of the algorithm is given on `Steve Eddin's blog
|
||||
<http://blogs.mathworks.com/steve/2011/10/04/binary-image-convex-hull-algorithm-notes/>`__.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.morphology import convex_hull_image
|
||||
|
||||
|
||||
image = np.array(
|
||||
[[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 1, 0, 0, 0, 0],
|
||||
[0, 0, 0, 1, 0, 1, 0, 0, 0],
|
||||
[0, 0, 1, 0, 0, 0, 1, 0, 0],
|
||||
[0, 1, 0, 0, 0, 0, 0, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=float)
|
||||
|
||||
original_image = np.copy(image)
|
||||
|
||||
chull = convex_hull_image(image)
|
||||
image[chull] += 1
|
||||
# image is now:
|
||||
# [[ 0. 0. 0. 0. 0. 0. 0. 0. 0.]
|
||||
# [ 0. 0. 0. 0. 2. 0. 0. 0. 0.]
|
||||
# [ 0. 0. 0. 2. 1. 2. 0. 0. 0.]
|
||||
# [ 0. 0. 2. 1. 1. 1. 2. 0. 0.]
|
||||
# [ 0. 2. 1. 1. 1. 1. 1. 2. 0.]
|
||||
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
|
||||
|
||||
|
||||
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 6))
|
||||
|
||||
ax1.set_title('Original picture')
|
||||
ax1.imshow(original_image, cmap=plt.cm.gray, interpolation='nearest')
|
||||
ax1.set_xticks([]), ax1.set_yticks([])
|
||||
|
||||
ax2.set_title('Transformed picture')
|
||||
ax2.imshow(image, cmap=plt.cm.gray, interpolation='nearest')
|
||||
ax2.set_xticks([]), ax2.set_yticks([])
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
==============
|
||||
Edge operators
|
||||
==============
|
||||
|
||||
Edge operators are used in image processing within edge detection algorithms.
|
||||
They are discrete differentiation operators, computing an approximation of the
|
||||
gradient of the image intensity function.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.data import camera
|
||||
from skimage.filters import roberts, sobel, scharr, prewitt
|
||||
|
||||
|
||||
image = camera()
|
||||
edge_roberts = roberts(image)
|
||||
edge_sobel = sobel(image)
|
||||
|
||||
fig, (ax0, ax1) = plt.subplots(ncols=2, sharex=True, sharey=True, subplot_kw={'adjustable':'box-forced'})
|
||||
|
||||
ax0.imshow(edge_roberts, cmap=plt.cm.gray)
|
||||
ax0.set_title('Roberts Edge Detection')
|
||||
ax0.axis('off')
|
||||
|
||||
ax1.imshow(edge_sobel, cmap=plt.cm.gray)
|
||||
ax1.set_title('Sobel Edge Detection')
|
||||
ax1.axis('off')
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Different operators compute different finite-difference approximations of the
|
||||
gradient. For example, the Scharr filter results in a less rotational variance
|
||||
than the Sobel filter that is in turn better than the Prewitt filter [1]_ [2]_
|
||||
[3]_. The difference between the Prewitt and Sobel filters and the Scharr filter
|
||||
is illustrated below with an image that is the discretization of a rotation-
|
||||
invariant continuous function. The discrepancy between the Prewitt and Sobel
|
||||
filters, and the Scharr filter is stronger for regions of the image where the
|
||||
direction of the gradient is close to diagonal, and for regions with high
|
||||
spatial frequencies. For the example image the differences between the filter
|
||||
results are very small and the filter results are visually almost
|
||||
indistinguishable.
|
||||
|
||||
.. [1] https://en.wikipedia.org/wiki/Sobel_operator#Alternative_operators
|
||||
|
||||
.. [2] B. Jaehne, H. Scharr, and S. Koerkel. Principles of filter design. In
|
||||
Handbook of Computer Vision and Applications. Academic Press, 1999.
|
||||
|
||||
.. [3] https://en.wikipedia.org/wiki/Prewitt_operator
|
||||
"""
|
||||
|
||||
x, y = np.ogrid[:100, :100]
|
||||
# Rotation-invariant image with different spatial frequencies
|
||||
img = np.exp(1j * np.hypot(x, y)**1.3 / 20.).real
|
||||
|
||||
edge_sobel = sobel(img)
|
||||
edge_scharr = scharr(img)
|
||||
edge_prewitt = prewitt(img)
|
||||
|
||||
diff_scharr_prewitt = edge_scharr - edge_prewitt
|
||||
diff_scharr_sobel = edge_scharr - edge_sobel
|
||||
max_diff = np.max(np.maximum(diff_scharr_prewitt, diff_scharr_sobel))
|
||||
|
||||
fig, ((ax0, ax1), (ax2, ax3)) = plt.subplots(nrows=2, ncols=2, sharex=True, sharey=True, subplot_kw={'adjustable':'box-forced'})
|
||||
|
||||
ax0.imshow(img, cmap=plt.cm.gray)
|
||||
ax0.set_title('Original image')
|
||||
ax0.axis('off')
|
||||
|
||||
ax1.imshow(edge_scharr, cmap=plt.cm.gray)
|
||||
ax1.set_title('Scharr Edge Detection')
|
||||
ax1.axis('off')
|
||||
|
||||
ax2.imshow(diff_scharr_prewitt, cmap=plt.cm.jet, vmax=max_diff)
|
||||
ax2.set_title('Scharr - Prewitt')
|
||||
ax2.axis('off')
|
||||
|
||||
ax3.imshow(diff_scharr_sobel, cmap=plt.cm.jet, vmax=max_diff)
|
||||
ax3.set_title('Scharr - Sobel')
|
||||
ax3.axis('off')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
"""
|
||||
@@ -0,0 +1,133 @@
|
||||
r"""
|
||||
=============================
|
||||
Straight line Hough transform
|
||||
=============================
|
||||
|
||||
The Hough transform in its simplest form is a `method to detect straight lines
|
||||
<http://en.wikipedia.org/wiki/Hough_transform>`__.
|
||||
|
||||
In the following example, we construct an image with a line intersection. We
|
||||
then use the Hough transform to explore a parameter space for straight lines
|
||||
that may run through the image.
|
||||
|
||||
Algorithm overview
|
||||
------------------
|
||||
|
||||
Usually, lines are parameterised as :math:`y = mx + c`, with a gradient
|
||||
:math:`m` and y-intercept `c`. However, this would mean that :math:`m` goes to
|
||||
infinity for vertical lines. Instead, we therefore construct a segment
|
||||
perpendicular to the line, leading to the origin. The line is represented by
|
||||
the length of that segment, :math:`r`, and the angle it makes with the x-axis,
|
||||
:math:`\theta`.
|
||||
|
||||
The Hough transform constructs a histogram array representing the parameter
|
||||
space (i.e., an :math:`M \times N` matrix, for :math:`M` different values of
|
||||
the radius and :math:`N` different values of :math:`\theta`). For each
|
||||
parameter combination, :math:`r` and :math:`\theta`, we then find the number of
|
||||
non-zero pixels in the input image that would fall close to the corresponding
|
||||
line, and increment the array at position :math:`(r, \theta)` appropriately.
|
||||
|
||||
We can think of each non-zero pixel "voting" for potential line candidates. The
|
||||
local maxima in the resulting histogram indicates the parameters of the most
|
||||
probably lines. In our example, the maxima occur at 45 and 135 degrees,
|
||||
corresponding to the normal vector angles of each line.
|
||||
|
||||
Another approach is the Progressive Probabilistic Hough Transform [1]_. It is
|
||||
based on the assumption that using a random subset of voting points give a good
|
||||
approximation to the actual result, and that lines can be extracted during the
|
||||
voting process by walking along connected components. This returns the
|
||||
beginning and end of each line segment, which is useful.
|
||||
|
||||
The function `probabilistic_hough` has three parameters: a general threshold
|
||||
that is applied to the Hough accumulator, a minimum line length and the line
|
||||
gap that influences line merging. In the example below, we find lines longer
|
||||
than 10 with a gap less than 3 pixels.
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
.. [1] C. Galamhos, J. Matas and J. Kittler,"Progressive probabilistic
|
||||
Hough transform for line detection", in IEEE Computer Society
|
||||
Conference on Computer Vision and Pattern Recognition, 1999.
|
||||
|
||||
.. [2] Duda, R. O. and P. E. Hart, "Use of the Hough Transformation to
|
||||
Detect Lines and Curves in Pictures," Comm. ACM, Vol. 15,
|
||||
pp. 11-15 (January, 1972)
|
||||
|
||||
"""
|
||||
|
||||
from skimage.transform import (hough_line, hough_line_peaks,
|
||||
probabilistic_hough_line)
|
||||
from skimage.feature import canny
|
||||
from skimage import data
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Construct test image
|
||||
|
||||
image = np.zeros((100, 100))
|
||||
|
||||
|
||||
# Classic straight-line Hough transform
|
||||
|
||||
idx = np.arange(25, 75)
|
||||
image[idx[::-1], idx] = 255
|
||||
image[idx, idx] = 255
|
||||
|
||||
h, theta, d = hough_line(image)
|
||||
|
||||
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(8,4))
|
||||
|
||||
ax1.imshow(image, cmap=plt.cm.gray)
|
||||
ax1.set_title('Input image')
|
||||
ax1.set_axis_off()
|
||||
|
||||
ax2.imshow(np.log(1 + h),
|
||||
extent=[np.rad2deg(theta[-1]), np.rad2deg(theta[0]),
|
||||
d[-1], d[0]],
|
||||
cmap=plt.cm.gray, aspect=1/1.5)
|
||||
ax2.set_title('Hough transform')
|
||||
ax2.set_xlabel('Angles (degrees)')
|
||||
ax2.set_ylabel('Distance (pixels)')
|
||||
ax2.axis('image')
|
||||
|
||||
ax3.imshow(image, cmap=plt.cm.gray)
|
||||
rows, cols = image.shape
|
||||
for _, angle, dist in zip(*hough_line_peaks(h, theta, d)):
|
||||
y0 = (dist - 0 * np.cos(angle)) / np.sin(angle)
|
||||
y1 = (dist - cols * np.cos(angle)) / np.sin(angle)
|
||||
ax3.plot((0, cols), (y0, y1), '-r')
|
||||
ax3.axis((0, cols, rows, 0))
|
||||
ax3.set_title('Detected lines')
|
||||
ax3.set_axis_off()
|
||||
|
||||
# Line finding, using the Probabilistic Hough Transform
|
||||
|
||||
image = data.camera()
|
||||
edges = canny(image, 2, 1, 25)
|
||||
lines = probabilistic_hough_line(edges, threshold=10, line_length=5,
|
||||
line_gap=3)
|
||||
|
||||
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(8,4), sharex=True, sharey=True)
|
||||
|
||||
ax1.imshow(image, cmap=plt.cm.gray)
|
||||
ax1.set_title('Input image')
|
||||
ax1.set_axis_off()
|
||||
ax1.set_adjustable('box-forced')
|
||||
|
||||
ax2.imshow(edges, cmap=plt.cm.gray)
|
||||
ax2.set_title('Canny edges')
|
||||
ax2.set_axis_off()
|
||||
ax2.set_adjustable('box-forced')
|
||||
|
||||
ax3.imshow(edges * 0)
|
||||
|
||||
for line in lines:
|
||||
p0, p1 = line
|
||||
ax3.plot((p0[0], p1[0]), (p0[1], p1[1]))
|
||||
|
||||
ax3.set_title('Probabilistic Hough')
|
||||
ax3.set_axis_off()
|
||||
ax3.set_adjustable('box-forced')
|
||||
plt.show()
|
||||
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
==============
|
||||
Marching Cubes
|
||||
==============
|
||||
|
||||
Marching cubes is an algorithm to extract a 2D surface mesh from a 3D volume.
|
||||
This can be conceptualized as a 3D generalization of isolines on topographical
|
||||
or weather maps. It works by iterating across the volume, looking for regions
|
||||
which cross the level of interest. If such regions are found, triangulations
|
||||
are generated and added to an output mesh. The final result is a set of
|
||||
vertices and a set of triangular faces.
|
||||
|
||||
The algorithm requires a data volume and an isosurface value. For example, in
|
||||
CT imaging Hounsfield units of +700 to +3000 represent bone. So, one potential
|
||||
input would be a reconstructed CT set of data and the value +700, to extract
|
||||
a mesh for regions of bone or bone-like density.
|
||||
|
||||
This implementation also works correctly on anisotropic datasets, where the
|
||||
voxel spacing is not equal for every spatial dimension, through use of the
|
||||
`spacing` kwarg.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
|
||||
|
||||
from skimage import measure
|
||||
from skimage.draw import ellipsoid
|
||||
|
||||
# Generate a level set about zero of two identical ellipsoids in 3D
|
||||
ellip_base = ellipsoid(6, 10, 16, levelset=True)
|
||||
ellip_double = np.concatenate((ellip_base[:-1, ...],
|
||||
ellip_base[2:, ...]), axis=0)
|
||||
|
||||
# Use marching cubes to obtain the surface mesh of these ellipsoids
|
||||
verts, faces = measure.marching_cubes(ellip_double, 0)
|
||||
|
||||
# Display resulting triangular mesh using Matplotlib. This can also be done
|
||||
# with mayavi (see skimage.measure.marching_cubes docstring).
|
||||
fig = plt.figure(figsize=(10, 12))
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
|
||||
# Fancy indexing: `verts[faces]` to generate a collection of triangles
|
||||
mesh = Poly3DCollection(verts[faces])
|
||||
ax.add_collection3d(mesh)
|
||||
|
||||
ax.set_xlabel("x-axis: a = 6 per ellipsoid")
|
||||
ax.set_ylabel("y-axis: b = 10")
|
||||
ax.set_zlabel("z-axis: c = 16")
|
||||
|
||||
ax.set_xlim(0, 24) # a = 6 (times two for 2nd ellipsoid)
|
||||
ax.set_ylim(0, 20) # b = 10
|
||||
ax.set_zlim(0, 32) # c = 16
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,65 @@
|
||||
"""
|
||||
===========================
|
||||
Medial axis skeletonization
|
||||
===========================
|
||||
|
||||
The medial axis of an object is the set of all points having more than one
|
||||
closest point on the object's boundary. It is often called the **topological
|
||||
skeleton**, because it is a 1-pixel wide skeleton of the object, with the same
|
||||
connectivity as the original object.
|
||||
|
||||
Here, we use the medial axis transform to compute the width of the foreground
|
||||
objects. As the function ``medial_axis`` (``skimage.morphology.medial_axis``)
|
||||
returns the distance transform in addition to the medial axis (with the keyword
|
||||
argument ``return_distance=True``), it is possible to compute the distance to
|
||||
the background for all points of the medial axis with this function. This gives
|
||||
an estimate of the local width of the objects.
|
||||
|
||||
For a skeleton with fewer branches, there exists another skeletonization
|
||||
algorithm in ``skimage``: ``skimage.morphology.skeletonize``, that computes
|
||||
a skeleton by iterative morphological thinnings.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
from scipy import ndimage as ndi
|
||||
from skimage.morphology import medial_axis
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
def microstructure(l=256):
|
||||
"""
|
||||
Synthetic binary data: binary microstructure with blobs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
l: int, optional
|
||||
linear size of the returned image
|
||||
|
||||
"""
|
||||
n = 5
|
||||
x, y = np.ogrid[0:l, 0:l]
|
||||
mask = np.zeros((l, l))
|
||||
generator = np.random.RandomState(1)
|
||||
points = l * generator.rand(2, n**2)
|
||||
mask[(points[0]).astype(np.int), (points[1]).astype(np.int)] = 1
|
||||
mask = ndi.gaussian_filter(mask, sigma=l/(4.*n))
|
||||
return mask > mask.mean()
|
||||
|
||||
data = microstructure(l=64)
|
||||
|
||||
# Compute the medial axis (skeleton) and the distance transform
|
||||
skel, distance = medial_axis(data, return_distance=True)
|
||||
|
||||
# Distance to the background for pixels of the skeleton
|
||||
dist_on_skel = distance * skel
|
||||
|
||||
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4), sharex=True, sharey=True, subplot_kw={'adjustable':'box-forced'})
|
||||
ax1.imshow(data, cmap=plt.cm.gray, interpolation='nearest')
|
||||
ax1.axis('off')
|
||||
ax2.imshow(dist_on_skel, cmap=plt.cm.spectral, interpolation='nearest')
|
||||
ax2.contour(data, [0.5], colors='w')
|
||||
ax2.axis('off')
|
||||
|
||||
fig.subplots_adjust(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0, right=1)
|
||||
plt.show()
|
||||
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
==================================
|
||||
Approximate and subdivide polygons
|
||||
==================================
|
||||
|
||||
This example shows how to approximate (Douglas-Peucker algorithm) and subdivide
|
||||
(B-Splines) polygonal chains.
|
||||
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.draw import ellipse
|
||||
from skimage.measure import find_contours, approximate_polygon, \
|
||||
subdivide_polygon
|
||||
|
||||
|
||||
hand = np.array([[1.64516129, 1.16145833],
|
||||
[1.64516129, 1.59375],
|
||||
[1.35080645, 1.921875],
|
||||
[1.375, 2.18229167],
|
||||
[1.68548387, 1.9375],
|
||||
[1.60887097, 2.55208333],
|
||||
[1.68548387, 2.69791667],
|
||||
[1.76209677, 2.56770833],
|
||||
[1.83064516, 1.97395833],
|
||||
[1.89516129, 2.75],
|
||||
[1.9516129, 2.84895833],
|
||||
[2.01209677, 2.76041667],
|
||||
[1.99193548, 1.99479167],
|
||||
[2.11290323, 2.63020833],
|
||||
[2.2016129, 2.734375],
|
||||
[2.25403226, 2.60416667],
|
||||
[2.14919355, 1.953125],
|
||||
[2.30645161, 2.36979167],
|
||||
[2.39112903, 2.36979167],
|
||||
[2.41532258, 2.1875],
|
||||
[2.1733871, 1.703125],
|
||||
[2.07782258, 1.16666667]])
|
||||
|
||||
# subdivide polygon using 2nd degree B-Splines
|
||||
new_hand = hand.copy()
|
||||
for _ in range(5):
|
||||
new_hand = subdivide_polygon(new_hand, degree=2, preserve_ends=True)
|
||||
|
||||
# approximate subdivided polygon with Douglas-Peucker algorithm
|
||||
appr_hand = approximate_polygon(new_hand, tolerance=0.02)
|
||||
|
||||
print("Number of coordinates:", len(hand), len(new_hand), len(appr_hand))
|
||||
|
||||
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(9, 4))
|
||||
|
||||
ax1.plot(hand[:, 0], hand[:, 1])
|
||||
ax1.plot(new_hand[:, 0], new_hand[:, 1])
|
||||
ax1.plot(appr_hand[:, 0], appr_hand[:, 1])
|
||||
|
||||
|
||||
# create two ellipses in image
|
||||
img = np.zeros((800, 800), 'int32')
|
||||
rr, cc = ellipse(250, 250, 180, 230, img.shape)
|
||||
img[rr, cc] = 1
|
||||
rr, cc = ellipse(600, 600, 150, 90, img.shape)
|
||||
img[rr, cc] = 1
|
||||
|
||||
plt.gray()
|
||||
ax2.imshow(img)
|
||||
|
||||
# approximate / simplify coordinates of the two ellipses
|
||||
for contour in find_contours(img, 0):
|
||||
coords = approximate_polygon(contour, tolerance=2.5)
|
||||
ax2.plot(coords[:, 1], coords[:, 0], '-r', linewidth=2)
|
||||
coords2 = approximate_polygon(contour, tolerance=39.5)
|
||||
ax2.plot(coords2[:, 1], coords2[:, 0], '-g', linewidth=2)
|
||||
print("Number of coordinates:", len(contour), len(coords), len(coords2))
|
||||
|
||||
ax2.axis((0, 800, 0, 800))
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
======
|
||||
Shapes
|
||||
======
|
||||
|
||||
This example shows how to draw several different shapes:
|
||||
|
||||
- line
|
||||
- Bezier curve
|
||||
- polygon
|
||||
- circle
|
||||
- ellipse
|
||||
|
||||
Anti-aliased drawing for:
|
||||
|
||||
- line
|
||||
- circle
|
||||
|
||||
"""
|
||||
import math
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.draw import (line, polygon, circle,
|
||||
circle_perimeter,
|
||||
ellipse, ellipse_perimeter,
|
||||
bezier_curve)
|
||||
|
||||
|
||||
fig, (ax1, ax2) = plt.subplots(ncols=2, nrows=1, figsize=(10, 6))
|
||||
|
||||
|
||||
img = np.zeros((500, 500, 3), dtype=np.double)
|
||||
|
||||
# draw line
|
||||
rr, cc = line(120, 123, 20, 400)
|
||||
img[rr, cc, 0] = 255
|
||||
|
||||
# fill polygon
|
||||
poly = np.array((
|
||||
(300, 300),
|
||||
(480, 320),
|
||||
(380, 430),
|
||||
(220, 590),
|
||||
(300, 300),
|
||||
))
|
||||
rr, cc = polygon(poly[:, 0], poly[:, 1], img.shape)
|
||||
img[rr, cc, 1] = 1
|
||||
|
||||
# fill circle
|
||||
rr, cc = circle(200, 200, 100, img.shape)
|
||||
img[rr, cc, :] = (1, 1, 0)
|
||||
|
||||
# fill ellipse
|
||||
rr, cc = ellipse(300, 300, 100, 200, img.shape)
|
||||
img[rr, cc, 2] = 1
|
||||
|
||||
# circle
|
||||
rr, cc = circle_perimeter(120, 400, 15)
|
||||
img[rr, cc, :] = (1, 0, 0)
|
||||
|
||||
# Bezier curve
|
||||
rr, cc = bezier_curve(70, 100, 10, 10, 150, 100, 1)
|
||||
img[rr, cc, :] = (1, 0, 0)
|
||||
|
||||
# ellipses
|
||||
rr, cc = ellipse_perimeter(120, 400, 60, 20, orientation=math.pi / 4.)
|
||||
img[rr, cc, :] = (1, 0, 1)
|
||||
rr, cc = ellipse_perimeter(120, 400, 60, 20, orientation=-math.pi / 4.)
|
||||
img[rr, cc, :] = (0, 0, 1)
|
||||
rr, cc = ellipse_perimeter(120, 400, 60, 20, orientation=math.pi / 2.)
|
||||
img[rr, cc, :] = (1, 1, 1)
|
||||
|
||||
ax1.imshow(img)
|
||||
ax1.set_title('No anti-aliasing')
|
||||
ax1.axis('off')
|
||||
|
||||
|
||||
from skimage.draw import line_aa, circle_perimeter_aa
|
||||
|
||||
|
||||
img = np.zeros((100, 100), dtype=np.double)
|
||||
|
||||
# anti-aliased line
|
||||
rr, cc, val = line_aa(12, 12, 20, 50)
|
||||
img[rr, cc] = val
|
||||
|
||||
# anti-aliased circle
|
||||
rr, cc, val = circle_perimeter_aa(60, 40, 30)
|
||||
img[rr, cc] = val
|
||||
|
||||
|
||||
ax2.imshow(img, cmap=plt.cm.gray, interpolation='nearest')
|
||||
ax2.set_title('Anti-aliasing')
|
||||
ax2.axis('off')
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,63 @@
|
||||
"""
|
||||
===========
|
||||
Skeletonize
|
||||
===========
|
||||
|
||||
Skeletonization reduces binary objects to 1 pixel wide representations. This
|
||||
can be useful for feature extraction, and/or representing an object's topology.
|
||||
|
||||
The algorithm works by making successive passes of the image. On each pass,
|
||||
border pixels are identified and removed on the condition that they do not
|
||||
break the connectivity of the corresponding object.
|
||||
|
||||
This module provides an example of calling the routine and displaying the
|
||||
results. The input is a 2D ndarray, with either boolean or integer elements.
|
||||
In the case of boolean, 'True' indicates foreground, and for integer arrays,
|
||||
the foreground is 1's.
|
||||
"""
|
||||
from skimage.morphology import skeletonize
|
||||
from skimage import draw
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# an empty image
|
||||
image = np.zeros((400, 400))
|
||||
|
||||
# foreground object 1
|
||||
image[10:-10, 10:100] = 1
|
||||
image[-100:-10, 10:-10] = 1
|
||||
image[10:-10, -100:-10] = 1
|
||||
|
||||
# foreground object 2
|
||||
rs, cs = draw.line(250, 150, 10, 280)
|
||||
for i in range(10):
|
||||
image[rs + i, cs] = 1
|
||||
rs, cs = draw.line(10, 150, 250, 280)
|
||||
for i in range(20):
|
||||
image[rs + i, cs] = 1
|
||||
|
||||
# foreground object 3
|
||||
ir, ic = np.indices(image.shape)
|
||||
circle1 = (ic - 135)**2 + (ir - 150)**2 < 30**2
|
||||
circle2 = (ic - 135)**2 + (ir - 150)**2 < 20**2
|
||||
image[circle1] = 1
|
||||
image[circle2] = 0
|
||||
|
||||
# perform skeletonization
|
||||
skeleton = skeletonize(image)
|
||||
|
||||
# display results
|
||||
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4.5), sharex=True, sharey=True, subplot_kw={'adjustable':'box-forced'})
|
||||
|
||||
ax1.imshow(image, cmap=plt.cm.gray)
|
||||
ax1.axis('off')
|
||||
ax1.set_title('original', fontsize=20)
|
||||
|
||||
ax2.imshow(skeleton, cmap=plt.cm.gray)
|
||||
ax2.axis('off')
|
||||
ax2.set_title('skeleton', fontsize=20)
|
||||
|
||||
fig.subplots_adjust(wspace=0.02, hspace=0.02, top=0.98,
|
||||
bottom=0.02, left=0.02, right=0.98)
|
||||
|
||||
plt.show()
|
||||
Reference in New Issue
Block a user