基于内容的图像分析的重点是提取出图像中具有代表性的特征,而线条、轮廓、块往往是最能体现特征的几个元素,这篇文章就针对于这几个重要的图像特征,研究它们在OpenCV中的用法,以及做一些简单的基础应用。

一、Canny检测轮廓

在 上一篇文章中有提到sobel边缘检测,并重写了soble的C++代码让其与matlab中算法效果一致,而soble边缘检测是基于单一阈值的,我们 不能兼顾到低阈值的丰富边缘和高阈值时的边缘缺失这两个问题。而canny算子则很好的弥补了这一不足,从目前看来,canny边缘检测在做图像轮廓提取 方面是最优秀的边缘检测算法。

canny边缘检测采用双阈值值法,高阈值用来检测图像中重要的、显著的线条、轮廓等,而低阈值用来保证不丢失细节部分,低阈值检测出来的边缘更丰富,但是很多边缘并不是我们关心的。最后采用一种查找算法,将低阈值中与高阈值的边缘有重叠的线条保留,其他的线条都删除。

本篇文章中不对canny的算法原理作进一步说明,稍后会在图像处理算法相关的文章中详细介绍。

下面我们用OpenCV中的Canny函数来检测图像边缘

 
  1. int main()
  2. {
  3. Mat I=imread("../cat.png");
  4. cvtColor(I,I,CV_BGR2GRAY);
  5. Mat contours;
  6. Canny(I,contours,125,350);
  7. threshold(contours,contours,128,255,THRESH_BINARY);
  8. namedWindow("Canny");
  9. imshow("Canny",contours);
  10. waitKey();
  11. return 0;
  12. }

显示效果如下:

二、直线检测

直线在图像中出现的频率非常之高,而直线作为图像的特征对于基本内容的图像分析有着很重要的作用,本文通过OpenCV中的hough变换来检测图像中的线条。

我们先看最基本的Hough变换函数HoughLines,它的原型如下:

 
  1. void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 );

它的输入是一个二值的轮廓图像,往往是边缘检测得到的结果图像;它的输出是一个包含多个Vec2f点的数组,数组中的每个元素是一个二元浮点数据 对<rou,theta>,rou代表直线离坐标原点的距离,theta代表角度。第3和第4个参数代表步长,因为Hough变换实际上是一 个穷举的算法,rho表示距离的步长,theta代表角度的步长。第5个参数是一个阈值设置直接的最低投票个数,知道Hough原理的,这个参数应该很容 易理解。

从 这个函数的输出结果我们可以看出,得到的直线并没有指定在图像中的开始点与结束点,需要我们自己去计算,如果我们想把直接显示在图像中就会比较麻烦,而且 会有很多角度接近的直线,其实它们是重复的,为了解决上面这些问题,OpenCV又提供了一个函数HoughLinesP()。它的输出是一个 Vector of Vec4i。Vector每一个元素代表一条直线,是由一个4元浮点数组构成,前两个点一组,后两个点一组,代表了在图像中直线的起始和结束点。

 
  1. void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta,int threshold, double minLineLength=0, double maxLineGap=0 );

解释一下最后两个参数,minLineLength指定了检测直线中的最小宽度,如果低于最小宽度则舍弃掉,maxLineGap指定通过同一点的直线,如果距离小于maxLineGap就会进行合并。

下面是一个用HoughLinesP检测直线的例子:

 
  1. int main()
  2. {
  3. Mat image=imread("../car.png");
  4. Mat I;
  5. cvtColor(image,I,CV_BGR2GRAY);
  6. Mat contours;
  7. Canny(I,contours,125,350);
  8. threshold(contours,contours,128,255,THRESH_BINARY);
  9. vector<Vec4i> lines;
  10. // 检测直线,最小投票为90,线条不短于50,间隙不小于10
  11. HoughLinesP(contours,lines,1,CV_PI/180,80,50,10);
  12. drawDetectLines(image,lines,Scalar(0,255,0));
  13. namedWindow("Lines");
  14. imshow("Lines",image);
  15. waitKey();
  16. return 0;
  17. }

上面程序将检测到的线条保存在lines变量内,我们需要进一步将它们画在图像上:

 
  1. void drawDetectLines(Mat& image,const vector<Vec4i>& lines,Scalar & color)
  2. {
  3. // 将检测到的直线在图上画出来
  4. vector<Vec4i>::const_iterator it=lines.begin();
  5. while(it!=lines.end())
  6. {
  7. Point pt1((*it)[0],(*it)[1]);
  8. Point pt2((*it)[2],(*it)[3]);
  9. line(image,pt1,pt2,color,2); //  线条宽度设置为2
  10. ++it;
  11. }
  12. }

实 际上Hough变换可以检测很多固定的形状,比如:圆、正方形等。它们的原理基本相同,都是构造一个投票矩阵。OpenCV里提供了检测圆的函数 HoughCircles,它的输出是一个Vector of Vec3i,Vector的每个元素包含了3个浮点数,前2个是圆的中心坐标,最后一个是半径。

三、轮廓的提取与描述

在目标识别中我们首先要把感兴趣的目标提取出来,而一般常见的步骤都是通过颜色或纹理提取出目标的前景图(一幅黑白图像,目标以白色显示在图像中),接下来我们要对前景图进行分析进一步地把目标提取出来,而这里常常用到的就是提取目标的轮廓。

OpenCV 里提取目标轮廓的函数是findContours,它的输入图像是一幅二值图像,输出的是每一个连通区域的轮廓点的集 合:vector<vector<Point>>。外层vector的size代表了图像中轮廓的个数,里面vector的 size代表了轮廓上点的个数。下面我们通过实例来看函数的用法。

 
  1. int main()
  2. {
  3. using namespace cv;
  4. Mat image=imread("../shape.png");
  5. cvtColor(image,image,CV_BGR2GRAY);
  6. vector<vector<Point>> contours;
  7. // find
  8. findContours(image,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);
  9. // draw
  10. Mat result(image.size(),CV_8U,Scalar(0));
  11. drawContours(result,contours,-1,Scalar(255),2);
  12. namedWindow("contours");
  13. imshow("contours",result);
  14. waitKey();
  15. return 0;
  16. }

上面程序中包含了2个函数,第一个是查找轮廓函数,它的第三个参数说明查找轮廓的类型,这里我们使用的是外轮廓,还可以查找所有轮廓,即包括一些孔洞的部 分,像图像人物胳膊与腰间形成的轮廓。第4个参数说明了轮廓表示的方法,程序中的参数说明轮廓包括了所有点,也可以用其他参数让有点直线的地方,只保存直 线起始与终点的位置点,具体参数用法可以参考手册里函数的介绍。

第二个函数drawContours是一个画轮廓的函数,它的第3个参数程序里设置-1表示所有的轮廓都画,你也可以指定要画的轮廓的序号。

提取到轮廓后,其实我们更关心的是如果把这些轮廓转换为可以利用的特征,也就是涉及到轮廓的描述问题,这时就有多种方法可以选择,比如矢量化为多边形、矩形、椭圆等。OpenCV里提供了一些这样的函数。

 
  1. // 轮廓表示为一个矩形
  2. Rect r = boundingRect(Mat(contours[0]));
  3. rectangle(result, r, Scalar(255), 2);
  4. // 轮廓表示为一个圆
  5. float radius;
  6. Point2f center;
  7. minEnclosingCircle(Mat(contours[1]), center, radius);
  8. circle(result, Point(center), static_cast<int>(radius), Scalar(255), 2);
  9. // 轮廓表示为一个多边形
  10. vector<Point> poly;
  11. approxPolyDP(Mat(contours[2]), poly, 5, true);
  12. vector<Point>::const_iterator itp = poly.begin();
  13. while (itp != (poly.end() - 1))
  14. {
  15. line(result, *itp, *(itp + 1), Scalar(255), 2);
  16. ++itp;
  17. }
  18. line(result, *itp, *(poly.begin()), Scalar(255), 2);
  19. // 轮廓表示为凸多边形
  20. vector<Point> hull;
  21. convexHull(Mat(contours[3]), hull);
  22. vector<Point>::const_iterator ith = hull.begin();
  23. while (ith != (hull.end() - 1))
  24. {
  25. line(result, *ith, *(ith + 1), Scalar(255), 2);
  26. ++ith;
  27. }
  28. line(result, *ith, *(hull.begin()), Scalar(255), 2);

程序中我们依次画了矩形、圆、多边形和凸多边形。最终效果如下:

对连通区域的分析到此远远没有结束,我们可以进一步计算每一个连通区域的其他属性,比如:重心、中心矩等特征,这些内容以后有机会展开来写。

以 下几个函数可以尝试:minAreaRect:计算一个最小面积的外接矩形,contourArea可以计算轮廓内连通区域的面 积;pointPolygenTest可以用来判断一个点是否在一个多边形内。mathShapes可以比较两个形状的相似性,相当有用的一个函数。

原文出处:http://ronny.blog.51cto.com/8801997/1394139

【OpenCV】直线、轮廓的提取与描述相关推荐

  1. OpenCV成长之路:直线、轮廓的提取与描述

    http://ronny.blog.51cto.com/8801997/1394139 OpenCV成长之路:直线.轮廓的提取与描述 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 . ...

  2. OPENCV之寻找并绘制轮廓以及提取轮廓重心坐标

    OPENCV之寻找并绘制轮廓以及提取轮廓重心坐标 1.寻找轮廓 声明:在寻找图像轮廓之前需要对图像进行阈值分割或者Canny.拉普拉斯等边缘检测算子处理. 寻找轮廓的算子: findContours( ...

  3. 《OpenCv视觉之眼》Python图像处理十四 :Opencv图像轮廓提取之Scharr算法和Canny算法

    本专栏主要介绍如果通过OpenCv-Python进行图像处理,通过原理理解OpenCv-Python的函数处理原型,在具体情况中,针对不同的图像进行不同等级的.不同方法的处理,以达到对图像进行去噪.锐 ...

  4. 使用Opencv进行轮廓检测,字符提取,简单的直方图字符识别!

    一.使用Opencv进行轮廓检测! 所需函数: 1. cvFindContours 函数功能:从二值图像中检索轮廓,并返回检测到的轮廓的个数 函数原型: int)  cvFindContours( C ...

  5. 《OpenCv视觉之眼》Python图像处理十二 :Opencv图像轮廓提取之基于一阶导数的Roberts算法、Prewitt算法及Sobel算法

    本专栏主要介绍如果通过OpenCv-Python进行图像处理,通过原理理解OpenCv-Python的函数处理原型,在具体情况中,针对不同的图像进行不同等级的.不同方法的处理,以达到对图像进行去噪.锐 ...

  6. OPENCV图像轮廓检测

    前面在图像转换的时候学到canny算子,可以检测出图像的轮廓信息,但是,该算子检测到的轮廓信息还需要我们手动的用眼睛去识别,而实际工程应用中,我们需要得到轮廓的具体数学信息,这就涉及到今天的主题,图像 ...

  7. 基于OpencV的轮廓填充算法在3D打印机中的应用

    在这之前,我们需要了解一下SLC文件的格式,只有对格式有一点了解,我们才能做接下来的工作,首先SLC文件中是通过描述各层中的多段线来描述整个模型的,多段线之间两两相连.对单个轮廓来说,最后一点必须等于 ...

  8. Python+Opencv身份证号码区域提取及识别实现

    前端时间智能信息处理实训,我选择的课题为身份证号码识别,对中华人民共和国公民身份证进行识别,提取并识别其中的身份证号码,将身份证号码识别为字符串的形式输出.现在实训结束了将代码发布出来供大家参考,识别 ...

  9. opencv 基础(6):基于OpenCV的轮廓检测

    利用轮廓检测,我们可以检测出目标的边界,并容易地定位.它通常是许多有趣应用,如图像前景提取,简单图像分割,检测和识别. 轮廓线在计算机视觉中的应用 一些非常酷的应用程序已经建立,使用轮廓进行运动检测或 ...

最新文章

  1. 排除一例电脑启动故障
  2. 邮件回复功能失效 谁遇到过?
  3. 如何从零开始开发一款嵌入式产品(20年的嵌入式经验分享学习,来自STM32神舟系列开发板设计师的总结)
  4. spring mvc学习(49):返回json数据
  5. 双目密集匹配的一般过程
  6. python原理及代码_lightgbm原理以及Python代码
  7. 支付宝将砸十亿支持中国女足发展:她们才是第一女子天团
  8. This is why you don’t think you’re creative 你为什么会觉得自己没有创造力?
  9. linux重定向文件容加时间,linux – Shell重定向和文件I / O持续时间
  10. 单片机基础学-按键篇
  11. c语言程序书写时每行必须有行号,C语言阶段复习一.doc
  12. 某热门单击手游lua解密.md
  13. 肯耐珂萨java_站在尤里奇的肩膀上,肯耐珂萨探讨适用于国内的HR胜任力模型
  14. [NOIp2015]推销员
  15. CoordinatorLayout布局和自定义Behavior
  16. 前端开发技术-promise是什么?
  17. 欧几里得算法 辗转相除法 C语言
  18. 一篇很完整的元器件选型指南
  19. 第063讲: 论一只爬虫的自我修养11:Scrapy框架之初窥门径 | 学习记录(小甲鱼零基础入门学习Python)
  20. 你真的了解 defer 吗?(二)

热门文章

  1. ABP.VNEXT框架系列之一:第一个ABP VNEXT 应用
  2. 从云端到球场—从智能化数据分析看足球如何演化
  3. C++ —— 调用微软语音接口(SAPI),实现文字转语音播放
  4. 【Tableau server 日常维护14】清理Tableau server日志和临时文件
  5. Arduino-ESP8266 HC-SR04 用云平台读取雷达测距数据
  6. 使用python写一个选股策略
  7. 脚本录制软件python 按键精灵 tc_键鼠录制工具(KeymouseGo)
  8. 【基于RFID的门禁系统】
  9. 深度学习(三)denoise autoencoder的Python实现
  10. Windows10 ms-settings的设置界面跳转的几种方式