轮廓查找问题小记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jianjian1992/article/details/51027230

opencv中使用Mat进行轮廓查找的函数如下:
void findContours(InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset=Point())
opencv文档地址
这个函数看起来挺简单的,我们输入一张图像,它就给我们返回相应的轮廓,但是一张图像经过不同处理,得到的轮廓可是会有非常大的不同的。

使用轮廓查找我遇到了如下的几个问题:

1.CV_RETR_EXTERNAL下找到的轮廓居然是整张图像边缘

此处查找轮廓的步骤为:

  1. 输入图像
  2. 将图像转为gray
  3. 将gray图像转为二值图像
  4. 将二值图像作为findContours输入
  5. 画出轮廓

使用的图片为一张发票图像(这张图片是从一篇新闻里边取到的,应该不涉及隐私信息吧):
这里写图片描述
二值化图像结果为:
这里写图片描述
最后画出的轮廓如下:
这里写图片描述
运行输出的轮廓数如下:
这里写图片描述
代码如下:

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);

此时二值图变为:
这里写图片描述
得到的轮廓就变成如下结果啦:
这里写图片描述

2.输入给findContours的图片被破坏了

这是一个挺奇怪的问题

下面代码中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()深拷贝一份,原图像则不传入里边。

扫描二维码关注公众号,回复: 3767001 查看本文章

3.过滤小轮廓

下面是用CV_RETR_TREE找到的轮廓,我想把里边那些小子都给去掉,该怎么办呢?
这里写图片描述
其实就是遍历找到的轮廓,然后验证该轮廓的size是否大于阈值,如果不大于,则删掉这个轮廓。
我写了个函数如下:

/** 
 *  功能描述: 轮廓过滤
 *  @param 需处理的图片inputImg、轮廓的最小阈值minThreshold
 *   
 *  @return  过滤后的轮廓
 */ 
vector<std::vector<Point>> 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;
}  

处理之后轮廓如下,小轮廓被去掉,剩下的线条非常清晰:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/jianjian1992/article/details/51027230