在现实生活中使用 Python 进行反卷积

信息处理 Python 反卷积 scipy
2022-02-01 11:59:02

我测量了一个与测量设备的轮廓卷积的信号。现在我想删除这个贡献以获得“真实”信号。我正在尝试用 Python 来做到这一点。为此,我正在使用该scipy.signal.deconvolve功能。为了了解如何使用它,我从Stack Overflow 中的这个问题开始,特别是Cleb 的回答所以我写了一个玩具脚本来玩。

玩具示例

import numpy as np
import scipy.signal
import plotly.graph_objects as go
from plotly.subplots import make_subplots

x = np.arange(0., 20.01, 0.01)
original = np.zeros(len(x))
original[900:1100] = 1
filter_sigma = .2
filter_from_measuring_apparatus = np.exp(-(x-x.mean())**2/2/filter_sigma**2)
filter_from_measuring_apparatus = filter_from_measuring_apparatus[filter_from_measuring_apparatus>.1]
filter_for_deconv = filter_from_measuring_apparatus

measured = scipy.signal.convolve(original, filter_from_measuring_apparatus, mode='full') / filter_from_measuring_apparatus.sum()
deconvoluted, remainder = scipy.signal.deconvolve(measured, filter_for_deconv)
deconvoluted *= filter_for_deconv.sum()

signals = [original, filter_from_measuring_apparatus, measured, filter_for_deconv, deconvoluted]
labels = ['original', 'filter_from_measuring_apparatus', 'measured', 'filter_for_deconv', 'deconvoluted']
fig = make_subplots(rows = len(signals), shared_xaxes = True, vertical_spacing = 0.02)
for fig_row,(signal, label) in enumerate(zip(signals, labels)):
    fig.add_trace(
        go.Scatter(
            x = [i for i in range(len(signal))],
            y = signal, 
            name = label,
        ),
        row = fig_row+1,
        col = 1,
    )

fig.show()

这个玩具示例的输出如下所示:

在此处输入图像描述

这是我们都期望的,很好。我正在削减高斯filter_from_measuring_apparatus>0.1因为在ImportanceOfBeingErnest 的回答中,他说“过滤器应该比任何地方都大得多”,这是可以理解的,因为在某些时候它会分裂。

现实生活中的测量仪器,但仍然没有噪音

所以现在我想进入现实生活。在现实生活中,测量设备的过滤器可以是任何东西,特别是“不是到处都比零大得多”的高斯。所以我只修改filter_from_measuring_apparatus

import numpy as np
import scipy.signal
import plotly.graph_objects as go
from plotly.subplots import make_subplots

x = np.arange(0., 20.01, 0.01)
original = np.zeros(len(x))
original[900:1100] = 1
filter_sigma = .2
filter_from_measuring_apparatus = np.exp(-(x-x.mean())**2/2/filter_sigma**2)
filter_for_deconv = filter_from_measuring_apparatus[filter_from_measuring_apparatus>.1] # The filter I will use for deconv is still "much bigger than zero".

measured = scipy.signal.convolve(original, filter_from_measuring_apparatus, mode='full') / filter_from_measuring_apparatus.sum()
deconvoluted, remainder = scipy.signal.deconvolve(measured, filter_for_deconv)
deconvoluted *= filter_for_deconv.sum()
deconvoluted[(deconvoluted<-2)|(deconvoluted>2)] = float('NaN') # Remove so we can see in the plot.

signals = [original, filter_from_measuring_apparatus, measured, filter_for_deconv, deconvoluted]
labels = ['original', 'filter_from_measuring_apparatus', 'measured', 'filter_for_deconv', 'deconvoluted']
fig = make_subplots(rows = len(signals), shared_xaxes = True, vertical_spacing = 0.02)
for fig_row,(signal, label) in enumerate(zip(signals, labels)):
    fig.add_trace(
        go.Scatter(
            x = [i for i in range(len(signal))],
            y = signal, 
            name = label,
        ),
        row = fig_row+1,
        col = 1,
    )

fig.show()

现在输出如下所示:

在此处输入图像描述

为什么会这样?我希望输出与前一种情况非常相似,因为measured信号几乎相同(只是更长)。

添加噪声,为测量仪器保留玩具过滤器

现在让我回到使用假设备滤波器的玩具示例,但在测量中添加(非常小的)噪声:

import numpy as np
import scipy.signal
import plotly.graph_objects as go
from plotly.subplots import make_subplots

x = np.arange(0., 20.01, 0.01)
original = np.zeros(len(x))
original[900:1100] = 1
filter_sigma = .2
filter_from_measuring_apparatus = np.exp(-(x-x.mean())**2/2/filter_sigma**2)
filter_from_measuring_apparatus = filter_from_measuring_apparatus[filter_from_measuring_apparatus>.1]
filter_for_deconv = filter_from_measuring_apparatus


measured = scipy.signal.convolve(original, filter_from_measuring_apparatus, mode='full') / filter_from_measuring_apparatus.sum()
measured += 0.001*(np.random.randn(len(measured))**2)**.5 # Add some noise to the measurement, as I am measuring power the noise is only positive.
deconvoluted, remainder = scipy.signal.deconvolve(measured, filter_for_deconv)
deconvoluted *= filter_for_deconv.sum()
deconvoluted[(deconvoluted<-2)|(deconvoluted>2)] = float('NaN') # Remove so we can see in the plot.

signals = [original, filter_from_measuring_apparatus, measured, filter_for_deconv, deconvoluted]
labels = ['original', 'filter_from_measuring_apparatus', 'measured', 'filter_for_deconv', 'deconvoluted']
fig = make_subplots(rows = len(signals), shared_xaxes = True, vertical_spacing = 0.02)
for fig_row,(signal, label) in enumerate(zip(signals, labels)):
    fig.add_trace(
        go.Scatter(
            x = [i for i in range(len(signal))],
            y = signal, 
            name = label,
        ),
        row = fig_row+1,
        col = 1,
    )

fig.show()

产生输出:

在此处输入图像描述

WTF?!?!?! 噪声是信号的1/1000强,在measured信号图中几乎看不到,怎么可能在反卷积后产生如此剧烈的废话?

当然,同时将测量设备的真实过滤器和噪声结合起来只会使结果变差。

所以我的问题是,该deconvolve功能应该如何在现实生活中使用?或者我应该如何在 Python 中做我想做的事情?我将不胜感激解决我的玩具示例的答案。

1个回答

时域的反卷积相当于频域的划分,即我们尝试将输入恢复为

X(ω)=Y(ω)H(ω)

如果有频率非常小,这成为一个定义不明确的问题,因为您正在接近“零除以零”。这就是您在第二个示例中看到的内容。在高频下,滤波器的传递函数非常小,甚至双精度算术的数值噪声也会抛出结果。|H(ω)|

一旦你添加噪音,它会变得更糟。假设我们得到Y(ω)=H(ω)X(ω)+N(ω)

X(ω)=Y(ω)H(ω)=X(ω)+N(ω)H(ω)=X(ω)+N(ω)H(ω)

反卷积通过滤波器传递函数的逆来放大噪声。如果您的传递函数降低 60 dB,您的噪声将被放大 60 dB,这正是您所看到的。高斯是一种非常有效的低通滤波器!

现实生活中的反卷积

..很棘手。如果您的传递函数在足够宽的频率范围内相当平坦,那么时域反卷积就可以工作。但它始终需要仔细检查作为频率函数的信噪比 (SNR),并且可能需要“定制”以解决 SNR 较差的频段。

根本问题是根本性的。如果滤波器衰减输入信号的频带,使其低于本底噪声,则信息将丢失且不可恢复。