这个系列的目的是通过对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个人脸用于测试
for( int i = ; i < ; i++ )
    {
        int dx = (i%)* - ;
        int dy = (i/)*;
        const Scalar white = Scalar();
        const Scalar black = Scalar();
 
        if( i ==  )
        {
            for( int j = ; j <= ; j++ )
            {
                double angle = (j+)*CV_PI/;
                line(img, Point(cvRound(dx++j*-*cos(angle)),
                    cvRound(dy+-*sin(angle))),
                    Point(cvRound(dx++j*-*cos(angle)),
                    cvRound(dy+-*sin(angle))), white, , , );
            }
        }
 
        ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, ,  );
        ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
        ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
        ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, ,  );
        ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, ,  );
        ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
        ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
        ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
        ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, ,  );
        ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, ,  );
        ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, ,  );
    }

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代码
 #include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <math.h>
#include <iostream> using namespace cv;
using namespace std; static void help()
{
cout
<< "\nThis program illustrates the use of findContours and drawContours\n"
<< "The original image is put up along with the image of drawn contours\n"
<< "Usage:\n"
<< "./contours2\n"
<< "\nA trackbar is put up which controls the contour level from -3 to 3\n"
<< endl;
} const int w = ;
int levels = ; vector<vector<Point> > contours;
vector<Vec4i> hierarchy; static void on_trackbar(int, void*)
{
Mat cnt_img = Mat::zeros(w, w, CV_8UC3);
int _levels = levels - ;
drawContours( cnt_img, contours, _levels <= ? : -, Scalar(,,),
, LINE_AA, hierarchy, std::abs(_levels) ); imshow("contours", cnt_img);
} int main( int argc, char**)
{
Mat img = Mat::zeros(w, w, CV_8UC1);
if(argc > )
{
help();
return -;
}
//Draw 6 faces
for( int i = ; i < ; i++ )
{
int dx = (i%)* - ;
int dy = (i/)*;
const Scalar white = Scalar();
const Scalar black = Scalar(); if( i == )
{
for( int j = ; j <= ; j++ )
{
double angle = (j+)*CV_PI/;
line(img, Point(cvRound(dx++j*-*cos(angle)),
cvRound(dy+-*sin(angle))),
Point(cvRound(dx++j*-*cos(angle)),
cvRound(dy+-*sin(angle))), white, , , );
}
} ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, , );
ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, , );
ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, , );
ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
ellipse( img, Point(dx+, dy+), Size(,), , , , black, -, , );
ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, , );
ellipse( img, Point(dx+, dy+), Size(,), , , , white, -, , );
}
//show the faces
namedWindow( "image", );
imshow( "image", img );
//Extract the contours so that
vector<vector<Point> > contours0;
findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE); contours.resize(contours0.size());
for( size_t k = ; k < contours0.size(); k++ )
approxPolyDP(Mat(contours0[k]), contours[k], , true); namedWindow( "contours", );
createTrackbar( "levels+3", "contours", &levels, , on_trackbar ); on_trackbar(,);
waitKey(); return ;
}
 
参考资料:
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. Linux:最终用途cat命令查看不可见的字符

    常,的程序或软件,并没有语法错误,你检查其内容没有发现相关问题.这是时间,因为你用普通的文本编辑软件来查看,有很多的字符显示不出来的,但在最终用途cat命令可以很easy地检測出是否存在这些字符. ~ ...

  2. IOC 容器初始化

    WebApi 插件式构建方案:IOC 容器初始化 一般来说,一个现代化的网站加载流程是这样的:程序集加载后,我们会初始化 IOC 容器,以便于接下来解析对象用. 我们插件式的开发,这一步更为重要.这是 ...

  3. Web Service单元测试工具实例介绍之SoapUI

    原文  Web Service单元测试工具实例介绍之SoapUI SoapUI是当前比较简单实用的开源Web Service测试工具,提供桌面应用程序和IDE插件程序两种使用方式.能够快速构建项目和组 ...

  4. CentOS上部署Apache、MySQL和PHP

    centos上yum安装很方便,下面介绍编译安装的方式. 第一步要在CentOS上安装gcc.g++等开发工具 可以从系统光盘上安装,或者 #yum groupinstall "Develo ...

  5. Windows在结构FTPserver

    同Windows8 案件,结构介绍 FTPserver脚步: 1.为Windows开启FTP功能:控制面板->程序->启用或关闭Windows功能.将下图所看到的的复选框选中 waterm ...

  6. leetcode第三题--Longest Substring Without Repeating Characters

    Problem:Given a string, find the length of the longest substring without repeating characters. For e ...

  7. Android-异步图像装载机

    在ListView加载图像是非常常见的场景,图像加载几个要求满足以下的: (1)是否画面位于网络或本地上,装载不应同步.但应该异步加载,例如,使用AsyncTask. (2)为了避免重复下载图片和网页 ...

  8. rem测试用实现移动端自适应页面

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

  9. unique_ptr简谈

    看到文章里的同学留言说到unique_ptr,这两天看了一下cplusplus提供的reference才知道这个东西是c++11的新特性,对c++11的新特性不是很了解,花时间了解了下unique_p ...

  10. Mysql 嵌套游标添以及任意位置声明变量的方法

    在写存储过程的时候,会遇到某个游标的筛选条件来自于 先前语句运行的结果,比较常见的方式是 再写一个存储过程,通过调用来完成 动态参数的配置, 或者使用 动态sql的功能,而这两种方式都不能很好的解决这 ...