您的位置:首页 > 编程语言 > C语言/C++

OpenCV示例学习笔记(1)-contours2.cpp-通过findContours 函数实现轮廓提取

2017-01-12 20:29 1011 查看
这个系列的目的是通过对OpenCV示例,进一步了解OpenCV函数的使用,不涉及具体原理。
示例代码地址:http://docs.opencv.org/3.0.0/examples.html(安装openCV时可框选)

目录
简介
Example运行截图
Example分析
Example代码

[b]简介[/b]
本文记录了对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运行截图
原图

#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 = 500;
int levels = 3;

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 - 3;
drawContours( cnt_img, contours, _levels <= 0 ? 3 : -1, Scalar(128,255,255),
3, 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 > 1)
{
help();
return -1;
}
//Draw 6 faces
for( int i = 0; i < 6; i++ )
{
int dx = (i%2)*250 - 30;
int dy = (i/2)*150;
const Scalar white = Scalar(255);
const Scalar black = Scalar(0);

if( i == 0 )
{
for( int j = 0; j <= 10; j++ )
{
double angle = (j+5)*CV_PI/21;
line(img, Point(cvRound(dx+100+j*10-80*cos(angle)),
cvRound(dy+100-90*sin(angle))),
Point(cvRound(dx+100+j*10-30*cos(angle)),
cvRound(dy+100-30*sin(angle))), white, 1, 8, 0);
}
}

ellipse( img, Point(dx+150, dy+100), Size(100,70), 0, 0, 360, white, -1, 8, 0 );
ellipse( img, Point(dx+115, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 );
ellipse( img, Point(dx+185, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 );
ellipse( img, Point(dx+115, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 );
ellipse( img, Point(dx+185, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 );
ellipse( img, Point(dx+115, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 );
ellipse( img, Point(dx+185, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 );
ellipse( img, Point(dx+150, dy+100), Size(10,5), 0, 0, 360, black, -1, 8, 0 );
ellipse( img, Point(dx+150, dy+150), Size(40,10), 0, 0, 360, black, -1, 8, 0 );
ellipse( img, Point(dx+27, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 );
ellipse( img, Point(dx+273, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 );
}
//show the faces
namedWindow( "image", 1 );
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 = 0; k < contours0.size(); k++ )
approxPolyDP(Mat(contours0[k]), contours[k], 3, true);

namedWindow( "contours", 1 );
createTrackbar( "levels+3", "contours", &levels, 7, on_trackbar );

on_trackbar(0,0);
waitKey();

return 0;
}


View Code

参考资料:
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
4.《opencv中的approxPolyDP函数和boundingRect函数说明
5.《OPENCV轮廓提取findContours和drawContours
6.《createTrackbar使用方法及步骤
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: