OpenCV 4.13.0
开源计算机视觉库 (Open Source Computer Vision)
正在加载...
正在搜索...
未找到匹配项
主成分分析 (PCA) 简介

上一篇教程: 非线性可分数据的支持向量机

原作者Theodore Tsesmelis
兼容性OpenCV >= 3.0

目标

在本教程中,你将学习如何:

  • 使用 OpenCV 类 cv::PCA 来计算物体的方向。

什么是 PCA?

主成分分析 (PCA) 是一种提取数据集最重要特征的统计程序。

假设你有一组如上图所示的 2D 点。每个维度对应你感兴趣的一个特征。在这里,有人可能会认为这些点是随机排列的。然而,如果你仔细观察,你会发现一个难以忽视的线性模式(由蓝色直线指示)。PCA 的一个关键点是降维。降维是减少给定数据集维度数量的过程。例如,在上述情况下,可以将点集近似为一条直线,从而将给定点的维度从 2D 降至 1D。

此外,你还可以看到,点在蓝色直线方向上的变化最大,超过了它们沿特征 1 或特征 2 轴的变化。这意味着,如果你知道一个点在蓝色直线上的位置,你得到的信息要比仅知道它在特征 1 轴或特征 2 轴上的位置更多。

因此,PCA 允许我们找到数据变化最大的方向。事实上,对图表中的点集运行 PCA 的结果包含 2 个向量,称为特征向量 (eigenvectors),它们是数据集的主成分 (principal components)

每个特征向量的大小编码在相应的特征值 (eigenvalue) 中,并指示数据沿该主成分变化的幅度。特征向量的起点是数据集中所有点的中心。将 PCA 应用于 N 维数据集会产生 N 个 N 维特征向量、N 个特征值和 1 个 N 维中心点。理论讲得够多了,让我们看看如何将这些想法转化为代码。

特征向量和特征值是如何计算的?

目标是将维度为 p 的给定数据集 X 转换为较小维度 L 的替代数据集 Y。等价地,我们寻求找到矩阵 Y,其中 Y 是矩阵 XKarhunen–Loève 变换 (KLT)

\[ \mathbf{Y} = \mathbb{K} \mathbb{L} \mathbb{T} \{\mathbf{X}\} \ ]

整理数据集

假设你的数据包含一组对 p 个变量的观测值,并且你想减少数据,以便每个观测值仅用 L 个变量来描述,且 L < p。进一步假设,数据被排列为一组 n 个数据向量 \( x_1...x_n \),每个 \( x_i \) 代表对这 p 个变量的一次分组观测。

  • 将 \( x_1...x_n \) 写成行向量,每个向量有 p 列。
  • 将行向量放入一个维度为 \( n\times p \) 的单一矩阵 X 中。

计算经验均值

  • 计算每个维度 \( j = 1, ..., p \) 的经验均值。
  • 将计算出的均值放入一个维度为 \( p\times 1 \) 的经验均值向量 u 中。

    \[ \mathbf{u[j]} = \frac{1}{n}\sum_{i=1}^{n}\mathbf{X[i,j]} \ ]

计算与均值的偏差

减去均值是寻找能够最小化近似数据均方误差的主成分基底的解法中不可或缺的一部分。因此,我们通过如下方式对数据进行中心化

  • 从数据矩阵 X 的每一行中减去经验均值向量 u
  • 将减去均值后的数据存储在 \( n\times p \) 矩阵 B 中。

    \[ \mathbf{B} = \mathbf{X} - \mathbf{h}\mathbf{u^{T}} \ ]

    其中 h 是一个全为 1 的 \( n\times 1 \) 列向量

    \[ h[i] = 1, i = 1, ..., n \ ]

寻找协方差矩阵

  • 从矩阵 B 与其自身的外部乘积中找到 \( p\times p \) 经验协方差矩阵 C

    \[ \mathbf{C} = \frac{1}{n-1} \mathbf{B^{*}} \cdot \mathbf{B} \ ]

    其中 * 是共轭转置算子。注意,如果 B 完全由实数组成(许多应用中都是如此),则“共轭转置”与常规转置相同。

寻找协方差矩阵的特征向量和特征值

  • 计算使协方差矩阵 C 对角化的特征向量矩阵 V

    \[ \mathbf{V^{-1}} \mathbf{C} \mathbf{V} = \mathbf{D} \ ]

    其中 DC 的特征值对角矩阵。

  • 矩阵 D 将采用 \( p \times p \) 对角矩阵的形式

    \[ D[k,l] = \left\{\begin{matrix} \lambda_k, k = l \\ 0, k \neq l \end{matrix}\right. \ ]

    这里,\( \lambda_j \) 是协方差矩阵 C 的第 j 个特征值

  • 矩阵 V 的维度也是 p x p,包含 p 个列向量,每个向量长度为 p,代表协方差矩阵 Cp 个特征向量。
  • 特征值和特征向量是有序且成对的。第 j 个特征值对应第 j 个特征向量。
注意
来源 [1], [2] 特别感谢 Svetlin Penkov 提供的原始教程。

源代码

注意
另一个使用 PCA 进行降维同时保持方差量的示例可以在此处找到:opencv_source_code/samples/cpp/pca.cpp

说明

  • 读取图像并转换为二值图

在这里,我们应用必要的预处理程序,以便能够检测感兴趣的物体。

  • 提取感兴趣的物体

然后按大小查找并过滤轮廓,并获取剩余轮廓的方向。

  • 提取方向

方向是通过调用 getOrientation() 函数提取的,该函数执行所有的 PCA 程序。

首先需要将数据排列成 n x 2 的矩阵,其中 n 是我们的数据点数量。然后我们可以执行 PCA 分析。计算出的均值(即质心)存储在 cntr 变量中,特征向量和特征值存储在相应的 std::vector 中。

  • 可视化结果

最终结果通过 drawAxis() 函数可视化,其中主成分以直线绘制,每个特征向量乘以其特征值并平移到均值位置。

结果

代码打开一张图像,找到检测到的感兴趣物体的方向,然后通过绘制检测到的物体的轮廓、中心点以及基于提取方向的 x 轴和 y 轴来可视化结果。