import numpy as np
from .GaussianPyramid import GaussianPyramid
from .filters import parse_filter
from .c.wrapper import upConv
[docs]
class LaplacianPyramid(GaussianPyramid):
"""Laplacian pyramid
Parameters
----------
image : `array_like`
1d or 2d image upon which to construct to the pyramid.
height : 'auto' or `int`.
The height of the pyramid. If 'auto', will automatically determine based on the size of
`image`.
downsample_filter_name : {'binomN', 'haar', 'qmf8', 'qmf12', 'qmf16', 'daub2', 'daub3',
'daub4', 'qmf5', 'qmf9', 'qmf13'}
name of filter to use for (separable) convolution to downsample the image. All scaled so
L-2 norm is 1.0
* `'binomN'` (default: 'binom5') - binomial coefficient filter of order N-1
* `'haar'` - Haar wavelet
* `'qmf8'`, `'qmf12'`, `'qmf16'` - Symmetric Quadrature Mirror Filters [1]_
* `'daub2'`, `'daub3'`, `'daub4'` - Daubechies wavelet [2]_
* `'qmf5'`, `'qmf9'`, `'qmf13'` - Symmetric Quadrature Mirror Filters [3]_, [4]_
upsample_filter_name : {None, 'binomN', 'haar', 'qmf8', 'qmf12', 'qmf16', 'daub2', 'daub3',
'daub4', 'qmf5', 'qmf9', 'qmf13'}
name of filter to use as the "expansion" filter. All scaled so L-2 norm is 1.0
* None (default) - same as `downsample_filter_name`
* `'binomN'` - binomial coefficient filter of order N-1
* `'haar'` - Haar wavelet
* `'qmf8'`, `'qmf12'`, `'qmf16'` - Symmetric Quadrature Mirror Filters [1]_
* `'daub2'`, `'daub3'`, `'daub4'` - Daubechies wavelet [2]_
* `'qmf5'`, `'qmf9'`, `'qmf13'` - Symmetric Quadrature Mirror Filters [3]_, [4]_
edge_type : {'circular', 'reflect1', 'reflect2', 'repeat', 'zero', 'extend', 'dont-compute'}
Specifies how to handle edges. Options are:
* `'circular'` - circular convolution
* `'reflect1'` - reflect about the edge pixels
* `'reflect2'` - reflect, doubling the edge pixels
* `'repeat'` - repeat the edge pixels
* `'zero'` - assume values of zero outside image boundary
* `'extend'` - reflect and invert
* `'dont-compute'` - zero output when filter overhangs imput boundaries.
Attributes
----------
image : `array_like`
The input image used to construct the pyramid.
image_size : `tuple`
The size of the input image.
pyr_type : `str` or `None`
Human-readable string specifying the type of pyramid. For base class, is None.
edge_type : `str`
Specifies how edges were handled.
pyr_coeffs : `dict`
Dictionary containing the coefficients of the pyramid. Keys are `(level, band)` tuples and
values are 1d or 2d numpy arrays (same number of dimensions as the input image)
pyr_size : `dict`
Dictionary containing the sizes of the pyramid coefficients. Keys are `(level, band)`
tuples and values are tuples.
is_complex : `bool`
Whether the coefficients are complex- or real-valued. Only `SteerablePyramidFreq` can have
a value of True, all others must be False.
References
----------
.. [1] J D Johnston, "A filter family designed for use in quadrature mirror filter banks",
Proc. ICASSP, pp 291-294, 1980.
.. [2] I Daubechies, "Orthonormal bases of compactly supported wavelets", Commun. Pure Appl.
Math, vol. 42, pp 909-996, 1988.
.. [3] E P Simoncelli, "Orthogonal sub-band image transforms", PhD Thesis, MIT Dept. of Elec.
Eng. and Comp. Sci. May 1988. Also available as: MIT Media Laboratory Vision and Modeling
Technical Report #100.
.. [4] E P Simoncelli and E H Adelson, "Subband image coding", Subband Transforms, chapter 4,
ed. John W Woods, Kluwer Academic Publishers, Norwell, MA, 1990, pp 143--192.
"""
def __init__(self, image, height='auto', downsample_filter_name='binom5',
upsample_filter_name=None, edge_type='reflect1'):
self.pyr_type = 'Laplacian'
if upsample_filter_name is None:
upsample_filter_name = downsample_filter_name
super().__init__(image, height, downsample_filter_name, edge_type, upsample_filter_name=upsample_filter_name)
def _build_pyr(self):
"""build the pyramid
This should not be called directly by users, it's a helper function for constructing the
pyramid
"""
im = self.image
for lev in range(self.num_scales - 1):
im_next = self._build_next(im)
im_recon = self._recon_prev(im_next, output_size=im.shape)
im_residual = im - im_recon
self.pyr_coeffs[(lev, 0)] = im_residual.copy()
self.pyr_size[(lev, 0)] = im_residual.shape
im = im_next
self.pyr_coeffs[(lev+1, 0)] = im.copy()
self.pyr_size[(lev+1, 0)] = im.shape
def _recon_prev(self, image, output_size, upsample_filter=None, edge_type=None):
"""Reconstruct the previous level of the pyramid.
Should not be called by users directly, this is a helper function for reconstructing the
input image using pyramid coefficients.
"""
if upsample_filter is None:
upsample_filter = self.filters['upsample_filter']
else:
upsample_filter = parse_filter(upsample_filter, normalize=False)
if edge_type is None:
edge_type = self.edge_type
if image.shape[0] == 1:
res = upConv(image=image, filt=upsample_filter.T, edge_type=edge_type, step=(1, 2), stop=(output_size[0], output_size[1]))
elif image.shape[1] == 1:
res = upConv(image=image, filt=upsample_filter, edge_type=edge_type, step=(2, 1), stop=(output_size[0], output_size[1]))
else:
tmp = upConv(image=image, filt=upsample_filter, edge_type=edge_type, step=(2, 1), stop=(output_size[0], image.shape[1]))
res = upConv(image=tmp, filt=upsample_filter.T, edge_type=edge_type, step=(1, 2), stop=(output_size[0], output_size[1]))
return res
[docs]
def recon_pyr(self, upsample_filter_name=None, edge_type=None, levels='all'):
"""Reconstruct the input image using pyramid coefficients
Parameters
----------
upsample_filter_name : {None, 'binomN', 'haar', 'qmf8', 'qmf12', 'qmf16', 'daub2', 'daub3',
'daub4', 'qmf5', 'qmf9', 'qmf13'}
name of filter to use as "expansion" filter. All scaled so L-2 norm is 1.0
* None (default) - use `self.upsample_filter_name`, the expansion filter set during
initialization.
* `'binomN'` - binomial coefficient filter of order N-1
* `'haar'` - Haar wavelet
* `'qmf8'`, `'qmf12'`, `'qmf16'` - Symmetric Quadrature Mirror Filters [1]_
* `'daub2'`, `'daub3'`, `'daub4'` - Daubechies wavelet [2]_
* `'qmf5'`, `'qmf9'`, `'qmf13'` - Symmetric Quadrature Mirror Filters [3]_, [4]_
edge_type : {None, 'circular', 'reflect1', 'reflect2', 'repeat', 'zero', 'extend',
'dont-compute'}
Specifies how to handle edges. Options are:
* None (default) - use `self.edge_type`, the edge_type used to construct the pyramid
* `'circular'` - circular convolution
* `'reflect1'` - reflect about the edge pixels
* `'reflect2'` - reflect, doubling the edge pixels
* `'repeat'` - repeat the edge pixels
* `'zero'` - assume values of zero outside image boundary
* `'extend'` - reflect and invert
* `'dont-compute'` - zero output when filter overhangs imput boundaries.
levels : `list`, `int`, or {`'all'`, `'residual_highpass'`}
If `list` should contain some subset of integers from `0` to `self.num_scales-1`
(inclusive) and `'residual_lowpass'`. If `'all'`, returned value will contain all
valid levels. Otherwise, must be one of the valid levels.
Returns
-------
recon : `np.array`
The reconstructed image.
"""
recon_keys = self._recon_keys(levels, 'all')
recon = np.zeros_like(self.pyr_coeffs[(self.num_scales-1, 0)])
for lev in reversed(range(self.num_scales)):
# upsample to generate higher reconolution image
recon = self._recon_prev(recon, self.pyr_size[(lev, 0)], upsample_filter_name, edge_type)
if (lev, 0) in recon_keys:
recon += self.pyr_coeffs[(lev, 0)]
return recon