コード例 #1
0
ファイル: DWT.py プロジェクト: rosafilgueira/code_inspector
class DWT(LinearOperator):
    """One dimensional Wavelet operator.

    Apply 1D-Wavelet Transform along a specific direction ``dir`` of a
    multi-dimensional array of size ``dims``.

    Note that the Wavelet operator is an overload of the ``pywt``
    implementation of the wavelet transform. Refer to
    https://pywavelets.readthedocs.io for a detailed description of the
    input parameters.

    Parameters
    ----------
    dims : :obj:`int` or :obj:`tuple`
        Number of samples for each dimension
    dir : :obj:`int`, optional
        Direction along which DWT is applied.
    wavelet : :obj:`str`, optional
        Name of wavelet type. Use :func:`pywt.wavelist(kind='discrete')` for
        a list of
        available wavelets.
    level : :obj:`int`, optional
        Number of scaling levels (must be >=0).
    dtype : :obj:`str`, optional
        Type of elements in input array.

    Attributes
    ----------
    shape : :obj:`tuple`
        Operator shape
    explicit : :obj:`bool`
        Operator contains a matrix that can be solved explicitly
        (True) or not (False)

    Raises
    ------
    ModuleNotFoundError
        If ``pywt`` is not installed
    ValueError
        If ``wavelet`` does not belong to ``pywt.families``

    Notes
    -----
    The Wavelet operator applies the multilevel Discrete Wavelet Transform
    (DWT) in forward mode and the multilevel Inverse Discrete Wavelet Transform
    (IDWT) in adjoint mode.

    Wavelet transforms can be used to compress signals and present
    a key advantage over Fourier transforms in that they captures both
    frequency and location information in time. Consider using this operator
    as sparsifying transform when using L1 solvers.

    """
    def __init__(self, dims, dir=0, wavelet='haar', level=1, dtype='float64'):
        if pywt is None:
            raise ModuleNotFoundError(pywt_message)
        _checkwavelet(wavelet)

        if isinstance(dims, int):
            dims = (dims, )

        # define padding for length to be power of 2
        ndimpow2 = max(2**ceil(log(dims[dir], 2)), 2**level)
        pad = [(0, 0)] * len(dims)
        pad[dir] = (0, ndimpow2 - dims[dir])
        self.pad = Pad(dims, pad)
        self.dims = dims
        self.dir = dir
        self.dimsd = list(dims)
        self.dimsd[self.dir] = ndimpow2

        # apply transform to find out slices
        _, self.sl = \
            pywt.coeffs_to_array(pywt.wavedecn(np.ones(self.dimsd),
                                               wavelet=wavelet,
                                               level=level,
                                               mode='periodization',
                                               axes=(self.dir,)),
                                 axes=(self.dir,))

        self.wavelet = wavelet
        self.waveletadj = _adjointwavelet(wavelet)
        self.level = level
        self.reshape = True if len(self.dims) > 1 else False
        self.shape = (int(np.prod(self.dimsd)), int(np.prod(self.dims)))
        self.dtype = np.dtype(dtype)
        self.explicit = False

    def _matvec(self, x):
        x = self.pad.matvec(x)
        if self.reshape:
            x = np.reshape(x, self.dimsd)
        y = pywt.coeffs_to_array(pywt.wavedecn(x,
                                               wavelet=self.wavelet,
                                               level=self.level,
                                               mode='periodization',
                                               axes=(self.dir, )),
                                 axes=(self.dir, ))[0]
        return y.ravel()

    def _rmatvec(self, x):
        if self.reshape:
            x = np.reshape(x, self.dimsd)
        x = pywt.array_to_coeffs(x, self.sl, output_format='wavedecn')
        y = pywt.waverecn(x,
                          wavelet=self.waveletadj,
                          mode='periodization',
                          axes=(self.dir, ))
        y = self.pad.rmatvec(y.ravel())
        return y
コード例 #2
0
class Seislet(LinearOperator):
    r"""Two dimensional Seislet operator.

    Apply 2D-Seislet Transform to an input array given an
    estimate of its local ``slopes``. In forward mode, the input array is
    reshaped into a two-dimensional array of size :math:`n_x \times n_t` and
    the transform is performed along the first (spatial) axis (see Notes for
    more details).

    Parameters
    ----------
    slopes : :obj:`numpy.ndarray`
        Slope field of size :math:`n_x \times n_t`
    sampling : :obj:`tuple`, optional
        Sampling steps in x- and t-axis.
    level : :obj:`int`, optional
        Number of scaling levels (must be >=0).
    kind : :obj:`str`, optional
        Basis function used for predict and update steps: ``haar`` or
        ``linear``.
    inv : :obj:`int`, optional
        Apply inverse transform when invoking the adjoint (``True``)
        or not (``False``). Note that in some scenario it may be more
        appropriate to use the exact inverse as adjoint of the Seislet
        operator even if this is not an orthogonal operator and the dot-test
        would not be satisfied (see Notes for details). Otherwise, the user
        can access the inverse directly as method of this class.
    dtype : :obj:`str`, optional
        Type of elements in input array.

    Attributes
    ----------
    shape : :obj:`tuple`
        Operator shape
    explicit : :obj:`bool`
        Operator contains a matrix that can be solved explicitly
        (True) or not (False)

    Raises
    ------
    NotImplementedError
        If ``kind`` is different from haar or linear
    ValueError
        If ``sampling`` has more or less than two elements.

    Notes
    -----
    The Seislet transform [1]_ is implemented using the lifting scheme.

    In its simplest form (i.e., corresponding to the Haar basis function for
    the Wavelet transform) the input dataset is separated into even
    (:math:`\mathbf{e}`) and odd (:math:`\mathbf{o}`) traces. Even traces are
    used to forward predict the odd traces using local slopes and the
    new odd traces (also referred to as residual) is defined as:

    .. math::
        \mathbf{o}^{i+1} = \mathbf{r}^i = \mathbf{o}^i - P(\mathbf{e}^i)

    where :math:`P = P^+` is the slope-based forward prediction operator
    (which is here implemented as a sinc-based resampling).
    The residual is then updated and summed to the even traces to obtain the
    new even traces (also referred to as coarse representation):

    .. math::
        \mathbf{e}^{i+1} = \mathbf{c}^i = \mathbf{e}^i + U(\mathbf{o}^{i+1})

    where :math:`U = P^- / 2` is the update operator which performs a
    slope-based backward prediction. At this point
    :math:`\mathbf{e}^{i+1}` becomes the new data and the procedure is repeated
    `level` times (at maximum until :math:`\mathbf{e}^{i+1}` is a single trace.
    The Seislet transform is effectively composed of all residuals and
    the coarsest data representation.

    In the inverse transform the two operations are reverted. Starting from the
    coarsest scale data representation :math:`\mathbf{c}` and residual
    :math:`\mathbf{r}`, the even and odd parts of the previous scale are
    reconstructed as:

    .. math::
        \mathbf{e}^i = \mathbf{c}^i - U(\mathbf{r}^i)
        = \mathbf{e}^{i+1} - U(\mathbf{o}^{i+1})

    and:

    .. math::
        \mathbf{o}^i  = \mathbf{r}^i + P(\mathbf{e}^i)
        = \mathbf{o}^{i+1} + P(\mathbf{e}^i)

    A new data is formed by interleaving :math:`\mathbf{e}^i` and
    :math:`\mathbf{o}^i` and the procedure repeated until the new data as the
    same number of traces as the original one.

    Finally the adjoint operator can be easily derived by writing the lifting
    scheme in a matricial form:

    .. math::
        \begin{bmatrix}
           \mathbf{r}_1  \\ \mathbf{r}_2  \\ ... \\ \mathbf{r}_N \\
           \mathbf{c}_1  \\ \mathbf{c}_2  \\ ... \\ \mathbf{c}_N
        \end{bmatrix} =
        \begin{bmatrix}
           \mathbf{I} & \mathbf{0} & ... & \mathbf{0} & -\mathbf{P} & \mathbf{0}  & ... & \mathbf{0}  \\
           \mathbf{0} & \mathbf{I} & ... & \mathbf{0} & \mathbf{0}  & -\mathbf{P} & ... & \mathbf{0}  \\
           ...        & ...        & ... & ...        & ...         & ...         & ... & ...         \\
           \mathbf{0} & \mathbf{0} & ... & \mathbf{I} & \mathbf{0}  & \mathbf{0}  & ... & -\mathbf{P} \\
           \mathbf{U} & \mathbf{0} & ... & \mathbf{0} & \mathbf{I}-\mathbf{UP} & \mathbf{0}  & ... & \mathbf{0}  \\
           \mathbf{0} & \mathbf{U} & ... & \mathbf{0} & \mathbf{0}  & \mathbf{I}-\mathbf{UP} & ... & \mathbf{0}  \\
           ...        & ...        & ... & ...        & ...         & ...         & ... & ...         \\
           \mathbf{0} & \mathbf{0} & ... & \mathbf{U} & \mathbf{0}  & \mathbf{0}  & ... & \mathbf{I}-\mathbf{UP} \\
        \end{bmatrix}
        \begin{bmatrix}
           \mathbf{o}_1  \\ \mathbf{o}_2  \\ ... \\ \mathbf{o}_N \\
           \mathbf{e}_1  \\ \mathbf{e}_2  \\ ... \\ \mathbf{e}_N \\
        \end{bmatrix}

    Transposing the operator leads to:

    .. math::
        \begin{bmatrix}
           \mathbf{o}_1  \\ \mathbf{o}_2  \\ ... \\ \mathbf{o}_N \\
           \mathbf{e}_1  \\ \mathbf{e}_2  \\ ... \\ \mathbf{e}_N \\
        \end{bmatrix} =
        \begin{bmatrix}
           \mathbf{I} & \mathbf{0} & ... & \mathbf{0} & -\mathbf{U^T} & \mathbf{0}  & ... & \mathbf{0}  \\
           \mathbf{0} & \mathbf{I} & ... & \mathbf{0} & \mathbf{0} & -\mathbf{U^T} & ... & \mathbf{0}  \\
           ...        & ...        & ... & ...        & ...        & ...        & ... & ...         \\
           \mathbf{0} & \mathbf{0} & ... & \mathbf{I} & \mathbf{0} & \mathbf{0} & ... & -\mathbf{U^T} \\
           \mathbf{P^T} & \mathbf{0} & ... & \mathbf{0} & \mathbf{I}-\mathbf{P^TU^T} & \mathbf{0} & ... & \mathbf{0}  \\
           \mathbf{0} & \mathbf{P^T} & ... & \mathbf{0} & \mathbf{0} & \mathbf{I}-\mathbf{P^TU^T} & ... & \mathbf{0}  \\
           ...        & ...        & ... & ...          & ...        & ...        & ... & ...         \\
           \mathbf{0} & \mathbf{0} & ... & \mathbf{P^T} & \mathbf{0} & \mathbf{0} & ... & \mathbf{I}-\mathbf{P^TU^T} \\
        \end{bmatrix}
        \begin{bmatrix}
           \mathbf{r}_1  \\ \mathbf{r}_2  \\ ... \\ \mathbf{r}_N \\
           \mathbf{c}_1  \\ \mathbf{c}_2  \\ ... \\ \mathbf{c}_N
        \end{bmatrix}

    which can be written more easily in the following two steps:

    .. math::
        \mathbf{o} = \mathbf{r} + \mathbf{U}^H\mathbf{c}

    and:

    .. math::
        \mathbf{e} = \mathbf{c} - \mathbf{P}^H(\mathbf{r} + \mathbf{U}^H(\mathbf{c})) =
                     \mathbf{c} - \mathbf{P}^H\mathbf{o}

    Similar derivations follow for more complex wavelet bases.

    .. [1] Fomel, S.,  Liu, Y., "Seislet transform and seislet frame",
       Geophysics, 75, no. 3, V25-V38. 2010.

    """
    def __init__(self,
                 slopes,
                 sampling=(1., 1.),
                 level=None,
                 kind='haar',
                 inv=False,
                 dtype='float64'):
        if len(sampling) != 2:
            raise ValueError('provide two sampling steps')

        # define predict and update steps
        if kind == 'haar':
            self.predict = _predict_haar
        elif kind == 'linear':
            self.predict = _predict_lin
        else:
            raise NotImplementedError('kind should be haar or linear')

        # define padding for length to be power of 2
        dims = slopes.shape
        ndimpow2 = 2**ceil(log(dims[0], 2))
        pad = [(0, 0)] * len(dims)
        pad[0] = (0, ndimpow2 - dims[0])
        self.pad = Pad(dims, pad)
        self.dims = list(dims)
        self.dims[0] = ndimpow2
        self.nx, self.nt = self.dims

        # define levels
        nlevels_max = int(np.log2(self.dims[0]))
        self.levels_size = np.flip(np.array([2**i
                                             for i in range(nlevels_max)]))
        if level is not None:
            self.levels_size = self.levels_size[:level]
        else:
            self.levels_size = self.levels_size[:-1]
            level = nlevels_max - 1
        self.level = level
        self.levels_cum = np.cumsum(self.levels_size)
        self.levels_cum = np.insert(self.levels_cum, 0, 0)

        self.dx, self.dt = sampling
        self.slopes = (self.pad * slopes.ravel()).reshape(self.dims)
        self.inv = inv
        self.shape = (int(np.prod(self.slopes.size)),
                      int(np.prod(slopes.size)))
        self.dtype = np.dtype(dtype)
        self.explicit = False

    def _matvec(self, x):
        x = self.pad.matvec(x)
        x = np.reshape(x, self.dims)
        y = np.zeros(
            (np.sum(self.levels_size) + self.levels_size[-1], self.nt))
        for ilevel in range(self.level):
            odd = x[1::2]
            even = x[::2]
            res = odd - self.predict(even,
                                     self.dt,
                                     self.dx,
                                     self.slopes,
                                     repeat=ilevel,
                                     backward=False)
            x = even + self.predict(res,
                                    self.dt,
                                    self.dx,
                                    self.slopes,
                                    repeat=ilevel,
                                    backward=True) / 2.
            y[self.levels_cum[ilevel]:self.levels_cum[ilevel + 1]] = res
        y[self.levels_cum[-1]:] = x
        return y.ravel()

    def _rmatvec(self, x):
        if not self.inv:
            x = np.reshape(x, self.dims)
            y = x[self.levels_cum[-1]:]
            for ilevel in range(self.level, 0, -1):
                res = x[self.levels_cum[ilevel - 1]:self.levels_cum[ilevel]]
                odd = res + self.predict(y,
                                         self.dt,
                                         self.dx,
                                         self.slopes,
                                         repeat=ilevel - 1,
                                         backward=True,
                                         adj=True) / 2.
                even = y - self.predict(odd,
                                        self.dt,
                                        self.dx,
                                        self.slopes,
                                        repeat=ilevel - 1,
                                        backward=False,
                                        adj=True)
                y = np.zeros((2 * even.shape[0], self.nt))
                y[1::2] = odd
                y[::2] = even
            y = self.pad.rmatvec(y.ravel())
        else:
            y = self.inverse(x)
        return y

    def inverse(self, x):
        x = np.reshape(x, self.dims)
        y = x[self.levels_cum[-1]:]
        for ilevel in range(self.level, 0, -1):
            res = x[self.levels_cum[ilevel - 1]:self.levels_cum[ilevel]]
            even = y - self.predict(res,
                                    self.dt,
                                    self.dx,
                                    self.slopes,
                                    repeat=ilevel - 1,
                                    backward=True) / 2.
            odd = res + self.predict(even,
                                     self.dt,
                                     self.dx,
                                     self.slopes,
                                     repeat=ilevel - 1,
                                     backward=False)
            y = np.zeros((2 * even.shape[0], self.nt))
            y[1::2] = odd
            y[::2] = even
        y = self.pad.rmatvec(y.ravel())
        return y
コード例 #3
0
class DWT2D(LinearOperator):
    """Two dimensional Wavelet operator.

    Apply 2D-Wavelet Transform along two directions ``dirs`` of a
    multi-dimensional array of size ``dims``.

    Note that the Wavelet operator is an overload of the ``pywt``
    implementation of the wavelet transform. Refer to
    https://pywavelets.readthedocs.io for a detailed description of the
    input parameters.

    Parameters
    ----------
    dims : :obj:`tuple`
        Number of samples for each dimension
    dirs : :obj:`tuple`, optional
        Direction along which DWT2D is applied.
    wavelet : :obj:`str`, optional
        Name of wavelet type. Use :func:`pywt.wavelist(kind='discrete')` for
        a list of available wavelets.
    level : :obj:`int`, optional
        Number of scaling levels (must be >=0).
    dtype : :obj:`str`, optional
        Type of elements in input array.

    Attributes
    ----------
    shape : :obj:`tuple`
        Operator shape
    explicit : :obj:`bool`
        Operator contains a matrix that can be solved explicitly
        (``True``) or not (``False``)

    Raises
    ------
    ModuleNotFoundError
        If ``pywt`` is not installed
    ValueError
        If ``wavelet`` does not belong to ``pywt.families``

    Notes
    -----
    The Wavelet operator applies the 2-dimensional multilevel Discrete
    Wavelet Transform (DWT2) in forward mode and the 2-dimensional multilevel
    Inverse Discrete Wavelet Transform (IDWT2) in adjoint mode.

    """
    def __init__(self,
                 dims,
                 dirs=(0, 1),
                 wavelet="haar",
                 level=1,
                 dtype="float64"):
        if pywt is None:
            raise ModuleNotFoundError("The wavelet operator requires "
                                      "the pywt package t be installed. "
                                      'Run "pip install PyWavelets" or '
                                      '"conda install pywavelets".')
        _checkwavelet(wavelet)

        # define padding for length to be power of 2
        ndimpow2 = [max(2**ceil(log(dims[dir], 2)), 2**level) for dir in dirs]
        pad = [(0, 0)] * len(dims)
        for i, dir in enumerate(dirs):
            pad[dir] = (0, ndimpow2[i] - dims[dir])
        self.pad = Pad(dims, pad)
        self.dims = dims
        self.dirs = dirs
        self.dimsd = list(dims)
        for i, dir in enumerate(dirs):
            self.dimsd[dir] = ndimpow2[i]

        # apply transform once again to find out slices
        _, self.sl = pywt.coeffs_to_array(
            pywt.wavedec2(
                np.ones(self.dimsd),
                wavelet=wavelet,
                level=level,
                mode="periodization",
                axes=self.dirs,
            ),
            axes=self.dirs,
        )
        self.wavelet = wavelet
        self.waveletadj = _adjointwavelet(wavelet)
        self.level = level
        self.shape = (int(np.prod(self.dimsd)), int(np.prod(self.dims)))
        self.dtype = np.dtype(dtype)
        self.explicit = False

    def _matvec(self, x):
        x = self.pad.matvec(x)
        x = np.reshape(x, self.dimsd)
        y = pywt.coeffs_to_array(
            pywt.wavedec2(
                x,
                wavelet=self.wavelet,
                level=self.level,
                mode="periodization",
                axes=self.dirs,
            ),
            axes=(self.dirs),
        )[0]
        return y.ravel()

    def _rmatvec(self, x):
        x = np.reshape(x, self.dimsd)
        x = pywt.array_to_coeffs(x, self.sl, output_format="wavedec2")
        y = pywt.waverec2(x,
                          wavelet=self.waveletadj,
                          mode="periodization",
                          axes=self.dirs)
        y = self.pad.rmatvec(y.ravel())
        return y