多边形拟合
多边形近似
API
approxPolyDP(curve, approxCurve, epsilon, closed)
- curve:输入像素轮廓点,类型为vector<Point>或者Mat
- approxCurve:多边形逼近结果,以多边形顶点坐标的形式给出,类型为 vector<Point2f> 或 CV_32SC2 类型的 Nx1 的Mat 类矩阵
- epsilon:逼近的精度,即原始曲线和逼近曲线之间的最大距离,类型为 double
- closed:逼近曲线是否为封闭曲线的标志,true 表示曲线封闭,即最后一个顶点与第一个顶点相连
示例代码:
#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
// 绘制轮廓函数
inline void drawapp(vector<Point2f> result, Mat img2)
{
for (int i = 0; i < result.size(); i++)
{
// 最后一个坐标点与第一个坐标点相连
if (i == result.size() - 1)
{
Point2f point1 = result.at(i);
Point2f point2 = result.at(0);
line(img2, point1, point2, Scalar(0, 255, 0), 2);
break;
}
Point2f point1 = result.at(i);
Point2f point2 = result.at(i + 1);
line(img2, point1, point2, Scalar(0, 255, 0), 2);
}
}
int main()
{
Mat img = imread("/home/kslas/test.jpg");
imshow("original", img);
// 边缘检测
Mat canny;
Canny(img, canny, 80, 160, 3, false);
// 膨胀运算
Mat kernel = getStructuringElement(0, Size(3, 3));
dilate(canny, canny, kernel);
// 轮廓的发现与绘制
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(canny, contours, hierarchy, 0, 2);
// 绘制多边形
for (int i = 0; i < contours.size(); i++)
{
// 用最小外接矩形求取轮廓中心
RotatedRect rrect = minAreaRect(contours[i]);
Point2f center = rrect.center;
circle(img, center, 2, Scalar(0, 255, 0), 2);
vector<Point2f> result;
approxPolyDP(contours[i], result, 4, true); // 多边形逼近
drawapp(result, img);
}
imshow("result", img);
waitKey(0);
destroyAllWindows();
return 0;
}
运行结果:
凸包检测
简介
-
有时物体的形状过于复杂,用多边形逼近后处理起来依然较为复杂,例如人手、海星等。对于形状较为复杂的物体,可以利用凸包近似表示
-
凸包是图形学中常见的概念,将二维平面上的点集最外层的点连接起来构成的凸多边形称为凸包
-
虽然凸包检测也是对轮廓进行多边形逼近,但是逼近结果一定为凸多边形
-
凸包检测往往用于加强
approxPolyDP()
的结果
API
convexHull(points, hull, clockwise, returnPoints)
- points:输入的二维点集或轮廓坐标,类型为 vector<Point> 或者 Mat
- hull:输出凸包的顶点坐标或索引,类型为 vector<Point> 或者 vector<int>
- clockwise:方向标志。当参数取值为 true 时,凸包顺序为顺时针方向;当参数取值为 false 时,凸包顺序为逆时针方向
- returnPoints:输出数据的类型标志。当参数为 true 时,第二个参数输出的结果是凸包顶点的坐标;当参数取值为为 false 时,第二个参数输出的结果是凸包顶点的索引
示例代码:
在该程序中,首先对图像进行二值化,并利用开运算消除二值化过程中产生的较小区域,之后寻找图像的轮廓,最后对图像中的每一个轮廓进行凸包检测,并绘制凸包的顶点和每一条边
#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
int main()
{
Mat img = imread("/home/kslas/OpenCV/hand.jpg");
// 二值化
Mat gray, binary;
cvtColor(img, gray, COLOR_BGR2GRAY);
threshold(gray, binary, 230, 255, THRESH_BINARY);
bitwise_not(binary, binary);
// 开运算消除细小区域
Mat k = getStructuringElement(MORPH_RECT, Size(3, 3));
morphologyEx(binary, binary, MORPH_OPEN, k);
imshow("binary", binary);
// 轮廓发现
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(binary, contours, hierarchy, 0, 2);
for (int i = 0; i < contours.size(); i++)
{
// 计算凸包
vector<Point> hull;
convexHull(contours[i], hull);
// 绘制凸包
for (int j = 0; i < hull.size(); j++)
{
// 绘制凸包顶点
circle(img, hull[j], 4, Scalar(255, 0, 0), 2);
// 连接凸包
if (j == hull.size() - 1)
{
line(img, hull[j], hull[0], Scalar(0, 0, 255), 2);
break;
}
line(img, hull[j], hull[j + 1], Scalar(0, 0, 255), 2);
}
}
imshow("hull", img);
waitKey(0);
destroyAllWindows();
return 0;
}
运行结果: