Exemplo n.º 1
0
 def test_fftn(self):
     x = random((30, 20, 10)) + 1j*random((30, 20, 10))
     assert_array_almost_equal(
         fft.fft(fft.fft(fft.fft(x, axis=2), axis=1), axis=0),
         fft.fftn(x))
     assert_array_almost_equal(fft.fftn(x) / np.sqrt(30 * 20 * 10),
                               fft.fftn(x, norm="ortho"))
Exemplo n.º 2
0
    def _prepare(self, data):
        """Calculate filter and data FFT in preparation for filtering.

        """
        dshape = np.array(data.shape)
        dshape += (dshape % 2 == 0)  # all filter dimensions must be uneven
        oshape = np.array(data.shape) * 2 - 1

        float_dtype = _supported_float_type(data.dtype)
        data = data.astype(float_dtype, copy=False)

        if self._cache is None or np.any(self._cache.shape != oshape):
            coords = np.mgrid[[slice(0, float(n)) for n in dshape]]
            # this steps over two sets of coordinates,
            # not over the coordinates individually
            for k, coord in enumerate(coords):
                coord -= (dshape[k] - 1) / 2.
            coords = coords.reshape(2, -1).T  # coordinate pairs (r,c)
            coords = coords.astype(float_dtype, copy=False)

            f = self.impulse_response(coords[:, 0], coords[:, 1],
                                      **self.filter_params).reshape(dshape)

            f = _pad(f, oshape)
            F = fft.fftn(f)
            self._cache = F
        else:
            F = self._cache

        data = _pad(data, oshape)
        G = fft.fftn(data)

        return F, G
Exemplo n.º 3
0
 def test_fftn(self):
     x = random((30, 20, 10)) + 1j * random((30, 20, 10))
     expect = fft.fft(fft.fft(fft.fft(x, axis=2), axis=1), axis=0)
     assert_array_almost_equal(expect, fft.fftn(x))
     assert_array_almost_equal(expect, fft.fftn(x, norm="backward"))
     assert_array_almost_equal(expect / np.sqrt(30 * 20 * 10),
                               fft.fftn(x, norm="ortho"))
     assert_array_almost_equal(expect / (30 * 20 * 10),
                               fft.fftn(x, norm="forward"))
Exemplo n.º 4
0
def spacecharge_meshes(rho_mesh, deltas, gamma=1, offset=(0,0,0), components=['Ex', 'Ey', 'Ez']):
    """
    Computes several components at once using an explicit FFT convolution.
    
    This is the preferred routine.
    
    """
    
    # FFT Configuration
    fft  = lambda x: sp_fft.fftn(x,  overwrite_x=True)
    ifft = lambda x: sp_fft.ifftn(x, overwrite_x=True)
    
    # Make double sized array
    nx, ny, nz = rho_mesh.shape
    crho = np.zeros( (2*nx, 2*ny, 2*nz))
    crho[0:nx,0:ny,0:nz] = rho_mesh[0:nx,0:ny,0:nz]
    # FFT
    crho = fft(crho)
    
    # Factor to convert to V/m
    factor = 1/(4*np.pi*scipy.constants.epsilon_0)
 
    field = {'deltas':deltas}
    for component in components:
        # Green gunction
        green_mesh = igf_mesh3(rho_mesh.shape, deltas, gamma=gamma, offset=offset, component=component)

        # Convolution of double-sized arrays
        field_mesh = ifft(crho*fft(green_mesh))
        # The result is in a shifted location in the output array
        field[component] = factor*np.real(field_mesh[nx-1:2*nx-1,ny-1:2*ny-1,nx-1:2*nz-1])
        
    return field
Exemplo n.º 5
0
def fft(x, direction='C2C', inverse=False):
    """
        Interface with torch FFT routines for 2D signals.

        Example
        -------
        x = torch.randn(128, 32, 32, 2)
        x_fft = fft(x, inverse=True)

        Parameters
        ----------
        input : tensor
            complex input for the FFT
        direction : string
            'C2R' for complex to real, 'C2C' for complex to complex
        inverse : bool
            True for computing the inverse FFT.
            NB : if direction is equal to 'C2R', then an error is raised.

    """
    if direction == 'C2R':
        if not inverse:
            raise RuntimeError(
                'C2R mode can only be done with an inverse FFT.')

    if direction == 'C2R':
        output = np.real(ifftn(x, axes=(-3, -2, -1)))
    elif direction == 'C2C':
        if inverse:
            output = ifftn(x, axes=(-3, -2, -1))
        else:
            output = fftn(x, axes=(-3, -2, -1))

    return output
def convolution_square_root(PSI,
                            pre_expansion=0,
                            post_contraction=0,
                            positive_real_branch_cut=np.pi,
                            negative_real_branch_cut=0.0):
    # Input PSI and output Z are BoxFunction convolution kernels
    # Z is the convolution kernel for the square root of the operator that does convolution with PSI
    initial_width = PSI.width
    s = pre_expansion
    PSI = PSI.restrict_to_new_box(PSI.min - s * PSI.width, PSI.max +
                                  s * PSI.width)  # expand PSI box by zero

    PSI_shifted_array = np.roll(PSI.array,
                                -PSI.zeropoint_index,
                                axis=np.arange(PSI.ndim))

    fft_PSI = fft.fftn(PSI_shifted_array)
    sqrt_fft_PSI = np.zeros(fft_PSI.shape, dtype=complex)
    sqrt_fft_PSI[fft_PSI.real >= 0] = square_root(fft_PSI[fft_PSI.real >= 0],
                                                  positive_real_branch_cut)
    sqrt_fft_PSI[fft_PSI.real < 0] = square_root(fft_PSI[fft_PSI.real < 0],
                                                 negative_real_branch_cut)

    Z_shifted_array = fft.ifftn(sqrt_fft_PSI)

    Z_array = np.roll(Z_shifted_array,
                      PSI.zeropoint_index,
                      axis=np.arange(PSI.ndim)) / np.sqrt(PSI.dV)

    Z = BoxFunction(PSI.min, PSI.max, Z_array)

    t = post_contraction
    Z = Z.restrict_to_new_box(Z.min + t * initial_width,
                              Z.max - t * initial_width)  # contract Z box
    return Z
Exemplo n.º 7
0
def _elser_particle_old(resolution):
    """This is the original function from Veit Elser without the
    tunable feature size."""
    x_coordinates = _numpy.arange(resolution +
                                  2) - (resolution + 2) / 2.0 + 0.5
    y_coordinates = _numpy.arange(resolution +
                                  2) - (resolution + 2) / 2.0 + 0.5
    z_coordinates = _numpy.arange(resolution +
                                  2) - (resolution + 2) / 2.0 + 0.5
    radius = _numpy.sqrt(x_coordinates[_numpy.newaxis, _numpy.newaxis, :]**2 +
                         y_coordinates[_numpy.newaxis, :, _numpy.newaxis]**2 +
                         z_coordinates[:, _numpy.newaxis, _numpy.newaxis]**2)
    particle_mask = radius > resolution / 2.
    lp_mask = _fft.fftshift(radius > resolution / 4.)

    particle = _numpy.random.random(
        (resolution + 2, resolution + 2, resolution + 2))

    for _ in range(4):
        particle[particle_mask] = 0.0
        particle_ft = _fft.fftn(particle)
        particle_ft[lp_mask] = 0.0
        particle[:, :] = abs(_fft.ifftn(particle))
        particle_average = _numpy.average(particle.flatten())
        particle[particle > 0.5 * particle_average] = 1.0
        particle[particle <= 0.5 * particle_average] = 0.0

    particle[particle_mask] = 0.0
    return particle
Exemplo n.º 8
0
def ufftn(inarray, dim=None):
    """N-dimensional unitary Fourier transform.

    Parameters
    ----------
    inarray : ndarray
        The array to transform.
    dim : int, optional
        The last axis along which to compute the transform. All
        axes by default.

    Returns
    -------
    outarray : ndarray (same shape than inarray)
        The unitary N-D Fourier transform of ``inarray``.

    Examples
    --------
    >>> input = np.ones((3, 3, 3))
    >>> output = ufftn(input)
    >>> np.allclose(np.sum(input) / np.sqrt(input.size), output[0, 0, 0])
    True
    >>> output.shape
    (3, 3, 3)
    """
    if dim is None:
        dim = inarray.ndim
    outarray = fft.fftn(inarray, axes=range(-dim, 0), norm='ortho')
    return outarray
Exemplo n.º 9
0
def fft_wfc_R2G(wfc, igwx, mill, omega):
  tmp = FFT.fftn(wfc) / np.sqrt(omega)
  
  wfcg = np.zeros((igwx,), dtype=complex)
  for ig in range(igwx):
    wfgc[tmp] = tmp[mill[0,ig],mill[1,ig],mill[2,ig]]
    
  return wfcg
def test_4d_input_pixel():
    phantom = img_as_float(binary_blobs(length=32, n_dim=4))
    reference_image = fft.fftn(phantom)
    shift = (-2., 1., 5., -3)
    shifted_image = fourier_shift(reference_image, shift)
    result, error, diffphase = phase_cross_correlation(reference_image,
                                                       shifted_image,
                                                       space="fourier")
    assert_allclose(result, -np.array(shift), atol=0.05)
Exemplo n.º 11
0
 def test_rfftn(self):
     x = random((30, 20, 10))
     expect = fft.fftn(x)[:, :, :6]
     assert_array_almost_equal(expect, fft.rfftn(x))
     assert_array_almost_equal(expect, fft.rfftn(x, norm="backward"))
     assert_array_almost_equal(expect / np.sqrt(30 * 20 * 10),
                               fft.rfftn(x, norm="ortho"))
     assert_array_almost_equal(expect / (30 * 20 * 10),
                               fft.rfftn(x, norm="forward"))
def test_correlation(normalization):
    reference_image = fft.fftn(camera())
    shift = (-7, 12)
    shifted_image = fourier_shift(reference_image, shift)

    # pixel precision
    result, _, _ = phase_cross_correlation(reference_image,
                                           shifted_image,
                                           space="fourier",
                                           normalization=normalization)
    assert_allclose(result[:2], -np.array(shift))
Exemplo n.º 13
0
def k_space_sampling(x, ss, idx):
    '''
    x:signal, of total lenght equal prod(ss).
    ss: signal shape
    idx: indexes to take the k-space measurements
    '''

    x = x.reshape(ss)
    X = fftn(x, s=ss)
    X = X.reshape((-1, 1))
    return X[idx]
def test_correlation_invalid_normalization(normalization):
    reference_image = fft.fftn(camera())
    shift = (-7, 12)
    shifted_image = fourier_shift(reference_image, shift)

    # pixel precision
    with pytest.raises(ValueError):
        phase_cross_correlation(reference_image,
                                shifted_image,
                                space="fourier",
                                normalization=normalization)
def test_size_one_dimension_input():
    # take a strip of the input image
    reference_image = fft.fftn(camera()[:, 15]).reshape((-1, 1))
    subpixel_shift = (-2.4, 4)
    shifted_image = fourier_shift(reference_image, subpixel_shift)

    # subpixel precision
    result, error, diffphase = phase_cross_correlation(reference_image,
                                                       shifted_image,
                                                       upsample_factor=20,
                                                       space="fourier")
    assert_allclose(result[:2], -np.array((-2.4, 0)), atol=0.05)
def test_subpixel_precision(normalization):
    reference_image = fft.fftn(camera())
    subpixel_shift = (-2.4, 1.32)
    shifted_image = fourier_shift(reference_image, subpixel_shift)

    # subpixel precision
    result, _, _ = phase_cross_correlation(reference_image,
                                           shifted_image,
                                           upsample_factor=100,
                                           space="fourier",
                                           normalization=normalization)
    assert_allclose(result[:2], -np.array(subpixel_shift), atol=0.05)
def test_real_input(dtype):
    reference_image = camera().astype(dtype, copy=False)
    subpixel_shift = (-2.4, 1.32)
    shifted_image = fourier_shift(fft.fftn(reference_image), subpixel_shift)
    shifted_image = fft.ifftn(shifted_image).real.astype(dtype, copy=False)

    # subpixel precision
    result, error, diffphase = phase_cross_correlation(reference_image,
                                                       shifted_image,
                                                       upsample_factor=100)
    assert result.dtype == _supported_float_type(dtype)
    assert_allclose(result[:2], -np.array(subpixel_shift), atol=0.05)
Exemplo n.º 18
0
def elser_particle_nd(array_shape,
                      feature_size,
                      mask=None,
                      return_blured=True):
    """Return a binary contrast particle of arbitrary dimensionality and shape.
    Feature size is given in pixels. If no mask is provided the paritlce will
    be spherical."""
    radius = tools.radial_distance(array_shape)
    if mask is None:
        particle_size = min(array_shape) - 2
        mask = radius < particle_size / 2.
    elif mask.shape != array_shape:
        raise ValueError("array_shape and mask.shape are different "
                         f"({array_shape} != {mask.shape})")
    coordinates = [
        _numpy.arange(this_shape) - this_shape / 2 + 0.5
        for this_shape in array_shape
    ]
    scaling = float(feature_size)**2 / float(len(coordinates[0]))**2
    component_exp = [
        _numpy.exp(-this_coordinate**2 * scaling)
        for this_coordinate in coordinates
    ]
    lp_kernel = _numpy.ones(array_shape)
    for index, this_exp in enumerate(component_exp):
        this_slice = [_numpy.newaxis] * len(array_shape)
        this_slice[index] = slice(None)
        this_slice = tuple(this_slice)
        lp_kernel *= this_exp[this_slice]
    lp_kernel = _fft.fftshift(lp_kernel)

    particle = _numpy.random.random(array_shape)
    for _ in range(4):
        particle_average = _numpy.median(particle[mask])
        particle[particle > particle_average] = 1.
        particle[particle <= particle_average] = 0.
        particle[~mask] = 0.
        particle_ft = _fft.fftn(particle)
        particle_ft *= lp_kernel
        particle[:] = abs(_fft.ifftn(particle_ft))

    if not return_blured:
        particle_average = _numpy.median(particle[mask])
        particle[particle > particle_average] = 1.
        particle[particle <= particle_average] = 0.
    return particle
Exemplo n.º 19
0
def random_channel(n, rand, fpower=2.0):
    freq = fftn(rand.rand(n, n))

    fx = fftfreq(n)[:, None]
    fy = fftfreq(n)[None, :]

    # combine as l2 norm of freq
    f = (fx**2 + fy**2)**0.5

    i = f > 0
    freq[i] /= f[i]**fpower
    freq[0, 0] = 0.0

    data = np.real(ifftn(freq))
    data -= data.min()
    data /= data.max()
    return data
Exemplo n.º 20
0
def fftconvolve3(rho, *greens):
    """
    Efficiently perform a 3D convolution of a charge density rho and multiple Green functions. 
    
    Parameters
    ----------
    
    rho : np.array (3D)
        Charge mesh
        
    *greens : np.arrays (3D)
        Charge meshes for the Green functions, which should be twice the size of rho    
        
        
    Returns
    -------
    
    fields : tuple of np.arrays with the same shape as rho. 
    
    """

    # FFT Configuration
    fft = lambda x: sp_fft.fftn(x, overwrite_x=True)
    ifft = lambda x: sp_fft.ifftn(x, overwrite_x=True)

    # Place rho in double-sized array. Should match the shape of green
    nx, ny, nz = rho.shape
    crho = np.zeros((2 * nx, 2 * ny, 2 * nz))
    crho[0:nx, 0:ny, 0:nz] = rho[0:nx, 0:ny, 0:nz]

    # FFT
    crho = fft(crho)

    results = []
    for green in greens:
        assert crho.shape == green.shape, f'Green array shape {green.shape} should be twice rho shape {rho.shape}'
        result = ifft(crho * fft(green))
        # Extract the result
        result = np.real(result[nx - 1:2 * nx - 1, ny - 1:2 * ny - 1,
                                nz - 1:2 * nz - 1])
        results.append(result)

    return tuple(results)
def test_3d_input():
    phantom = img_as_float(binary_blobs(length=32, n_dim=3))
    reference_image = fft.fftn(phantom)
    shift = (-2., 1., 5.)
    shifted_image = fourier_shift(reference_image, shift)

    result, error, diffphase = phase_cross_correlation(reference_image,
                                                       shifted_image,
                                                       space="fourier")
    assert_allclose(result, -np.array(shift), atol=0.05)

    # subpixel precision now available for 3-D data

    subpixel_shift = (-2.3, 1.7, 5.4)
    shifted_image = fourier_shift(reference_image, subpixel_shift)
    result, error, diffphase = phase_cross_correlation(reference_image,
                                                       shifted_image,
                                                       upsample_factor=100,
                                                       space="fourier")
    assert_allclose(result, -np.array(subpixel_shift), atol=0.05)
Exemplo n.º 22
0
    def fourier(self, field_Re, field_Im=None):
        if field_Im:
            FT = np.squeeze(self.cg[field_Re][:, :, :].d +
                            1j * self.cg[field_Im][:, :, :].d)
        else:
            FT = np.squeeze(self.cg[field_Re][:, :, :].d)

        # use fftn to do an N-dimensional FFT on an N-dimensional numpy array
        FT = fft.fftn(FT)

        # we're shifting the sampling frequencies next, so we have to shift the FFT values
        FT = fft.fftshift(FT)

        # get the absolute value of the fft
        FT_mag = np.abs(FT)

        # get the phase of the fft
        FT_phi = np.angle(FT)

        if self.Nx > 1:
            # find the sampling frequencies in X & shift them
            kx = fft.fftfreq(self.Nx, self.dx)
            kx = fft.fftshift(kx)
        else:
            kx = None

        if self.Ny > 1:
            # find the sampling frequencies in Y & shift them
            ky = fft.fftfreq(self.Ny, self.dy)
            ky = fft.fftshift(ky)
        else:
            ky = None

        if self.Nz > 1:
            # find the sampling frequencies in Z & shift them
            kz = fft.fftfreq(self.Nz, self.dz)
            kz = fft.fftshift(kz)
        else:
            kz = None

        return FourierData(self.ds.current_time, kx, ky, kz, FT_mag, FT_phi)
Exemplo n.º 23
0
def gaussian_blur_nonperiodic(array, sigma):
    """Only 1d at this point"""
    small_size = len(array)
    pad_size = int(sigma * 10)
    large_size = small_size + 2 * pad_size
    large_array = _numpy.zeros(large_size)
    large_array[pad_size:(small_size + pad_size)] = array
    large_array[:pad_size] = array[0]
    large_array[(small_size + pad_size):] = array[-1]

    x_array = _numpy.arange(-large_size // 2, large_size // 2)

    kernel_ft = _numpy.exp(-2. * sigma**2 * _numpy.pi**2 *
                           (x_array / large_size)**2)
    kernel_ft = ((large_size / _numpy.sqrt(2. * _numpy.pi) / sigma) *
                 _fft.fftshift(kernel_ft))

    image_ft = _fft.fftn(large_array)
    image_ft *= kernel_ft
    blured_large_array = _fft.ifftn(image_ft)
    return blured_large_array[pad_size:-pad_size]
Exemplo n.º 24
0
def scale_image_3d(image, factor):
    """Scales up the image by the scaling factor.
    Input:
    image
    factor"""
    size_x = image.shape[0]
    size_y = image.shape[1]
    size_z = image.shape[2]
    center_x = size_x // 2
    center_y = size_y // 2
    center_z = size_z // 2
    window_x = int(size_x // factor)
    window_y = int(size_y // factor)
    window_z = int(size_z // factor)
    image_ft = _fft.fftn(
        image[center_x - window_x // 2:center_x + window_x // 2,
              center_y - window_y // 2:center_y + window_y // 2,
              center_z - window_z // 2:center_z + window_z // 2],
        [size_x, size_y, size_z])
    image_scaled = abs(
        _fft.ifftn(_fft.fftshift(image_ft), [size_x, size_y, size_z]))
    return image_scaled
Exemplo n.º 25
0
def elser_particle(array_size,
                   particle_size,
                   feature_size,
                   return_blured=True):
    """Return a binary contrast particle. 'particle_size' and 'feature_size'
    should both be given in pixels."""
    if particle_size > array_size - 2:
        raise ValueError("Particle size must be <= array_size is "
                         f"({particle_size} > {array_size}-2)")

    x_coordinates = _numpy.arange(array_size) - array_size / 2. + 0.5
    y_coordinates = _numpy.arange(array_size) - array_size / 2. + 0.5
    z_coordinates = _numpy.arange(array_size) - array_size / 2. + 0.5
    radius = _numpy.sqrt(x_coordinates[_numpy.newaxis, _numpy.newaxis, :]**2 +
                         y_coordinates[_numpy.newaxis, :, _numpy.newaxis]**2 +
                         z_coordinates[:, _numpy.newaxis, _numpy.newaxis]**2)
    particle_mask = radius > particle_size / 2.
    kernel_scaling = float(feature_size)**2 / float(array_size)**2
    lp_kernel = _fft.fftshift(_numpy.exp(-radius**2 * kernel_scaling))

    particle = _numpy.random.random((array_size, ) * 3)

    for _ in range(4):
        # binary constrast
        particle_average = _numpy.median(particle[~particle_mask])
        particle[particle > particle_average] = 1.
        particle[particle <= particle_average] = 0.
        particle[particle_mask] = 0.
        # smoothen
        particle_ft = _fft.fftn(particle)
        particle_ft *= lp_kernel
        particle[:, :] = abs(_fft.ifftn(particle_ft))

    if not return_blured:
        particle_average = _numpy.median(particle[~particle_mask])
        particle[particle > particle_average] = 1.
        particle[particle <= particle_average] = 0.

    return particle
Exemplo n.º 26
0
 def transformation_3d(group):
     # return pywt.dwtn(group, 'bior1.5')
     return fftn(group)
Exemplo n.º 27
0
 def test_rfftn(self):
     x = random((30, 20, 10))
     assert_array_almost_equal(fft.fftn(x)[:, :, :6], fft.rfftn(x))
     assert_array_almost_equal(fft.rfftn(x) / np.sqrt(30 * 20 * 10),
                               fft.rfftn(x, norm="ortho"))
def _fft(image):
    """shifted fft 2D
    """
    return fftshift(fftn(fftshift(image)))
Exemplo n.º 29
0
def phase_cross_correlation(reference_image, moving_image, *,
                            upsample_factor=1, space="real",
                            return_error=True, reference_mask=None,
                            moving_mask=None, overlap_ratio=0.3):
    """Efficient subpixel image translation registration by cross-correlation.

    This code gives the same precision as the FFT upsampled cross-correlation
    in a fraction of the computation time and with reduced memory requirements.
    It obtains an initial estimate of the cross-correlation peak by an FFT and
    then refines the shift estimation by upsampling the DFT only in a small
    neighborhood of that estimate by means of a matrix-multiply DFT.

    Parameters
    ----------
    reference_image : array
        Reference image.
    moving_image : array
        Image to register. Must be same dimensionality as
        ``reference_image``.
    upsample_factor : int, optional
        Upsampling factor. Images will be registered to within
        ``1 / upsample_factor`` of a pixel. For example
        ``upsample_factor == 20`` means the images will be registered
        within 1/20th of a pixel. Default is 1 (no upsampling).
        Not used if any of ``reference_mask`` or ``moving_mask`` is not None.
    space : string, one of "real" or "fourier", optional
        Defines how the algorithm interprets input data. "real" means
        data will be FFT'd to compute the correlation, while "fourier"
        data will bypass FFT of input data. Case insensitive. Not
        used if any of ``reference_mask`` or ``moving_mask`` is not
        None.
    return_error : bool, optional
        Returns error and phase difference if on, otherwise only
        shifts are returned. Has noeffect if any of ``reference_mask`` or
        ``moving_mask`` is not None. In this case only shifts is returned.
    reference_mask : ndarray
        Boolean mask for ``reference_image``. The mask should evaluate
        to ``True`` (or 1) on valid pixels. ``reference_mask`` should
        have the same shape as ``reference_image``.
    moving_mask : ndarray or None, optional
        Boolean mask for ``moving_image``. The mask should evaluate to ``True``
        (or 1) on valid pixels. ``moving_mask`` should have the same shape
        as ``moving_image``. If ``None``, ``reference_mask`` will be used.
    overlap_ratio : float, optional
        Minimum allowed overlap ratio between images. The correlation for
        translations corresponding with an overlap ratio lower than this
        threshold will be ignored. A lower `overlap_ratio` leads to smaller
        maximum translation, while a higher `overlap_ratio` leads to greater
        robustness against spurious matches due to small overlap between
        masked images. Used only if one of ``reference_mask`` or
        ``moving_mask`` is None.

    Returns
    -------
    shifts : ndarray
        Shift vector (in pixels) required to register ``moving_image``
        with ``reference_image``. Axis ordering is consistent with
        numpy (e.g. Z, Y, X)
    error : float
        Translation invariant normalized RMS error between
        ``reference_image`` and ``moving_image``.
    phasediff : float
        Global phase difference between the two images (should be
        zero if images are non-negative).

    References
    ----------
    .. [1] Manuel Guizar-Sicairos, Samuel T. Thurman, and James R. Fienup,
           "Efficient subpixel image registration algorithms,"
           Optics Letters 33, 156-158 (2008). :DOI:`10.1364/OL.33.000156`
    .. [2] James R. Fienup, "Invariant error metrics for image reconstruction"
           Optics Letters 36, 8352-8357 (1997). :DOI:`10.1364/AO.36.008352`
    .. [3] Dirk Padfield. Masked Object Registration in the Fourier Domain.
           IEEE Transactions on Image Processing, vol. 21(5),
           pp. 2706-2718 (2012). :DOI:`10.1109/TIP.2011.2181402`
    .. [4] D. Padfield. "Masked FFT registration". In Proc. Computer Vision and
           Pattern Recognition, pp. 2918-2925 (2010).
           :DOI:`10.1109/CVPR.2010.5540032`

    """
    if (reference_mask is not None) or (moving_mask is not None):
        return _masked_phase_cross_correlation(reference_image, moving_image,
                                               reference_mask, moving_mask,
                                               overlap_ratio)

    # images must be the same shape
    if reference_image.shape != moving_image.shape:
        raise ValueError("images must be same shape")

    # assume complex data is already in Fourier space
    if space.lower() == 'fourier':
        src_freq = reference_image
        target_freq = moving_image
    # real data needs to be fft'd.
    elif space.lower() == 'real':
        src_freq = fftn(reference_image)
        target_freq = fftn(moving_image)
    else:
        raise ValueError('space argument must be "real" of "fourier"')

    # Whole-pixel shift - Compute cross-correlation by an IFFT
    shape = src_freq.shape
    image_product = src_freq * target_freq.conj()
    cross_correlation = ifftn(image_product)

    # Locate maximum
    maxima = np.unravel_index(np.argmax(np.abs(cross_correlation)),
                              cross_correlation.shape)
    midpoints = np.array([np.fix(axis_size / 2) for axis_size in shape])

    float_dtype = image_product.real.dtype

    shifts = np.stack(maxima).astype(float_dtype, copy=False)
    shifts[shifts > midpoints] -= np.array(shape)[shifts > midpoints]

    if upsample_factor == 1:
        if return_error:
            src_amp = np.sum(np.real(src_freq * src_freq.conj()))
            src_amp /= src_freq.size
            target_amp = np.sum(np.real(target_freq * target_freq.conj()))
            target_amp /= target_freq.size
            CCmax = cross_correlation[maxima]
    # If upsampling > 1, then refine estimate with matrix multiply DFT
    else:
        # Initial shift estimate in upsampled grid
        upsample_factor = np.array(upsample_factor, dtype=float_dtype)
        shifts = np.round(shifts * upsample_factor) / upsample_factor
        upsampled_region_size = np.ceil(upsample_factor * 1.5)
        # Center of output array at dftshift + 1
        dftshift = np.fix(upsampled_region_size / 2.0)
        # Matrix multiply DFT around the current shift estimate
        sample_region_offset = dftshift - shifts*upsample_factor
        cross_correlation = _upsampled_dft(image_product.conj(),
                                           upsampled_region_size,
                                           upsample_factor,
                                           sample_region_offset).conj()
        # Locate maximum and map back to original pixel grid
        maxima = np.unravel_index(np.argmax(np.abs(cross_correlation)),
                                  cross_correlation.shape)
        CCmax = cross_correlation[maxima]

        maxima = np.stack(maxima).astype(float_dtype, copy=False)
        maxima -= dftshift

        shifts += maxima / upsample_factor

        if return_error:
            src_amp = np.sum(np.real(src_freq * src_freq.conj()))
            target_amp = np.sum(np.real(target_freq * target_freq.conj()))

    # If its only one row or column the shift along that dimension has no
    # effect. We set to zero.
    for dim in range(src_freq.ndim):
        if shape[dim] == 1:
            shifts[dim] = 0

    if return_error:
        # Redirect user to masked_phase_cross_correlation if NaNs are observed
        if np.isnan(CCmax) or np.isnan(src_amp) or np.isnan(target_amp):
            raise ValueError(
                "NaN values found, please remove NaNs from your "
                "input data or use the `reference_mask`/`moving_mask` "
                "keywords, eg: "
                "phase_cross_correlation(reference_image, moving_image, "
                "reference_mask=~np.isnan(reference_image), "
                "moving_mask=~np.isnan(moving_image))")

        return shifts, _compute_error(CCmax, src_amp, target_amp),\
            _compute_phasediff(CCmax)
    else:
        return shifts
Exemplo n.º 30
0
def _fft_centered(x):
    return fftmodule.fftshift(fftmodule.fftn(fftmodule.fftshift(x)))