def test_output_size(): Y = coldfilt(lena, (-1,1), (1,-1)) assert Y.shape == (lena.shape[0]/2, lena.shape[1])
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_good_input_size(): coldfilt(lena[:,:511], (-1,1), (1,-1))
def test_good_input_size_non_orthogonal(): coldfilt(lena[:,:511], (1,1), (1,1))
def test_different_size(): coldfilt(lena, (-0.5,-1,2,1,0.5), (-1,2,-1))
def test_bad_input_size(): coldfilt(lena[:511,:], (-1,1), (1,-1))
def test_odd_filter(): coldfilt(lena, (-1,2,-1), (-1,2,1))
def _level2_xfm(X, h0a, h0b, h1a, h1b, ext_mode): """Perform level 2 or greater of the 3d transform. """ if ext_mode == 4: if X.shape[0] % 4 != 0: X = np.concatenate((X[[0],:,:], X, X[[-1],:,:]), 0) if X.shape[1] % 4 != 0: X = np.concatenate((X[:,[0],:], X, X[:,[-1],:]), 1) if X.shape[2] % 4 != 0: X = np.concatenate((X[:,:,[0]], X, X[:,:,[-1]]), 2) elif ext_mode == 8: if X.shape[0] % 8 != 0: X = np.concatenate((X[(0,0),:,:], X, X[(-1,-1),:,:]), 0) if X.shape[1] % 8 != 0: X = np.concatenate((X[:,(0,0),:], X, X[:,(-1,-1),:]), 1) if X.shape[2] % 8 != 0: X = np.concatenate((X[:,:,(0,0)], X, X[:,:,(-1,-1)]), 2) # Create work area work_shape = np.asanyarray(X.shape) 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) # Assign input work = X # Loop over 2nd dimension extracting 2D slice from first and 3rd dimensions for f in xrange(work.shape[1]): # extract slice (copy required because we overwrite the work array) y = work[:, f, :].T.copy() # Do even Qshift filters on 3rd dim. work[:, f, s2b] = coldfilt(y, h1b, h1a).T work[:, f, s2a] = coldfilt(y, h0b, h0a).T # Loop over 3rd dimension extracting 2D slice from first and 2nd dimensions for f in xrange(work.shape[2]): # Do even Qshift filters on rows. y1 = work[:, :, f].T y2 = np.vstack((coldfilt(y1, h0b, h0a), coldfilt(y1, h1b, h1a))).T # Do even Qshift filters on columns. work[s0a, :, f] = coldfilt(y2, h0b, h0a) work[s0b, :, f] = coldfilt(y2, h1b, h1a) # Return appropriate slices of output return ( work[s0a, s1a, s2a], # LLL np.concatenate(( cube2c(work[s0a, s1b, s2a]), # HLL cube2c(work[s0b, s1a, s2a]), # LHL cube2c(work[s0b, s1b, s2a]), # HHL cube2c(work[s0a, s1a, s2b]), # LLH cube2c(work[s0a, s1b, s2b]), # HLH cube2c(work[s0b, s1a, s2b]), # LHH cube2c(work[s0b, s1b, s2b]), # 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 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 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)