C ++ OpenCV最大存储容量为cv :: Mat

时间:2016-11-10 00:57:06

标签: c++ opencv image-processing mat

在我的程序中,我加载一些图像,从中提取一些功能并使用cv::Mat来存储这些功能。基于我知道的图像数量,cv::Mat的大小为700.000 x 256(行x列),大约为720Mb。但是当我运行我的程序时它大约400.000 x 256(400Mb)并尝试添加更多它只是崩溃与致命错误。任何人都可以确认400Mb确实是cv::Mat存储容量的限制吗?我应该检查更多问题吗?克服这个问题的可能方法是什么?

3 个答案:

答案 0 :(得分:3)

使用push_back

挖掘源代码

它检查是否有足够的空间容纳新元素,如果没有,则重新分配矩阵,空间为(current_size * 3 + 1)/ 2 see here)。在您的示例中,大约400,000 * 256(总共102,​​400,000个元素)尝试另一个分配,因此它尝试为307,200,001 / 2 = 153,600,000 元素分配空间。但是为了移动它,它需要分配一个新空间然后复制数据

来自matrix.cpp

Mat m(dims, size.p, type());
size.p[0] = r;
if( r > 0 )
{
    Mat mpart = m.rowRange(0, r);
    copyTo(mpart);
}

*this = m;

基本上是这样的:

  1. 使用默认构造函数为所有新创建的元素分配新矩阵
  2. 复制数据,然后删除旧数据
  3. 为此矩阵创建一个新标题,其中包含足够的列
  4. 元素指向新分配的数据(释放旧的已分配内存)
  5. 这意味着,在您的情况下,它需要足够的空间(600,000 + 400,000)* 256 - 1GB的数据,使用4个字节的整数。而且,它创建了一行的辅助矩阵,在这种情况下,600,000列,占额外字节2,400,000。

    因此,在下一次迭代中,当它达到600,000列时,它会尝试分配900,000x256个元素(900Mb)+ 600,000x256个元素(600Mb)+ 600,000个(~3.4Mb)。所以,只需通过这种方式分配(使用push_back),就可以进行多次重新分配。

    换句话说:由于您已经知道矩阵的大致大小,因此必须使用reserve。它快几倍(将避免重新分配和复制)。

    此外,作为一种解决方法,您可以尝试插入转置矩阵,然后在完成该过程后再次转置它。

    附带问题:此实施不应使用realloc代替malloc / memcpy吗?

答案 1 :(得分:1)

按如下方式创建矩阵。使用CV_8UC4,因为它提供大约700 MB。没问题。所以不,400Mb不是限制。 700Mb不是限制。尝试了两倍(1400000行,1.4Gb) - 仍然没有限制(我的默认图像查看器无法显示生成的BMP文件)。

const unsigned int N_rows = 700000;
const unsigned int N_cols = 256;
cv::Mat m(N_rows, N_cols, CV_8UC4);
for (int r = 0; r < m.rows; ++r)
{
    for (int c = 0; c < m.cols; ++c)
    {
        m.data[(r*N_cols + c) * 4] =  c % 256;
    }
}
cv::imwrite("test.bmp", m);

克服这个问题的可能方法:

  • 在开始时在cv::Mat分配足够的空间,甚至可能需要额外的一些空间来确保。如果您的问题是由重新分配引起的,这将有所帮助。
  • 如果您的应用是32位,正如您的评论所示,将其转换为64位以增加内存限制。
  • 即使对于32位应用程序,720Mb应该不是问题。但是,您的应用程序可能会为其他内容使用更多内存。如果是这种情况,将图像移动到一个单独的过程可能会有所帮助,因此它会获得自己独立的2Gb。但是,进程间通信很痛苦。
  • 如果您仍然需要处理不适合内存的文件,可能会查看内存映射文件,我认为OpenCV至少有一些支持,但不能多说,从不使用它们。 / LI>
  • 根据您的需要,使用一组较小的矩阵,读/写/分割/加入它们。

答案 2 :(得分:1)

cv::Mat的大小没有严格的限制。只要可用,您就应该能够分配内存。

这是一个小程序,显示多次运行cv::Mat::push_back时数据指针会发生什么。使用rowscols的值可能会导致为a.data打印一个或多个值,最后会抛出内存不足异常。

#include <opencv2/opencv.hpp>
int main()
{
  int rows = 1, cols = 10000;
  cv::Mat a(rows, cols, CV_8UC1);
  while(1) {
    a.push_back(cv::Mat(1, cols, CV_8UC1));
    std::cout << (size_t) a.data << std::endl;
  }
}

这实际上取决于分配器上面的代码对各种行和列的值所做的事情。因此,应考虑a的小和大初始大小。

请记住,与C ++ 11 std::vector一样,cv::Mat中的元素是连续的。可以通过cv::Mat::data成员获取对基础数据的访问权限。连续调用std::vector::push_backcv::Mat::push_back可能会导致重新分配底层内存。在这种情况下,必须将内存移动到一个新地址,并且可能需要大约两倍的内存量才能将旧内容移动到新内容(禁止使用较少内存的任何棘手算法)。