OpenCV 4.11.0
开源计算机视觉库
加载中…
搜索中…
无匹配项
主成分分析 (PCA) 入门

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

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

目标

在本教程中,您将学习如何

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

什么是 PCA?

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

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

此外,您还可以看到,沿着蓝线方向,点的变化比沿着特征 1 或特征 2 轴方向的变化更大。这意味着,如果您知道点沿蓝线的位移,那么您比只知道它在特征 1 轴或特征 2 轴上的位置时,拥有更多关于该点的信息。

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

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

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

目标是将给定维度为 *p* 的数据集 **X** 变换为另一个维度较小的数据集 **Y**,维度为 *L*。等效地,我们正在寻找矩阵 **Y**,其中 **Y** 是矩阵 **X** 的 *Karhunen–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** 是一个 \( n\times 1 \) 的全为 1 的列向量

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

找到协方差矩阵

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

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

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

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

  • 计算特征向量矩阵 **V**,它对协方差矩阵 **C** 进行对角化

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

    其中 **D** 是 **C** 的特征值的对角矩阵。

  • 矩阵 **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*,它们代表协方差矩阵 **C** 的 *p* 个特征向量。
  • 特征值和特征向量是有序且成对的。第 *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轴来可视化结果。