代码整体思路为:
Mat labels,centers;//聚类的点
cv::kmeans(Mat(polypoints), 4, labels, TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 10, 1.0),5, cv::KMEANS_RANDOM_CENTERS, centers);
vector<Point2f> pixel_points4;
for (int i = 0; i < centers.rows; i++) {
cv::drawMarker(testimg_real, Point(int(centers.at<float>(i, 0)), int(centers.at<float>(i, 1))), (255, 255, 255), 1, 20, 5);
pixel_points4.push_back(Point2f(centers.at<float>(i, 0), centers.at<float>(i, 1)));
}
cout << pixel_points4<<"四个聚类点的坐标" << endl;
//提取多边形顶点的顺序:labels本身的顺序继承了多边形点的顺序,也就是轮廓的顺序,逆时针顺序
vector<int> labels_lst = { labels.at<int>(0,0) };//**花括号元素赋初值 第一个点属于哪个类型
vector<int>::iterator ret;//**迭代器
for (int i = 0; i < labels.rows; i++) {
ret = std::find(labels_lst.begin(), labels_lst.end(), labels.at<int>(0, i));
if (ret == labels_lst.end()) {
//not find
labels_lst.push_back(labels.at<int>(i, 0));
}
}
//顶点顺序重构,多边形顶点顺序为逆时针
vector<Point2f> pixel_points4_regroup;
int x_min_idx;
float x_min = 9999.0;
for (int i = 0; i < labels_lst.size(); i++) {
pixel_points4_regroup.push_back(pixel_points4[labels_lst[i]]);
if (pixel_points4_regroup[i].x < x_min) {
//找pixel_points4_regroup中的最小x的索引
x_min = pixel_points4_regroup[i].x;
x_min_idx = i;
}
}
//长短边探测,找到真正的起始点
vector<Point2f> pixel_points4_regroup2;
//起始点
Point2f pt1 = pixel_points4_regroup[x_min_idx];
//起始点逆时针下一个点
Point2f pt2 = pixel_points4_regroup[(x_min_idx + 1) % 4];
//起始点逆时针上一个点
Point2f pt3 = pixel_points4_regroup[(x_min_idx + 3) % 4];
cout << "pt1\t" << pt1 << endl;
cout << "pt2\t" << pt2 << endl;
cout << "pt3\t" << pt3 << endl;
if (getDistance(pt1, pt2) > getDistance(pt1, pt3)) {
for (int i = 0; i < 4; i++) {
pixel_points4_regroup2.push_back(pixel_points4_regroup[(x_min_idx + 3 + i) % 4]);
}
}
else {
for (int i = 0; i < 4; i++) {
pixel_points4_regroup2.push_back(pixel_points4_regroup[(x_min_idx + i) % 4]);
}
}
//cv::putText(testimg_real, "1", Point2f(pixel_points4_regroup[0].x, pixel_points4_regroup[0].y), 1, 10, cv::Scalar(255, 255, 255));
cout << pixel_points4_regroup2 << endl;
//最小外接矩形顶点,奇葩,输出来的是矩阵
Mat point4;
cv::RotatedRect rect = cv::minAreaRect(c);
cv::boxPoints(rect, point4);
//cout << point4.size() << endl;
//cout << point4.at(0, 3) << endl;
//找到pixel_points4_regroup2与最小外接矩形顶点最近的点,并将pixel_points4_regroup2的点向该方向拓展
//保证截取的画面包含圆形按钮
float rate = 0.5;
for (int i = 0; i < 4; i++) {
float min_norm = 99999;
int min_idx = -1;
for (int j = 0; j < 4; j++) {
float tmp_norm = getDistancepp(pixel_points4_regroup2[i].x, pixel_points4_regroup2[i].y, point4.at<float>(j, 0), point4.at<float>(j, 1));
if (tmp_norm < min_norm) {
min_idx = j;
min_norm = tmp_norm;
}
}
if (min_idx >= 0) {
pixel_points4_regroup2[i].x += rate * (point4.at<float>(min_idx, 0) - pixel_points4_regroup2[i].x);
pixel_points4_regroup2[i].y += rate * (point4.at<float>(min_idx, 1) - pixel_points4_regroup2[i].y);
}
}
//cout << pixel_points4_regroup2 << endl;
//需要变换到的坐标
//正面
//vector h_p = { Point2f(0, 0),Point2f(0, 500) ,Point2f(1043, 500) ,Point2f(1043, 0) };
//背面
vector<Point2f> h_p = { Point2f(0, 0),Point2f(0, 341) ,Point2f(697, 341) ,Point2f(697, 0) };
Mat warp_img;
Mat M = cv::findHomography(pixel_points4_regroup2, h_p);
//cv::warpPerspective(testimg_gray, warp_img, M, cv::Size(1043, 500));
cv::warpPerspective(testimg_gray, warp_img, M, cv::Size(697, 341));
cv::imshow("img", testimg_real);
cv::imshow("warp_img", warp_img);
//cv::waitKey();
注
double cv::kmeans( InputArray data, int K, InputOutputArray bestLabels, TermCriteria citeria, int attempts, int flags, OutputArray)
(1)data: 需要自动聚类的数据,一般是一个Mat。浮点型的矩阵,每行为一个样本。
(2)k: 取成几类,比较关键的一个参数。
(3)bestLabels: 返回的类别标记,整型数字。
(4)criteria: 算法结束的标准,获取期望精度的迭代最大次数
(5)attempts: 判断某个样本为某个类的最少聚类次数,比如值为3时,则某个样本聚类3次都为同一个类,则确定下来。
(6)flags: 确定簇心的计算方式。有三个值可选:KMEANS_RANDOM_CENTERS 表示随机初始化簇心。KMEANS_PP_CENTERS 表示用kmeans++算法来初始化簇心(没用过),KMEANS_USE_INITIAL_LABELS 表示第一次聚类时用用户给定的值初始化聚类,后面几次的聚类,则自动确定簇心。
(7)centers: 用来初始化簇心的。与前一个flags参数的选择有关。如果选择KMEANS_RANDOM_CENTERS随机初始化簇心,则这个参数可省略。