open cv凸包
什么是凸包(Convex Hull)?
凸:得到的结果是一个凸出的形状,可以理解为一个凸多边形
包:包围图像中的所有东西
未封闭的凸包叫做凸壳
在一个多边形边缘或者内部任意两个点的连线都包含在多边形边界或者内部
凸包跟多边形逼近很像,只不过它是物体最外层的”凸”多边形:集合A内连接任意两个点的直线都在A的内部,则称集合A是凸形的
当然,这个是对于二维平面,我们通过二维平面来理解凸包,那我们现在给出在多维平面(向量空间)中的定义:
在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,…Xn)的凸组合来构造.
正式定义:包含点集合S中所有点的最小凸多边形称为凸包。
检测算法-Graham扫描法和Jarvis步进法
Graham扫描法:
首先选择Y方向最低的点作为起始点p0
从p0开始极坐标扫描,依次添加p1…pn(排序顺序是根据极坐标的角度大小,逆时针方向)
对每个点pi来说,如果添加pi点到凸包中导致一个左转向(逆时针方向)则添加该点到凸包
反之如果导致一个右转向(顺时针方向)则从凸包中删除该点
OpenCV中已经实现了凸包发现算法和API提供我们使用
API说明cv::convexHull
(
InputArray points–输入候选点,来自findContours(首先要寻找轮廓并获得轮廓点集合数组contourPoints)
OutputArray hull–凸包,构成凸包的点
bool clockwise–default true,顺时针方向
bool returnPoints–true表示返回点个数,如果第二个参数是vector则自动忽略
)
1.首先把图像转换为灰度图像cvtColor
2.将图像转屏幕坐标系为二值图像threshold
3.通过发现轮廓得到候选点findContours
4.凸包API调用convexHull
5.绘制显示drawContours,凸包也是一种特殊的轮廓(最小化的 满足一定条件的轮廓)
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
Mat src, dst,srcGray;
void ConvexHULL(int, void*);
int thre = 100;
int threMax = 255;
int main()
{
src = imread("D:/实验台/机器视觉/测试图片/白人女.jpg");
if (src.empty())//如果src这个数据库属性为空
{
cout << "无法打开" << endl;
return -1;
}
imshow("原图", src);
//进行图像的预处理(灰度化和模糊去噪)
cvtColor(src,srcGray,CV_BGR2GRAY);
blur(srcGray,srcGray,Size(3,3),Point(-1,-1),BORDER_DEFAULT);
//BORDER_DEFAULT用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT
//这里进行边缘的去除 主要是为了舍弃位于边界的点 防止边界上的某个点影响凸包结果
imshow("灰度化", srcGray);
namedWindow("凸包", CV_WINDOW_AUTOSIZE);
createTrackbar("阈值调节","凸包",&thre,threMax,ConvexHULL);
ConvexHULL(0, 0);
waitKey(0);
return 0;
}
void ConvexHULL(int, void*)
{
Mat bin;//bin=BINARY(二值化)
//使用阈值二值化或其他边源处理算子对图像进行二值化处理和简单的边缘提取
threshold(srcGray,bin,thre,threMax,THRESH_BINARY);//准备进行凸包检测最好是背景和被检测区域二值化明显的图像
//Canny(srcGray, bin, thre, thre*2, 3, false);//不能使用边缘提取处理过的图像进行凸包检测 效果很差 无法达到一个凸包包裹全部检测区域的目的
imshow("二值化", bin);
//凸包可以理解为不是那么太标准的轮廓 所以也需要二值图像的轮廓信息 也需要轮廓的二维点集合和层次结构信息
vector<vector<Point>>contourPoints;//双重动态向量Point类型容器 存储轮廓二维点集合
vector<Vec4i>hierarchy;//4维int类型动态向量数组 存储轮廓的层次结构信息
findContours(bin,contourPoints,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
//轮廓发现
//输入的图像(8位单通道灰度或二值化图像)
//输出的轮廓二维点集合
//输出的轮廓层次结构信息
//轮廓的检索模式(RETR_TREE 提取所有轮廓 重建网状轮廓结构)
//轮廓近似方法(CHAIN_APPROX_SIMPLE 压缩水平,垂直,对角线方向元素 仅仅保存轮廓的拐点,存入contours向量内)
//轮廓的偏移量(Point(0,0) 默认不进行偏移)
vector<vector<Point>>convexs(contourPoints.size());//存储被压入栈的凸包点二维集合,容量初始化为轮廓点的数量
vector<Vec4i>empty(0);//设置层次结构信息为空 或者直接在绘制凸包直接把层次结构参数改为Mat()空数组
for(size_t i=0;i<contourPoints.size();i++)//凸包查找次数为轮廓点的数量
{
//凸包的算法原理详解https://blog.csdn.net/u013377068/article/details/80095620?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159257753219724846444125%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=159257753219724846444125&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduend~default-6-80095620.pc_v1_rank_blog_v1&utm_term=%E5%87%B8%E5%8C%85
convexHull(contourPoints[i], convexs[i],true,false);
//凸包
// (1)InputArray类型的points,输入二维点集,存储在std::vector或Mat中
// (2)OutputArray类型的hull,输出凸包.它可以是索引的整数向量,也可以是点的向量
//(3)bool类型的clockwise,方向标志.如果为true,则输出凸包将顺时针方向 如果为false是逆时针方向的(假定坐标系的X轴指向右侧,Y轴指向上方)***此案例中修改为true和false返回的坐标信息会变化但凸包绘制效果不变***
//(4)bool类型的returnPoints,操作标志.对于矩阵,当标志为true时,函数返回凸壳点.如果为false,返回凸壳点的索引 ***此案例中不论修改为true还是false返回的结果不变n***
//***cv.convexHull() 有个可选参数returnPoints,默认是True,代表返回角点的x / y坐标;如果为False的话,表示返回轮廓中是凸包角点的索引
}//对每一组轮廓的全部轮廓点计算其凸包,留下符合凸包的点存入convexs堆栈中,其余从栈中出栈舍弃
RNG rng(12345);
dst = Mat::zeros(srcGray.size(), CV_8UC3);//在纯黑色背景中绘制
//src.copyTo(dst);//在原图中绘制
//不能直接在src里绘制 需要先把src的数据转移给不会在程序循环里被别的处理程序处理(src会在main函数中被反复灰度化和模糊)的图片dst
for (size_t k = 0; k < contourPoints.size(); k++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
Scalar color2 = Scalar(0, 255, 0);
drawContours(dst,contourPoints,k,color,2,LINE_8,hierarchy,0,Point(0,0));
drawContours(dst, convexs, k, color2, 2, LINE_8, empty, 0, Point(0, 0));//empty==Mat();
//轮廓绘制
//绘制目标图像
//输入的轮廓信息数组
//轮廓的索引编号
//轮廓颜色
//线条宽度 -1为填充轮廓
//线条类型
//可选的层次结构信息 凸包不需要层次结构信息
//绘制轮廓的最高级别 默认为0
//轮廓绘制偏移量坐标
}
imshow("凸包", dst);
/*
for (size_t i = 0; i < convexs.size(); i++)//
{
cout <<"第"<<i+1 <<"个凸包轮廓点集合"<<endl;
{
cout << convexs[i];//
}
cout << endl;
}*/
}
应用场景
关于凸包概念和凸包查找算法 Graham扫描法
https://blog.csdn.net/shuiyixin/article/details/104625013?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159249288619195264558210%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=159249288619195264558210&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v1~rank_blog_v1-1-104625013.pc_v1_rank_blog_v1&utm_term=%E5%87%B8%E5%8C%85