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
存储容量的极限?我应该检查更多问题吗?解决这个问题的可能方法?
创建了一个矩阵如下。使用 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
中分配足够的space,甚至可能多一点,以确保。如果您的问题是由重新分配引起的,这将有所帮助。
- 如果您的应用程序是 32 位的,如您的评论所建议的那样,请将其转换为 64 位以增加内存限制。
- 即使对于 32 位应用程序,720Mb 也应该不是问题。但是,您的应用程序可能会为其他内容使用更多内存。如果是这种情况,将您的图像移到一个单独的进程中可能会有所帮助,因此它会获得自己单独的 2Gb。不过,进程间通信很痛苦。
- 如果您仍然需要处理内存中放不下的文件,也许可以查看内存映射文件,我认为 OpenCV 至少对这些文件有一些支持,但不能说更多,从未使用过它们.
- 使用一组较小的矩阵,reading/writing/splitting/joining 它们,具体取决于您的需要。
acv::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
可能会导致重新分配底层内存。在这种情况下,必须将内存移动到新地址,并且可能需要大约两倍的内存量才能将旧地址移动到新地址(除非使用任何使用较少内存的棘手算法)。
挖掘源代码,使用 push_back
:
它检查是否有足够的 space 用于新元素,如果没有,它重新分配矩阵,space for (current_size * 3 + 1) / 2 (see here)。在您的示例中,大约 400,000 * 256(总共 102,400,000 个元素)它尝试另一个分配,因此它尝试为 307,200,001 / 2 = 153,600,000 元素分配 space。但是为了移动这个,它需要分配一个新的space然后复制数据
来自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;
所以它本质上是:
- 分配一个新矩阵,对所有新创建的元素使用默认构造函数
- 复制数据,然后删除旧数据
- 为此矩阵创建一个新的 header,具有足够的列
- 将this元素指向新分配的数据(释放旧分配的内存)
意思是,在您的情况下,它需要足够的 space 用于 (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
的大小为 700.000 x 256(行 x 列),约为 720Mb。但是,当我 运行 我的程序达到大约 400.000 x 256 (400Mb) 并尝试添加更多时,它只会因致命错误而崩溃。谁能确认 400Mb 确实是 cv::Mat
存储容量的极限?我应该检查更多问题吗?解决这个问题的可能方法?
创建了一个矩阵如下。使用 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
中分配足够的space,甚至可能多一点,以确保。如果您的问题是由重新分配引起的,这将有所帮助。 - 如果您的应用程序是 32 位的,如您的评论所建议的那样,请将其转换为 64 位以增加内存限制。
- 即使对于 32 位应用程序,720Mb 也应该不是问题。但是,您的应用程序可能会为其他内容使用更多内存。如果是这种情况,将您的图像移到一个单独的进程中可能会有所帮助,因此它会获得自己单独的 2Gb。不过,进程间通信很痛苦。
- 如果您仍然需要处理内存中放不下的文件,也许可以查看内存映射文件,我认为 OpenCV 至少对这些文件有一些支持,但不能说更多,从未使用过它们.
- 使用一组较小的矩阵,reading/writing/splitting/joining 它们,具体取决于您的需要。
acv::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
可能会导致重新分配底层内存。在这种情况下,必须将内存移动到新地址,并且可能需要大约两倍的内存量才能将旧地址移动到新地址(除非使用任何使用较少内存的棘手算法)。
挖掘源代码,使用 push_back
:
它检查是否有足够的 space 用于新元素,如果没有,它重新分配矩阵,space for (current_size * 3 + 1) / 2 (see here)。在您的示例中,大约 400,000 * 256(总共 102,400,000 个元素)它尝试另一个分配,因此它尝试为 307,200,001 / 2 = 153,600,000 元素分配 space。但是为了移动这个,它需要分配一个新的space然后复制数据
来自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;
所以它本质上是:
- 分配一个新矩阵,对所有新创建的元素使用默认构造函数
- 复制数据,然后删除旧数据
- 为此矩阵创建一个新的 header,具有足够的列
- 将this元素指向新分配的数据(释放旧分配的内存)
意思是,在您的情况下,它需要足够的 space 用于 (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
吗?