예제 #1
0
def coldfilt(X, ha, hb, queue=None):
    """Filter the columns of image X using the two filters ha and hb =
    reverse(ha).  ha operates on the odd samples of X and hb on the even
    samples.  Both filters should be even length, and h should be approx linear
    phase with a quarter sample advance from its mid pt (i.e. :math:`|h(m/2)| >
    |h(m/2 + 1)|`).

    .. code-block:: text

                          ext        top edge                     bottom edge       ext
        Level 1:        !               |               !               |               !
        odd filt on .    b   b   b   b   a   a   a   a   a   a   a   a   b   b   b   b
        odd filt on .      a   a   a   a   b   b   b   b   b   b   b   b   a   a   a   a
        Level 2:        !               |               !               |               !
        +q filt on x      b       b       a       a       a       a       b       b
        -q filt on o          a       a       b       b       b       b       a       a

    The output is decimated by two from the input sample rate and the results
    from the two filters, Ya and Yb, are interleaved to give Y.  Symmetric
    extension with repeated end samples is used on the composite X columns
    before each filter is applied.

    Raises ValueError if the number of rows in X is not a multiple of 4, the
    length of ha does not match hb or the lengths of ha or hb are non-even.

    .. codeauthor:: Rich Wareham <*****@*****.**>, August 2013
    .. codeauthor:: Cian Shaffrey, Cambridge University, August 2000
    .. codeauthor:: Nick Kingsbury, Cambridge University, August 2000

    """
    queue = to_queue(queue)

    # Make sure all inputs are arrays
    X = asfarray(X)
    ha = asfarray(ha)
    hb = asfarray(hb)

    r, c = X.shape
    if r % 4 != 0:
        raise ValueError('No. of rows in X must be a multiple of 4')

    if ha.shape != hb.shape:
        raise ValueError('Shapes of ha and hb must be the same')

    if ha.shape[0] % 2 != 0:
        raise ValueError('Lengths of ha and hb must be even')

    # Perform filtering on columns of extended matrix X(xe,:) in 4 ways.
    Y = axis_convolve_dfilter(X, ha, queue=queue)

    return to_array(Y)
예제 #2
0
def coldfilt(X, ha, hb, queue=None):
    """Filter the columns of image X using the two filters ha and hb =
    reverse(ha).  ha operates on the odd samples of X and hb on the even
    samples.  Both filters should be even length, and h should be approx linear
    phase with a quarter sample advance from its mid pt (i.e. :math:`|h(m/2)| >
    |h(m/2 + 1)|`).

    .. code-block:: text

                          ext        top edge                     bottom edge       ext
        Level 1:        !               |               !               |               !
        odd filt on .    b   b   b   b   a   a   a   a   a   a   a   a   b   b   b   b
        odd filt on .      a   a   a   a   b   b   b   b   b   b   b   b   a   a   a   a
        Level 2:        !               |               !               |               !
        +q filt on x      b       b       a       a       a       a       b       b
        -q filt on o          a       a       b       b       b       b       a       a

    The output is decimated by two from the input sample rate and the results
    from the two filters, Ya and Yb, are interleaved to give Y.  Symmetric
    extension with repeated end samples is used on the composite X columns
    before each filter is applied.

    Raises ValueError if the number of rows in X is not a multiple of 4, the
    length of ha does not match hb or the lengths of ha or hb are non-even.

    .. codeauthor:: Rich Wareham <*****@*****.**>, August 2013
    .. codeauthor:: Cian Shaffrey, Cambridge University, August 2000
    .. codeauthor:: Nick Kingsbury, Cambridge University, August 2000

    """
    queue = to_queue(queue)

    # Make sure all inputs are arrays
    X = asfarray(X)
    ha = asfarray(ha)
    hb = asfarray(hb)

    r, c = X.shape
    if r % 4 != 0:
        raise ValueError("No. of rows in X must be a multiple of 4")

    if ha.shape != hb.shape:
        raise ValueError("Shapes of ha and hb must be the same")

    if ha.shape[0] % 2 != 0:
        raise ValueError("Lengths of ha and hb must be even")

    # Perform filtering on columns of extended matrix X(xe,:) in 4 ways.
    Y = axis_convolve_dfilter(X, ha, queue=queue)

    return to_array(Y)
예제 #3
0
파일: sampling.py 프로젝트: timseries/dtcwt
def upsample(image, method=None):
    """Specialised function to upsample an image by a factor of two using
    a specified sampling method. If *image* is an array of shape (NxMx...) then
    the output will have shape (2Nx2Mx...). Only rows and columns are
    upsampled, depth axes and greater are interpolated but are not upsampled.

    :param image: an array containing the image to upsample
    :param method: if non-None, a string specifying the sampling method to use.

    If *method* is ``None``, the default sampling method ``'lanczos'`` is used.
    The following sampling methods are supported:

    =========== ===========
    Name        Description 
    =========== ===========
    nearest     Nearest-neighbour sampling
    bilinear    Bilinear sampling
    lanczos     Lanczos sampling with window radius of 3
    =========== ===========
    """
    image = np.atleast_2d(asfarray(image))

    # The default '.T' operator doesn't quite do what we want since it
    # reverses the axes rather than only swapping the first two
    def _t(X):
        axes = np.arange(len(X.shape))
        axes[:2] = (1,0)
        return np.transpose(X, axes)

    return _upsample_columns(_t(_upsample_columns(_t(image), method)), method)
예제 #4
0
def colfilter(X, h):
    """Filter the columns of image *X* using filter vector *h*, without decimation.
    If len(h) is odd, each output sample is aligned with each input sample
    and *Y* is the same size as *X*.  If len(h) is even, each output sample is
    aligned with the mid point of each pair of input samples, and Y.shape =
    X.shape + [1 0].

    :param X: an image whose columns are to be filtered
    :param h: the filter coefficients.
    :returns Y: the filtered image.

    .. codeauthor:: Rich Wareham <*****@*****.**>, August 2013
    .. codeauthor:: Cian Shaffrey, Cambridge University, August 2000
    .. codeauthor:: Nick Kingsbury, Cambridge University, August 2000

    """

    # Interpret all inputs as arrays
    X = asfarray(X)
    h = as_column_vector(h)

    r, c = X.shape
    m = h.shape[0]
    m2 = np.fix(m * 0.5)

    # Symmetrically extend with repeat of end samples.
    # Use 'reflect' so r < m2 works OK.
    xe = reflect(np.arange(-m2, r + m2, dtype=np.int), -0.5, r - 0.5)

    # Perform filtering on the columns of the extended matrix X(xe,:), keeping
    # only the 'valid' output samples, so Y is the same size as X if m is odd.
    Y = _column_convolve(X[xe, :], h)

    return Y
예제 #5
0
파일: lowlevel.py 프로젝트: timseries/dtcwt
def colfilter(X, h):
    """Filter the columns of image *X* using filter vector *h*, without decimation.
    If len(h) is odd, each output sample is aligned with each input sample
    and *Y* is the same size as *X*.  If len(h) is even, each output sample is
    aligned with the mid point of each pair of input samples, and Y.shape =
    X.shape + [1 0].

    :param X: an image whose columns are to be filtered
    :param h: the filter coefficients.
    :returns Y: the filtered image.

    .. codeauthor:: Rich Wareham <*****@*****.**>, August 2013
    .. codeauthor:: Cian Shaffrey, Cambridge University, August 2000
    .. codeauthor:: Nick Kingsbury, Cambridge University, August 2000

    """
    
    # Interpret all inputs as arrays
    X = asfarray(X)
    h = as_column_vector(h)

    r, c = X.shape
    m = h.shape[0]
    m2 = np.fix(m*0.5)

    # Symmetrically extend with repeat of end samples.
    # Use 'reflect' so r < m2 works OK.
    xe = reflect(np.arange(-m2, r+m2, dtype=np.int), -0.5, r-0.5)

    # Perform filtering on the columns of the extended matrix X(xe,:), keeping
    # only the 'valid' output samples, so Y is the same size as X if m is odd.
    Y = _column_convolve(X[xe,:], h)

    return Y
예제 #6
0
def upsample(image, method=None):
    """Specialised function to upsample an image by a factor of two using
    a specified sampling method. If *image* is an array of shape (NxMx...) then
    the output will have shape (2Nx2Mx...). Only rows and columns are
    upsampled, depth axes and greater are interpolated but are not upsampled.

    :param image: an array containing the image to upsample
    :param method: if non-None, a string specifying the sampling method to use.

    If *method* is ``None``, the default sampling method ``'lanczos'`` is used.
    The following sampling methods are supported:

    =========== ===========
    Name        Description 
    =========== ===========
    nearest     Nearest-neighbour sampling
    bilinear    Bilinear sampling
    lanczos     Lanczos sampling with window radius of 3
    =========== ===========
    """
    image = np.atleast_2d(asfarray(image))

    # The default '.T' operator doesn't quite do what we want since it
    # reverses the axes rather than only swapping the first two
    def _t(X):
        axes = np.arange(len(X.shape))
        axes[:2] = (1, 0)
        return np.transpose(X, axes)

    return _upsample_columns(_t(_upsample_columns(_t(image), method)), method)
예제 #7
0
파일: sampling.py 프로젝트: timseries/dtcwt
def _upsample_columns(X, method=None):
    """
    The centre of columns of X, an M-columned matrix, are assumed to have co-ordinates
    { 0, 1, 2, ... , M-1 } which means that the up-sampled matrix's columns should sample
    from { -0.25, 0.25, 0.75, ... , M-1.25 }. We can view that as an interleaved set of teo
    *convolutions* of X. The first, A, using a kernel equivalent to sampling the { -0.25, 0.75,
    1.75, 2.75, ... M-1.25 } columns and the second, B, sampling the { 0.25, 1.25, ... , M-0.75 }
    columns.
    """
    if method is None:
        method = 'lanczos'
    
    X = np.atleast_2d(asfarray(X))
    
    out_shape = list(X.shape)
    out_shape[1] *= 2
    output = np.zeros(out_shape, dtype=X.dtype)
    
    # Centres of sampling for A and B convolutions
    M = X.shape[1]
    A_columns = np.linspace(-0.25, M-1.25, M)
    B_columns = A_columns + 0.5
    
    # For A columns sample at x = ceil(x) - 0.25 with ceil(x) = { 0, 1, 2, ..., M-1 }
    # For B columns sample at x = floor(x) + 0.25 with floor(x) = { 0, 1, 2, ..., M-1 }
    int_columns = np.linspace(0, M-1, M)
    
    if method == 'lanczos':
        # Lanczos kernel width
        a = 3.0
        sample_offsets = np.arange(-a, a+1)
       
        # For A: if i = ceil(x) + di, => ceil(x) - i = -0.25 - di
        # For B: if i = floor(x) + di, => floor(x) - i = 0.25 - di
        l_as = np.sinc(-0.25-sample_offsets)*np.sinc((-0.25-sample_offsets)/a)   
        l_bs = np.sinc(0.25-sample_offsets)*np.sinc((0.25-sample_offsets)/a)
    elif method == 'nearest':
        # Nearest neighbour kernel width is 1
        sample_offsets = [0,]
        l_as = l_bs = [1,]
    elif method == 'bilinear':
        # Bilinear kernel width is technically 2 but we need to offset the kernels differently
        # for A and B columns:
        sample_offsets = [-1,0,1]
        l_as = [0.25, 0.75, 0]
        l_bs = [0, 0.75, 0.25]
    else:
        raise ValueError('Unknown interpolation mode: {0}'.format(mode))
    
    # Convolve
    for di, l_a, l_b in zip(sample_offsets, l_as, l_bs):
        columns = reflect(int_columns + di, -0.5, M-0.5).astype(np.int)
        
        output[:,0::2,...] += l_a * X[:,columns,...]
        output[:,1::2,...] += l_b * X[:,columns,...]
    
    return output
예제 #8
0
파일: sampling.py 프로젝트: timseries/dtcwt
def upsample_highpass(im, method=None):
    """As :py:func:`upsample` except that the highpass image is first phase
    rolled so that the filter has approximate DC centre frequency. The upshot
    is that this is the function to use when re-sampling complex subband
    images.

    """
    im = np.atleast_2d(asfarray(im))

    # Sampled co-ordinates
    dxs, dys = np.meshgrid(np.arange(im.shape[1]*2), np.arange(im.shape[0]*2))
    sxs = 0.5 * (dxs + 0.5) - 0.5
    sys = 0.5 * (dys + 0.5) - 0.5

    # phase unwrap
    X, Y = np.meshgrid(np.arange(im.shape[1]), np.arange(im.shape[0]))
    im_unwrap = im * _phase_image(X, Y, True)
    
    # sample
    im_sampled = upsample(im_unwrap, method)
    
    # re-wrap
    return im_sampled * _phase_image(sxs, sys, False)
예제 #9
0
def colfilter(X, h):
    """Filter the columns of image *X* using filter vector *h*, without decimation.
    If len(h) is odd, each output sample is aligned with each input sample
    and *Y* is the same size as *X*.  If len(h) is even, each output sample is
    aligned with the mid point of each pair of input samples, and Y.shape =
    X.shape + [1 0].

    The filtering will be accelerated via OpenCL.

    :param X: an image whose columns are to be filtered
    :param h: the filter coefficients.
    :returns Y: the filtered image.

    .. codeauthor:: Rich Wareham <*****@*****.**>, August 2013
    .. codeauthor:: Cian Shaffrey, Cambridge University, August 2000
    .. codeauthor:: Nick Kingsbury, Cambridge University, August 2000

    """

    # Interpret all inputs as arrays
    X = asfarray(X)
    h = as_column_vector(h)

    return to_array(axis_convolve(X, h))
예제 #10
0
def upsample_highpass(im, method=None):
    """As :py:func:`upsample` except that the highpass image is first phase
    rolled so that the filter has approximate DC centre frequency. The upshot
    is that this is the function to use when re-sampling complex subband
    images.

    """
    im = np.atleast_2d(asfarray(im))

    # Sampled co-ordinates
    dxs, dys = np.meshgrid(np.arange(im.shape[1] * 2),
                           np.arange(im.shape[0] * 2))
    sxs = 0.5 * (dxs + 0.5) - 0.5
    sys = 0.5 * (dys + 0.5) - 0.5

    # phase unwrap
    X, Y = np.meshgrid(np.arange(im.shape[1]), np.arange(im.shape[0]))
    im_unwrap = im * _phase_image(X, Y, True)

    # sample
    im_sampled = upsample(im_unwrap, method)

    # re-wrap
    return im_sampled * _phase_image(sxs, sys, False)
예제 #11
0
def colfilter(X, h):
    """Filter the columns of image *X* using filter vector *h*, without decimation.
    If len(h) is odd, each output sample is aligned with each input sample
    and *Y* is the same size as *X*.  If len(h) is even, each output sample is
    aligned with the mid point of each pair of input samples, and Y.shape =
    X.shape + [1 0].

    The filtering will be accelerated via OpenCL.

    :param X: an image whose columns are to be filtered
    :param h: the filter coefficients.
    :returns Y: the filtered image.

    .. codeauthor:: Rich Wareham <*****@*****.**>, August 2013
    .. codeauthor:: Cian Shaffrey, Cambridge University, August 2000
    .. codeauthor:: Nick Kingsbury, Cambridge University, August 2000

    """

    # Interpret all inputs as arrays
    X = asfarray(X)
    h = as_column_vector(h)

    return to_array(axis_convolve(X, h))
예제 #12
0
    def forward(self, X, nlevels=3, include_scale=False, discard_level_1=False):
        """Perform a *n*-level DTCWT-3D decompostion on a 3D matrix *X*.

        :param X: 3D real array-like object
        :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`.
        :param ext_mode: Extension mode. See below.
        :param include_scale: True if level 1 high-pass bands are to be discarded.

        :returns Yl: The real lowpass image from the final level
        :returns Yh: A tuple containing the complex highpass subimages for each level.

        Each element of *Yh* is a 4D complex array with the 4th dimension having
        size 28. The 3D slice ``Yh[l][:,:,:,d]`` corresponds to the complex higpass
        coefficients for direction d at level l where d and l are both 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).

        There are two values for *ext_mode*, either 4 or 8. If *ext_mode* = 4,
        check whether 1st level is divisible by 2 (if not we raise a
        ``ValueError``). Also check whether from 2nd level onwards, the coefs can
        be divided by 4. If any dimension size is not a multiple of 4, append extra
        coefs by repeating the edges. If *ext_mode* = 8, check whether 1st level is
        divisible by 4 (if not we raise a ``ValueError``). Also check whether from
        2nd level onwards, the coeffs can be divided by 8. If any dimension size is
        not a multiple of 8, append extra coeffs by repeating the edges twice.

        If *include_scale* is True the highpass coefficients at level 1 will not be
        discarded. (And, in fact, will never be calculated.) This turns the
        transform from being 8:1 redundant to being 1:1 redundant at the cost of
        no-longer allowing perfect reconstruction. If this option is selected then
        `Yh[0]` will be `None`. Note that :py:func:`dtwaveifm3` will accepts
        `Yh[0]` being `None` and will treat it as being zero.

        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Huizhong Chen, Jan 2009
        .. codeauthor:: Nick Kingsbury, Cambridge University, July 1999.

        """
        X = np.atleast_3d(asfarray(X))

        if len(self.biort) == 4:
            h0o, g0o, h1o, g1o = self.biort
        elif len(self.biort) == 6:
            h0o, g0o, h1o, g1o, h2o, g2o = self.biort
        else:
            raise ValueError('Biort wavelet must have 6 or 4 components.')

        if len(self.qshift) == 8:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = self.qshift
        elif len(self.qshift) == 12:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b, h2a, h2b = self.qshift[:10]
        else:
            raise ValueError('Qshift wavelet must have 12 or 8 components.')

        # Check value of ext_mode. TODO: this should really be an enum :S
        if self.ext_mode != 4 and self.ext_mode != 8:
            raise ValueError('ext_mode must be one of 4 or 8')

        Yl = X
        Yh = [None,] * nlevels

        if include_scale:
            # this is only required if the user specifies a third output component.
            Yscale = [None,] * nlevels

        # level is 0-indexed
        for level in xrange(nlevels):
            # Transform
            if level == 0 and discard_level_1:
                Yl = _level1_xfm_no_highpass(Yl, h0o, h1o, self.ext_mode)
                if include_scale:
                    Yscale[0] = Yl
            elif level == 0 and not discard_level_1:
                Yl, Yh[level] = _level1_xfm(Yl, h0o, h1o, self.ext_mode)
                if include_scale:
                    Yscale[0] = Yl
            else:
                Yl, Yh[level] = _level2_xfm(Yl, h0a, h0b, h1a, h1b, self.ext_mode)
                if include_scale:
                    Yscale[level] = Yl
        #FIXME: need some way to separate the Yscale component to include the scale when necessary.
        if include_scale:
            return Pyramid(Yl, tuple(Yh), tuple(Yscale))
        else:
            return Pyramid(Yl, tuple(Yh))
        return Pyramid(Yl, tuple(Yh))
예제 #13
0
    def forward(self, X, nlevels=3, 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

        :returns: A :py:class:`dtcwt.Pyramid` compatible object representing the transform-domain signal

        .. note::

            *X* may be a :py:class:`pyopencl.array.Array` instance which has
            already been copied to the device. In which case, it must be 2D.
            (I.e. a vector will not be auto-promoted.)

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

        """
        queue = self.queue

        if isinstance(X, CLArray):
            if len(X.shape) != 2:
                raise ValueError('Input array must be two-dimensional')
        else:
            # If not an array, copy to device
            X = np.atleast_2d(asfarray(X))

        # If biort has 6 elements instead of 4, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.biort) == 4:
            h0o, g0o, h1o, g1o = self.biort
        elif len(self.biort) == 6:
            h0o, g0o, h1o, g1o, h2o, g2o = self.biort
        else:
            raise ValueError('Biort wavelet must have 6 or 4 components.')

        # If qshift has 12 elements instead of 8, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.qshift) == 8:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = self.qshift
        elif len(self.qshift) == 12:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b, h2a, h2b = self.qshift[:10]
        else:
            raise ValueError('Qshift wavelet must have 12 or 8 components.')

        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 divisible by 2 then we need to extend X by adding a row at the bottom
            X = to_array(X)
            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 divisible by 2 then we need to extend X by adding a col to the left
            X = to_array(X)
            X = np.hstack((X, X[:,[-1]]))
            initial_col_extend = 1

        extended_size = X.shape

        # Copy X to the device if necessary
        X = to_device(X, queue=queue)

        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 a third output component.
            Yscale = [None,] * nlevels

        complex_dtype = np.complex64

        if nlevels >= 1:
            # Do odd top-level filters on cols.
            Lo = axis_convolve(X,h0o,axis=0,queue=queue)
            Hi = axis_convolve(X,h1o,axis=0,queue=queue)
            if len(self.biort) >= 6:
                Ba = axis_convolve(X,h2o,axis=0,queue=queue)

            # Do odd top-level filters on rows.
            LoLo = axis_convolve(Lo,h0o,axis=1)

            if len(self.biort) >= 6:
                diag = axis_convolve(Ba,h2o,axis=1,queue=queue)
            else:
                diag = axis_convolve(Hi,h1o,axis=1,queue=queue)

            Yh[0] = q2c(
                axis_convolve(Hi,h0o,axis=1,queue=queue),
                axis_convolve(Lo,h1o,axis=1,queue=queue),
                diag,
            )

            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 divisible by 4
                LoLo = to_array(LoLo)
                LoLo = np.vstack((LoLo[:1,:], LoLo, LoLo[-1:,:]))

            if col_size % 4 != 0:
                # Extend by 2 cols if no. of cols of LoLo are not divisible by 4
                LoLo = to_array(LoLo)
                LoLo = np.hstack((LoLo[:,:1], LoLo, LoLo[:,-1:]))

            # Do even Qshift filters on rows.
            Lo = axis_convolve_dfilter(LoLo,h0b,axis=0,queue=queue)
            Hi = axis_convolve_dfilter(LoLo,h1b,axis=0,queue=queue)
            if len(self.qshift) >= 12:
                Ba = axis_convolve_dfilter(LoLo,h2b,axis=0,queue=queue)

            # Do even Qshift filters on columns.
            LoLo = axis_convolve_dfilter(Lo,h0b,axis=1,queue=queue)

            if len(self.qshift) >= 12:
                diag = axis_convolve_dfilter(Ba,h2b,axis=1,queue=queue)
            else:
                diag = axis_convolve_dfilter(Hi,h1b,axis=1,queue=queue)

            Yh[level] = q2c(
                axis_convolve_dfilter(Hi,h0b,axis=1,queue=queue),
                axis_convolve_dfilter(Lo,h1b,axis=1,queue=queue),
                diag,
            )

            if include_scale:
                Yscale[level] = 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 Pyramid(Yl, tuple(Yh), tuple(Yscale))
        else:
            return Pyramid(Yl, tuple(Yh))
예제 #14
0
def coldfilt(X, ha, hb):
    """Filter the columns of image X using the two filters ha and hb =
    reverse(ha).  ha operates on the odd samples of X and hb on the even
    samples.  Both filters should be even length, and h should be approx linear
    phase with a quarter sample advance from its mid pt (i.e. :math:`|h(m/2)| >
    |h(m/2 + 1)|`).

    .. code-block:: text

                          ext        top edge                     bottom edge       ext
        Level 1:        !               |               !               |               !
        odd filt on .    b   b   b   b   a   a   a   a   a   a   a   a   b   b   b   b
        odd filt on .      a   a   a   a   b   b   b   b   b   b   b   b   a   a   a   a
        Level 2:        !               |               !               |               !
        +q filt on x      b       b       a       a       a       a       b       b
        -q filt on o          a       a       b       b       b       b       a       a

    The output is decimated by two from the input sample rate and the results
    from the two filters, Ya and Yb, are interleaved to give Y.  Symmetric
    extension with repeated end samples is used on the composite X columns
    before each filter is applied.

    Raises ValueError if the number of rows in X is not a multiple of 4, the
    length of ha does not match hb or the lengths of ha or hb are non-even.

    .. codeauthor:: Rich Wareham <*****@*****.**>, August 2013
    .. codeauthor:: Cian Shaffrey, Cambridge University, August 2000
    .. codeauthor:: Nick Kingsbury, Cambridge University, August 2000

    """
    # Make sure all inputs are arrays
    X = asfarray(X)
    ha = asfarray(ha)
    hb = asfarray(hb)

    r, c = X.shape
    if r % 4 != 0:
        raise ValueError('No. of rows in X must be a multiple of 4')

    if ha.shape != hb.shape:
        raise ValueError('Shapes of ha and hb must be the same')

    if ha.shape[0] % 2 != 0:
        raise ValueError('Lengths of ha and hb must be even')

    m = ha.shape[0]
    m2 = np.fix(m * 0.5)

    # Set up vector for symmetric extension of X with repeated end samples.
    xe = reflect(np.arange(-m, r + m), -0.5, r - 0.5)

    # Select odd and even samples from ha and hb. Note that due to 0-indexing
    # 'odd' and 'even' are not perhaps what you might expect them to be.
    hao = as_column_vector(ha[0:m:2])
    hae = as_column_vector(ha[1:m:2])
    hbo = as_column_vector(hb[0:m:2])
    hbe = as_column_vector(hb[1:m:2])
    t = np.arange(5, r + 2 * m - 2, 4)
    r2 = r // 2
    Y = np.zeros((r2, c), dtype=X.dtype)

    if np.sum(ha * hb) > 0:
        s1 = slice(0, r2, 2)
        s2 = slice(1, r2, 2)
    else:
        s2 = slice(0, r2, 2)
        s1 = slice(1, r2, 2)

    # Perform filtering on columns of extended matrix X(xe,:) in 4 ways.
    Y[s1, :] = _column_convolve(X[xe[t - 1], :], hao) + _column_convolve(
        X[xe[t - 3], :], hae)
    Y[s2, :] = _column_convolve(X[xe[t], :], hbo) + _column_convolve(
        X[xe[t - 2], :], hbe)

    return Y
예제 #15
0
    def forward(self, X, nlevels=3, include_scale=False):
        """ Perform a forward transform on an image.

        Can provide the forward transform with either an np array (naive
        usage), or a tensorflow variable or placeholder (designed usage). To
        transform batches of images, use the :py:meth:`forward_channels` method.

        :param ndarray X: Input image which you wish to transform. Can be a
            numpy array, tensorflow Variable or tensorflow placeholder. See
            comments below.
        :param int nlevels: Number of levels of the dtcwt transform to
            calculate.
        :param bool include_scale: Whether or not to return the lowpass results
            at each scale of the transform, or only at the highest scale (as is
            custom for multi-resolution analysis)

        :returns: A :py:class:`dtcwt.tf.Pyramid` object

        .. note::

            If a numpy array is provided, the forward function will create a
            tensorflow variable to hold the input image, and then create the
            graph of the right size to match the input, and then feed the
            input into the graph and evaluate it.  This operation will
            return a :py:class:`Pyramid` object similar to how running
            the numpy version would.

        .. codeauthor:: Fergal Cotter <*****@*****.**>, Feb 2017
        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Nick Kingsbury, Cambridge University, Sept 2001
        .. codeauthor:: Cian Shaffrey, Cambridge University, Sept 2001
        """

        # Check if a numpy array was provided
        numpy = False
        try:
            dtype = X.dtype
        except AttributeError:
            X = asfarray(X)
            dtype = X.dtype

        if dtype in np_dtypes:
            numpy = True
            X = np.atleast_2d(X)
            X = tf.Variable(X, dtype=tf.float32, trainable=False)

        if X.dtype not in tf_dtypes:
            raise ValueError('I cannot handle the variable you have ' +
                             'provided of type ' + str(X.dtype) + '. ' +
                             'Inputs should be a numpy or tf array')

        X_shape = tuple(X.get_shape().as_list())
        if len(X_shape) == 2:
            # Need to make it a batch for tensorflow
            X = tf.expand_dims(X, axis=0)
        elif len(X_shape) >= 3:
            raise ValueError(
                'The entered variable has too many ' +
                'dimensions - ' + str(X_shape) + '. For batches of ' +
                'images with multiple channels (i.e. 3 or 4 dimensions), ' +
                'please either enter each channel separately, or use ' +
                'the forward_channels method.')

        X_shape = tuple(X.get_shape().as_list())
        original_size = X_shape[1:]
        size = '{}x{}'.format(original_size[0], original_size[1])
        name = 'dtcwt_fwd_{}'.format(size)
        with tf.variable_scope(name):
            Yl, Yh, Yscale = self._forward_ops(X, nlevels)

        Yl = Yl[0]
        Yh = tuple(x[0] for x in Yh)
        Yscale = tuple(x[0] for x in Yscale)

        if include_scale:
            return Pyramid(Yl, Yh, Yscale, numpy)
        else:
            return Pyramid(Yl, Yh, None, numpy)
예제 #16
0
    def forward(self, X, nlevels=3, 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

        :returns: A :py:class:`dtcwt.Pyramid` compatible object representing the transform-domain signal

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

        """
        # If biort has 6 elements instead of 4, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.biort) == 4:
            h0o, g0o, h1o, g1o = self.biort
        elif len(self.biort) == 6:
            h0o, g0o, h1o, g1o, h2o, g2o = self.biort
        else:
            raise ValueError('Biort wavelet must have 6 or 4 components.')

        # If qshift has 12 elements instead of 8, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.qshift) == 8:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = self.qshift
        elif len(self.qshift) == 12:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b, h2a, h2b = self.qshift[:10]
        else:
            raise ValueError('Qshift wavelet must have 12 or 8 components.')

        X = np.atleast_2d(asfarray(X))
        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 Pyramid(X, (), ())
            else:
                return Pyramid(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
            if len(self.biort) >= 6:
                Ba = colfilter(X, h2o).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:6:5] = q2c(colfilter(Hi, h0o).T)  # Horizontal pair
            Yh[0][:, :, 2:4:1] = q2c(colfilter(Lo, h1o).T)  # Vertical pair
            if len(self.biort) >= 6:
                Yh[0][:, :, 1:5:3] = q2c(colfilter(Ba, h2o).T)  # Diagonal pair
            else:
                Yh[0][:, :, 1:5:3] = 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[:1, :], 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[:, :1], LoLo, LoLo[:, -1:]))

            # Do even Qshift filters on rows.
            Lo = coldfilt(LoLo, h0b, h0a).T
            Hi = coldfilt(LoLo, h1b, h1a).T
            if len(self.qshift) >= 12:
                Ba = coldfilt(LoLo, h2b, h2a).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:6:5] = q2c(coldfilt(Hi, h0b,
                                                  h0a).T)  # Horizontal
            Yh[level][:, :, 2:4:1] = q2c(coldfilt(Lo, h1b, h1a).T)  # Vertical
            if len(self.qshift) >= 12:
                Yh[level][:, :, 1:5:3] = q2c(coldfilt(Ba, h2b,
                                                      h2a).T)  # Diagonal
            else:
                Yh[level][:, :, 1:5:3] = q2c(coldfilt(Hi, h1b,
                                                      h1a).T)  # Diagonal

            if include_scale:
                Yscale[level] = 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 Pyramid(Yl, tuple(Yh), tuple(Yscale))
        else:
            return Pyramid(Yl, tuple(Yh))
예제 #17
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*).

        Can provide the forward transform with either an np array (naive usage),
        or a tensorflow variable or placeholder (designed usage). To transform
        batches of vectors, use the :py:meth:`forward_channels` method.

        :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.tf.Pyramid` 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:: Fergal Cotter <*****@*****.**>, Sep 2017
        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002
        .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002

        """
        # Check if a numpy array was provided
        numpy = False
        try:
            dtype = X.dtype
        except AttributeError:
            X = asfarray(X)
            dtype = X.dtype

        if dtype in np_dtypes:
            numpy = True
            # Need this because colfilter and friends assumes input is 2d
            if len(X.shape) == 1:
                X = np.atleast_2d(X).T
            X = tf.Variable(X, dtype=tf.float32, trainable=False)
        elif dtype in tf_dtypes:
            if len(X.get_shape().as_list()) == 1:
                X = tf.expand_dims(X, axis=-1)
        else:
            raise ValueError('I cannot handle the variable you have ' +
                             'provided of type ' + str(X.dtype) + '. ' +
                             'Inputs should be a numpy or tf array')

        X_shape = tuple(X.get_shape().as_list())
        size = '{}'.format(X_shape[0])
        name = 'dtcwt_fwd_{}'.format(size)
        if len(X_shape) == 2:
            # Need to make it a batch for tensorflow
            X = tf.expand_dims(X, axis=0)
        elif len(X_shape) >= 3:
            raise ValueError(
                'The entered variable has too many ' +
                'dimensions - ' + str(X_shape) + '.')

        # Do the forward transform
        with tf.variable_scope(name):
            Yl, Yh, Yscale = self._forward_ops(X, nlevels)

        Yl = Yl[0]
        Yh = tuple(x[0] for x in Yh)
        Yscale = tuple(x[0] for x in Yscale)

        if include_scale:
            return Pyramid(Yl, Yh, Yscale, numpy)
        else:
            return Pyramid(Yl, Yh, None, numpy)
예제 #18
0
파일: lowlevel.py 프로젝트: timseries/dtcwt
def coldfilt(X, ha, hb):
    """Filter the columns of image X using the two filters ha and hb =
    reverse(ha).  ha operates on the odd samples of X and hb on the even
    samples.  Both filters should be even length, and h should be approx linear
    phase with a quarter sample advance from its mid pt (i.e. :math:`|h(m/2)| >
    |h(m/2 + 1)|`).

    .. code-block:: text

                          ext        top edge                     bottom edge       ext
        Level 1:        !               |               !               |               !
        odd filt on .    b   b   b   b   a   a   a   a   a   a   a   a   b   b   b   b   
        odd filt on .      a   a   a   a   b   b   b   b   b   b   b   b   a   a   a   a
        Level 2:        !               |               !               |               !
        +q filt on x      b       b       a       a       a       a       b       b       
        -q filt on o          a       a       b       b       b       b       a       a

    The output is decimated by two from the input sample rate and the results
    from the two filters, Ya and Yb, are interleaved to give Y.  Symmetric
    extension with repeated end samples is used on the composite X columns
    before each filter is applied.

    Raises ValueError if the number of rows in X is not a multiple of 4, the
    length of ha does not match hb or the lengths of ha or hb are non-even.

    .. codeauthor:: Rich Wareham <*****@*****.**>, August 2013
    .. codeauthor:: Cian Shaffrey, Cambridge University, August 2000
    .. codeauthor:: Nick Kingsbury, Cambridge University, August 2000

    """
    # Make sure all inputs are arrays
    X = asfarray(X)
    ha = asfarray(ha)
    hb = asfarray(hb)

    r, c = X.shape
    if r % 4 != 0:
        raise ValueError('No. of rows in X must be a multiple of 4')

    if ha.shape != hb.shape:
        raise ValueError('Shapes of ha and hb must be the same')

    if ha.shape[0] % 2 != 0:
        raise ValueError('Lengths of ha and hb must be even')

    m = ha.shape[0]
    m2 = np.fix(m*0.5)

    # Set up vector for symmetric extension of X with repeated end samples.
    xe = reflect(np.arange(-m, r+m), -0.5, r-0.5)

    # Select odd and even samples from ha and hb. Note that due to 0-indexing
    # 'odd' and 'even' are not perhaps what you might expect them to be.
    hao = as_column_vector(ha[0:m:2])
    hae = as_column_vector(ha[1:m:2])
    hbo = as_column_vector(hb[0:m:2])
    hbe = as_column_vector(hb[1:m:2])
    t = np.arange(5, r+2*m-2, 4)
    r2 = r/2;
    Y = np.zeros((r2,c), dtype=X.dtype)

    if np.sum(ha*hb) > 0:
       s1 = slice(0, r2, 2)
       s2 = slice(1, r2, 2)
    else:
       s2 = slice(0, r2, 2)
       s1 = slice(1, r2, 2)
    
    # Perform filtering on columns of extended matrix X(xe,:) in 4 ways. 
    Y[s1,:] = _column_convolve(X[xe[t-1],:],hao) + _column_convolve(X[xe[t-3],:],hae)
    Y[s2,:] = _column_convolve(X[xe[t],:],hbo) + _column_convolve(X[xe[t-2],:],hbe)

    return Y
예제 #19
0
    def forward(self, X, nlevels=3, 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

        :returns: A :py:class:`dtcwt.Pyramid` compatible object representing the transform-domain signal

        .. note::

            *X* may be a :py:class:`pyopencl.array.Array` instance which has
            already been copied to the device. In which case, it must be 2D.
            (I.e. a vector will not be auto-promoted.)

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

        """
        queue = self.queue

        if isinstance(X, CLArray):
            if len(X.shape) != 2:
                raise ValueError('Input array must be two-dimensional')
        else:
            # If not an array, copy to device
            X = np.atleast_2d(asfarray(X))

        # If biort has 6 elements instead of 4, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.biort) == 4:
            h0o, g0o, h1o, g1o = self.biort
        elif len(self.biort) == 6:
            h0o, g0o, h1o, g1o, h2o, g2o = self.biort
        else:
            raise ValueError('Biort wavelet must have 6 or 4 components.')

        # If qshift has 12 elements instead of 8, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.qshift) == 8:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = self.qshift
        elif len(self.qshift) == 12:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b, h2a, h2b = self.qshift[:10]
        else:
            raise ValueError('Qshift wavelet must have 12 or 8 components.')

        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 divisible by 2 then we need to extend X by adding a row at the bottom
            X = to_array(X)
            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 divisible by 2 then we need to extend X by adding a col to the left
            X = to_array(X)
            X = np.hstack((X, X[:,[-1]]))
            initial_col_extend = 1

        extended_size = X.shape

        # Copy X to the device if necessary
        X = to_device(X, queue=queue)

        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 a third output component.
            Yscale = [None,] * nlevels

        complex_dtype = np.complex64

        if nlevels >= 1:
            # Do odd top-level filters on cols.
            Lo = axis_convolve(X,h0o,axis=0,queue=queue)
            Hi = axis_convolve(X,h1o,axis=0,queue=queue)
            if len(self.biort) >= 6:
                Ba = axis_convolve(X,h2o,axis=0,queue=queue)

            # Do odd top-level filters on rows.
            LoLo = axis_convolve(Lo,h0o,axis=1,queue=queue)

            if len(self.biort) >= 6:
                diag = axis_convolve(Ba,h2o,axis=1,queue=queue)
            else:
                diag = axis_convolve(Hi,h1o,axis=1,queue=queue)

            Yh[0] = q2c(
                axis_convolve(Hi,h0o,axis=1,queue=queue),
                axis_convolve(Lo,h1o,axis=1,queue=queue),
                diag,
                queue=queue
            )

            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 divisible by 4
                LoLo = to_array(LoLo)
                LoLo = np.vstack((LoLo[:1,:], LoLo, LoLo[-1:,:]))

            if col_size % 4 != 0:
                # Extend by 2 cols if no. of cols of LoLo are not divisible by 4
                LoLo = to_array(LoLo)
                LoLo = np.hstack((LoLo[:,:1], LoLo, LoLo[:,-1:]))

            # Do even Qshift filters on rows.
            Lo = axis_convolve_dfilter(LoLo,h0b,axis=0,queue=queue)
            Hi = axis_convolve_dfilter(LoLo,h1b,axis=0,queue=queue)
            if len(self.qshift) >= 12:
                Ba = axis_convolve_dfilter(LoLo,h2b,axis=0,queue=queue)

            # Do even Qshift filters on columns.
            LoLo = axis_convolve_dfilter(Lo,h0b,axis=1,queue=queue)

            if len(self.qshift) >= 12:
                diag = axis_convolve_dfilter(Ba,h2b,axis=1,queue=queue)
            else:
                diag = axis_convolve_dfilter(Hi,h1b,axis=1,queue=queue)

            Yh[level] = q2c(
                axis_convolve_dfilter(Hi,h0b,axis=1,queue=queue),
                axis_convolve_dfilter(Lo,h1b,axis=1,queue=queue),
                diag,
                queue=queue
            )

            if include_scale:
                Yscale[level] = 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 Pyramid(Yl, tuple(Yh), tuple(Yscale))
        else:
            return Pyramid(Yl, tuple(Yh))
예제 #20
0
    def forward_channels(self, X, nlevels=3, include_scale=False):
        """Perform a *n*-level DTCWT decompostion on a 3D array *X*.

        Can provide the forward transform with either an np array (naive usage),
        or a tensorflow variable or placeholder (designed usage).

        :param X: 3D real array. Batch of matrices whose columns are to be
            transformed (i.e. the second dimension).
        :param nlevels: Number of levels of wavelet decomposition

        :returns: A :py:class:`dtcwt.tf.Pyramid` 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:: Fergal Cotter <*****@*****.**>, Sep 2017
        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Nick Kingsbury, Cambridge University, May 2002
        .. codeauthor:: Cian Shaffrey, Cambridge University, May 2002

        """
        # Check if a numpy array was provided
        numpy = False
        try:
            dtype = X.dtype
        except AttributeError:
            X = asfarray(X)
            dtype = X.dtype

        if dtype in np_dtypes:
            numpy = True
            if len(X.shape) != 3:
                raise ValueError(
                    'Incorrect input shape for the forward_channels ' +
                    'method ' + str(X.shape) + '. For Inputs of 1 or 2 ' +
                    'dimensions, use the forward method.')
            # Need this because colfilter and friends assumes input is 2d
            X = tf.Variable(X, dtype=tf.float32, trainable=False)
        elif dtype in tf_dtypes:
            X_shape = X.get_shape().as_list()
            if len(X.get_shape().as_list()) != 3:
                raise ValueError(
                    'Incorrect input shape for the forward_channels ' +
                    'method ' + str(X_shape) + '. For Inputs of 1 or 2 ' +
                    'dimensions, use the forward method.')
        else:
            raise ValueError('I cannot handle the variable you have ' +
                             'provided of type ' + str(X.dtype) + '. ' +
                             'Inputs should be a numpy or tf array')

        X_shape = tuple(X.get_shape().as_list())
        size = '{}'.format(X_shape[1])
        name = 'dtcwt_fwd_{}'.format(size)

        # Do the forward transform
        with tf.variable_scope(name):
            Yl, Yh, Yscale = self._forward_ops(X, nlevels)

        if include_scale:
            return Pyramid(Yl, Yh, Yscale, numpy)
        else:
            return Pyramid(Yl, Yh, None, numpy)
예제 #21
0
파일: transform2d.py 프로젝트: rjw57/dtcwt
    def forward(self, X, nlevels=3, 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

        :returns: A :py:class:`dtcwt.Pyramid` compatible object representing the transform-domain signal

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

        """
        # If biort has 6 elements instead of 4, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.biort) == 4:
            h0o, g0o, h1o, g1o = self.biort
        elif len(self.biort) == 6:
            h0o, g0o, h1o, g1o, h2o, g2o = self.biort
        else:
            raise ValueError('Biort wavelet must have 6 or 4 components.')

        # If qshift has 12 elements instead of 8, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.qshift) == 8:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = self.qshift
        elif len(self.qshift) == 12:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b, h2a, h2b = self.qshift[:10]
        else:
            raise ValueError('Qshift wavelet must have 12 or 8 components.')

        X = np.atleast_2d(asfarray(X))
        original_size = X.shape

        if len(X.shape) >= 3:
            raise ValueError('The entered image is {0}, which is invalid '.
                             format('x'.join(list(str(s) for s in X.shape))) +
                             'for the 2D transform in a numpy backend. ' +
                             'Please enter each image slice separately.')

        # 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 Pyramid(X, (), ())
            else:
                return Pyramid(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
            if len(self.biort) >= 6:
                Ba = colfilter(X,h2o).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:6:5] = q2c(colfilter(Hi,h0o).T)     # Horizontal pair
            Yh[0][:,:,2:4:1] = q2c(colfilter(Lo,h1o).T)     # Vertical pair
            if len(self.biort) >= 6:
                Yh[0][:,:,1:5:3] = q2c(colfilter(Ba,h2o).T)     # Diagonal pair
            else:
                Yh[0][:,:,1:5:3] = 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[:1,:], 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[:,:1], LoLo, LoLo[:,-1:]))

            # Do even Qshift filters on rows.
            Lo = coldfilt(LoLo,h0b,h0a).T
            Hi = coldfilt(LoLo,h1b,h1a).T
            if len(self.qshift) >= 12:
                Ba = coldfilt(LoLo,h2b,h2a).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:6:5] = q2c(coldfilt(Hi,h0b,h0a).T)  # Horizontal
            Yh[level][:,:,2:4:1] = q2c(coldfilt(Lo,h1b,h1a).T)  # Vertical
            if len(self.qshift) >= 12:
                Yh[level][:,:,1:5:3] = q2c(coldfilt(Ba,h2b,h2a).T)  # Diagonal
            else:
                Yh[level][:,:,1:5:3] = q2c(coldfilt(Hi,h1b,h1a).T)  # Diagonal

            if include_scale:
                Yscale[level] = 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 Pyramid(Yl, tuple(Yh), tuple(Yscale))
        else:
            return Pyramid(Yl, tuple(Yh))
예제 #22
0
    def forward(self,
                X,
                nlevels=3,
                include_scale=False,
                discard_level_1=False):
        """Perform a *n*-level DTCWT-3D decompostion on a 3D matrix *X*.

        :param X: 3D real array-like object
        :param nlevels: Number of levels of wavelet decomposition
        :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.

        :returns: a :py:class:`dtcwt.Pyramid` instance

        Each element of the Pyramid *highpasses* tuple is a 4D complex array
        with the 4th dimension having size 28. The 3D slice ``[l][:,:,:,d]``
        corresponds to the complex higpass coefficients for direction d at
        level l where d and l are both 0-indexed.

        If *biort* or *qshift* are strings, they are used as an argument to the
        :py:func:`dtcwt.coeffs.biort` or :py:func:`dtcwt.coeffs.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).

        There are two values for *ext_mode*, either 4 or 8. If *ext_mode* = 4,
        check whether 1st level is divisible by 2 (if not we raise a
        ``ValueError``). Also check whether from 2nd level onwards, the coefs can
        be divided by 4. If any dimension size is not a multiple of 4, append extra
        coefs by repeating the edges. If *ext_mode* = 8, check whether 1st level is
        divisible by 4 (if not we raise a ``ValueError``). Also check whether from
        2nd level onwards, the coeffs can be divided by 8. If any dimension size is
        not a multiple of 8, append extra coeffs by repeating the edges twice.

        If *discard_level_1* is True the highpass coefficients at level 1 will not be
        discarded. (And, in fact, will never be calculated.) This turns the
        transform from being 8:1 redundant to being 1:1 redundant at the cost of
        no-longer allowing perfect reconstruction. If this option is selected
        then the first element of the *highpasses* tuple will be `None`. Note
        that :py:func:`dtcwt.Transform3d.inverse` will accept the first element
        being `None` and will treat it as being zero.

        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Huizhong Chen, Jan 2009
        .. codeauthor:: Nick Kingsbury, Cambridge University, July 1999.

        """
        X = np.atleast_3d(asfarray(X))

        # If biort has 6 elements instead of 4, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.biort) == 4:
            h0o, g0o, h1o, g1o = self.biort
        elif len(self.biort) == 6:
            h0o, g0o, h1o, g1o, h2o, g2o = self.biort
        else:
            raise ValueError('Biort wavelet must have 6 or 4 components.')
        # If qshift has 12 elements instead of 8, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.qshift) == 8:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = self.qshift
        elif len(self.qshift) == 12:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b, h2a, h2b = self.qshift[:10]
        else:
            raise ValueError('Qshift wavelet must have 12 or 8 components.')

        # Check value of ext_mode. TODO: this should really be an enum :S
        if self.ext_mode != 4 and self.ext_mode != 8:
            raise ValueError('ext_mode must be one of 4 or 8')

        Yl = X
        Yh = [
            None,
        ] * nlevels

        if include_scale:
            # this is only required if the user specifies a third output component.
            Yscale = [
                None,
            ] * nlevels

        #pdb.set_trace()
        # level is 0-indexed
        for level in xrange(nlevels):
            # Transform
            if level == 0 and discard_level_1:
                Yl = _level1_xfm_no_highpass(Yl, h0o, h1o, self.ext_mode)
            elif level == 0 and not discard_level_1:
                Yl, Yh[level] = _level1_xfm(Yl, h0o, h1o, self.ext_mode)
            else:
                Yl, Yh[level] = _level2_xfm(Yl, h0a, h0b, h1a, h1b,
                                            self.ext_mode)
            if include_scale:
                Yscale[level] = Yl.copy()

                #Yh[nlevels+1]=1 #to throw an error for debugging in nose
        if include_scale:
            return Pyramid(Yl, tuple(Yh), tuple(Yscale))
        else:
            return Pyramid(Yl, tuple(Yh))
예제 #23
0
def _upsample_columns(X, method=None):
    """
    The centre of columns of X, an M-columned matrix, are assumed to have co-ordinates
    { 0, 1, 2, ... , M-1 } which means that the up-sampled matrix's columns should sample
    from { -0.25, 0.25, 0.75, ... , M-1.25 }. We can view that as an interleaved set of teo
    *convolutions* of X. The first, A, using a kernel equivalent to sampling the { -0.25, 0.75,
    1.75, 2.75, ... M-1.25 } columns and the second, B, sampling the { 0.25, 1.25, ... , M-0.75 }
    columns.
    """
    if method is None:
        method = 'lanczos'

    X = np.atleast_2d(asfarray(X))

    out_shape = list(X.shape)
    out_shape[1] *= 2
    output = np.zeros(out_shape, dtype=X.dtype)

    # Centres of sampling for A and B convolutions
    M = X.shape[1]
    A_columns = np.linspace(-0.25, M - 1.25, M)
    B_columns = A_columns + 0.5

    # For A columns sample at x = ceil(x) - 0.25 with ceil(x) = { 0, 1, 2, ..., M-1 }
    # For B columns sample at x = floor(x) + 0.25 with floor(x) = { 0, 1, 2, ..., M-1 }
    int_columns = np.linspace(0, M - 1, M)

    if method == 'lanczos':
        # Lanczos kernel width
        a = 3.0
        sample_offsets = np.arange(-a, a + 1)

        # For A: if i = ceil(x) + di, => ceil(x) - i = -0.25 - di
        # For B: if i = floor(x) + di, => floor(x) - i = 0.25 - di
        l_as = np.sinc(-0.25 - sample_offsets) * np.sinc(
            (-0.25 - sample_offsets) / a)
        l_bs = np.sinc(0.25 - sample_offsets) * np.sinc(
            (0.25 - sample_offsets) / a)
    elif method == 'nearest':
        # Nearest neighbour kernel width is 1
        sample_offsets = [
            0,
        ]
        l_as = l_bs = [
            1,
        ]
    elif method == 'bilinear':
        # Bilinear kernel width is technically 2 but we need to offset the kernels differently
        # for A and B columns:
        sample_offsets = [-1, 0, 1]
        l_as = [0.25, 0.75, 0]
        l_bs = [0, 0.75, 0.25]
    else:
        raise ValueError('Unknown interpolation mode: {0}'.format(mode))

    # Convolve
    for di, l_a, l_b in zip(sample_offsets, l_as, l_bs):
        columns = reflect(int_columns + di, -0.5, M - 0.5).astype(np.int)

        output[:, 0::2, ...] += l_a * X[:, columns, ...]
        output[:, 1::2, ...] += l_b * X[:, columns, ...]

    return output
예제 #24
0
파일: common.py 프로젝트: xiaoxwxw/dtcwt
 def __init__(self, lowpass, highpasses, scales=None):
     self.lowpass = asfarray(lowpass)
     self.highpasses = tuple(
         asfarray(x) if x is not None else None for x in highpasses)
     self.scales = tuple(asfarray(x)
                         for x in scales) if scales is not None else None
예제 #25
0
    def forward_channels(self, X, data_format, nlevels=3,
                         include_scale=False):
        """ Perform a forward transform on an image with multiple channels.

        Will perform the DTCWT independently on each channel.

        :param X: Input image which you wish to transform.
        :param int nlevels: Number of levels of the dtcwt transform to
            calculate.
        :param bool include_scale: Whether or not to return the lowpass results
            at each scale of the transform, or only at the highest scale (as is
            custom for multiresolution analysis)
        :param str data_format: An optional string of the form:
            "nhw" (or "chw"), "hwn" (or "hwc"), "nchw" or "nhwc". Note that for
            these strings, 'n' is used to indicate where the batch dimension is,
            'c' is used to indicate where the image channels are, 'h' is used to
            indicate where the row dimension is, and 'c' is used to indicate
            where the columns are. If the data_format is:

                - "nhw" : the input will be interpreted as a batch of 2D images,
                  with the batch dimension as the first.
                - "chw" : will function exactly the same as "nhw" but is offered
                  to indicate the input is a 2D image with channels.
                - "hwn" : the input will be interpreted as a batch of 2D images
                  with the batch dimension as the last.
                - "hwc" : will function exatly the same as "hwc" but is offered
                  to indicate the input is a 2D image with channels.
                - "nchw" : the input is a batch of images with channel dimension
                  as the second dimension. Batch dimension is first.
                - "nhwc" : the input is a batch of images with channel dimension
                  as the last dimension. Batch dimension is first.

        :returns: A :py:class:`dtcwt.tf.Pyramid` object

        .. codeauthor:: Fergal Cotter <*****@*****.**>, Feb 2017
        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Nick Kingsbury, Cambridge University, Sept 2001
        .. codeauthor:: Cian Shaffrey, Cambridge University, Sept 2001
        """
        data_format = data_format.lower()
        formats_3d = ("nhw", "chw", "hwn", "hwc")
        formats_4d = ("nchw", "nhwc")
        formats = formats_3d + formats_4d
        if data_format not in formats:
            raise ValueError('The data format must be one of: {}'.
                             format(formats))

        try:
            dtype = X.dtype
        except AttributeError:
            X = asfarray(X)
            dtype = X.dtype

        numpy = False
        if dtype in np_dtypes:
            numpy = True
            X = np.atleast_2d(X)
            X = tf.Variable(X, dtype=tf.float32, trainable=False)

        if X.dtype not in tf_dtypes:
            raise ValueError('I cannot handle the variable you have ' +
                             'provided of type ' + str(X.dtype) + '. ' +
                             'Inputs should be a numpy or tf array.')

        X_shape = X.get_shape().as_list()
        if not ((len(X_shape) == 3 and data_format in formats_3d) or
                (len(X_shape) == 4 and data_format in formats_4d)):
            raise ValueError(
                'The entered variable has incorrect shape - ' +
                str(X_shape) + ' for the specified data_format ' +
                data_format + '.')

        # Reshape the inputs to all be 3d inputs of shape (batch, h, w)
        if data_format in formats_4d:
            # Move all of the channels into the batch dimension for the
            # input.  This may involve transposing, depending on the data
            # format
            with tf.variable_scope('ch_to_batch'):
                s = X.get_shape().as_list()[1:]
                size = '{}x{}'.format(s[0], s[1])
                name = 'dtcwt_fwd_{}'.format(size)
                if data_format == 'nhwc':
                    nch = s[2]
                    X = tf.transpose(X, perm=[0, 3, 1, 2])
                    X = tf.reshape(X, [-1, s[0], s[1]])
                else:
                    nch = s[0]
                    X = tf.reshape(X, [-1, s[1], s[2]])
        elif data_format == "hwn" or data_format == "hwc":
            s = X.get_shape().as_list()[:2]
            size = '{}x{}'.format(s[0], s[1])
            name = 'dtcwt_fwd_{}'.format(size)
            with tf.variable_scope('ch_to_start'):
                X = tf.transpose(X, perm=[2,0,1])
        else:
            s = X.get_shape().as_list()[1:3]
            size = '{}x{}'.format(s[0], s[1])
            name = 'dtcwt_fwd_{}'.format(size)

        # Do the dtcwt, now with a 3 dimensional input
        with tf.variable_scope(name):
            Yl, Yh, Yscale = self._forward_ops(X, nlevels)

        # Reshape it all again to match the input
        if data_format in formats_4d:
            # Put the channels back into their correct positions
            with tf.variable_scope('batch_to_ch'):
                # Reshape Yl
                s = Yl.get_shape().as_list()[1:]
                Yl = tf.reshape(Yl, [-1, nch, s[0], s[1]], name='Yl_reshape')
                if data_format == 'nhwc':
                    Yl = tf.transpose(Yl, [0, 2, 3, 1], name='Yl_ch_to_end')

                # Reshape Yh
                with tf.variable_scope('Yh'):
                    Yh_new = [None,] * nlevels
                    for i in range(nlevels):
                        s = Yh[i].get_shape().as_list()[1:]
                        Yh_new[i] = tf.reshape(
                            Yh[i], [-1, nch, s[0], s[1], s[2]],
                            name='scale{}_reshape'.format(i))
                        if data_format == 'nhwc':
                            Yh_new[i] = tf.transpose(
                                Yh_new[i], [0, 2, 3, 1, 4],
                                name='scale{}_ch_to_end'.format(i))
                    Yh = tuple(Yh_new)

                # Reshape Yscale
                if include_scale:
                    with tf.variable_scope('Yscale'):
                        Yscale_new = [None,] * nlevels
                        for i in range(nlevels):
                            s = Yscale[i].get_shape().as_list()[1:]
                            Yscale_new[i] = tf.reshape(
                                Yscale[i], [-1, nch, s[0], s[1]],
                                name='scale{}_reshape'.format(i))
                            if data_format == 'nhwc':
                                Yscale_new[i] = tf.transpose(
                                    Yscale_new[i], [0, 2, 3, 1],
                                    name='scale{}_ch_to_end'.format(i))
                        Yscale = tuple(Yscale_new)

        elif data_format == "hwn" or data_format == "hwc":
            with tf.variable_scope('ch_to_end'):
                Yl = tf.transpose(Yl, perm=[1,2,0], name='Yl')
                Yh = tuple(
                    tf.transpose(x, [1, 2, 0, 3], name='Yh{}'.format(i))
                    for i,x in enumerate(Yh))
                if include_scale:
                    Yscale = tuple(
                        tf.transpose(x, [1, 2, 0], name='Yscale{}'.format(i))
                        for i,x in enumerate(Yscale))

        # Return the pyramid
        if include_scale:
            return Pyramid(Yl, Yh, Yscale, numpy)
        else:
            return Pyramid(Yl, Yh, None, numpy)
예제 #26
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)
예제 #27
0
파일: lowlevel.py 프로젝트: timseries/dtcwt
def colifilt(X, ha, hb):
    """ Filter the columns of image X using the two filters ha and hb =
    reverse(ha).  ha operates on the odd samples of X and hb on the even
    samples.  Both filters should be even length, and h should be approx linear
    phase with a quarter sample advance from its mid pt (i.e `:math:`|h(m/2)| >
    |h(m/2 + 1)|`).
    
    .. code-block:: text

                          ext       left edge                      right edge       ext
        Level 2:        !               |               !               |               !
        +q filt on x      b       b       a       a       a       a       b       b       
        -q filt on o          a       a       b       b       b       b       a       a
        Level 1:        !               |               !               |               !
        odd filt on .    b   b   b   b   a   a   a   a   a   a   a   a   b   b   b   b   
        odd filt on .      a   a   a   a   b   b   b   b   b   b   b   b   a   a   a   a
   
    The output is interpolated by two from the input sample rate and the
    results from the two filters, Ya and Yb, are interleaved to give Y.
    Symmetric extension with repeated end samples is used on the composite X
    columns before each filter is applied.
   
    .. codeauthor:: Rich Wareham <*****@*****.**>, August 2013
    .. codeauthor:: Cian Shaffrey, Cambridge University, August 2000
    .. codeauthor:: Nick Kingsbury, Cambridge University, August 2000

    """
    # Make sure all inputs are arrays
    X = asfarray(X)
    ha = asfarray(ha)
    hb = asfarray(hb)

    r, c = X.shape
    if r % 2 != 0:
        raise ValueError('No. of rows in X must be a multiple of 2')

    if ha.shape != hb.shape:
        raise ValueError('Shapes of ha and hb must be the same')

    if ha.shape[0] % 2 != 0:
        raise ValueError('Lengths of ha and hb must be even')

    m = ha.shape[0]
    m2 = np.fix(m*0.5)

    Y = np.zeros((r*2,c), dtype=X.dtype)
    if not np.any(np.nonzero(X[:])[0]):
        return Y

    if m2 % 2 == 0:
        # m/2 is even, so set up t to start on d samples.
        # Set up vector for symmetric extension of X with repeated end samples.
        # Use 'reflect' so r < m2 works OK.
        xe = reflect(np.arange(-m2, r+m2, dtype=np.int), -0.5, r-0.5)
       
        t = np.arange(3, r+m, 2)
        if np.sum(ha*hb) > 0:
            ta = t
            tb = t - 1
        else:
            ta = t - 1
            tb = t
       
        # Select odd and even samples from ha and hb. Note that due to 0-indexing
        # 'odd' and 'even' are not perhaps what you might expect them to be.
        hao = as_column_vector(ha[0:m:2])
        hae = as_column_vector(ha[1:m:2])
        hbo = as_column_vector(hb[0:m:2])
        hbe = as_column_vector(hb[1:m:2])
       
        s = np.arange(0,r*2,4)
       
        Y[s,:]   = _column_convolve(X[xe[tb-2],:],hae)
        Y[s+1,:] = _column_convolve(X[xe[ta-2],:],hbe)
        Y[s+2,:] = _column_convolve(X[xe[tb  ],:],hao)
        Y[s+3,:] = _column_convolve(X[xe[ta  ],:],hbo)
    else:
        # m/2 is odd, so set up t to start on b samples.
        # Set up vector for symmetric extension of X with repeated end samples.
        # Use 'reflect' so r < m2 works OK.
        xe = reflect(np.arange(-m2, r+m2, dtype=np.int), -0.5, r-0.5)

        t = np.arange(2, r+m-1, 2)
        if np.sum(ha*hb) > 0:
            ta = t
            tb = t - 1
        else:
            ta = t - 1
            tb = t
       
        # Select odd and even samples from ha and hb. Note that due to 0-indexing
        # 'odd' and 'even' are not perhaps what you might expect them to be.
        hao = as_column_vector(ha[0:m:2])
        hae = as_column_vector(ha[1:m:2])
        hbo = as_column_vector(hb[0:m:2])
        hbe = as_column_vector(hb[1:m:2])
       
        s = np.arange(0,r*2,4)
       
        Y[s,:]   = _column_convolve(X[xe[tb],:],hao)
        Y[s+1,:] = _column_convolve(X[xe[ta],:],hbo)
        Y[s+2,:] = _column_convolve(X[xe[tb],:],hae)
        Y[s+3,:] = _column_convolve(X[xe[ta],:],hbe)

    return Y
예제 #28
0
    def forward(self, X, nlevels=3, include_scale=False):
        """Perform a *n*-level DTCWT-3D decompostion on a 3D matrix *X*.

        :param X: 3D real array-like object
        :param nlevels: Number of levels of wavelet decomposition

        :returns: a :py:class:`dtcwt.Pyramid` instance

        Each element of the Pyramid *highpasses* tuple is a 4D complex array
        with the 4th dimension having size 28. The 3D slice ``[l][:,:,:,d]``
        corresponds to the complex higpass coefficients for direction d at
        level l where d and l are both 0-indexed.

        If *biort* or *qshift* are strings, they are used as an argument to the
        :py:func:`dtcwt.coeffs.biort` or :py:func:`dtcwt.coeffs.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).

        There are two values for *ext_mode*, either 4 or 8. If *ext_mode* = 4,
        check whether 1st level is divisible by 2 (if not we raise a
        ``ValueError``). Also check whether from 2nd level onwards, the coefs can
        be divided by 4. If any dimension size is not a multiple of 4, append extra
        coefs by repeating the edges. If *ext_mode* = 8, check whether 1st level is
        divisible by 4 (if not we raise a ``ValueError``). Also check whether from
        2nd level onwards, the coeffs can be divided by 8. If any dimension size is
        not a multiple of 8, append extra coeffs by repeating the edges twice.

        If *discard_level_1* is True the highpass coefficients at level 1 will not be
        discarded. (And, in fact, will never be calculated.) This turns the
        transform from being 8:1 redundant to being 1:1 redundant at the cost of
        no-longer allowing perfect reconstruction. If this option is selected
        then the first element of the *highpasses* tuple will be `None`. Note
        that :py:func:`dtcwt.Transform3d.inverse` will accept the first element
        being `None` and will treat it as being zero.

        .. codeauthor:: Rich Wareham <*****@*****.**>, Aug 2013
        .. codeauthor:: Huizhong Chen, Jan 2009
        .. codeauthor:: Nick Kingsbury, Cambridge University, July 1999.

        """
        X = np.atleast_3d(asfarray(X))

        # If biort has 6 elements instead of 4, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.biort) == 4:
            h0o, g0o, h1o, g1o = self.biort
        elif len(self.biort) == 6:
            h0o, g0o, h1o, g1o, h2o, g2o = self.biort
        else:
            raise ValueError('Biort wavelet must have 6 or 4 components.')
        # If qshift has 12 elements instead of 8, then it's a modified
        # rotationally symmetric wavelet
        # FIXME: there's probably a nicer way to do this
        if len(self.qshift) == 8:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b = self.qshift
        elif len(self.qshift) == 12:
            h0a, h0b, g0a, g0b, h1a, h1b, g1a, g1b, h2a, h2b = self.qshift[:10]
        else:
            raise ValueError('Qshift wavelet must have 12 or 8 components.')

        # Check value of ext_mode. TODO: this should really be an enum :S
        if self.ext_mode != 4 and self.ext_mode != 8:
            raise ValueError('ext_mode must be one of 4 or 8')

        Yl = X
        Yh = [None,] * nlevels

        if include_scale:
            # this is only required if the user specifies a third output component.
            Yscale = [None,] * nlevels

        # level is 0-indexed
        for level in xrange(nlevels):
            # Transform
            if level == 0 and self.discard_level_1:
                Yl = _level1_xfm_no_highpass(Yl, h0o, h1o, self.ext_mode)
            elif level == 0 and not self.discard_level_1:
                Yl, Yh[level] = _level1_xfm(Yl, h0o, h1o, self.ext_mode)
            else:
                Yl, Yh[level] = _level2_xfm(Yl, h0a, h0b, h1a, h1b, self.ext_mode)
            if include_scale:
                Yscale[level] = Yl.copy()

        if include_scale:
            return Pyramid(Yl, tuple(Yh), tuple(Yscale))
        else:
            return Pyramid(Yl, tuple(Yh))
예제 #29
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)
예제 #30
0
파일: common.py 프로젝트: ghisvail/dtcwt
 def __init__(self, lowpass, highpasses, scales=None):
     self.lowpass = asfarray(lowpass)
     self.highpasses = tuple(asfarray(x) if x is not None else None for x in highpasses)
     self.scales = tuple(asfarray(x) for x in scales) if scales is not None else None
예제 #31
0
def colifilt(X, ha, hb):
    """ Filter the columns of image X using the two filters ha and hb =
    reverse(ha).  ha operates on the odd samples of X and hb on the even
    samples.  Both filters should be even length, and h should be approx linear
    phase with a quarter sample advance from its mid pt (i.e `:math:`|h(m/2)| >
    |h(m/2 + 1)|`).

    .. code-block:: text

                          ext       left edge                      right edge       ext
        Level 2:        !               |               !               |               !
        +q filt on x      b       b       a       a       a       a       b       b
        -q filt on o          a       a       b       b       b       b       a       a
        Level 1:        !               |               !               |               !
        odd filt on .    b   b   b   b   a   a   a   a   a   a   a   a   b   b   b   b
        odd filt on .      a   a   a   a   b   b   b   b   b   b   b   b   a   a   a   a

    The output is interpolated by two from the input sample rate and the
    results from the two filters, Ya and Yb, are interleaved to give Y.
    Symmetric extension with repeated end samples is used on the composite X
    columns before each filter is applied.

    .. codeauthor:: Rich Wareham <*****@*****.**>, August 2013
    .. codeauthor:: Cian Shaffrey, Cambridge University, August 2000
    .. codeauthor:: Nick Kingsbury, Cambridge University, August 2000

    """
    # Make sure all inputs are arrays
    X = asfarray(X)
    ha = asfarray(ha)
    hb = asfarray(hb)

    r, c = X.shape
    if r % 2 != 0:
        raise ValueError('No. of rows in X must be a multiple of 2')

    if ha.shape != hb.shape:
        raise ValueError('Shapes of ha and hb must be the same')

    if ha.shape[0] % 2 != 0:
        raise ValueError('Lengths of ha and hb must be even')

    m = ha.shape[0]
    m2 = np.fix(m * 0.5)

    Y = np.zeros((r * 2, c), dtype=X.dtype)
    if not np.any(np.nonzero(X[:])[0]):
        return Y

    if m2 % 2 == 0:
        # m/2 is even, so set up t to start on d samples.
        # Set up vector for symmetric extension of X with repeated end samples.
        # Use 'reflect' so r < m2 works OK.
        xe = reflect(np.arange(-m2, r + m2, dtype=np.int), -0.5, r - 0.5)

        t = np.arange(3, r + m, 2)
        if np.sum(ha * hb) > 0:
            ta = t
            tb = t - 1
        else:
            ta = t - 1
            tb = t

        # Select odd and even samples from ha and hb. Note that due to 0-indexing
        # 'odd' and 'even' are not perhaps what you might expect them to be.
        hao = as_column_vector(ha[0:m:2])
        hae = as_column_vector(ha[1:m:2])
        hbo = as_column_vector(hb[0:m:2])
        hbe = as_column_vector(hb[1:m:2])

        s = np.arange(0, r * 2, 4)

        Y[s, :] = _column_convolve(X[xe[tb - 2], :], hae)
        Y[s + 1, :] = _column_convolve(X[xe[ta - 2], :], hbe)
        Y[s + 2, :] = _column_convolve(X[xe[tb], :], hao)
        Y[s + 3, :] = _column_convolve(X[xe[ta], :], hbo)
    else:
        # m/2 is odd, so set up t to start on b samples.
        # Set up vector for symmetric extension of X with repeated end samples.
        # Use 'reflect' so r < m2 works OK.
        xe = reflect(np.arange(-m2, r + m2, dtype=np.int), -0.5, r - 0.5)

        t = np.arange(2, r + m - 1, 2)
        if np.sum(ha * hb) > 0:
            ta = t
            tb = t - 1
        else:
            ta = t - 1
            tb = t

        # Select odd and even samples from ha and hb. Note that due to 0-indexing
        # 'odd' and 'even' are not perhaps what you might expect them to be.
        hao = as_column_vector(ha[0:m:2])
        hae = as_column_vector(ha[1:m:2])
        hbo = as_column_vector(hb[0:m:2])
        hbe = as_column_vector(hb[1:m:2])

        s = np.arange(0, r * 2, 4)

        Y[s, :] = _column_convolve(X[xe[tb], :], hao)
        Y[s + 1, :] = _column_convolve(X[xe[ta], :], hbo)
        Y[s + 2, :] = _column_convolve(X[xe[tb], :], hae)
        Y[s + 3, :] = _column_convolve(X[xe[ta], :], hbe)

    return Y