关于OpenCv中使用Findcontours时所引发的程序崩溃问题(Debug assertion Failed)

最近在windows平台使用OpenCV的findContours函数时,会出现崩溃问题。
如显示is_block_type_valid等。

在网上搜了很多解决方案,比如:
1.配置属性->常规->项目默认值->MFC的使用->在共享DLL中使用MFC;
2.C/C++>代码生成->运行库->多线程DLL(/MD);
3.代码中vector要使用cv::vector,vector<vector>要事先分配空间;

我就不挨着列举了,相信很多小伙伴使用上述方法根本无法解决问题。或者个别小伙伴可以碰巧把问题解决,但程序的移植性肯定会存在风险。

接下来,我会提供一个真正能解决这个问题的方案。

问题原因

我们要明白,为什么findContours会出现异常。
我们来看findContours的源代码:

void cv::findContours( InputOutputArray _image, OutputArrayOfArrays _contours,
                   OutputArray _hierarchy, int mode, int method, Point offset )
{
    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.data);
    }
if( _hierarchy.needed() )
{
    _hierarchy.create(1, total, CV_32SC4, -1, true);
    Vec4i* hierarchy = _hierarchy.getMat().ptr&lt;Vec4i&gt;();

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

}

首先,我们可以看到,findContours内部其实调用的是cvFindContours这个函数,只不过findContours把输入输出数据结构重新封装了一遍而已。
在findContours使用异常的情况下,我测试使用cvFindContours,发现程序一切正常。这就给我增加了极大的信心。
然后,我们再仔细阅读findContours的函数,会发现以下代码:

int i, total = (int)all_contours.size();
_contours.create(total, 1, 0, -1, true);

 
 
  • 1
  • 2

以及

 CvSeq* c = *it;
((CvContour*)c)->color = (int)i;
_contours.create((int)c->total, 1, CV_32SC2, i, true);
Mat ci = _contours.getMat(i);

cvCvtSeqToArray(c, ci.data);

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们在使用findContours的时候习惯将_contours参数写作vector<vector>类型。
但从上述代码中我们发现,opencv在分配_contours内存空间的时候,只是粗暴地理解成动态创建数组。
尤其是在赋值点集数据的时候,opencv先将_contours中的每一个vector当做Mat数据结构,然后直接粗暴地进行数据填充。

综上,我猜测,要么是_contours.create()未必适合vector的内存分配,要么是vector的数据不能简单地理解为连续内存空间,并进行简单的数据拷贝填充。
最终导致内存被破坏,运行发生异常。

解决办法

前面说了,虽然findContours使用异常,但是cvFindContours使用是完全OK的。
那么我们重新利用cvFindContours进行一次封装,规避掉前面提到的存在风险的内存操作不就行了?

废话不多说,直接贴代码。拿好不谢!

void findContours(const Mat& src, vector<vector<Point>>& contours, vector<Vec4i>& hierarchy,
	int retr = RETR_LIST, int method = CHAIN_APPROX_SIMPLE, Point offset = Point(0, 0))
{
	CvMat c_image = src;
	MemStorage storage(cvCreateMemStorage());
	CvSeq* _ccontours = 0;
	cvFindContours(&c_image, storage, &_ccontours, sizeof(CvContour), retr, method, CvPoint(offset));
if (!_ccontours)
{
	contours.clear();
	return;
}
Seq&lt;CvSeq*&gt; all_contours(cvTreeToNodeSeq(_ccontours, sizeof(CvSeq), storage));
int total = (int)all_contours.size();
contours.resize(total);

SeqIterator&lt;CvSeq*&gt; it = all_contours.begin();
for (int i = 0; i &lt; total; i++, ++it)
{
	CvSeq* c = *it;
	((CvContour*)c)-&gt;color = (int)i;
	int count = (int)c-&gt;total;
	int* data = new int[count * 2];
	cvCvtSeqToArray(c, data);
	for (int j = 0; j &lt; count; j++)
	{
		contours[i].push_back(Point(data[j * 2], data[j * 2 + 1]));
	}
	delete[] data;
}

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

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
OpenCV,findContours是一个用于查找图像轮廓的函数。它可以帮助我们找到图像的对象边界,并将其表示为一系列的点集。这些点集可以用于后续的分析和处理。 findContours函数接受输入图像和一些可选参数,并返回一个包含所有轮廓的列表。每个轮廓都是一个表示边界的点集。可以使用这些轮廓进行形状分析、图像分割、对象检测等操作。 以下是一个简单的示例代码,演示了如何使用findContours函数来查找图像的轮廓: ```python import cv2 # 读取图像,确保图像是二值化的 image = cv2.imread('image.jpg') gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 查找轮廓 contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 绘制轮廓 cv2.drawContours(image, contours, -1, (0, 0, 255), 2) # 显示结果 cv2.imshow('Contours', image) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在这个示例,首先读取一张图像,并将其转换为灰度图像。然后,使用阈值化操作将图像二值化,以便更好地识别图像的对象边界。接下来,使用findContours函数查找图像的轮廓。最后,使用drawContours函数将轮廓绘制在原始图像上,并显示结果。 注意,在findContours函数,我们传递了一些参数。第一个参数是二值化后的图像,第二个参数是轮廓检索模式,第三个参数是轮廓近似方法。 希望这个例子能帮助你理解OpenCV的findContours函数的用法。如果有任何疑问,请随时向我提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值