1.凸包概念
在一个多变形边缘或者内部任意两个点的连线都包含在多边形边界或者内部。 包含点集合S中所有点的最小凸多边形称为凸包。
使用Graham扫描算法。
2.Graham算法
首先选择Y方向最低的点作为起始点p0
从p0开始极坐标扫描,依次添加p1….pn(排序顺序是根据极坐标的角度大小,逆时针方向)
对每个点pi来说,如果添加pi点到凸包中导致一个左转向(逆时针方法)则添加该点到凸包, 反之如果导致一个右转向(顺时针方向)删除该点从凸包中。
3.相关API
convexHull(InputArray point,OutputArray hull,bool clockwise,bool returnPoints)
point: 输入候选点,来自findContours
hull:凸包
clockwise:default true, 顺时针方向
returnPoints:true 表示返回点个数,如果第二个参数是 vector<Point>则自动忽略
4.代码实现
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
Mat src,srcGray, dst;
char inputName[] = "input name";
char outputName[] = "output name";
int threshold_value = 100;
int threshold_max = 255;
RNG rng(12345);
void Convex_Demo(int, void *);
int main()
{
src = imread("D:/demo.jpg");
if (src.empty())
{
cout << "找不到图像!" << endl;
return -1;
}
namedWindow(inputName, CV_WINDOW_AUTOSIZE);
namedWindow(outputName, CV_WINDOW_AUTOSIZE);
imshow(inputName, src);
//第一步转灰度
cvtColor(src, srcGray, CV_BGR2GRAY);
blur(srcGray, srcGray, Size(3, 3), Point(-1, -1));
createTrackbar("Threshold Value", outputName, &threshold_value, threshold_max, Convex_Demo);
Convex_Demo(0, 0);
waitKey(0);
return 0;
}
void Convex_Demo(int, void *)
{
Mat threshold_output;
vector<vector<Point>> points;
vector<Vec4i> hierachy;
//第二步二值化获取轮廓
threshold(srcGray, threshold_output, threshold_value, 255, THRESH_BINARY);
//第三步寻找轮廓放入points容器
findContours(threshold_output, points, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
//第四步调用凸包
vector<vector<Point>> hull(points.size());
for (size_t i = 0; i < points.size();i++)
{
convexHull(points[i], hull[i], false);
}
dst = Mat::zeros(src.size(), CV_8UC3);
//第五步,绘制
for (size_t i = 0; i < points.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(dst, hull, i, color, 1, LINE_AA, hierachy, 0, Point(0, 0));
}
imshow(outputName, dst);
}
先通过转灰度图像,threshold方法获取二值图像,再寻找轮廓,之后调用凸包API,进行画凸包针对每个轮廓,最后使用轮廓绘制画出凸包。