|
|
|
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))
|
|
|
|
else:
|
|
|
|
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')
|