c++ OpenCV最大存储容量cv::Mat
C++ OpenCV Max storage capacity of cv::Mat
在我的程序中,我加载了一些图像,从中提取了一些特征,并使用cv::Mat
来存储这些特征。根据图像的数量,我知道cv::Mat
的大小为700.000 x 256(行x色),约为720Mb。但是当我运行我的程序时,当它得到400.000 x 256 (400Mb)并试图添加更多时,它只是崩溃并出现致命错误。有人能确认400Mb确实是cv::Mat
的存储容量上限吗?我应该检查更多的问题吗?克服这个问题的可能方法?
挖掘源代码,使用push_back
:
检查是否有足够的空间容纳新元素,如果没有,它重新分配矩阵,为(current_size * 3 + 1)/2留出空间(见这里)。在您的示例中,在大约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;
也就是
- 分配一个新的矩阵,为所有新创建的元素使用默认构造函数
- 复制数据,然后删除旧数据
- 为这个矩阵创建一个新的标题,包含足够的列
- 将this元素指向新分配的数据(释放旧分配的内存)
意味着,在您的情况下,它需要足够的空间(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
吗?
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;
}
}
这实际上取决于分配器,取决于上面的代码如何处理不同的行和cols值。因此,应该考虑a
的大小初始尺寸。
请记住,与c++ 11 std::vector
一样,cv::Mat
中的元素是连续的。对底层数据的访问可以通过cv::Mat::data
成员获得。连续调用std::vector::push_back
或cv::Mat::push_back
可能会导致底层内存的重新分配。在这种情况下,内存必须移动到一个新地址,并且大约需要两倍的内存量来移动旧地址到新地址(没有任何使用更少内存的棘手算法)。
创建如下矩阵。使用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至少有一些支持这些,但不能说更多,从来没有使用过它们。
- 使用一组较小的矩阵,读取/写入/拆分/连接它们,这取决于你需要什么。
- 将CHW格式的浮点向量转换为cv::Mat
- C++:从GPU内存(cudaMemcpy2D)获取BGR图像(cv::Mat)
- 选择基于另一个垫子的非零像素的cv::Mat的一部分?
- 将 cv::mat 转换为 QImage
- 将 cv::Mat 转换为 std::vector 的通用函数
- 在 QML VideoOutput 中将 cv::mat 显示为 QVideoFrame
- 如何将 cv::Mat 转换为 vtkImageData?
- 如何将 vtkImageData 转换为 cv::Mat?
- 如何总结所有cv::Mat元素
- Qt-工作线程崩溃时将cv::Mat转换为QImage
- 在 cv::Mat 中生成 2D 坐标网格
- 将 cv::Mat 转换为无符号的 int 像素
- OpenCV 分段错误(核心转储)在使用 cv::Mat::at 时
- 如何将多个 cv::Mat 从 c++ dll 传递到 opencvsharp Mat c#?
- 如何在不复制数据的情况下将 cv::Mat 转换为 2d 标准::矢量
- 如何使用 (*mpORBextractorLeft)(im,cv::Mat(),mvKeys,mDescriptors)
- 从数组创建 Mat 时,'cv::Mat' 和 'int' 类型不兼容
- 如何将 cv::mat 对象从 python 模块传递到 c++ 函数并返回返回的 cv::mat 类型为对象?
- 移动语义和 cv::Mat
- OpenCV 错误:断言在 cv::Mat 行 522 中失败