1.什么是凸包
- 在一个多变形边缘或者内部任意两个点的连线都包含在多边形边界或者内部。
- 包含点集合S中所有点的最小凸多边形称为凸包
2. Graham扫描算法
- 首先选择Y方向最低的点作为起始点p0
- 从p0开始极坐标扫描,依次添加p1….pn(排序顺序是根据极坐标的角度大小,逆时针方向)
- 对每个点pi来说,如果添加pi点到凸包中导致一个左转向(逆时针方法)则添加该点到凸包, 反之如果导致一个右转向(顺时针方向)删除该点从凸包中
3. 步骤
- 首先把图像从RGB转为灰度
- 然后再转为二值图像
- 再通过发现轮廓得到候选点
- 凸包API调用
- 绘制显示。
4. 相关API
4.1 cv::convexHull()
convexHull(
InputArray points,// 输入候选点,来自findContours
OutputArray hull,// 凸包
bool clockwise,// default true, 顺时针方向
bool returnPoints)// true 表示返回点个数,如果第二个参数是 vector<Point>则自动忽略
5. 例程
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
using namespace std;
void CallBack(int, void*);
Mat src, dst, binout, src_gray;
int Threshold = 10;
int main() {
//输入图像并转化为灰度
src = imread("D:/resource/images/手.png");
if (src.empty()) {
printf("src couldn't be loaded...");
return -1;
}
imshow("input", src);
cvtColor(src, src_gray, COLOR_BGR2GRAY);
GaussianBlur(src_gray, src_gray, Size(3, 3),0, 0, BORDER_DEFAULT);
imshow("gray", src_gray);
//设置阈值后执行CallBack()
namedWindow("output");
createTrackbar("Threshold", "output", &Threshold, 255, CallBack);
CallBack(0, 0);
waitKey(0);
return 0;
}
void CallBack(int, void*) {
//二值化
threshold(src_gray, binout, Threshold, 255, THRESH_BINARY);
//寻找边缘
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
findContours(binout, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
//凸包检测
vector<vector<Point>> convexs(contours.size());
for(size_t k=0;k<contours.size();k++)
convexHull(contours[k], convexs[k], false, true);
//画出所有边缘(蓝色)和凸包(红色)
dst = Mat::zeros(src.size(), CV_8UC3);
for (size_t i = 0; i < convexs.size(); i++)
{
drawContours(dst, contours, i, Scalar(255, 0, 0), 1, LINE_8, Mat());
drawContours(dst, convexs, i, Scalar(0, 0, 255), 1, LINE_8, Mat());
}
imshow("output", dst);
cout << contours.size()<<endl;
}