PCA 主軸方向角度,範圍 [-45,135] 度
點集排序(從左到右、從右至左)不同,角度在-45度時有差異
double calLineOrientationInDegree(const vector<Point>& pts)
{
//Construct a buffer used by the pca analysis
int sz = static_cast<int>(pts.size());
Mat data_pts = Mat(sz, 2, CV_64F);
for (int i = 0; i < data_pts.rows; i++)
{
data_pts.at<double>(i, 0) = pts[i].x;
data_pts.at<double>(i, 1) = pts[i].y;
}
//Perform PCA analysis
PCA pca_analysis(data_pts, Mat(), PCA::DATA_AS_ROW);
//Store the center of the object
Point2d cntr = Point2d(static_cast<double>(pca_analysis.mean.at<double>(0, 0)),
static_cast<double>(pca_analysis.mean.at<double>(0, 1)));
//Store the eigenvalues and eigenvectors
vector<Point2d> eigen_vecs(2);
vector<double> eigen_val(2);
for (int i = 0; i < 2; i++)
{
eigen_vecs[i] = Point2d(pca_analysis.eigenvectors.at<double>(i, 0),
pca_analysis.eigenvectors.at<double>(i, 1));
eigen_val[i] = pca_analysis.eigenvalues.at<double>(i);
}
eigenVecs = eigen_vecs;
double angle = atan2(eigen_vecs[0].y, eigen_vecs[0].x) * 180 / 3.1415926; // orientation in degrees
return angle;
}
// 測試PCA 主軸角度範圍
// 點集從左到右,從右到左
void test_PCA_mainAxis_AngleRange()
{
// 構造點集
double angle = 10;
//int ang = 135;
for (int ang = 0;ang < 365;ang += 5)
{
angle = ang;
cv::Mat white(1000, 1000, CV_8UC3, Scalar(255, 255, 255));
std::vector<cv::Point> pts;
pts.clear();
double vsin = sin(angle / 180.0 * 3.1415926);
double vcos = cos(angle / 180.0 * 3.1415926);
Point offset(300, 300);
for (int i = 0;i < 200;i++)
{
int px = int(double(i) * vcos) + offset.x;
int py = int(double(i) * vsin) + offset.y;
Point pt(px, py);
pts.push_back(pt);
circle(white, pt, 3, Scalar(0, 0, 255), -1);
}
// 排序
std::sort(pts.begin(), pts.end(), [](Point& p1, Point& p2) {
return p1.x < p2.x;
});
// 計算pca 角度
double pac_angle = calLineOrientationInDegree(pts);
cv::putText(white, cv::format("%.1f", pac_angle), Point(30, 30), cv::FONT_HERSHEY_PLAIN, 2, Scalar(255, 0, 20), 2);
// 從右向左
std::sort(pts.begin(), pts.end(), [](Point& p1, Point& p2) {
return p1.x > p2.x;
});
double pac_angle2 = calLineOrientationInDegree(pts);
cv::putText(white, cv::format("%.1f", pac_angle2), Point(130, 30), cv::FONT_HERSHEY_PLAIN, 2, Scalar(25, 0, 220), 2);
// show pts
cv::imshow("xx", white);
//cv::waitKey(0);
while (1)
{
if (cv::waitKey(10) == 27)
{
cv::destroyAllWindows();
break;
}
}
}
}