版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huanghuangjin/article/details/81627618
PCA原理:
通过对高维数据分析发现他们的相同与不同表达为一个低维数据模式,
主成分不变
细微损失
高维数据到低维数据
PCA可逆,可以用于数据压缩,但是有损耗
代码
#include "../common/common.hpp"
static void calcPCAorientation(vector<Point>& pts, Mat &image); // 计算PCA算出的特征向量的夹角
void main(int argc, char ** argv)
{
Mat src = imread(getCVImagesPath("images/pca_test1.jpg"));
imshow("src8-4", src);
Mat gray, binary;
cvtColor(src, gray, COLOR_BGR2GRAY);
threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
vector<Vec4i> hireachy;
vector <vector<Point>> contours;
findContours(binary, contours, hireachy, RETR_LIST, CHAIN_APPROX_NONE);
Mat ret = src.clone();
for (size_t i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area > 1e5 || area < 1e2) continue; // 面积太大或太小的过滤,1e5 表示10的5次方
drawContours(ret, contours, i, Scalar(0, 0, 255), 2, 8);
calcPCAorientation(contours[i], ret); // 计算PCA算出的特征向量的夹角
}
imshow("ret", ret);
waitKey(0);
}
void calcPCAorientation(vector<Point>& pts, Mat &image) // 计算PCA算出的特征向量的夹角
{
int size = static_cast<int>(pts.size());
Mat data_pts = Mat(size, 2, CV_64FC1);
for (int i = 0; i < size; i++)
{
data_pts.at<double>(i, 0) = pts[i].x;
data_pts.at<double>(i, 1) = pts[i].y;
}
/*
PCA( // PCA类
InputArray data, // 原始矩阵数据
InputArray mean, // 原始矩阵数据均值,输入空函数会自己计算
int flags, // 每行(CV_PCA_DATA_AS_ROW) 列(CV_PCA_DATA_AS_COL)代表一个样本
double retainedVariance // 保留多少特征值,百分比,默认全保留
);
*/
PCA pca_analysis(data_pts, Mat(), CV_PCA_DATA_AS_ROW); // pca分析
// 因为维度为2,所以均值 pca_analysis.mean 为2行1列的矩阵,本例中的均值为轮廓几何矩的中心位置
Point center = Point(static_cast<int>(pca_analysis.mean.at<double>(0, 0)), static_cast<int>(pca_analysis.mean.at<double>(0, 1)));
circle(image, center, 2, Scalar(0, 255, 0), 2, 8); // 绘制几何矩的中心位置
vector<double> vals(2); // 特征值,2个
vector<Point2d> vecs(2); // 特征向量,2*2的矩阵
for (int i = 0; i < 2; i++)
{
vals[i] = pca_analysis.eigenvalues.at<double>(i, 0); // pca_analysis.eigenvalues 表示PCA计算出来的特征值
// pca_analysis.eigenvectors 表示PCA计算出来的特征向量
vecs[i] = Point2d(pca_analysis.eigenvectors.at<double>(i, 0), pca_analysis.eigenvectors.at<double>(i, 1));
cout << i << ", vals=" << vals[i] << ", vecs.x=" << vecs[i].x << ", vecs.y=" << vecs[i].y << endl;
}
Point p1 = center + 0.02*Point(static_cast<int>(vecs[0].x*vals[0]), static_cast<int>(vecs[0].y*vals[0])); // 为什么是乘以0.02 0.05 ?
Point p2 = center - 0.05*Point(static_cast<int>(vecs[1].x*vals[1]), static_cast<int>(vecs[1].y*vals[1]));
line(image, center, p1, Scalar(255, 0, 0), 2, 8); // 画线,这两条线是正交的,因为一个是x方向一个是y方向
line(image, center, p2, Scalar(255, 0, 255), 2, 8);
double angle = atan2(vecs[0].y, vecs[0].x); // 瞎算的?
cout << "angle=" << 180 * angle / CV_PI << endl;
/* 日志
0, vals=13507.4, vecs.x=0.966421, vecs.y=-0.256964
1, vals=499.892, vecs.x=0.256964, vecs.y=0.966421
angle=-14.89
0, vals=13275.5, vecs.x=0.972491, vecs.y=-0.23294
1, vals=486.813, vecs.x=0.23294, vecs.y=0.972491
angle=-13.4702
0, vals=12837.3, vecs.x=0.975379, vecs.y=-0.220535
1, vals=463.17, vecs.x=0.220535, vecs.y=0.975379
angle=-12.7405
0, vals=12497, vecs.x=0.98585, vecs.y=-0.167631
1, vals=430.128, vecs.x=0.167631, vecs.y=0.98585
angle=-9.65011
0, vals=11958.1, vecs.x=0.0609259, vecs.y=0.998142
1, vals=463.379, vecs.x=0.998142, vecs.y=-0.0609259
angle=86.507
0, vals=12096.5, vecs.x=0.985447, vecs.y=-0.169983
1, vals=414.236, vecs.x=0.169983, vecs.y=0.985447
angle=-9.78686
*/
}