def simcheck(data, nphases): norients = data.shape[0]//nphases # need to use integer divide # check user input before proceeding assert nphases*norients == data.shape[0] newshape = norients, nphases, data.shape[-2], data.shape[-1] # FT data only along spatial dimensions ft_data = fftshift( fftn(ifftshift( data, axes=(1, 2)), axes=(1, 2)), axes=(1, 2)) # average only along phase, **not** orientation # This should be the equivalent of the FT of the average image per each # phase (i.e it should be symmetric as the phases will have averaged out) ft_data_avg = ft_data.reshape(newshape).mean(1) # Do the same, but take the absolute value before averaging, in this case # the signal should add up because the phase has been removed ft_data_avg_abs = np.abs(ft_data).reshape(newshape).mean(1) # Take the difference of the average power and the power of the average ft_data_diff = ft_data_avg_abs-abs(ft_data_avg) return ft_data_diff, ft_data_avg_abs, ft_data_avg
def get_der_dLv(A, v, a, b): # A = (a * delta + bE) # v = Km, m = Lv # L = A^(-2) G = np.zeros(v.shape, dtype=np.complex128) # delta = laplacian delta = get_delta(A, a, b) # turn v into furier space for i in range(len(v)): G[i] = fftn(v[i]) # in fourier space we get a simple multiplication and get delta*v, delta^2*v(like Lv) delta_v = G * delta delta2_v = G * delta**2 # get back from fourier space Dv = np.zeros_like(G) D2v = np.zeros_like(G) for i in range(len(v)): Dv[i] = np.real(ifftn(delta_v[i])) D2v[i] = np.real(ifftn(delta2_v[i])) # return derivative of Lv # dLv/da = ((a*delta + bE)^2 v)/da = 2(a*delta +bE)*delta*v = 2(a*delta^2 + b*delta)*v # dLv/db = 2(a*delta + bE) * E * v = 2(a*delta + bE)v del delta2_v, delta_v, G return np.real(2 * a * D2v + 2 * b * Dv), np.real(2 * a * Dv + 2 * b * v)
def Lv(A, v): G = np.zeros(v.shape, dtype=np.complex128) for i in range(len(v)): G[i] = fftn(v[i]) Lv = A * G Dv = np.zeros_like(G) for i in range(len(v)): Dv[i] = np.real(ifftn(Lv[i])) return np.real(Dv)
def __call__(self, momentum): if (hasattr(self, 'operator') and momentum.shape[1:] == self.operator.shape): G = np.zeros(momentum.shape, dtype=np.complex128) for i in xrange(len(momentum)): G[i] = fftn(momentum[i]) F = G * self.operator vector_field = np.zeros_like(momentum) for i in xrange(len(momentum)): vector_field[i] = np.real(ifftn(F[i])) return vector_field else: self.set_operator(momentum.shape[1:]) return self.__call__(momentum)
def fftx(ar): return fftn(ar,axes=[0,1])
def norm_xcorr(t,a,method=None,trim=True,do_ssd=False): """ Fast normalized cross-correlation for n-dimensional arrays Inputs: ---------------- t The template. Must have at least 2 elements, which cannot all be equal. a The search space. Its dimensionality must match that of the template. method The convolution method to use when computing the cross-correlation. Can be either 'direct', 'fourier' or None. If method == None (default), the convolution time is estimated for both methods and the best one is chosen for the given input array sizes. trim If True (default), the output array is trimmed down to the size of the search space. Otherwise, its size will be (f.shape[dd] + t.shape[dd] -1) for dimension dd. do_ssd If True, the sum of squared differences between the template and the search image will also be calculated. It is very efficient to calculate normalized cross-correlation and the SSD simultaneously, since they require many of the same quantities. Output: ---------------- nxcorr An array of cross-correlation coefficients, which may vary from -1.0 to 1.0. [ssd] [Returned if do_ssd == True. See fast_ssd for details.] Wherever the search space has zero variance under the template, normalized cross-correlation is undefined. In such regions, the correlation coefficients are set to zero. References: Hermosillo et al 2002: Variational Methods for Multimodal Image Matching, International Journal of Computer Vision 50(3), 329-343, 2002 <http://www.springerlink.com/content/u4007p8871w10645/> Lewis 1995: Fast Template Matching, Vision Interface, p.120-123, 1995 <http://www.idiom.com/~zilla/Papers/nvisionInterface/nip.html> <http://en.wikipedia.org/wiki/Cross-correlation#Normalized_cross-correlation> Alistair Muldal Department of Pharmacology University of Oxford <*****@*****.**> Sept 2012 """ if t.size < 2: raise Exception('Invalid template') if t.size > a.size: raise Exception('The input array must be smaller than the template') std_t,mean_t = np.std(t),np.mean(t) if std_t == 0: raise Exception('The values of the template must not all be equal') t = np.float64(t) a = np.float64(a) # output dimensions of xcorr need to match those of local_sum outdims = np.array([a.shape[dd]+t.shape[dd]-1 for dd in xrange(a.ndim)]) # would it be quicker to convolve in the spatial or frequency domain? NB # this is not very accurate since the speed of the Fourier transform # varies quite a lot with the output dimensions (e.g. 2-radix case) if method == None: spatialtime, ffttime = get_times(t,a,outdims) if spatialtime < ffttime: method = 'spatial' else: method = 'fourier' if method == 'fourier': # # in many cases, padding the dimensions to a power of 2 # # *dramatically* improves the speed of the Fourier transforms # # since it allows using radix-2 FFTs # fftshape = [nextpow2(ss) for ss in a.shape] # Fourier transform of the input array and the inverted template # af = fftn(a,shape=fftshape) # tf = fftn(ndflip(t),shape=fftshape) af = fftn(a,shape=outdims) tf = fftn(ndflip(t),shape=outdims) # 'non-normalized' cross-correlation xcorr = np.real(ifftn(tf*af)) else: xcorr = convolve(a,t,mode='constant',cval=0) # local linear and quadratic sums of input array in the region of the # template ls_a = local_sum(a,t.shape) ls2_a = local_sum(a**2,t.shape) # now we need to make sure xcorr is the same size as ls_a xcorr = procrustes(xcorr,ls_a.shape,side='both') # local standard deviation of the input array ls_diff = ls2_a - (ls_a**2)/t.size ls_diff = np.where(ls_diff < 0,0,ls_diff) sigma_a = np.sqrt(ls_diff) # standard deviation of the template sigma_t = np.sqrt(t.size-1.)*std_t # denominator: product of standard deviations denom = sigma_t*sigma_a # numerator: local mean corrected cross-correlation numer = (xcorr - ls_a*mean_t) # sigma_t cannot be zero, so wherever the denominator is zero, this must # be because sigma_a is zero (and therefore the normalized cross- # correlation is undefined), so set nxcorr to zero in these regions tol = np.sqrt(np.finfo(denom.dtype).eps) nxcorr = np.where(denom < tol,0,numer/denom) # if any of the coefficients are outside the range [-1 1], they will be # unstable to small variance in a or t, so set them to zero to reflect # the undefined 0/0 condition nxcorr = np.where(np.abs(nxcorr-1.) > np.sqrt(np.finfo(nxcorr.dtype).eps),nxcorr,0) # calculate the SSD if requested if do_ssd: # quadratic sum of the template tsum2 = np.sum(t**2.) # SSD between template and image ssd = ls2_a + tsum2 - 2.*xcorr # normalise to between 0 and 1 ssd -= ssd.min() ssd /= ssd.max() if trim: nxcorr = procrustes(nxcorr,a.shape,side='both') ssd = procrustes(ssd,a.shape,side='both') return nxcorr,ssd else: if trim: nxcorr = procrustes(nxcorr,a.shape,side='both') return nxcorr
def benchmark(t,a,outdims,maxtime=60): import resource # benchmark spatial convolutions # --------------------------------- convreps = 0 tic = resource.getrusage(resource.RUSAGE_SELF).ru_utime toc = tic while (toc-tic) < maxtime: convolve(a,t,mode='constant',cval=0) # xcorr = convolve(a,t,mode='full') convreps += 1 toc = resource.getrusage(resource.RUSAGE_SELF).ru_utime convtime = (toc-tic)/convreps # convtime == k(N1+N2) N = t.size*a.size k_conv = convtime/N # benchmark 1D Fourier transforms # --------------------------------- veclist = [np.random.randn(ss) for ss in outdims] fft1times = [] fftreps = [] for vec in veclist: reps = 0 tic = resource.getrusage(resource.RUSAGE_SELF).ru_utime toc = tic while (toc-tic) < maxtime: fftn(vec) toc = resource.getrusage(resource.RUSAGE_SELF).ru_utime reps += 1 fft1times.append((toc-tic)/reps) fftreps.append(reps) fft1times = np.asarray(fft1times) # fft1_time == k*N*log(N) N = np.asarray([vec.size for vec in veclist]) k_fft = np.mean(fft1times/(N*np.log(N))) # # benchmark ND Fourier transforms # # --------------------------------- # arraylist = [t,a] # fftntimes = [] # fftreps = [] # for array in arraylist: # reps = 0 # tic = resource.getrusage(resource.RUSAGE_SELF).ru_utime # toc = tic # while (toc-tic) < maxtime: # fftn(array,shape=a.shape) # reps += 1 # toc = resource.getrusage(resource.RUSAGE_SELF).ru_utime # fftntimes.append((toc-tic)/reps) # fftreps.append(reps) # fftntimes = np.asarray(fftntimes) # # fftn_time == k*prod(dimensions)*log(prod(dimensions)) for an M-dimensional array # nlogn = np.array([aa.size*np.log(aa.size) for aa in arraylist]) # k_fft = np.mean(fftntimes/nlogn) return k_conv,k_fft,convreps,fftreps
def fast_ssd(t,a,method=None,trim=True): """ Fast sum of squared differences (SSD block matching) for n-dimensional arrays Inputs: ---------------- t The template. Must have at least 2 elements, which cannot all be equal. a The search space. Its dimensionality must match that of the template. method The convolution method to use when computing the cross-correlation. Can be either 'direct', 'fourier' or None. If method == None (default), the convolution time is estimated for both methods and the best one is chosen for the given input array sizes. trim If True (default), the output array is trimmed down to the size of the search space. Otherwise, its size will be (f.shape[dd] + t.shape[dd] -1) for dimension dd. Output: ---------------- ssd An array containing the sum of squared differences between the image and the template, with the values normalized in the range -1.0 to 1.0. Wherever the search space has zero variance under the template, normalized cross-correlation is undefined. In such regions, the correlation coefficients are set to zero. References: Hermosillo et al 2002: Variational Methods for Multimodal Image Matching, International Journal of Computer Vision 50(3), 329-343, 2002 <http://www.springerlink.com/content/u4007p8871w10645/> Lewis 1995: Fast Template Matching, Vision Interface, p.120-123, 1995 <http://www.idiom.com/~zilla/Papers/nvisionInterface/nip.html> Alistair Muldal Department of Pharmacology University of Oxford <*****@*****.**> Sept 2012 """ if t.size < 2: raise Exception('Invalid template') if t.size > a.size: raise Exception('The input array must be smaller than the template') std_t,mean_t = np.std(t),np.mean(t) if std_t == 0: raise Exception('The values of the template must not all be equal') # output dimensions of xcorr need to match those of local_sum outdims = np.array([a.shape[dd]+t.shape[dd]-1 for dd in xrange(a.ndim)]) # would it be quicker to convolve in the spatial or frequency domain? NB # this is not very accurate since the speed of the Fourier transform # varies quite a lot with the output dimensions (e.g. 2-radix case) if method == None: spatialtime, ffttime = get_times(t,a,outdims) if spatialtime < ffttime: method = 'spatial' else: method = 'fourier' if method == 'fourier': # # in many cases, padding the dimensions to a power of 2 # # *dramatically* improves the speed of the Fourier transforms # # since it allows using radix-2 FFTs # fftshape = [nextpow2(ss) for ss in a.shape] # Fourier transform of the input array and the inverted template # af = fftn(a,shape=fftshape) # tf = fftn(ndflip(t),shape=fftshape) af = fftn(a,shape=outdims) tf = fftn(ndflip(t),shape=outdims) # 'non-normalized' cross-correlation xcorr = np.real(ifftn(tf*af)) else: xcorr = convolve(a,t,mode='constant',cval=0) # quadratic sum of the template tsum2 = np.sum(t**2.) # local quadratic sum of input array in the region of the template ls2_a = local_sum(a**2,t.shape) # now we need to make sure xcorr is the same size as ls2_a xcorr = procrustes(xcorr,ls2_a.shape,side='both') # SSD between template and image ssd = ls2_a + tsum2 - 2.*xcorr # normalise to between 0 and 1 ssd -= ssd.min() ssd /= ssd.max() if trim: ssd = procrustes(ssd,a.shape,side='both') return ssd
def fftx(ar): return fftn(ar, axes=[0, 1])
def make_wavelets(filename, order, nangles, nscales, sym): """ Make a wavelet transform from an HDF5 file """ # Set wavelet type if sym: wav = dgauss.dgauss_nd_sym else: wav = dgauss.dgauss_nd # Get info from input signal with h5py.File(filename) as src: spacing = ( abs(src['Longitude'][1] - src['Longitude'][0]), abs(src['Latitude'][1] - src['Latitude'][0])) nxs, nys, _ = src['Raster'].shape shape = (nxs, nys) # Generate axes for transform scales = dgauss.generate_scales(nscales, shape, spacing, order) angles = linspace( 0, pi * (1 - 1 / nangles), nangles) axes = [ (0, 'Angle', angles), (1, 'Scale', scales), (2, 'Longitude', src['Longitude'][...]), (3, 'Latitude', src['Latitude'][...]), ] # Remove NaNs and pad array... raster = src['Raster'][..., 0] mean = nanmean(nanmean(raster)) raster[isnan(raster)] = mean pad_raster, pad_mask = pad_array(raster) pad_shape = pad_raster.shape fft_data = fftn(pad_raster) # Generate sink file sink_fname = os.path.splitext(filename)[0] \ + '_deriv_order{0}.hdf5'.format(order) with h5py.File(sink_fname) as sink: sink_shape = angles.shape + scales.shape + shape sink.require_dataset('Raster', shape=sink_shape, dtype=float64) # Attach dimension labels to raster, write to sink for idx, label, dim in axes: sink.require_dataset(name=label, shape=dim.shape, dtype=float64, exact=True, data=dim) sink['Raster'].dims.create_scale(dset=sink[label], name=label) sink['Raster'].dims[idx].attach_scale(sink[label]) # Evaluate transforms progbar = pyprind.ProgBar(len(angles) * len(scales) + 1) freqs = fft_frequencies(pad_shape, spacing) for aidx, angle in enumerate(angles): rfreqs = rotate(freqs, (angle,)) for sidx, scale in enumerate(scales): item = 'Angle: {0:0.2f} deg, Scale: {1:0.2f} deg'.format( angle * 180 / pi, scale) progbar.update(item_id=item) filtered = ifftn( fft_data * wav(rfreqs, order=order, scale=scale)) sink['Raster'][aidx, sidx, ...] = filtered[pad_mask].real
def make_wavelets(filename, order, nangles, nscales, sym): """ Make a wavelet transform from an HDF5 file """ # Set wavelet type if sym: wav = dgauss.dgauss_nd_sym else: wav = dgauss.dgauss_nd # Get info from input signal with h5py.File(filename) as src: spacing = (abs(src['Longitude'][1] - src['Longitude'][0]), abs(src['Latitude'][1] - src['Latitude'][0])) nxs, nys, _ = src['Raster'].shape shape = (nxs, nys) # Generate axes for transform scales = dgauss.generate_scales(nscales, shape, spacing, order) angles = linspace(0, pi * (1 - 1 / nangles), nangles) axes = [ (0, 'Angle', angles), (1, 'Scale', scales), (2, 'Longitude', src['Longitude'][...]), (3, 'Latitude', src['Latitude'][...]), ] # Remove NaNs and pad array... raster = src['Raster'][..., 0] mean = nanmean(nanmean(raster)) raster[isnan(raster)] = mean pad_raster, pad_mask = pad_array(raster) pad_shape = pad_raster.shape fft_data = fftn(pad_raster) # Generate sink file sink_fname = os.path.splitext(filename)[0] \ + '_deriv_order{0}.hdf5'.format(order) with h5py.File(sink_fname) as sink: sink_shape = angles.shape + scales.shape + shape sink.require_dataset('Raster', shape=sink_shape, dtype=float64) # Attach dimension labels to raster, write to sink for idx, label, dim in axes: sink.require_dataset(name=label, shape=dim.shape, dtype=float64, exact=True, data=dim) sink['Raster'].dims.create_scale(dset=sink[label], name=label) sink['Raster'].dims[idx].attach_scale(sink[label]) # Evaluate transforms progbar = pyprind.ProgBar(len(angles) * len(scales) + 1) freqs = fft_frequencies(pad_shape, spacing) for aidx, angle in enumerate(angles): rfreqs = rotate(freqs, (angle, )) for sidx, scale in enumerate(scales): item = 'Angle: {0:0.2f} deg, Scale: {1:0.2f} deg'.format( angle * 180 / pi, scale) progbar.update(item_id=item) filtered = ifftn(fft_data * wav(rfreqs, order=order, scale=scale)) sink['Raster'][aidx, sidx, ...] = filtered[pad_mask].real
def cfftn(data,axes): '''Centered fft''' return fftpack.ifftshift(fftpack.fftn(fftpack.fftshift(data,axes=axes)))
def convolve_weighted_fft(in1, in2, mode="full", weighting="None", displayplots=False): """Convolve two N-dimensional arrays using FFT. Convolve `in1` and `in2` using the fast Fourier transform method, with the output size determined by the `mode` argument. This is generally much faster than `convolve` for large arrays (n > ~500), but can be slower when only a few output values are needed, and can only output float arrays (int or object array inputs will be cast to float). Parameters ---------- in1 : array_like First input. in2 : array_like Second input. Should have the same number of dimensions as `in1`; if sizes of `in1` and `in2` are not equal then `in1` has to be the larger array. mode : str {'full', 'valid', 'same'}, optional A string indicating the size of the output: ``full`` The output is the full discrete linear convolution of the inputs. (Default) ``valid`` The output consists only of those elements that do not rely on the zero-padding. ``same`` The output is the same size as `in1`, centered with respect to the 'full' output. Returns ------- out : array An N-dimensional array containing a subset of the discrete linear convolution of `in1` with `in2`. """ in1 = np.asarray(in1) in2 = np.asarray(in2) if np.isscalar(in1) and np.isscalar(in2): # scalar inputs return in1 * in2 elif not in1.ndim == in2.ndim: raise ValueError("in1 and in2 should have the same rank") elif in1.size == 0 or in2.size == 0: # empty arrays return np.array([]) s1 = np.array(in1.shape) s2 = np.array(in2.shape) complex_result = np.issubdtype(in1.dtype, complex) or np.issubdtype(in2.dtype, complex) size = s1 + s2 - 1 if mode == "valid": _check_valid_mode_shapes(s1, s2) # Always use 2**n-sized FFT fsize = 2 ** np.ceil(np.log2(size)).astype(int) fslice = tuple([slice(0, int(sz)) for sz in size]) if not complex_result: fft1 = rfftn(in1, fsize) fft2 = rfftn(in2, fsize) theorigmax = np.max(np.absolute(irfftn(gccproduct(fft1, fft2, "None"), fsize)[fslice])) ret = irfftn(gccproduct(fft1, fft2, weighting, displayplots=displayplots), fsize)[ fslice ].copy() ret = irfftn(gccproduct(fft1, fft2, weighting, displayplots=displayplots), fsize)[ fslice ].copy() ret = ret.real ret *= theorigmax / np.max(np.absolute(ret)) else: fft1 = fftpack.fftn(in1, fsize) fft2 = fftpack.fftn(in2, fsize) theorigmax = np.max(np.absolute(fftpack.ifftn(gccproduct(fft1, fft2, "None"))[fslice])) ret = fftpack.ifftn(gccproduct(fft1, fft2, weighting, displayplots=displayplots))[ fslice ].copy() ret *= theorigmax / np.max(np.absolute(ret)) # scale to preserve the maximum if mode == "full": return ret elif mode == "same": return _centered(ret, s1) elif mode == "valid": return _centered(ret, s1 - s2 + 1)