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
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
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
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
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
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
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)
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(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
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