예제 #1
0
def Radon3D(taxis, hyaxis, hxaxis, pyaxis, pxaxis, kind='linear',
            centeredh=True, interp=True, onthefly=False,
            engine='numpy', dtype='float64'):
    r"""Three dimensional Radon transform.

    Apply three dimensional Radon forward (and adjoint) transform to a
    3-dimensional array of size :math:`[n_{py} \times n_{px} \times n_t]`
    (and :math:`[n_y \times n_x \times n_t]`).

    In forward mode this entails to spreading the model vector
    along parametric curves (lines, parabolas, or hyperbolas depending on the
    choice of ``kind``), while  stacking values in the data vector
    along the same parametric curves is performed in adjoint mode.

    Parameters
    ----------
    taxis : :obj:`np.ndarray`
        Time axis
    hxaxis : :obj:`np.ndarray`
        Fast patial axis
    hyaxis : :obj:`np.ndarray`
        Slow spatial axis
    pyaxis : :obj:`np.ndarray`
        Axis of scanning variable :math:`p_y` of parametric curve
    pxaxis : :obj:`np.ndarray`
        Axis of scanning variable :math:`p_x` of parametric curve
    kind : :obj:`str`, optional
        Curve to be used for stacking/spreading (``linear``, ``parabolic``,
        and ``hyperbolic`` are currently supported)
    centeredh : :obj:`bool`, optional
        Assume centered spatial axis (``True``) or not (``False``)
    interp : :obj:`bool`, optional
        Apply linear interpolation (``True``) or nearest interpolation
        (``False``) during stacking/spreading along parametric curve
    onthefly : :obj:`bool`, optional
        Compute stacking parametric curves on-the-fly as part of forward
        and adjoint modelling (``True``) or at initialization and store them
        in look-up table (``False``). Using a look-up table is computationally
        more efficient but increases the memory burden
    engine : :obj:`str`, optional
        Engine used for computation (``numpy`` or ``numba``)
    dtype : :obj:`str`, optional
        Type of elements in input array.

    Returns
    -------
    r3op : :obj:`pylops.LinearOperator`
        Radon operator

    Raises
    ------
    KeyError
        If ``engine`` is neither ``numpy`` nor ``numba``
    NotImplementedError
        If ``kind`` is not ``linear``, ``parabolic``, or ``hyperbolic``

    See Also
    --------
    pylops.signalprocessing.Radon2D: Two dimensional Radon transform
    pylops.Spread: Spread operator

    Notes
    -----
    The Radon3D operator applies the following linear transform in adjoint mode
    to the data after reshaping it into a 3-dimensional array of
    size :math:`[n_y \times n_x \times n_t]` in adjoint mode:

    .. math::
        m(p_y, p_x, t_0) = \int{d(y, x, t = f(p_y, p_x, y, x, t))} dx dy

    where :math:`f(p_y, p_x, y, x, t) = t_0 + p_y * y + p_x * x` in linear
    mode, :math:`f(p_y, p_x, y, x, t) = t_0 + p_y * y^2 + p_x * x^2` in
    parabolic mode, and
    :math:`f(p_y, p_x, y, x, t) = \sqrt{t_0^2 + y^2 / p_y^2 + x^2 / p_x^2}`
    in hyperbolic mode. Note that internally the :math:`p_x` and :math:`p_y`
    axes will be normalized by the ratio of the spatial and time axes and
    used alongside unitless axes. Whilst this makes the linear mode fully
    unitless, users are required to apply additional scalings to the :math:`p_x`
    axis for other relationships (e.g., :math:`p_x` should be pre-multipled by
    :math:`(d_t/d_x)^2` for the hyperbolic relationship).

    As the adjoint operator can be interpreted as a repeated summation of sets
    of elements of the model vector along chosen parametric curves, the
    forward is implemented as spreading of values in the data vector along the
    same parametric curves. This operator is actually a thin wrapper around
    the :class:`pylops.Spread` operator.

    """
    # engine
    if not engine in ['numpy', 'numba']:
        raise KeyError('engine must be numpy or numba')
    if engine == 'numba' and jit is None:
        engine = 'numpy'

    # axes
    nt, nhy, nhx = taxis.size, hyaxis.size, hxaxis.size
    npy, npx = pyaxis.size, pxaxis.size
    if kind == 'linear':
        f = _linear if engine == 'numpy' else _linear_numba
    elif kind == 'parabolic':
        f = _parabolic if engine == 'numpy' else _parabolic_numba
    elif kind == 'hyperbolic':
        f = _hyperbolic if engine == 'numpy' else _hyperbolic_numba
    else:
        raise NotImplementedError('kind must be linear, '
                                  'parabolic, or hyperbolic...')
    # make axes unitless
    dpy = (np.abs(hyaxis[1] - hyaxis[0]) /
           np.abs(taxis[1] - taxis[0]))
    pyaxis = pyaxis * dpy
    hyaxisunitless = np.arange(nhy)
    dpx = (np.abs(hxaxis[1] - hxaxis[0]) /
           np.abs(taxis[1] - taxis[0]))
    pxaxis = pxaxis * dpx
    hxaxisunitless = np.arange(nhx)
    if centeredh:
        hyaxisunitless -= nhy // 2
        hxaxisunitless -= nhx // 2

    # create grid for py and px axis
    hyaxisunitless, hxaxisunitless = \
        np.meshgrid(hyaxisunitless, hxaxisunitless, indexing='ij')
    pyaxis, pxaxis = np.meshgrid(pyaxis, pxaxis, indexing='ij')

    dims = (npy*npx, nt)
    dimsd = (nhy*nhx, nt)

    if onthefly:
        if engine == 'numba':
            @jit(nopython=True, nogil=True)
            def ontheflyfunc(x, y):
                return _indices_3d_onthefly_numba(f, hyaxisunitless.ravel(),
                                                  hxaxisunitless.ravel(),
                                                  pyaxis.ravel(),
                                                  pxaxis.ravel(),
                                                  x, y, nt, interp=interp)[1:]
        else:
            if interp:
                ontheflyfunc = \
                    lambda x, y: _indices_3d_onthefly(f,
                                                      hyaxisunitless.ravel(),
                                                      hxaxisunitless.ravel(),
                                                      pyaxis.ravel(),
                                                      pxaxis.ravel(),
                                                      x, y, nt, interp=interp)[1:]
            else:
                ontheflyfunc = \
                    lambda x, y: _indices_3d_onthefly(f,
                                                      hyaxisunitless.ravel(),
                                                      hxaxisunitless.ravel(),
                                                      pyaxis.ravel(),
                                                      pxaxis.ravel(),
                                                      x, y, nt, interp=interp)[1]
        r3op = Spread(dims, dimsd, fh=ontheflyfunc, interp=interp,
                      engine=engine, dtype=dtype)
    else:
        if engine == 'numba':
            tablefunc = _create_table_numba
        else:
            tablefunc = _create_table

        table, dtable = tablefunc(f, hyaxisunitless.ravel(),
                                  hxaxisunitless.ravel(),
                                  pyaxis.ravel(), pxaxis.ravel(),
                                  nt, npy, npx, nhy, nhx, interp=interp)
        if not interp:
            dtable = None
        r3op = Spread(dims, dimsd, table=table,
                      dtable=dtable, interp=interp,
                      engine=engine, dtype=dtype)
    return r3op
예제 #2
0
def Radon2D(taxis,
            haxis,
            pxaxis,
            kind='linear',
            centeredh=True,
            interp=True,
            onthefly=False,
            engine='numpy',
            dtype='float64'):
    r"""Two dimensional Radon transform.

    Apply two dimensional Radon forward (and adjoint) transform to a
    2-dimensional array of size :math:`[n_{px} \times n_t]`
    (and :math:`[n_x \times n_t]`).

    In forward mode this entails to spreading the model vector
    along parametric curves (lines, parabolas, or hyperbolas depending on the
    choice of ``kind``), while  stacking values in the data vector
    along the same parametric curves is performed in adjoint mode.

    Parameters
    ----------
    taxis : :obj:`np.ndarray`
        Time axis
    haxis : :obj:`np.ndarray`
        Spatial axis
    pxaxis : :obj:`np.ndarray`
        Axis of scanning variable :math:`p_x` of parametric curve
    kind : :obj:`str`, optional
        Curve to be used for stacking/spreading (``linear``, ``parabolic``,
        and ``hyperbolic`` are currently supported)
    centeredh : :obj:`bool`, optional
        Assume centered spatial axis (``True``) or not (``False``)
    interp : :obj:`bool`, optional
        Apply linear interpolation (``True``) or nearest interpolation
        (``False``) during stacking/spreading along parametric curve
    onthefly : :obj:`bool`, optional
        Compute stacking parametric curves on-the-fly as part of forward
        and adjoint modelling (``True``) or at initialization and store them
        in look-up table (``False``). Using a look-up table is computationally
        more efficient but increases the memory burden
    engine : :obj:`str`, optional
        Engine used for computation (``numpy`` or ``numba``)
    dtype : :obj:`str`, optional
        Type of elements in input array.

    Returns
    -------
    r2op : :obj:`pylops.LinearOperator`
        Radon operator

    Raises
    ------
    KeyError
        If ``engine`` is neither ``numpy`` nor ``numba``
    NotImplementedError
        If ``kind`` is not ``linear``, ``parabolic``, or ``hyperbolic``

    See Also
    --------
    pylops.signalprocessing.Radon3D: Three dimensional Radon transform
    pylops.Spread: Spread operator

    Notes
    -----
    The Radon2D operator applies the following linear transform in adjoint mode
    to the data after reshaping it into a 2-dimensional array of
    size :math:`[n_x \times n_t]` in adjoint mode:

    .. math::
        m(p_x, t_0) = \int{d(x, t = f(p_x, x, t))} dx

    where :math:`f(p_x, x, t) = t_0 + p_x * x` where
    :math:`p_x = sin( \theta)/v` in linear mode,
    :math:`f(p_x, x, t) = t_0 + p_x * x^2` in parabolic mode, and
    :math:`f(p_x, x, t) = \sqrt{t_0^2 + x^2 / p_x^2}` in hyperbolic mode.

    As the adjoint operator can be interpreted as a repeated summation of sets
    of elements of the model vector along chosen parametric curves, the
    forward is implemented as spreading of values in the data vector along the
    same parametric curves. This operator is actually a thin wrapper around
    the :class:`pylops.Spread` operator.
    """
    # engine
    if not engine in ['numpy', 'numba']:
        raise KeyError('engine must be numpy or numba')
    if engine == 'numba' and jit is None:
        engine = 'numpy'
    # axes
    nt, nh, npx = taxis.size, haxis.size, pxaxis.size
    if kind == 'linear':
        f = _linear if engine == 'numpy' else _linear_numba
    elif kind == 'parabolic':
        f = _parabolic if engine == 'numpy' else _parabolic_numba
    elif kind == 'hyperbolic':
        f = _hyperbolic if engine == 'numpy' else _hyperbolic_numba
    else:
        raise NotImplementedError('kind must be linear, '
                                  'parabolic, or hyperbolic...')
    # make axes unitless
    dpx = (np.abs(haxis[1] - haxis[0]) / np.abs(taxis[1] - taxis[0]))
    pxaxis = pxaxis * dpx
    haxisunitless = np.arange(nh)
    if centeredh:
        haxisunitless -= nh // 2
    dims = (npx, nt)
    dimsd = (nh, nt)

    if onthefly:
        if engine == 'numba':

            @jit(nopython=True, nogil=True)
            def ontheflyfunc(x, y):
                return _indices_2d_onthefly_numba(f,
                                                  haxisunitless,
                                                  pxaxis,
                                                  x,
                                                  y,
                                                  nt,
                                                  interp=interp)[1:]
        else:
            if interp:
                ontheflyfunc = \
                    lambda x, y : _indices_2d_onthefly(f, haxisunitless,
                                                       pxaxis, x, y, nt,
                                                       interp=interp)[1:]
            else:
                ontheflyfunc = \
                    lambda x, y: _indices_2d_onthefly(f, haxisunitless,
                                                      pxaxis, x, y, nt,
                                                      interp=interp)[1]
        r2op = Spread(dims,
                      dimsd,
                      fh=ontheflyfunc,
                      interp=interp,
                      engine=engine,
                      dtype=dtype)
    else:
        if engine == 'numba':
            tablefunc = _create_table_numba
        else:
            tablefunc = _create_table

        table, dtable = tablefunc(f, haxisunitless, pxaxis, nt, npx, nh,
                                  interp)
        if not interp:
            dtable = None
        r2op = Spread(dims,
                      dimsd,
                      table=table,
                      dtable=dtable,
                      interp=interp,
                      engine=engine,
                      dtype=dtype)
    return r2op
예제 #3
0
파일: Radon2D.py 프로젝트: mrava87/pylops
def Radon2D(
    taxis,
    haxis,
    pxaxis,
    kind="linear",
    centeredh=True,
    interp=True,
    onthefly=False,
    engine="numpy",
    dtype="float64",
):
    r"""Two dimensional Radon transform.

    Apply two dimensional Radon forward (and adjoint) transform to a
    2-dimensional array of size :math:`[n_{p_x} \times n_t]`
    (and :math:`[n_x \times n_t]`).

    In forward mode this entails to spreading the model vector
    along parametric curves (lines, parabolas, or hyperbolas depending on the
    choice of ``kind``), while stacking values in the data vector
    along the same parametric curves is performed in adjoint mode.

    Parameters
    ----------
    taxis : :obj:`np.ndarray`
        Time axis
    haxis : :obj:`np.ndarray`
        Spatial axis
    pxaxis : :obj:`np.ndarray`
        Axis of scanning variable :math:`p_x` of parametric curve
    kind : :obj:`str`, optional
        Curve to be used for stacking/spreading (``linear``, ``parabolic``,
        and ``hyperbolic`` are currently supported) or a function that takes
        :math:`(x, t_0, p_x)` as input and returns :math:`t` as output
    centeredh : :obj:`bool`, optional
        Assume centered spatial axis (``True``) or not (``False``). If ``True``
        the original ``haxis`` is ignored and a new axis is created.
    interp : :obj:`bool`, optional
        Apply linear interpolation (``True``) or nearest interpolation
        (``False``) during stacking/spreading along parametric curve
    onthefly : :obj:`bool`, optional
        Compute stacking parametric curves on-the-fly as part of forward
        and adjoint modelling (``True``) or at initialization and store them
        in look-up table (``False``). Using a look-up table is computationally
        more efficient but increases the memory burden
    engine : :obj:`str`, optional
        Engine used for computation (``numpy`` or ``numba``)
    dtype : :obj:`str`, optional
        Type of elements in input array.

    Returns
    -------
    r2op : :obj:`pylops.LinearOperator`
        Radon operator

    Raises
    ------
    KeyError
        If ``engine`` is neither ``numpy`` nor ``numba``
    NotImplementedError
        If ``kind`` is not ``linear``, ``parabolic``, or ``hyperbolic``

    See Also
    --------
    pylops.signalprocessing.Radon3D: Three dimensional Radon transform
    pylops.Spread: Spread operator

    Notes
    -----
    The Radon2D operator applies the following linear transform in adjoint mode
    to the data after reshaping it into a 2-dimensional array of
    size :math:`[n_x \times n_t]` in adjoint mode:

    .. math::
        m(p_x, t_0) = \int{d(x, t = f(p_x, x, t))} \,\mathrm{d}x

    where :math:`f(p_x, x, t) = t_0 + p_x x` where
    :math:`p_x = \sin(\theta)/v` in linear mode,
    :math:`f(p_x, x, t) = t_0 + p_x x^2` in parabolic mode, and
    :math:`f(p_x, x, t) = \sqrt{t_0^2 + x^2 / p_x^2}` in hyperbolic mode. Note
    that internally the :math:`p_x` axis will be normalized by the ratio of the
    spatial and time axes and used alongside unitless axes. Whilst this makes
    the linear mode fully unitless, users are required to apply additional
    scalings to the :math:`p_x` axis for other relationships:

    - :math:`p_x` should be pre-multipled by :math:`d_x` for the parabolic
      relationship;
    - :math:`p_x` should be pre-multipled by :math:`(d_t/d_x)^2` for the
      hyperbolic relationship.

    As the adjoint operator can be interpreted as a repeated summation of sets
    of elements of the model vector along chosen parametric curves, the
    forward is implemented as spreading of values in the data vector along the
    same parametric curves. This operator is actually a thin wrapper around
    the :class:`pylops.Spread` operator.

    """
    # engine
    if engine not in ["numpy", "numba"]:
        raise KeyError("engine must be numpy or numba")
    if engine == "numba" and jit is None:
        engine = "numpy"
    # axes
    nt, nh, npx = taxis.size, haxis.size, pxaxis.size
    if kind == "linear":
        f = _linear if engine == "numpy" else _linear_numba
    elif kind == "parabolic":
        f = _parabolic if engine == "numpy" else _parabolic_numba
    elif kind == "hyperbolic":
        f = _hyperbolic if engine == "numpy" else _hyperbolic_numba
    elif callable(kind):
        f = kind
    else:
        raise NotImplementedError("kind must be linear, "
                                  "parabolic, or hyperbolic...")
    # make axes unitless
    dh, dt = np.abs(haxis[1] - haxis[0]), np.abs(taxis[1] - taxis[0])
    dpx = dh / dt
    pxaxis = pxaxis * dpx
    if not centeredh:
        haxisunitless = haxis // dh
    else:
        haxisunitless = np.arange(nh) - nh // 2
    dims = (npx, nt)
    dimsd = (nh, nt)

    if onthefly:
        if engine == "numba":

            @jit(nopython=True, nogil=True)
            def ontheflyfunc(x, y):
                return _indices_2d_onthefly_numba(f,
                                                  haxisunitless,
                                                  pxaxis,
                                                  x,
                                                  y,
                                                  nt,
                                                  interp=interp)[1:]

        else:
            if interp:

                def ontheflyfunc(x, y):
                    return _indices_2d_onthefly(f,
                                                haxisunitless,
                                                pxaxis,
                                                x,
                                                y,
                                                nt,
                                                interp=interp)[1:]

            else:

                def ontheflyfunc(x, y):
                    return _indices_2d_onthefly(f,
                                                haxisunitless,
                                                pxaxis,
                                                x,
                                                y,
                                                nt,
                                                interp=interp)[1]

        r2op = Spread(dims,
                      dimsd,
                      fh=ontheflyfunc,
                      interp=interp,
                      engine=engine,
                      dtype=dtype)
    else:
        if engine == "numba":
            tablefunc = _create_table_numba
        else:
            tablefunc = _create_table

        table, dtable = tablefunc(f, haxisunitless, pxaxis, nt, npx, nh,
                                  interp)
        if not interp:
            dtable = None
        r2op = Spread(
            dims,
            dimsd,
            table=table,
            dtable=dtable,
            interp=interp,
            engine=engine,
            dtype=dtype,
        )
    return r2op