You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

414 lines
11 KiB
Python

import numpy as np
from scipy.fftpack import dct as _dct, idct as _idct
from scipy.fftpack import dst as _dst, idst as _idst
import os
path = os.path.dirname(os.path.realpath(__file__))
__all__ = ['dct', 'idct', 'dctn', 'idctn', 'dst', 'idst', 'dstn', 'idstn']
def dct(x, type=2, n=None, axis=-1, norm='ortho'): # @ReservedAssignment
"""
Return the Discrete Cosine Transform of arbitrary type sequence x.
Parameters
----------
x : array_like
The input array.
type : {1, 2, 3}, optional
Type of the DCT (see Notes). Default type is 2.
n : int, optional
Length of the transform.
axis : int, optional
Axis over which to compute the transform.
norm : {None, 'ortho'}, optional
Normalization mode (see Notes). Default is 'ortho'.
Returns
-------
y : ndarray of real
The transformed input array.
See Also
--------
idct
Notes
-----
For a single dimension array ``x``, ``dct(x, norm='ortho')`` is equal to
MATLAB ``dct(x)``.
There are theoretically 8 types of the DCT, only the first 3 types are
implemented in scipy. 'The' DCT generally refers to DCT type 2, and 'the'
Inverse DCT generally refers to DCT type 3.
type I
~~~~~~
There are several definitions of the DCT-I; we use the following
(for ``norm=None``)::
N-2
y[k] = x[0] + (-1)**k x[N-1] + 2 * sum x[n]*cos(pi*k*n/(N-1))
n=1
Only None is supported as normalization mode for DCT-I. Note also that the
DCT-I is only supported for input size > 1
type II
~~~~~~~
There are several definitions of the DCT-II; we use the following
(for ``norm=None``)::
N-1
y[k] = 2* sum x[n]*cos(pi*k*(2n+1)/(2*N)), 0 <= k < N.
n=0
If ``norm='ortho'``, ``y[k]`` is multiplied by a scaling factor `f`::
f = sqrt(1/(4*N)) if k = 0,
f = sqrt(1/(2*N)) otherwise.
Which makes the corresponding matrix of coefficients orthonormal
(``OO' = Id``).
type III
~~~~~~~~
There are several definitions, we use the following
(for ``norm=None``)::
N-1
y[k] = x[0] + 2 * sum x[n]*cos(pi*(k+0.5)*n/N), 0 <= k < N.
n=1
or, for ``norm='ortho'`` and 0 <= k < N::
N-1
y[k] = x[0] / sqrt(N) + sqrt(1/N) * sum x[n]*cos(pi*(k+0.5)*n/N)
n=1
The (unnormalized) DCT-III is the inverse of the (unnormalized) DCT-II, up
to a factor `2N`. The orthonormalized DCT-III is exactly the inverse of
the orthonormalized DCT-II.
Examples
--------
>>> import numpy as np
>>> x = np.arange(5)
>>> np.abs(x-idct(dct(x)))<1e-14
array([ True, True, True, True, True], dtype=bool)
>>> np.abs(x-dct(idct(x)))<1e-14
array([ True, True, True, True, True], dtype=bool)
References
----------
http://en.wikipedia.org/wiki/Discrete_cosine_transform
http://users.ece.utexas.edu/~bevans/courses/ee381k/lectures/
'A Fast Cosine Transform in One and Two Dimensions', by J. Makhoul, `IEEE
Transactions on acoustics, speech and signal processing` vol. 28(1),
pp. 27-34, http://dx.doi.org/10.1109/TASSP.1980.1163351 (1980).
"""
return _dct(x, type, n, axis, norm)
def dst(x, type=2, n=None, axis=-1, norm='ortho'): # @ReservedAssignment
return _dst(x, type, n, axis, norm)
def idct(x, type=2, n=None, axis=-1, norm='ortho'): # @ReservedAssignment
"""
Return the Inverse Discrete Cosine Transform of an arbitrary type sequence.
Parameters
----------
x : array_like
The input array.
type : {1, 2, 3}, optional
Type of the DCT (see Notes). Default type is 2.
n : int, optional
Length of the transform.
axis : int, optional
Axis over which to compute the transform.
norm : {None, 'ortho'}, optional
Normalization mode (see Notes). Default is 'ortho'.
Returns
-------
y : ndarray of real
The transformed input array.
See Also
--------
dct
Notes
-----
For a single dimension array `x`, ``idct(x, norm='ortho')`` is equal to
matlab ``idct(x)``.
'The' IDCT is the IDCT of type 2, which is the same as DCT of type 3.
IDCT of type 1 is the DCT of type 1, IDCT of type 2 is the DCT of type 3,
and IDCT of type 3 is the DCT of type 2. For the definition of these types,
see `dct`.
"""
return _idct(x, type, n, axis, norm)
def idst(x, type=2, n=None, axis=-1, norm='ortho'): # @ReservedAssignment
return _idst(x, type, n, axis, norm)
def _get_shape(y, shape, axes):
if shape is None:
if axes is None:
shape = y.shape
else:
shape = np.take(y.shape, axes)
shape = tuple(shape)
return shape
def _get_axes(y, shape, axes):
if axes is None:
axes = range(y.ndim)
if len(axes) != len(shape):
raise ValueError("when given, axes and shape arguments "
"have to be of the same length")
return list(axes)
def _raw_dctn(y, type, shape, axes, norm, fun): # @ReservedAssignment
shape = _get_shape(y, shape, axes)
axes = _get_axes(y, shape, axes)
shape0 = list(y.shape)
ndim = y.ndim
isvector = max(shape0) == y.size
if isvector and ndim == 1:
y = np.atleast_2d(y)
y = y.T
for dim in range(ndim):
y = shiftdim(y, 1)
if dim not in axes:
continue
n = shape[axes.index(dim)]
shape0[dim] = n
y = fun(y, type, n, norm=norm)
return y.reshape(shape0)
def dctn(x, type=2, shape=None, axes=None, # @ReservedAssignment
norm='ortho'):
"""
Return the N-D Discrete Cosine Transform of array x.
Parameters
----------
x : array_like
The input array.
type : {1, 2, 3}, optional
Type of the DCT (see Notes). Default type is 2.
shape : tuple of ints, optional
The shape of the result. If both `shape` and `axes` (see below) are
None, `shape` is ``x.shape``; if `shape` is None but `axes` is
not None, then `shape` is ``scipy.take(x.shape, axes, axis=0)``.
If ``shape[i] > x.shape[i]``, the i-th dimension is padded with zeros.
If ``shape[i] < x.shape[i]``, the i-th dimension is truncated to
length ``shape[i]``.
axes : array_like of ints, optional
The axes of `x` (`y` if `shape` is not None) along which the
transform is applied.
norm : {None, 'ortho'}, optional
Normalization mode (see Notes in dct). Default is 'ortho'.
Returns
-------
y : ndarray of real
The transformed input array.
Notes
-----
Y = DCTN(X) returns the discrete cosine transform of X. The array Y is
the same size as X and contains the discrete cosine transform
coefficients. This transform can be inverted using IDCTN.
Input array can be numeric or logical. The returned array is of class
double.
Reference
---------
Narasimha M. et al, On the computation of the discrete cosine
transform, IEEE Trans Comm, 26, 6, 1978, pp 934-936.
Example
-------
>>> import os
>>> import numpy as np
>>> from PIL import Image
>>> import matplotlib.pyplot as plt
>>> name = os.path.join(path, 'autumn.gif')
>>> rgb = Image.open(name)
>>> J = dctn(rgb)
>>> (np.abs(rgb-idctn(J))<1e-7).all()
True
plt.imshow(np.log(np.abs(J)))
plt.colorbar() #colormap(jet), colorbar
The commands below set values less than magnitude 10 in the DCT matrix
to zero, then reconstruct the image using the inverse DCT.
J[np.abs(J)<10] = 0
K = idctn(J)
plt.figure(0)
plt.imshow(rgb)
plt.figure(1)
plt.imshow(K,[0 255])
See also
--------
idctn, dct, idct
"""
y = np.atleast_1d(x)
return _raw_dctn(y, type, shape, axes, norm, dct)
def dstn(x, type=2, shape=None, axes=None, # @ReservedAssignment
norm='ortho'):
y = np.atleast_1d(x)
return _raw_dctn(y, type, shape, axes, norm, dst)
def idctn(x, type=2, shape=None, axes=None, # @ReservedAssignment
norm='ortho'):
"""Return inverse N-D Discrete Cosine Transform of array x.
For description of parameters see `dctn`.
See Also
--------
dctn : for detailed information.
"""
y = np.atleast_1d(x)
return _raw_dctn(y, type, shape, axes, norm, idct)
def idstn(x, type=2, shape=None, axes=None, # @ReservedAssignment
norm='ortho'):
y = np.atleast_1d(x)
return _raw_dctn(y, type, shape, axes, norm, idst)
def num_leading_ones(x):
first = 0
for i, xi in enumerate(x):
if xi != 1:
first = i
break
return first
def no_leading_ones(x):
first = num_leading_ones(x)
return x[first:]
def shiftdim(x, n=None):
"""
Shift dimensions
Parameters
----------
x : array
n : int
Notes
-----
Shiftdim is handy for functions that intend to work along the first
non-singleton dimension of the array.
If n is None returns the array with the same number of elements as X but
with any leading singleton dimensions removed.
When n is positive, shiftdim shifts the dimensions to the left and wraps
the n leading dimensions to the end.
When n is negative, shiftdim shifts the dimensions to the right and pads
with singletons.
See also
--------
reshape, squeeze
"""
if n is None:
return x.reshape(no_leading_ones(x.shape))
elif n >= 0:
return x.transpose(np.roll(range(x.ndim), -n))
return x.reshape((1,) * -n + x.shape)
def example_dct2(debug=True):
"""
Example
-------
>>> import numpy as np
>>> out = example_dct2(debug=False)
>>> out['nnz_dct_rgb'] == 1096200
True
>>> np.allclose(out['diff_rgb_rgb_10'], 1.2524607233242335)
True
>>> out['nnz_dct_rgb_10']==986580
True
>>> out['min_rgb'] == 0.0
True
>>> out['max_rgb'] == 251.0
True
>>> np.allclose(out['percentile_dct_rgb_10'], 1.3451573865136308)
True
>>> np.allclose(out['diff_rgb_irgb'], 1.4850343177386094e-12)
True
"""
import scipy.ndimage as sn
import matplotlib.pyplot as plt
name = os.path.join(path, 'autumn.gif')
plt.figure(1)
rgb = np.asarray(sn.imread(name), dtype=np.float)
# np.fft.fft(rgb)
out = dict(max_rgb=np.max(rgb), min_rgb=np.min(rgb))
plt.set_cmap('jet')
J = dctn(rgb)
irgb = idctn(J)
out['diff_rgb_irgb'] = np.abs(rgb - irgb).max()
plt.imshow(np.log(np.abs(J)))
# plt.colorbar() #colormap(jet), colorbar
# plt.show('hold')
plt.figure(2)
v = np.percentile(np.abs(J.ravel()), 10.0)
out['nnz_dct_rgb'] = len(np.flatnonzero(J))
out['percentile_dct_rgb_10'] = v
J[np.abs(J) < v] = 0
out['nnz_dct_rgb_10'] = len(np.flatnonzero(J))
plt.imshow(np.log(np.abs(J)))
# plt.show('hold')
rgb_10 = idctn(J)
out['diff_rgb_rgb_10'] = np.abs(rgb - rgb_10).max()
plt.figure(3)
plt.imshow(rgb)
plt.figure(4)
plt.imshow(rgb_10, vmin=0, vmax=255)
if debug:
print(out)
return out
if __name__ == '__main__':
from wafo.testing import test_docstrings
test_docstrings(__file__)
# import matplotlib.pyplot as plt
# example_dct2()
# plt.show('hold')