OpenCV对象析构时错误

!!!!还是没有找到问题所在!!!!

一个非常奇怪的问题

这几天在调试自己“抄写”ORB_SLAM代码时遇到的非常奇怪的问题。主要发生在各种对象的析构时,比如cv::Mat,std::vector<cv::Keypoint>等等。这些对象的析构时机不同,但不外乎有下面这几种:

  1. 离开函数作用域时
  2. 对象被销毁时(类对象内有成员是cv::Mat或者容器内的元素是opencv对象时)
  3. 手动调用容器的clear方法时

当这些时刻发生,程序经常会漰溃,用gdb查看core文件查找漰溃的代码时,基本都发生在下面的时刻:

  1. cv::Mat析构时,调用了cv::Mat::release()方法
  2. std::vector<cv::Keypoint>析构时或者其他stl容器析构时,会调用容器内元素的析构函数std::vector<xxx,stl::alloc<xxx>>::deallocate的时候

一开始以为是各种对象没有被初始化,所以导致析构时错误。但是后面各种对象都补上了初始化,依旧会存在析构时错误。几经折腾,终于摸清了问题所在。

启发

能够搞清楚这个问题主要受到下面这篇文章的启发opencv+vs2015 堆内存析构异常

首先,要知道,opencv的默认的编译都是动态库编译,opencv要编译成静态库十分困难,特别是里面的视频部分,由于要避免开源协议,在编译成静态库时会去掉视频模块。所以,opencv的默认编译方式是动态库。

然后opencv会链接一些静态库,这样就导致了opencv内部拥有一个所谓的local heap,如果我们在程序中声明一些对象,但是没有在程序中进行初始化,然后直接调用opencv的一些接口,让这些对象在opencv内部进行初始化,就会导致这些对象是存放在opencv的local heap中,而不是我们程序的堆中。这样就导致了我们在程序中调用析构函数时,会出现错误。

解决方法

知道上面的原因其实解决方法就比较简单,只需要在声明opencv对象的时候同时进行初始化或者更准确地说,是分配内存。注意,这里不单普通对象要进行初始化,比如容器内包含opencv类型的对象也要进行内存分配,即先调用reserve方法分配好容器的内存。

例子1

对于cv::Mat对象,我们可以这样声明:

1
2
cv::Mat img = cv::Mat();
img = cv::imread("xxx.jpg");

出错用例:

1
2
cv::Mat img = cv::imread("xxx.jpg");    // 此时img的内存是在opencv内部申请的
cv::Mat img2 = cv::Mat::zeros(100,100,CV_8UC3); // 此时img2的内存是在opencv内部申请的

例子2

对于std::vector<cv::Keypoint>对象,我们可以这样声明:

1
2
3
std::vector<cv::Keypoint> kps;
kps.reserve(100);
opencvFUNC(kps);

出错用例:

1
2
std::vector<cv::Keypoint> kps;
opencvFUNC(kps); // 此时kps的内存是在opencv内部申请的