#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void approx_polys_demo(Mat &image);
void fit_circle_demo(Mat &image);
void fit_line_demo(Mat &image);
int main(int argc, const char** argv) {
Mat src1 = imread("../sample/contours.png");
Mat src2 = imread("../sample/stuff.png");
Mat src3 = imread("../sample/fitline.png");
if (src1.empty() || src2.empty()) {
cout << "could not load image..." << endl;
return -1;
}
approx_polys_demo(src1);
fit_circle_demo(src2);
fit_line_demo(src3);
waitKey(0);
destroyAllWindows();
return 0;
}
void approx_polys_demo(Mat &image) {
// 二值化
GaussianBlur(image, image, Size(3, 3), 0);
Mat gray, binary;
cvtColor(image, gray, COLOR_BGR2GRAY);
threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
// 轮廓发现
imshow("poly binary", binary);
vector<vector<Point>> contours;
vector<Vec4i> hirearchy;
findContours(binary, contours, hirearchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
// 多边形逼近演示程序
for (size_t t = 0; t < contours.size(); t++) {
Moments mm = moments(contours[t]);
double cx = mm.m10 / mm.m00;
double cy = mm.m01 / mm.m00;
drawMarker(image, Point(cx, cy), Scalar(255, 0, 0), MARKER_TILTED_CROSS, 10, 2, 8);
double area = contourArea(contours[t]);
double clen = arcLength(contours[t], true);
Mat result;
approxPolyDP(contours[t], result, 4, true);
printf("corners : %d , contour area : %.2f, contour length : %.2f n", result.rows, area, clen);
if (result.rows == 6) {
putText(image, "hexagon", Point(cx, cy - 10), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 0, 255), 1, 8);
}
if (result.rows == 4) {
putText(image, "rectangle", Point(cx, cy - 10), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 255, 255), 1, 8);
}
if (result.rows == 3) {
putText(image, "triangle", Point(cx, cy - 10), FONT_HERSHEY_PLAIN, 1.0, Scalar(255, 0, 255), 1, 8);
}
if (result.rows > 10) {
putText(image, "circle", Point(cx, cy - 10), FONT_HERSHEY_PLAIN, 1.0, Scalar(255, 255, 0), 1, 8);
}
}
imshow("find contours demo", image);
}
void fit_circle_demo(Mat &image) {
// 二值化
Mat gray, binary;
cvtColor(image, gray, COLOR_BGR2GRAY);
threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
// 轮廓发现
imshow("circle binary", binary);
vector<vector<Point>> contours;
vector<Vec4i> hirearchy;
findContours(binary, contours, hirearchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
// 拟合圆或者椭圆
for (size_t t = 0; t < contours.size(); t++) {
// drawContours(image, contours, t, Scalar(0, 0, 255), 2, 8);
RotatedRect rrt = fitEllipse(contours[t]);
float w = rrt.size.width;
float h = rrt.size.height;
Point center = rrt.center;
drawMarker(image, center, Scalar(255, 0, 0), MARKER_TILTED_CROSS, 10, 2, 8);
ellipse(image, rrt, Scalar(0, 255, 0), 2, 8);
}
imshow("fit circle result", image);
}
void fit_line_demo(Mat &image) {
// 二值化
Mat gray, binary;
cvtColor(image, gray, COLOR_BGR2GRAY);
threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
// 轮廓发现
imshow("line binary", binary);
vector<vector<Point>> contours;
vector<Vec4i> hirearchy;
findContours(binary, contours, hirearchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());
for (size_t t = 0; t < contours.size(); t++) {
Vec4f line_param;
fitLine(contours[t], line_param, DIST_L2, 0, 0.01, 0.01);
//获取点斜式的点和斜率
Point point0;
point0.x = line_param[2];
point0.y = line_param[3];
double k = line_param[1] / line_param[0];
//计算直线的端点(y = k(x - x0) + y0)
Point point1, point2;
point1.x = 0;
point1.y = k * (0 - point0.x) + point0.y;
point2.x = 640;
point2.y = k * (640 - point0.x) + point0.y;
line(image, point1, point2, cv::Scalar(0, 255, 0), 2, 8, 0);
}
imshow("fit line result", image);
}
轮廓逼近,本质是减少编码点。但减少不是无限制的,轮廓逼近得越厉害,反而编码点会变多。
approxPolyDP:多边形逼近
void approxPolyDP( InputArray curve, OutputArray approxCurve, double epsilon, bool closed );
- curve:输入,使用std::vector容器存储2D点集,或Mat对象
- approxCurve:输出,逼近结果,type与curve一致
- epsilon:指定逼近的精度
- closed:当为true,逼近结果是闭合的,否则,不是闭合的
fitEllipse:拟合椭圆
RotatedRect fitEllipse( InputArray points );
- points:输入,使用std::vector容器存储2D点集,或Mat对象
- return:返回拟合椭圆的内切旋转矩形
fitLine:拟合直线
void fitLine( InputArray points, OutputArray line, int distType, double param, double reps, double aeps );
- points:输入,使用std::vector容器存储2D/3D点集,或Mat对象
- line:输出拟合直线的参数。例如2D直线拟合,line应该为4个元素的集合(可以使用Vec4f存储):(vx, vy, x0, y0),其中通过vx, vy可以计算出直线的方向, x0, y0为拟合直线上的一点;如果为3D直线拟合,line应该为6个元素的集合(可以使用Vec6f存储):(vx, vy, vz, x0, y0, z0) ,其中通过vx, vy, vz可以计算出直线的方向, x0, y0, z0为拟合直线上的一点。即直线的点截式。
- distType:距离类型,拟合直线时,要使输入点到拟合直线的距离和最小化
- DIST_L2:最简单的最小二乘法
- param:距离参数,跟所选的距离类型有关,如果设置为0,会自动选择最优化的值
- reps:半径精度,通常设为0.01
- aeps:角度精度,通常设为0.01