"""Spectral Residual """ # Author: Andreas Mueller import numpy as np def SR(X, window_size): X = (X - X.min()) / (X.max() - X.min()) X = X.ravel() fft = np.fft.fft(X) amp = np.abs(fft) log_amp = np.log(amp) phase = np.angle(fft) # split spectrum into bias term and symmetric frequencies bias, sym_freq = log_amp[:1], log_amp[1:] # select just the first half of the sym_freq freq = sym_freq[:(len(sym_freq) + 1) // 2] window_amp = 100 pad_left = (window_amp - 1) // 2 padded_freq = np.concatenate([np.tile(X[0], pad_left), freq, np.tile(X[-1], window_amp - pad_left - 1)]) conv_amp = np.ones(window_amp) / window_amp ma_freq = np.convolve(padded_freq, conv_amp, 'valid') # construct moving average log amplitude spectrum ma_log_amp = np.concatenate([ bias, ma_freq, (ma_freq[:-1] if len(sym_freq) % 2 == 1 else ma_freq)[::-1] ]) assert ma_log_amp.shape[0] == log_amp.shape[0], "`ma_log_amp` size does not match `log_amp` size." # compute residual spectrum and transform back to time domain res_amp = log_amp - ma_log_amp sr = np.abs(np.fft.ifft(np.exp(res_amp + 1j * phase))) return sr