Exemple #1
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)
Exemple #2
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)
Exemple #3
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
Exemple #4
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)
Exemple #5
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)
Exemple #6
0
def dtwavexfm(X,
              nlevels=3,
              biort=DEFAULT_BIORT,
              qshift=DEFAULT_QSHIFT,
              include_scale=False):
    """Perform a *n*-level DTCWT decompostion on a 1D column vector *X* (or on
    the columns of a matrix *X*).

    :param X: 1D real array or 2D real array whose columns are to be transformed
    :param nlevels: Number of levels of wavelet decomposition
    :param biort: Level 1 wavelets to use. See :py:func:`biort`.
    :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`.

    :returns Yl: The real lowpass image from the final level
    :returns Yh: A tuple containing the (N, M, 6) shape complex highpass subimages for each level.
    :returns Yscale: If *include_scale* is True, a tuple containing real lowpass coefficients for every scale.

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

    Example::

        # Performs a 5-level transform on the real image X using the 13,19-tap
        # filters for level 1 and the Q-shift 14-tap filters for levels >= 2.
        Yl, Yh = dtwavexfm(X,5,'near_sym_b','qshift_b')

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

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

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

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

    L = np.asanyarray(X.shape)

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

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

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

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

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

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

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

    Yl = Lo

    if include_scale:
        return Yl, Yh, Yscale
    else:
        return Yl, Yh
Exemple #7
0
def dtwavexfm3(X, nlevels=3, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, ext_mode=4, 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 discard_level_1: 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 *discard_level_1* is True the highpass coefficients at level 1 will 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.

    Example::

        # Performs a 3-level transform on the real 3D array X using the 13,19-tap
        # filters for level 1 and the Q-shift 14-tap filters for levels >= 2.
        Yl, Yh = dtwavexfm3(X, 3, 'near_sym_b', 'qshift_b')

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

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

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

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

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

    Yl = X
    Yh = [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, ext_mode)
        elif level == 0 and not discard_level_1:
            Yl, Yh[level] = _level1_xfm(Yl, h0o, h1o, ext_mode)
        else:
            Yl, Yh[level] = _level2_xfm(Yl, h0a, h0b, h1a, h1b, ext_mode)

    return Yl, tuple(Yh)
Exemple #8
0
def dtwavexfm(X, nlevels=3, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, include_scale=False):
    """Perform a *n*-level DTCWT decompostion on a 1D column vector *X* (or on
    the columns of a matrix *X*).

    :param X: 1D real array or 2D real array whose columns are to be transformed
    :param nlevels: Number of levels of wavelet decomposition
    :param biort: Level 1 wavelets to use. See :py:func:`biort`.
    :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`.

    :returns Yl: The real lowpass image from the final level
    :returns Yh: A tuple containing the (N, M, 6) shape complex highpass subimages for each level.
    :returns Yscale: If *include_scale* is True, a tuple containing real lowpass coefficients for every scale.

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

    Example::

        # Performs a 5-level transform on the real image X using the 13,19-tap
        # filters for level 1 and the Q-shift 14-tap filters for levels >= 2.
        Yl, Yh = dtwavexfm(X,5,'near_sym_b','qshift_b')

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

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

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

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

    L = np.asanyarray(X.shape)

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

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

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

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

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

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

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

    Yl = Lo

    if include_scale:
        return Yl, Yh, Yscale
    else:
        return Yl, Yh
Exemple #9
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
Exemple #10
0
def dtwavexfm2(X, nlevels=3, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, include_scale=False):
    """Perform a *n*-level DTCWT-2D decompostion on a 2D matrix *X*.

    :param X: 2D real array
    :param nlevels: Number of levels of wavelet decomposition
    :param biort: Level 1 wavelets to use. See :py:func:`biort`.
    :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`.

    :returns Yl: The real lowpass image from the final level
    :returns Yh: A tuple containing the complex highpass subimages for each level.
    :returns Yscale: If *include_scale* is True, a tuple containing real lowpass coefficients for every scale.

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

    Example::

        # Performs a 3-level transform on the real image X using the 13,19-tap
        # filters for level 1 and the Q-shift 14-tap filters for levels >= 2.
        Yl, Yh = dtwavexfm2(X, 3, 'near_sym_b', 'qshift_b')

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

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

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

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

    original_size = X.shape

    if len(X.shape) >= 3:
        raise ValueError('The entered image is {0}, please enter each image slice separately.'.
                format('x'.join(list(str(s) for s in X.shape))))

    # The next few lines of code check to see if the image is odd in size, if so an extra ...
    # row/column will be added to the bottom/right of the image
    initial_row_extend = 0  #initialise
    initial_col_extend = 0
    if original_size[0] % 2 != 0:
        # if X.shape[0] is not divisable by 2 then we need to extend X by adding a row at the bottom
        X = np.vstack((X, X[[-1],:]))  # Any further extension will be done in due course.
        initial_row_extend = 1

    if original_size[1] % 2 != 0:
        # if X.shape[1] is not divisable by 2 then we need to extend X by adding a col to the left
        X = np.hstack((X, X[:,[-1]]))
        initial_col_extend = 1

    extended_size = X.shape

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

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

    complex_dtype = appropriate_complex_type_for(X)

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

        # Do odd top-level filters on rows.
        LoLo = colfilter(Lo,h0o).T
        Yh[0] = np.zeros((LoLo.shape[0] >> 1, LoLo.shape[1] >> 1, 6), dtype=complex_dtype)
        Yh[0][:,:,[0, 5]] = q2c(colfilter(Hi,h0o).T)     # Horizontal pair
        Yh[0][:,:,[2, 3]] = q2c(colfilter(Lo,h1o).T)     # Vertical pair
        Yh[0][:,:,[1, 4]] = q2c(colfilter(Hi,h1o).T)     # Diagonal pair

        if include_scale:
            Yscale[0] = LoLo

    for level in xrange(1, nlevels):
        row_size, col_size = LoLo.shape
        if row_size % 4 != 0:
            # Extend by 2 rows if no. of rows of LoLo are not divisable by 4
            LoLo = np.vstack((LoLo[[0],:], LoLo, LoLo[[-1],:]))

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

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

        # Do even Qshift filters on columns.
        LoLo = coldfilt(Lo,h0b,h0a).T

        Yh[level] = np.zeros((LoLo.shape[0]>>1, LoLo.shape[1]>>1, 6), dtype=complex_dtype)
        Yh[level][:,:,[0, 5]] = q2c(coldfilt(Hi,h0b,h0a).T)  # Horizontal
        Yh[level][:,:,[2, 3]] = q2c(coldfilt(Lo,h1b,h1a).T)  # Vertical
        Yh[level][:,:,[1, 4]] = q2c(coldfilt(Hi,h1b,h1a).T)  # Diagonal   

        if include_scale:
            Yscale[0] = LoLo

    Yl = LoLo

    if initial_row_extend == 1 and initial_col_extend == 1:
        logging.warn('The image entered is now a {0} NOT a {1}.'.format(
            'x'.join(list(str(s) for s in extended_size)),
            'x'.join(list(str(s) for s in original_size))))
        logging.warn(
            'The bottom row and rightmost column have been duplicated, prior to decomposition.')

    if initial_row_extend == 1 and initial_col_extend == 0:
        logging.warn('The image entered is now a {0} NOT a {1}.'.format(
            'x'.join(list(str(s) for s in extended_size)),
            'x'.join(list(str(s) for s in original_size))))
        logging.warn(
            'The bottom row has been duplicated, prior to decomposition.')

    if initial_row_extend == 0 and initial_col_extend == 1:
        logging.warn('The image entered is now a {0} NOT a {1}.'.format(
            'x'.join(list(str(s) for s in extended_size)),
            'x'.join(list(str(s) for s in original_size))))
        logging.warn(
            'The rightmost column has been duplicated, prior to decomposition.')

    if include_scale:
        return Yl, tuple(Yh), tuple(Yscale)
    else:
        return Yl, tuple(Yh)
Exemple #11
0
def dtwavexfm2(X, nlevels=3, biort=DEFAULT_BIORT, qshift=DEFAULT_QSHIFT, include_scale=False):
    """Perform a *n*-level DTCWT-2D decompostion on a 2D matrix *X*.

    :param X: 2D real array
    :param nlevels: Number of levels of wavelet decomposition
    :param biort: Level 1 wavelets to use. See :py:func:`biort`.
    :param qshift: Level >= 2 wavelets to use. See :py:func:`qshift`.

    :returns Yl: The real lowpass image from the final level
    :returns Yh: A tuple containing the complex highpass subimages for each level.
    :returns Yscale: If *include_scale* is True, a tuple containing real lowpass coefficients for every scale.

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

    Example::

        # Performs a 3-level transform on the real image X using the 13,19-tap
        # filters for level 1 and the Q-shift 14-tap filters for levels >= 2.
        Yl, Yh = dtwavexfm2(X, 3, 'near_sym_b', 'qshift_b')

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

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

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

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

    original_size = X.shape

    if len(X.shape) >= 3:
        raise ValueError(
            "The entered image is {0}, please enter each image slice separately.".format(
                "x".join(list(str(s) for s in X.shape))
            )
        )

    # The next few lines of code check to see if the image is odd in size, if so an extra ...
    # row/column will be added to the bottom/right of the image
    initial_row_extend = 0  # initialise
    initial_col_extend = 0
    if original_size[0] % 2 != 0:
        # if X.shape[0] is not divisable by 2 then we need to extend X by adding a row at the bottom
        X = np.vstack((X, X[[-1], :]))  # Any further extension will be done in due course.
        initial_row_extend = 1

    if original_size[1] % 2 != 0:
        # if X.shape[1] is not divisable by 2 then we need to extend X by adding a col to the left
        X = np.hstack((X, X[:, [-1]]))
        initial_col_extend = 1

    extended_size = X.shape

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

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

    complex_dtype = appropriate_complex_type_for(X)

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

        # Do odd top-level filters on rows.
        LoLo = colfilter(Lo, h0o).T
        Yh[0] = np.zeros((LoLo.shape[0] >> 1, LoLo.shape[1] >> 1, 6), dtype=complex_dtype)
        Yh[0][:, :, [0, 5]] = q2c(colfilter(Hi, h0o).T)  # Horizontal pair
        Yh[0][:, :, [2, 3]] = q2c(colfilter(Lo, h1o).T)  # Vertical pair
        Yh[0][:, :, [1, 4]] = q2c(colfilter(Hi, h1o).T)  # Diagonal pair

        if include_scale:
            Yscale[0] = LoLo

    for level in xrange(1, nlevels):
        row_size, col_size = LoLo.shape
        if row_size % 4 != 0:
            # Extend by 2 rows if no. of rows of LoLo are not divisable by 4
            LoLo = np.vstack((LoLo[[0], :], LoLo, LoLo[[-1], :]))

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

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

        # Do even Qshift filters on columns.
        LoLo = coldfilt(Lo, h0b, h0a).T

        Yh[level] = np.zeros((LoLo.shape[0] >> 1, LoLo.shape[1] >> 1, 6), dtype=complex_dtype)
        Yh[level][:, :, [0, 5]] = q2c(coldfilt(Hi, h0b, h0a).T)  # Horizontal
        Yh[level][:, :, [2, 3]] = q2c(coldfilt(Lo, h1b, h1a).T)  # Vertical
        Yh[level][:, :, [1, 4]] = q2c(coldfilt(Hi, h1b, h1a).T)  # Diagonal

        if include_scale:
            Yscale[0] = LoLo

    Yl = LoLo

    if initial_row_extend == 1 and initial_col_extend == 1:
        logging.warn(
            "The image entered is now a {0} NOT a {1}.".format(
                "x".join(list(str(s) for s in extended_size)), "x".join(list(str(s) for s in original_size))
            )
        )
        logging.warn("The bottom row and rightmost column have been duplicated, prior to decomposition.")

    if initial_row_extend == 1 and initial_col_extend == 0:
        logging.warn(
            "The image entered is now a {0} NOT a {1}.".format(
                "x".join(list(str(s) for s in extended_size)), "x".join(list(str(s) for s in original_size))
            )
        )
        logging.warn("The bottom row has been duplicated, prior to decomposition.")

    if initial_row_extend == 0 and initial_col_extend == 1:
        logging.warn(
            "The image entered is now a {0} NOT a {1}.".format(
                "x".join(list(str(s) for s in extended_size)), "x".join(list(str(s) for s in original_size))
            )
        )
        logging.warn("The rightmost column has been duplicated, prior to decomposition.")

    if include_scale:
        return Yl, tuple(Yh), tuple(Yscale)
    else:
        return Yl, tuple(Yh)