《PCA图像转正C++》
去年写的博客,写到另一个号里面了,今天恰好一个同事问到了,想起来把这篇博客迁移过来,顺便完善一下。PCA的功能还是挺多的,最常用的就是降维,原理也比较简单,找到方差最大的方向。这里直接把工程中的两个脚本抽离出来,方便查阅,供大家参考。
Key Words:主成分分析、C++实现
Beijing, 2021.01
作者:RaySue
PCA原理
简单说下PCA的原理,就是求解向量 v,让原始数据在 v 方向上的方差最大,各个主成分之间相互正交。求解目标就是最大化在向量 v 上的方差,来求解 v 向量,原理细节参见参考链接。
具体计算就比较简单了,数据去中心化,计算特征间的协方差矩阵,然后计算协方差的特征值和特征向量就是对应的主成分和方差贡献率。
MaskRotation.h
//
// Created by surui on 2019/12/17.
//
#ifndef IDEATRAINER_MASKROTATION_H
#define IDEATRAINER_MASKROTATION_H
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
#define PI acos(-1)
class MaskRotation {
public:
/**
* input mask foreground pixels' value is 255
* @param mask
* @param points
*/
static void getAllForeGroundPoints(Mat mask, Mat &points);
/**
* pca fit data
* @param Points
* @return angle
*/
static float pca(const Mat &Points);
/**
* rotate mask to upright by pca's angle
* @param angle
* @param mask
*/
static void rotateToUpright(float angle, Mat &mask);
};
#endif //IDEATRAINER_MASKROTATION_H
MaskRotation.cpp
//
// Created by surui on 2019/12/17.
//
void MaskRotation::getAllForeGroundPoints(Mat mask, Mat &points) {
int height = mask.rows;
int width = mask.cols;
for (int i = 0; i < height; ++i) {
const uchar *rowi = mask.ptr<uchar>(i);
for (int j = 0; j < width; ++j) {
if (rowi[j] == 255) {
Mat point = Mat(cv::Size(2, 1), CV_32FC1);
point.at<float>(0, 0) = i;
point.at<float>(0, 1) = j;
points.push_back(point);
}
}
}
}
float MaskRotation::pca(const Mat &Points) {
int components = 2;
float angle = 0.0;
try {
cv::PCA pca(Points, cv::Mat(), CV_PCA_DATA_AS_ROW, components);
float scx = pca.eigenvectors.at<float>(0, 0);
float scy = pca.eigenvectors.at<float>(0, 1);
float cosx = scy / sqrt(scx * scx + scy * scy);
angle = (float) ((acos(cosx) - PI / 2) / PI * 180);
} catch (cv::Exception e) {
cout << "Error pca : " << e.msg << endl;
}
return angle;
}
void MaskRotation::rotateToUpright(float angle, Mat &mask) {
int height = mask.rows;
int width = mask.cols;
try {
Point2f center(width / 2, height / 2);
Mat M = cv::getRotationMatrix2D(center, angle, 1.0);
cv::warpAffine(mask, mask, M, cv::Size(width, height), cv::INTER_LINEAR, cv::BORDER_REPLICATE);
M.release();
} catch (cv::Exception e) {
cout << "Error image rotate : " << e.msg << endl;
}
}