OpenCV 图像大小限制

6
我正在尝试使用ITK到OpenCV桥接程序在OpenCV和ITK之间传递一个巨大的Mat图像(98304x51968)。但是,我遇到了以下错误:

Insufficient memory (OverFlow for imageSize) in cvIniyImageHeader, file opencv\modules\core\src\array.cpp line 2961.

这是否意味着OpenCV对图像大小有限制?

1
错误信息表明内存不足,超出了一些OpenCV的限制。你的RAM有多少?请注意,你的图像几乎有5亿像素。每个像素使用多少位? - Daniel Langr
2
8个像素,我有156 GB,所以这不是RAM问题。我找到了一个讨论类似问题的话题: https://github.com/opencv/opencv/pull/7507 - user2827482
如果你在Linux或者macOS操作系统上,尝试运行ulimit -a命令来查看你是否有内存分配的限制。 - Mark Setchell
这是Windows系统,所以我不认为可以。 - user2827482
OpenCV有图像大小限制,但你的错误仍然是“内存不足”。你可以尝试手动分配相同数量的内存吗?如果每个像素确实有8个通道,应该接近39 GB。也许你的RAM被分配给了不同的CPU或者碎片化了。 - Micka
显示剩余4条评论
2个回答

2

好消息,自从这个拉取请求:正确处理大矩阵 #11505 之后,你应该可以像下面这样做(代码取自测试):

Mat m(65000, 40000, CV_8U);
ASSERT_FALSE(m.isContinuous());

uint64 i, n = (uint64)m.rows*m.cols;
for( i = 0; i < n; i++ )
    m.data[i] = (uchar)(i & 255);

cv::threshold(m, m, 127, 255, cv::THRESH_BINARY);
int nz = cv::countNonZero(m);  // FIXIT 'int' is not enough here (overflow is possible with other inputs)
ASSERT_EQ((uint64)nz, n / 2);

由于 countNonZero() 返回一个 int,因此可能会发生溢出。这意味着您应该能够创建巨大的矩阵,但并非所有 OpenCV 函数都能正确处理巨大的矩阵。
关于您的问题,以下是 v5.0a02ITKImageToCVMat 的代码:
template<typename TInputImageType>
cv::Mat
OpenCVImageBridge::ITKImageToCVMat(const TInputImageType* in, bool force3Channels)
{
  // Extra copy, but necessary to prevent memory leaks
  IplImage* temp = ITKImageToIplImage<TInputImageType>(in, force3Channels);
  cv::Mat out = cv::cvarrToMat( temp, true );
  cvReleaseImage(&temp);
  return out;
}

正如您所看到的,IplImage图像仍在使用中,可能是您出错的源头。目前最好的选择应该是自己进行转换。也许可以尝试以下代码(我不熟悉ITK,输入和输出类型相同,通道数为1):

typename ImageType::RegionType  region = in->GetLargestPossibleRegion();
typename ImageType::SizeType    size = region.GetSize();
unsigned int w = static_cast< unsigned int >( size[0] );
unsigned int h = static_cast< unsigned int >( size[1] );
Mat m(h, w, CV_8UC1, in->GetBufferPointer());

这里没有涉及复制操作。如果您想要复制,可以执行以下操作:

Mat m_copy = m.clone();

1
谢谢Catree。的确,我自己做了,而且成功了。 - user2827482

2
似乎在IplImage中存在一个有符号整数(通常为32位)的限制: 从这里的命名.cpp文件中,以下代码片段导致了错误:
const int64 imageSize_tmp = (int64)image->widthStep*(int64)image->height;
image->imageSize = (int)imageSize_tmp;
if( (int64)image->imageSize != imageSize_tmp )
    CV_Error( CV_StsNoMem, "Overflow for imageSize" );

这段代码中的image->imageSize看起来像是一个32位有符号整数,并且这部分代码会检测和处理溢出。根据您在评论中发布的链接,IplImage的“漏洞”可能已经被修复了(我没有检查),所以也许您可以删除OpenCV代码中用于检测溢出的步骤以支持新版本的IplImage,但这只是一种猜测,需要进行确认。您需要检查image->imageSize的类型。如果它是一个64位类型,那么您可能可以更改openCV的代码以支持大于2147483647字节的Mats。

编辑:备注:我检查了OpenCV 3.4中的代码,该行代码是正确的,因此可能在版本4.0中尚未发生变化。

如果您确定IplImage的限制已得到修复,则可以尝试以下内容:

const int64 imageSize_tmp = (int64)image->widthStep*(int64)image->height;
image->imageSize = imageSize_tmp; // imageSize isn't 32 bit signed int anymore!
//if( (int64)image->imageSize != imageSize_tmp ) // no overflow detection necessary anymore
//    CV_Error( CV_StsNoMem, "Overflow for imageSize" ); // no overflow detection necessary anymore

“但最好确保IplImage的图像大小现在是64位的;)”
“更新:链接的修复https://github.com/opencv/opencv/pull/7507/commits/a89aa8c90a625c78e40f4288d145996d9cda3599添加了溢出检测,因此可能IplImage仍然具有32位int imageSize限制!在这里要小心!”

int 打起来比 size_t 快多了 :D - Micka
1
现在你可以使用 Mat 来实现这个功能(参见):Mat m; m.create(65000, 40000, CV_8U);。在现代 OpenCV(>=2.4)中,不应再使用 IplImage - Catree
当OpenCV使用IPP支持时,可能会在内部创建IplImages? - Micka

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