ali*_*i_m 24 numpy fft image-processing scipy spectrum
我有一些数据由一系列视频帧组成,这些视频帧表示相对于移动基线的亮度随时间的变化.在这些视频中,可能会出现两种"事件" - "局部"事件,其中包括小组聚集像素中的亮度变化,以及影响帧中大多数像素的污染"漫反射"事件:
我希望能够从漫反射事件中隔离亮度的局部变化.我打算通过减去每帧的适当低通滤波版本来做到这一点.为了设计一个最佳滤波器,我想知道我的帧的哪些空间频率在漫射和局部事件期间被调制,即我想生成我的电影随时间变化的频谱图.
我可以找到很多关于生成一维数据(例如音频)光谱图的信息,但是我没有太多关于生成二维数据的光谱图.到目前为止我所尝试的是从帧的傅立叶变换生成2D功率谱,然后对DC分量执行极坐标变换,然后跨角度平均以获得1D功率谱:
然后我将它应用于我的电影中的每一帧,并生成随时间变化的光谱功率的光栅图:
这看起来像是一种明智的做法吗?是否有更"标准"的方法对2D数据进行光谱分析?
这是我的代码:
import numpy as np
# from pyfftw.interfaces.scipy_fftpack import fft2, fftshift, fftfreq
from scipy.fftpack import fft2, fftshift, fftfreq
from matplotlib import pyplot as pp
from matplotlib.colors import LogNorm
from scipy.signal import windows
from scipy.ndimage.interpolation import map_coordinates
def compute_2d_psd(img, doplot=True, winfun=windows.hamming, winfunargs={}):
nr, nc = img.shape
win = make2DWindow((nr, nc), winfun, **winfunargs)
f2 = fftshift(fft2(img*win))
psd = np.abs(f2*f2)
pol_psd = polar_transform(psd, centre=(nr//2, nc//2))
mpow = np.nanmean(pol_psd, 0)
stdpow = np.nanstd(pol_psd, 0)
freq_r = fftshift(fftfreq(nr))
freq_c = fftshift(fftfreq(nc))
pos_freq = np.linspace(0, np.hypot(freq_r[-1], freq_c[-1]),
pol_psd.shape[1])
if doplot:
fig,ax = pp.subplots(2,2)
im0 = ax[0,0].imshow(img*win, cmap=pp.cm.gray)
ax[0,0].set_axis_off()
ax[0,0].set_title('Windowed image')
lnorm = LogNorm(vmin=psd.min(), vmax=psd.max())
ax[0,1].set_axis_bgcolor('k')
im1 = ax[0,1].imshow(psd, extent=(freq_c[0], freq_c[-1],
freq_r[0], freq_r[-1]), aspect='auto',
cmap=pp.cm.hot, norm=lnorm)
# cb1 = pp.colorbar(im1, ax=ax[0,1], use_gridspec=True)
# cb1.set_label('Power (A.U.)')
ax[0,1].set_title('2D power spectrum')
ax[1,0].set_axis_bgcolor('k')
im2 = ax[1,0].imshow(pol_psd, cmap=pp.cm.hot, norm=lnorm,
extent=(pos_freq[0],pos_freq[-1],0,360),
aspect='auto')
ax[1,0].set_ylabel('Angle (deg)')
ax[1,0].set_xlabel('Frequency (cycles/px)')
# cb2 = pp.colorbar(im2, ax=(ax[0,1],ax[1,1]), use_gridspec=True)
# cb2.set_label('Power (A.U.)')
ax[1,0].set_title('Polar-transformed power spectrum')
ax[1,1].hold(True)
# ax[1,1].fill_between(pos_freq, mpow - stdpow, mpow + stdpow,
# color='r', alpha=0.3)
ax[1,1].axvline(0, c='k', ls='--', alpha=0.3)
ax[1,1].plot(pos_freq, mpow, lw=3, c='r')
ax[1,1].set_xlabel('Frequency (cycles/px)')
ax[1,1].set_ylabel('Power (A.U.)')
ax[1,1].set_yscale('log')
ax[1,1].set_xlim(-0.05, None)
ax[1,1].set_title('1D power spectrum')
fig.tight_layout()
return mpow, stdpow, pos_freq
def make2DWindow(shape,winfunc,*args,**kwargs):
assert callable(winfunc)
r,c = shape
rvec = winfunc(r,*args,**kwargs)
cvec = winfunc(c,*args,**kwargs)
return np.outer(rvec,cvec)
def polar_transform(image, centre=(0,0), n_angles=None, n_radii=None):
"""
Polar transformation of an image about the specified centre coordinate
"""
shape = image.shape
if n_angles is None:
n_angles = shape[0]
if n_radii is None:
n_radii = shape[1]
theta = -np.linspace(0, 2*np.pi, n_angles, endpoint=False).reshape(-1,1)
d = np.hypot(shape[0]-centre[0], shape[1]-centre[1])
radius = np.linspace(0, d, n_radii).reshape(1,-1)
x = radius * np.sin(theta) + centre[0]
y = radius * np.cos(theta) + centre[1]
# nb: map_coordinates can give crazy negative values using higher order
# interpolation, which introduce nans when you take the log later on
output = map_coordinates(image, [x, y], order=1, cval=np.nan,
prefilter=True)
return output
Run Code Online (Sandbox Code Playgroud)
小智 2
我相信您描述的方法通常是进行此分析的最佳方法。
但是,我确实在您的代码中发现了一个错误。作为:
np.abs(f2*f2)
Run Code Online (Sandbox Code Playgroud)
不是复数数组 f2 的 PSD,您需要将 f2 乘以它的复共轭而不是其本身(|f2^2| 与 |f2|^2 不同)。
相反,你应该做类似的事情
(f2*np.conjugate(f2)).astype(float)
Run Code Online (Sandbox Code Playgroud)
或者,更干净地说:
np.abs(f2)**2.
Run Code Online (Sandbox Code Playgroud)
二维功率谱中的振荡是此类错误的明显标志(我自己之前就做过这样的事情!)