您的位置:首页 > 运维架构

【第二部分 图像处理】第3章 Opencv图像处理进阶【4 图像轮廓A】

2018-03-08 21:29 661 查看

3.1查找并绘制轮廓

1985年,satoshi suzuki发表的《Topological structural analysis of digitized binary images by border following》论文介绍了两种算法来实现轮廓的提取,当然输入的图像是二值图像。findcontour就是基于这篇论文的思路来实现。他主要介绍了两种算法,用来对数字二值图像进行拓扑分析。第一种算法是在确定二值图像边界的围绕关系,即确定外边界、孔边界以及他们的层次关系,由于这些边界和原图的区域具有一一对应关系(外边界对应像素值为1的连通区域,孔边界对应像素值为0的区域),因此我们就可以用边界来表示原图。第二种算法,是第一种算法的修改版本,本质一样,但是它只找最外面的边界。

下面就跟着笔者一起使用OpenCV查找轮廓并绘制出来。

3.1.1查找并绘制轮廓API

3.1.1.1查找并绘制轮廓函数

 寻找轮廓:findContours()函数

C++:findContours( InputOutputArray image,
OutputArrayOfArrays contours,
OutputArray hierarchy,
int mode,
int method,
Point offset=Point());


【参数】

第一个参数:image,单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;

第二个参数:contours,定义为“vector

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


 绘制轮廓:drawContours()函数

C++: void drawContours(InputOutputArray image,
InputArrayOfArrays contours,
int contourIdx,
const Scalar& color,
int thickness=1,

4000
int lineType=8,
InputArray hierarchy=noArray(),
int maxLevel=INT_MAX,
Point offset=Point() )


【参数】

第一个参数,image – 目标图像,填充Mat的对象;

第二个参数,contours – 所有的输入轮廓。每一个轮廓都存储一个点向量,即point类型的 vector表示。

第三个参数,contourIdx – 轮廓绘制的指示。如果为负值,则绘制所有轮廓。

第四个参数,color – 轮廓的颜色。

第五个参数,thickness – Thickness of lines the contours are drawn with. If it is negative (for example, thickness=CV_FILLED ), the contour interiors are drawn.

第六个参数,lineType – Line connectivity. See line() for details.

第七个参数,hierarchy – Optional information about hierarchy. It is only needed if you want to draw only some of the contours (see maxLevel ).

第八个参数,maxLevel – Maximal level for drawn contours. If it is 0, only the specified contour is drawn. If it is 1, the function draws the contour(s) and all the nested contours. If it is 2, the function draws the contours, all the nested contours, all the nested-to-nested contours, and so on. This parameter is only taken into account when there is hierarchy available.

第九个参数,offset – Optional contour shift parameter. Shift all the drawn contours by the specified .

【注】OpenCV3在thickness的每个取值去掉了“CV_”。

3.1.1.2查找并绘制轮廓函数源码

 寻找轮廓:findContours()函数

/*【findContours ( )源代码】***********************************************************
* @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)
* @源码路径:…\opencv\sources\modules\imgproc\src\ contours.cpp
* @起始行数:1705行
********************************************************************************/
void cv::findContours( InputOutputArray _image, OutputArrayOfArrays _contours,
OutputArray _hierarchy, int mode, int method, Point offset )
{
// Sanity check: output must be of type vector<vector<Point>>
CV_Assert((_contours.kind() == _InputArray::STD_VECTOR_VECTOR || _contours.kind() == _InputArray::STD_VECTOR_MAT ||
_contours.kind() == _InputArray::STD_VECTOR_UMAT));

CV_Assert(_contours.empty() || (_contours.channels() == 2 && _contours.depth() == CV_32S));

Mat image = _image.getMat();
MemStorage storage(cvCreateMemStorage());
CvMat _cimage = image;
CvSeq* _ccontours = 0;
if( _hierarchy.needed() )
_hierarchy.clear();
cvFindContours(&_cimage, storage, &_ccontours, sizeof(CvContour), mode, method, offset);
if( !_ccontours )
{
_contours.clear();
return;
}
Seq<CvSeq*> all_contours(cvTreeToNodeSeq( _ccontours, sizeof(CvSeq), storage ));
int i, total = (int)all_contours.size();
_contours.create(total, 1, 0, -1, true);
SeqIterator<CvSeq*> it = all_contours.begin();
for( i = 0; i < total; i++, ++it )
{
CvSeq* c = *it;
((CvContour*)c)->color = (int)i;
_contours.create((int)c->total, 1, CV_32SC2, i, true);
Mat ci = _contours.getMat(i);
CV_Assert( ci.isContinuous() );
cvCvtSeqToArray(c, ci.ptr());
}

if( _hierarchy.needed() )
{
_hierarchy.create(1, total, CV_32SC4, -1, true);
Vec4i* hierarchy = _hierarchy.getMat().ptr<Vec4i>();

it = all_contours.begin();
for( i = 0; i < total; i++, ++it )
{
CvSeq* c = *it;
int h_next = c->h_next ? ((CvContour*)c->h_next)->color : -1;
int h_prev = c->h_prev ? ((CvContour*)c->h_prev)->color : -1;
int v_next = c->v_next ? ((CvContour*)c->v_next)->color : -1;
int v_prev = c->v_prev ? ((CvContour*)c->v_prev)->color : -1;
hierarchy[i] = Vec4i(h_next, h_prev, v_next, v_prev);
}
}
}


/*【findContours ( )源代码】***********************************************************
* @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)
* @源码路径:…\opencv\sources\modules\imgproc\src\ contours.cpp
* @起始行数:1758行
********************************************************************************/
void cv::findContours( InputOutputArray _image, OutputArrayOfArrays _contours,
int mode, int method, Point offset)
{
findContours(_image, _contours, noArray(), mode, method, offset);
}


从源码可以看出,上面一种实现就是第一种方式的特殊实现而已。

 绘制轮廓:drawContours()函数

/*【drawContours ( )源代码】**********************************************************
* @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)
* @源码路径:…\opencv\sources\modules\imgproc\src\ drawing.cpp
* @起始行数:2275行
********************************************************************************/
void cv::drawContours( InputOutputArray _image, InputArrayOfArrays _contours,
int contourIdx, const Scalar& color, int thickness,
int lineType, InputArray _hierarchy,
int maxLevel, Point offset )
{
Mat image = _image.getMat(), hierarchy = _hierarchy.getMat();
CvMat _cimage = image;

size_t ncontours = _contours.total();
size_t i = 0, first = 0, last = ncontours;
std::vector<CvSeq> seq;
std::vector<CvSeqBlock> block;

if( !last )
return;

seq.resize(last);
block.resize(last);

for( i = first; i < last; i++ )
seq[i].first = 0;

if( contourIdx >= 0 )
{
CV_Assert( 0 <= contourIdx && contourIdx < (int)last );
first = contourIdx;
last = contourIdx + 1;
}

for( i = first; i < last; i++ )
{
Mat ci = _contours.getMat((int)i);
if( ci.empty() )
continue;
int npoints = ci.checkVector(2, CV_32S);
CV_Assert( npoints > 0 );
cvMakeSeqHeaderForArray( CV_SEQ_POLYGON, sizeof(CvSeq), sizeof(Point),
ci.ptr(), npoints, &seq[i], &block[i] );
}

if( hierarchy.empty() || maxLevel == 0 )
for( i = first; i < last; i++ )
{
seq[i].h_next = i < last-1 ? &seq[i+1] : 0;
seq[i].h_prev = i > first ? &seq[i-1] : 0;
}
else
{
size_t count = last - first;
CV_Assert(hierarchy.total() == ncontours && hierarchy.type() == CV_32SC4 );
const Vec4i* h = hierarchy.ptr<Vec4i>();

if( count == ncontours )
{
for( i = first; i < last; i++ )
{
int h_next = h[i][0], h_prev = h[i][1],
v_next = h[i][2], v_prev = h[i][3];
seq[i].h_next = (size_t)h_next < count ? &seq[h_next] : 0;
seq[i].h_prev = (size_t)h_prev < count ? &seq[h_prev] : 0;
seq[i].v_next = (size_t)v_next < count ? &seq[v_next] : 0;
seq[i].v_prev = (size_t)v_prev < count ? &seq[v_prev] : 0;
}
}
else
{
int child = h[first][2];
if( child >= 0 )
{
addChildContour(_contours, ncontours, h, child, seq, block);
seq[first].v_next = &seq[child];
}
}
}

cvDrawContours( &_cimage, &seq[first], color, color, contourIdx >= 0 ?
-maxLevel : maxLevel, thickness, lineType, offset );
}


3.1.2查找并绘制轮廓实例

 轮廓查找实例

代码参看附件【demo1】。



图1



图2

 查找并绘制轮廓综合实例

代码参看附件【demo2】。



图3

参考:

中文

英文

本章参考代码

点击进入
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐