在我的程序中,我加载一些图像,从中提取一些功能并使用cv::Mat
来存储这些功能。基于我知道的图像数量,cv::Mat
的大小为700.000 x 256(行x列),大约为720Mb。但是当我运行我的程序时它大约400.000 x 256(400Mb)并尝试添加更多它只是崩溃与致命错误。任何人都可以确认400Mb确实是cv::Mat
存储容量的限制吗?我应该检查更多问题吗?克服这个问题的可能方法是什么?
答案 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;
基本上是这样的:
这意味着,在您的情况下,它需要足够的空间(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
分配足够的空间,甚至可能需要额外的一些空间来确保。如果您的问题是由重新分配引起的,这将有所帮助。答案 2 :(得分:1)
cv::Mat
的大小没有严格的限制。只要可用,您就应该能够分配内存。
这是一个小程序,显示多次运行cv::Mat::push_back
时数据指针会发生什么。使用rows
和cols
的值可能会导致为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_back
或cv::Mat::push_back
可能会导致重新分配底层内存。在这种情况下,必须将内存移动到一个新地址,并且可能需要大约两倍的内存量才能将旧内容移动到新内容(禁止使用较少内存的任何棘手算法)。