diff --git a/doc/Makefile b/doc/Makefile index 63523365..7535afb6 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -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 diff --git a/doc/examples/README.txt b/doc/examples/README.txt index ae692ec4..82328224 100644 --- a/doc/examples/README.txt +++ b/doc/examples/README.txt @@ -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. + diff --git a/doc/examples/color_exposure/plot_adapt_rgb.py b/doc/examples/color_exposure/plot_adapt_rgb.py index 64b2d00b..1cf3e10c 100644 --- a/doc/examples/color_exposure/plot_adapt_rgb.py +++ b/doc/examples/color_exposure/plot_adapt_rgb.py @@ -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. diff --git a/doc/examples/color_exposure/plot_equalize.py b/doc/examples/color_exposure/plot_equalize.py index 079b8b6d..4b520897 100644 --- a/doc/examples/color_exposure/plot_equalize.py +++ b/doc/examples/color_exposure/plot_equalize.py @@ -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') diff --git a/doc/examples/color_exposure/plot_ihc_color_separation.py b/doc/examples/color_exposure/plot_ihc_color_separation.py index 2db2b552..b8f579d6 100644 --- a/doc/examples/color_exposure/plot_ihc_color_separation.py +++ b/doc/examples/color_exposure/plot_ihc_color_separation.py @@ -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 -""" diff --git a/doc/examples/color_exposure/plot_regional_maxima.py b/doc/examples/color_exposure/plot_regional_maxima.py index a2f9c570..f28ecd67 100644 --- a/doc/examples/color_exposure/plot_regional_maxima.py +++ b/doc/examples/color_exposure/plot_regional_maxima.py @@ -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. diff --git a/doc/examples/color_exposure/plot_tinting_grayscale_images.py b/doc/examples/color_exposure/plot_tinting_grayscale_images.py index 2ba54396..236cc60d 100644 --- a/doc/examples/color_exposure/plot_tinting_grayscale_images.py +++ b/doc/examples/color_exposure/plot_tinting_grayscale_images.py @@ -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 -`_. - -""" +###################################################################### +# For coloring multiple regions, you may also be interested in +# `skimage.color.label2rgb `_. diff --git a/doc/examples/edges/plot_active_contours.py b/doc/examples/edges/plot_active_contours.py index 50d6fe48..6d74bed8 100644 --- a/doc/examples/edges/plot_active_contours.py +++ b/doc/examples/edges/plot_active_contours.py @@ -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 -""" diff --git a/doc/examples/edges/plot_circular_elliptical_hough_transform.py b/doc/examples/edges/plot_circular_elliptical_hough_transform.py old mode 100755 new mode 100644 index 389a1afa..65d77c82 --- a/doc/examples/edges/plot_circular_elliptical_hough_transform.py +++ b/doc/examples/edges/plot_circular_elliptical_hough_transform.py @@ -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 - -""" diff --git a/doc/examples/edges/plot_edge_filter.py b/doc/examples/edges/plot_edge_filter.py index 0770ed48..6a9d724f 100644 --- a/doc/examples/edges/plot_edge_filter.py +++ b/doc/examples/edges/plot_edge_filter.py @@ -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 -""" diff --git a/doc/examples/edges/plot_line_hough_transform.py b/doc/examples/edges/plot_line_hough_transform.py index c4d7cf95..06f23203 100644 --- a/doc/examples/edges/plot_line_hough_transform.py +++ b/doc/examples/edges/plot_line_hough_transform.py @@ -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 diff --git a/doc/examples/features_detection/plot_gabors_from_astronaut.py b/doc/examples/features_detection/plot_gabors_from_astronaut.py index a8cb50c2..8523d225 100644 --- a/doc/examples/features_detection/plot_gabors_from_astronaut.py +++ b/doc/examples/features_detection/plot_gabors_from_astronaut.py @@ -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? diff --git a/doc/examples/features_detection/plot_holes_and_peaks.py b/doc/examples/features_detection/plot_holes_and_peaks.py index 23c119e8..53a75f6f 100644 --- a/doc/examples/features_detection/plot_holes_and_peaks.py +++ b/doc/examples/features_detection/plot_holes_and_peaks.py @@ -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 -""" diff --git a/doc/examples/features_detection/plot_local_binary_pattern.py b/doc/examples/features_detection/plot_local_binary_pattern.py index f35c9f50..0b53ef99 100644 --- a/doc/examples/features_detection/plot_local_binary_pattern.py +++ b/doc/examples/features_detection/plot_local_binary_pattern.py @@ -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() diff --git a/doc/examples/features_detection/plot_multiblock_local_binary_pattern.py b/doc/examples/features_detection/plot_multiblock_local_binary_pattern.py index 46b8b3ac..acf890c1 100644 --- a/doc/examples/features_detection/plot_multiblock_local_binary_pattern.py +++ b/doc/examples/features_detection/plot_multiblock_local_binary_pattern.py @@ -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. diff --git a/doc/examples/features_detection/plot_windowed_histogram.py b/doc/examples/features_detection/plot_windowed_histogram.py index 65e67706..09de5ce0 100644 --- a/doc/examples/features_detection/plot_windowed_histogram.py +++ b/doc/examples/features_detection/plot_windowed_histogram.py @@ -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 diff --git a/doc/examples/filters/plot_phase_unwrap.py b/doc/examples/filters/plot_phase_unwrap.py index 73298b10..aba03ff6 100644 --- a/doc/examples/filters/plot_phase_unwrap.py +++ b/doc/examples/filters/plot_phase_unwrap.py @@ -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 diff --git a/doc/examples/transform/plot_radon_transform.py b/doc/examples/transform/plot_radon_transform.py index 541abf99..2613d9c9 100644 --- a/doc/examples/transform/plot_radon_transform.py +++ b/doc/examples/transform/plot_radon_transform.py @@ -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) diff --git a/doc/examples/transform/plot_seam_carving.py b/doc/examples/transform/plot_seam_carving.py index 129b0da0..38e2e23d 100644 --- a/doc/examples/transform/plot_seam_carving.py +++ b/doc/examples/transform/plot_seam_carving.py @@ -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 -""" +###################################################################### diff --git a/doc/examples/xx_applications/plot_coins_segmentation.py b/doc/examples/xx_applications/plot_coins_segmentation.py index f0710012..a543f1f4 100644 --- a/doc/examples/xx_applications/plot_coins_segmentation.py +++ b/doc/examples/xx_applications/plot_coins_segmentation.py @@ -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() diff --git a/doc/examples/xx_applications/plot_geometric.py b/doc/examples/xx_applications/plot_geometric.py index 86dd5aaa..84cab5a5 100644 --- a/doc/examples/xx_applications/plot_geometric.py +++ b/doc/examples/xx_applications/plot_geometric.py @@ -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() diff --git a/doc/examples/xx_applications/plot_morphology.py b/doc/examples/xx_applications/plot_morphology.py index 35bf79e9..6c94606f 100644 --- a/doc/examples/xx_applications/plot_morphology.py +++ b/doc/examples/xx_applications/plot_morphology.py @@ -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 -`_ -2. `Auckland university's tutorial on Morphological Image Processing -`_ -3. http://en.wikipedia.org/wiki/Mathematical_morphology - -""" - -plt.show() +###################################################################### +# +# Additional Resources +# ==================== +# +# 1. `MathWorks tutorial on morphological processing +# `_ +# +# 2. `Auckland university's tutorial on Morphological Image +# Processing `_ +# +# 3. http://en.wikipedia.org/wiki/Mathematical_morphology diff --git a/doc/examples/xx_applications/plot_rank_filters.py b/doc/examples/xx_applications/plot_rank_filters.py index 8ff02be1..3d7fa692 100644 --- a/doc/examples/xx_applications/plot_rank_filters.py +++ b/doc/examples/xx_applications/plot_rank_filters.py @@ -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() diff --git a/doc/ext/sphinx_gallery/LICENSE b/doc/ext/sphinx_gallery/LICENSE new file mode 100644 index 00000000..1ad8acd4 --- /dev/null +++ b/doc/ext/sphinx_gallery/LICENSE @@ -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. diff --git a/doc/ext/sphinx_gallery/README.txt b/doc/ext/sphinx_gallery/README.txt new file mode 100644 index 00000000..a9cd77a7 --- /dev/null +++ b/doc/ext/sphinx_gallery/README.txt @@ -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). diff --git a/doc/ext/sphinx_gallery/__init__.py b/doc/ext/sphinx_gallery/__init__.py new file mode 100644 index 00000000..c5ff4151 --- /dev/null +++ b/doc/ext/sphinx_gallery/__init__.py @@ -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')) diff --git a/doc/ext/sphinx_gallery/_static/broken_example.png b/doc/ext/sphinx_gallery/_static/broken_example.png new file mode 100644 index 00000000..4fea24e7 Binary files /dev/null and b/doc/ext/sphinx_gallery/_static/broken_example.png differ diff --git a/doc/ext/sphinx_gallery/_static/broken_stamp.svg b/doc/ext/sphinx_gallery/_static/broken_stamp.svg new file mode 100644 index 00000000..3aa3671d --- /dev/null +++ b/doc/ext/sphinx_gallery/_static/broken_stamp.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/doc/ext/sphinx_gallery/_static/gallery.css b/doc/ext/sphinx_gallery/_static/gallery.css new file mode 100644 index 00000000..e8b6c7de --- /dev/null +++ b/doc/ext/sphinx_gallery/_static/gallery.css @@ -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; +} diff --git a/doc/ext/sphinx_gallery/_static/no_image.png b/doc/ext/sphinx_gallery/_static/no_image.png new file mode 100644 index 00000000..8c2d48d5 Binary files /dev/null and b/doc/ext/sphinx_gallery/_static/no_image.png differ diff --git a/doc/ext/sphinx_gallery/backreferences.py b/doc/ext/sphinx_gallery/backreferences.py new file mode 100644 index 00000000..4fe579c6 --- /dev/null +++ b/doc/ext/sphinx_gallery/backreferences.py @@ -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 + +
+ +.. only:: html + + .. figure:: /{thumbnail} + + :ref:`sphx_glr_{ref_name}` + +.. raw:: html + +
+""" + +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) diff --git a/doc/ext/sphinx_gallery/docs_resolv.py b/doc/ext/sphinx_gallery/docs_resolv.py new file mode 100644 index 00000000..1843ebdb --- /dev/null +++ b/doc/ext/sphinx_gallery/docs_resolv.py @@ -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 = '%s' + orig_pattern = '%s' + period = '.' + + 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'(? 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) diff --git a/doc/ext/sphinx_gallery/gen_gallery.py b/doc/ext/sphinx_gallery/gen_gallery.py new file mode 100644 index 00000000..e1698230 --- /dev/null +++ b/doc/ext/sphinx_gallery/gen_gallery.py @@ -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 diff --git a/doc/ext/sphinx_gallery/gen_rst.py b/doc/ext/sphinx_gallery/gen_rst.py new file mode 100644 index 00000000..318bc60f --- /dev/null +++ b/doc/ext/sphinx_gallery/gen_rst.py @@ -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^#{20,}.*)\s(?P(?:^#.*\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 +
\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 diff --git a/doc/ext/sphinx_gallery/notebook.py b/doc/ext/sphinx_gallery/notebook.py new file mode 100644 index 00000000..121e945a --- /dev/null +++ b/doc/ext/sphinx_gallery/notebook.py @@ -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) diff --git a/doc/release/release_0.10.txt b/doc/release/release_0.10.rst similarity index 100% rename from doc/release/release_0.10.txt rename to doc/release/release_0.10.rst diff --git a/doc/release/release_0.11.txt b/doc/release/release_0.11.rst similarity index 100% rename from doc/release/release_0.11.txt rename to doc/release/release_0.11.rst diff --git a/doc/release/release_0.12.txt b/doc/release/release_0.12.rst similarity index 100% rename from doc/release/release_0.12.txt rename to doc/release/release_0.12.rst diff --git a/doc/release/release_0.3.txt b/doc/release/release_0.3.rst similarity index 100% rename from doc/release/release_0.3.txt rename to doc/release/release_0.3.rst diff --git a/doc/release/release_0.4.txt b/doc/release/release_0.4.rst similarity index 100% rename from doc/release/release_0.4.txt rename to doc/release/release_0.4.rst diff --git a/doc/release/release_0.5.txt b/doc/release/release_0.5.rst similarity index 100% rename from doc/release/release_0.5.txt rename to doc/release/release_0.5.rst diff --git a/doc/release/release_0.6.txt b/doc/release/release_0.6.rst similarity index 100% rename from doc/release/release_0.6.txt rename to doc/release/release_0.6.rst diff --git a/doc/release/release_0.7.txt b/doc/release/release_0.7.rst similarity index 100% rename from doc/release/release_0.7.txt rename to doc/release/release_0.7.rst diff --git a/doc/release/release_0.8.txt b/doc/release/release_0.8.rst similarity index 100% rename from doc/release/release_0.8.txt rename to doc/release/release_0.8.rst diff --git a/doc/release/release_0.9.txt b/doc/release/release_0.9.rst similarity index 100% rename from doc/release/release_0.9.txt rename to doc/release/release_0.9.rst diff --git a/doc/release/release_template.txt b/doc/release/release_dev.rst similarity index 100% rename from doc/release/release_template.txt rename to doc/release/release_dev.rst diff --git a/doc/release/release_dev.txt b/doc/release/release_template.rst similarity index 100% rename from doc/release/release_dev.txt rename to doc/release/release_template.rst diff --git a/doc/source/api_changes.txt b/doc/source/api_changes.rst similarity index 100% rename from doc/source/api_changes.txt rename to doc/source/api_changes.rst diff --git a/doc/source/cell_profiler.txt b/doc/source/cell_profiler.rst similarity index 100% rename from doc/source/cell_profiler.txt rename to doc/source/cell_profiler.rst diff --git a/doc/source/conf.py b/doc/source/conf.py index ae734c3d..b460a23b 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -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)) + + diff --git a/doc/source/contribute.txt b/doc/source/contribute.rst similarity index 100% rename from doc/source/contribute.txt rename to doc/source/contribute.rst diff --git a/doc/source/gitwash/configure_git.txt b/doc/source/gitwash/configure_git.rst similarity index 100% rename from doc/source/gitwash/configure_git.txt rename to doc/source/gitwash/configure_git.rst diff --git a/doc/source/gitwash/development_workflow.txt b/doc/source/gitwash/development_workflow.rst similarity index 100% rename from doc/source/gitwash/development_workflow.txt rename to doc/source/gitwash/development_workflow.rst diff --git a/doc/source/gitwash/following_latest.txt b/doc/source/gitwash/following_latest.rst similarity index 100% rename from doc/source/gitwash/following_latest.txt rename to doc/source/gitwash/following_latest.rst diff --git a/doc/source/gitwash/forking_hell.txt b/doc/source/gitwash/forking_hell.rst similarity index 100% rename from doc/source/gitwash/forking_hell.txt rename to doc/source/gitwash/forking_hell.rst diff --git a/doc/source/gitwash/git_development.txt b/doc/source/gitwash/git_development.rst similarity index 100% rename from doc/source/gitwash/git_development.txt rename to doc/source/gitwash/git_development.rst diff --git a/doc/source/gitwash/git_install.txt b/doc/source/gitwash/git_install.rst similarity index 100% rename from doc/source/gitwash/git_install.txt rename to doc/source/gitwash/git_install.rst diff --git a/doc/source/gitwash/git_intro.txt b/doc/source/gitwash/git_intro.rst similarity index 100% rename from doc/source/gitwash/git_intro.txt rename to doc/source/gitwash/git_intro.rst diff --git a/doc/source/gitwash/git_resources.txt b/doc/source/gitwash/git_resources.rst similarity index 100% rename from doc/source/gitwash/git_resources.txt rename to doc/source/gitwash/git_resources.rst diff --git a/doc/source/gitwash/index.txt b/doc/source/gitwash/index.rst similarity index 100% rename from doc/source/gitwash/index.txt rename to doc/source/gitwash/index.rst diff --git a/doc/source/gitwash/maintainer_workflow.txt b/doc/source/gitwash/maintainer_workflow.rst similarity index 100% rename from doc/source/gitwash/maintainer_workflow.txt rename to doc/source/gitwash/maintainer_workflow.rst diff --git a/doc/source/gitwash/patching.txt b/doc/source/gitwash/patching.rst similarity index 100% rename from doc/source/gitwash/patching.txt rename to doc/source/gitwash/patching.rst diff --git a/doc/source/gitwash/set_up_fork.txt b/doc/source/gitwash/set_up_fork.rst similarity index 100% rename from doc/source/gitwash/set_up_fork.txt rename to doc/source/gitwash/set_up_fork.rst diff --git a/doc/source/gsoc2011.txt b/doc/source/gsoc2011.rst similarity index 100% rename from doc/source/gsoc2011.txt rename to doc/source/gsoc2011.rst diff --git a/doc/source/index.txt b/doc/source/index.rst similarity index 100% rename from doc/source/index.txt rename to doc/source/index.rst diff --git a/doc/source/install.txt b/doc/source/install.rst similarity index 100% rename from doc/source/install.txt rename to doc/source/install.rst diff --git a/doc/source/license.txt b/doc/source/license.rst similarity index 100% rename from doc/source/license.txt rename to doc/source/license.rst diff --git a/doc/source/overview.txt b/doc/source/overview.rst similarity index 100% rename from doc/source/overview.txt rename to doc/source/overview.rst diff --git a/doc/source/release_notes_and_installation.txt b/doc/source/release_notes_and_installation.rst similarity index 59% rename from doc/source/release_notes_and_installation.txt rename to doc/source/release_notes_and_installation.rst index f07fd101..4a92e797 100644 --- a/doc/source/release_notes_and_installation.txt +++ b/doc/source/release_notes_and_installation.rst @@ -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 diff --git a/doc/source/user_guide.txt b/doc/source/user_guide.rst similarity index 100% rename from doc/source/user_guide.txt rename to doc/source/user_guide.rst diff --git a/doc/source/user_guide/data_types.txt b/doc/source/user_guide/data_types.rst similarity index 100% rename from doc/source/user_guide/data_types.txt rename to doc/source/user_guide/data_types.rst diff --git a/doc/source/user_guide/getting_help.txt b/doc/source/user_guide/getting_help.rst similarity index 100% rename from doc/source/user_guide/getting_help.txt rename to doc/source/user_guide/getting_help.rst diff --git a/doc/source/user_guide/getting_started.txt b/doc/source/user_guide/getting_started.rst similarity index 100% rename from doc/source/user_guide/getting_started.txt rename to doc/source/user_guide/getting_started.rst diff --git a/doc/source/user_guide/numpy_images.txt b/doc/source/user_guide/numpy_images.rst similarity index 99% rename from doc/source/user_guide/numpy_images.txt rename to doc/source/user_guide/numpy_images.rst index 00f49bbb..13727161 100644 --- a/doc/source/user_guide/numpy_images.txt +++ b/doc/source/user_guide/numpy_images.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 diff --git a/doc/source/user_guide/plugins.txt b/doc/source/user_guide/plugins.rst similarity index 100% rename from doc/source/user_guide/plugins.txt rename to doc/source/user_guide/plugins.rst diff --git a/doc/source/user_guide/transforming_image_data.txt b/doc/source/user_guide/transforming_image_data.rst similarity index 91% rename from doc/source/user_guide/transforming_image_data.txt rename to doc/source/user_guide/transforming_image_data.rst index 472b55dc..79c6e03a 100644 --- a/doc/source/user_guide/transforming_image_data.txt +++ b/doc/source/user_guide/transforming_image_data.rst @@ -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` diff --git a/doc/source/user_guide/tutorial_parallelization.txt b/doc/source/user_guide/tutorial_parallelization.rst similarity index 100% rename from doc/source/user_guide/tutorial_parallelization.txt rename to doc/source/user_guide/tutorial_parallelization.rst diff --git a/doc/source/user_guide/tutorial_segmentation.txt b/doc/source/user_guide/tutorial_segmentation.rst similarity index 87% rename from doc/source/user_guide/tutorial_segmentation.txt rename to doc/source/user_guide/tutorial_segmentation.rst index e5382528..5ff5146c 100644 --- a/doc/source/user_guide/tutorial_segmentation.txt +++ b/doc/source/user_guide/tutorial_segmentation.rst @@ -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 diff --git a/doc/source/user_guide/tutorials.txt b/doc/source/user_guide/tutorials.rst similarity index 100% rename from doc/source/user_guide/tutorials.txt rename to doc/source/user_guide/tutorials.rst diff --git a/doc/source/user_guide/viewer.txt b/doc/source/user_guide/viewer.rst similarity index 100% rename from doc/source/user_guide/viewer.txt rename to doc/source/user_guide/viewer.rst diff --git a/doc/tools/apigen.py b/doc/tools/apigen.py index 590b3e40..61da3fea 100644 --- a/doc/tools/apigen.py +++ b/doc/tools/apigen.py @@ -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, ):