OpenCV C++(九)----几何形状的检测和拟合

9.1、点集的最小外包

点集是坐标点的集。

9.1.1、最小外包旋转矩形

//点集
Mat points = (Mat_<float>(5, 2) << 1, 1, 5, 1, 1, 10, 5, 10, 2, 5);
//计算点集的最小外包旋转矩阵
RotatedRect rRect = minAreaRect(points);
//打印旋转矩形的信息
cout << "旋转矩形的角度" << rRect.angle << endl;
cout << "旋转矩形的中心" << rRect.center << endl;
cout << "旋转矩形的尺寸" << rRect.size << endl;

9.1.2、旋转矩形的四个顶点

OpenCV3新特性

void boxPoints(RotatedRect box,OutputArray points)

便于计算旋转矩形的四个顶点,这样就可以使用函数line画出四个顶点的连线,从而画出旋转矩形。

9.1.3、最小外包圆

void minEnclosingCircle( InputArray points,CV_OUT Point2f& center, CV_OUT float& radius );

9.1.4、最小外包直立矩形

Rect boundingRect( InputArray points );

9.1.5、最小凸包

凸包是将最外层的点连接起来构成的凸多边形,它能包含点集的所有点。

void convexHull( InputArray points, OutputArray hull,
                              bool clockwise = false, bool returnPoints = true );

利用该函数求出的凸包,坐标点的顺序不是随机排列的,而是按照某顺序排列的,也就是把这些点依次相连就可以得到连线。

9.1.6、最小外包三角形

OpenCV3新特性

double minEnclosingTriangle( InputArray points, CV_OUT OutputArray triangle );

    //5行2列的单通道Mat
    Mat point = (Mat_<int>(5, 2) << 1, 1, 5, 1, 1, 10, 5, 10, 2, 5);
    //转换为双通道矩阵
    point = point.reshape(2, 5);
    //存储三角形的三个顶点
    vector<Point> triangle;
    //点集的最小外包三角形
    double area;
    area = minEnclosingTriangle(point, triangle);
    //打印结果
    cout << "三角形的三个顶点";
    for (int i = 0; i < 3; i++)
    {
        cout << triangle[i] << ",";
    }
    cout << "面积" << area << endl;

9.2、霍夫直线检测

如果知道原点到一条直线的代数距离ρ和与x轴的夹角θ,则直线方程可由以下方式表示:

image.png

当然, 反过来也可以, 如果知道平面内的一条直线, 那么可以计算出唯一的ρ和θ, 即xoy平面内的任意一条直线对应参数空间(或称霍夫空间) θoρ中的一点(ρ, θ) 。

image.png
image.png

Hough直线检测的基本原理在于利用点与线的对偶性。霍夫变换运用两个坐标空间之间的变换,将在一个空间中具有相同形状的曲线或直线映射到另一个坐标空间的一个点上形成峰值,从而把检测任意形状的问题转化为统计峰值问题。

结论:判断xoy平面内哪些点是共线的,首先求出每一个点对应到霍夫空间的曲线,然后判断哪几条曲线相交于一点,最后将相交于一点的曲线反过来对应到xoy平面内的点,这些点是共线的。

在理论上,一个点对应无数条直线或者说任意方向的直线(在参数空间中坐标轴表示的斜率k或者说θ有无数个),但在实际应用中,我们必须限定直线的数量(即有限数量的方向)才能够进行计算。

因此,我们将直线的方向θ离散化为有限个等间距的离散值,参数ρ也就对应离散化为有限个值,于是参数空间不再是连续的,而是被离散量化为一个个等大小网格单元。将图像空间(直角坐标系)中每个像素点坐标值变换到参数空间(极坐标系)后,所得值会落在某个网格内,使该网格单元的累加计数器加1。当图像空间中所有的像素都经过霍夫变换后,对网格单元进行检查,累加计数值最大的网格,其坐标值(ρ0, θ0)就对应图像空间中所求的直线。

如下图解

image.png

总结:使用霍夫变换检测直线具体步骤:
1.彩色图像->灰度图
2.去噪(高斯核)
3.边缘提取(梯度算子、拉普拉斯算子、cannysobel
4.二值化(判断此处是否为边缘点,就看灰度值==255)
5.映射到霍夫空间(准备两个容器,一个用来展示hough-space概况,一个数组hough-space用来储存voting的值,因为投票过程往往有某个极大值超过阈值,多达几千,不能直接用灰度图来记录投票信息)
6.取局部极大值,设定阈值,过滤干扰直线
7.绘制直线、标定角点

优点:Hough直线检测的优点是抗干扰能力强,对图像中直线的殘缺部分、噪声以及其它共存的非直线结构不敏感,能容忍特征边界描述中的间隙,并且相对不受图像噪声的影响

缺点:Hough变换算法的特点导致其时间复杂度和空间复杂度都很高,并且在检测过程中只能确定直线方向,丢失了线段的长度信息。由于霍夫检测过程中进行了离散化,因此检测精度受参数离散间隔制约

OpenCV支持三种霍夫直线检测算法:
1)Standard Hough Transform(SHT,标准霍夫变换)
2)Multiscale Hough Transform(MSHT,多尺度霍夫变换)
3)Progressive Probability Houth Transform(PPHT,渐进概率式霍夫变换)

在OpenCV3.0及以上版本中,霍夫直线检测算法定义了两个函数:HoughLines(1、2)、HoughLinesP(3)

CV_EXPORTS_W void 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 );
  
  //InputArray image:输入图像,必须是8位单通道图像。 
  //OutputArray lines:检测到的线条参数集合。 
  //double rho:以像素为单位的距离步长。 
  //double theta:以弧度为单位的角度步长。 
  //int threshold:累加计数值的阈值参数,当参数空间某个交点的累加计数的值超过该阈值,则认为该交点对应了图像空间的一条直线。 
  //double srn:默认值为0,用于在多尺度霍夫变换中作为参数rho的除数,rho=rho/srn。 
  //double stn:默认值为0,用于在多尺度霍夫变换中作为参数theta的除数,theta=theta/stn。
  //如果srn和stn同时为0,就表示HoughLines函数执行标准霍夫变换,否则就是执行多尺度霍夫变换。

CV_EXPORTS_W void HoughLinesP( InputArray image, OutputArray lines, 
  double rho, double theta, int threshold, 
  double minLineLength = 0, double maxLineGap = 0 ); 
  
  //InputArray image:输入图像,必须是8位单通道图像。 
  //OutputArray lines:检测到的线条参数集合。 
  //double rho:直线搜索时的距离步长,以像素为单位。 
  //double theta:直线搜索时的角度步长,以弧度为单位。 
  //int threshold:累加计数值的阈值参数,当参数空间某个交点的累加计数的值超过该阈值,则认为该交点对应了图像空间的一条直线。 
  //double minLineLength:默认值为0,表示最小线段长度阈值(像素)。 
  //double maxLineGap:线段上最近两点之间的阈值.默认值为0,表示直线断裂的最大间隔距离阈值。即如果有两条线段是在一条直线上,但它们之间有间隙,那么如果这个间隔距离小于该值,则被认为是一条线段,否则认为是两条线段。 

注意:霍夫直线变换是一种用来在图像空间寻找直线的方法,输入图像要求是二值图像,同时为了提高检测直线的效率和准确率,在使用霍夫线变换之前,最好对图像进行边缘检测生成边缘二值图像,这样的检测效果是最好的。

9.3、霍夫圆检测

9.3.2、标准霍夫圆检测

与霍夫直线检测类似, 图像的霍夫圆检测就是检测哪些前景或边缘像素点在同一个 圆上, 并给出对应圆的圆心坐标及圆的半径; 而且仍然需要计数器来完成该过程, 只是 这里的计数器从二维变成了三维。

9.3.2、基于梯度的霍夫圆检测

步骤:首先定位圆心(两个参数),然后计算半径 (一个参数)。在代码实现中,首先构造一个二维计数器,然后再构造一个一维计数器。

OpenCV提供的函数HoughCircles实现了基于梯度的霍夫圆检测, 在该函数的实现过 程中, 使用了Sobel算子且内部实现了边缘的二值图, 所以输入的图像不用像函数 HoughLinesPHoughLines一样必须是二值图。

CV_EXPORTS_W void HoughCircles( InputArray image, OutputArray circles,
                               int method, double dp, double minDist,
                               double param1 = 100, double param2 = 100,
                               int minRadius = 0, int maxRadius = 0 );

缺点是在不知道一些先验知识的情况下, 需要多次调整参数才有可能得到我们想要的结果。

对于霍夫变换不限于对直线、圆进行检测,也可以对椭圆等其他几何形状进行拟合。同时从原理中可以看出,霍夫变换的一个较大的优点就是可以检测出部分或者遮挡的直线和圆,当然其他几何形状也可以。

9.4、轮廓

9.4.1、查找、拟制轮廓

轮廓:有序点集

CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,
                              OutputArray hierarchy, int mode,
                              int method, Point offset = Point());

CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours,
                              int contourIdx, const Scalar& color,
                              int thickness = 1, int lineType = LINE_8,
                              InputArray hierarchy = noArray(),
                              int maxLevel = INT_MAX, Point offset = Point() );

9.4.2、外包、拟合轮廓

介绍了寻找图像中轮廓的方法和点集的拟合, 那么这两部分合起来, 就可以处理图像目标的定位问题了 。

第一步: 对图像边缘检测或者阈值分割得到二值图,有时也需要对这些二值图进行形态学处理。
第二步: 利用函数findContours寻找二值图中的多个轮廓。
第三步: 对于通过第二步得到的多个轮廓,其中每一个轮廓都可以作为函数 convexHullminAreaRect等的输入参数,然后就可以拟合出包含这个轮廓的最小凸包、最小旋转矩形等。

Mat img = imread("Koala.jpg", IMREAD_GRAYSCALE);
//step1:边缘检测,得到边缘二值图
GaussianBlur(img, img, Size(3, 3), 0.5);
Mat binaryImg;
Canny(img, binaryImg, 50, 200);
//step2:边缘的轮廓
vector<vector<Point>> contours;
findContours(binaryImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
//step3:对每一个轮廓进行拟合
for (int i = 0; i < contours.size(); i++)
{
    Rect rect = boundingRect(contours[i]);
    if (rect.area() > 500)
    {
        rectangle(img, rect, Scalar(255));
    }
}   

9.4.3、轮廓的周长和面积

用来计算点集所围区域的周长:
参数closed是指点集是否首尾相接。

CV_EXPORTS_W double arcLength( InputArray curve, bool closed );

用来计算点集所围区域的面积

CV_EXPORTS_W double contourArea( InputArray contour, bool oriented = false );

9.4.4、点和轮廓的位置关系

点集可以围成一个封闭的轮廓, 那么空间中任意一点和这个轮廓无非有三种关系: 点在轮廓外、 点在轮廓上、 点在轮廓内。

CV_EXPORTS_W double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist );

注:measureDist是bool类型,当其值为false时,函数pointPolygonTest的返回值有三种, 即+1、0、-1, +1代表pt在点集围成的轮廓内,0代表pt在点集围成的轮廓上,-1代表pt在点集围成的轮廓外;当其值为true时,则返回值为pt到轮廓的实际距离。

9.4.5、轮廓的凸包缺陷

用来衡量凸包的缺陷:

void convexityDefects( InputArray contour, InputArray convexhull, OutputArray convexityDefects );

convexityDefects代表返回的凸包缺陷的信息,形式为vector< Vec4i>,每一个Vec4i代表一个缺陷,它的四个元素依次代表:缺陷的起点、终点、最远点的索引及最远点到凸包的距离。

对凸包的缺陷检测在判断物体形状等方面发挥着很重要的作用,与凸包缺陷类似的还有如矩形度、椭圆度、 圆度等,它们均是衡量目标物体形态的度量。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容