mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-27 11:27:08 +08:00
Merge pull request #302 from ahojnnes/pyramids
Add function to build image pyramids
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
====================
|
||||
Build image pyramids
|
||||
====================
|
||||
|
||||
The `build_gauassian_pyramid` function takes an image and yields successive
|
||||
images shrunk by a constant scale factor. Image pyramids are often used, e.g.,
|
||||
to implement algorithms for denoising, texture discrimination, and scale-
|
||||
invariant detection.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage.transform import build_gaussian_pyramid
|
||||
|
||||
|
||||
image = data.lena()
|
||||
rows, cols, dim = image.shape
|
||||
pyramid = tuple(build_gaussian_pyramid(image, downscale=2))
|
||||
|
||||
composite_image = np.zeros((rows, cols + cols / 2, 3), dtype=np.double)
|
||||
|
||||
composite_image[:rows, :cols, :] = pyramid[0]
|
||||
|
||||
i_row = 0
|
||||
for p in pyramid[1:]:
|
||||
n_rows, n_cols = p.shape[:2]
|
||||
composite_image[i_row:i_row + n_rows, cols:cols + n_cols] = p
|
||||
i_row += n_rows
|
||||
|
||||
plt.imshow(composite_image)
|
||||
plt.show()
|
||||
@@ -4,7 +4,8 @@ from .finite_radon_transform import *
|
||||
from .integral import *
|
||||
from ._geometric import (warp, warp_coords, estimate_transform,
|
||||
SimilarityTransform, AffineTransform,
|
||||
ProjectiveTransform, PolynomialTransform,
|
||||
ProjectiveTransform, PolynomialTransform,
|
||||
PiecewiseAffineTransform)
|
||||
from ._warps import swirl, homography, resize, rotate
|
||||
|
||||
from .pyramids import (pyramid_reduce, pyramid_expand,
|
||||
build_gaussian_pyramid, build_laplacian_pyramid)
|
||||
|
||||
@@ -1016,5 +1016,7 @@ def warp(image, inverse_map=None, map_args={}, output_shape=None, order=1,
|
||||
if mode == 'constant' and not (0 <= cval <= 1):
|
||||
clipped[out == cval] = cval
|
||||
|
||||
# Remove singleton dim introduced by atleast_3d
|
||||
return clipped.squeeze()
|
||||
if clipped.shape[0] == 1 or clipped.shape[1] == 1:
|
||||
return clipped
|
||||
else: # remove singleton dim introduced by atleast_3d
|
||||
return clipped.squeeze()
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
import math
|
||||
import numpy as np
|
||||
from scipy import ndimage
|
||||
from skimage.transform import resize
|
||||
from skimage.util import img_as_float
|
||||
|
||||
|
||||
def _smooth(image, sigma, mode, cval):
|
||||
"""Return image with each channel smoothed by the gaussian filter."""
|
||||
|
||||
smoothed = np.empty(image.shape, dtype=np.double)
|
||||
|
||||
if image.ndim == 3: # apply gaussian filter to all dimensions independently
|
||||
for dim in range(image.shape[2]):
|
||||
ndimage.gaussian_filter(image[..., dim], sigma,
|
||||
output=smoothed[..., dim],
|
||||
mode=mode, cval=cval)
|
||||
else:
|
||||
ndimage.gaussian_filter(image, sigma, output=smoothed,
|
||||
mode=mode, cval=cval)
|
||||
|
||||
return smoothed
|
||||
|
||||
|
||||
def _check_factor(factor):
|
||||
if factor <= 1:
|
||||
raise ValueError('scale factor must be greater than 1')
|
||||
|
||||
|
||||
def pyramid_reduce(image, downscale=2, sigma=None, order=1,
|
||||
mode='reflect', cval=0):
|
||||
"""Smooth and then downsample image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
Input image.
|
||||
downscale : float, optional
|
||||
Downscale factor.
|
||||
sigma : float, optional
|
||||
Sigma for gaussian filter. Default is `2 * downscale / 6.0` which
|
||||
corresponds to a filter mask twice the size of the scale factor that
|
||||
covers more than 99% of the gaussian distribution.
|
||||
order : int, optional
|
||||
Order of splines used in interpolation of downsampling. See
|
||||
`scipy.ndimage.map_coordinates` for detail.
|
||||
mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
|
||||
The mode parameter determines how the array borders are handled, where
|
||||
cval is the value when mode is equal to 'constant'.
|
||||
cval : float, optional
|
||||
Value to fill past edges of input if mode is 'constant'.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : array
|
||||
Smoothed and downsampled float image.
|
||||
|
||||
References
|
||||
----------
|
||||
..[1] http://web.mit.edu/persci/people/adelson/pub_pdfs/pyramid83.pdf
|
||||
|
||||
"""
|
||||
|
||||
_check_factor(downscale)
|
||||
|
||||
image = img_as_float(image)
|
||||
|
||||
rows = image.shape[0]
|
||||
cols = image.shape[1]
|
||||
out_rows = math.ceil(rows / float(downscale))
|
||||
out_cols = math.ceil(cols / float(downscale))
|
||||
|
||||
if sigma is None:
|
||||
# automatically determine sigma which covers > 99% of distribution
|
||||
sigma = 2 * downscale / 6.0
|
||||
|
||||
smoothed = _smooth(image, sigma, mode, cval)
|
||||
out = resize(smoothed, (out_rows, out_cols), order=order,
|
||||
mode=mode, cval=cval)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def pyramid_expand(image, upscale=2, sigma=None, order=1,
|
||||
mode='reflect', cval=0):
|
||||
"""Upsample and then smooth image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
Input image.
|
||||
upscale : float, optional
|
||||
Upscale factor.
|
||||
sigma : float, optional
|
||||
Sigma for gaussian filter. Default is `2 * upscale / 6.0` which
|
||||
corresponds to a filter mask twice the size of the scale factor that
|
||||
covers more than 99% of the gaussian distribution.
|
||||
order : int, optional
|
||||
Order of splines used in interpolation of upsampling. See
|
||||
`scipy.ndimage.map_coordinates` for detail.
|
||||
mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
|
||||
The mode parameter determines how the array borders are handled, where
|
||||
cval is the value when mode is equal to 'constant'.
|
||||
cval : float, optional
|
||||
Value to fill past edges of input if mode is 'constant'.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : array
|
||||
Upsampled and smoothed float image.
|
||||
|
||||
References
|
||||
----------
|
||||
..[1] http://web.mit.edu/persci/people/adelson/pub_pdfs/pyramid83.pdf
|
||||
|
||||
"""
|
||||
|
||||
_check_factor(upscale)
|
||||
|
||||
image = img_as_float(image)
|
||||
|
||||
rows = image.shape[0]
|
||||
cols = image.shape[1]
|
||||
out_rows = math.ceil(upscale * rows)
|
||||
out_cols = math.ceil(upscale * cols)
|
||||
|
||||
if sigma is None:
|
||||
# automatically determine sigma which covers > 99% of distribution
|
||||
sigma = 2 * upscale / 6.0
|
||||
|
||||
resized = resize(image, (out_rows, out_cols), order=order,
|
||||
mode=mode, cval=cval)
|
||||
out = _smooth(resized, sigma, mode, cval)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def build_gaussian_pyramid(image, max_layer=-1, downscale=2, sigma=None,
|
||||
order=1, mode='reflect', cval=0):
|
||||
"""Yield images of the gaussian pyramid formed by the input image.
|
||||
|
||||
Recursively applies the `pyramid_reduce` function to the image, and yields
|
||||
the downscaled images. Note that the first image of the pyramid will be the
|
||||
original, unscaled image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
Input image.
|
||||
max_layer : int
|
||||
Number of layers for the pyramid. 0th layer is the original image.
|
||||
Default is -1 which builds all possible layers.
|
||||
downscale : float, optional
|
||||
Downscale factor.
|
||||
sigma : float, optional
|
||||
Sigma for gaussian filter. Default is `2 * downscale / 6.0` which
|
||||
corresponds to a filter mask twice the size of the scale factor that
|
||||
covers more than 99% of the gaussian distribution.
|
||||
order : int, optional
|
||||
Order of splines used in interpolation of downsampling. See
|
||||
`scipy.ndimage.map_coordinates` for detail.
|
||||
mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
|
||||
The mode parameter determines how the array borders are handled, where
|
||||
cval is the value when mode is equal to 'constant'.
|
||||
cval : float, optional
|
||||
Value to fill past edges of input if mode is 'constant'.
|
||||
|
||||
Returns
|
||||
-------
|
||||
pyramid : generator
|
||||
Generator yielding pyramid layers as float images.
|
||||
|
||||
References
|
||||
----------
|
||||
..[1] http://web.mit.edu/persci/people/adelson/pub_pdfs/pyramid83.pdf
|
||||
|
||||
"""
|
||||
|
||||
_check_factor(downscale)
|
||||
|
||||
# cast to float for consistent data type in pyramid
|
||||
image = img_as_float(image)
|
||||
|
||||
layer = 0
|
||||
rows = image.shape[0]
|
||||
cols = image.shape[1]
|
||||
|
||||
prev_layer_image = image
|
||||
yield image
|
||||
|
||||
# build downsampled images until max_layer is reached or downscale process
|
||||
# does not change image size
|
||||
while layer != max_layer:
|
||||
layer += 1
|
||||
|
||||
layer_image = pyramid_reduce(prev_layer_image, downscale, sigma, order,
|
||||
mode, cval)
|
||||
|
||||
prev_rows = rows
|
||||
prev_cols = cols
|
||||
prev_layer_image = layer_image
|
||||
rows = layer_image.shape[0]
|
||||
cols = layer_image.shape[1]
|
||||
|
||||
# no change to previous pyramid layer
|
||||
if prev_rows == rows and prev_cols == cols:
|
||||
break
|
||||
|
||||
yield layer_image
|
||||
|
||||
|
||||
def build_laplacian_pyramid(image, max_layer=-1, downscale=2, sigma=None,
|
||||
order=1, mode='reflect', cval=0):
|
||||
"""Yield images of the laplacian pyramid formed by the input image.
|
||||
|
||||
Each layer contains the difference between the downsampled and the
|
||||
downsampled plus smoothed image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
Input image.
|
||||
max_layer : int
|
||||
Number of layers for the pyramid. 0th layer is the original image.
|
||||
Default is -1 which builds all possible layers.
|
||||
downscale : float, optional
|
||||
Downscale factor.
|
||||
sigma : float, optional
|
||||
Sigma for gaussian filter. Default is `2 * downscale / 6.0` which
|
||||
corresponds to a filter mask twice the size of the scale factor that
|
||||
covers more than 99% of the gaussian distribution.
|
||||
order : int, optional
|
||||
Order of splines used in interpolation of downsampling. See
|
||||
`scipy.ndimage.map_coordinates` for detail.
|
||||
mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
|
||||
The mode parameter determines how the array borders are handled, where
|
||||
cval is the value when mode is equal to 'constant'.
|
||||
cval : float, optional
|
||||
Value to fill past edges of input if mode is 'constant'.
|
||||
|
||||
Returns
|
||||
-------
|
||||
pyramid : generator
|
||||
Generator yielding pyramid layers as float images.
|
||||
|
||||
References
|
||||
----------
|
||||
..[1] http://web.mit.edu/persci/people/adelson/pub_pdfs/pyramid83.pdf
|
||||
..[2] http://sepwww.stanford.edu/~morgan/texturematch/paper_html/node3.html
|
||||
|
||||
"""
|
||||
|
||||
_check_factor(downscale)
|
||||
|
||||
# cast to float for consistent data type in pyramid
|
||||
image = img_as_float(image)
|
||||
|
||||
if sigma is None:
|
||||
# automatically determine sigma which covers > 99% of distribution
|
||||
sigma = 2 * downscale / 6.0
|
||||
|
||||
layer = 0
|
||||
rows = image.shape[0]
|
||||
cols = image.shape[1]
|
||||
|
||||
smoothed_image = _smooth(image, sigma, mode, cval)
|
||||
yield image - smoothed_image
|
||||
|
||||
# build downsampled images until max_layer is reached or downscale process
|
||||
# does not change image size
|
||||
while layer != max_layer:
|
||||
layer += 1
|
||||
|
||||
out_rows = math.ceil(rows / float(downscale))
|
||||
out_cols = math.ceil(cols / float(downscale))
|
||||
|
||||
resized_image = resize(smoothed_image, (out_rows, out_cols),
|
||||
order=order, mode=mode, cval=cval)
|
||||
smoothed_image = _smooth(resized_image, sigma, mode, cval)
|
||||
|
||||
prev_rows = rows
|
||||
prev_cols = cols
|
||||
rows = resized_image.shape[0]
|
||||
cols = resized_image.shape[1]
|
||||
|
||||
# no change to previous pyramid layer
|
||||
if prev_rows == rows and prev_cols == cols:
|
||||
break
|
||||
|
||||
yield resized_image - smoothed_image
|
||||
@@ -0,0 +1,41 @@
|
||||
from numpy.testing import assert_array_equal, run_module_suite
|
||||
from skimage import data
|
||||
from skimage.transform import (pyramid_reduce, pyramid_expand,
|
||||
build_gaussian_pyramid, build_laplacian_pyramid)
|
||||
|
||||
|
||||
image = data.lena()
|
||||
|
||||
|
||||
def test_pyramid_reduce():
|
||||
rows, cols, dim = image.shape
|
||||
out = pyramid_reduce(image, downscale=2)
|
||||
assert_array_equal(out.shape, (rows / 2, cols / 2, dim))
|
||||
|
||||
|
||||
def test_pyramid_expand():
|
||||
rows, cols, dim = image.shape
|
||||
out = pyramid_expand(image, upscale=2)
|
||||
assert_array_equal(out.shape, (rows * 2, cols * 2, dim))
|
||||
|
||||
|
||||
def test_build_gaussian_pyramid():
|
||||
rows, cols, dim = image.shape
|
||||
pyramid = build_gaussian_pyramid(image, downscale=2)
|
||||
|
||||
for layer, out in enumerate(pyramid):
|
||||
layer_shape = (rows / 2 ** layer, cols / 2 ** layer, dim)
|
||||
assert_array_equal(out.shape, layer_shape)
|
||||
|
||||
|
||||
def test_build_laplacian_pyramid():
|
||||
rows, cols, dim = image.shape
|
||||
pyramid = build_laplacian_pyramid(image, downscale=2)
|
||||
|
||||
for layer, out in enumerate(pyramid):
|
||||
layer_shape = (rows / 2 ** layer, cols / 2 ** layer, dim)
|
||||
assert_array_equal(out.shape, layer_shape)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_module_suite()
|
||||
@@ -4,7 +4,7 @@ CollectionViewer demo
|
||||
=====================
|
||||
|
||||
Demo of CollectionViewer for viewing collections of images. This demo uses
|
||||
successively darker versions of the same image to fake an image collection.
|
||||
the different layers of the gaussian pyramid as image collection.
|
||||
|
||||
You can scroll through images with the slider, or you can interact with the
|
||||
viewer using your keyboard:
|
||||
@@ -21,9 +21,11 @@ home/end keys
|
||||
import numpy as np
|
||||
from skimage import data
|
||||
from skimage.viewer import CollectionViewer
|
||||
from skimage.transform import build_gaussian_pyramid
|
||||
|
||||
|
||||
img = data.lena()
|
||||
img_collection = [np.uint8(img * 0.9**i) for i in range(20)]
|
||||
img_collection = tuple(build_gaussian_pyramid(img))
|
||||
|
||||
view = CollectionViewer(img_collection)
|
||||
view.show()
|
||||
|
||||
Reference in New Issue
Block a user