最近通过使用opencv做标志牌检测时,涉及到了opencv库中findContours函数的调用,在对该函数使用时出现了程序崩溃的问题,花了两天的时间才解决的该问题。
下面先对findContour函数进行简单的介绍,再次介绍一下碰到findcontours导致程序崩溃时,网络上出现的一些的解决方案,最后介绍一下我自己碰到的该问题时的解决方案。
首先,在这里先简单的对findContours函数进行简单的介绍一下吧。
findContours函数,这个函数的原型为:
void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierar-
chy, int mode, int method, Point offset=Point())
参数说明:
第一个参数:输入图像image必须为一个2值单通道图像。
第二个参数:contours参数为检测的轮廓数组,每一个轮廓用一个point类型的vector表示。
第三个参数:hiararchy参数和轮廓个数相同,每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,该值设置为负数。
第四个参数:mode表示轮廓的检索模式。有以下几种模式:
1.CV_RETR_EXTERNAL表示只检测外轮廓
2.CV_RETR_LIST检测的轮廓不建立等级关系
3.CV_RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。(简单的说就是检测双层轮廓)
4.CV_RETR_TREE建立一个等级树结构的轮廓。具体参考contours.c这个demo
第五个参数:method为轮廓的近似办法,主要有以下几种:
1.CV_CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
2.CV_CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
3.CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
第六个参数:offset表示代表轮廓点的偏移量,可以设置为任意值。对ROI图像中找出的轮廓,并要在整个图像中进行分析时,这个参数还是很有用的。一般设置为默认值point(0,0).
findContours函数的调用小示例如下:
vector<vector<Point>>contours;//轮廓数组,也可以这样写 vector<Mat>contours;
findContours(Dst, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
//CV_RETR_CCOMP参数用来检测双层轮廓```
//注:在检测双层轮廓时,若只提取内轮廓时,可以使用以下语句对内轮廓进行筛选出来
语句如下:
if (hierarchy[i][3] < 0)
{
continue;
}
通过对该参数的判断就能将内轮廓筛选出来。
FindContours函数先介绍这么多了。现在看一下我这篇文章的重点吧。
我在程序中使用如下:
vector<Vec4i>hierarchy;//可以用来过滤内外轮廓
vector<vector<Point>>contours;//轮廓数组,也可以这样写 vector<Mat>contours;
findContours(Dst, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
我现在一个工程中使用该函数对单张图像进行轮廓检测,并没有出现程序崩溃问题,但是对视频中每帧图像进行轮廓检测时,在检测第三帧图像时,程序直接崩溃,将该语句屏蔽后,程序则可以正常运行并没有问题。
我的运行环境为VS2015+Opencv2.4.11
FindContours函数出现的调用异常问题如下:
错误:File: minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp
Line: 980
Expression: __acrt_first_block == header
For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.
以下是网上一些大神给出的解决方案(然而并不能解决我的问题)
方案一:
如果使用vector<vector<Point> > contours;
作为findContours的参数,在运行时会得到
Assertion failed (mtype == type0 || (CV_MAT_CN(mtype) == CV_MAT_CN(type0) && ((1 << type0) & f…
原因是标准库里有std::vector 和 Point 和findContours里要用到的vector和Point不是一回事所以,声明的时候要用cv::vector和cv::Point就可以了。
方案二:
“修改了当前程序的vc运行库配置,问题解决。具体方法是:项目-属性-配置属性-C/C++-代码生成-运行库,将其改为“多线程调试(/MTd)”。”
方案三:
在配置属性->常规->MFC的使用中,将在静态库中使用MFC改为在共享DLL中使用MFC。
方案四:
将程序改为:
vector<Mat> contours(100);
Mat hierarchy;
findContours( BW, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE );
方案五:
当一个DLL采用静态的方式链接到C运行时库时,会创建一个相对于该DLL的堆(Heap),而如果采用共享的方式链接到C运行时库的时候则使用的是应用程序的堆内存。而_CrtIsValidHeapPointer()在 DEBug模式下将确保传入的地址在本地的堆内存中。 因此就有理由相信,真有可能是静态链接的问题。所以,我立即尝试将:
项目–属性–配置属性–常规–MFC的使用– 选择在共享DLL中使用MFC ;同时,
项目–属性–配置属性–C/C++–代码生成–运行库–选择 多线程DLL(/MD)。
方案六:
方案七:
通过以上可以看出,网络中从来都不缺少该领域的大神,给出的方案也是非常之多,然而并不能解决我的问题。
以下是我对于我自己程序出现的问题给出的解决方案:
通过对出现的错误:__acrt_first_block == header可以大致的知道是堆内存出现的问题,堆区一般都是用来申请分配动态数组时才会使用,而申请动态数组用的最多的就是使用关键字new[]进行申请分配。而我在程序中并未使用new,哪来的堆区的使用呢,通过查找资料了解到vector可以动态分配内存,因此问题极可能就出现在这上面。通过查阅资料了解到是vector析构异常导致的问题,可以借鉴这篇文章看一下http://www.aiuxian.com/article/p-1722238.html。原文部分如下:
大概是因为 dll 如果静态链接了运行时库,dll 就会拥有独立于应用程序堆(也称作local heap)的运行时堆实例。此时在 dll 外部就不能访问此 local heap,所以也就有上面所出现的异常啦。MSDN 中也有介绍:
The _CrtIsValidHeapPointer function is used to ensure that a specific memory address is within the local heap. The local heap refers to the heap created and managed by a particular instance of the C run-time library. If a dynamic-link library (DLL) contains a static link to the run-time library, it has its own instance of the run-time heap, and therefore its own heap, independent of the application’s local heap. When _DEBUG is not defined, calls to _CrtIsValidHeapPointer are removed during preprocessing.
程序崩溃在当析构一个带有vector成员函数对象的时候,在析构vector时,会出现这个错误,大致原因是因为析构的时候找不到vector分配的空间。
一行一行查看代码发现,对象里面的points2, status等vector变量是在calcOpticalFlowPyrLK(img1, img2, points1, points2, status, similarity, window_size, level, term_criteria, lambda, 0); 函数中分配的,即opencv的dll,所以当对象进行析构的时候,因为不能访问此local heap所以会有异常崩溃。
我的解决方法:
在调用opencv的函数之前,自己进行空间的分配。
通过阅读该片文章之后,了解到vector 析构异常 opencv Assert _CrtIsValidHeapPointer,只要在调用opencv的函数之前,自己进行空间的手动分配。于是,我对程序进行部分修改,修改程序如下:
vector<Vec4i>hierarchy(10000);
vector<Mat>contours(10000);//手动分配内存空间大小
findContours(Dst, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
//注意,内存空间不已设置过大,否则也会导致程序崩溃.**
至此,我的程序中调用findContours函数时出现的程序崩溃问题已经完美地解决。虽然这次bug调试花了两天多的时间才解决,但是,让我知道对于程序出现同类问题,网上给出的方案不一定能解决你碰到的问题,还是要学会具体问题具体分析,不能将网上的方案进行生搬硬套,这样对于你解决问题是没有任何帮助的。最重要的是对于出现问题以及对于问题的解决方案要养成记录的好习惯,这样不仅可以在网络上实现交流共享,有助于他人学习借鉴,还可以有助于我们下次碰到该类问题时,能快速的解决问题。