OpenCV Mat内存管理

3

内存管理对于图像类来说是至关重要的。在OpenCV中,图像类是cv::Mat,它具有精细的内存管理方案。假设我已经有自己的图像类SelfImage

class SelfImage
{
  public:
    int width_;
    int height_;
    unsigned char *pPixel_;    
};

一开始,我将把所有图像像素内容放入这个类:

SelfImage myImage;
myImage.width_ = 300;
myImage.height_ = 200;
myImage.pPixel_ = new [300*200];
for(int i=0; i<300*200; i++)
      myImage.pPixel_[i] = i%200;

那么我的问题是如何以非常高效的方式将这个类转换为 cv::Mat,我有一个解决方案:

  cv::Mat mat;
    mat.create( myImage.height_, myImage.width_, CV_8UC1);
    mat.data = myImage.pPixel_;

我不确定这是否是一个好的解决方案。如果cv::Mat::create函数也会分配内存,那么上述代码存在内存泄漏的危险。有什么想法吗?
编辑: 我必须明确表示,如果我可以使用cv::Mat::create方法但与SelfImage类共享内存将会很好。原因是已定义一个函数来执行图像类转换工作:void TransImageType(const SelfImage &geo_img, cv::Mat &mat)
4个回答

12

cv::Mat 有一个构造函数,可以在其中指定用户数据:

Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)

关于data参数,文档如下所述:

指向用户数据的指针。需要使用数据和步长参数的矩阵构造函数并不分配矩阵数据。相反,它们只初始化指向指定数据的矩阵头,这意味着没有数据被复制。这个操作非常高效,并且可以使用OpenCV函数处理外部数据。外部数据不会自动释放,所以您需要自己处理。


我必须明确指出,如果我可以使用cv::Mat::create方法并与SelfImage类共享内存,那将是很好的。 - feelfree
我认为那是不可能的...请参考相关问题。你为什么不能使用那个构造函数? - BConic
谢谢,原因是定义了一个函数来执行图像类转换工作 void TransImageType(const SelfImage &geo_img, cv::Mat &mat); - feelfree
那么为什么不在函数体中执行 mat = cv::Mat(geo_img.height_, geo_img.width_, CV_8U, geo_img.pPixel_); 呢? - BConic
谢谢,我没有意识到可以以这种方式调用cv::Mat。我对cv::Mat不太熟悉,当我看到像mat = cv::Mat(geo_img.height_, geo_img.width_, CV_8U, geo_img.pPixel_)这样的代码时,我认为它会涉及深度复制。 - feelfree
不,当你这样做时,只有 cv::Mat 的头文件被复制(行、列、类型等),底层像素数据是相同的。 - BConic

1

Mat::create()函数分配数据(total()*elemSize()字节),并将分配的数据的内部引用计数初始化为1,除非已经存在一个与create()方法中指定的大小/类型相同的Mat对象。

是的,你的代码会导致内存泄漏,因为Mat::create()分配的数据在移动Mat::data指针时丢失了。

正确的做法应该是(我个人认为)使用memcpymyImage.pPixel_复制到mat.data(在调用create()之后)。这可能看起来效率低下,但好处是Mat析构函数将处理数据释放。


1

你可以简单地使用

Mat mat = Mat(myImage.height_, myImage.width_, CV_8UC1, myImage.pPixel_);

这种方式不会复制任何数据。当然,作为代价,您需要注意释放内存。

来自其文档:

[...] 相反,它们只初始化指向指定数据的矩阵头,这意味着不会复制任何数据。这个操作非常高效,可以使用OpenCV函数处理外部数据。外部数据不会自动释放,所以您需要注意它。


我必须明确指出,如果我可以使用cv::Mat::create方法并与SelfImage类共享内存,那将是很好的。 - feelfree

1

这取决于您是否想要复制数据。

从您的建议来看,似乎您想要共享数据。在这种情况下,这是最好的解决方案:

cv::Mat mat(myImage.height_, myImage.width_, CV_8U, myImage.pPixel_);
mat在被释放时不会释放内存,您需要自行释放。

如果您想要复制数据,请创建一个普通的cv::Mat,稍后使用std::copy(或 @KeillRandor 建议的memcpy)进行复制。


谢谢您的评论,但是在这里我不能直接使用cv :: Mat mat(myImage.height_,myImage.width_,CV_8U,myImage.pPixel_)。 - feelfree

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接