def cwt_diff(signal, wv_width, order=1, method='auto'): """ Take a numerical derivative using a Haar wavelet (noise-supression) Parameters ---------- signal : ndarray (1D) Signal data wv_width : int Width of wavelet to use (balance noise suppression and distortion) order : int, optional (default=1) Order of derivative (e.g., 1st-order derivative) method : str {'auto' (default), 'fft', 'direct'} Returns ------- deriv : ndarray (1D) Derivative of input signal """ deriv = _copy.deepcopy(signal) for count in range(order): try: deriv = _convolve(deriv, PeakFinder.haar(wv_width), mode='same', method=method) except: print('peakfind.py | cwt_diff: Likely using an old version of SciPy (no convolve method parameter)') deriv = _convolve(deriv, PeakFinder.haar(wv_width), mode='same') return deriv
def cwt_diff(signal, wv_width, order=1, method='auto'): """ Take a numerical derivative using a Haar wavelet (noise-supression) Parameters ---------- signal : ndarray (1D) Signal data wv_width : int Width of wavelet to use (balance noise suppression and distortion) order : int, optional (default=1) Order of derivative (e.g., 1st-order derivative) method : str {'auto' (default), 'fft', 'direct'} Returns ------- deriv : ndarray (1D) Derivative of input signal """ deriv = _copy.deepcopy(signal) for count in range(order): deriv = _convolve(deriv, PeakFinder.haar(wv_width), mode='same', method=method) return deriv
def max_patch(im, filt_size): """Return the patch of maximum response (highest intensity) in the image. Might be used on an image containing fixation densities to determine where to crop the real image to have "maximum saliency". Args: im (2D matrix): the image to find the max response. filt_size (scalar): the filter size in pixels. Only set up for square filter regions, currently. Returns: max_im: a 2D array of size (filt_size, filt_size) containing the maximum response. max_x: a scalar containing the left-hand x coord. max_y: the top y coord of the max_im box. """ filter_kernel = _np.ones((filt_size, filt_size)) filter_responses = _convolve(im, filter_kernel, mode='valid') position_of_maxium = _np.unravel_index(filter_responses.argmax(), filter_responses.shape) max_y, max_x = position_of_maxium max_im = im[max_y:max_y+filt_size, max_x:max_x+filt_size] return(max_im, max_x, max_y)
def convolve_overlap_add(signal, impulse_responses, nhop, ntaps, initial_values=None): """Convolve iterable `signal` with time-variant `impulse_responses`. :param signal: Signal in blocks of size `nblock`. :param impulse_responses: Impulse responses of length `ntaps`. :param nhop: Impulse responses is updated every `nhop` samples. This should correspond to the blocksize of `signal`. :param ntaps: Length of impulse responses. :param initial_values. Value to use before convolution kicks in. :returns: Convolution. This function implements the overlap-add method. Time-variant `impulse_responses is supported. Each item (`block`) in `signal` corresponds to an impulse response (`ir`) in `impulse_responses`. This implementation of overlap-add buffers only one segment. Therefore, only `nblock>=ntaps` is supported. .. warning:: This function cannot be used when `ntaps > nblock`. .. seealso:: :func:`convolve_overlap_save` """ nblock = nhop if not nblock >= ntaps: raise ValueError( "Amount of samples in block should be the same or more than the amount of filter taps." ) if initial_values is None: tail_previous_block = np.zeros(ntaps - 1) else: tail_previous_block = initial_values for block, ir in zip(signal, impulse_responses): # The result of the convolution consists of a head, a body and a tail # - the head and tail have length `taps-1` # - the body has length `signal-taps+1`?? try: convolved = _convolve(block, ir, mode='full') except ValueError: raise GeneratorExit # The final block consists of # - the head and body of the current convolution # - and the tail of the previous convolution. resulting_block = convolved[:-ntaps + 1] #print(resulting_block) #resulting_block[:ntaps-1] += tail_previous_block resulting_block[:ntaps - 1] = resulting_block[:ntaps - 1] + tail_previous_block # Because of possibly different dtypes # We store the tail for the next cycle tail_previous_block = convolved[-ntaps + 1:] #print(tail_previous_block) # Yield the result of this step yield resulting_block
def convolve_overlap_add(signal, impulse_responses, nhop, ntaps, initial_values=None): """Convolve iterable `signal` with time-variant `impulse_responses`. :param signal: Signal in blocks of size `nblock`. :param impulse_responses: Impulse responses of length `ntaps`. :param nhop: Impulse responses is updated every `nhop` samples. This should correspond to the blocksize of `signal`. :param ntaps: Length of impulse responses. :param initial_values. Value to use before convolution kicks in. :returns: Convolution. This function implements the overlap-add method. Time-variant `impulse_responses is supported. Each item (`block`) in `signal` corresponds to an impulse response (`ir`) in `impulse_responses`. This implementation of overlap-add buffers only one segment. Therefore, only `nblock>=ntaps` is supported. .. warning:: This function cannot be used when `ntaps > nblock`. .. seealso:: :func:`convolve_overlap_save` """ nblock = nhop if not nblock >= ntaps: raise ValueError("Amount of samples in block should be the same or more than the amount of filter taps.") if initial_values is None: tail_previous_block = np.zeros(ntaps - 1) else: tail_previous_block = initial_values for block, ir in zip(signal, impulse_responses): # The result of the convolution consists of a head, a body and a tail # - the head and tail have length `taps-1` # - the body has length `signal-taps+1`?? try: convolved = _convolve(block, ir, mode="full") except ValueError: raise GeneratorExit # The final block consists of # - the head and body of the current convolution # - and the tail of the previous convolution. resulting_block = convolved[: -ntaps + 1] # print(resulting_block) # resulting_block[:ntaps-1] += tail_previous_block resulting_block[: ntaps - 1] = ( resulting_block[: ntaps - 1] + tail_previous_block ) # Because of possibly different dtypes # We store the tail for the next cycle tail_previous_block = convolved[-ntaps + 1 :] # print(tail_previous_block) # Yield the result of this step yield resulting_block
def _convolve_crossfade(block, ir1, ir2, fading1): """Convolve block with two impulse responses and crossfade the result. :param block: Block. :param ir1: Impulse response 1. :param ir2: Impulse response 2. :param fading1: Fading. :returns: Crossfaded convolutions of block and impulse responses. We switch from `ir1` to `ir2`. A more efficient method is presented in *Efficient time-varying FIR filtering using crossfading implemented in the DFT domain* by Frank Wefers. """ # Convolve segment with both impulse responses. convolved1 = _convolve(block, ir1, mode="full") convolved2 = _convolve(block, ir2, mode="full") # Fading windows fading2 = 1.0 - fading1 # Crossfaded convolutions convolved = convolved1 * fading1 + convolved2 * fading2 return convolved
def _convolve_crossfade(block, ir1, ir2, fading1): """Convolve block with two impulse responses and crossfade the result. :param block: Block. :param ir1: Impulse response 1. :param ir2: Impulse response 2. :param fading1: Fading. :returns: Crossfaded convolutions of block and impulse responses. We switch from `ir1` to `ir2`. A more efficient method is presented in *Efficient time-varying FIR filtering using crossfading implemented in the DFT domain* by Frank Wefers. """ # Convolve segment with both impulse responses. convolved1 = _convolve(block, ir1, mode='full') convolved2 = _convolve(block, ir2, mode='full') # Fading windows fading2 = 1. - fading1 # Crossfaded convolutions convolved = convolved1 * fading1 + convolved2 * fading2 return convolved
def algorithm(cls, signal, params): fsamp = signal.get_sampling_freq() fp, fs, loss, att, wtype = params["fp"], params["fs"], params[ "loss"], params["att"], params["wtype"] if isinstance(signal, _UnevenlySignal): cls.warn( 'Filtering Unevenly signal is undefined. Returning original signal.' ) return signal fp = _np.array(fp) fs = _np.array(fs) if att > 0: att = -att d1 = 10**(loss / 10) d2 = 10**(att / 10) Dsamp = _np.min(abs(fs - fp)) / fsamp # from https://dsp.stackexchange.com/questions/31066/how-many-taps-does-an-fir-filter-need N = int(2 / 3 * _np.log10(1 / (10 * d1 * d2)) * fsamp / Dsamp) pass_zero = True if isinstance(fp, Sequence): if fp[0] > fs[0]: pass_zero = False else: if fp > fs: pass_zero = False nyq = 0.5 * fsamp fp = _np.array(fp) wp = fp / nyq if N % 2 == 0: N += 1 b = _firwin(N, wp, width=Dsamp, window=wtype, pass_zero=pass_zero) sig_filtered = signal.clone_properties( _convolve(signal.get_values(), b, mode='same')) if _np.isnan(sig_filtered[0]): cls.warn( 'Filter parameters allow no solution. Returning original signal.' ) return signal else: return sig_filtered
def algorithm(cls, signal, params): fsamp = signal.get_sampling_freq() fp, fs, loss, att, wtype = params["fp"], params["fs"], params["loss"], params["att"], params["wtype"] if isinstance(signal, _UnevenlySignal): cls.warn('Filtering Unevenly signal is undefined. Returning original signal.') return signal fp = _np.array(fp) fs = _np.array(fs) if att>0: att = -att d1 = 10**(loss/10) d2 = 10**(att/10) Dsamp = _np.min(abs(fs-fp))/fsamp # from https://dsp.stackexchange.com/questions/31066/how-many-taps-does-an-fir-filter-need N = int(2/3*_np.log10(1/(10*d1*d2))*fsamp/Dsamp) pass_zero=True if isinstance(fp, Sequence): if fp[0]>fs[0]: pass_zero=False else: if fp>fs: pass_zero=False nyq = 0.5 * fsamp fp = _np.array(fp) wp = fp / nyq print(N) b = _firwin(N, wp, width=Dsamp, window=wtype, pass_zero=pass_zero) sig_filtered = signal.clone_properties(_convolve(signal.get_values(), b, mode='same')) if _np.isnan(sig_filtered[0]): cls.warn('Filter parameters allow no solution. Returning original signal.') return signal else: return sig_filtered
def convolve_overlap_save(signal, impulse_responses, nhop, ntaps): """Convolve signal with linear time-invariant `impulse_response` using overlap-discard method. :param signal: Signal. Consists of samples. :param impulse_responses: Linear time-variant impulses response of filter. :param nhop: Impulse response is renewed every `nhop` samples. :returns: Convolution of `signal` with `impulse_responses`. :rtype: Generator consisting of arrays. .. note:: The *overlap-discard* method is more commonly known as *overlap-save*. """ nwindow = nhop + ntaps - 1 noverlap = ntaps - 1 windows = blocks(signal, nwindow, noverlap) # Convolve function to use _convolve_func = lambda x, y: _convolve(x, y, mode="valid") # Convolved blocks convolved = map(_convolve_func, windows, impulse_responses) yield from convolved
def convolve_overlap_save(signal, impulse_responses, nhop, ntaps): """Convolve signal with linear time-invariant `impulse_response` using overlap-discard method. :param signal: Signal. Consists of samples. :param impulse_responses: Linear time-variant impulses response of filter. :param nhop: Impulse response is renewed every `nhop` samples. :returns: Convolution of `signal` with `impulse_responses`. :rtype: Generator consisting of arrays. .. note:: The *overlap-discard* method is more commonly known as *overlap-save*. """ nwindow = nhop + ntaps - 1 noverlap = ntaps - 1 windows = blocks(signal, nwindow, noverlap) # Convolve function to use _convolve_func = lambda x, y: _convolve(x, y, mode='valid') # Convolved blocks convolved = map(_convolve_func, windows, impulse_responses) yield from convolved
def convolve_overlap_discard(signal, impulse_response, nblock_in=None, nblock_out=None): """Convolve signal with linear time-invariant `impulse_response` using overlap-discard method. :param signal: Signal. Can either consists of blocks or samples. `nblock_in` should be set to the block size of the signal. :param impulse_response: Linear time-invariant impulse response of filter. :param nblock_in: Actual input blocksize of signal. Should be set to `None` is `signal` is sample-based. :param nblock_out: Desired output blocksize. :returns: Convolution of `signal` with `impulse_response`. :rtype: Generator consisting of arrays. Setting the input blocksize can be useful because this gives control over the delay of the process. Setting the output blocksize is convenient because you know on beforehand the output blocksize. Setting neither will result in blocksize of one, or individual samples. This will be slow. Setting both is not possible. .. note:: The *overlap-discard* method is more commonly known as *overlap-save*. """ # Amount of filter taps ntaps = len(impulse_response) # Amount of overlap that is needed noverlap = ntaps - 1 # In the following block we create overlapping windows. # Both are set. if nblock_in is not None and nblock_out is not None: raise ValueError("Set block size of either input or output.") # Only output blocksize is explicitly mentioned elif nblock_out is not None: nblock_in = nblock_out + ntaps - 1 # Only input blocksize is explicitly mentioned elif nblock_in is not None: if not nblock_in >= ntaps: raise ValueError("Amount of samples in block should be the same or more than the amount of filter taps.") nblock_out = nblock_in - ntaps + 1 else: nblock_in = ntaps nblock_out = nblock_in - ntaps + 1 windows = blocks(signal, nblock_in, noverlap) ## We have sample-based signal and we want blocks with specified size out. # if nblock_in is None and nblock_out is not None: # nblock_in = nblock_out + ntaps -1 # windows = blocks(signal, nblock_in, noverlap) ## We have sample-based signal and we want samples out (actually blocks of size 1). # elif nblock_in is None and nblock_out is None: # nblock_in = ntaps # windows = blocks(signal, nblock_in, noverlap) ## We have block-based signal and we don't mind output block size # elif nblock_in is not None and nblock_out is None: # if not nblock_in >= ntaps: # raise ValueError("Amount of samples in block should be the same or more than the amount of filter taps.") # nblock_out = nblock_in - ntaps + 1 # windows = change_blocks(signal, nblock_in, 0, nblock_in, noverlap) ## We have block-based signal and we have specified an output block. We need to change the block size. # elif nblock_in is not None and nblock_out is not None: # if not nblock_in >= ntaps: # raise ValueError("Amount of samples in block should be the same or more than the amount of filter taps.") # nblock_in_new = nblock_out + ntaps -1 # windows = change_blocks(signal, nblock_in, 0, nblock_in_new, noverlap, ) # nblock_in = nblock_in_new # Convolve function to use _convolve_func = lambda x: _convolve(x, impulse_response, mode="valid") # Convolved blocks convolved = map(_convolve_func, windows) return convolved, nblock_out
def convolve_overlap_discard(signal, impulse_response, nblock_in=None, nblock_out=None): """Convolve signal with linear time-invariant `impulse_response` using overlap-discard method. :param signal: Signal. Can either consists of blocks or samples. `nblock_in` should be set to the block size of the signal. :param impulse_response: Linear time-invariant impulse response of filter. :param nblock_in: Actual input blocksize of signal. Should be set to `None` is `signal` is sample-based. :param nblock_out: Desired output blocksize. :returns: Convolution of `signal` with `impulse_response`. :rtype: Generator consisting of arrays. Setting the input blocksize can be useful because this gives control over the delay of the process. Setting the output blocksize is convenient because you know on beforehand the output blocksize. Setting neither will result in blocksize of one, or individual samples. This will be slow. Setting both is not possible. .. note:: The *overlap-discard* method is more commonly known as *overlap-save*. """ # Amount of filter taps ntaps = len(impulse_response) # Amount of overlap that is needed noverlap = ntaps - 1 # In the following block we create overlapping windows. # Both are set. if nblock_in is not None and nblock_out is not None: raise ValueError("Set block size of either input or output.") # Only output blocksize is explicitly mentioned elif nblock_out is not None: nblock_in = nblock_out + ntaps - 1 # Only input blocksize is explicitly mentioned elif nblock_in is not None: if not nblock_in >= ntaps: raise ValueError( "Amount of samples in block should be the same or more than the amount of filter taps." ) nblock_out = nblock_in - ntaps + 1 else: nblock_in = ntaps nblock_out = nblock_in - ntaps + 1 windows = blocks(signal, nblock_in, noverlap) ## We have sample-based signal and we want blocks with specified size out. #if nblock_in is None and nblock_out is not None: #nblock_in = nblock_out + ntaps -1 #windows = blocks(signal, nblock_in, noverlap) ## We have sample-based signal and we want samples out (actually blocks of size 1). #elif nblock_in is None and nblock_out is None: #nblock_in = ntaps #windows = blocks(signal, nblock_in, noverlap) ## We have block-based signal and we don't mind output block size #elif nblock_in is not None and nblock_out is None: #if not nblock_in >= ntaps: #raise ValueError("Amount of samples in block should be the same or more than the amount of filter taps.") #nblock_out = nblock_in - ntaps + 1 #windows = change_blocks(signal, nblock_in, 0, nblock_in, noverlap) ## We have block-based signal and we have specified an output block. We need to change the block size. #elif nblock_in is not None and nblock_out is not None: #if not nblock_in >= ntaps: #raise ValueError("Amount of samples in block should be the same or more than the amount of filter taps.") #nblock_in_new = nblock_out + ntaps -1 #windows = change_blocks(signal, nblock_in, 0, nblock_in_new, noverlap, ) #nblock_in = nblock_in_new # Convolve function to use _convolve_func = lambda x: _convolve(x, impulse_response, mode='valid') # Convolved blocks convolved = map(_convolve_func, windows) return convolved, nblock_out