1. introduction_to_pca.cpp 主成分分析
/**
* @file introduction_to_pca.cpp
* @brief 这个程序演示了如何使用OpenCV PCA 提取物体的方向
* @author OpenCV团队
*/
// 包含OpenCV函数库所需要的头文件
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream> // 包含输入输出流的头文件
// 使用std和cv的命名空间,这样我们就可以直接使用它们提供的方法,不用每次都写std::和cv::
using namespace std;
using namespace cv;
// 函数声明
void drawAxis(Mat&, Point, Point, Scalar, const float);
double getOrientation(const vector<Point> &, Mat&);
/**
* @function drawAxis
* @brief 绘制轴线的函数
*/
void drawAxis(Mat& img, Point p, Point q, Scalar colour, const float scale = 0.2)
{
//! [visualization1]
// 计算以弧度为单位的角度
double angle = atan2((double)p.y - q.y, (double)p.x - q.x); // p到q的线段角度
// 计算p和q之间的直线的长度
double hypotenuse = sqrt((double)(p.y - q.y) * (p.y - q.y) + (p.x - q.x) * (p.x - q.x));
// 这里通过缩放因子来延长线段的长度
q.x = (int)(p.x - scale * hypotenuse * cos(angle)); // 计算新的q点的x坐标
q.y = (int)(p.y - scale * hypotenuse * sin(angle)); // 计算新的q点的y坐标
// 绘制p点到新q点的直线,这是主线段
line(img, p, q, colour, 1, LINE_AA);
// 创建箭头的勾
// 根据箭头角度计算箭头勾的端点,并绘制箭头勾的第一部分
p.x = (int)(q.x + 9 * cos(angle + CV_PI / 4)); // 计算箭头勾的一个端点的x坐标
p.y = (int)(q.y + 9 * sin(angle + CV_PI / 4)); // 计算箭头勾的一个端点的y坐标
line(img, p, q, colour, 1, LINE_AA); // 绘制箭头勾的第一部分
// 绘制箭头勾的第二部分
p.x = (int)(q.x + 9 * cos(angle - CV_PI / 4)); // 计算箭头勾的另一个端点的x坐标
p.y = (int)(q.y + 9 * sin(angle - CV_PI / 4)); // 计算箭头勾的另一个端点的y坐标
line(img, p, q, colour, 1, LINE_AA); // 绘制箭头勾的第二部分
//! [visualization1]
}
/**
* @function getOrientation
* @brief 获取方向的函数
*/
double getOrientation(const vector<Point> &pts, Mat &img)
{
//! [pca]
// 构造PCA分析使用的数据缓冲区, 每个点的x和y坐标为一行
int sz = static_cast<int>(pts.size()); // 获取点集的大小
Mat data_pts = Mat(sz, 2, CV_64F); // 创建Mat对象用于存储点坐标
for (int i = 0; i < data_pts.rows; i++) // 遍历所有点
{
data_pts.at<double>(i, 0) = pts[i].x; // 将点的x坐标放入Mat对象
data_pts.at<double>(i, 1) = pts[i].y; // 将点的y坐标放入Mat对象
}
// 执行PCA分析
PCA pca_analysis(data_pts, Mat(), PCA::DATA_AS_ROW); // 使用点集进行PCA分析
// 获取物体的中心点
Point cntr = Point(static_cast<int>(pca_analysis.mean.at<double>(0, 0)), // 计算平均值点的x坐标
static_cast<int>(pca_analysis.mean.at<double>(0, 1))); // 计算平均值点的y坐标
// 储存特征值和特征向量
vector<Point2d> eigen_vecs(2); // 创建存储特征向量的向量
vector<double> eigen_val(2); // 创建存储特征值的向量
for (int i = 0; i < 2; i++) // 只考虑x和y坐标,因此遍历两个维度
{
eigen_vecs[i] = Point2d(pca_analysis.eigenvectors.at<double>(i, 0), // 获取第i个特征向量的x分量
pca_analysis.eigenvectors.at<double>(i, 1)); // 获取第i个特征向量的y分量
eigen_val[i] = pca_analysis.eigenvalues.at<