为什么scipy.signal.sosfilt在使用scipy.signal.butter中的sos值时会失败,其阶数大于2?

fruv7luv  于 2023-08-05  发布在  其他
关注(0)|答案(2)|浏览(161)

我正在使用scipy.signal来过滤用pysoundfile导入的wav文件。我已经成功地使用filtfilt执行阶数为2和4的高通滤波器,并使用sosfilt执行阶数为2的高通滤波器,但当我指定阶数为4时,sosfilt失败:
第一个月
我写了下面的代码来演示这个问题。

import soundfile
import numpy
import scipy

def butter_highpass_sosfilt(data, lowcut, fs, order=2):
    return scipy.signal.sosfilt(*scipy.signal.butter(order, [lowcut], fs=fs, btype='high', output='sos'), data)

def butter_highpass_filtfilt(data, lowcut, fs, order=1):
    return scipy.signal.filtfilt(*scipy.signal.butter(order, [lowcut], fs=fs, btype='high', output='ba'), data)

audio, samplerate = soundfile.read("Test Audio.wav") #This is 24bit 44100 PCM

output1 = numpy.zeros((numpy.size(audio[:,0]),2))
output1[:,0] = butter_highpass_filtfilt(audio[:,0], 160, samplerate, order=2)
output1[:,1] = butter_highpass_filtfilt(audio[:,1], 160, samplerate, order=2)
soundfile.write("test output 1.wav", output1, samplerate, 'PCM_24')

output2 = numpy.zeros((numpy.size(audio[:,0]),2))
output2[:,0] = butter_highpass_filtfilt(audio[:,0], 160, samplerate, order=4)
output2[:,1] = butter_highpass_filtfilt(audio[:,1], 160, samplerate, order=4)
soundfile.write("test output 2.wav", output2, samplerate, 'PCM_24')

output3 = numpy.zeros((numpy.size(audio[:,0]),2))
output3[:,0] = butter_highpass_sosfilt(audio[:,0], 160, samplerate, order=2)
output3[:,1] = butter_highpass_sosfilt(audio[:,1], 160, samplerate, order=2)
soundfile.write("test output 3.wav", output3, samplerate, 'PCM_24')

output4 = numpy.zeros((numpy.size(audio[:,0]),2))
output4[:,0] = butter_highpass_sosfilt(audio[:,0], 160, samplerate, order=4)
output4[:,1] = butter_highpass_sosfilt(audio[:,1], 160, samplerate, order=4)
soundfile.write("test output 4.wav", output4, samplerate, 'PCM_24')

字符串
Output1、output2和output3都写入wav文件并按预期播放。程序在写入output4之前停止,并出现以下错误:

Traceback (most recent call last):
  File "M:\Documents\Python\DSP try\Sosfilt error mre\Sosfilt error mre.py", line 29, in <module>
    output4[:,0] = butter_highpass_sosfilt(audio[:,0], 160, int(samplerate/3), order=4)
  File "M:\Documents\Python\DSP try\Sosfilt error mre\Sosfilt error mre.py", line 6, in butter_highpass_sosfilt
    return scipy.signal.sosfilt(*scipy.signal.butter(order, [lowcut], fs=fs, btype='high', output='sos'), data)
  File "C:\Users\edwar\AppData\Local\Programs\Python\Python311\Lib\site-packages\scipy\signal\_signaltools.py", line 4277, in sosfilt
    x_zi_shape[axis] = 2
TypeError: only integer scalar arrays can be converted to a scalar index

vqlkdk9b

vqlkdk9b1#

对于阶数4,scipy.signal.butter返回形状为(2,6)的数组。
这是一个长度为 * 2 * 的序列。
当你用*操作符将序列 * 解压缩到一个函数调用中时,数组的两个元素(数组行)将分散在调用的第一个 * 和第二个 * 参数上。
然后所有进一步的参数都“向下移动一个”。* 这就是问题所在。此处不应使用用于解包的*。*
您的data参数在解包过程中被沿着,然后替换sosfilt()axis参数。
axis参数需要一个整数,而不是data
这个函数似乎感觉到了这个参数的“麻木”,并试图理解它。它可能是一个 * 零 * 维np.ndarray?如果是的话,这是可以接受的。但它不是,它有尺寸。错误消息如下:
TypeError: only integer scalar arrays can be converted to a scalar index
这里的关键词是“scalar arrays”。“整数”在这里是次要的。我们还没查到那一步。

np.array(42).shape == ()

字符串
这是一个标量数组如果您为axis参数传递该值,则函数会很高兴(直到它看到42,这是一个令人震惊的轴数)。
如果你传递np.array([42]),它会抛出同样的错误消息,因为它有维度,也就是说,一个维度的大小为1。形状为(1,)
axis参数的位置传递的值,即data,* 肯定 * 有维数。
这一切都是因为无害的解包*将一个序列变成了两个参数。
我不得不写这个答案,因为另一个答案的解释是错误的。是的,修正是正确的(我建议*运算符是问题所在),但这并不能使 * 解释 * 正确。
不涉及元组。不存在“单一价值”。解包对任何类型的序列都有效。numpy数组是序列。butter()返回numpy数组。是的,它是一个单一的对象,但在这里,它也是一个序列,解包操作符喜欢序列。这部分没有失败。

  • 确实出错 * 的部分是参数到参数的Map,这是由于解包。
8iwquhpp

8iwquhpp2#

正如Christoph正确指出的那样,问题出在*a_tuple符号上。
该函数返回单个值,而不是元组,这会破坏代码。

scipy.signal.butter(order, [lowcut], fs=fs, btype='high', output='sos')

字符串
更正版本:

def butter_highpass_sosfilt(data, lowcut, fs, order=2):
    sos = scipy.signal.butter(order, [lowcut], fs=fs, btype='high', output='sos')
    return scipy.signal.sosfilt(sos, data)

相关问题