Merge pull request #2078 from emmanuelle/sphinx-gallery

Sphinx gallery for example gallery
This commit is contained in:
Egor Panfilov
2016-06-07 17:22:48 +03:00
81 changed files with 2594 additions and 1007 deletions
+2 -2
View File
@@ -43,7 +43,7 @@ api:
release_notes:
@echo "Copying release notes"
@tail -n +4 `ls release/*.txt | sort -k 2 -t . -n | tail -n 1` > release/_release_notes_for_docs.txt
@tail -n +4 `ls release/*.rst | sort -k 2 -t . -n | tail -n 1` > release/_release_notes_for_docs.rst
html: api release_notes
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(DEST)/html
@@ -129,4 +129,4 @@ gitwash:
--project-ml-url=http://groups.google.com/group/scikit-image \
--repo-name=scikit-image \
--github-user=scikit-image \
--source-suffix=.txt
--source-suffix=.rst
+3 -1
View File
@@ -5,4 +5,6 @@ General examples
General-purpose and introductory examples for scikit-image.
The `narrative documentation <../user_guide.html>`_ introduces conventions and basic image manipulations.
The `narrative documentation <../user_guide.html>`_ introduces
conventions and basic image manipulations.
+37 -45
View File
@@ -35,11 +35,10 @@ def sobel_each(image):
def sobel_hsv(image):
return filters.sobel(image)
"""
We can use these functions as we would normally use them, but now they work
with both gray-scale and color images. Let's plot the results with a color
image:
"""
######################################################################
# We can use these functions as we would normally use them, but now they work
# with both gray-scale and color images. Let's plot the results with a color
# image:
from skimage import data
from skimage.exposure import rescale_intensity
@@ -63,30 +62,27 @@ ax_hsv.imshow(rescale_intensity(1 - sobel_hsv(image)))
ax_hsv.set_xticks([]), ax_hsv.set_yticks([])
ax_hsv.set_title("Sobel filter computed\n on (V)alue converted image (HSV)")
"""
.. image:: PLOT2RST.current_figure
######################################################################
# Notice that the result for the value-filtered image preserves the color of
# the original image, but channel filtered image combines in a more
# surprising way. In other common cases, smoothing for example, the channel
# filtered image will produce a better result than the value-filtered image.
#
# You can also create your own handler functions for ``adapt_rgb``. To do so,
# just create a function with the following signature::
#
# def handler(image_filter, image, *args, **kwargs):
# # Manipulate RGB image here...
# image = image_filter(image, *args, **kwargs)
# # Manipulate filtered image here...
# return image
#
# Note that ``adapt_rgb`` handlers are written for filters where the image is
# the first argument.
#
# As a very simple example, we can just convert any RGB image to grayscale
# and then return the filtered result:
Notice that the result for the value-filtered image preserves the color of the
original image, but channel filtered image combines in a more surprising way.
In other common cases, smoothing for example, the channel filtered image will
produce a better result than the value-filtered image.
You can also create your own handler functions for ``adapt_rgb``. To do so,
just create a function with the following signature::
def handler(image_filter, image, *args, **kwargs):
# Manipulate RGB image here...
image = image_filter(image, *args, **kwargs)
# Manipulate filtered image here...
return image
Note that ``adapt_rgb`` handlers are written for filters where the image is the
first argument.
As a very simple example, we can just convert any RGB image to grayscale and
then return the filtered result:
"""
from skimage.color import rgb2gray
@@ -94,13 +90,12 @@ def as_gray(image_filter, image, *args, **kwargs):
gray_image = rgb2gray(image)
return image_filter(gray_image, *args, **kwargs)
"""
It's important to create a signature that uses ``*args`` and ``**kwargs`` to
pass arguments along to the filter so that the decorated function is allowed to
have any number of positional and keyword arguments.
Finally, we can use this handler with ``adapt_rgb`` just as before:
"""
######################################################################
# It's important to create a signature that uses ``*args`` and ``**kwargs``
# to pass arguments along to the filter so that the decorated function is
# allowed to have any number of positional and keyword arguments.
#
# Finally, we can use this handler with ``adapt_rgb`` just as before:
@adapt_rgb(as_gray)
@@ -119,13 +114,10 @@ ax.set_title("Sobel filter computed\n on the converted grayscale image")
plt.show()
"""
.. image:: PLOT2RST.current_figure
.. note::
A very simple check of the array shape is used for detecting RGB images, so
``adapt_rgb`` is not recommended for functions that support 3D volumes or
color images in non-RGB spaces.
"""
######################################################################
#
# .. note::
#
# A very simple check of the array shape is used for detecting RGB
# images, so ``adapt_rgb`` is not recommended for functions that support
# 3D volumes or color images in non-RGB spaces.
+6 -6
View File
@@ -72,12 +72,12 @@ img_adapteq = exposure.equalize_adapthist(img, clip_limit=0.03)
# Display results
fig = plt.figure(figsize=(8, 5))
axes = np.zeros((2,4), dtype=np.object)
axes[0,0] = fig.add_subplot(2, 4, 1)
for i in range(1,4):
axes[0,i] = fig.add_subplot(2, 4, 1+i, sharex=axes[0,0], sharey=axes[0,0])
for i in range(0,4):
axes[1,i] = fig.add_subplot(2, 4, 5+i)
axes = np.zeros((2, 4), dtype=np.object)
axes[0, 0] = fig.add_subplot(2, 4, 1)
for i in range(1, 4):
axes[0, i] = fig.add_subplot(2, 4, 1+i, sharex=axes[0,0], sharey=axes[0,0])
for i in range(0, 4):
axes[1, i] = fig.add_subplot(2, 4, 5+i)
ax_img, ax_hist, ax_cdf = plot_img_and_hist(img, axes[:, 0])
ax_img.set_title('Low contrast image')
@@ -48,11 +48,9 @@ for ax in axes.ravel():
fig.tight_layout()
"""
.. image:: PLOT2RST.current_figure
######################################################################
# Now we can easily manipulate the hematoxylin and DAB "channels":
Now we can easily manipulate the hematoxylin and DAB "channels":
"""
import numpy as np
from skimage.exposure import rescale_intensity
@@ -62,7 +60,6 @@ h = rescale_intensity(ihc_hed[:, :, 0], out_range=(0, 1))
d = rescale_intensity(ihc_hed[:, :, 2], out_range=(0, 1))
zdh = np.dstack((np.zeros_like(h), d, h))
#fig, ax = plt.subplots()
fig = plt.figure()
ax = plt.subplot(1, 1, 1, sharex=ax0, sharey=ax0, adjustable='box-forced')
ax.imshow(zdh)
@@ -70,6 +67,3 @@ ax.set_title("Stain separated image (rescaled)")
ax.axis('off')
plt.show()
"""
.. image:: PLOT2RST.current_figure
"""
@@ -31,10 +31,9 @@ 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.
"""
######################################################################
# 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(1, 3, figsize=(8, 2.5), sharex=True, sharey=True)
@@ -55,29 +54,25 @@ ax3.set_adjustable('box-forced')
fig.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``.
"""
######################################################################
# 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).
"""
######################################################################
# 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(1, 3, figsize=(8, 2.5))
@@ -104,14 +99,11 @@ ax3.axis('off')
fig.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.
"""
######################################################################
# 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.
@@ -34,26 +34,23 @@ ax2.imshow(yellow_multiplier * image)
ax1.set_adjustable('box-forced')
ax2.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
In many cases, dealing with RGB values may not be ideal. Because of that, there
are many other `color spaces`_ in which you can represent a color image. One
popular color space is called HSV, which represents hue (~the color),
saturation (~colorfulness), and value (~brightness). For example, a color
(hue) might be green, but its saturation is how intense that green is---where
olive is on the low end and neon on the high end.
In some implementations, the hue in HSV goes from 0 to 360, since hues wrap
around in a circle. In scikit-image, however, hues are float values from 0 to
1, so that hue, saturation, and value all share the same scale.
.. _color spaces:
http://en.wikipedia.org/wiki/List_of_color_spaces_and_their_uses
Below, we plot a linear gradient in the hue, with the saturation and value
turned all the way up:
"""
######################################################################
# In many cases, dealing with RGB values may not be ideal. Because of that,
# there are many other `color spaces`_ in which you can represent a color
# image. One popular color space is called HSV, which represents hue (~the
# color), saturation (~colorfulness), and value (~brightness). For example, a
# color (hue) might be green, but its saturation is how intense that green is
# ---where olive is on the low end and neon on the high end.
#
# In some implementations, the hue in HSV goes from 0 to 360, since hues wrap
# around in a circle. In scikit-image, however, hues are float values from 0
# to 1, so that hue, saturation, and value all share the same scale.
#
# .. _color spaces:
# http://en.wikipedia.org/wiki/List_of_color_spaces_and_their_uses
#
# Below, we plot a linear gradient in the hue, with the saturation and value
# turned all the way up:
import numpy as np
hue_gradient = np.linspace(0, 1)
@@ -67,22 +64,17 @@ fig, ax = plt.subplots(figsize=(5, 2))
ax.imshow(all_hues, extent=(0, 1, 0, 0.2))
ax.set_axis_off()
"""
.. image:: PLOT2RST.current_figure
Notice how the colors at the far left and far right are the same. That reflects
the fact that the hues wrap around like the color wheel (see HSV_ for more
info).
.. _HSV: http://en.wikipedia.org/wiki/HSL_and_HSV
Now, let's create a little utility function to take an RGB image and:
1. Transform the RGB image to HSV
2. Set the hue and saturation
3. Transform the HSV image back to RGB
"""
######################################################################
# Notice how the colors at the far left and far right are the same. That
# reflects the fact that the hues wrap around like the color wheel (see HSV_
# for more info).
#
# .. _HSV: http://en.wikipedia.org/wiki/HSL_and_HSV
#
# Now, let's create a little utility function to take an RGB image and:
#
# 1. Transform the RGB image to HSV 2. Set the hue and saturation 3.
# Transform the HSV image back to RGB
def colorize(image, hue, saturation=1):
@@ -96,13 +88,13 @@ def colorize(image, hue, saturation=1):
return color.hsv2rgb(hsv)
"""
Notice that we need to bump up the saturation; images with zero saturation are
grayscale, so we need to a non-zero value to actually see the color we've set.
Using the function above, we plot six images with a linear gradient in the hue
and a non-zero saturation:
"""
######################################################################
# Notice that we need to bump up the saturation; images with zero saturation
# are grayscale, so we need to a non-zero value to actually see the color
# we've set.
#
# Using the function above, we plot six images with a linear gradient in the
# hue and a non-zero saturation:
hue_rotations = np.linspace(0, 1, 6)
@@ -116,15 +108,12 @@ for ax, hue in zip(axes.flat, hue_rotations):
ax.set_adjustable('box-forced')
fig.tight_layout()
"""
.. image:: PLOT2RST.current_figure
You can combine this tinting effect with numpy slicing and fancy-indexing to
selectively tint your images. In the example below, we set the hue of some
rectangles using slicing and scale the RGB values of some pixels found by
thresholding. In practice, you might want to define a region for tinting based
on segmentation results or blob detection methods.
"""
######################################################################
# You can combine this tinting effect with numpy slicing and fancy-indexing
# to selectively tint your images. In the example below, we set the hue of
# some rectangles using slicing and scale the RGB values of some pixels found
# by thresholding. In practice, you might want to define a region for tinting
# based on segmentation results or blob detection methods.
from skimage.filters import rank
@@ -153,11 +142,7 @@ ax2.set_adjustable('box-forced')
plt.show()
"""
.. image:: PLOT2RST.current_figure
For coloring multiple regions, you may also be interested in
`skimage.color.label2rgb
<http://scikit-image.org/docs/0.9.x/api/skimage.color.html#label2rgb>`_.
"""
######################################################################
# For coloring multiple regions, you may also be interested in
# `skimage.color.label2rgb <http://scikit-
# image.org/docs/0.9.x/api/skimage.color.html#label2rgb>`_.
+5 -12
View File
@@ -64,14 +64,11 @@ if new_scipy:
ax.set_xticks([]), ax.set_yticks([])
ax.axis([0, img.shape[1], img.shape[0], 0])
"""
.. image:: PLOT2RST.current_figure
Here we initialize a straight line between two points, `(5, 136)` and
`(424, 50)`, and require that the spline has its end points there by giving
the boundary condition `bc='fixed'`. We furthermore make the algorithm search
for dark lines by giving a negative `w_line` value.
"""
######################################################################
# Here we initialize a straight line between two points, `(5, 136)` and
# `(424, 50)`, and require that the spline has its end points there by giving
# the boundary condition `bc='fixed'`. We furthermore make the algorithm
# search for dark lines by giving a negative `w_line` value.
img = data.text()
@@ -93,7 +90,3 @@ if new_scipy:
ax.axis([0, img.shape[1], img.shape[0], 0])
plt.show()
"""
.. image:: PLOT2RST.current_figure
"""
+24 -32
View File
@@ -77,33 +77,30 @@ for idx in np.argsort(accums)[::-1][:5]:
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
"""
######################################################################
# 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
@@ -149,8 +146,3 @@ ax2.set_title('Edge (white) and result (red)')
ax2.imshow(edges)
plt.show()
"""
.. image:: PLOT2RST.current_figure
"""
+20 -26
View File
@@ -31,28 +31,26 @@ 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
"""
######################################################################
# 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
@@ -86,7 +84,3 @@ ax3.axis('off')
plt.tight_layout()
plt.show()
"""
.. image:: PLOT2RST.current_figure
"""
@@ -18,14 +18,14 @@ Usually, lines are parameterised as :math:`y = mx + c`, with a gradient
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`.
: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
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.
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
@@ -3,7 +3,7 @@
Gabors / Primary Visual Cortex "Simple Cells" from an Image
============================================================
How to build a (bio-plausible) "sparse" dictionary (or 'codebook', or
How to build a (bio-plausible) *sparse* dictionary (or 'codebook', or
'filterbank') for e.g. image classification without any fancy math and
with just standard python scientific libraries?
@@ -3,11 +3,10 @@
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 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:
@@ -21,24 +20,13 @@ image = data.moon()
# Rescale image intensity so that we can see dim features.
image = rescale_intensity(image, in_range=(50, 200))
fig,ax = plt.subplots(2, 2, figsize=(5, 4), sharex=True, sharey=True, subplot_kw={'adjustable':'box-forced'})
ax = ax.ravel()
ax[0].imshow(image)
ax[0].set_title('Original image')
ax[0].axis('off')
"""
.. 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.
"""
######################################################################
# 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
@@ -49,41 +37,40 @@ mask = image
filled = reconstruction(seed, mask, method='erosion')
ax[1].imshow(filled)
ax[1].set_title('after filling holes')
ax[1].axis('off')
"""
.. 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.
"""
ax[2].imshow(image-filled)
ax[2].set_title('holes')
ax[2].axis('off')
"""
.. 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.
"""
######################################################################
# 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.
#
# 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')
ax[3].imshow(image-rec)
fig, ax = plt.subplots(2, 2, figsize=(5, 4), sharex=True, sharey=True,
subplot_kw={'adjustable': 'box-forced'})
ax = ax.ravel()
ax[0].imshow(image, cmap='gray')
ax[0].set_title('Original image')
ax[0].axis('off')
ax[1].imshow(filled, cmap='gray')
ax[1].set_title('after filling holes')
ax[1].axis('off')
ax[2].imshow(image-filled, cmap='gray')
ax[2].set_title('holes')
ax[2].axis('off')
ax[3].imshow(image-rec, cmap='gray')
ax[3].set_title('peaks')
ax[3].axis('off')
plt.show()
"""
.. image:: PLOT2RST.current_figure
"""
@@ -69,21 +69,18 @@ for ax, values, name in zip(axes, binary_patterns, titles):
plot_lbp_model(ax, values)
ax.set_title(name)
"""
.. image:: PLOT2RST.current_figure
The figure above shows example results with black (or white) representing
pixels that are less (or more) intense than the central pixel. When surrounding
pixels are all black or all white, then that image region is flat (i.e.
featureless). Groups of continuous black or white pixels are considered
"uniform" patterns that can be interpreted as corners or edges. If pixels
switch back-and-forth between black and white pixels, the pattern is considered
"non-uniform".
When using LBP to detect texture, you measure a collection of LBPs over an
image patch and look at the distribution of these LBPs. Lets apply LBP to
a brick texture.
"""
######################################################################
# The figure above shows example results with black (or white) representing
# pixels that are less (or more) intense than the central pixel. When
# surrounding pixels are all black or all white, then that image region is
# flat (i.e. featureless). Groups of continuous black or white pixels are
# considered "uniform" patterns that can be interpreted as corners or edges.
# If pixels switch back-and-forth between black and white pixels, the pattern
# is considered "non-uniform".
#
# When using LBP to detect texture, you measure a collection of LBPs over an
# image patch and look at the distribution of these LBPs. Lets apply LBP to a
# brick texture.
from skimage.transform import rotate
from skimage.feature import local_binary_pattern
@@ -145,16 +142,13 @@ for ax in ax_img:
ax.axis('off')
"""
.. image:: PLOT2RST.current_figure
The above plot highlights flat, edge-like, and corner-like regions of the
image.
The histogram of the LBP result is a good measure to classify textures. Here,
we test the histogram distributions against each other using the
Kullback-Leibler-Divergence.
"""
######################################################################
# The above plot highlights flat, edge-like, and corner-like regions of the
# image.
#
# The histogram of the LBP result is a good measure to classify textures.
# Here, we test the histogram distributions against each other using the
# Kullback-Leibler-Divergence.
# settings for LBP
radius = 2
@@ -222,8 +216,4 @@ ax3.imshow(wall)
ax3.axis('off')
hist(ax6, refs['wall'])
"""
.. image:: PLOT2RST.current_figure
"""
plt.show()
@@ -43,10 +43,10 @@ lbp_code = multiblock_lbp(int_img, 0, 0, 3, 3)
assert_equal(correct_answer, lbp_code)
"""
Now let's apply the operator to a real image and see how the
visualization works.
"""
######################################################################
# Now let's apply the operator to a real image and see how the visualization
# works.
from skimage import data
from matplotlib import pyplot as plt
from skimage.feature import draw_multiblock_lbp
@@ -65,11 +65,9 @@ plt.imshow(img, interpolation='nearest')
plt.show()
"""
.. image:: PLOT2RST.current_figure
On the above plot we see the result of computing a MB-LBP and visualization of
the computed feature. The rectangles that have less intensities' sum than the
central rectangle are marked in cyan. The ones that have higher intensity
values are marked in white. The central rectangle is left untouched.
"""
######################################################################
# On the above plot we see the result of computing a MB-LBP and visualization
# of the computed feature. The rectangles that have less intensities' sum
# than the central rectangle are marked in cyan. The ones that have higher
# intensity values are marked in white. The central rectangle is left
# untouched.
@@ -1,4 +1,3 @@
from __future__ import division
"""
========================
Sliding window histogram
@@ -35,6 +34,7 @@ References
.. [2] S.Perreault and P.Hebert. Median filtering in constant time.
Trans. Image Processing, 16(9):2389-2394, 2007.
"""
from __future__ import division
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
+23 -27
View File
@@ -42,14 +42,12 @@ ax3.set_title('After phase unwrapping')
fig.colorbar(ax4.imshow(image_unwrapped - image, cmap='gray'), ax=ax4)
ax4.set_title('Unwrapped minus original')
"""
.. image:: PLOT2RST.current_figure
The unwrapping procedure accepts masked arrays, and can also optionally
assume cyclic boundaries to connect edges of an image. In the example below,
we study a simple phase ramp which has been split in two by masking
a row of the image.
"""
###########################################################################
# The unwrapping procedure accepts masked arrays, and can also optionally
# assume cyclic boundaries to connect edges of an image. In the example below,
# we study a simple phase ramp which has been split in two by masking
# a row of the image.
# Create a simple ramp
image = np.ones((100, 100)) * np.linspace(0, 8 * np.pi, 100).reshape((-1, 1))
@@ -84,24 +82,22 @@ ax4.set_title('Unwrapped with wrap_around')
plt.show()
"""
.. image:: PLOT2RST.current_figure
In the figures above, the masked row can be seen as a white line across
the image. The difference between the two unwrapped images in the bottom row
is clear: Without unwrapping (lower left), the regions above and below the
masked boundary do not interact at all, resulting in an offset between the
two regions of an arbitrary integer times two pi. We could just as well have
unwrapped the regions as two separate images. With wrap around enabled for the
vertical direction (lower right), the situation changes: Unwrapping paths are
now allowed to pass from the bottom to the top of the image and vice versa, in
effect providing a way to determine the offset between the two regions.
References
----------
.. [1] Miguel Arevallilo Herraez, David R. Burton, Michael J. Lalor,
and Munther A. Gdeisat, "Fast two-dimensional phase-unwrapping
algorithm based on sorting by reliability following a noncontinuous
path", Journal Applied Optics, Vol. 41, No. 35, pp. 7437, 2002
"""
###########################################################################
# In the figures above, the masked row can be seen as a white line across
# the image. The difference between the two unwrapped images in the bottom row
# is clear: Without unwrapping (lower left), the regions above and below the
# masked boundary do not interact at all, resulting in an offset between the
# two regions of an arbitrary integer times two pi. We could just as well have
# unwrapped the regions as two separate images. With wrap around enabled for the
# vertical direction (lower right), the situation changes: Unwrapping paths are
# now allowed to pass from the bottom to the top of the image and vice versa, in
# effect providing a way to determine the offset between the two regions.
#
# References
# ----------
#
# .. [1] Miguel Arevallilo Herraez, David R. Burton, Michael J. Lalor,
# and Munther A. Gdeisat, "Fast two-dimensional phase-unwrapping
# algorithm based on sorting by reliability following a noncontinuous
# path", Journal Applied Optics, Vol. 41, No. 35, pp. 7437, 2002
+58 -69
View File
@@ -76,23 +76,20 @@ ax2.imshow(sinogram, cmap=plt.cm.Greys_r,
fig.tight_layout()
plt.show()
"""
.. image:: PLOT2RST.current_figure
Reconstruction with the Filtered Back Projection (FBP)
======================================================
The mathematical foundation of the filtered back projection is the Fourier
slice theorem [2]_. It uses Fourier transform of the projection and
interpolation in Fourier space to obtain the 2D Fourier transform of the image,
which is then inverted to form the reconstructed image. The filtered back
projection is among the fastest methods of performing the inverse Radon
transform. The only tunable parameter for the FBP is the filter, which is
applied to the Fourier transformed projections. It may be used to suppress
high frequency noise in the reconstruction. ``skimage`` provides a few
different options for the filter.
"""
######################################################################
#
# Reconstruction with the Filtered Back Projection (FBP)
# ======================================================
#
# The mathematical foundation of the filtered back projection is the Fourier
# slice theorem [2]_. It uses Fourier transform of the projection and
# interpolation in Fourier space to obtain the 2D Fourier transform of the
# image, which is then inverted to form the reconstructed image. The filtered
# back projection is among the fastest methods of performing the inverse
# Radon transform. The only tunable parameter for the FBP is the filter,
# which is applied to the Fourier transformed projections. It may be used to
# suppress high frequency noise in the reconstruction. ``skimage`` provides a
# few different options for the filter.
from skimage.transform import iradon
@@ -110,42 +107,39 @@ ax2.set_title("Reconstruction error\nFiltered back projection")
ax2.imshow(reconstruction_fbp - image, cmap=plt.cm.Greys_r, **imkwargs)
plt.show()
"""
.. image:: PLOT2RST.current_figure
Reconstruction with the Simultaneous Algebraic Reconstruction Technique
=======================================================================
Algebraic reconstruction techniques for tomography are based on a
straightforward idea: for a pixelated image the value of a single ray in a
particular projection is simply a sum of all the pixels the ray passes through
on its way through the object. This is a way of expressing the forward Radon
transform. The inverse Radon transform can then be formulated as a (large) set
of linear equations. As each ray passes through a small fraction of the pixels
in the image, this set of equations is sparse, allowing iterative solvers for
sparse linear systems to tackle the system of equations. One iterative method
has been particularly popular, namely Kaczmarz' method [3]_, which has the
property that the solution will approach a least-squares solution of the
equation set.
The combination of the formulation of the reconstruction problem as a set
of linear equations and an iterative solver makes algebraic techniques
relatively flexible, hence some forms of prior knowledge can be incorporated
with relative ease.
``skimage`` provides one of the more popular variations of the algebraic
reconstruction techniques: the Simultaneous Algebraic Reconstruction Technique
(SART) [1]_ [4]_. It uses Kaczmarz' method [3]_ as the iterative solver. A good
reconstruction is normally obtained in a single iteration, making the method
computationally effective. Running one or more extra iterations will normally
improve the reconstruction of sharp, high frequency features and reduce the
mean squared error at the expense of increased high frequency noise (the user
will need to decide on what number of iterations is best suited to the problem
at hand. The implementation in ``skimage`` allows prior information of the
form of a lower and upper threshold on the reconstructed values to be supplied
to the reconstruction.
"""
######################################################################
#
# Reconstruction with the Simultaneous Algebraic Reconstruction Technique
# =======================================================================
#
# Algebraic reconstruction techniques for tomography are based on a
# straightforward idea: for a pixelated image the value of a single ray in a
# particular projection is simply a sum of all the pixels the ray passes
# through on its way through the object. This is a way of expressing the
# forward Radon transform. The inverse Radon transform can then be formulated
# as a (large) set of linear equations. As each ray passes through a small
# fraction of the pixels in the image, this set of equations is sparse,
# allowing iterative solvers for sparse linear systems to tackle the system
# of equations. One iterative method has been particularly popular, namely
# Kaczmarz' method [3]_, which has the property that the solution will
# approach a least-squares solution of the equation set.
#
# The combination of the formulation of the reconstruction problem as a set
# of linear equations and an iterative solver makes algebraic techniques
# relatively flexible, hence some forms of prior knowledge can be
# incorporated with relative ease.
#
# ``skimage`` provides one of the more popular variations of the algebraic
# reconstruction techniques: the Simultaneous Algebraic Reconstruction
# Technique (SART) [1]_ [4]_. It uses Kaczmarz' method [3]_ as the iterative
# solver. A good reconstruction is normally obtained in a single iteration,
# making the method computationally effective. Running one or more extra
# iterations will normally improve the reconstruction of sharp, high
# frequency features and reduce the mean squared error at the expense of
# increased high frequency noise (the user will need to decide on what number
# of iterations is best suited to the problem at hand. The implementation in
# ``skimage`` allows prior information of the form of a lower and upper
# threshold on the reconstructed values to be supplied to the reconstruction.
from skimage.transform import iradon_sart
@@ -176,19 +170,14 @@ ax4.set_title("Reconstruction error\nSART, 2 iterations")
ax4.imshow(reconstruction_sart2 - image, cmap=plt.cm.Greys_r, **imkwargs)
plt.show()
"""
.. image:: PLOT2RST.current_figure
.. [1] AC Kak, M Slaney, "Principles of Computerized Tomographic Imaging",
IEEE Press 1988. http://www.slaney.org/pct/pct-toc.html
.. [2] Wikipedia, Radon transform,
http://en.wikipedia.org/wiki/Radon_transform#Relationship_with_the_Fourier_transform
.. [3] S Kaczmarz, "Angenaeherte Aufloesung von Systemen linearer
Gleichungen", Bulletin International de l'Academie Polonaise des
Sciences et des Lettres 35 pp 355--357 (1937)
.. [4] AH Andersen, AC Kak, "Simultaneous algebraic reconstruction technique
(SART): a superior implementation of the ART algorithm", Ultrasonic
Imaging 6 pp 81--94 (1984)
"""
######################################################################
# References
#
# .. [1] AC Kak, M Slaney, "Principles of Computerized Tomographic Imaging", IEEE Press 1988. http://www.slaney.org/pct/pct-toc.html
#
# .. [2] Wikipedia, Radon transform, http://en.wikipedia.org/wiki/Radon_transform#Relationship_with_the_Fourier_transform
#
# .. [3] S Kaczmarz, "Angenaeherte Aufloesung von Systemen linearer Gleichungen", Bulletin International de l'Academie Polonaise des Sciences et des Lettres, 35 pp 355--357 (1937)
#
# .. [4] AH Andersen, AC Kak, "Simultaneous algebraic reconstruction
# technique (SART): a superior implementation of the ART algorithm", Ultrasonic Imaging 6 pp 81--94 (1984)
+15 -27
View File
@@ -30,39 +30,31 @@ eimg = filters.sobel(color.rgb2gray(img))
plt.title('Original Image')
plt.imshow(img)
"""
.. image:: PLOT2RST.current_figure
"""
######################################################################
resized = transform.resize(img, (img.shape[0], img.shape[1] - 200))
plt.figure()
plt.title('Resized Image')
plt.imshow(resized)
"""
.. image:: PLOT2RST.current_figure
"""
######################################################################
out = transform.seam_carve(img, eimg, 'vertical', 200)
plt.figure()
plt.title('Resized using Seam Carving')
plt.imshow(out)
"""
.. image:: PLOT2RST.current_figure
Resizing distorts the rocket and surrounding objects, whereas seam carving
removes empty spaces and preserves object proportions.
Object Removal
--------------
Seam carving can also be used to remove artifacts from images.
This requires weighting the artifact with low values. Recall lower weights are
preferentially removed in seam carving. The following code masks the rocket's
region with low weights, indicating it should be removed.
"""
######################################################################
# Resizing distorts the rocket and surrounding objects, whereas seam carving
# removes empty spaces and preserves object proportions.
#
# Object Removal
# --------------
#
# Seam carving can also be used to remove artifacts from images. This
# requires weighting the artifact with low values. Recall lower weights are
# preferentially removed in seam carving. The following code masks the
# rocket's region with low weights, indicating it should be removed.
masked_img = img.copy()
@@ -78,9 +70,7 @@ plt.title('Object Marked')
plt.imshow(masked_img)
"""
.. image:: PLOT2RST.current_figure
"""
######################################################################
eimg[rr, cc] -= 1000
@@ -91,6 +81,4 @@ resized = transform.resize(img, out.shape)
plt.imshow(out)
plt.show()
"""
.. image:: PLOT2RST.current_figure
"""
######################################################################
@@ -22,18 +22,15 @@ ax1.axis('off')
ax2.plot(hist[1][:-1], hist[0], lw=2)
ax2.set_title('histogram of grey values')
"""
.. 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:
"""
######################################################################
#
# 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:
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(6, 3), sharex=True, sharey=True)
ax1.imshow(coins > 100, cmap=plt.cm.gray, interpolation='nearest')
@@ -47,17 +44,13 @@ ax2.set_adjustable('box-forced')
margins = dict(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0, right=1)
fig.subplots_adjust(**margins)
"""
.. image:: PLOT2RST.current_figure
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.
"""
######################################################################
# 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.feature import canny
edges = canny(coins/255.)
@@ -67,11 +60,8 @@ ax.imshow(edges, cmap=plt.cm.gray, interpolation='nearest')
ax.axis('off')
ax.set_title('Canny detector')
"""
.. image:: PLOT2RST.current_figure
These contours are then filled using mathematical morphology.
"""
######################################################################
# These contours are then filled using mathematical morphology.
from scipy import ndimage as ndi
@@ -82,12 +72,9 @@ ax.imshow(fill_coins, cmap=plt.cm.gray, interpolation='nearest')
ax.axis('off')
ax.set_title('Filling the holes')
"""
.. image:: PLOT2RST.current_figure
Small spurious objects are easily removed by setting a minimum size for valid
objects.
"""
######################################################################
# Small spurious objects are easily removed by setting a minimum size for
# valid objects.
from skimage import morphology
coins_cleaned = morphology.remove_small_objects(fill_coins, 21)
@@ -96,20 +83,16 @@ ax.imshow(coins_cleaned, cmap=plt.cm.gray, interpolation='nearest')
ax.axis('off')
ax.set_title('Removing small objects')
"""
.. 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
=========================
We therefore try a region-based method using the watershed transform. First, we
find an elevation map using the Sobel gradient of the image.
"""
######################################################################
# 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
#=========================
#
#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.filters import sobel
@@ -120,12 +103,9 @@ ax.imshow(elevation_map, cmap=plt.cm.gray, interpolation='nearest')
ax.axis('off')
ax.set_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.
"""
######################################################################
# 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
@@ -136,13 +116,9 @@ ax.imshow(markers, cmap=plt.cm.spectral, interpolation='nearest')
ax.axis('off')
ax.set_title('markers')
"""
.. image:: PLOT2RST.current_figure
Finally, we use the watershed transform to fill regions of the elevation map
starting from the markers determined above:
"""
######################################################################
# Finally, we use the watershed transform to fill regions of the elevation
# map starting from the markers determined above:
segmentation = morphology.watershed(elevation_map, markers)
fig, ax = plt.subplots(figsize=(4, 3))
@@ -150,13 +126,9 @@ ax.imshow(segmentation, cmap=plt.cm.gray, interpolation='nearest')
ax.axis('off')
ax.set_title('segmentation')
"""
.. image:: PLOT2RST.current_figure
This last method works even better, and the coins can be segmented and labeled
individually.
"""
######################################################################
# This last method works even better, and the coins can be segmented and
# labeled individually.
from skimage.color import label2rgb
@@ -175,9 +147,3 @@ ax2.set_adjustable('box-forced')
fig.subplots_adjust(**margins)
"""
.. image:: PLOT2RST.current_figure
"""
plt.show()
+42 -52
View File
@@ -18,48 +18,45 @@ from skimage import transform as tf
margins = dict(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0, right=1)
"""
Basics
======
Several different geometric transformation types are supported: similarity,
affine, projective and polynomial.
Geometric transformations can either be created using the explicit parameters
(e.g. scale, shear, rotation and translation) or the transformation matrix:
First we create a transformation using explicit parameters:
"""
######################################################################
# Basics
# ======
#
# Several different geometric transformation types are supported: similarity,
# affine, projective and polynomial.
#
# Geometric transformations can either be created using the explicit
# parameters (e.g. scale, shear, rotation and translation) or the
# transformation matrix:
#
# First we create a transformation using explicit parameters:
tform = tf.SimilarityTransform(scale=1, rotation=math.pi / 2,
translation=(0, 1))
print(tform.params)
"""
Alternatively you can define a transformation by the transformation matrix
itself:
"""
######################################################################
# Alternatively you can define a transformation by the transformation matrix
# itself:
matrix = tform.params.copy()
matrix[1, 2] = 2
tform2 = tf.SimilarityTransform(matrix)
"""
These transformation objects can then be used to apply forward and inverse
coordinate transformations between the source and destination coordinate
systems:
"""
######################################################################
# These transformation objects can then be used to apply forward and inverse
# coordinate transformations between the source and destination coordinate
# systems:
coord = [1, 0]
print(tform2(coord))
print(tform2.inverse(tform(coord)))
"""
Image warping
=============
Geometric transformations can also be used to warp images:
"""
######################################################################
# Image warping
# =============
#
# Geometric transformations can also be used to warp images:
text = data.text()
@@ -79,25 +76,24 @@ ax2.axis('off')
ax3.imshow(back_rotated)
ax3.axis('off')
"""
.. image:: PLOT2RST.current_figure
Parameter estimation
====================
In addition to the basic functionality mentioned above you can also estimate the
parameters of a geometric transformation using the least-squares method.
This can amongst other things be used for image registration or rectification,
where you have a set of control points or homologous/corresponding points in two
images.
Let's assume we want to recognize letters on a photograph which was not taken
from the front but at a certain angle. In the simplest case of a plane paper
surface the letters are projectively distorted. Simple matching algorithms would
not be able to match such symbols. One solution to this problem would be to warp
the image so that the distortion is removed and then apply a matching algorithm:
"""
######################################################################
# Parameter estimation
# ====================
#
# In addition to the basic functionality mentioned above you can also
# estimate the parameters of a geometric transformation using the least-
# squares method.
#
# This can amongst other things be used for image registration or
# rectification, where you have a set of control points or
# homologous/corresponding points in two images.
#
# Let's assume we want to recognize letters on a photograph which was not
# taken from the front but at a certain angle. In the simplest case of a
# plane paper surface the letters are projectively distorted. Simple matching
# algorithms would not be able to match such symbols. One solution to this
# problem would be to warp the image so that the distortion is removed and
# then apply a matching algorithm:
text = data.text()
@@ -126,9 +122,3 @@ ax1.plot(dst[:, 0], dst[:, 1], '.r')
ax1.axis('off')
ax2.imshow(warped)
ax2.axis('off')
"""
.. image:: PLOT2RST.current_figure
"""
plt.show()
+136 -170
View File
@@ -36,11 +36,9 @@ orig_phantom = img_as_ubyte(io.imread(os.path.join(data_dir, "phantom.png"),
fig, ax = plt.subplots()
ax.imshow(orig_phantom, cmap=plt.cm.gray)
"""
.. image:: PLOT2RST.current_figure
######################################################################
# Let's also define a convenience function for plotting comparisons:
Let's also define a convenience function for plotting comparisons:
"""
def plot_comparison(original, filtered, filter_name):
@@ -55,16 +53,15 @@ def plot_comparison(original, filtered, filter_name):
ax2.axis('off')
ax2.set_adjustable('box-forced')
"""
Erosion
=======
Morphological ``erosion`` sets a pixel at (i, j) to the *minimum over all
pixels in the neighborhood centered at (i, j)*. The structuring element,
``selem``, passed to ``erosion`` is a boolean array that describes this
neighborhood. Below, we use ``disk`` to create a circular structuring element,
which we use for most of the following examples.
"""
######################################################################
# Erosion
# =======
#
# Morphological ``erosion`` sets a pixel at (i, j) to the *minimum over all
# pixels in the neighborhood centered at (i, j)*. The structuring element,
# ``selem``, passed to ``erosion`` is a boolean array that describes this
# neighborhood. Below, we use ``disk`` to create a circular structuring
# element, which we use for most of the following examples.
from skimage.morphology import erosion, dilation, opening, closing, white_tophat
from skimage.morphology import black_tophat, skeletonize, convex_hull_image
@@ -74,68 +71,58 @@ selem = disk(6)
eroded = erosion(orig_phantom, selem)
plot_comparison(orig_phantom, eroded, 'erosion')
"""
.. image:: PLOT2RST.current_figure
Notice how the white boundary of the image disappears or gets eroded as we
increase the size of the disk. Also notice the increase in size of the two
black ellipses in the center and the disappearance of the 3 light grey
patches in the lower part of the image.
Dilation
========
Morphological ``dilation`` sets a pixel at (i, j) to the *maximum over all
pixels in the neighborhood centered at (i, j)*. Dilation enlarges bright
regions and shrinks dark regions.
"""
######################################################################
# Notice how the white boundary of the image disappears or gets eroded as we
# increase the size of the disk. Also notice the increase in size of the two
# black ellipses in the center and the disappearance of the 3 light grey
# patches in the lower part of the image.
#
#Dilation
#========
#
#Morphological ``dilation`` sets a pixel at (i, j) to the *maximum over all
#pixels in the neighborhood centered at (i, j)*. Dilation enlarges bright
#regions and shrinks dark regions.
dilated = dilation(orig_phantom, selem)
plot_comparison(orig_phantom, dilated, 'dilation')
"""
.. image:: PLOT2RST.current_figure
Notice how the white boundary of the image thickens, or gets dilated, as we
increase the size of the disk. Also notice the decrease in size of the two
black ellipses in the centre, and the thickening of the light grey circle in
the center and the 3 patches in the lower part of the image.
Opening
=======
Morphological ``opening`` on an image is defined as an *erosion followed by a
dilation*. Opening can remove small bright spots (i.e. "salt") and connect
small dark cracks.
"""
######################################################################
# Notice how the white boundary of the image thickens, or gets dilated, as we
#increase the size of the disk. Also notice the decrease in size of the two
#black ellipses in the centre, and the thickening of the light grey circle
#in the center and the 3 patches in the lower part of the image.
#
#Opening
#=======
#
#Morphological ``opening`` on an image is defined as an *erosion followed by
#a dilation*. Opening can remove small bright spots (i.e. "salt") and
#connect small dark cracks.
opened = opening(orig_phantom, selem)
plot_comparison(orig_phantom, opened, 'opening')
"""
.. image:: PLOT2RST.current_figure
Since ``opening`` an image starts with an erosion operation, light regions that
are *smaller* than the structuring element are removed. The dilation operation
that follows ensures that light regions that are *larger* than the structuring
element retain their original size. Notice how the light and dark shapes in the
center their original thickness but the 3 lighter patches in the bottom get
completely eroded. The size dependence is highlighted by the outer white ring:
The parts of the ring thinner than the structuring element were completely
erased, while the thicker region at the top retains its original thickness.
Closing
=======
Morphological ``closing`` on an image is defined as a *dilation followed by an
erosion*. Closing can remove small dark spots (i.e. "pepper") and connect
small bright cracks.
To illustrate this more clearly, let's add a small crack to the white border:
"""
######################################################################
#Since ``opening`` an image starts with an erosion operation, light regions
#that are *smaller* than the structuring element are removed. The dilation
#operation that follows ensures that light regions that are *larger* than
#the structuring element retain their original size. Notice how the light
#and dark shapes in the center their original thickness but the 3 lighter
#patches in the bottom get completely eroded. The size dependence is
#highlighted by the outer white ring: The parts of the ring thinner than the
#structuring element were completely erased, while the thicker region at the
#top retains its original thickness.
#
#Closing
#=======
#
#Morphological ``closing`` on an image is defined as a *dilation followed by
#an erosion*. Closing can remove small dark spots (i.e. "pepper") and
#connect small bright cracks.
#
#To illustrate this more clearly, let's add a small crack to the white
#border:
phantom = orig_phantom.copy()
phantom[10:30, 200:210] = 0
@@ -143,26 +130,23 @@ phantom[10:30, 200:210] = 0
closed = closing(phantom, selem)
plot_comparison(phantom, closed, 'closing')
"""
.. image:: PLOT2RST.current_figure
Since ``closing`` an image starts with an dilation operation, dark regions
that are *smaller* than the structuring element are removed. The dilation
operation that follows ensures that dark regions that are *larger* than the
structuring element retain their original size. Notice how the white ellipses
at the bottom get connected because of dilation, but other dark region retain
their original sizes. Also notice how the crack we added is mostly removed.
White tophat
============
The ``white_tophat`` of an image is defined as the *image minus its
morphological opening*. This operation returns the bright spots of the image
that are smaller than the structuring element.
To make things interesting, we'll add bright and dark spots to the image:
"""
######################################################################
# Since ``closing`` an image starts with an dilation operation, dark regions
# that are *smaller* than the structuring element are removed. The dilation
# operation that follows ensures that dark regions that are *larger* than the
# structuring element retain their original size. Notice how the white
# ellipses at the bottom get connected because of dilation, but other dark
# region retain their original sizes. Also notice how the crack we added is
# mostly removed.
#
# White tophat
# ============
#
# The ``white_tophat`` of an image is defined as the *image minus its
# morphological opening*. This operation returns the bright spots of the
# image that are smaller than the structuring element.
#
# To make things interesting, we'll add bright and dark spots to the image:
phantom = orig_phantom.copy()
phantom[340:350, 200:210] = 255
@@ -171,86 +155,70 @@ phantom[100:110, 200:210] = 0
w_tophat = white_tophat(phantom, selem)
plot_comparison(phantom, w_tophat, 'white tophat')
"""
.. image:: PLOT2RST.current_figure
As you can see, the 10-pixel wide white square is highlighted since it is
smaller than the structuring element. Also, the thin, white edges around most
of the ellipse are retained because they're smaller than the structuring
element, but the thicker region at the top disappears.
Black tophat
============
The ``black_tophat`` of an image is defined as its morphological **closing
minus the original image**. This operation returns the *dark spots of the
image that are smaller than the structuring element*.
"""
######################################################################
# As you can see, the 10-pixel wide white square is highlighted since it is
# smaller than the structuring element. Also, the thin, white edges around
# most of the ellipse are retained because they're smaller than the
# structuring element, but the thicker region at the top disappears.
#
# Black tophat
# ============
#
# The ``black_tophat`` of an image is defined as its morphological **closing
# minus the original image**. This operation returns the *dark spots of the
# image that are smaller than the structuring element*.
b_tophat = black_tophat(phantom, selem)
plot_comparison(phantom, b_tophat, 'black tophat')
"""
.. image:: PLOT2RST.current_figure
As you can see, the 10-pixel wide black square is highlighted since it is
smaller than the structuring element.
Duality
-------
As you should have noticed, many of these operations are simply the reverse
of another operation. This duality can be summarized as follows:
1. Erosion <-> Dilation
2. Opening <-> Closing
3. White tophat <-> Black tophat
Skeletonize
===========
Thinning is used to reduce each connected component in a binary image to a
*single-pixel wide skeleton*. It is important to note that this is performed
on binary images only.
"""
######################################################################
#As you can see, the 10-pixel wide black square is highlighted since
#it is smaller than the structuring element.
#
#**Duality**
#
#As you should have noticed, many of these operations are simply the reverse
#of another operation. This duality can be summarized as follows:
#
# 1. Erosion <-> Dilation
#
# 2. Opening <-> Closing
#
# 3. White tophat <-> Black tophat
#
#Skeletonize
#===========
#
#Thinning is used to reduce each connected component in a binary image to a
#*single-pixel wide skeleton*. It is important to note that this is
#performed on binary images only.
horse = io.imread(os.path.join(data_dir, "horse.png"), as_grey=True)
sk = skeletonize(horse == 0)
plot_comparison(horse, sk, 'skeletonize')
"""
.. image:: PLOT2RST.current_figure
As the name suggests, this technique is used to thin the image to 1-pixel wide
skeleton by applying thinning successively.
Convex hull
===========
The ``convex_hull_image`` is the *set of pixels included in the smallest
convex polygon that surround all white pixels in the input image*. Again note
that this is also performed on binary images.
"""
######################################################################
#
# As the name suggests, this technique is used to thin the image to 1-pixel
# wide skeleton by applying thinning successively.
#
# Convex hull
# ===========
#
# The ``convex_hull_image`` is the *set of pixels included in the smallest
# convex polygon that surround all white pixels in the input image*. Again
# note that this is also performed on binary images.
hull1 = convex_hull_image(horse == 0)
plot_comparison(horse, hull1, 'convex hull')
"""
.. image:: PLOT2RST.current_figure
As the figure illustrates, ``convex_hull_image`` gives the smallest polygon
which covers the white or True completely in the image.
If we add a small grain to the image, we can see how the convex hull adapts to
enclose that grain:
"""
######################################################################
# As the figure illustrates, ``convex_hull_image`` gives the smallest polygon
# which covers the white or True completely in the image.
#
# If we add a small grain to the image, we can see how the convex hull adapts
# to enclose that grain:
import numpy as np
@@ -260,19 +228,17 @@ horse_mask[45:50, 75:80] = 1
hull2 = convex_hull_image(horse_mask)
plot_comparison(horse_mask, hull2, 'convex hull')
"""
.. image:: PLOT2RST.current_figure
Additional Resources
====================
1. `MathWorks tutorial on morphological processing
<http://www.mathworks.com/help/images/morphology-fundamentals-dilation-and-erosion.html>`_
2. `Auckland university's tutorial on Morphological Image Processing
<http://www.cs.auckland.ac.nz/courses/compsci773s1c/lectures/ImageProcessing-html/topic4.htm>`_
3. http://en.wikipedia.org/wiki/Mathematical_morphology
"""
plt.show()
######################################################################
#
# Additional Resources
# ====================
#
# 1. `MathWorks tutorial on morphological processing
# <http://www.mathworks.com/help/images/morphology-fundamentals-dilation-and-
# erosion.html>`_
#
# 2. `Auckland university's tutorial on Morphological Image
# Processing <http://www.cs.auckland.ac.nz/courses/compsci773s1c/lectures
# /ImageProcessing-html/topic4.htm>`_
#
# 3. http://en.wikipedia.org/wiki/Mathematical_morphology
+156 -241
View File
@@ -50,17 +50,14 @@ ax1.axis('off')
ax2.plot(hist[1][:-1], hist[0], lw=2)
ax2.set_title('Histogram of grey values')
"""
.. image:: PLOT2RST.current_figure
Noise removal
=============
Some noise is added to the image, 1% of pixels are randomly set to 255, 1% are
randomly set to 0. The **median** filter is applied to remove the noise.
"""
######################################################################
#
# Noise removal
# =============
#
# Some noise is added to the image, 1% of pixels are randomly set to 255, 1%
# are randomly set to 0. The **median** filter is applied to remove the
# noise.
from skimage.filters.rank import median
from skimage.morphology import disk
@@ -96,23 +93,20 @@ ax4.axis('off')
ax4.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
The added noise is efficiently removed, as the image defaults are small (1
pixel wide), a small filter radius is sufficient. As the radius is increasing,
objects with bigger sizes are filtered as well, such as the camera tripod. The
median filter is often used for noise removal because borders are preserved and
e.g. salt and pepper noise typically does not distort the gray-level.
Image smoothing
================
The example hereunder shows how a local **mean** filter smooths the camera man
image.
"""
######################################################################
#
# The added noise is efficiently removed, as the image defaults are small (1
# pixel wide), a small filter radius is sufficient. As the radius is
# increasing, objects with bigger sizes are filtered as well, such as the
# camera tripod. The median filter is often used for noise removal because
# borders are preserved and e.g. salt and pepper noise typically does not
# distort the gray-level.
#
# Image smoothing
# ===============
#
# The example hereunder shows how a local **mean** filter smooths the camera
# man image.
from skimage.filters.rank import mean
@@ -130,21 +124,17 @@ ax2.set_title('Local mean $r=10$')
ax2.axis('off')
ax2.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
One may be interested in smoothing an image while preserving important borders
(median filters already achieved this), here we use the **bilateral** filter
that restricts the local neighborhood to pixel having a gray-level similar to
the central one.
.. note::
A different implementation is available for color images in
`skimage.filters.denoise_bilateral`.
"""
######################################################################
#
# One may be interested in smoothing an image while preserving important
# borders (median filters already achieved this), here we use the
# **bilateral** filter that restricts the local neighborhood to pixel having
# a gray-level similar to the central one.
#
# .. note::
#
# A different implementation is available for color images in
# `skimage.filters.denoise_bilateral`.
from skimage.filters.rank import mean_bilateral
@@ -173,27 +163,21 @@ ax4.imshow(bilat[200:350, 350:450], cmap=plt.cm.gray)
ax4.axis('off')
ax4.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
One can see that the large continuous part of the image (e.g. sky) is smoothed
whereas other details are preserved.
Contrast enhancement
====================
We compare here how the global histogram equalization is applied locally.
The equalized image [2]_ has a roughly linear cumulative distribution function
for each pixel neighborhood. The local version [3]_ of the histogram
equalization emphasizes every local gray-level variations.
.. [2] http://en.wikipedia.org/wiki/Histogram_equalization
.. [3] http://en.wikipedia.org/wiki/Adaptive_histogram_equalization
"""
######################################################################
# One can see that the large continuous part of the image (e.g. sky) is
# smoothed whereas other details are preserved.
#
# Contrast enhancement
# ====================
#
# We compare here how the global histogram equalization is applied locally.
#
# The equalized image [2]_ has a roughly linear cumulative distribution
# function for each pixel neighborhood. The local version [3]_ of the
# histogram equalization emphasizes every local gray-level variations.
#
# .. [2] http://en.wikipedia.org/wiki/Histogram_equalization
# .. [3] http://en.wikipedia.org/wiki/Adaptive_histogram_equalization
from skimage import exposure
from skimage.filters import rank
@@ -230,18 +214,13 @@ ax5.axis('off')
ax6.plot(loc_hist[1][:-1], loc_hist[0], lw=2)
ax6.set_title('Histogram of gray values')
"""
.. image:: PLOT2RST.current_figure
Another way to maximize the number of gray-levels used for an image is to apply
a local auto-leveling, i.e. the gray-value of a pixel is proportionally
remapped between local minimum and local maximum.
The following example shows how local auto-level enhances the camara man
picture.
"""
######################################################################
# Another way to maximize the number of gray-levels used for an image is to
# apply a local auto-leveling, i.e. the gray-value of a pixel is
# proportionally remapped between local minimum and local maximum.
#
# The following example shows how local auto-level enhances the camara man
# picture.
from skimage.filters.rank import autolevel
@@ -261,19 +240,14 @@ ax2.set_title('Local autolevel')
ax2.axis('off')
ax2.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
This filter is very sensitive to local outliers, see the little white spot in
the left part of the sky. This is due to a local maximum which is very high
comparing to the rest of the neighborhood. One can moderate this using the
percentile version of the auto-level filter which uses given percentiles (one
inferior, one superior) in place of local minimum and maximum. The example
below illustrates how the percentile parameters influence the local auto-level
result.
"""
######################################################################
# This filter is very sensitive to local outliers, see the little white spot
# in the left part of the sky. This is due to a local maximum which is very
# high comparing to the rest of the neighborhood. One can moderate this using
# the percentile version of the auto-level filter which uses given
# percentiles (one inferior, one superior) in place of local minimum and
# maximum. The example below illustrates how the percentile parameters
# influence the local auto-level result.
from skimage.filters.rank import autolevel_percentile
@@ -310,15 +284,10 @@ for i in range(0,len(image_list)):
axes_list[i].axis('off')
axes_list[i].set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
The morphological contrast enhancement filter replaces the central pixel by the
local maximum if the original pixel value is closest to local maximum,
otherwise by the minimum local.
"""
######################################################################
# The morphological contrast enhancement filter replaces the central pixel by
# the local maximum if the original pixel value is closest to local maximum,
# otherwise by the minimum local.
from skimage.filters.rank import enhance_contrast
@@ -347,14 +316,9 @@ ax4.imshow(enh[200:350, 350:450], cmap=plt.cm.gray)
ax4.axis('off')
ax4.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
The percentile version of the local morphological contrast enhancement uses
percentile *p0* and *p1* instead of the local minimum and maximum.
"""
######################################################################
# The percentile version of the local morphological contrast enhancement uses
# percentile *p0* and *p1* instead of the local minimum and maximum.
from skimage.filters.rank import enhance_contrast_percentile
@@ -379,29 +343,25 @@ for ax in ax.ravel():
ax.axis('off')
ax.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
Image threshold
===============
The Otsu threshold [1]_ method can be applied locally using the local gray-
level distribution. In the example below, for each pixel, an "optimal"
threshold is determined by maximizing the variance between two classes of
pixels of the local neighborhood defined by a structuring element.
The example compares the local threshold with the global threshold
`skimage.filters.threshold_otsu`.
.. note::
Local is much slower than global thresholding. A function for global Otsu
thresholding can be found in : `skimage.filters.threshold_otsu`.
.. [4] http://en.wikipedia.org/wiki/Otsu's_method
"""
######################################################################
#
# Image threshold
# ===============
#
# The Otsu threshold [1]_ method can be applied locally using the local gray-
# level distribution. In the example below, for each pixel, an "optimal"
# threshold is determined by maximizing the variance between two classes of
# pixels of the local neighborhood defined by a structuring element.
#
# The example compares the local threshold with the global threshold
# `skimage.filters.threshold_otsu`.
#
# .. note::
#
# Local is much slower than global thresholding. A function for global
# Otsu thresholding can be found in : `skimage.filters.threshold_otsu`.
#
# .. [4] http://en.wikipedia.org/wiki/Otsu's_method
from skimage.filters.rank import otsu
from skimage.filters import threshold_otsu
@@ -438,14 +398,9 @@ for ax in ax.ravel():
ax.axis('off')
ax.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
The following example shows how local Otsu thresholding handles a global level
shift applied to a synthetic image.
"""
######################################################################
# The following example shows how local Otsu thresholding handles a global
# level shift applied to a synthetic image.
n = 100
theta = np.linspace(0, 10 * np.pi, n)
@@ -467,25 +422,20 @@ ax2.set_title('Local Otsu ($r=%d$)' % radius)
ax2.axis('off')
ax2.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
Image morphology
================
Local maximum and local minimum are the base operators for gray-level
morphology.
.. note::
`skimage.dilate` and `skimage.erode` are equivalent filters (see below for
comparison).
Here is an example of the classical morphological gray-level filters: opening,
closing and morphological gradient.
"""
######################################################################
# Image morphology
# ================
#
# Local maximum and local minimum are the base operators for gray-level
# morphology.
#
# .. note::
#
# `skimage.dilate` and `skimage.erode` are equivalent filters (see below
# for comparison).
#
# Here is an example of the classical morphological gray-level filters:
# opening, closing and morphological gradient.
from skimage.filters.rank import maximum, minimum, gradient
@@ -514,28 +464,24 @@ ax4.set_title('Morphological gradient')
for ax in ax.ravel():
ax.axis('off')
ax.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
Feature extraction
===================
Local histograms can be exploited to compute local entropy, which is related to
the local image complexity. Entropy is computed using base 2 logarithm i.e. the
filter returns the minimum number of bits needed to encode local gray-level
distribution.
`skimage.rank.entropy` returns the local entropy on a given structuring
element. The following example shows applies this filter on 8- and 16-bit
images.
.. note::
to better use the available image bit, the function returns 10x entropy for
8-bit images and 1000x entropy for 16-bit images.
"""
######################################################################
#
# Feature extraction
# ===================
#
# Local histograms can be exploited to compute local entropy, which is
# related to the local image complexity. Entropy is computed using base 2
# logarithm i.e. the filter returns the minimum number of bits needed to
# encode local gray-level distribution.
#
# `skimage.rank.entropy` returns the local entropy on a given structuring
# element. The following example shows applies this filter on 8- and 16-bit
# images.
#
# .. note::
#
# to better use the available image bit, the function returns 10x entropy
# for 8-bit images and 1000x entropy for 16-bit images.
from skimage import data
from skimage.filters.rank import entropy
@@ -557,22 +503,18 @@ ax2.set_title('Entropy')
ax2.axis('off')
ax2.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
Implementation
==============
The central part of the `skimage.rank` filters is build on a sliding window
that updates the local gray-level histogram. This approach limits the algorithm
complexity to O(n) where n is the number of image pixels. The complexity is
also limited with respect to the structuring element size.
In the following we compare the performance of different implementations
available in `skimage`.
"""
######################################################################
#
# Implementation
# ==============
#
# The central part of the `skimage.rank` filters is build on a sliding window
# that updates the local gray-level histogram. This approach limits the
# algorithm complexity to O(n) where n is the number of image pixels. The
# complexity is also limited with respect to the structuring element size.
#
# In the following we compare the performance of different implementations
# available in `skimage`.
from time import time
@@ -611,16 +553,13 @@ def cm_dil(image, selem):
def ndi_med(image, n):
return percentile_filter(image, 50, size=n * 2 - 1)
"""
Comparison between
* `filters.rank.maximum`
* `morphology.dilate`
on increasing structuring element size:
"""
######################################################################
# Comparison between
#
# * `filters.rank.maximum`
# * `morphology.dilate`
#
# on increasing structuring element size:
a = data.camera()
@@ -641,13 +580,8 @@ ax.set_xlabel('Element radius')
ax.plot(e_range, rec)
ax.legend(['filters.rank.maximum', 'morphology.dilate'])
"""
.. image:: PLOT2RST.current_figure
and increasing image size:
"""
######################################################################
# and increasing image size:
r = 9
elem = disk(r + 1)
@@ -670,18 +604,13 @@ ax.plot(s_range, rec)
ax.legend(['filters.rank.maximum', 'morphology.dilate'])
"""
.. image:: PLOT2RST.current_figure
Comparison between:
* `filters.rank.median`
* `scipy.ndimage.percentile`
on increasing structuring element size:
"""
######################################################################
# Comparison between:
#
# * `filters.rank.median`
# * `scipy.ndimage.percentile`
#
# on increasing structuring element size:
a = data.camera()
@@ -702,12 +631,8 @@ ax.legend(['filters.rank.median', 'scipy.ndimage.percentile'])
ax.set_ylabel('Time (ms)')
ax.set_xlabel('Element radius')
"""
.. image:: PLOT2RST.current_figure
Comparison of outcome of the three methods:
"""
######################################################################
# Comparison of outcome of the three methods:
fig, (ax0, ax1) = plt.subplots(ncols=2, sharex=True, sharey=True)
ax0.set_title('filters.rank.median')
@@ -719,12 +644,8 @@ ax1.imshow(rndi)
ax1.axis('off')
ax1.set_adjustable('box-forced')
"""
.. image:: PLOT2RST.current_figure
and increasing image size:
"""
######################################################################
# and increasing image size:
r = 9
elem = disk(r + 1)
@@ -746,9 +667,3 @@ ax.legend(['filters.rank.median', 'scipy.ndimage.percentile'])
ax.set_ylabel('Time (ms)')
ax.set_xlabel('Image size')
"""
.. image:: PLOT2RST.current_figure
"""
plt.show()
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2015, Óscar Nájera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of sphinx-gallery nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+6
View File
@@ -0,0 +1,6 @@
This directory was taken from
https://github.com/sphinx-gallery/sphinx-gallery
Files should not diverge from the original sphinx-gallery project, and
any modifications should be submitted as pull requests to sphinx-gallery
(with some exceptions such as CSS tweaking).
+13
View File
@@ -0,0 +1,13 @@
"""
==============
Sphinx Gallery
==============
"""
import os
__version__ = '0.1.2'
def glr_path_static():
"""Returns path to packaged static files"""
return os.path.abspath(os.path.join(os.path.dirname(__file__), '_static'))
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 52 KiB

+137
View File
@@ -0,0 +1,137 @@
/*
Sphinx-Gallery has compatible CSS to fix default sphinx themes
Tested for Sphinx 1.3.1 for all themes: default, alabaster, sphinxdoc,
scrolls, agogo, traditional, nature, haiku, pyramid
Tested for Read the Docs theme 0.1.7 */
.sphx-glr-thumbcontainer {
background: #fff;
border: solid #fff 1px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
box-shadow: none;
float: left;
margin: 5px;
min-height: 240px;
padding-top: 5px;
position: relative;
}
.sphx-glr-thumbcontainer:hover {
border: solid #b4ddfc 1px;
box-shadow: 0 0 15px rgba(142, 176, 202, 0.5);
}
.sphx-glr-thumbcontainer a.internal {
bottom: 0;
display: block;
left: 0;
padding: 150px 10px 0;
position: absolute;
right: 0;
top: 0;
}
/* Next one is to avoid Sphinx traditional theme to cover all the
thumbnail with its default link Background color */
.sphx-glr-thumbcontainer a.internal:hover {
background-color: transparent;
}
.sphx-glr-thumbcontainer p {
margin: 0 0 .1em 0;
}
.sphx-glr-thumbcontainer .figure {
margin: 10px;
width: 200px;
}
.sphx-glr-thumbcontainer img {
display: inline;
max-height: 200px;
width: 200px;
}
.sphx-glr-thumbcontainer[tooltip]:hover:after {
background: rgba(0, 0, 0, 0.8);
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
color: #fff;
content: attr(tooltip);
left: 95%;
padding: 5px 15px;
position: absolute;
z-index: 98;
width: 240px;
bottom: 52%;
}
.sphx-glr-thumbcontainer[tooltip]:hover:before {
border: solid;
border-color: #333 transparent;
border-width: 18px 0 0 20px;
bottom: 58%;
content: '';
left: 85%;
position: absolute;
z-index: 99;
}
.highlight-pytb pre {
background-color: #ffe4e4;
border: 1px solid #f66;
margin-top: 10px;
padding: 7px;
}
.sphx-glr-script-out {
color: #888;
margin: 0;
}
.sphx-glr-script-out .highlight {
background-color: transparent;
margin-left: 2.5em;
margin-top: -1.4em;
}
.sphx-glr-script-out .highlight pre {
background-color: #fafae2;
border: 0;
max-height: 30em;
overflow: auto;
padding-left: 1ex;
margin: 0px;
word-break: break-word;
}
.sphx-glr-script-out + p {
margin-top: 1.8em;
}
blockquote.sphx-glr-script-out {
margin-left: 0pt;
}
.sphx-glr-download {
background-color: #ffc;
border: 1px solid #c2c22d;
border-radius: 4px;
margin: 1em auto 1ex auto;
max-width: 45ex;
padding: 1ex;
}
.sphx-glr-download a {
color: #4b4600;
}
ul.sphx-glr-horizontal {
list-style: none;
padding: 0;
}
ul.sphx-glr-horizontal li {
display: inline;
}
ul.sphx-glr-horizontal img {
height: auto !important;
}
p.sphx-glr-signature a.reference.external {
background-color: #EBECED;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
padding: 3px;
position: absolute;
right: 15px;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+180
View File
@@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-
# Author: Óscar Nájera
# License: 3-clause BSD
"""
========================
Backreferences Generator
========================
Reviews generated example files in order to keep track of used modules
"""
from __future__ import print_function
import ast
import os
# Try Python 2 first, otherwise load from Python 3
try:
import cPickle as pickle
except ImportError:
import pickle
class NameFinder(ast.NodeVisitor):
"""Finds the longest form of variable names and their imports in code
Only retains names from imported modules.
"""
def __init__(self):
super(NameFinder, self).__init__()
self.imported_names = {}
self.accessed_names = set()
def visit_Import(self, node, prefix=''):
for alias in node.names:
local_name = alias.asname or alias.name
self.imported_names[local_name] = prefix + alias.name
def visit_ImportFrom(self, node):
self.visit_Import(node, node.module + '.')
def visit_Name(self, node):
self.accessed_names.add(node.id)
def visit_Attribute(self, node):
attrs = []
while isinstance(node, ast.Attribute):
attrs.append(node.attr)
node = node.value
if isinstance(node, ast.Name):
# This is a.b, not e.g. a().b
attrs.append(node.id)
self.accessed_names.add('.'.join(reversed(attrs)))
else:
# need to get a in a().b
self.visit(node)
def get_mapping(self):
for name in self.accessed_names:
local_name = name.split('.', 1)[0]
remainder = name[len(local_name):]
if local_name in self.imported_names:
# Join import path to relative path
full_name = self.imported_names[local_name] + remainder
yield name, full_name
def get_short_module_name(module_name, obj_name):
""" Get the shortest possible module name """
parts = module_name.split('.')
short_name = module_name
for i in range(len(parts) - 1, 0, -1):
short_name = '.'.join(parts[:i])
try:
exec('from %s import %s' % (short_name, obj_name))
except Exception: # libraries can throw all sorts of exceptions...
# get the last working module name
short_name = '.'.join(parts[:(i + 1)])
break
return short_name
def identify_names(code):
"""Builds a codeobj summary by identifying and resolving used names
>>> code = '''
... from a.b import c
... import d as e
... print(c)
... e.HelloWorld().f.g
... '''
>>> for name, o in sorted(identify_names(code).items()):
... print(name, o['name'], o['module'], o['module_short'])
c c a.b a.b
e.HelloWorld HelloWorld d d
"""
finder = NameFinder()
finder.visit(ast.parse(code))
example_code_obj = {}
for name, full_name in finder.get_mapping():
# name is as written in file (e.g. np.asarray)
# full_name includes resolved import path (e.g. numpy.asarray)
module, attribute = full_name.rsplit('.', 1)
# get shortened module name
module_short = get_short_module_name(module, attribute)
cobj = {'name': attribute, 'module': module,
'module_short': module_short}
example_code_obj[name] = cobj
return example_code_obj
def scan_used_functions(example_file, gallery_conf):
"""save variables so we can later add links to the documentation"""
example_code_obj = identify_names(open(example_file).read())
if example_code_obj:
codeobj_fname = example_file[:-3] + '_codeobj.pickle'
with open(codeobj_fname, 'wb') as fid:
pickle.dump(example_code_obj, fid, pickle.HIGHEST_PROTOCOL)
backrefs = set('{module_short}.{name}'.format(**entry)
for entry in example_code_obj.values()
if entry['module'].startswith(gallery_conf['doc_module']))
return backrefs
THUMBNAIL_TEMPLATE = """
.. raw:: html
<div class="sphx-glr-thumbcontainer" tooltip="{snippet}">
.. only:: html
.. figure:: /{thumbnail}
:ref:`sphx_glr_{ref_name}`
.. raw:: html
</div>
"""
BACKREF_THUMBNAIL_TEMPLATE = THUMBNAIL_TEMPLATE + """
.. only:: not html
* :ref:`sphx_glr_{ref_name}`
"""
def _thumbnail_div(full_dir, fname, snippet, is_backref=False):
"""Generates RST to place a thumbnail in a gallery"""
thumb = os.path.join(full_dir, 'images', 'thumb',
'sphx_glr_%s_thumb.png' % fname[:-3])
ref_name = os.path.join(full_dir, fname).replace(os.path.sep, '_')
template = BACKREF_THUMBNAIL_TEMPLATE if is_backref else THUMBNAIL_TEMPLATE
return template.format(snippet=snippet, thumbnail=thumb, ref_name=ref_name)
def write_backreferences(seen_backrefs, gallery_conf,
target_dir, fname, snippet):
"""Writes down back reference files, which include a thumbnail list
of examples using a certain module"""
example_file = os.path.join(target_dir, fname)
backrefs = scan_used_functions(example_file, gallery_conf)
for backref in backrefs:
include_path = os.path.join(gallery_conf['mod_example_dir'],
'%s.examples' % backref)
seen = backref in seen_backrefs
with open(include_path, 'a' if seen else 'w') as ex_file:
if not seen:
heading = '\n\nExamples using ``%s``' % backref
ex_file.write(heading + '\n')
ex_file.write('^' * len(heading) + '\n')
ex_file.write(_thumbnail_div(target_dir, fname, snippet,
is_backref=True))
seen_backrefs.add(backref)
+435
View File
@@ -0,0 +1,435 @@
# -*- coding: utf-8 -*-
# Author: Óscar Nájera
# License: 3-clause BSD
###############################################################################
# Documentation link resolver objects
from __future__ import print_function
import gzip
import os
import posixpath
import re
import shelve
import sys
# Try Python 2 first, otherwise load from Python 3
try:
import cPickle as pickle
import urllib2 as urllib
from urllib2 import HTTPError, URLError
except ImportError:
import pickle
import urllib.request
import urllib.error
import urllib.parse
from urllib.error import HTTPError, URLError
from io import StringIO
def _get_data(url):
"""Helper function to get data over http or from a local file"""
if url.startswith('http://'):
# Try Python 2, use Python 3 on exception
try:
resp = urllib.urlopen(url)
encoding = resp.headers.dict.get('content-encoding', 'plain')
except AttributeError:
resp = urllib.request.urlopen(url)
encoding = resp.headers.get('content-encoding', 'plain')
data = resp.read()
if encoding == 'plain':
pass
elif encoding == 'gzip':
data = StringIO(data)
data = gzip.GzipFile(fileobj=data).read()
else:
raise RuntimeError('unknown encoding')
else:
with open(url, 'r') as fid:
data = fid.read()
return data
def get_data(url, gallery_dir):
"""Persistent dictionary usage to retrieve the search indexes"""
# shelve keys need to be str in python 2
if sys.version_info[0] == 2 and isinstance(url, unicode):
url = url.encode('utf-8')
cached_file = os.path.join(gallery_dir, 'searchindex')
search_index = shelve.open(cached_file)
if url in search_index:
data = search_index[url]
else:
data = _get_data(url)
search_index[url] = data
search_index.close()
return data
def _select_block(str_in, start_tag, end_tag):
"""Select first block delimited by start_tag and end_tag"""
start_pos = str_in.find(start_tag)
if start_pos < 0:
raise ValueError('start_tag not found')
depth = 0
for pos in range(start_pos, len(str_in)):
if str_in[pos] == start_tag:
depth += 1
elif str_in[pos] == end_tag:
depth -= 1
if depth == 0:
break
sel = str_in[start_pos + 1:pos]
return sel
def _parse_dict_recursive(dict_str):
"""Parse a dictionary from the search index"""
dict_out = dict()
pos_last = 0
pos = dict_str.find(':')
while pos >= 0:
key = dict_str[pos_last:pos]
if dict_str[pos + 1] == '[':
# value is a list
pos_tmp = dict_str.find(']', pos + 1)
if pos_tmp < 0:
raise RuntimeError('error when parsing dict')
value = dict_str[pos + 2: pos_tmp].split(',')
# try to convert elements to int
for i in range(len(value)):
try:
value[i] = int(value[i])
except ValueError:
pass
elif dict_str[pos + 1] == '{':
# value is another dictionary
subdict_str = _select_block(dict_str[pos:], '{', '}')
value = _parse_dict_recursive(subdict_str)
pos_tmp = pos + len(subdict_str)
else:
raise ValueError('error when parsing dict: unknown elem')
key = key.strip('"')
if len(key) > 0:
dict_out[key] = value
pos_last = dict_str.find(',', pos_tmp)
if pos_last < 0:
break
pos_last += 1
pos = dict_str.find(':', pos_last)
return dict_out
def parse_sphinx_searchindex(searchindex):
"""Parse a Sphinx search index
Parameters
----------
searchindex : str
The Sphinx search index (contents of searchindex.js)
Returns
-------
filenames : list of str
The file names parsed from the search index.
objects : dict
The objects parsed from the search index.
"""
# Make sure searchindex uses UTF-8 encoding
if hasattr(searchindex, 'decode'):
searchindex = searchindex.decode('UTF-8')
# parse objects
query = 'objects:'
pos = searchindex.find(query)
if pos < 0:
raise ValueError('"objects:" not found in search index')
sel = _select_block(searchindex[pos:], '{', '}')
objects = _parse_dict_recursive(sel)
# parse filenames
query = 'filenames:'
pos = searchindex.find(query)
if pos < 0:
raise ValueError('"filenames:" not found in search index')
filenames = searchindex[pos + len(query) + 1:]
filenames = filenames[:filenames.find(']')]
filenames = [f.strip('"') for f in filenames.split(',')]
return filenames, objects
class SphinxDocLinkResolver(object):
""" Resolve documentation links using searchindex.js generated by Sphinx
Parameters
----------
doc_url : str
The base URL of the project website.
searchindex : str
Filename of searchindex, relative to doc_url.
extra_modules_test : list of str
List of extra module names to test.
relative : bool
Return relative links (only useful for links to documentation of this
package).
"""
def __init__(self, doc_url, gallery_dir, searchindex='searchindex.js',
extra_modules_test=None, relative=False):
self.doc_url = doc_url
self.gallery_dir = gallery_dir
self.relative = relative
self._link_cache = {}
self.extra_modules_test = extra_modules_test
self._page_cache = {}
if doc_url.startswith('http://'):
if relative:
raise ValueError('Relative links are only supported for local '
'URLs (doc_url cannot start with "http://)"')
searchindex_url = doc_url + '/' + searchindex
else:
searchindex_url = os.path.join(doc_url, searchindex)
# detect if we are using relative links on a Windows system
if os.name.lower() == 'nt' and not doc_url.startswith('http://'):
if not relative:
raise ValueError('You have to use relative=True for the local'
' package on a Windows system.')
self._is_windows = True
else:
self._is_windows = False
# download and initialize the search index
sindex = get_data(searchindex_url, gallery_dir)
filenames, objects = parse_sphinx_searchindex(sindex)
self._searchindex = dict(filenames=filenames, objects=objects)
def _get_link(self, cobj):
"""Get a valid link, False if not found"""
fname_idx = None
full_name = cobj['module_short'] + '.' + cobj['name']
if full_name in self._searchindex['objects']:
value = self._searchindex['objects'][full_name]
if isinstance(value, dict):
value = value[next(iter(value.keys()))]
fname_idx = value[0]
elif cobj['module_short'] in self._searchindex['objects']:
value = self._searchindex['objects'][cobj['module_short']]
if cobj['name'] in value.keys():
fname_idx = value[cobj['name']][0]
if fname_idx is not None:
fname = self._searchindex['filenames'][fname_idx] + '.html'
if self._is_windows:
fname = fname.replace('/', '\\')
link = os.path.join(self.doc_url, fname)
else:
link = posixpath.join(self.doc_url, fname)
if hasattr(link, 'decode'):
link = link.decode('utf-8', 'replace')
if link in self._page_cache:
html = self._page_cache[link]
else:
html = get_data(link, self.gallery_dir)
self._page_cache[link] = html
# test if cobj appears in page
comb_names = [cobj['module_short'] + '.' + cobj['name']]
if self.extra_modules_test is not None:
for mod in self.extra_modules_test:
comb_names.append(mod + '.' + cobj['name'])
url = False
if hasattr(html, 'decode'):
# Decode bytes under Python 3
html = html.decode('utf-8', 'replace')
for comb_name in comb_names:
if hasattr(comb_name, 'decode'):
# Decode bytes under Python 3
comb_name = comb_name.decode('utf-8', 'replace')
if comb_name in html:
url = link + u'#' + comb_name
link = url
else:
link = False
return link
def resolve(self, cobj, this_url):
"""Resolve the link to the documentation, returns None if not found
Parameters
----------
cobj : dict
Dict with information about the "code object" for which we are
resolving a link.
cobi['name'] : function or class name (str)
cobj['module_short'] : shortened module name (str)
cobj['module'] : module name (str)
this_url: str
URL of the current page. Needed to construct relative URLs
(only used if relative=True in constructor).
Returns
-------
link : str | None
The link (URL) to the documentation.
"""
full_name = cobj['module_short'] + '.' + cobj['name']
link = self._link_cache.get(full_name, None)
if link is None:
# we don't have it cached
link = self._get_link(cobj)
# cache it for the future
self._link_cache[full_name] = link
if link is False or link is None:
# failed to resolve
return None
if self.relative:
link = os.path.relpath(link, start=this_url)
if self._is_windows:
# replace '\' with '/' so it on the web
link = link.replace('\\', '/')
# for some reason, the relative link goes one directory too high up
link = link[3:]
return link
def _embed_code_links(app, gallery_conf, gallery_dir):
# Add resolvers for the packages for which we want to show links
working_dir = os.getcwd()
os.chdir(app.builder.srcdir)
doc_resolvers = {}
for this_module, url in gallery_conf['reference_url'].items():
try:
if url is None:
doc_resolvers[this_module] = SphinxDocLinkResolver(
app.builder.outdir,
gallery_dir,
relative=True)
else:
doc_resolvers[this_module] = SphinxDocLinkResolver(url,
gallery_dir)
except HTTPError as e:
print("The following HTTP Error has occurred:\n")
print(e.code)
except URLError as e:
print("\n...\n"
"Warning: Embedding the documentation hyperlinks requires "
"Internet access.\nPlease check your network connection.\n"
"Unable to continue embedding `{0}` links due to a URL "
"Error:\n".format(this_module))
print(e.args)
html_gallery_dir = os.path.abspath(os.path.join(app.builder.outdir,
gallery_dir))
# patterns for replacement
link_pattern = '<a href="%s">%s</a>'
orig_pattern = '<span class="n">%s</span>'
period = '<span class="o">.</span>'
for dirpath, _, filenames in os.walk(html_gallery_dir):
for fname in filenames:
print('\tprocessing: %s' % fname)
full_fname = os.path.join(html_gallery_dir, dirpath, fname)
subpath = dirpath[len(html_gallery_dir) + 1:]
pickle_fname = os.path.join(gallery_dir, subpath,
fname[:-5] + '_codeobj.pickle')
if os.path.exists(pickle_fname):
# we have a pickle file with the objects to embed links for
with open(pickle_fname, 'rb') as fid:
example_code_obj = pickle.load(fid)
fid.close()
str_repl = {}
# generate replacement strings with the links
for name, cobj in example_code_obj.items():
this_module = cobj['module'].split('.')[0]
if this_module not in doc_resolvers:
continue
try:
link = doc_resolvers[this_module].resolve(cobj,
full_fname)
except (HTTPError, URLError) as e:
print("The following error has occurred:\n")
print(repr(e))
continue
if link is not None:
parts = name.split('.')
name_html = period.join(orig_pattern % part
for part in parts)
str_repl[name_html] = link_pattern % (link, name_html)
# do the replacement in the html file
# ensure greediness
names = sorted(str_repl, key=len, reverse=True)
expr = re.compile(r'(?<!\.)\b' + # don't follow . or word
'|'.join(re.escape(name)
for name in names))
def substitute_link(match):
return str_repl[match.group()]
if len(str_repl) > 0:
with open(full_fname, 'rb') as fid:
lines_in = fid.readlines()
with open(full_fname, 'wb') as fid:
for line in lines_in:
line = line.decode('utf-8')
line = expr.sub(substitute_link, line)
fid.write(line.encode('utf-8'))
print('[done]')
os.chdir(working_dir)
def embed_code_links(app, exception):
"""Embed hyperlinks to documentation into example code"""
if exception is not None:
return
# No need to waste time embedding hyperlinks when not running the examples
# XXX: also at the time of writing this fixes make html-noplot
# for some reason I don't fully understand
if not app.builder.config.plot_gallery:
return
# XXX: Whitelist of builders for which it makes sense to embed
# hyperlinks inside the example html. Note that the link embedding
# require searchindex.js to exist for the links to the local doc
# and there does not seem to be a good way of knowing which
# builders creates a searchindex.js.
if app.builder.name not in ['html', 'readthedocs']:
return
print('Embedding documentation hyperlinks in examples..')
gallery_conf = app.config.sphinx_gallery_conf
gallery_dirs = gallery_conf['gallery_dirs']
if not isinstance(gallery_dirs, list):
gallery_dirs = [gallery_dirs]
for gallery_dir in gallery_dirs:
_embed_code_links(app, gallery_conf, gallery_dir)
+169
View File
@@ -0,0 +1,169 @@
# -*- coding: utf-8 -*-
# Author: Óscar Nájera
# License: 3-clause BSD
"""
========================
Sphinx-Gallery Generator
========================
Attaches Sphinx-Gallery to Sphinx in order to generate the galleries
when building the documentation.
"""
from __future__ import division, print_function, absolute_import
import re
import os
from . import glr_path_static
from .gen_rst import generate_dir_rst
from .docs_resolv import embed_code_links
def clean_gallery_out(build_dir):
"""Deletes images under the sphx_glr namespace in the build directory"""
# Sphinx hack: sphinx copies generated images to the build directory
# each time the docs are made. If the desired image name already
# exists, it appends a digit to prevent overwrites. The problem is,
# the directory is never cleared. This means that each time you build
# the docs, the number of images in the directory grows.
#
# This question has been asked on the sphinx development list, but there
# was no response: http://osdir.com/ml/sphinx-dev/2011-02/msg00123.html
#
# The following is a hack that prevents this behavior by clearing the
# image build directory from gallery images each time the docs are built.
# If sphinx changes their layout between versions, this will not
# work (though it should probably not cause a crash).
# Tested successfully on Sphinx 1.0.7
build_image_dir = os.path.join(build_dir, '_images')
if os.path.exists(build_image_dir):
filelist = os.listdir(build_image_dir)
for filename in filelist:
if filename.startswith('sphx_glr') and filename.endswith('png'):
os.remove(os.path.join(build_image_dir, filename))
def generate_gallery_rst(app):
"""Generate the Main examples gallery reStructuredText
Start the sphinx-gallery configuration and recursively scan the examples
directories in order to populate the examples gallery
"""
try:
plot_gallery = eval(app.builder.config.plot_gallery)
except TypeError:
plot_gallery = bool(app.builder.config.plot_gallery)
gallery_conf.update(app.config.sphinx_gallery_conf)
gallery_conf.update(plot_gallery=plot_gallery)
gallery_conf.update(abort_on_example_error=app.builder.config.abort_on_example_error)
# this assures I can call the config in other places
app.config.sphinx_gallery_conf = gallery_conf
app.config.html_static_path.append(glr_path_static())
clean_gallery_out(app.builder.outdir)
examples_dirs = gallery_conf['examples_dirs']
gallery_dirs = gallery_conf['gallery_dirs']
if not isinstance(examples_dirs, list):
examples_dirs = [examples_dirs]
if not isinstance(gallery_dirs, list):
gallery_dirs = [gallery_dirs]
# cd to the appropriate directory regardless of sphinx configuration
working_dir = os.getcwd()
os.chdir(app.builder.srcdir)
mod_examples_dir = os.path.relpath(gallery_conf['mod_example_dir'],
app.builder.srcdir)
seen_backrefs = set()
computation_times = []
for examples_dir, gallery_dir in zip(examples_dirs, gallery_dirs):
examples_dir = os.path.relpath(examples_dir,
app.builder.srcdir)
gallery_dir = os.path.relpath(gallery_dir,
app.builder.srcdir)
for workdir in [examples_dir, gallery_dir, mod_examples_dir]:
if not os.path.exists(workdir):
os.makedirs(workdir)
# we create an index.rst with all examples
fhindex = open(os.path.join(gallery_dir, 'index.rst'), 'w')
# Here we don't use an os.walk, but we recurse only twice: flat is
# better than nested.
this_fhindex, this_computation_times = \
generate_dir_rst(examples_dir, gallery_dir, gallery_conf,
seen_backrefs)
computation_times += this_computation_times
fhindex.write(this_fhindex)
for directory in sorted(os.listdir(examples_dir)):
if os.path.isdir(os.path.join(examples_dir, directory)):
src_dir = os.path.join(examples_dir, directory)
target_dir = os.path.join(gallery_dir, directory)
this_fhindex, this_computation_times = \
generate_dir_rst(src_dir, target_dir, gallery_conf,
seen_backrefs)
fhindex.write(this_fhindex)
computation_times += this_computation_times
fhindex.flush()
# Back to initial directory
os.chdir(working_dir)
print("Computation time summary:")
for time_elapsed, fname in sorted(computation_times)[::-1]:
if time_elapsed is not None:
print("\t- %s : %.2g sec" % (fname, time_elapsed))
else:
print("\t- %s : not run" % fname)
def touch_empty_backreferences(app, what, name, obj, options, lines):
"""Generate empty back-reference example files
This avoids inclusion errors/warnings if there are no gallery
examples for a class / module that is being parsed by autodoc"""
examples_path = os.path.join(app.srcdir,
app.config.sphinx_gallery_conf["mod_example_dir"],
"%s.examples" % name)
if not os.path.exists(examples_path):
# touch file
open(examples_path, 'w').close()
gallery_conf = {
'filename_pattern': re.escape(os.sep) + 'plot',
'examples_dirs': '../examples',
'gallery_dirs': 'auto_examples',
'mod_example_dir': os.path.join('modules', 'generated'),
'doc_module': (),
'reference_url': {},
}
def setup(app):
"""Setup sphinx-gallery sphinx extension"""
app.add_config_value('plot_gallery', True, 'html')
app.add_config_value('abort_on_example_error', False, 'html')
app.add_config_value('sphinx_gallery_conf', gallery_conf, 'html')
app.add_stylesheet('gallery.css')
if 'sphinx.ext.autodoc' in app._extensions:
app.connect('autodoc-process-docstring', touch_empty_backreferences)
app.connect('builder-inited', generate_gallery_rst)
app.connect('build-finished', embed_code_links)
def setup_module():
# HACK: Stop nosetests running setup() above
pass
+656
View File
@@ -0,0 +1,656 @@
# -*- coding: utf-8 -*-
# Author: Óscar Nájera
# License: 3-clause BSD
"""
==================
RST file generator
==================
Generate the rst files for the examples by iterating over the python
example files.
Files that generate images should start with 'plot'
"""
# Don't use unicode_literals here (be explicit with u"..." instead) otherwise
# tricky errors come up with exec(code_blocks, ...) calls
from __future__ import division, print_function, absolute_import
from time import time
import ast
import codecs
import hashlib
import os
import re
import shutil
import subprocess
import sys
import traceback
import warnings
# Try Python 2 first, otherwise load from Python 3
from textwrap import dedent
try:
# textwrap indent only exists in python 3
from textwrap import indent
except ImportError:
def indent(text, prefix, predicate=None):
"""Adds 'prefix' to the beginning of selected lines in 'text'.
If 'predicate' is provided, 'prefix' will only be added to the lines
where 'predicate(line)' is True. If 'predicate' is not provided,
it will default to adding 'prefix' to all non-empty lines that do not
consist solely of whitespace characters.
"""
if predicate is None:
def predicate(line):
return line.strip()
def prefixed_lines():
for line in text.splitlines(True):
yield (prefix + line if predicate(line) else line)
return ''.join(prefixed_lines())
from io import StringIO
try:
# make sure that the Agg backend is set before importing any
# matplotlib
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
except ImportError:
# this script can be imported by nosetest to find tests to run: we should
# not impose the matplotlib requirement in that case.
pass
from . import glr_path_static
from .backreferences import write_backreferences, _thumbnail_div
from .notebook import Notebook
try:
basestring
except NameError:
basestring = str
unicode = str
###############################################################################
class Tee(object):
"""A tee object to redirect streams to multiple outputs"""
def __init__(self, file1, file2):
self.file1 = file1
self.file2 = file2
def write(self, data):
self.file1.write(data)
self.file2.write(data)
def flush(self):
self.file1.flush()
self.file2.flush()
class MixedEncodingStringIO(StringIO):
"""Helper when both ASCII and unicode strings will be written"""
def write(self, data):
if not isinstance(data, unicode):
data = data.decode('utf-8')
StringIO.write(self, data)
###############################################################################
CODE_DOWNLOAD = """**Total running time of the script:**
({0:.0f} minutes {1:.3f} seconds)\n\n
\n.. container:: sphx-glr-download
**Download Python source code:** :download:`{2} <{2}>`\n
\n.. container:: sphx-glr-download
**Download IPython notebook:** :download:`{3} <{3}>`\n"""
# The following strings are used when we have several pictures: we use
# an html div tag that our CSS uses to turn the lists into horizontal
# lists.
HLIST_HEADER = """
.. rst-class:: sphx-glr-horizontal
"""
HLIST_IMAGE_TEMPLATE = """
*
.. image:: /%s
:scale: 47
"""
SINGLE_IMAGE = """
.. image:: /%s
:align: center
"""
# This one could contain unicode
CODE_OUTPUT = u""".. rst-class:: sphx-glr-script-out
Out::
{0}\n"""
def get_docstring_and_rest(filename):
"""Separate `filename` content between docstring and the rest
Strongly inspired from ast.get_docstring.
Returns
-------
docstring: str
docstring of `filename`
rest: str
`filename` content without the docstring
"""
# can't use codecs.open(filename, 'r', 'utf-8') here b/c ast doesn't
# seem to work with unicode strings in Python2.7
# "SyntaxError: encoding declaration in Unicode string"
with open(filename, 'rb') as f:
content = f.read()
node = ast.parse(content)
if not isinstance(node, ast.Module):
raise TypeError("This function only supports modules. "
"You provided {0}".format(node.__class__.__name__))
if node.body and isinstance(node.body[0], ast.Expr) and \
isinstance(node.body[0].value, ast.Str):
docstring_node = node.body[0]
docstring = docstring_node.value.s
if hasattr(docstring, 'decode'): # python2.7
docstring = docstring.decode('utf-8')
# This get the content of the file after the docstring last line
# Note: 'maxsplit' argument is not a keyword argument in python2
rest = content.decode('utf-8').split('\n', docstring_node.lineno)[-1]
return docstring, rest
else:
raise ValueError(('Could not find docstring in file "{0}". '
'A docstring is required by sphinx-gallery')
.format(filename))
def split_code_and_text_blocks(source_file):
"""Return list with source file separated into code and text blocks.
Returns
-------
blocks : list of (label, content)
List where each element is a tuple with the label ('text' or 'code'),
and content string of block.
"""
docstring, rest_of_content = get_docstring_and_rest(source_file)
blocks = [('text', docstring)]
pattern = re.compile(
r'(?P<header_line>^#{20,}.*)\s(?P<text_content>(?:^#.*\s)*)',
flags=re.M)
pos_so_far = 0
for match in re.finditer(pattern, rest_of_content):
match_start_pos, match_end_pos = match.span()
code_block_content = rest_of_content[pos_so_far:match_start_pos]
text_content = match.group('text_content')
sub_pat = re.compile('^#', flags=re.M)
text_block_content = dedent(re.sub(sub_pat, '', text_content)).lstrip()
if code_block_content.strip():
blocks.append(('code', code_block_content))
if text_block_content.strip():
blocks.append(('text', text_block_content))
pos_so_far = match_end_pos
remaining_content = rest_of_content[pos_so_far:]
if remaining_content.strip():
blocks.append(('code', remaining_content))
return blocks
def codestr2rst(codestr, lang='python'):
"""Return reStructuredText code block from code string"""
code_directive = "\n.. code-block:: {0}\n\n".format(lang)
indented_block = indent(codestr, ' ' * 4)
return code_directive + indented_block
def text2string(content):
"""Returns a string without the extra triple quotes"""
try:
return ast.literal_eval(content) + '\n'
except Exception:
return content + '\n'
def extract_thumbnail_number(text):
""" Pull out the thumbnail image number specified in the docstring. """
# check whether the user has specified a specific thumbnail image
pattr = re.compile("^\s*#\s*sphinx_gallery_thumbnail_number\s*=\s*([0-9]+)\s*$", flags=re.MULTILINE)
match = pattr.search(text)
if match is None:
# by default, use the first figure created
thumbnail_number = 1
else:
thumbnail_number = int(match.groups()[0])
return thumbnail_number
def extract_intro(filename):
""" Extract the first paragraph of module-level docstring. max:95 char"""
docstring, _ = get_docstring_and_rest(filename)
# lstrip is just in case docstring has a '\n\n' at the beginning
paragraphs = docstring.lstrip().split('\n\n')
if len(paragraphs) > 1:
first_paragraph = re.sub('\n', ' ', paragraphs[1])
first_paragraph = (first_paragraph[:95] + '...'
if len(first_paragraph) > 95 else first_paragraph)
else:
raise ValueError(
"Example docstring should have a header for the example title "
"and at least a paragraph explaining what the example is about. "
"Please check the example file:\n {}\n".format(filename))
return first_paragraph
def get_md5sum(src_file):
"""Returns md5sum of file"""
with open(src_file, 'r') as src_data:
src_content = src_data.read()
# data needs to be encoded in python3 before hashing
if sys.version_info[0] == 3:
src_content = src_content.encode('utf-8')
src_md5 = hashlib.md5(src_content).hexdigest()
return src_md5
def check_md5sum_change(src_file):
"""Returns True if src_file has a different md5sum"""
src_md5 = get_md5sum(src_file)
src_md5_file = src_file + '.md5'
src_file_changed = True
if os.path.exists(src_md5_file):
with open(src_md5_file, 'r') as file_checksum:
ref_md5 = file_checksum.read()
if src_md5 == ref_md5:
src_file_changed = False
if src_file_changed:
with open(src_md5_file, 'w') as file_checksum:
file_checksum.write(src_md5)
return src_file_changed
def _plots_are_current(src_file, image_path):
"""Test existence of image file and no change in md5sum of
example"""
has_image = os.path.exists(image_path)
src_file_changed = check_md5sum_change(src_file)
return has_image and not src_file_changed
def save_figures(image_path, fig_count, gallery_conf):
"""Save all open matplotlib figures of the example code-block
Parameters
----------
image_path : str
Path where plots are saved (format string which accepts figure number)
fig_count : int
Previous figure number count. Figure number add from this number
Returns
-------
list of strings containing the full path to each figure
"""
figure_list = []
fig_managers = matplotlib._pylab_helpers.Gcf.get_all_fig_managers()
for fig_mngr in fig_managers:
# Set the fig_num figure as the current figure as we can't
# save a figure that's not the current figure.
fig = plt.figure(fig_mngr.num)
kwargs = {}
to_rgba = matplotlib.colors.colorConverter.to_rgba
for attr in ['facecolor', 'edgecolor']:
fig_attr = getattr(fig, 'get_' + attr)()
default_attr = matplotlib.rcParams['figure.' + attr]
if to_rgba(fig_attr) != to_rgba(default_attr):
kwargs[attr] = fig_attr
current_fig = image_path.format(fig_count + fig_mngr.num)
fig.savefig(current_fig, **kwargs)
figure_list.append(current_fig)
if gallery_conf.get('find_mayavi_figures', False):
from mayavi import mlab
e = mlab.get_engine()
last_matplotlib_fig_num = fig_count + len(figure_list)
total_fig_num = last_matplotlib_fig_num + len(e.scenes)
mayavi_fig_nums = range(last_matplotlib_fig_num + 1, total_fig_num + 1)
for scene, mayavi_fig_num in zip(e.scenes, mayavi_fig_nums):
current_fig = image_path.format(mayavi_fig_num)
mlab.savefig(current_fig, figure=scene)
# make sure the image is not too large
scale_image(current_fig, current_fig, 850, 999)
figure_list.append(current_fig)
mlab.close(all=True)
return figure_list
def scale_image(in_fname, out_fname, max_width, max_height):
"""Scales an image with the same aspect ratio centered in an
image with a given max_width and max_height
if in_fname == out_fname the image can only be scaled down
"""
# local import to avoid testing dependency on PIL:
try:
from PIL import Image
except ImportError:
import Image
img = Image.open(in_fname)
width_in, height_in = img.size
scale_w = max_width / float(width_in)
scale_h = max_height / float(height_in)
if height_in * scale_w <= max_height:
scale = scale_w
else:
scale = scale_h
if scale >= 1.0 and in_fname == out_fname:
return
width_sc = int(round(scale * width_in))
height_sc = int(round(scale * height_in))
# resize the image
img.thumbnail((width_sc, height_sc), Image.ANTIALIAS)
# insert centered
thumb = Image.new('RGB', (max_width, max_height), (255, 255, 255))
pos_insert = ((max_width - width_sc) // 2, (max_height - height_sc) // 2)
thumb.paste(img, pos_insert)
thumb.save(out_fname)
# Use optipng to perform lossless compression on the resized image if
# software is installed
if os.environ.get('SKLEARN_DOC_OPTIPNG', False):
try:
subprocess.call(["optipng", "-quiet", "-o", "9", out_fname])
except Exception:
warnings.warn('Install optipng to reduce the size of the \
generated images')
def save_thumbnail(thumbnail_image_path, base_image_name, gallery_conf):
"""Save the thumbnail image"""
thumb_dir = os.path.join(os.path.dirname(thumbnail_image_path), 'thumb')
if not os.path.exists(thumb_dir):
os.makedirs(thumb_dir)
thumb_file = os.path.join(thumb_dir,
'sphx_glr_%s_thumb.png' % base_image_name)
if os.path.exists(thumbnail_image_path):
scale_image(thumbnail_image_path, thumb_file, 400, 280)
elif not os.path.exists(thumb_file):
# create something to replace the thumbnail
default_thumb_file = os.path.join(glr_path_static(), 'no_image.png')
default_thumb_file = gallery_conf.get("default_thumb_file",
default_thumb_file)
scale_image(default_thumb_file, thumb_file, 200, 140)
def generate_dir_rst(src_dir, target_dir, gallery_conf, seen_backrefs):
"""Generate the gallery reStructuredText for an example directory"""
if not os.path.exists(os.path.join(src_dir, 'README.txt')):
print(80 * '_')
print('Example directory %s does not have a README.txt file' %
src_dir)
print('Skipping this directory')
print(80 * '_')
return "", [] # because string is an expected return type
fhindex = open(os.path.join(src_dir, 'README.txt')).read()
if not os.path.exists(target_dir):
os.makedirs(target_dir)
sorted_listdir = [fname for fname in sorted(os.listdir(src_dir))
if fname.endswith('.py')]
entries_text = []
computation_times = []
for fname in sorted_listdir:
amount_of_code, time_elapsed = \
generate_file_rst(fname, target_dir, src_dir, gallery_conf)
computation_times.append((time_elapsed, fname))
new_fname = os.path.join(src_dir, fname)
intro = extract_intro(new_fname)
write_backreferences(seen_backrefs, gallery_conf,
target_dir, fname, intro)
this_entry = _thumbnail_div(target_dir, fname, intro) + """
.. toctree::
:hidden:
/%s/%s\n""" % (target_dir, fname[:-3])
entries_text.append((amount_of_code, this_entry))
# sort to have the smallest entries in the beginning
entries_text.sort()
for _, entry_text in entries_text:
fhindex += entry_text
# clear at the end of the section
fhindex += """.. raw:: html\n
<div style='clear:both'></div>\n\n"""
return fhindex, computation_times
def execute_script(code_block, example_globals, image_path, fig_count,
src_file, gallery_conf):
"""Executes the code block of the example file"""
time_elapsed = 0
stdout = ''
# We need to execute the code
print('plotting code blocks in %s' % src_file)
plt.close('all')
cwd = os.getcwd()
# Redirect output to stdout and
orig_stdout = sys.stdout
try:
# First cd in the original example dir, so that any file
# created by the example get created in this directory
os.chdir(os.path.dirname(src_file))
my_buffer = MixedEncodingStringIO()
my_stdout = Tee(sys.stdout, my_buffer)
sys.stdout = my_stdout
t_start = time()
# don't use unicode_literals at the top of this file or you get
# nasty errors here on Py2.7
exec(code_block, example_globals)
time_elapsed = time() - t_start
sys.stdout = orig_stdout
my_stdout = my_buffer.getvalue().strip().expandtabs()
# raise RuntimeError
if my_stdout:
stdout = CODE_OUTPUT.format(indent(my_stdout, u' ' * 4))
os.chdir(cwd)
figure_list = save_figures(image_path, fig_count, gallery_conf)
# Depending on whether we have one or more figures, we're using a
# horizontal list or a single rst call to 'image'.
image_list = ""
if len(figure_list) == 1:
figure_name = figure_list[0]
image_list = SINGLE_IMAGE % figure_name.lstrip('/')
elif len(figure_list) > 1:
image_list = HLIST_HEADER
for figure_name in figure_list:
image_list += HLIST_IMAGE_TEMPLATE % figure_name.lstrip('/')
except Exception:
formatted_exception = traceback.format_exc()
sys.stdout = orig_stdout # need this here so these lines don't bomb
print(80 * '_')
print('%s is not compiling:' % src_file)
print(formatted_exception)
print(80 * '_')
figure_list = []
image_list = codestr2rst(formatted_exception, lang='pytb')
# Overrides the output thumbnail in the gallery for easy identification
broken_img = os.path.join(glr_path_static(), 'broken_example.png')
shutil.copyfile(broken_img, os.path.join(cwd, image_path.format(1)))
fig_count += 1 # raise count to avoid overwriting image
# Breaks build on first example error
# XXX This check can break during testing e.g. if you uncomment the
# `raise RuntimeError` by the `my_stdout` call, maybe use `.get()`?
if gallery_conf['abort_on_example_error']:
raise
finally:
os.chdir(cwd)
sys.stdout = orig_stdout
print(" - time elapsed : %.2g sec" % time_elapsed)
code_output = u"\n{0}\n\n{1}\n\n".format(image_list, stdout)
return code_output, time_elapsed, fig_count + len(figure_list)
def generate_file_rst(fname, target_dir, src_dir, gallery_conf):
""" Generate the rst file for a given example.
Returns the amout of code (in characters) of the corresponding
files.
"""
src_file = os.path.join(src_dir, fname)
example_file = os.path.join(target_dir, fname)
shutil.copyfile(src_file, example_file)
image_dir = os.path.join(target_dir, 'images')
if not os.path.exists(image_dir):
os.makedirs(image_dir)
base_image_name = os.path.splitext(fname)[0]
image_fname = 'sphx_glr_' + base_image_name + '_{0:03}.png'
image_path_template = os.path.join(image_dir, image_fname)
script_blocks = split_code_and_text_blocks(example_file)
# read specification of the figure to display as thumbnail from main text
_, content = get_docstring_and_rest(example_file)
thumbnail_number = extract_thumbnail_number(content)
amount_of_code = sum([len(bcontent)
for blabel, bcontent in script_blocks
if blabel == 'code'])
first_image_path = image_path_template.format(1)
if _plots_are_current(example_file, first_image_path):
return amount_of_code, 0
time_elapsed = 0
ref_fname = example_file.replace(os.path.sep, '_')
example_rst = """\n\n.. _sphx_glr_{0}:\n\n""".format(ref_fname)
example_nb = Notebook(fname, target_dir)
filename_pattern = gallery_conf.get('filename_pattern')
if re.search(filename_pattern, src_file) and gallery_conf['plot_gallery']:
example_globals = {
# A lot of examples contains 'print(__doc__)' for example in
# scikit-learn so that running the example prints some useful
# information. Because the docstring has been separated from
# the code blocks in sphinx-gallery, __doc__ is actually
# __builtin__.__doc__ in the execution context and we do not
# want to print it
'__doc__': '',
# Examples may contain if __name__ == '__main__' guards
# for in example scikit-learn if the example uses multiprocessing
'__name__': '__main__'}
fig_count = 0
# A simple example has two blocks: one for the
# example introduction/explanation and one for the code
is_example_notebook_like = len(script_blocks) > 2
for blabel, bcontent in script_blocks:
if blabel == 'code':
code_output, rtime, fig_count = execute_script(bcontent,
example_globals,
image_path_template,
fig_count,
src_file,
gallery_conf)
time_elapsed += rtime
example_nb.add_code_cell(bcontent)
if is_example_notebook_like:
example_rst += codestr2rst(bcontent) + '\n'
example_rst += code_output
else:
example_rst += code_output
if 'sphx-glr-script-out' in code_output:
# Add some vertical space after output
example_rst += "\n\n|\n\n"
example_rst += codestr2rst(bcontent) + '\n'
else:
example_rst += text2string(bcontent) + '\n'
example_nb.add_markdown_cell(text2string(bcontent))
else:
for blabel, bcontent in script_blocks:
if blabel == 'code':
example_rst += codestr2rst(bcontent) + '\n'
example_nb.add_code_cell(bcontent)
else:
example_rst += bcontent + '\n'
example_nb.add_markdown_cell(text2string(bcontent))
thumbnail_image_path = image_path_template.format(thumbnail_number)
save_thumbnail(thumbnail_image_path, base_image_name, gallery_conf)
time_m, time_s = divmod(time_elapsed, 60)
example_nb.save_file()
with codecs.open(os.path.join(target_dir, base_image_name + '.rst'),
mode='w', encoding='utf-8') as f:
example_rst += CODE_DOWNLOAD.format(time_m, time_s, fname,
example_nb.file_name)
f.write(example_rst)
return amount_of_code, time_elapsed
+123
View File
@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*-
r"""
============================
Parser for Jupyter notebooks
============================
Class that holds the Jupyter notebook information
"""
# Author: Óscar Nájera
# License: 3-clause BSD
from __future__ import division, absolute_import, print_function
import json
import os
import re
import sys
def ipy_notebook_skeleton():
"""Returns a dictionary with the elements of a Jupyter notebook"""
py_version = sys.version_info
notebook_skeleton = {
"cells": [],
"metadata": {
"kernelspec": {
"display_name": "Python " + str(py_version[0]),
"language": "python",
"name": "python" + str(py_version[0])
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": py_version[0]
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython" + str(py_version[0]),
"version": '{0}.{1}.{2}'.format(*sys.version_info[:3])
}
},
"nbformat": 4,
"nbformat_minor": 0
}
return notebook_skeleton
def rst2md(text):
"""Converts the RST text from the examples docstrigs and comments
into markdown text for the Jupyter notebooks"""
top_heading = re.compile(r'^=+$\s^([\w\s-]+)^=+$', flags=re.M)
text = re.sub(top_heading, r'# \1', text)
math_eq = re.compile(r'^\.\. math::((?:.+)?(?:\n+^ .+)*)', flags=re.M)
text = re.sub(math_eq,
lambda match: r'$${0}$$'.format(match.group(1).strip()),
text)
inline_math = re.compile(r':math:`(.+)`')
text = re.sub(inline_math, r'$\1$', text)
return text
class Notebook(object):
"""Jupyter notebook object
Constructs the file cell-by-cell and writes it at the end"""
def __init__(self, file_name, target_dir):
"""Declare the skeleton of the notebook
Parameters
----------
file_name : str
original script file name, .py extension will be renamed
target_dir: str
directory where notebook file is to be saved
"""
self.file_name = file_name.replace('.py', '.ipynb')
self.write_file = os.path.join(target_dir, self.file_name)
self.work_notebook = ipy_notebook_skeleton()
self.add_code_cell("%matplotlib inline")
def add_code_cell(self, code):
"""Add a code cell to the notebook
Parameters
----------
code : str
Cell content
"""
code_cell = {
"cell_type": "code",
"execution_count": None,
"metadata": {"collapsed": False},
"outputs": [],
"source": [code.strip()]
}
self.work_notebook["cells"].append(code_cell)
def add_markdown_cell(self, text):
"""Add a markdown cell to the notebook
Parameters
----------
code : str
Cell content
"""
markdown_cell = {
"cell_type": "markdown",
"metadata": {},
"source": [rst2md(text)]
}
self.work_notebook["cells"].append(markdown_cell)
def save_file(self):
"""Saves the notebook to a file"""
with open(self.write_file, 'w') as out_nb:
json.dump(self.work_notebook, out_nb, indent=2)
+24 -2
View File
@@ -31,11 +31,31 @@ extensions = ['sphinx.ext.autodoc',
'sphinx.ext.imgmath',
'numpydoc',
'sphinx.ext.autosummary',
'plot2rst',
'sphinx.ext.intersphinx',
'sphinx.ext.linkcode',
'sphinx_gallery.gen_gallery'
]
autosummary_generate = True
#------------------------------------------------------------------------
# Sphinx-gallery configuration
#------------------------------------------------------------------------
sphinx_gallery_conf = {
'doc_module' : 'skimage',
# path to your examples scripts
'examples_dirs' : '../examples',
# path where to save gallery generated examples
'gallery_dirs' : 'auto_examples',
'mod_example_dir': 'api',
'reference_url' : {
'skimage': None,
'matplotlib': 'http://matplotlib.org',
'numpy': 'http://docs.scipy.org/doc/numpy-1.6.0',
'scipy': 'http://docs.scipy.org/doc/scipy-0.11.0/reference',}
}
# Determine if the matplotlib has a recent enough version of the
# plot_directive, otherwise use the local fork.
try:
@@ -57,7 +77,7 @@ else:
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.txt'
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
@@ -366,3 +386,5 @@ def linkcode_resolve(domain, info):
else:
return ("http://github.com/scikit-image/scikit-image/blob/"
"v%s/skimage/%s%s" % (skimage.__version__, fn, linespec))
@@ -6,9 +6,9 @@
Release notes
=============
.. include:: ../release/_release_notes_for_docs.txt
.. include:: ../release/_release_notes_for_docs.rst
Installation
============
.. include:: install.txt
.. include:: install.rst
@@ -81,7 +81,7 @@ disk: ::
... (nrows / 2)**2)
>>> camera[outer_disk_mask] = 0
.. image:: ../auto_examples/numpy_operations/images/plot_camera_numpy_1.png
.. image:: ../auto_examples/numpy_operations/images/sphx_glr_plot_camera_numpy_001.png
:width: 45%
:target: ../auto_examples/numpy_operations/plot_camera_numpy.html
@@ -78,7 +78,7 @@ using an array of labels to encode the regions to be represented with the
same color.
.. image: ../auto_examples/segmentation/images/plot_join_segmentations_1.png
.. image:: ../auto_examples/segmentation/images/sphx_glr_plot_join_segmentations_001.png
:target: ../auto_examples/segmentation/plot_join_segmentations.html
:align: center
:width: 80%
@@ -87,9 +87,9 @@ same color.
.. topic:: Examples:
* :ref:`example_color_exposure_plot_tinting_grayscale_images.py`
* :ref:`example_segmentation_plot_join_segmentations.py`
* :ref:`example_segmentation_plot_rag_mean_color.py`
* :ref:`sphx_glr_auto_examples_color_exposure_plot_tinting_grayscale_images.py`
* :ref:`sphx_glr_auto_examples_segmentation_plot_join_segmentations.py`
* :ref:`sphx_glr_auto_examples_segmentation_plot_rag_mean_color.py`
Contrast and exposure
@@ -157,9 +157,9 @@ details are enhanced in large regions with poor contrast. As a further
refinement, histogram equalization can be performed in subregions of the
image with :func:`equalize_adapthist`, in order to correct for exposure
gradients across the image. See the example
:ref:`example_color_exposure_plot_equalize.py`.
:ref:`sphx_glr_auto_examples_color_exposure_plot_equalize.py`.
.. image:: ../auto_examples/color_exposure/images/plot_equalize_1.png
.. image:: ../auto_examples/color_exposure/images/sphx_glr_plot_equalize_001.png
:target: ../auto_examples/color_exposure/plot_equalize.html
:align: center
:width: 90%
@@ -167,6 +167,6 @@ gradients across the image. See the example
.. topic:: Examples:
* :ref:`example_color_exposure_plot_equalize.py`
* :ref:`sphx_glr_auto_examples_color_exposure_plot_equalize.py`
@@ -11,7 +11,7 @@ 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:: ../auto_examples/xx_applications/images/plot_coins_segmentation_1.png
.. image:: ../auto_examples/xx_applications/images/sphx_glr_plot_coins_segmentation_001.png
:target: ../auto_examples/xx_applications/plot_coins_segmentation.html
:align: center
@@ -26,7 +26,7 @@ Simply thresholding the image leads either to missing significant parts
of the coins, or to merging parts of the background with the
coins. This is due to the inhomogeneous lighting of the image.
.. image:: ../auto_examples/xx_applications/images/plot_coins_segmentation_2.png
.. image:: ../auto_examples/xx_applications/images/sphx_glr_plot_coins_segmentation_002.png
:target: ../auto_examples/xx_applications/plot_coins_segmentation.html
:align: center
@@ -53,7 +53,7 @@ boundary of the coins, or inside the coins.
>>> from scipy import ndimage as ndi
>>> fill_coins = ndi.binary_fill_holes(edges)
.. image:: ../auto_examples/xx_applications/images/plot_coins_segmentation_3.png
.. image:: ../auto_examples/xx_applications/images/sphx_glr_plot_coins_segmentation_003.png
:target: ../auto_examples/xx_applications/plot_coins_segmentation.html
:align: center
@@ -62,7 +62,7 @@ we fill the inner part of the coins using the
``ndi.binary_fill_holes`` function, which uses mathematical morphology
to fill the holes.
.. image:: ../auto_examples/xx_applications/images/plot_coins_segmentation_4.png
.. image:: ../auto_examples/xx_applications/images/sphx_glr_plot_coins_segmentation_004.png
:target: ../auto_examples/xx_applications/plot_coins_segmentation.html
:align: center
@@ -83,7 +83,7 @@ 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.
.. image:: ../auto_examples/xx_applications/images/plot_coins_segmentation_5.png
.. image:: ../auto_examples/xx_applications/images/sphx_glr_plot_coins_segmentation_005.png
:target: ../auto_examples/xx_applications/plot_coins_segmentation.html
:align: center
@@ -128,7 +128,7 @@ separate the coins from the background.
and here is the corresponding 2-D plot:
.. image:: ../auto_examples/xx_applications/images/plot_coins_segmentation_6.png
.. image:: ../auto_examples/xx_applications/images/sphx_glr_plot_coins_segmentation_006.png
:target: ../auto_examples/xx_applications/plot_coins_segmentation.html
:align: center
@@ -139,7 +139,7 @@ extreme parts of the histogram of grey values::
>>> markers[coins < 30] = 1
>>> markers[coins > 150] = 2
.. image:: ../auto_examples/xx_applications/images/plot_coins_segmentation_7.png
.. image:: ../auto_examples/xx_applications/images/sphx_glr_plot_coins_segmentation_007.png
:target: ../auto_examples/xx_applications/plot_coins_segmentation.html
:align: center
@@ -148,7 +148,7 @@ Let us now compute the watershed transform::
>>> from skimage.morphology import watershed
>>> segmentation = watershed(elevation_map, markers)
.. image:: ../auto_examples/xx_applications/images/plot_coins_segmentation_8.png
.. image:: ../auto_examples/xx_applications/images/sphx_glr_plot_coins_segmentation_008.png
:target: ../auto_examples/xx_applications/plot_coins_segmentation.html
:align: center
@@ -165,7 +165,7 @@ We can now label all the coins one by one using ``ndi.label``::
>>> labeled_coins, _ = ndi.label(segmentation)
.. image:: ../auto_examples/xx_applications/images/plot_coins_segmentation_9.png
.. image:: ../auto_examples/xx_applications/images/sphx_glr_plot_coins_segmentation_009.png
:target: ../auto_examples/xx_applications/plot_coins_segmentation.html
:align: center
+1 -1
View File
@@ -36,7 +36,7 @@ class ApiDocWriter(object):
def __init__(self,
package_name,
rst_extension='.txt',
rst_extension='.rst',
package_skip_patterns=None,
module_skip_patterns=None,
):