def correlation_func(cor_win_1, cor_win_2, window_size, correlation_method='circular'): '''This function is doing the cross-correlation. Right now circular cross-correlation That means no zero-padding is done the .real is to cut off possible imaginary parts that remains due to finite numerical accuarcy ''' if correlation_method == 'linear': cor_win_1 = cor_win_1 - cor_win_1.mean(axis=(1, 2)).reshape( cor_win_1.shape[0], 1, 1) cor_win_2 = cor_win_2 - cor_win_2.mean(axis=(1, 2)).reshape( cor_win_1.shape[0], 1, 1) cor_win_1[cor_win_1 < 0] = 0 cor_win_2[cor_win_2 < 0] = 0 corr = fftshift(irfft2( np.conj(rfft2(cor_win_1, s=(2 * window_size, 2 * window_size))) * rfft2(cor_win_2, s=(2 * window_size, 2 * window_size))).real, axes=(1, 2)) corr = corr[:, window_size // 2:3 * window_size // 2, window_size // 2:3 * window_size // 2] else: corr = fftshift(irfft2(np.conj(rfft2(cor_win_1)) * rfft2(cor_win_2)).real, axes=(1, 2)) return corr
def fft_correlate_images(image_a, image_b, correlation_method="circular", normalized_correlation=True): """ FFT based cross correlation of two images with multiple views of np.stride_tricks() The 2D FFT should be applied to the last two axes (-2,-1) and the zero axis is the number of the interrogation window This should also work out of the box for rectangular windows. Parameters ---------- image_a : 3d np.ndarray, first dimension is the number of windows, and two last dimensions are interrogation windows of the first image image_b : similar correlation_method : string one of the three methods implemented: 'circular' or 'linear' [default: 'circular]. normalized_correlation : string decides wetehr normalized correlation is done or not: True or False [default: True]. """ if normalized_correlation: # remove the effect of stronger laser or # longer exposure for frame B # image_a = match_histograms(image_a, image_b) # remove mean background, normalize to 0..1 range image_a = normalize_intensity(image_a) image_b = normalize_intensity(image_b) s1 = np.array(image_a.shape[-2:]) s2 = np.array(image_b.shape[-2:]) if correlation_method == "linear": # have to be normalized, mainly because of zero padding size = s1 + s2 - 1 fsize = 2**np.ceil(np.log2(size)).astype(int) fslice = (slice(0, image_a.shape[0]), slice((fsize[0] - s1[0]) // 2, (fsize[0] + s1[0]) // 2), slice((fsize[1] - s1[1]) // 2, (fsize[1] + s1[1]) // 2)) f2a = rfft2(image_a, fsize, axes=(-2, -1)).conj() f2b = rfft2(image_b, fsize, axes=(-2, -1)) corr = fftshift(irfft2(f2a * f2b).real, axes=(-2, -1))[fslice] elif correlation_method == "circular": corr = fftshift(irfft2(rfft2(image_a).conj() * rfft2(image_b)).real, axes=(-2, -1)) else: print("method is not implemented!") if normalized_correlation: corr = corr / (s2[0] * s2[1]) # for extended search area corr = np.clip(corr, 0, 1) return corr
def deconvolve_sinogram(sinogram, psf, l=20, mode='laplacian', clip=True): """ Deconvolve a sinogram with given PSF. Parameters ---------- sinogram : numpy.ndarray [TPY] The blurred sinogram. psf : numpy.ndarray [ZXY] or [XY] The PSF used to deconvolve. l : float, optional Strength of the regularization. clip : bool, optional Clip negative values to 0, default is True. Returns ------- numpy.ndarray [TPY] The deconvolved sinogram. """ fft_shape = [fft_size(s) for s in sinogram.shape[1:]] inverse = inverse_psf_rfft(psf, shape=fft_shape, l=l, mode=mode) s_fft = fft.rfft2(sinogram, s=fft_shape) i_fft = fft.irfft2(s_fft * inverse, s=fft_shape, overwrite_x=True) i_fft = i_fft[:, :sinogram.shape[1], :sinogram.shape[2]] if clip: np.clip(i_fft, 0, None, out=i_fft) return i_fft
def fft_correlate_strided_images(image_a, image_b): """FFT based cross correlation of two images with multiple views of np.stride_tricks() The 2D FFT should be applied to the last two axes (-2,-1) and the zero axis is the number of the interrogation window This should also work out of the box for rectangular windows. Parameters ---------- image_a : 3d np.ndarray, first dimension is the number of windows, and two last dimensions are interrogation windows of the first image image_b : similar """ s1 = np.array(image_a.shape[-2:]) s2 = np.array(image_b.shape[-2:]) size = s1 + s2 - 1 fsize = 2**np.ceil(np.log2(size)).astype(int) fslice = tuple([slice(0, image_a.shape[0])] + [slice(0, int(sz)) for sz in size]) f2a = rfft2(image_a, fsize, axes=(-2, -1)) f2b = rfft2(image_b[:, ::-1, ::-1], fsize, axes=(-2, -1)) corr = irfft2(f2a * f2b, axes=(-2, -1)).real[fslice] return corr
def get_cor(self, fname, fit_size=3): s = self.roi.size data = np.asarray(Image.open(fname)) for a, b in zip(self.ref_rois, self.roi_iter(data, flip=True)): cor = fft.irfft2(a * b, (4 * s, 4 * s))[:-1, :-1] (yp, xp), q = subpixel_peak(cor, fit_size) xp = xp - 2 * s + 1 yp = yp - 2 * s + 1 yield cor, xp, yp, q
def test_dirac(self): for psf in self.psfs: i_psf = fpsopt.inverse_psf_rfft(psf, l=0) psfft = fft.rfft2(psf.sum(0)) dirac = fft.irfft2(psfft * i_psf, s=psf.shape[1:]) ref = np.zeros_like(dirac) ref[(psf.shape[0] - 1) // 2, (psf.shape[0] - 1) // 2] = 1 np.testing.assert_allclose(dirac, ref, atol=1e-12)
def makeFilter(shape: List[int], alpha: float, dtype=float) -> np.ndarray: assert 1 <= len(shape) <= 2, "shape must be one or two elements" if len(shape) == 1: shape = [shape[0], shape[0]] # Generate filter in the frequency domain fy = sf.fftfreq(shape[0])[:, np.newaxis].astype(dtype) fx = sf.rfftfreq(shape[1])[np.newaxis, :].astype(dtype) H2 = (fx**2 + fy**2)**(alpha / 2.0) return sf.fftshift(sf.irfft2(H2))
def texshadeFFT(x: np.ndarray, alpha: float) -> np.ndarray: """FFT-based texture shading elevation Given an array `x` of elevation data and an `alpha` > 0, apply the texture-shading algorithm using the full (real-only) FFT: the entire `x` array will be FFT'd. `alpha` is the shading detail factor, i.e., the power of the fractional-Laplacian operator. `alpha=0` means no detail (output is the input). `alpha=2.0` is the full (non-fractional) Laplacian operator and is probably too high. `alpha <= 1.0` seem aesthetically pleasing. Returns an array the same dimensions as `x` that contains the texture-shaded version of the input array. If `x` is memory-mapped and/or your system doesn't have 5x `x`'s memory available, consider using `texshade.texshadeSpatial`, which implements a low-memory version of the algorithm by approximating the frequency response of the fractional-Laplacian filter with a finite impulse response filter applied in the spatial-domain. Implementation note: this function uses Scipy's FFTPACK routines (in `scipy.fftpack`) instead of Numpy's FFT (`numpy.fft`) because the former can return single-precision float32. In newer versions of Numpy/Scipy, this advantage may have evaporated [1], [2]. [1] https://github.com/numpy/numpy/issues/6012 [2] https://github.com/scipy/scipy/issues/2487 """ Nyx = [nextprod([2, 3, 5, 7], x) for x in x.shape] # Generate filter in the frequency domain fy = sf.fftfreq(Nyx[0])[:, np.newaxis].astype(x.dtype) fx = sf.rfftfreq(Nyx[1])[np.newaxis, :].astype(x.dtype) H2 = (fx**2 + fy**2)**(alpha / 2.0) # Compute the FFT of the input and apply the filter xr = sf.rfft2(x, s=Nyx) * H2 H2 = None # potentially trigger GC here to reclaim H2's memory xr = sf.irfft2(xr) # Return the same size as input return xr[:x.shape[0], :x.shape[1]]
def fft_correlate_windows(window_a, window_b): """ FFT based cross correlation it is a so-called linear convolution based, since we increase the size of the FFT to reduce the edge effects. This should also work out of the box for rectangular windows. Parameters ---------- window_a : 2d np.ndarray a two dimensions array for the first interrogation window, window_b : 2d np.ndarray a two dimensions array for the second interrogation window. # from Stackoverflow: from scipy import linalg import numpy as np # works for rectangular windows as well x = [[1 , 0 , 0 , 0] , [0 , -1 , 0 , 0] , [0 , 0 , 3 , 0] , [0 , 0 , 0 , 1], [0 , 0 , 0 , 1]] x = np.array(x,dtype=np.float) y = [[4 , 5] , [3 , 4]] y = np.array(y) print ("conv:" , signal.convolve2d(x , y , 'full')) s1 = np.array(x.shape) s2 = np.array(y.shape) size = s1 + s2 - 1 fsize = 2 ** np.ceil(np.log2(size)).astype(int) fslice = tuple([slice(0, int(sz)) for sz in size]) new_x = np.fft.fft2(x , fsize) new_y = np.fft.fft2(y , fsize) result = np.fft.ifft2(new_x*new_y)[fslice].copy() print("fft for my method:" , np.array(result.real, np.int32)) """ s1 = np.array(window_a.shape) s2 = np.array(window_b.shape) size = s1 + s2 - 1 fsize = 2 ** np.ceil(np.log2(size)).astype(int) fslice = tuple([slice(0, int(sz)) for sz in size]) f2a = rfft2(window_a, fsize) f2b = rfft2(window_b[::-1, ::-1], fsize) corr = irfft2(f2a * f2b).real[fslice] return corr
if __name__ == '__main__': from cbi_toolbox.simu import optics, primitives, imaging import cbi_toolbox.splineradon as spl import napari TEST_SIZE = 64 s_psf = optics.gaussian_psf(numerical_aperture=0.3, npix_axial=TEST_SIZE + 1, npix_lateral=TEST_SIZE + 1) i_psf = inverse_psf_rfft(s_psf, l=1e-15, mode='constant') psfft = fft.rfft2(s_psf.sum(0)) dirac = fft.irfft2(psfft * i_psf, s=s_psf.shape[1:]) sample = primitives.boccia(TEST_SIZE, radius=(0.8 * TEST_SIZE) // 2, n_stripes=4) s_theta = np.arange(90) s_radon = spl.radon(sample, theta=s_theta, circle=True) s_fpsopt = imaging.fps_opt(sample, s_psf, theta=s_theta) s_deconv = deconvolve_sinogram(s_fpsopt, s_psf, l=0) viewer = napari.view_image(s_radon) viewer.add_image(s_fpsopt) viewer.add_image(s_deconv)
def test_irfft2(self): x = random((30, 20)) assert_array_almost_equal(x, fft.irfft2(fft.rfft2(x))) assert_array_almost_equal( x, fft.irfft2(fft.rfft2(x, norm="ortho"), norm="ortho"))
def test_irfft2(self): x = random((30, 20)) assert_array_almost_equal(x, fft.irfft2(fft.rfft2(x))) for norm in ["backward", "ortho", "forward"]: assert_array_almost_equal( x, fft.irfft2(fft.rfft2(x, norm=norm), norm=norm))