OpenCV 例程200篇232. 特征描述之频谱方法
Posted 小白YouCans
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV 例程200篇232. 特征描述之频谱方法相关的知识,希望对你有一定的参考价值。
『youcans 的 OpenCV 例程200篇 - 总目录』
【youcans 的 OpenCV 例程200篇】232. 纹理特征之频谱方法
4.3 纹理特征之频谱方法
傅里叶谱可以描述图像中的周期性或半周期性二维模式的方向性,因此可以基于傅里叶变换对纹理进行频谱分析。
纹理与图像频谱中的高频分量密切相关,纹理模式在频谱图表现为高能量的爆发。
傅里叶谱具有三个特征可以描述纹理的性质:
(1)傅里叶频谱中的突出峰值对应于纹理模式的主方向;
(2)频域图中的峰值位置对应于纹理模式在空间上的基本周期;
(3)通过滤波消除周期分量,用统计方法描述剩下的非周期性信号。
把傅里叶幅度谱转换到极坐标中表示为函数 S ( r , θ ) S(r,\\theta) S(r,θ),可以简化对频谱特性的解释。S 是频谱函数,r 和 θ \\theta θ 是极坐标系的半径和角度坐标轴。S 对于每一个方向 θ \\theta θ 可以简化为一维函数 S θ ( r ) S_\\theta(r) Sθ(r);S 对于每一个半径 r 也可以简化为一维函数 S r ( θ ) S_r(\\theta) Sr(θ)。
分别对一维函数
S
θ
(
r
)
S_\\theta(r)
Sθ(r) 和
S
r
(
θ
)
S_r(\\theta)
Sr(θ) 积分,可以获得纹理频谱的全局描述:
S
(
r
)
=
∑
θ
=
0
π
S
θ
(
r
)
S
(
θ
)
=
∑
r
=
1
R
S
r
(
θ
)
S(r) = \\sum_\\theta=0^\\pi S_\\theta(r) \\\\ S(\\theta) = \\sum_r=1^R S_r (\\theta)
S(r)=θ=0∑πSθ(r)S(θ)=r=1∑RSr(θ)
如果纹理具有空间的周期性或确定的方向性,则一维函数
S
(
r
)
S(r)
S(r) 和
S
(
θ
)
S(\\theta)
S(θ) 在对应的频率具有峰值。
例程 14.13:特征描述之傅里叶频谱纹理分析
本例选自冈萨雷斯《数字图像处理(第四版)》P617 例11.14。一幅包含随机分布目标的图像和另一幅周期性排列目标的图像,在傅里叶变换的幅度谱具有不同的表现形式。
对于随机摆放和整齐摆放的物体图像,使用傅里叶频谱可以显示不同的图像,但直接分析频谱图像比较困难。将其转换到极坐标系中,用一维函数 S ( r ) S(r) S(r) 和 S ( θ ) S(\\theta) S(θ) 表示就非常直观。
对于随机摆放的火柴图像,函数 S ( r ) S(r) S(r) 曲线没有很强的周期分量;而对于整齐摆放的火柴图像,函数 S ( r ) S(r) S(r) 曲线在r=15、r=25 附近具有很强的峰值,对应于频谱图中的亮区域的周期性水平重复。类似地,随机摆放火柴图像的 S r ( θ ) S_r(\\theta) Sr(θ) 曲线的分布混乱没有显著的峰值,而在整齐摆放的火柴图像中, S r ( θ ) S_r(\\theta) Sr(θ) 曲线在 θ = 9 0 o \\theta = 90^o θ=90o 具有非常强烈的能量峰值,对应于频谱图中的水平方向的能量爆发。
# 14.13 特征描述之纹理谱分析
def halfcircle(radius, x0, y0): # 计算圆心(x0,y0) 半径为 r 的半圆的整数坐标
degree = np.arange(180, 360, 1) # 因对称性可以用半圆 (180,)
theta = np.float32(degree * np.pi / 180) # 弧度,一维数组 (180,)
xc = (x0 + radius * np.cos(theta)).astype(np.int) # 计算直角坐标,整数
yc = (y0 + radius * np.sin(theta)).astype(np.int)
return xc, yc
def intline(x1, x2, y1, y2): # 计算从(x1,y1)到(x2,y2)的线段上所有点的坐标
dx, dy = np.abs(x2-x1), np.abs(y2-y1) # x, y 的增量
if dx==0 and dy==0:
x, y = np.array([x1]), np.array([y1])
return x, y
if dx > dy:
if x1>x2:
x1, x2 = x2, x1
y1, y2 = y2, y1
m = (y2-y1) / (x2-x1)
x = np.arange(x1, x2+1, 1) #[x1,x2]
y = (y1 + m*(x-x1)).astype(np.int)
else:
if y1>y2:
x1, x2 = x2, x1
y1, y2 = y2, y1
m = (x2-x1) / (y2-y1)
y = np.arange(y1, y2+1, 1) # [y1,y2]
x = (x1 + m*(y-y1)).astype(np.int)
return x, y
def specxture(gray):
# cv2.dft 实现图像的傅里叶变换
height, width = gray.shape
x0, y0 = int(height / 2), int(width / 2) # x0=300, y0=300
rmax = min(height, width) // 2 - 1 # rmax=299
print(height, width, x0, y0, rmax)
# FFT 变换 (youcans)
gray32 = np.float32(gray) # 将图像转换成 float32
dft = cv2.dft(gray32, flags=cv2.DFT_COMPLEX_OUTPUT) # 傅里叶变换,(600, 600, 2)
dftShift = np.fft.fftshift(dft) # 将低频分量移动到频域图像的中心
sAmp = cv2.magnitude(dftShift[:, :, 0], dftShift[:, :, 1]) # 幅度谱,中心化 (600, 600)
sAmpLog = np.log10(1 + np.abs(sAmp)) # 幅度谱对数变换 (600, 600)
# FFT 频谱沿半径的分布函数
sRad = np.zeros((rmax,)) # (299,)
sRad[0] = sAmp[x0, y0]
for r in range(1, rmax):
xc, yc = halfcircle(r, x0, y0) # 半径为 r 的圆的整数坐标 (360,)
sRad[r] = sum(sAmp[xc[i], yc[i]] for i in range(xc.shape[0])) # (360,)
sRadLog = np.log10(1 + np.abs(sRad)) # 极坐标幅度谱 youcans 对数变换
# FFT 频谱沿角度的分布函数
xmax, ymax = halfcircle(rmax, x0, y0) # 半径为 xupt 的圆的整数坐标 (360,)
sAng = np.zeros((xmax.shape[0],)) # (360,)
for a in range(xmax.shape[0]): # xmax.shape[0]=(360,)
xr, yr = intline(x0, xmax[a], y0, ymax[a]) # 从(x0,y0)到(xa,ya)线段所有点的坐标 (300,)
sAng[a] = sum(sAmp[xr[i], yr[i]] for i in range(xr.shape[0])) # (360,)
return sAmpLog, sRadLog, sAng
# 纹理的傅里叶频谱分析
gray1 = cv2.imread("../images/Fig1135a.tif", flags=0) # flags=0 读取为灰度图像
gray2 = cv2.imread("../images/Fig1135b.tif", flags=0)
sAmpLog1, sRadLog1, sAng1 = specxture(gray1) # 图像纹理的频谱分析
sAmpLog2, sRadLog2, sAng2 = specxture(gray2)
print(sAmpLog1.shape, sRadLog1.shape, sAng1.shape)
plt.figure(figsize=(9, 6))
plt.subplot(241), plt.axis('off'), plt.title("Random matches"), plt.imshow(gray1, 'gray')
plt.subplot(242), plt.axis('off'), plt.title("Amp spectrum"), plt.imshow(sAmpLog1, 'gray')
plt.subplot(243), plt.axis('off'), plt.title("Arranged matches"), plt.imshow(gray2, 'gray')
plt.subplot(244), plt.axis('off'), plt.title("Amp spectrum"), plt.imshow(sAmpLog2, 'gray')
plt.subplot(245), plt.plot(sRadLog1), plt.title("S1 (radius)"), plt.xlim(0,300), plt.yticks([])
plt.subplot(246), plt.plot(sAng1), plt.title("S1 (theta)"), plt.xlim(0,180), plt.yticks([])
plt.subplot(247), plt.plot(sRadLog2), plt.title("S2 (radius)"), plt.xlim(0,300), plt.yticks([])
plt.subplot(248), plt.plot(sAng2), plt.title("S2 (theta)"), plt.xlim(0,180), plt.yticks([])
plt.tight_layout()
plt.show()
【本节完】
版权声明:
youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/125704655)
Copyright 2022 youcans, XUPT
Crated:2022-7-10
以上是关于OpenCV 例程200篇232. 特征描述之频谱方法的主要内容,如果未能解决你的问题,请参考以下文章
OpenCV 例程200篇236. 特征提取之主成分分析(OpenCV)
OpenCV 例程200篇236. 特征提取之主成分分析(OpenCV)
OpenCV 例程200篇227. 特征描述之 LBP 纹理特征算子
OpenCV 例程200篇227. 特征描述之 LBP 纹理特征算子