一、凸包检测1、凸包检测原理凸包用来描述一组点的最小凸多边形包围结构。给定一组平面点集凸包就是“用一根橡皮筋把所有点外侧紧紧包住后形成的形状”。2、凸包检测函数/* 用途用于计算点集或轮廓的凸包 即能够完全包围目标的最小凸多边形 */ void cv::convexHull( InputArray points, OutputArray hull, bool clockwise false, bool returnPoints true ); /* points输入的2D点集 hull输出凸包的顶点 clockwise方向标志当参数为true时凸包顺序为顺时针方向否则为逆时针方向 returnPoints输出数据的类型标志当参数为true时第二个参数输出的结果是凸包顶点的坐标 否则第二个参数输出的结果是凸包顶点的索引 */3、示例代码QString imgPath QApplication::applicationDirPath() /Images; cv::String s_imgPath imgPath.toLocal8Bit().data(); Mat img imread(s_imgPath /hand.png); if (img.empty()) { qDebug() 图片加载失败, 请确认图像文件名称是否正确; return; } /*二值化*/ Mat gray, binary; cvtColor(img, gray, COLOR_BGR2GRAY); threshold(gray, binary, 105, 255, THRESH_BINARY); /*开运算消除细小区域*/ Mat k getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); morphologyEx(binary, binary, MORPH_OPEN, k); imshow(binary, binary); vectorvectorPoint contours; vectorVec4i hierarchy; findContours(binary, contours, hierarchy, 0, 2, Point()); for (int n 0; n contours.size(); n) { /*计算凸包*/ vectorPoint hull; convexHull(contours[n], hull); /*绘制凸包*/ for (int i 0; i hull.size(); i) { /*绘制凸包顶点*/ circle(img, hull[i], 4, Scalar(255, 0, 0), 2, 8, 0); /*连接凸包*/ if (i hull.size() - 1) { line(img, hull[i], hull[0], Scalar(0, 0, 255), 2, 8, 0); break; } line(img, hull[i], hull[i 1], Scalar(0, 0, 255), 2, 8, 0); } } imshow(hull, img); waitKey(0); destroyAllWindows();二、直线检测1、检测直线的霍夫变换原理霍夫变换Hough Transform是一种用于检测图像中几何形状的方法最常用于直线检测。其核心思想是将图像空间中的点转换到参数空间中进行投票当多个点在参数空间中的曲线交汇于同一点时则认为这些点属于同一条直线。2、相关函数检测直线流程将参数空间的坐标轴离散化将图像中每个非0像素通过映射关系求取在参数空间通过的方格统计参数空间内每个方格出现的次数选取次数大于某一阈值的方格作为表示直线的方格将参数空间中表示直线的方格的参数作为图像中直线的参数/* 用途用于通过霍夫变换检测图像中的直线 能够从边缘图像中提取满足条件的直线参数 */ void cv::HoughLines( InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn 0, double stn 0, double min_theta 0, double max_theta CV_PI ); /* image输入图像必须为8位单通道二值图像通常为Canny边缘检测结果 lines检测到的直线参数输出每条直线由(rho, theta)表示 rho距离分辨率单位为像素 theta角度分辨率单位为弧度 threshold直线检测阈值 累加器中投票数超过该值才认为是直线 srn多尺度霍夫变换的rho分辨率默认0表示标准霍夫变换 stn多尺度霍夫变换的theta分辨率 min_theta检测直线的最小角度 max_theta检测直线的最大角度 *//* 用途用于通过概率霍夫变换检测图像中的线段。 与HoughLines()不同 HoughLinesP()直接返回线段两个端点坐标 而不是返回无限延长直线的参数 */ void cv::HoughLinesP( InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength 0, double maxLineGap 0 ); /* image输入图像必须是CV_8C的单通道二值图像 lines检测到的线段结果每条线段由(x1,y1,x2,y2)表示 rho距离分辨率单位为像素 theta角度分辨率单位为弧度 threshold霍夫累加器阈值 投票数超过该值才认为检测到线段 minLineLength最小线段长度 小于该长度的线段将被忽略 maxLineGap同一直线上两点之间允许的最大间隔 小于该间隔的断裂线段会被连接 */3、示例代码void drawLine(Mat img,/*要标记直线的图像*/ vectorVec2f lines,/*检测的直线数据*/ double rows,/*原图像的行数高*/ double cols,/*原图像的列数宽*/ Scalar scalar,/*绘制直线的颜色*/ int n)/*绘制直线的线宽*/ { Point pt1, pt2; for (int i 0; i lines.size(); i) { float rho lines[i][0];/*直线距离坐标原点的距离*/ float theta lines[i][1];/*直线过坐标原点垂线与x轴夹角*/ double a cos(theta);/*夹角的余弦值*/ double b sin(theta);/*夹角的正弦值*/ double x0 a * rho, y0 b * rho;/*直线与过坐标原点的垂线的交点*/ double length max(rows, cols);/*图像高宽的最大值*/ /*计算直线上的一点*/ pt1.x cvRound(x0 length * (-b)); pt1.y cvRound(y0 length * (a)); /*计算直线上的另一点*/ pt2.x cvRound(x0 - length * (-b)); pt2.y cvRound(y0 - length * (a)); /*两点绘制一条直线*/ line(img, pt1, pt2, scalar, n); } } void HoughLinesStudy() { QString imgPath QApplication::applicationDirPath() /Images; cv::String s_imgPath imgPath.toLocal8Bit().data(); Mat img imread(s_imgPath /HoughLines.png); if (img.empty()) { qDebug() 图片加载失败, 请确认图像文件名称是否正确; return; } Mat edge; /*检测边缘图像并二值化*/ Canny(img, edge, 80, 180, 3, false); threshold(edge, edge, 170, 255, THRESH_BINARY); /*用不同的累加器进行检测直线*/ vectorVec2f lines1, lines2; HoughLines(edge, lines1, 1, CV_PI / 180, 50, 0, 0); HoughLines(edge, lines2, 1, CV_PI / 180, 150, 0, 0); /*在原图像中绘制直线*/ Mat img1, img2; img.copyTo(img1); img.copyTo(img2); drawLine(img1, lines1, edge.rows, edge.cols, Scalar(255), 2); drawLine(img2, lines2, edge.rows, edge.cols, Scalar(255), 2); imshow(img, img); imshow(edge, edge); imshow(img1, img1); imshow(img2, img2); waitKey(0); destroyAllWindows(); }QString imgPath QApplication::applicationDirPath() /Images; cv::String s_imgPath imgPath.toLocal8Bit().data(); Mat img imread(s_imgPath /HoughLines.png); if (img.empty()) { qDebug() 图片加载失败, 请确认图像文件名称是否正确; return; } Mat edge; /*检测边缘图像并二值化*/ Canny(img, edge, 80, 180, 3, false); /*用不同的累加器进行检测直线*/ vectorVec4i linesP1, linesP2; HoughLinesP(edge, linesP1, 1, CV_PI / 180, 150, 30, 10);/*两个点连接最大距离10*/ HoughLinesP(edge, linesP2, 1, CV_PI / 180, 150, 30, 30);/*两个点连接最大距离30*/ /*绘制两个点连接最大距离10 直线检测结果*/ Mat img1; img.copyTo(img1); for (int i 0; i linesP1.size(); i) { line(img1, Point(linesP1[i][0], linesP1[i][1]), Point(linesP1[i][2], linesP1[i][3]), Scalar(255), 3); } /*绘制两个点连接最大距离10 直线检测结果*/ Mat img2; img.copyTo(img2); for (int i 0; i linesP2.size(); i) { line(img2, Point(linesP2[i][0], linesP2[i][1]), Point(linesP2[i][2], linesP2[i][3]), Scalar(255), 3); } imshow(img, img); imshow(edge, edge); imshow(img1, img1); imshow(img2, img2); waitKey(0); destroyAllWindows();三、点集拟合1、点集拟合的含义点集拟合是指根据一组离散的点数据寻找一个最能表示这些点分布规律的几何模型或数学函数。2、直线拟合函数/* 用途用于根据输入点集进行直线拟合 计算最能代表这些点整体趋势的一条直线 */ void cv::fitLine( InputArray points, OutputArray line, int distType, double param, double reps, double aeps ); /* points输入待拟合直线直线的2D或者3D点集 line输出拟合直线的参数2D点集描述参数为Vec4f类型2D点集描述参数为Vec6f类型 distTypeM-estimator算法使用的距离类型标志 param某些类型距离的数值参数C如果数值为0则自动选择最佳值 reps直线坐标原点与直线之间的距离精度数值0表示选择自适应参数一般常选择0.01 aeps直线角度精度数值0表示选择自适应参数一般常选择0.01 */距离类型选择标志标志参数简记距离计算公式DIST_L11DIST_L22DIST_L124DIST_FAIR5其中C1.3998DIST_WEILSCH6其中C2.9846DIST_L1HUBER7其中C1.3453、圆形拟合/* 用途用于计算能够完全包围输入点集的最小外接圆 即在保证包含所有点的前提下 面积最小的圆形区域 */ void cv::minEnclosingCircle( InputArray points, CV_OUT Point2f center, CV_OUT float radius ); /* points输入二维点集通常为轮廓点集合 center输出最小外接圆的圆心坐标 radius输出最小外接圆的半径 */4、三角形拟合/* 返回值最小外接三角形的面积 用途用于计算能够完全包围输入点集的最小面积三角形 即在保证包含所有点的前提下 面积最小的外接三角形 */ double cv::minEnclosingTriangle( InputArray points, CV_OUT OutputArray triangle ); /* points输入二维点集通常为轮廓点集合 triangle输出最小外接三角形的三个顶点坐标 */5、示例代码Vec4f lines;/*存放拟合后的直线*/ vectorPoint2f point;/*待检测是否存在直线的所有点*/ const static float Points[20][2] { {0.0f,0.0f},{10.0f,11.0f},{21.0f,20.0f},{30.0f,30.0f}, {40.0f,42.0f},{50.0f,50.0f},{60.0f,60.0f},{70.0f,70.0f}, {80.0f,80.0f},{90.0f,92.0f},{100.0f,100.0f},{110.0f,110.0f}, {120.0f,120.0f},{136.0f,130.0f},{138.0f,140.0f},{150.0f,150.0f}, {160.0f,163.0f},{175.0f,170.0f},{181.0f,180.0f},{200.0f,190.0f} }; /*将所有点存放在vector中用于输入函数中*/ for (int i 0; i 20; i) { point.push_back(Point2f(Points[i][0], Points[i][1])); } /*参数设置*/ double param 0;/*距离模型中的数值参数C*/ double reps 0.01;/*坐标原点与直线之间的距离精度*/ double aeps 0.01;/*角度精度*/ fitLine(point, lines, DIST_L1, 0, 0.01, 0.01); double k lines[1] / lines[0];/*直线斜率*/ cout Line Slope: k endl; cout Point on the Line Coordinates x: lines[2] ,y: lines[3] endl; cout Line Equation: y k (x- lines[2] ) lines[3] endl; waitKey(0); Mat img(500, 500, CV_8UC3, Scalar::all(0)); RNG rng theRNG();/*生成随机点*/ while (true) { int count rng.uniform(1, 101); vectorPoint2f points; /*生成随机点*/ for (int i 0; i count; i) { Point pt; pt.x rng.uniform(img.cols / 4, img.cols * 3 / 4); pt.y rng.uniform(img.rows / 4, img.rows * 3 / 4); points.push_back(pt); } /*寻找包围点集的三角形*/ vectorPoint2f triangle; double area minEnclosingTriangle(points, triangle); /*寻找包围点集的圆形*/ Point2f center; float radius 0; minEnclosingCircle(points, center, radius); /*创建两个图片用于输出结果*/ img Scalar::all(0); Mat img2; img.copyTo(img2); /*在图像中绘制坐标点*/ for (int i 0; i count; i) { circle(img, points[i], 3, Scalar(255, 255, 255), FILLED, LINE_AA); circle(img2, points[i], 3, Scalar(255, 255, 255), FILLED, LINE_AA); } /*绘制三角形*/ for (int i 0; i 3; i) { if (i 2) { line(img, triangle[i], triangle[0], Scalar(255, 255, 255), 1, 16); break; } line(img, triangle[i], triangle[i 1], Scalar(255, 255, 255), 1, 16); } /*绘制圆形*/ circle(img2, center, cvRound(radius), Scalar(255, 255, 255), 1, LINE_AA); imshow(triangle, img); imshow(circle, img2); /*按q键或者ESC键退出*/ char key (char)waitKey(); if (key 27 || key q || key Q) { break; } } waitKey(0); destroyAllWindows();