Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Register images in time series cube #12

Open
robmarkcole opened this issue Oct 2, 2023 · 1 comment
Open

Register images in time series cube #12

robmarkcole opened this issue Oct 2, 2023 · 1 comment

Comments

@robmarkcole
Copy link
Collaborator

I've observed pixel shifts of order a couple of pixels on Sentinel 2 and created the following solution which can be integrated/adapted:

from scipy.ndimage import shift as nd_shift
from skimage.registration import phase_cross_correlation
from scipy import fftpack


def low_pass_filter_fourier(image, cutoff_radius):
    """
    Apply a low-pass filter to an image in the Fourier domain.

    The function performs the Fourier transform on the input image, multiplies 
    it with a low-pass filter mask, and then performs an inverse Fourier 
    transform to get the filtered image.

    Parameters:
    ----------
    image : np.ndarray
        The input 2D image to be filtered.
        
    cutoff_radius : float
        The cutoff radius for the low-pass filter in the Fourier domain.
        This value should be between 0 and 0.5, where 0.5 corresponds to
        the Nyquist frequency.

    Returns:
    -------
    np.ndarray
        The filtered image in the spatial domain.

    Example:
    -------
    >>> image = np.random.rand(100, 100)
    >>> filtered_image = low_pass_filter_fourier(image, 0.2)
    """
    # Fourier Transform
    F = fftpack.fftshift(fftpack.fft2(image))

    # Generate low-pass filter mask
    rows, cols = image.shape
    x = np.fft.fftfreq(rows)
    y = np.fft.fftfreq(cols)
    x, y = np.meshgrid(x, y, indexing='ij')
    radius = np.sqrt(x ** 2 + y ** 2)

    # Apply low-pass filter
    filter_mask = np.fft.fftshift(radius <= cutoff_radius)
    F_filtered = F * filter_mask

    # Inverse Fourier Transform
    image_filtered = np.fft.ifft2(np.fft.ifftshift(F_filtered)).real
    
    return image_filtered


def register_xarrays(xarray1: xr.DataArray, xarray2: xr.DataArray) -> xr.DataArray:
    """Register xarray2 onto xarray1 using the 'red' band for registration."""
    
    array1 = xarray1.sel(band='red').values
    array2 = xarray2.sel(band='red').values
    
    array1 = low_pass_filter_fourier(array1, 0.2)
    array2 = low_pass_filter_fourier(array2, 0.2)
    
    shift, _, _ = phase_cross_correlation(array1, array2, upsample_factor=100)
    print(shift)
    
    corrected_xarray2 = xarray2.copy()
    
    # Apply shift to all bands
    for band in xarray2['band'].values:
        corrected_xarray2.loc[dict(band=band)] = nd_shift(xarray2.sel(band=band).values, shift[:2])
    
    return corrected_xarray2
@nkarasiak
Copy link
Collaborator

Wow really nice coregistration function to implement for sure !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants