Exemple #1
0
    def __init__(self, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT):
        try:
            self.biort = _biort(biort)
        except TypeError:
            self.biort = biort

        # Load quarter sample shift wavelets
        try:
            self.qshift = _qshift(qshift)
        except TypeError:
            self.qshift = qshift
Exemple #2
0
    def __init__(self, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT):
        # Load bi-orthogonal wavelets
        try:
            self.biort = _biort(biort)
        except TypeError:
            self.biort = biort

        # Load quarter sample shift wavelets
        try:
            self.qshift = _qshift(qshift)
        except TypeError:
            self.qshift = qshift
Exemple #3
0
    def __init__(self, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, ext_mode=4):
        # Load bi-orthogonal wavelets
        try:
            self.biort = _biort(biort)
        except TypeError:
            self.biort = biort

        # Load quarter sample shift wavelets
        try:
            self.qshift = _qshift(qshift)
        except TypeError:
            self.qshift = qshift

        self.ext_mode = ext_mode
Exemple #4
0
    def __init__(self, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, ext_mode=4, discard_level_1=False):
        """
        Constructor for the 3D DT-CWT transform class (NumPy).
        :param biort: Level 1 wavelets to use. See :py:func:`dtcwt.coeffs.biort`.
        :param qshift: Level >= 2 wavelets to use. See :py:func:`dtcwt.coeffs.qshift`.
        :param discard_level_1: True if level 1 high-pass bands are to be discarded.
        """
        # Load bi-orthogonal wavelets
        try:
            self.biort = _biort(biort)
        except TypeError:
            self.biort = biort

        # Load quarter sample shift wavelets
        try:
            self.qshift = _qshift(qshift)
        except TypeError:
            self.qshift = qshift

        self.ext_mode = ext_mode
        self.discard_level_1 = discard_level_1
    def forward(self, X, nlevels=3, include_scale=False):
        """Perform a *n*-level DTCWT decompostion on a 1D column vector *X* (or on
        the columns of a matrix *X*).

        :param X: 1D real array or 2D real array whose columns are to be transformed
        :param nlevels: Number of levels of wavelet decomposition

        :returns: A :py:class:`DTCWT.Pyramid`-like object representing the transform result.

        If *biort* or *qshift* are strings, they are used as an argument to the
        :py:func:`biort` or :py:func:`qshift` functions. Otherwise, they are
        interpreted as tuples of vectors giving filter coefficients. In the *biort*
        case, this should be (h0o, g0o, h1o, g1o). In the *qshift* case, this should
        be (h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b).

        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002
        .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002

        """
        # Which wavelets are to be used?
        biort = self.biort
        qshift = self.qshift

        # Need this because colfilter and friends assumes input is 2d
        X = asfarray(X)
        if len(X.shape) == 1:
            X = np.atleast_2d(X).T

        # Try to load coefficients if biort is a string parameter
        try:
            h0o, g0o, h1o, g1o = _biort(biort)
        except TypeError:
            h0o, g0o, h1o, g1o = biort

        # Try to load coefficients if qshift is a string parameter
        try:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = _qshift(qshift)
        except TypeError:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = qshift

        L = np.asanyarray(X.shape)

        # ensure that X is an even length, thus enabling it to be extended if needs be.
        if X.shape[0] % 2 != 0:
            raise ValueError('Size of input X must be a multiple of 2')

        if nlevels == 0:
            if include_scale:
                return Pyramid(X, (), ())
            else:
                return Pyramid(X, ())

        # initialise
        Yh = [
            None,
        ] * nlevels
        if include_scale:
            # This is only required if the user specifies scales are to be outputted
            Yscale = [
                None,
            ] * nlevels

        # Level 1.
        Hi = colfilter(X, h1o)
        Lo = colfilter(X, h0o)
        Yh[0] = Hi[::2, :] + 1j * Hi[1::2, :]  # Convert Hi to complex form.
        if include_scale:
            Yscale[0] = Lo

        # Levels 2 and above.
        for level in xrange(1, nlevels):
            # Check to see if height of Lo is divisable by 4, if not extend.
            if Lo.shape[0] % 4 != 0:
                Lo = np.vstack((Lo[0, :], Lo, Lo[-1, :]))

            Hi = coldfilt(Lo, h1b, h1a)
            Lo = coldfilt(Lo, h0b, h0a)

            Yh[level] = Hi[::2, :] + 1j * Hi[
                1::2, :]  # Convert Hi to complex form.
            if include_scale:
                Yscale[level] = Lo

        Yl = Lo

        if include_scale:
            return Pyramid(Yl, Yh, Yscale)
        else:
            return Pyramid(Yl, Yh)
    def inverse_hu(self, pyramid, gain_mask=None):
        """Perform an *n*-level dual-tree complex wavelet (DTCWT) 1D
        reconstruction.

        :param pyramid: A :py:class:`DTCWT.Pyramid`-like object containing the transformed signal.
        :param gain_mask: Gain to be applied to each subband.

        :returns: Reconstructed real array.

        The *l*-th element of *gain_mask* is gain for wavelet subband at level l.
        If gain_mask[l] == 0, no computation is performed for band *l*. Default
        *gain_mask* is all ones. Note that *l* is 0-indexed.

        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002
        .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002

        """
        # Which wavelets are to be used?
        biort = self.biort
        qshift = self.qshift

        Yl = pyramid.lowpass
        Yh = pyramid.highpasses

        a = len(Yh)  # No of levels.

        if gain_mask is None:
            gain_mask = np.ones(a)  # Default gain_mask.

        # Try to load coefficients if biort is a string parameter
        try:
            h0o, g0o, h1o, g1o = _biort(biort)
        except TypeError:
            h0o, g0o, h1o, g1o = biort

        # Try to load coefficients if qshift is a string parameter
        try:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = _qshift(qshift)
        except TypeError:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = qshift

        level = a - 1  # No of levels = no of rows in L.
        if level < 0:
            # if there are no levels in the input, just return the Yl value
            return Yl
        """ 此处可以将 hi Lo 拆开 逐层恢复,考虑到卷积是可拆分的 (x1 + x2) * k = x1 * k + x2 * k """
        low_band_signal = np.copy(Yl)
        high_band_signals = [c2q1d(np.copy(item)) for item in Yh[::-1]]

        for i in range(level):
            low_band_signal = colifilt(low_band_signal, g0b, g0a)
            high_band_signals[i] = colifilt(high_band_signals[i], g1b, g1a)

            if low_band_signal.shape[0] != high_band_signals[i + 1].shape[0]:
                low_band_signal = low_band_signal[1:-1, ...]

            if high_band_signals[i].shape[0] != high_band_signals[i +
                                                                  1].shape[0]:
                high_band_signals[i] = high_band_signals[i][1:-1, ...]

            for j in range(i):
                high_band_signals[j] = colifilt(high_band_signals[j], g0b, g0a)
                if high_band_signals[j].shape[0] != high_band_signals[
                        i + 1].shape[0]:
                    high_band_signals[j] = high_band_signals[j][1:-1, ...]

        low_band_signal = colfilter(low_band_signal, g0o)
        high_band_signals[-1] = colfilter(high_band_signals[-1], g1o)
        for i in range(level):
            high_band_signals[i] = colfilter(high_band_signals[i], g0o)

        return low_band_signal, high_band_signals
Exemple #7
0
def wavelet(x, nlevels, biort='near_sym_b_bp', qshift='qshift_b_bp',
            data_format="nhwc"):
    """ Perform an nlevel dtcwt on the input data.

    Parameters
    ----------
    x: tf tensor of shape (batch, h, w) or (batch, h, w, c)
        The input to be transformed. If the input has a channel dimension, the
        dtcwt will be applied to each of the channels independently.
    nlevels : int
        the number of scales to use. 0 is a special case, if nlevels=0, then we
        return a lowpassed version of the x, and Yh and Yscale will be
        empty lists
    biort : str
        which biorthogonal filters to use. 'near_sym_b_bp' are my favourite, as
        they have 45° and 135° filters with the same period as the others.
    qshift : str
        which quarter shift filters to use. These should match up with the
        biorthogonal used. 'qshift_b_bp' are my favourite for the same reason.
    data_format : str
        An optional string of the form "nchw" or "nhwc" (for 4D data), or "nhw"
        or "hwn" (for 3D data). This specifies the data format of the input.
        E.g. If format is "nchw" (the default), then data is in the form [batch,
        channels, h, w]. If the format is "nhwc", then the data is in the form
        [batch, h, w, c].

    Returns
    -------
    out : a tuple of (lowpass, highpasses and scales).

        * Lowpass is a tensor of the lowpass data. This is a real float.  If
          x has shape [batch, height, width, channels], the dtcwt will be
          applied independently for each channel and combined.
        * Highpasses is a list of length <nlevels>, each entry has the six
          orientations of wavelet coefficients for the given scale. These are
          returned as tf.complex64 data type.
        * Scales is a list of length <nlevels>, each entry containing the
          lowpass signal that gets passed to the next level of the dtcwt
          transform.
    """

    # If nlevels was 0, lowpass the input
    with tf.variable_scope('wavelet'):
        if nlevels == 0:
            Yh, Yscale = [], []
            filters = _biort(biort)
            h0o = np.reshape(filters[0], [1, -1, 1, 1])
            h0oT = np.transpose(h0o, [1, 0, 2, 3])
            Xshape = x.get_shape().as_list()

            # Put the channels to the batch dimension and back after
            X = tf.reshape(x, [-1, Xshape[1], Xshape[2], 1])
            Yl = separable_conv_with_pad(X, h0o, h0oT)
            Yl = tf.reshape(Yl, [-1, Xshape[1], Xshape[2], Xshape[3]])
        else:
            transform = Transform2d(biort=biort, qshift=qshift)
            # Use either forward_channels or forward, depending on the input
            # shape
            noch = len(['batch', 'height', 'width'])
            ch = len(['batch', 'height', 'width', 'channel'])
            l = len(x.get_shape().as_list())
            if l == noch:
                data_format = 'nhw'
                Yl, Yh, Yscale = dtcwt.utils.unpack(
                    transform.forward_channels(
                        x, data_format, nlevels=nlevels, include_scale=True),
                    'tf')

            elif l == ch:
                Yl, Yh, Yscale = dtcwt.utils.unpack(
                    transform.forward_channels(
                        x, data_format, nlevels=nlevels, include_scale=True),
                    'tf')
            else:
                raise ValueError("Unkown length {} for wavelet block".format(l))

    return Yl, _dtcwt_correct_phases(Yh), Yscale
Exemple #8
0
    def _inverse_ops(self, Yl, Yh, gain_mask=None):
        """Perform an *n*-level dual-tree complex wavelet (DTCWT) 1D
        reconstruction.

        :param Yl: The lowpass output from a forward transform. Should be a
            tensorflow variable.
        :param Yh: The tuple of highpass outputs from a forward transform.
            Should be tensorflow variables.
        :param gain_mask: Gain to be applied to each subband.

        :returns: A tf.Variable holding the output

        The *l*-th element of *gain_mask* is gain for wavelet subband at level
        l.  If gain_mask[l] == 0, no computation is performed for band *l*.
        Default *gain_mask* is all ones. Note that *l* is 0-indexed.

        .. codeauthor:: Fergal Cotter <*****@*****.**>, Sep 2017
        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002
        .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002

        """
        # Which wavelets are to be used?
        biort = self.biort
        qshift = self.qshift
        a = len(Yh)  # No of levels.

        if gain_mask is None:
            gain_mask = np.ones(a)  # Default gain_mask.
        gain_mask = np.array(gain_mask)

        # Try to load coefficients if biort is a string parameter
        try:
            h0o, g0o, h1o, g1o = _biort(biort)
        except TypeError:
            h0o, g0o, h1o, g1o = biort

        # Try to load coefficients if qshift is a string parameter
        try:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = _qshift(qshift)
        except TypeError:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = qshift

        level = a-1   # No of levels = no of rows in L.
        if level < 0:
            # if there are no levels in the input, just return the Yl value
            return Yl

        # Reconstruct levels 2 and above in reverse order.
        Lo = Yl
        while level >= 1:
            Hi = c2q1d(Yh[level]*gain_mask[level])
            Lo = colifilt(Lo, g0b, g0a) + colifilt(Hi, g1b, g1a)

            # If Lo is not the same length as the next Therefore we have to clip
            # Lo so it is the same height as the next Yh. Yh => t1 was extended.
            Lo_shape = Lo.get_shape().as_list()
            next_shape = Yh[level-1].get_shape().as_list()
            if Lo_shape[1] != 2 * next_shape[1]:
                Lo = Lo[:,1:-1]
                Lo_shape = Lo.get_shape().as_list()

            # Check the row shapes across the entire matrix
            if (np.any(np.asanyarray(Lo_shape[1:]) !=
                       np.asanyarray(next_shape[1:] * np.array((2,1))))):
                raise ValueError('Yh sizes are not valid for DTWAVEIFM')

            level -= 1

        # Reconstruct level 1.
        if level == 0:
            Hi = c2q1d(Yh[level]*gain_mask[level])
            Z = colfilter(Lo,g0o) + colfilter(Hi,g1o)

        return Z
Exemple #9
0
    def _forward_ops(self, X, nlevels=3):
        """ Perform a *n*-level DTCWT-2D decompostion on a 2D matrix *X*.

        For column inputs, we still need the input shape to be 3D, but with 1 as
        the last dimension.

        :param X: 3D real array of size [batch, h, w]
        :param nlevels: Number of levels of wavelet decomposition
        :param extended: True if a singleton dimension was added at the
            beginning of the input. Signal to remove afterwards.

        :returns: A tuple of Yl, Yh, Yscale
        """
        biort = self.biort
        qshift = self.qshift

        # Try to load coefficients if biort is a string parameter
        try:
            h0o, g0o, h1o, g1o = _biort(biort)
        except TypeError:
            h0o, g0o, h1o, g1o = biort

        # Try to load coefficients if qshift is a string parameter
        try:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = _qshift(qshift)
        except TypeError:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = qshift

        # Check the shape and form of the input
        if X.dtype not in tf_dtypes:
            raise ValueError('X needs to be a tf variable or placeholder')

        original_size = X.get_shape().as_list()[1:]

        # ############################ Resize #################################
        # The next few lines of code check to see if the image is odd in size,
        # if so an extra ... row/column will be added to the bottom/right of the
        # image
        #  initial_row_extend = 0
        #  initial_col_extend = 0
        # If the row count of X is not divisible by 2 then we need to
        # extend X by adding a row at the bottom
        if original_size[0] % 2 != 0:
            #  X = tf.pad(X, [[0, 0], [0, 1], [0, 0]], 'SYMMETRIC')
            raise ValueError('Size of input X must be a multiple of 2')

        #  extended_size = X.get_shape().as_list()[1:]

        if nlevels == 0:
            return X, (), ()

        # ########################### Initialise ###############################
        Yh = [None, ] * nlevels
        # This is only required if the user specifies a third output
        # component.
        Yscale = [None, ] * nlevels

        # ############################ Level 1 #################################
        # Uses the biorthogonal filters
        if nlevels >= 1:
            # Do odd top-level filters on cols.
            Hi = colfilter(X, h1o)
            Lo = colfilter(X, h0o)

            # Convert Hi to complex form by taking alternate rows
            Yh[0] = tf.cast(Hi[:,::2,:], tf.complex64) + \
                1j*tf.cast(Hi[:,1::2,:], tf.complex64)
            Yscale[0] = Lo

        # ############################ Level 2+ ################################
        # Uses the qshift filters
        for level in xrange(1, nlevels):
            # If the row count of Lo is not divisible by 4 (it will be
            # divisible by 2), add 2 extra rows to make it so
            if Lo.get_shape().as_list()[1] % 4 != 0:
                Lo = tf.pad(Lo, [[0, 0], [1, 1], [0, 0]], 'SYMMETRIC')

            # Do even Qshift filters on cols.
            Hi = coldfilt(Lo, h1b, h1a)
            Lo = coldfilt(Lo, h0b, h0a)

            # Convert Hi to complex form by taking alternate rows
            Yh[level] = tf.cast(Hi[:,::2,:], tf.complex64) + \
                1j * tf.cast(Hi[:,1::2,:], tf.complex64)
            Yscale[level] = Lo

        Yl = Lo

        return Yl, tuple(Yh), tuple(Yscale)
Exemple #10
0
    def forward(self, X, nlevels=3, include_scale=False):
        """Perform a *n*-level DTCWT decompostion on a 1D column vector *X* (or on
        the columns of a matrix *X*).

        :param X: 1D real array or 2D real array whose columns are to be transformed
        :param nlevels: Number of levels of wavelet decomposition

        :returns: A :py:class:`dtcwt.Pyramid`-like object representing the transform result.

        If *biort* or *qshift* are strings, they are used as an argument to the
        :py:func:`biort` or :py:func:`qshift` functions. Otherwise, they are
        interpreted as tuples of vectors giving filter coefficients. In the *biort*
        case, this should be (h0o, g0o, h1o, g1o). In the *qshift* case, this should
        be (h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b).

        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002
        .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002

        """
        # Which wavelets are to be used?
        biort = self.biort
        qshift = self.qshift

        # Need this because colfilter and friends assumes input is 2d
        X = asfarray(X)
        if len(X.shape) == 1:
            X = np.atleast_2d(X).T

        # Try to load coefficients if biort is a string parameter
        try:
            h0o, g0o, h1o, g1o = _biort(biort)
        except TypeError:
            h0o, g0o, h1o, g1o = biort

        # Try to load coefficients if qshift is a string parameter
        try:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = _qshift(qshift)
        except TypeError:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = qshift

        L = np.asanyarray(X.shape)

        # ensure that X is an even length, thus enabling it to be extended if needs be.
        if X.shape[0] % 2 != 0:
            raise ValueError("Size of input X must be a multiple of 2")

        if nlevels == 0:
            if include_scale:
                return Pyramid(X, (), ())
            else:
                return Pyramid(X, ())

        # initialise
        Yh = [None] * nlevels
        if include_scale:
            # This is only required if the user specifies scales are to be outputted
            Yscale = [None] * nlevels

        # Level 1.
        Hi = colfilter(X, h1o)
        Lo = colfilter(X, h0o)
        Yh[0] = Hi[::2, :] + 1j * Hi[1::2, :]  # Convert Hi to complex form.
        if include_scale:
            Yscale[0] = Lo

        # Levels 2 and above.
        for level in xrange(1, nlevels):
            # Check to see if height of Lo is divisable by 4, if not extend.
            if Lo.shape[0] % 4 != 0:
                Lo = np.vstack((Lo[0, :], Lo, Lo[-1, :]))

            Hi = coldfilt(Lo, h1b, h1a)
            Lo = coldfilt(Lo, h0b, h0a)

            Yh[level] = Hi[::2, :] + 1j * Hi[1::2, :]  # Convert Hi to complex form.
            if include_scale:
                Yscale[level] = Lo

        Yl = Lo

        if include_scale:
            return Pyramid(Yl, Yh, Yscale)
        else:
            return Pyramid(Yl, Yh)
Exemple #11
0
    def inverse(self, pyramid, gain_mask=None):
        """Perform an *n*-level dual-tree complex wavelet (DTCWT) 1D
        reconstruction.

        :param pyramid: A :py:class:`dtcwt.Pyramid`-like object containing the transformed signal.
        :param gain_mask: Gain to be applied to each subband.

        :returns: Reconstructed real array.

        The *l*-th element of *gain_mask* is gain for wavelet subband at level l.
        If gain_mask[l] == 0, no computation is performed for band *l*. Default
        *gain_mask* is all ones. Note that *l* is 0-indexed.

        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002
        .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002

        """
        # Which wavelets are to be used?
        biort = self.biort
        qshift = self.qshift

        Yl = pyramid.lowpass
        Yh = pyramid.highpasses

        a = len(Yh)  # No of levels.

        if gain_mask is None:
            gain_mask = np.ones(a)  # Default gain_mask.

        # Try to load coefficients if biort is a string parameter
        try:
            h0o, g0o, h1o, g1o = _biort(biort)
        except TypeError:
            h0o, g0o, h1o, g1o = biort

        # Try to load coefficients if qshift is a string parameter
        try:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = _qshift(qshift)
        except TypeError:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = qshift

        level = a - 1  # No of levels = no of rows in L.
        if level < 0:
            # if there are no levels in the input, just return the Yl value
            return Yl

        Lo = Yl
        while level >= 1:  # Reconstruct levels 2 and above in reverse order.
            Hi = c2q1d(Yh[level] * gain_mask[level])
            Lo = colifilt(Lo, g0b, g0a) + colifilt(Hi, g1b, g1a)

            if (
                Lo.shape[0] != 2 * Yh[level - 1].shape[0]
            ):  # If Lo is not the same length as the next Yh => t1 was extended.
                Lo = Lo[1:-1, ...]  # Therefore we have to clip Lo so it is the same height as the next Yh.

            if np.any(np.asanyarray(Lo.shape) != np.asanyarray(Yh[level - 1].shape * np.array((2, 1)))):
                raise ValueError("Yh sizes are not valid for DTWAVEIFM")

            level -= 1

        if level == 0:  # Reconstruct level 1.
            Hi = c2q1d(Yh[level] * gain_mask[level])
            Z = colfilter(Lo, g0o) + colfilter(Hi, g1o)

        # Return a 1d vector or a column vector
        if Z.shape[1] == 1:
            return Z.flatten()
        else:
            return Z
Exemple #12
0
    def inverse(self, pyramid, gain_mask=None):
        """Perform an *n*-level dual-tree complex wavelet (DTCWT) 1D
        reconstruction.

        :param pyramid: A :py:class:`dtcwt.Pyramid`-like object containing the transformed signal.
        :param gain_mask: Gain to be applied to each subband.

        :returns: Reconstructed real array.

        The *l*-th element of *gain_mask* is gain for wavelet subband at level l.
        If gain_mask[l] == 0, no computation is performed for band *l*. Default
        *gain_mask* is all ones. Note that *l* is 0-indexed.

        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002
        .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002

        """
        # Which wavelets are to be used?
        biort = self.biort
        qshift = self.qshift

        Yl = pyramid.lowpass
        Yh = pyramid.highpasses

        a = len(Yh)  # No of levels.

        if gain_mask is None:
            gain_mask = np.ones(a)  # Default gain_mask.

        # Try to load coefficients if biort is a string parameter
        try:
            h0o, g0o, h1o, g1o = _biort(biort)
        except TypeError:
            h0o, g0o, h1o, g1o = biort

        # Try to load coefficients if qshift is a string parameter
        try:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = _qshift(qshift)
        except TypeError:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = qshift

        level = a - 1  # No of levels = no of rows in L.
        if level < 0:
            # if there are no levels in the input, just return the Yl value
            return Yl

        Lo = Yl
        while level >= 1:  # Reconstruct levels 2 and above in reverse order.
            Hi = c2q1d(Yh[level] * gain_mask[level])
            Lo = colifilt(Lo, g0b, g0a) + colifilt(Hi, g1b, g1a)

            if Lo.shape[0] != 2 * Yh[level - 1].shape[
                    0]:  # If Lo is not the same length as the next Yh => t1 was extended.
                Lo = Lo[
                    1:-1,
                    ...]  # Therefore we have to clip Lo so it is the same height as the next Yh.

            if np.any(
                    np.asanyarray(Lo.shape) != np.asanyarray(
                        Yh[level - 1].shape * np.array((2, 1)))):
                raise ValueError('Yh sizes are not valid for DTWAVEIFM')

            level -= 1

        if level == 0:  # Reconstruct level 1.
            Hi = c2q1d(Yh[level] * gain_mask[level])
            Z = colfilter(Lo, g0o) + colfilter(Hi, g1o)

        # Return a 1d vector or a column vector
        if Z.shape[1] == 1:
            return Z.flatten()
        else:
            return Z