标签:
opencv中使用Mat进行轮廓查找的函数如下:
void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset=Point())
opencv文档地址
这个函数看起来挺简单的,我们输入一张图像,它就给我们返回相应的轮廓,但是一张图像经过不同处理,得到的轮廓可是会有非常大的不同的。
使用轮廓查找我遇到了如下的几个问题:
此处查找轮廓的步骤为:
使用的图片为一张发票图像(这张图片是从一篇新闻里边取到的,应该不涉及隐私信息吧):
二值化图像结果为:
最后画出的轮廓如下:
运行输出的轮廓数如下:
代码如下:
int main(int argc, char *argv[])
{
//载入图像,并判断图像是否为空
Mat srcImage = imread("fapiao.jpg");
if (srcImage.empty())
{
printf("Src Image is empty\n");
return -1;
}
//将图像正规化到一定大小方便查看
Size dsize = Size(800, 500);
Mat resizedSrcImage = Mat(dsize,CV_32S);
resize(srcImage, resizedSrcImage,dsize);
//转化为灰度图像
Mat grayImage;
cvtColor(resizedSrcImage, grayImage, CV_BGR2GRAY);
imshow("gray", grayImage);
//灰度图像二值化
Mat thresholdImage;
threshold(grayImage, thresholdImage, 200, 255, THRESH_BINARY);
imshow("threshold", thresholdImage);
//查找轮廓
vector<vector<Point>> contours;
findContours(thresholdImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
printf("contour size: %d\n", contours.size());
//画出轮廓
Mat result(resizedSrcImage.size(),CV_8U,Scalar(0));
drawContours(result,contours,-1,Scalar(255),1);
imshow("contours", result);
waitKey(0);
return 0;
}
问题分析:
是什么原因导致找到的轮廓居然是这样的呢?
难道是因为findContours的参数设置有误么?
代码中轮廓查找的方式为CV_RETR_EXTERNAL,它在opencv中的说明是只查找外轮廓,难道这就是原因么?
下面把CV_RETR_EXTERNAL修改为其它三种模式:
CV_RETR_LIST,CV_RETR_CCOMP,CV_RETR_TREE
用这三种模式都能得到如下的轮廓图:
问题似乎解决了,但是CV_RETR_EXTERNAL这种方式该如何得到轮廓呢?难道只能得到图片最外边缘这个轮廓么?那这种模式有什么用啊!
据我实验分析,opencv找轮廓是喜欢找的是图中的白色区域,所以上图中二值图像背景是白色导致把整张图片当做了一个大轮廓。
那么如何做修改呢?
二值化改成:
threshold(grayImage, thresholdImage, 200, 255, THRESH_BINARY_INV);
此时二值图变为:
得到的轮廓就变成如下结果啦:
这是一个挺奇怪的问题
下面代码中threshold为找轮廓前的图片,threshold2为找轮廓后的图片
imshow("threshold", thresholdImage);
//轮廓查找,并将小轮廓去掉
vector<vector<Point>> contours;
findContours(thresholdImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
printf("contour size: %d\n", contours.size());
imshow("threshold2", thresholdImage);
对比好明显哦,太可怕了,这一问题完全是想不到啊,感觉是opencv的一个陷阱。
解决办法:
给findContours的图片用clone()深拷贝一份,原图像则不传入里边。
下面是用CV_RETR_TREE找到的轮廓,我想把里边那些小子都给去掉,该怎么办呢?
其实就是遍历找到的轮廓,然后验证该轮廓的size是否大于阈值,如果不大于,则删掉这个轮廓。
我写了个函数如下:
/**
* 功能描述: 轮廓过滤
* @param 需处理的图片inputImg、轮廓的最小阈值minThreshold
*
* @return 过滤后的轮廓
*/
vector<std::vector<Point>> ocrImagePreprocess::findFilteredCountour(Mat inputImg,int minThreshold){
std::vector<std::vector<Point>> contours;
//如果直接把inputImg放进去会毁掉它的,好可怕!吓死了
Mat srcClone = inputImg.clone();
findContours(srcClone,contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
std::vector<std::vector<Point>>::const_iterator itc = contours.begin();
while(itc != contours.end())
{
if(itc->size() < minThreshold)
itc = contours.erase(itc);
else
++itc;
}
return contours;
}
处理之后轮廓如下,小轮廓被去掉,剩下的线条非常清晰:
标签:
原文地址:http://blog.csdn.net/jianjian1992/article/details/51027230