def _level1_xfm_no_highpass(X, h0o, h1o, ext_mode): """Perform level 1 of the 3d transform discarding highpass subbands. """ # Check shape of input according to ext_mode. Note that shape of X is # double original input in each direction. if ext_mode == 4 and np.any(np.fmod(X.shape, 2) != 0): raise ValueError('Input shape should be a multiple of 2 in each direction when ext_mode == 4') elif ext_mode == 8 and np.any(np.fmod(X.shape, 4) != 0): raise ValueError('Input shape should be a multiple of 4 in each direction when ext_mode == 8') out = np.zeros_like(X) # Loop over 2nd dimension extracting 2D slice from first and 3rd dimensions for f in xrange(X.shape[1]): # extract slice y = X[:, f, :].T out[:, f, :] = colfilter(y, h0o).T # Loop over 3rd dimension extracting 2D slice from first and 2nd dimensions for f in xrange(X.shape[2]): y = colfilter(out[:, :, f].T, h0o).T out[:, :, f] = colfilter(y, h0o) return out
def _level1_ifm_no_highpass(Yl, g0o, g1o): """Perform level 1 of the inverse 3d transform assuming highpass coefficients are zero. """ # Create work area output = np.zeros_like(Yl) for f in xrange(Yl.shape[2]): y = colfilter(Yl[:, :, f].T, g0o) output[:, :, f] = colfilter(y.T, g0o) for f in xrange(Yl.shape[1]): y = output[:, f, :].T.copy() output[:, f, :] = colfilter(y, g0o) return output
def _level1_ifm(Yl, Yh, g0o, g1o): """Perform level 1 of the inverse 3d transform. """ # Create work area work = np.zeros(np.asanyarray(Yl.shape) * 2, dtype=Yl.dtype) # Work out shape of output Xshape = np.asanyarray(work.shape) >> 1 if g0o.shape[0] % 2 == 0: # if we have an even length filter, we need to shrink the output by 1 # to compensate for the addition of an extra row/column/slice in # the forward transform Xshape -= 1 # Form some useful slices s0a = slice(None, work.shape[0] >> 1) s1a = slice(None, work.shape[1] >> 1) s2a = slice(None, work.shape[2] >> 1) s0b = slice(work.shape[0] >> 1, None) s1b = slice(work.shape[1] >> 1, None) s2b = slice(work.shape[2] >> 1, None) x0a = slice(None, Xshape[0]) x1a = slice(None, Xshape[1]) x2a = slice(None, Xshape[2]) x0b = slice(work.shape[0] >> 1, (work.shape[0] >> 1) + Xshape[0]) x1b = slice(work.shape[1] >> 1, (work.shape[1] >> 1) + Xshape[1]) x2b = slice(work.shape[2] >> 1, (work.shape[2] >> 1) + Xshape[2]) # Assign regions of work area work[s0a, s1a, s2a] = Yl work[x0a, x1b, x2a] = c2cube(Yh[:,:,:, 0:4 ]) work[x0b, x1a, x2a] = c2cube(Yh[:,:,:, 4:8 ]) work[x0b, x1b, x2a] = c2cube(Yh[:,:,:, 8:12]) work[x0a, x1a, x2b] = c2cube(Yh[:,:,:,12:16]) work[x0a, x1b, x2b] = c2cube(Yh[:,:,:,16:20]) work[x0b, x1a, x2b] = c2cube(Yh[:,:,:,20:24]) work[x0b, x1b, x2b] = c2cube(Yh[:,:,:,24:28]) for f in xrange(work.shape[2]): # Do odd top-level filters on rows. y = colfilter(work[:, x1a, f].T, g0o) + colfilter(work[:, x1b, f].T, g1o) # Do odd top-level filters on columns. work[s0a, s1a, f] = colfilter(y[:, x0a].T, g0o) + colfilter(y[:, x0b].T, g1o) for f in xrange(work.shape[1]>>1): # Do odd top-level filters on 3rd dim. y = work[s0a, f, :].T work[s0a, f, s2a] = (colfilter(y[x2a, :], g0o) + colfilter(y[x2b, :], g1o)).T if g0o.shape[0] % 2 == 0: return work[1:(work.shape[0]>>1), 1:(work.shape[1]>>1), 1:(work.shape[2]>>1)] else: return work[s0a, s1a, s2a]
def test_odd_size_non_array(): y = colfilter(lena.tolist(), (-1, 2, -1)) assert y.shape == lena.shape
def test_even_size(): y = colfilter(np.zeros_like(lena), (-1, 1)) assert y.shape == (lena.shape[0] + 1, lena.shape[1]) assert not np.any(y[:] != 0.0)
def dtwaveifm2(Yl, Yh, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, gain_mask=None): """Perform an *n*-level dual-tree complex wavelet (DTCWT) 2D reconstruction. :param Yl: The real lowpass subband from the final level :param Yh: A sequence containing the complex highpass subband for each level. :param biort: Level 1 wavelets to use. See :py:func:`biort`. :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`. :param gain_mask: Gain to be applied to each subband. :returns Z: Reconstructed real array The (*d*, *l*)-th element of *gain_mask* is gain for subband with direction *d* at level *l*. If gain_mask[d,l] == 0, no computation is performed for band (d,l). Default *gain_mask* is all ones. Note that both *d* and *l* are zero-indexed. 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). Example:: # Performs a 3-level reconstruction from Yl,Yh using the 13,19-tap # filters for level 1 and the Q-shift 14-tap filters for levels >= 2. Z = dtwaveifm2(Yl, Yh, 'near_sym_b', 'qshift_b') .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013 .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002 .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002 """ a = len(Yh) # No of levels. if gain_mask is None: gain_mask = np.ones((6, 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 current_level = a Z = Yl while current_level >= 2: # this ensures that for level 1 we never do the following lh = c2q(Yh[current_level - 1][:, :, [0, 5]], gain_mask[[0, 5], current_level - 1]) hl = c2q(Yh[current_level - 1][:, :, [2, 3]], gain_mask[[2, 3], current_level - 1]) hh = c2q(Yh[current_level - 1][:, :, [1, 4]], gain_mask[[1, 4], current_level - 1]) # Do even Qshift filters on columns. y1 = colifilt(Z, g0b, g0a) + colifilt(lh, g1b, g1a) y2 = colifilt(hl, g0b, g0a) + colifilt(hh, g1b, g1a) # Do even Qshift filters on rows. Z = (colifilt(y1.T, g0b, g0a) + colifilt(y2.T, g1b, g1a)).T # Check size of Z and crop as required [row_size, col_size] = Z.shape S = 2 * np.array(Yh[current_level - 2].shape) if row_size != S[0]: # check to see if this result needs to be cropped for the rows Z = Z[1:-1, :] if col_size != S[1]: # check to see if this result needs to be cropped for the cols Z = Z[:, 1:-1] if np.any(np.array(Z.shape) != S[:2]): raise ValueError("Sizes of subbands are not valid for DTWAVEIFM2") current_level = current_level - 1 if current_level == 1: lh = c2q(Yh[current_level - 1][:, :, [0, 5]], gain_mask[[0, 5], current_level - 1]) hl = c2q(Yh[current_level - 1][:, :, [2, 3]], gain_mask[[2, 3], current_level - 1]) hh = c2q(Yh[current_level - 1][:, :, [1, 4]], gain_mask[[1, 4], current_level - 1]) # Do odd top-level filters on columns. y1 = colfilter(Z, g0o) + colfilter(lh, g1o) y2 = colfilter(hl, g0o) + colfilter(hh, g1o) # Do odd top-level filters on rows. Z = (colfilter(y1.T, g0o) + colfilter(y2.T, g1o)).T return Z
def dtwavexfm(X, nlevels=3, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, 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 :param biort: Level 1 wavelets to use. See :py:func:`biort`. :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`. :returns Yl: The real lowpass image from the final level :returns Yh: A tuple containing the (N, M, 6) shape complex highpass subimages for each level. :returns Yscale: If *include_scale* is True, a tuple containing real lowpass coefficients for every scale. 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). Example:: # Performs a 5-level transform on the real image X using the 13,19-tap # filters for level 1 and the Q-shift 14-tap filters for levels >= 2. Yl, Yh = dtwavexfm(X,5,'near_sym_b','qshift_b') .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013 .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002 .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002 """ # 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 X, (), () else: return 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 Yl, Yh, Yscale else: return Yl, Yh
def test_odd_size_non_array(): y = colfilter(lena.tolist(), (-1,2,-1)) assert y.shape == lena.shape
def test_biort(): y = colfilter(lena, biort('antonini')[0]) assert y.shape == lena.shape
def test_odd_size(): y = colfilter(lena, (-1,2,-1)) assert y.shape == lena.shape
def _level1_xfm(X, h0o, h1o, ext_mode): """Perform level 1 of the 3d transform. """ # Check shape of input according to ext_mode. Note that shape of X is # double original input in each direction. if ext_mode == 4 and np.any(np.fmod(X.shape, 2) != 0): raise ValueError('Input shape should be a multiple of 2 in each direction when ext_mode == 4') elif ext_mode == 8 and np.any(np.fmod(X.shape, 4) != 0): raise ValueError('Input shape should be a multiple of 4 in each direction when ext_mode == 8') # Create work area work_shape = np.asanyarray(X.shape) * 2 # We need one extra row per octant if filter length is even if h0o.shape[0] % 2 == 0: work_shape += 2 work = np.zeros(work_shape, dtype=X.dtype) # Form some useful slices s0a = slice(None, work.shape[0] >> 1) s1a = slice(None, work.shape[1] >> 1) s2a = slice(None, work.shape[2] >> 1) s0b = slice(work.shape[0] >> 1, None) s1b = slice(work.shape[1] >> 1, None) s2b = slice(work.shape[2] >> 1, None) x0a = slice(None, X.shape[0]) x1a = slice(None, X.shape[1]) x2a = slice(None, X.shape[2]) x0b = slice(work.shape[0] >> 1, (work.shape[0] >> 1) + X.shape[0]) x1b = slice(work.shape[1] >> 1, (work.shape[1] >> 1) + X.shape[1]) x2b = slice(work.shape[2] >> 1, (work.shape[2] >> 1) + X.shape[2]) # Assign input if h0o.shape[0] % 2 == 0: work[:X.shape[0], :X.shape[1], :X.shape[2]] = X # Copy last rows/cols/slices work[ X.shape[0], :X.shape[1], :X.shape[2]] = X[-1, :, :] work[:X.shape[0], X.shape[1], :X.shape[2]] = X[:, -1, :] work[:X.shape[0], :X.shape[1], X.shape[2]] = X[:, :, -1] work[X.shape[0], X.shape[1], X.shape[2]] = X[-1,-1,-1] else: work[s0a, s1a, s2a] = X # Loop over 2nd dimension extracting 2D slice from first and 3rd dimensions for f in xrange(work.shape[1] >> 1): # extract slice y = work[s0a, f, x2a].T # Do odd top-level filters on 3rd dim. The order here is important # since the second filtering will modify the elements of y as well # since y is merely a view onto work. work[s0a, f, s2b] = colfilter(y, h1o).T work[s0a, f, s2a] = colfilter(y, h0o).T # Loop over 3rd dimension extracting 2D slice from first and 2nd dimensions for f in xrange(work.shape[2]): # Do odd top-level filters on rows. y1 = work[x0a, x1a, f].T y2 = np.vstack((colfilter(y1, h0o), colfilter(y1, h1o))).T # Do odd top-level filters on columns. work[s0a, :, f] = colfilter(y2, h0o) work[s0b, :, f] = colfilter(y2, h1o) # Return appropriate slices of output return ( work[s0a, s1a, s2a], # LLL np.concatenate(( cube2c(work[x0a, x1b, x2a]), # HLL cube2c(work[x0b, x1a, x2a]), # LHL cube2c(work[x0b, x1b, x2a]), # HHL cube2c(work[x0a, x1a, x2b]), # LLH cube2c(work[x0a, x1b, x2b]), # HLH cube2c(work[x0b, x1a, x2b]), # LHH cube2c(work[x0b, x1b, x2b]), # HLH ), axis=3) )
def dtwavexfm(X, nlevels=3, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, 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 :param biort: Level 1 wavelets to use. See :py:func:`biort`. :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`. :returns Yl: The real lowpass image from the final level :returns Yh: A tuple containing the (N, M, 6) shape complex highpass subimages for each level. :returns Yscale: If *include_scale* is True, a tuple containing real lowpass coefficients for every scale. 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). Example:: # Performs a 5-level transform on the real image X using the 13,19-tap # filters for level 1 and the Q-shift 14-tap filters for levels >= 2. Yl, Yh = dtwavexfm(X,5,'near_sym_b','qshift_b') .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013 .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002 .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002 """ # 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 X, (), () else: return 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 Yl, Yh, Yscale else: return Yl, Yh
def dtwaveifm(Yl, Yh, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, gain_mask=None): """Perform an *n*-level dual-tree complex wavelet (DTCWT) 1D reconstruction. :param Yl: The real lowpass subband from the final level :param Yh: A sequence containing the complex highpass subband for each level. :param biort: Level 1 wavelets to use. See :py:func:`biort`. :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`. :param gain_mask: Gain to be applied to each subband. :returns Z: 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. 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). Example:: # Performs a reconstruction from Yl,Yh using the 13,19-tap filters # for level 1 and the Q-shift 14-tap filters for levels >= 2. Z = dtwaveifm(Yl, Yh, 'near_sym_b', 'qshift_b') .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013 .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002 .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002 """ 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 dtwavexfm2(X, nlevels=3, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, include_scale=False): """Perform a *n*-level DTCWT-2D decompostion on a 2D matrix *X*. :param X: 2D real array :param nlevels: Number of levels of wavelet decomposition :param biort: Level 1 wavelets to use. See :py:func:`biort`. :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`. :returns Yl: The real lowpass image from the final level :returns Yh: A tuple containing the complex highpass subimages for each level. :returns Yscale: If *include_scale* is True, a tuple containing real lowpass coefficients for every scale. 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). Example:: # Performs a 3-level transform on the real image X using the 13,19-tap # filters for level 1 and the Q-shift 14-tap filters for levels >= 2. Yl, Yh = dtwavexfm2(X, 3, 'near_sym_b', 'qshift_b') .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013 .. codeauthor:: Nick Kingsbury, Cambridge University, Sept 2001 .. codeauthor:: Cian Shaffrey, Cambridge University, Sept 2001 """ X = np.atleast_2d(asfarray(X)) # 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 original_size = X.shape if len(X.shape) >= 3: raise ValueError('The entered image is {0}, please enter each image slice separately.'. format('x'.join(list(str(s) for s in X.shape)))) # 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 #initialise initial_col_extend = 0 if original_size[0] % 2 != 0: # if X.shape[0] is not divisable by 2 then we need to extend X by adding a row at the bottom X = np.vstack((X, X[[-1],:])) # Any further extension will be done in due course. initial_row_extend = 1 if original_size[1] % 2 != 0: # if X.shape[1] is not divisable by 2 then we need to extend X by adding a col to the left X = np.hstack((X, X[:,[-1]])) initial_col_extend = 1 extended_size = X.shape if nlevels == 0: if include_scale: return X, (), () else: return X, () # initialise Yh = [None,] * nlevels if include_scale: # this is only required if the user specifies a third output component. Yscale = [None,] * nlevels complex_dtype = appropriate_complex_type_for(X) if nlevels >= 1: # Do odd top-level filters on cols. Lo = colfilter(X,h0o).T Hi = colfilter(X,h1o).T # Do odd top-level filters on rows. LoLo = colfilter(Lo,h0o).T Yh[0] = np.zeros((LoLo.shape[0] >> 1, LoLo.shape[1] >> 1, 6), dtype=complex_dtype) Yh[0][:,:,[0, 5]] = q2c(colfilter(Hi,h0o).T) # Horizontal pair Yh[0][:,:,[2, 3]] = q2c(colfilter(Lo,h1o).T) # Vertical pair Yh[0][:,:,[1, 4]] = q2c(colfilter(Hi,h1o).T) # Diagonal pair if include_scale: Yscale[0] = LoLo for level in xrange(1, nlevels): row_size, col_size = LoLo.shape if row_size % 4 != 0: # Extend by 2 rows if no. of rows of LoLo are not divisable by 4 LoLo = np.vstack((LoLo[[0],:], LoLo, LoLo[[-1],:])) if col_size % 4 != 0: # Extend by 2 cols if no. of cols of LoLo are not divisable by 4 LoLo = np.hstack((LoLo[:,[0]], LoLo, LoLo[:,[-1]])) # Do even Qshift filters on rows. Lo = coldfilt(LoLo,h0b,h0a).T Hi = coldfilt(LoLo,h1b,h1a).T # Do even Qshift filters on columns. LoLo = coldfilt(Lo,h0b,h0a).T Yh[level] = np.zeros((LoLo.shape[0]>>1, LoLo.shape[1]>>1, 6), dtype=complex_dtype) Yh[level][:,:,[0, 5]] = q2c(coldfilt(Hi,h0b,h0a).T) # Horizontal Yh[level][:,:,[2, 3]] = q2c(coldfilt(Lo,h1b,h1a).T) # Vertical Yh[level][:,:,[1, 4]] = q2c(coldfilt(Hi,h1b,h1a).T) # Diagonal if include_scale: Yscale[0] = LoLo Yl = LoLo if initial_row_extend == 1 and initial_col_extend == 1: logging.warn('The image entered is now a {0} NOT a {1}.'.format( 'x'.join(list(str(s) for s in extended_size)), 'x'.join(list(str(s) for s in original_size)))) logging.warn( 'The bottom row and rightmost column have been duplicated, prior to decomposition.') if initial_row_extend == 1 and initial_col_extend == 0: logging.warn('The image entered is now a {0} NOT a {1}.'.format( 'x'.join(list(str(s) for s in extended_size)), 'x'.join(list(str(s) for s in original_size)))) logging.warn( 'The bottom row has been duplicated, prior to decomposition.') if initial_row_extend == 0 and initial_col_extend == 1: logging.warn('The image entered is now a {0} NOT a {1}.'.format( 'x'.join(list(str(s) for s in extended_size)), 'x'.join(list(str(s) for s in original_size)))) logging.warn( 'The rightmost column has been duplicated, prior to decomposition.') if include_scale: return Yl, tuple(Yh), tuple(Yscale) else: return Yl, tuple(Yh)
def dtwaveifm2(Yl,Yh,biort=DEFAULT_BIORT,qshift=DEFAULT_QSHIFT,gain_mask=None): """Perform an *n*-level dual-tree complex wavelet (DTCWT) 2D reconstruction. :param Yl: The real lowpass subband from the final level :param Yh: A sequence containing the complex highpass subband for each level. :param biort: Level 1 wavelets to use. See :py:func:`biort`. :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`. :param gain_mask: Gain to be applied to each subband. :returns Z: Reconstructed real array The (*d*, *l*)-th element of *gain_mask* is gain for subband with direction *d* at level *l*. If gain_mask[d,l] == 0, no computation is performed for band (d,l). Default *gain_mask* is all ones. Note that both *d* and *l* are zero-indexed. 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). Example:: # Performs a 3-level reconstruction from Yl,Yh using the 13,19-tap # filters for level 1 and the Q-shift 14-tap filters for levels >= 2. Z = dtwaveifm2(Yl, Yh, 'near_sym_b', 'qshift_b') .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013 .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002 .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002 """ a = len(Yh) # No of levels. if gain_mask is None: gain_mask = np.ones((6,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 current_level = a Z = Yl while current_level >= 2: # this ensures that for level 1 we never do the following lh = c2q(Yh[current_level-1][:,:,[0, 5]], gain_mask[[0, 5], current_level-1]) hl = c2q(Yh[current_level-1][:,:,[2, 3]], gain_mask[[2, 3], current_level-1]) hh = c2q(Yh[current_level-1][:,:,[1, 4]], gain_mask[[1, 4], current_level-1]) # Do even Qshift filters on columns. y1 = colifilt(Z,g0b,g0a) + colifilt(lh,g1b,g1a) y2 = colifilt(hl,g0b,g0a) + colifilt(hh,g1b,g1a) # Do even Qshift filters on rows. Z = (colifilt(y1.T,g0b,g0a) + colifilt(y2.T,g1b,g1a)).T # Check size of Z and crop as required [row_size, col_size] = Z.shape S = 2*np.array(Yh[current_level-2].shape) if row_size != S[0]: # check to see if this result needs to be cropped for the rows Z = Z[1:-1,:] if col_size != S[1]: # check to see if this result needs to be cropped for the cols Z = Z[:,1:-1] if np.any(np.array(Z.shape) != S[:2]): raise ValueError('Sizes of subbands are not valid for DTWAVEIFM2') current_level = current_level - 1 if current_level == 1: lh = c2q(Yh[current_level-1][:,:,[0, 5]],gain_mask[[0, 5],current_level-1]) hl = c2q(Yh[current_level-1][:,:,[2, 3]],gain_mask[[2, 3],current_level-1]) hh = c2q(Yh[current_level-1][:,:,[1, 4]],gain_mask[[1, 4],current_level-1]) # Do odd top-level filters on columns. y1 = colfilter(Z,g0o) + colfilter(lh,g1o) y2 = colfilter(hl,g0o) + colfilter(hh,g1o) # Do odd top-level filters on rows. Z = (colfilter(y1.T,g0o) + colfilter(y2.T,g1o)).T return Z
def test_even_size_non_array(): y = colfilter(lena.tolist(), (-1, 1)) assert y.shape == (lena.shape[0] + 1, lena.shape[1])
def test_even_size(): y = colfilter(lena, (-1,1)) assert y.shape == (lena.shape[0]+1, lena.shape[1])
def test_even_size(): y = colfilter(lena, (-1, 1)) assert y.shape == (lena.shape[0] + 1, lena.shape[1])
def test_qshift(): y = colfilter(lena, qshift('qshift_a')[0]) assert y.shape == (lena.shape[0]+1, lena.shape[1])
def test_odd_size(): y = colfilter(lena, (-1, 2, -1)) assert y.shape == lena.shape
def test_even_size(): y = colfilter(np.zeros_like(lena), (-1,1)) assert y.shape == (lena.shape[0]+1, lena.shape[1]) assert not np.any(y[:] != 0.0)
def test_qshift(): y = colfilter(lena, qshift('qshift_a')[0]) assert y.shape == (lena.shape[0] + 1, lena.shape[1])
def test_even_size_non_array(): y = colfilter(lena.tolist(), (-1,1)) assert y.shape == (lena.shape[0]+1, lena.shape[1])
def dtwaveifm(Yl, Yh, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, gain_mask=None): """Perform an *n*-level dual-tree complex wavelet (DTCWT) 1D reconstruction. :param Yl: The real lowpass subband from the final level :param Yh: A sequence containing the complex highpass subband for each level. :param biort: Level 1 wavelets to use. See :py:func:`biort`. :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`. :param gain_mask: Gain to be applied to each subband. :returns Z: 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. 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). Example:: # Performs a reconstruction from Yl,Yh using the 13,19-tap filters # for level 1 and the Q-shift 14-tap filters for levels >= 2. Z = dtwaveifm(Yl, Yh, 'near_sym_b', 'qshift_b') .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013 .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002 .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002 """ 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 dtwavexfm2(X, nlevels=3, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, include_scale=False): """Perform a *n*-level DTCWT-2D decompostion on a 2D matrix *X*. :param X: 2D real array :param nlevels: Number of levels of wavelet decomposition :param biort: Level 1 wavelets to use. See :py:func:`biort`. :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`. :returns Yl: The real lowpass image from the final level :returns Yh: A tuple containing the complex highpass subimages for each level. :returns Yscale: If *include_scale* is True, a tuple containing real lowpass coefficients for every scale. 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). Example:: # Performs a 3-level transform on the real image X using the 13,19-tap # filters for level 1 and the Q-shift 14-tap filters for levels >= 2. Yl, Yh = dtwavexfm2(X, 3, 'near_sym_b', 'qshift_b') .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013 .. codeauthor:: Nick Kingsbury, Cambridge University, Sept 2001 .. codeauthor:: Cian Shaffrey, Cambridge University, Sept 2001 """ X = np.atleast_2d(asfarray(X)) # 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 original_size = X.shape if len(X.shape) >= 3: raise ValueError( "The entered image is {0}, please enter each image slice separately.".format( "x".join(list(str(s) for s in X.shape)) ) ) # 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 # initialise initial_col_extend = 0 if original_size[0] % 2 != 0: # if X.shape[0] is not divisable by 2 then we need to extend X by adding a row at the bottom X = np.vstack((X, X[[-1], :])) # Any further extension will be done in due course. initial_row_extend = 1 if original_size[1] % 2 != 0: # if X.shape[1] is not divisable by 2 then we need to extend X by adding a col to the left X = np.hstack((X, X[:, [-1]])) initial_col_extend = 1 extended_size = X.shape if nlevels == 0: if include_scale: return X, (), () else: return X, () # initialise Yh = [None] * nlevels if include_scale: # this is only required if the user specifies a third output component. Yscale = [None] * nlevels complex_dtype = appropriate_complex_type_for(X) if nlevels >= 1: # Do odd top-level filters on cols. Lo = colfilter(X, h0o).T Hi = colfilter(X, h1o).T # Do odd top-level filters on rows. LoLo = colfilter(Lo, h0o).T Yh[0] = np.zeros((LoLo.shape[0] >> 1, LoLo.shape[1] >> 1, 6), dtype=complex_dtype) Yh[0][:, :, [0, 5]] = q2c(colfilter(Hi, h0o).T) # Horizontal pair Yh[0][:, :, [2, 3]] = q2c(colfilter(Lo, h1o).T) # Vertical pair Yh[0][:, :, [1, 4]] = q2c(colfilter(Hi, h1o).T) # Diagonal pair if include_scale: Yscale[0] = LoLo for level in xrange(1, nlevels): row_size, col_size = LoLo.shape if row_size % 4 != 0: # Extend by 2 rows if no. of rows of LoLo are not divisable by 4 LoLo = np.vstack((LoLo[[0], :], LoLo, LoLo[[-1], :])) if col_size % 4 != 0: # Extend by 2 cols if no. of cols of LoLo are not divisable by 4 LoLo = np.hstack((LoLo[:, [0]], LoLo, LoLo[:, [-1]])) # Do even Qshift filters on rows. Lo = coldfilt(LoLo, h0b, h0a).T Hi = coldfilt(LoLo, h1b, h1a).T # Do even Qshift filters on columns. LoLo = coldfilt(Lo, h0b, h0a).T Yh[level] = np.zeros((LoLo.shape[0] >> 1, LoLo.shape[1] >> 1, 6), dtype=complex_dtype) Yh[level][:, :, [0, 5]] = q2c(coldfilt(Hi, h0b, h0a).T) # Horizontal Yh[level][:, :, [2, 3]] = q2c(coldfilt(Lo, h1b, h1a).T) # Vertical Yh[level][:, :, [1, 4]] = q2c(coldfilt(Hi, h1b, h1a).T) # Diagonal if include_scale: Yscale[0] = LoLo Yl = LoLo if initial_row_extend == 1 and initial_col_extend == 1: logging.warn( "The image entered is now a {0} NOT a {1}.".format( "x".join(list(str(s) for s in extended_size)), "x".join(list(str(s) for s in original_size)) ) ) logging.warn("The bottom row and rightmost column have been duplicated, prior to decomposition.") if initial_row_extend == 1 and initial_col_extend == 0: logging.warn( "The image entered is now a {0} NOT a {1}.".format( "x".join(list(str(s) for s in extended_size)), "x".join(list(str(s) for s in original_size)) ) ) logging.warn("The bottom row has been duplicated, prior to decomposition.") if initial_row_extend == 0 and initial_col_extend == 1: logging.warn( "The image entered is now a {0} NOT a {1}.".format( "x".join(list(str(s) for s in extended_size)), "x".join(list(str(s) for s in original_size)) ) ) logging.warn("The rightmost column has been duplicated, prior to decomposition.") if include_scale: return Yl, tuple(Yh), tuple(Yscale) else: return Yl, tuple(Yh)