这个系列的目的是通过对OpenCV示例,进一步了解OpenCV函数的使用,不涉及具体原理。
示例代码地址:http://docs.opencv.org/3.0.0/examples.html(安装openCV时可框选)
 
目录
简介
Example运行截图
Example分析
Example代码
 
简介

本文记录了对OpenCV示例contours2.cpp的分析。

资料地址:http://docs.opencv.org/3.0.0/d0/d2a/contours2_8cpp-example.html

 
这个示例主要演示了如何使用findContours 对图像进行轮廓检测。
示例涉及到findContours ,approxPolyDP,drawContours,createTrackbar,和on_trackbar等四个函数的使用;

1.findContours函数轮廓检测函数原型:void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point());

 
参数说明:
image:输入图像必须为一个2值单通道图像
contours:检测的轮廓数组,每一个轮廓用一个point类型的vector表示
hiararchy:和轮廓个数相同,每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ]               
      [0 ] ~hierarchy[ i ][ 3 ],分别表示后一个轮廓、前一个轮廓、父轮廓、内
      嵌轮廓的索引编号,如果没有对应项,该值设置为负数。
mode:表示轮廓的检索模式    
    RETR_EXTERNAL表示只检测外轮廓    
    RETR_LIST检测的轮廓不建立等级关系    
    RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边 界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。    
    RETR_TREE建立一个等级树结构的轮廓。具体参考contours.c这个demo
method:为轮廓的近似办法
     CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
     CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
     CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
offset:代表轮廓点的偏移量,可以设置为任意值。对ROI图像中找出的轮廓,并要在整个图像中进行分析时,这个参数还是很有用的。
PS:findContours后会对输入的2值图像改变,所以如果不想改变该2值图像,需创建新mat来存放。
以上描述摘至参考资料3

 
2.approxPolyDP
点集逼近
 

函数原型:

void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed);

参数说明:

InputArray curve:输入的点集
OutputArray approxCurve:输出的点集,当前点集是能最小包容指定点集的。draw出来即是一个多边形;
double epsilon:指定的精度,也即是原始曲线与近似曲线之间的最大距离。
bool closed:若为true,则说明近似曲线是闭合的,它的首位都是相连,反之,若为false,则断开。

PS:findContours后的轮廓信息contours可能过于复杂不平滑,可以用approxPolyDP函数对该多边形曲线做适当近似。
以上描述摘至参考资料4  
 
3.drawContours
绘制轮廓
 
函数原型:
void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, int thickness=1, int lineType=8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point() )
 
参数说明:
InputOutputArray image:要绘制轮廓的图像
InputArrayOfArrays contours:所有输入的轮廓,每个轮廓被保存成一个point向量
int contourIdx:指定要绘制轮廓的编号,如果是负数,则绘制所有的轮廓
const Scalar& color:绘制轮廓所用的颜色
int thickness=1:绘制轮廓的线的粗细,如果是负数,则轮廓内部被填充
int lineType=8:绘制轮廓的线的连通性
InputArray hierarchy=noArray():关于层级的可选参数,只有绘制部分轮廓时才会用到
int maxLevel=INT_MAX:绘制轮廓的最高级别,这个参数只有hierarchy有效的时候才有效

maxLevel=0,绘制与输入轮廓属于同一等级的所有轮廓即输入轮廓和与其相邻的轮廓
                                           maxLevel=1, 绘制与输入轮廓同一等级的所有轮廓与其子节点。
                                           maxLevel=2,绘制与输入轮廓同一等级的所有轮廓与其子节点以及子节点的子节点

Point offset=Point():

PS:findContours()运行的时候,这个图像会被直接涂改,因此如果是将来还有用的图像,应该复制之后再传给findContours()。

以上描述摘至参考资料5

 
4.createTrackbar
窗口中快速创建一个滑动控件,用于手动调节阈值,具有非常直观的效果。
函数原型:
int createTrackbar(const string& trackbarname, const string& winname,
                              int* value, int count,
                              TrackbarCallback onChange = 0,
                              void* userdata = 0);
 
参数说明:

trackbarname:滑动空间的名称;

winname:滑动空间用于依附的图像窗口的名称;

value:初始化阈值;

count:滑动控件的刻度范围;

TrackbarCallback是回调函数。

 
5.TrackbarCallback
createTrackbar调用的回调函数,用于响应滑动条的交互消息。
 
函数原型:
typedef void (CV_CDECL *TrackbarCallback)(int pos, void* userdata);  
 
参数说明:
pos:滑动条当前位置;
userdata:调用滑动条传递的数据。
 
PS:事实上pos和userdata用得非常少,更多的时候直接使用全局变量完成参数传递。
Example运行截图
 原图
 
 
 效果图

取消approxPolyDP调用

Example分析

1.申明需要使用的变量

const int w = 500;
int levels = 3;
 
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
 
注意:
(1)w:图像的宽度和高度
(2)levels:绘制轮廓的级别和数量
(3)contours:保存轮廓
(4)hierarchy:层级参数
 
2.主函数
2.1创建一个灰度图像
Mat img = Mat::zeros(w, w, CV_8UC1);
 
2.2.绘制6个人脸用于测试
  1. for( int i = ; i < ; i++ )
  2.     {
  3.         int dx = (i%)* - ;
  4.         int dy = (i/)*;
  5.         const Scalar white = Scalar();
  6.         const Scalar black = Scalar();
  7.  
  8.         if( i ==  )
  9.         {
  10.             for( int j = ; j <= ; j++ )
  11.             {
  12.                 double angle = (j+)*CV_PI/;
  13.                 line(img, Point(cvRound(dx++j*-*cos(angle)),
  14.                     cvRound(dy+-*sin(angle))),
  15.                     Point(cvRound(dx++j*-*cos(angle)),
  16.                     cvRound(dy+-*sin(angle))), white, , , );
  17.             }
  18.         }
  19.  
  20.         ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, ,  );
  21.         ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
  22.         ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
  23.         ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, ,  );
  24.         ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, ,  );
  25.         ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
  26.         ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
  27.         ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
  28.         ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
  29.         ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, ,  );
  30.         ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, ,  );
  31.     }

2.3.创建显示源图像的窗口

namedWindow( "image", 1 );
 
2.4.显示源图像
imshow( "image", img );
 
2.5.查找轮廓
vector<vector<Point> > contours0;
findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
 
2.6.对轮廓进行逼近
contours.resize(contours0.size());
for( size_t k = 0; k < contours0.size(); k++ )
      approxPolyDP(Mat(contours0[k]), contours[k], 3, true);
 
2.7.创建显示目标图像(绘制轮廓图像)的窗口
namedWindow( "contours", 1 );
 
2.8.为目标图像预览窗口创建滑动条
createTrackbar( "levels+3", "contours", &levels, 7, on_trackbar );
 
注意:
为窗口添加滑动条,在OpenCV研发中非常普遍和实用,其大致步骤如下:
step1: 参数全局变量的声明与初始化;
step2:声明存储图像的全局变量;
step3:定义响应滑动条的回调函数,根据参数全局变量,和图像全局变量,以及图像算法进行处理,并显示;
step4:创建显示图像的窗口;
step5:在函数中使用createTrackbar创建滑动条;
step6:在函数中手动调用回调函数on_trackbar 一次,用于初始化。
 
2.9.手动调用
on_trackbar(0,0);
 
2.10等待键盘事件
waitKey();
 
注意:
在OpenCV中常常使用waitKey函数,因为显示图像后,需要驻留代码。以便于观察和操作。
 
3.on_trackbar函数分析
3.1创建目标图像
Mat cnt_img = Mat::zeros(w, w, CV_8UC3);
 
3.2处理参数
int _levels = levels - 3;
 
3.3绘制轮廓
drawContours( cnt_img, contours, _levels <= 0 ? 3 : -1, Scalar(128,255,255),
                  3, LINE_AA, hierarchy, std::abs(_levels) );
 
3.4显示目标图像
imshow("contours", cnt_img);
 
Example代码
  1. #include "opencv2/imgproc/imgproc.hpp"
  2. #include "opencv2/highgui/highgui.hpp"
  3. #include <math.h>
  4. #include <iostream>
  5.  
  6. using namespace cv;
  7. using namespace std;
  8.  
  9. static void help()
  10. {
  11. cout
  12. << "\nThis program illustrates the use of findContours and drawContours\n"
  13. << "The original image is put up along with the image of drawn contours\n"
  14. << "Usage:\n"
  15. << "./contours2\n"
  16. << "\nA trackbar is put up which controls the contour level from -3 to 3\n"
  17. << endl;
  18. }
  19.  
  20. const int w = ;
  21. int levels = ;
  22.  
  23. vector<vector<Point> > contours;
  24. vector<Vec4i> hierarchy;
  25.  
  26. static void on_trackbar(int, void*)
  27. {
  28. Mat cnt_img = Mat::zeros(w, w, CV_8UC3);
  29. int _levels = levels - ;
  30. drawContours( cnt_img, contours, _levels <= ? : -, Scalar(,,),
  31. , LINE_AA, hierarchy, std::abs(_levels) );
  32.  
  33. imshow("contours", cnt_img);
  34. }
  35.  
  36. int main( int argc, char**)
  37. {
  38. Mat img = Mat::zeros(w, w, CV_8UC1);
  39. if(argc > )
  40. {
  41. help();
  42. return -;
  43. }
  44. //Draw 6 faces
  45. for( int i = ; i < ; i++ )
  46. {
  47. int dx = (i%)* - ;
  48. int dy = (i/)*;
  49. const Scalar white = Scalar();
  50. const Scalar black = Scalar();
  51.  
  52. if( i == )
  53. {
  54. for( int j = ; j <= ; j++ )
  55. {
  56. double angle = (j+)*CV_PI/;
  57. line(img, Point(cvRound(dx++j*-*cos(angle)),
  58. cvRound(dy+-*sin(angle))),
  59. Point(cvRound(dx++j*-*cos(angle)),
  60. cvRound(dy+-*sin(angle))), white, , , );
  61. }
  62. }
  63.  
  64. ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, , );
  65. ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
  66. ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
  67. ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, , );
  68. ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, , );
  69. ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
  70. ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
  71. ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
  72. ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
  73. ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, , );
  74. ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, , );
  75. }
  76. //show the faces
  77. namedWindow( "image", );
  78. imshow( "image", img );
  79. //Extract the contours so that
  80. vector<vector<Point> > contours0;
  81. findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
  82.  
  83. contours.resize(contours0.size());
  84. for( size_t k = ; k < contours0.size(); k++ )
  85. approxPolyDP(Mat(contours0[k]), contours[k], , true);
  86.  
  87. namedWindow( "contours", );
  88. createTrackbar( "levels+3", "contours", &levels, , on_trackbar );
  89.  
  90. on_trackbar(,);
  91. waitKey();
  92.  
  93. return ;
  94. }
 
参考资料:
1.《opencv 例程四 寻找轮廓》http://blog.sina.com.cn/s/blog_662c78590100z0rg.html
2.《实用OpenCV》(六) 图像中的形状(1)》http://www.2cto.com/kf/201401/270283.html
3.《findContours函数参数说明及相关函数》http://blog.sina.com.cn/s/blog_7155fb1a0101a90h.html

OpenCV示例学习笔记(1)-contours2.cpp-通过findContours 函数实现轮廓提取的更多相关文章

  1. Android NDK开发及OpenCV初步学习笔记

    https://www.jianshu.com/p/c29bb20908da Android NDK开发及OpenCV初步学习笔记 Super_圣代 关注 2017.08.19 00:55* 字数 6 ...

  2. OpenCV入门学习笔记

    OpenCV入门学习笔记 参照OpenCV中文论坛相关文档(http://www.opencv.org.cn/) 一.简介 OpenCV(Open Source Computer Vision),开源 ...

  3. OpenCV图像处理学习笔记-Day1

    OpenCV图像处理学习笔记-Day1 目录 OpenCV图像处理学习笔记-Day1 第1课:图像读入.显示和保存 1. 读入图像 2. 显示图像 3. 保存图像 第2课:图像处理入门基础 1. 基本 ...

  4. OpenCV图像处理学习笔记-Day03

    OpenCV图像处理学习笔记-Day03 目录 OpenCV图像处理学习笔记-Day03 第31课:Canny边缘检测原理 第32课:Canny函数及使用 第33课:图像金字塔-理论基础 第34课:p ...

  5. OpenCV图像处理学习笔记-Day4(完结)

    OpenCV图像处理学习笔记-Day4(完结) 第41课:使用OpenCV统计直方图 第42课:绘制OpenCV统计直方图 pass 第43课:使用掩膜的直方图 第44课:掩膜原理及演示 第45课:直 ...

  6. MYSQL学习笔记三:日期和时间函数

    MYSQL学习笔记三:日期和时间函数 1. 获取当前日期的函数和获取当前时间的函数 /*获取当前日期的函数和获取当前时间的函数.将日期以'YYYY-MM-DD'或者'YYYYMMDD'格式返回 */ ...

  7. 播放一个视频并用滚动条控制进度-OpenCV应用学习笔记二

    今天我们来做个有趣的程序实现:利用OpenCV读取本地文件夹的视频文件,并且在窗口中创建拖动控制条来显示并且控制视频文件的读取进度. 此程序调试花费了笔者近一天时间,其实大体程序都已经很快写出,结果执 ...

  8. C++ Primer学习笔记(三) C++中函数是一种类型!!!

    C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...

  9. JavaScript学习笔记(十)——高阶函数之map,reduce,filter,sort

    在学习廖雪峰前辈的JavaScript教程中,遇到了一些需要注意的点,因此作为学习笔记列出来,提醒自己注意! 如果大家有需要,欢迎访问前辈的博客https://www.liaoxuefeng.com/ ...

随机推荐

  1. CSS3 实现六边形Div图片展示效果

    原文:CSS3 实现六边形Div图片展示效果 效果图: 实现原理: 这个效果的主要css样式有: 1.>transform: rotate(120deg); 图片旋转 2.>overflo ...

  2. 水晶报表使用经验谈--使用sql语句直接生成dataset做为报表的数据源

    概述: 上一次自己做了直接在rpt文件里使用oledb连接使用数据库的方法 但是不是很灵活 这次做了使用sql语句直接生成dataset做为报表的数据源(即push模式),这样就可以接受参数了.当然报 ...

  3. jsonp总结

    由于“同源策略”的限制,ajax不能做跨域请求,jsonp是当下解决跨域请求最流行的方案,来个例子(index.html): <!doctype html> <html lang=& ...

  4. Product Trader(操盘手)

    Product Trader(操盘手) 索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Product Trader 的示例实现. 意图 使客户程序可以通过命名抽象超类和给定规 ...

  5. Step one : 熟悉HTML

    //H1 1 <html> <head> <title>BeiJing</title> </head> <body> <h ...

  6. 自定义表单-jsonform

    项目文件地址:https://github.com/powmedia/backbone-forms#editor-list DEOM:http://jsfiddle.net/evilcelery/dW ...

  7. Oracle常用操作

      比较时间 select * from up_date where update < to_date('2007-09-07 00:00:00','yyyy-mm-dd hh24:mi:ss' ...

  8. C语言变参函数的编写

    1. 引言 一般我们编程的时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的实际参数.但在某些情况下我 们希望函数的参数个数可以根据需要确定,因此c语言引入可变参数函数.典型的 ...

  9. jquery实现城市选择器效果(二级联动)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. C#中鼠标划过按钮时候的提示信息

    ToolTip p = new ToolTip();            p.ShowAlways = true;            p.SetToolTip(this.Buton1, &quo ...