在使用OpenCV的过程中,findcontours是相对使用比较多的,在之前的博客中,介绍了vector<vector<Point> > contours
容器:
http://blog.csdn.net/chaipp0607/article/details/52858661
查找轮廓时内轮廓与外轮廓:
http://blog.csdn.net/chaipp0607/article/details/53765440
再来看下findCountours的函数原型:
CV_EXPORTS_W void findContours(
InputOutputArray image,
OutputArrayOfArrays contours,
OutputArray hierarchy,
int mode,
int method,
Point offset=Point());
其中第五个参数为轮廓的边缘近似方法,说白了就是放在contours
中的一堆点,到底以一个怎样的方式把轮廓表征出来,定义如下
enum
{
CHAIN_APPROX_NONE=CV_CHAIN_APPROX_NONE,
CHAIN_APPROX_SIMPLE=CV_CHAIN_APPROX_SIMPLE,
CHAIN_APPROX_TC89_L1=CV_CHAIN_APPROX_TC89_L1,
CHAIN_APPROX_TC89_KCOS=CV_CHAIN_APPROX_TC89_KCOS
};
依次为:
1为能够包围轮廓的所有的点;
2为压缩水平的、垂直的和斜的部分,也就是,函数只保留他们的终点部分;
3,4为使用the flavors of Teh-Chin chain近似算法的一种。
我们用一个例子试一下:
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>
#include <time.h>
#include<ctime>
#include <opencv/cv.h>
#include <opencv2/opencv.hpp>
#include <stdio.h>
#include<math.h>
using namespace cv;
using namespace std;
int main()
{
Mat SrcImage;
Mat ResizeImage;
Mat grayImage;
Mat thresholdImage;
Mat roiimage;
Mat SrcroiImage;
SrcImage = imread("2_000_0.bmp");
Size dsize = Size(SrcImage.cols*0.2,SrcImage.rows*0.2);
resize(SrcImage, ResizeImage,dsize);
SrcroiImage = ResizeImage(Rect(ResizeImage.cols/3,ResizeImage.rows/3,ResizeImage.cols/3,ResizeImage.rows/3));
cvtColor(SrcroiImage,grayImage,CV_BGR2GRAY);
threshold(grayImage,thresholdImage, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);
vector<vector<Point> > contours;
vector<Vec4i>hierarchy;
findContours(thresholdImage,contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_SIMPLE);
for(int k = 0; k < (int)contours.size(); k++) //查找轮廓
{
if (contours.at(k).size()>30&&contours.at(k).size()<50)
{
drawContours(SrcroiImage, contours, k, Scalar(255,0,0), CV_FILLED);
}
if (contours.at(k).size()>50)
{
drawContours(SrcroiImage, contours, k, Scalar(0,255,0), CV_FILLED);
}
}
imshow("兴趣区域二值化",thresholdImage);
imshow("兴趣区域原图",SrcroiImage);
waitKey(0);
getchar();
return 0;
}
这是截取出来的待检测的图像,我们对它进行轮廓查找,验证一下:
首先用CV_CHAIN_APPROX_SIMPLE
的方法,在for循环中,我们把包围轮廓的点个数作为判断条件,个数大于30个且小于50个是,把这个轮廓在原图中涂成蓝色,而大于50时,涂成绿色,运行结果如下:
可以看到,中间的兴趣区域的面积明显是要大于右下角的三角形面积的,但是包围它的轮廓的点的个数确要小于三角形的区域,这是因为中间的区域非常接近于矩形,区域规则时只需要很少的点就可以将它描绘出来。
然后我们将CV_CHAIN_APPROX_SIMPLE
方法换成CV_CHAIN_APPROX_NONE
方法,在修改下判断条件:
if (contours.at(k).size()>30&&contours.at(k).size()<100)
{
drawContours(SrcroiImage, contours, k, Scalar(255,0,0), CV_FILLED);
}
if (contours.at(k).size()>100)
{
drawContours(SrcroiImage, contours, k, Scalar(0,255,0), CV_FILLED);
}
if (contours.at(k).size()>200)
{
drawContours(SrcroiImage, contours, k, Scalar(0,0,255), CV_FILLED);
}
可以看到,此时的轮廓大小大概是和视觉看上去的面积大小成正比的。
最后补充一点:
findcontours函数将二值化后图像白色区域当作前景,黑色部分当做背景。所以找轮廓找到的是白色区域的轮廓。这个函数有一个特点,如果白色区域延伸到了图像边界,那么图像的边界也是被当作轮廓的一部分,这就造成了可能会出现一个很大的外轮廓。