OpenCV 例程200篇237. 基于主成分提取的方向校正(OpenCV)

Posted YouCans

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV 例程200篇237. 基于主成分提取的方向校正(OpenCV)相关的知识,希望对你有一定的参考价值。

『youcans 的 OpenCV 例程200篇 - 总目录』

【youcans 的 OpenCV 例程200篇】237. 基于主成分提取的方向校正(OpenCV)


主成分分析(Principal Components Analysis,PCA)是一种基于统计的数据降维方法,又称主元素分析、主分量分析。主成分分析只需要特征值分解,就可以对数据进行压缩、去噪,应用非常广泛。

主成分分析方法得到的主成分变量具有几个特点:(1)每个主成分变量都是原始变量的线性组合;(2)主成分的数目大大少于原始变量的数目;(3)主成分保留了原始变量的绝大多数信息;(4)各主成分变量之间彼此相互独立。

5.4 OpenCV 的主成分分析方法

OpenCV 中提供了主成分分析(Principal Components Analysis,PCA)方法的实现,即 cv::PCA 类。类的声明在 include/opencv2/core.hpp 文件中,类的实现在 modules/core/src/pca.cpp 文件中。

  • 成员函数:
    • PCA::PCA:默认构造并初始化一个空的 PCA 结构
    • PCA::backproject:将数据从 PCA 空间投影回原始空间,重建原始数据
    • PCA::operator():对提供的数据执行主成分分析操作
    • PCA::project:将输入数据投影到 PCA 特征空间;
    • PCA::read:从指定文件读入特征值、特征向量和均值;
    • PCA::write:向指定文件写入特征值、特征向量和均值;
  • 属性:
    • PCA::eigenvalues:协方差矩阵的特征值
    • PCA::eigenvectors:协方差矩阵的特征向量
    • PCA::mean:均值,投影前减去均值,投影后加上均值

PCA 类使用 Karhunen-Loeve 变换,由协方差矩阵的特征向量计算得到一组向量的正交基。

在 Python 语言中,OpenCV 提供了 PCA 类的接口函数 cv.PCACompute()cv.PCAProject()cv.PCABackProject()

函数说明:

cv.PCACompute(data, mean[, eigenvectors=None, maxComponents=0]) → mean, eigenvectors
cv.PCACompute(data, mean, retainedVariance[, eigenvectors=None]) → mean, eigenvectors
cv.PCACompute2(data, mean[, eigenvectors=None, eigenvalues=None, maxComponents=0]) → mean, eigenvectors, eigenvalues
cv.PCACompute2(data, mean, retainedVariance[, eigenvectors=None, eigenvalues=None]) → mean, eigenvectors, eigenvalues

cv.PCAProject(data, mean, eigenvectors[, result=None]) → result
cv.PCABackProject(data, mean, eigenvectors[, result=None]) → result

函数 cv.PCACompute 是 PCA::operator 的接口,用于对提供的数据执行主成分分析操作,返回均值、特征向量和特征值。

函数 **cv.PCAProject ** 是 PCA::project 的接口,用于将输入数据按选择的特征向量投影到 PCA 特征空间。

函数 cv.PCABackProject 是 PCA::backproject 的接口,用于将输入数据按选择的特征向量投影从 PCA 空间投影回原始空间,重建原始数据。

参数说明:

  • data:输入数据矩阵,对于 cv.PCACompute 和 PCAProject 是 m×P 原始数据矩阵,对于 PCABackProject 是 m×K 降维数据矩阵 ( K ≤ P ) (K \\le P) (KP)

  • mean:均值,形状为 (1,P),如果该参数的输入为空,则通过输入数据计算均值

  • maxComponents:保留主成分的个数,默认为保留全部主成份

  • retainedVariance:保留的累计方差的百分比,据此确定保留主成分的个数(至少保留 2个主成分)

  • eigenvectors:特征向量,全部特征向量的形状为 (P,P),前 K 个特征向量的形状为 (K,P)

  • eigenvalues:特征值,全部特征向量的形状为 (P,1),前 K 个特征向量的形状为 (K,1)


例程 14.18:特征描述之PCA 方向校正

特征描述应尽可能独立于大小、平移和旋转的变化。使用主成分分析方法,可以对目标进行归一化边界/区域,获得目标的主要方向。

本例对于二维图像,通过 PCA 方法对目标进行方向校正。

(1)通过主成分分析(PCA)获得目标的主方向,将数据投影到主方向及其垂直方向上。

(2)计算目标的均值,第一主方向的角度。

(3) 以目标的均值作为质心(旋转中心),按主方向角度旋转原始图像,得到归一化的目标图像。

    # 14.18 基于 PCA 的方向矫正 (OpenCV)
    img = cv2.imread("../images/airplane01.png", flags=1)
    height, width = img.shape[:2]  # 512 512
    print(height, width)
    # src = cv2.resize(img, (300, 300))
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    _, binary = cv2.threshold(gray, 205, 255, cv2.THRESH_BINARY_INV)

    # 寻找二值化图中的轮廓,检索所有轮廓,输出轮廓的每个像素点
    contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)  # OpenCV4~
    fullCnts = np.zeros(img.shape[:2], np.uint8)  # 绘制轮廓函数会修改原始图像
    fullCnts = cv2.drawContours(fullCnts, contours, -1, (255, 255, 255), thickness=3)  # 绘制全部轮廓

    # 按轮廓的面积排序,绘制面积最大的轮廓
    cnts = sorted(contours, key=cv2.contourArea, reverse=True)  # 所有轮廓按面积排序
    cnt = cnts[0]  # 第 0 个轮廓,面积最大的轮廓,(1445, 1, 2)
    maxCnt = np.zeros(img.shape[:2], np.uint8)  # 初始化最大轮廓图像
    cv2.drawContours(maxCnt, cnts[0], -1, (255, 255, 255), thickness=3)  # 仅绘制最大轮廓 cnt
    print("len(contours) =", len(contours))  # contours 所有轮廓的数量
    print("area of max contour: ", cv2.contourArea(cnt))  # 轮廓面积
    print("perimeter of max contour: :.1f".format(cv2.arcLength(cnt, True)))  # 轮廓周长

    # 主成分分析方法提取目标的方向
    markedCnt = maxCnt.copy()
    ptsXY = np.squeeze(cnt).astype(np.float64)  # 删除维度为1的数组维度,(1445, 1, 2)->(1445, 2)
    mean, eigenvectors, eigenvalues = cv2.PCACompute2(ptsXY, np.array([]))  # (1, 2) (2, 2) (2, 1)
    print("mean:, eigenvalues:".format(mean.round(1), eigenvalues[:,0].round(2)))
    # 绘制第一、第二主成分方向轴
    center = mean[0, :].astype(int)  # 近似作为目标的中心 [266 281]
    e1xy = eigenvectors[0,:] * eigenvalues[0,0]  # 第一主方向轴
    e2xy = eigenvectors[1,:] * eigenvalues[1,0]  # 第二主方向轴
    p1 = (center + 0.01*e1xy).astype(np.int)  # P1:[149 403]
    p2 = (center + 0.01*e2xy).astype(np.int)  # P2:[320 332]
    theta = np.arctan2(eigenvectors[0,1], eigenvectors[0,0]) * 180/np.pi  # 第一主方向角度 133.6
    cv2.circle(markedCnt, center, 6, 255, -1)  # 在PCA中心位置画一个圆圈  RGB
    cv2.arrowedLine(markedCnt, center, p1, (255, 0, 0), thickness=3, tipLength=0.1)  # 从 center 指向 pt1
    cv2.arrowedLine(markedCnt, center, p2, (255, 0, 0), thickness=3, tipLength=0.2)  # 从 center 指向 pt2
    print("center:, P1:, P2:".format(center, p1, p2))

    # 根据主方向角度和中心旋转原始图像
    alignCnt = img.copy()
    cv2.circle(alignCnt, center, 8, (255,255,255), 2)  # 在PCA中心位置画一个圆圈  BGR
    cv2.arrowedLine(alignCnt, center, p1, (0,0,255), thickness=3, tipLength=0.1)  # 从 center 指向 pt1
    cv2.arrowedLine(alignCnt, center, p2, (0,255,0), thickness=3, tipLength=0.2)  # 从 center 指向 pt2
    x0, y0 = int(center[0]), int(center[1])
    print("x0=, y0=, theta=:.1f(deg)".format(x0, y0, theta))
    MAR1 = cv2.getRotationMatrix2D((x0,y0), theta, 1)
    alignCnt = cv2.warpAffine(alignCnt, MAR1, alignCnt.shape[:2], borderValue=(255,255,255))  # 白色填充

    # 显示图像
    plt.figure(figsize=(9, 6))
    plt.subplot(231), plt.axis('off'), plt.title("Origin")
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.subplot(232), plt.axis('off'), plt.title("Binary")
    plt.imshow(binary, 'gray')
    plt.subplot(233), plt.axis('off'), plt.title("Contour")
    plt.imshow(fullCnts, 'gray')
    plt.subplot(234), plt.axis('off'), plt.title("Max contour")
    plt.imshow(maxCnt, 'gray')
    plt.subplot(235), plt.axis('off'), plt.title("Marked contour")
    plt.imshow(markedCnt, 'gray')
    plt.subplot(236), plt.axis('off'), plt.title("Alignment image")
    plt.imshow(cv2.cvtColor(alignCnt, cv2.COLOR_BGR2RGB))
    plt.tight_layout()

运行结果:

512 512
len(contours) = 42
area of max contour: 63499.5
perimeter of max contour: 1725.4
mean:[[266.2 281.6]], eigenvalues:[16866.21 7533.31]
center:[266 281], P1:[149 403], P2:[320 332]
x0=266, y0=281, theta=133.6(deg)



【本节完】

版权声明:
youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/125782405)
Copyright 2022 youcans, XUPT
Crated:2022-7-15

234. 特征提取之主成分分析(PCA)
235. 特征提取之主成分分析(sklearn)
236. 特征提取之主成分分析(OpenCV)
237. 基于主成分提取的方向校正(OpenCV.PCA)

以上是关于OpenCV 例程200篇237. 基于主成分提取的方向校正(OpenCV)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

OpenCV 例程300篇234. 特征提取之主成分分析(PCA)

OpenCV 例程300篇234. 特征提取之主成分分析(PCA)