opencv加载大图像

opencv imread()无法加载超大图像,会引起程序崩溃。
提示电脑内存不够了,但是很多时候电脑内存是足够,但是还是无法加载,原因很简单,主要是OpenCV本身对加载的图像大小是由限制的,这个限制定义在modules\imgcodecs\src\loadsave.cpp这个源码文件中,有三个关于图像imread时候最大图像宽、高、像素数目大小限制,定义的代码为:

1
2
3
static const size_t CV_IO_MAX_IMAGE_WIDTH = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_WIDTH", 1 << 20);
static const size_t CV_IO_MAX_IMAGE_HEIGHT = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_HEIGHT", 1 << 20);
static const size_t CV_IO_MAX_IMAGE_PIXELS = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_PIXELS", 1 << 30);

分别表示:

  • 支持的最大图像宽度 2^20
  • 支持的最大图像高度 2^20
  • 支持的最大像素数目 2^30

函数validateInputImageSize会首先校验图像的大小,

1
2
3
4
5
6
7
8
9
10
static Size validateInputImageSize(const Size& size)
{
CV_Assert(size.width > 0);
CV_Assert(static_cast<size_t>(size.width) <= CV_IO_MAX_IMAGE_WIDTH);
CV_Assert(size.height > 0);
CV_Assert(static_cast<size_t>(size.height) <= CV_IO_MAX_IMAGE_HEIGHT);
uint64 pixels = (uint64)size.width * (uint64)size.height;
CV_Assert(pixels <= CV_IO_MAX_IMAGE_PIXELS);
return size;
}

修改方式

方法一 源码修改

想要加载超过这些限制的图像文件,首先要确保你由足够的内存,然后手动修改OpenCV源码文件,把限制改到你想要的值,然后重新编译OpenCV即可。

方法二 文件读取方式

opencv用imread读超大(几个G)的图像会导致失败,可以直接读文件,然后把数据塞进mat的数据区(data)。以下代码记录读取BMP图像的方式,下次验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void readBigImage(const std::string &vPath)
{
FILE *fp = nullptr;
fp = fopen(vPath, "r");
if (!fp)
{
return;
}
int width = getImageWidth(vPath);
int height = getImageHeight(vPath);
int bitCount = getBitCount(vPath);
fseek(fp, 14 + 40 + 256 * 4, SEEK_SET); // bmp图像格式数据
cv::Mat img = cv::Mat::zeros(height, width, CV_8UC1);
freed(src.data, width * height, 1, fp);
fclose(fp);
return;
}

int getImageWidth(const std::string &vpath)
{
FILE *fp = nullptr;
if (!fp)
{
return 0;
}
fseek(fp, 18, SEEK_SET);
int width = 0;
fread(&width, 4, 1, fp);
fclose(fp);
return width;
}

int getImageHeight(const std::string &vpath)
{
FILE *fp = nullptr;
fp = fopen(path, "r");
if (!fp)
{
return 0;
}
fseek(fp, 22, SEEK_SET);
int height = 0;
fread(&height, 4, 1, fp);
fclose(fp);
return height;
}

由于bitmap文件在文件中的存储结构大多数都是第一行图像数据是存储在图像数据区后面,所以一般不需要翻转,但也有例外,所以需要上下翻转,至于4字节对齐那个,由于的图像本身就是直接对齐的,所以这里没有再去写判断了,具体可以百度查bitmap文件结构
基本思想就是先初始化一个需要大小的mat,然后读图像文件,文件指针偏移过文件前面的那几十个固定大小的文件头就行,256*4这个是调色板,如果图像文件有,就偏移,没有就不用偏移,灰度图是肯定有的,所以我这里偏移了

opencv wrapAffine()处理图像