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=1RSr(θ)
如果纹理具有空间的周期性或确定的方向性,则一维函数 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

231. 特征描述之 灰度共生矩阵(GLCM)

以上是关于OpenCV 例程200篇232. 特征描述之频谱方法的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV 例程200篇236. 特征提取之主成分分析(OpenCV)

OpenCV 例程200篇236. 特征提取之主成分分析(OpenCV)

OpenCV 例程200篇227. 特征描述之 LBP 纹理特征算子

OpenCV 例程200篇227. 特征描述之 LBP 纹理特征算子

OpenCV 例程200篇228. 特征描述之 extendLBP 改进算子

OpenCV 例程200篇228. 特征描述之 extendLBP 改进算子