mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-27 18:25:32 +08:00
Merge pull request #2078 from emmanuelle/sphinx-gallery
Sphinx gallery for example gallery
This commit is contained in:
+2
-2
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>`_.
|
||||
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
Executable → Regular
+24
-32
@@ -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
|
||||
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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 |
@@ -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 |
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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))
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+7
-7
@@ -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`
|
||||
|
||||
|
||||
+9
-9
@@ -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
@@ -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,
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user