def fftconvolve(in1, in2, mode="full", pad_to_power_of_two=True): """Convolve two N-dimensional arrays using FFT. See convolve. """ s1 = array(in1.shape) s2 = array(in2.shape) complex_result = (N.issubdtype(in1.dtype, N.complex) or N.issubdtype(in2.dtype, N.complex)) size = s1 + s2 - 1 if pad_to_power_of_two: # Use 2**n-sized FFT; it might improve performance fsize = 2 ** N.ceil(N.log2(size)) else: # Padding to a power of two might degrade performance, too fsize = size IN1 = N.fft.fftn(in1, fsize) IN1 *= N.fft.fftn(in2, fsize) fslice = tuple([slice(0, int(sz)) for sz in size]) ret = N.fft.ifftn(IN1)[fslice].copy() del IN1 if not complex_result: ret = ret.real if mode == "full": return ret elif mode == "same": if product(s1, axis=0) > product(s2, axis=0): osize = s1 else: osize = s2 return _centered(ret, osize) elif mode == "valid": return _centered(ret, abs(s2 - s1) + 1)
def fftconvolve(in1, in2, mode="same", threads=1): """Same as above but with pyfftw added in""" in1 = np.asarray(in1) in2 = np.asarray(in2) if in1.ndim == in2.ndim == 0: # scalar inputs return in1 * in2 elif not in1.ndim == in2.ndim: raise ValueError("in1 and in2 should have the same dimensionality") 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)) shape = s1 + s2 - 1 # Check that input sizes are compatible with 'valid' mode if sig._inputs_swap_needed(mode, s1, s2): # Convolution is commutative; order doesn't have any effect on output in1, s1, in2, s2 = in2, s2, in1, s1 # Speed up FFT by padding to optimal size for FFTPACK fshape = [sig.fftpack.helper.next_fast_len(int(d)) for d in shape] fslice = tuple([slice(0, int(sz)) for sz in shape]) # Pre-1.9 NumPy FFT routines are not threadsafe. For older NumPys, make # sure we only call rfftn/irfftn from one thread at a time. if not complex_result and (sig._rfft_mt_safe or sig._rfft_lock.acquire(False)): try: sp1 = rfftn(in1, fshape, threads=threads) sp2 = rfftn(in2, fshape, threads=threads) ret = (irfftn(sp1 * sp2, fshape, threads=threads)[fslice].copy()) finally: if not sig._rfft_mt_safe: sig._rfft_lock.release() else: # If we're here, it's either because we need a complex result, or we # failed to acquire _rfft_lock (meaning rfftn isn't threadsafe and # is already in use by another thread). In either case, use the # (threadsafe but slower) SciPy complex-FFT routines instead. sp1 = fftn(in1, fshape, threads=threads) sp2 = fftn(in2, fshape, threads=threads) ret = ifftn(sp1 * sp2, threads=threads)[fslice].copy() if not complex_result: ret = ret.real if mode == "full": return ret elif mode == "same": return sig._centered(ret, s1) elif mode == "valid": return sig._centered(ret, s1 - s2 + 1) else: raise ValueError("Acceptable mode flags are 'valid'," " 'same', or 'full'.")
def _fftconvolve(in1, in2, mode="full", axis=None): """ Convolve two N-dimensional arrays using FFT. See convolve. This is a fix of scipy.signal.fftconvolve, adding an axis argument and importing locally the stuff only needed for this function """ #Locally import stuff only required for this: from scipy.fftpack import fftn, fft, ifftn, ifft from scipy.signal.signaltools import _centered from numpy import array, product s1 = array(in1.shape) s2 = array(in2.shape) complex_result = (np.issubdtype(in1.dtype, np.complex) or np.issubdtype(in2.dtype, np.complex)) if axis is None: size = s1+s2-1 fslice = tuple([slice(0, int(sz)) for sz in size]) else: equal_shapes = s1==s2 # allow equal_shapes[axis] to be False equal_shapes[axis] = True assert equal_shapes.all(), 'Shape mismatch on non-convolving axes' size = s1[axis]+s2[axis]-1 fslice = [slice(l) for l in s1] fslice[axis] = slice(0, int(size)) fslice = tuple(fslice) # Always use 2**n-sized FFT fsize = 2**np.ceil(np.log2(size)) if axis is None: IN1 = fftn(in1,fsize) IN1 *= fftn(in2,fsize) ret = ifftn(IN1)[fslice].copy() else: IN1 = fft(in1,fsize,axis=axis) IN1 *= fft(in2,fsize,axis=axis) ret = ifft(IN1,axis=axis)[fslice].copy() if not complex_result: del IN1 ret = ret.real if mode == "full": return ret elif mode == "same": if product(s1,axis=0) > product(s2,axis=0): osize = s1 else: osize = s2 return _centered(ret,osize) elif mode == "valid": return _centered(ret,abs(s2-s1)+1)
def convolve_two_arrays(array1, array2): """Convolution based off the convolution theorem""" if len(array1) < len(array2): diff = len(array2) - len(array1) array1 = numpy.pad( array1, (int(numpy.floor(diff / 2)), int(numpy.ceil(diff / 2))), 'constant', constant_values=0) elif len(array2) < len(array1): diff = len(array1) - len(array2) array2 = numpy.pad( array2, (int(numpy.floor(diff / 2)), int(numpy.ceil(diff / 2))), 'constant', constant_values=0) #The next 8 lines follow closely with scipy.signal.fftconvolve shape = np.maximum(np.array(array1.shape), np.array(array2.shape)) s1 = np.array(array1.shape) s2 = np.array(array2.shape) shape = s1 + s2 - 1 fshape = [fftpack.helper.next_fast_len(d) for d in shape] fslice = tuple([slice(sz) for sz in shape]) array1_fft = DFT(array1, fshape) array2_fft = DFT(array2, fshape) #Perform convolution convolved_arr = numpy.asarray(numpy.real(IDFT(array1_fft * array2_fft)))[fslice] ret = _centered(convolved_arr, s1) #Recover wanted array return ret
def fft_convolve(v1, v2): s1 = v1.shape[-1] s2 = v2.shape[-1] valid = abs(s2-s1) + 1 fsize = 2**np.ceil(np.log2(s1+s2-1)) convolver = (EPS + signal.fft(v2, fsize, axis=-1)) convolved = np.real(signal.ifft(convolver * signal.fft(v1, fsize, axis=-1), axis=-1)) return _centered(convolved, valid)
def fftconvolve(in1, in2, mode="full", axis=None): """Convolve two N-dimensional arrays using FFT. See convolve. """ s1 = array(in1.shape) s2 = array(in2.shape) complex_result = (np.issubdtype(in1.dtype, np.complex) or np.issubdtype(in2.dtype, np.complex)) if axis is None: size = s1+s2-1 fslice = tuple([slice(0, int(sz)) for sz in size]) else: equal_shapes = s1==s2 # allow equal_shapes[axis] to be False equal_shapes[axis] = True assert equal_shapes.all(), 'Shape mismatch on non-convolving axes' size = s1[axis]+s2[axis]-1 fslice = [slice(l) for l in s1] fslice[axis] = slice(0, int(size)) fslice = tuple(fslice) # Always use 2**n-sized FFT fsize = 2**np.ceil(np.log2(size)) if axis is None: IN1 = fftn(in1,fsize) IN1 *= fftn(in2,fsize) ret = ifftn(IN1)[fslice].copy() else: IN1 = fft(in1,fsize,axis=axis) IN1 *= fft(in2,fsize,axis=axis) ret = ifft(IN1,axis=axis)[fslice].copy() del IN1 if not complex_result: ret = ret.real if mode == "full": return ret elif mode == "same": if product(s1,axis=0) > product(s2,axis=0): osize = s1 else: osize = s2 return _centered(ret,osize) elif mode == "valid": return _centered(ret,abs(s2-s1)+1)
def _fftconvolve_18(in1, in2, int2_fft, mode="same"): """ scipy routine scipy.signal.fftconvolve with kernel already fourier transformed """ in1 = signaltools.asarray(in1) in2 = signaltools.asarray(in2) if in1.ndim == in2.ndim == 0: # scalar inputs return in1 * in2 elif not in1.ndim == in2.ndim: raise ValueError("in1 and in2 should have the same dimensionality") elif in1.size == 0 or in2.size == 0: # empty arrays return signaltools.array([]) s1 = signaltools.array(in1.shape) s2 = signaltools.array(in2.shape) shape = s1 + s2 - 1 # Check that input sizes are compatible with 'valid' mode if signaltools._inputs_swap_needed(mode, s1, s2): # Convolution is commutative; order doesn't have any effect on output in1, s1, in2, s2 = in2, s2, in1, s1 # Speed up FFT by padding to optimal size for FFTPACK fshape = [signaltools.fftpack.helper.next_fast_len(int(d)) for d in shape] fslice = tuple([slice(0, int(sz)) for sz in shape]) # Pre-1.9 NumPy FFT routines are not threadsafe. For older NumPys, make # sure we only call rfftn/irfftn from one thread at a time. ret = np.fft.irfftn(np.fft.rfftn(in1, fshape) * int2_fft, fshape)[fslice].copy() #np.fft.rfftn(in2, fshape) if mode == "full": return ret elif mode == "same": return signaltools._centered(ret, s1) elif mode == "valid": return signaltools._centered(ret, s1 - s2 + 1) else: raise ValueError("Acceptable mode flags are 'valid'," " 'same', or 'full'.")
def _fftconvolve_14(in1, in2, int2_fft, mode="same"): """ scipy routine scipy.signal.fftconvolve with kernel already fourier transformed """ in1 = signaltools.asarray(in1) in2 = signaltools.asarray(in2) if in1.ndim == in2.ndim == 0: # scalar inputs return in1 * in2 elif not in1.ndim == in2.ndim: raise ValueError("in1 and in2 should have the same dimensionality") elif in1.size == 0 or in2.size == 0: # empty arrays return signaltools.array([]) s1 = signaltools.array(in1.shape) s2 = signaltools.array(in2.shape) shape = s1 + s2 - 1 # Speed up FFT by padding to optimal size for FFTPACK fshape = [signaltools._next_regular(int(d)) for d in shape] fslice = tuple([slice(0, int(sz)) for sz in shape]) # Pre-1.9 NumPy FFT routines are not threadsafe. For older NumPys, make # sure we only call rfftn/irfftn from one thread at a time. ret = signaltools.irfftn( signaltools.rfftn(in1, fshape) * int2_fft, fshape)[fslice].copy() #np.fft.rfftn(in2, fshape) if mode == "full": return ret elif mode == "same": return signaltools._centered(ret, s1) elif mode == "valid": return signaltools._centered(ret, s1 - s2 + 1) else: raise ValueError("Acceptable mode flags are 'valid'," " 'same', or 'full'.")
def numpy_normxcorr(templates, stream, pads, *args, **kwargs): """ Compute the normalized cross-correlation using numpy and bottleneck. :param templates: 2D Array of templates :type templates: np.ndarray :param stream: 1D array of continuous data :type stream: np.ndarray :param pads: List of ints of pad lengths in the same order as templates :type pads: list :return: np.ndarray of cross-correlations :return: np.ndarray channels used """ import bottleneck from scipy.signal.signaltools import _centered # Generate a template mask used_chans = ~np.isnan(templates).any(axis=1) # Currently have to use float64 as bottleneck runs into issues with other # types: https://github.com/kwgoodman/bottleneck/issues/164 stream = stream.astype(np.float64) templates = templates.astype(np.float64) template_length = templates.shape[1] stream_length = len(stream) fftshape = next_fast_len(template_length + stream_length - 1) # Set up normalizers stream_mean_array = bottleneck.move_mean( stream, template_length)[template_length - 1:] stream_std_array = bottleneck.move_std( stream, template_length)[template_length - 1:] # because stream_std_array is in denominator or res, nan all 0s stream_std_array[stream_std_array == 0] = np.nan # Normalize and flip the templates norm = ((templates - templates.mean(axis=-1, keepdims=True)) / (templates.std(axis=-1, keepdims=True) * template_length)) norm_sum = norm.sum(axis=-1, keepdims=True) stream_fft = np.fft.rfft(stream, fftshape) template_fft = np.fft.rfft(np.flip(norm, axis=-1), fftshape, axis=-1) res = np.fft.irfft(template_fft * stream_fft, fftshape)[:, 0:template_length + stream_length - 1] res = ((_centered(res, stream_length - template_length + 1)) - norm_sum * stream_mean_array) / stream_std_array res[np.isnan(res)] = 0.0 # res[np.isinf(res)] = 0.0 for i, pad in enumerate(pads): # range(len(pads)): res[i] = np.append(res[i], np.zeros(pad))[pad:] return res.astype(np.float32), used_chans
def run(self, f0=None, fir=None): """Run the lock-in amplifier at reference frequency ``f0``, using the finite impulse response filter ``fir``. """ if f0 is None: self.f0 = f0 = self.f0_est if fir is not None: self.fir = fir self.z = z = signal.fftconvolve(self.x * np.exp(-2j*np.pi*f0*self.t), 2*self.fir, "same") n_fir = self.fir.size indices = np.arange(self.t.size) # Valid region mask # This is borrowed explicitly from scipy.signal.sigtools.fftconvolve self.m = m = np.zeros_like(self.t, dtype=bool) self.m[_centered(indices, self.t.size - n_fir + 1)] = True self.A = abs(self.z) self.phi = np.angle(self.z)
def _broadcasted_convolution(in1, in2, orig_shape): fwr = in1[:, :, :, np.newaxis] * np.swapaxes(in2[:, :, :, np.newaxis], 0, 3) fwr = fwr.reshape(len(in1), *fwr.shape[1:3], len(in2)) e = ifftn(fwr, s=fwr.shape[1:3], axes=[1, 2]) return _centered(e, (len(in1), *orig_shape, len(in2)))