从一组电影帧中删除正弦伪影

信息处理 傅里叶变换 图片 Python 阶段 去噪
2022-01-15 04:12:19

我正在对由一系列被强周期性伪影污染的电影帧组成的数据集进行一些事后分析。我想从我的框架中删除这个工件。

为了便于绘图,我刚刚将M像素值数组重新整形为[nframes, npixels],然后对所有像素值进行平均,得到一个 1D 向量m这是这个信号在时域中的样子。您可以在放大的插图中非常清楚地看到振荡。

在此处输入图像描述

然后我制作了一个周期图,并根据频率Fm = rfft(m)绘制。abs(Fm)**2我在 ~1.5Hz 处看到一个非常尖锐的峰值:

在此处输入图像描述

除了时间周期性之外,这个伪影似乎还有一个较弱的空间分量,因为在精确的峰值频率值处,我的帧的 x 轴上的相位似乎有一个平滑的变化,因此像素右边倾向于滞后左边的像素:

在此处输入图像描述

作为一种蛮力方法,我尝试使用以 1.5Hz 为中心的陷波滤波器过滤时域中的每个像素。我使用了一个临界频率为 1.46 和 1.52Hz 的 4 阶巴特沃斯滤波器(我并不精通滤波器设计,所以我确信可能会有更合适的选择)。

这是过滤后平均像素信号的样子: 在此处输入图像描述

以及相应的周期图: 在此处输入图像描述

陷波滤波器在减少伪影方面做得相当好,但由于它基本上看起来像一个纯正弦曲线,我不禁认为我可以做得比仅仅衰减频率空间的那部分更好。

我最初(非常天真)的想法是做类似的事情:

  1. 从电影中每个像素的傅里叶频谱中获取振荡的频率、相位和幅度
  2. 重建时域中的振荡
  3. 从电影帧中减去它

我意识到这不是人们通常会做的事情,因为干扰通常不是那么光谱纯净和时间静止,但我想知道这对我来说是否有意义?

数据

完整的 16 位 TIFF 堆栈(约 2GB 未压缩)

空间抽取 8 位版本(约 35MB 未压缩)

1个回答

您提出的解决方案 - 根据 FFT 中的峰值计算时域中的正弦曲线,然后减去它 - 应该可以工作,但有一种更简单的方法可以做本质上相同的事情:修改 FFT 中的峰值,然后取反转变。

因此,对于您的光栅化视频,M[nframes, npixels]您会找到包含伪影的频率箱,然后系统地将其展平(例如,将其幅度设置为其相邻像素的平均值):

import numpy as np
nframes, npixels = np.shape(M)
# Identify the bin containing the sinusoidal artifact
# Use the average intensity for each image
m = np.mean(M, axis=1)
# Calculate the FFT
Fm = np.fft.rfft(m)
# Find the largest bin away from the low-frequency region
lowfreq = 100  # or something
badbin = lowfreq + np.argmax(Fm[lowfreq:]**2)

# Now adjust the amplitude of that bin in the FFT of each pixel
for pixel in range(npixels):
   Fpix = np.fft.rfft(M[:, pixel])
   # Scale magnitude of artifact bin to be the mean of its neighbors
   Fpix[badbin] *= np.mean(np.absolute(Fpix[[badbin-1, badbin+1]]))/np.absolute(Fpix[badbin])
   # Rewrite the time sequence of that pixel
   M[:,pixel] = np.fft.irfft(Fpix)

如果伪影是完全恒定的幅度和频率,并且它的频率正好落在序列长度的约数上(即,由 FFT 表示的正弦曲线),这应该可以工作。通常,您可能希望将两侧的一个或两个 bin 展平,badbin以处理一组稍宽的窄带损坏,例如

# ...

   # Scale magnitude of artifact binS to be the mean of neighbors
   spread = 3  # flatten bins from (badbin - (spread-1)) to (badbin + (spread-1))
   # target value for new bins
   targetmag = np.mean(np.absolute(Fpix[[badbin-spread, badbin+spread]]))
   bins = range(badbin - (spread-1), badbin + spread)
   Fpix[bins] *= targetmag/np.abs(Fpix[bins])
   # ...

如果您想约束从每个像素中移除的分量,使其具有在平均强度中检测到的伪影的相同频率和相位,您可以仅移除badbin幅度在该相位上的投影,例如

badbinphase = np.angle(Fm[badbin])
# ...

   Ncomponent = np.abs(Fpix[badbin])*np.cos(np.angle(Fpix[badbin]) - badbinphase)
   Fpix[badbin] -= Ncomponent * np.exp(0+1j * badbinphase)
   # ...

请注意,生成的分量badbin现在总是与badbinphase每个像素中的全局相移 90°(正交) - 任何恰好处于该频率和相位的信号分量都无法与伪影分离。