[C++] 问题分析和解决 - 如何使用opencv处理大于1G的bmp图像

关于源代码

已经上传到csdn,可以通过文章上方的连接下载。

创作背景

在当前项目中,需要使用opencv来处理从线扫相机保存下来的灰度bmp图像,一张bmp图像的像素大小为16384x84822,文件大小为1.29G,当使用“cv::imread”从本地读取bmp图像时,将会抛出以下错误:

项目环境

操作系统:Windows10

VS版本:VS 2022 + VS Code

OpenCV版本:4.8.0 

问题分析和解决方案

问题分析

使用vs code打开opencv的源代码目录(我这里时D:\opencv\opencv4.8.0\sources),根据报错的信息找到“loadsave.cpp”并打开:

static const size_t CV_IO_MAX_IMAGE_PIXELS = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_PIXELS", 1 << 30);

代码解释:尝试从环境变量“OPENCV_IO_MAX_IMAGE_PIXELS”中读取允许的最大像素大小,如果找不到,则设置为 2的30次方。

看来“imread”读取图像时会验证图像的像素大小,当读取超过1G像素大小的bmp图像时,会报错。

尝试设置通过cmake命令行设置一下项目的环境变量:

# 设置cmake项目的环境变量为2的32次方,也就是4G像素大小
set(ENV{OPENCV_IO_MAX_IMAGE_PIXELS} 4294967296)
message(STATUS "OPENCV_IO_MAX_IMAGE_PIXELS: $ENV{OPENCV_IO_MAX_IMAGE_PIXELS}")

通过cmake重新构建,

日志显示环境变量已经在cmake项目中生效,再次运行测试程序,但还是报同样的错误。

尝试给操作系统添加“OPENCV_IO_MAX_IMAGE_PIXELS”环境变量,添加完成,点击“确定”保存,并退出环境变量窗口:

 注释掉 “CMakeLists.txt”中的 set环境变量语句,直接从操作系统的环境变量来获取:

# set(ENV{OPENCV_IO_MAX_IMAGE_PIXELS} 4294967296)
message(STATUS "OPENCV_IO_MAX_IMAGE_PIXELS: $ENV{OPENCV_IO_MAX_IMAGE_PIXELS}")

日志显示无法获取到刚才设置的操作系统的环境变量。原因是,vc code只会在启动的时候,从操作系统加载一次环境变量,后续不会进行自动更新。

关闭vs code,从dos命令行窗口“code .”, 重新打开:

(备注:亲测,我的电脑必须从命令行窗口来打开 vs code,才能读取到操作系统设置的环境变量。通过快捷图标打开vs code,然后从vs code中打开项目目录,是无法获取到系统的环境变量的):

 重新构建,并运行测试程序,第一个问题解决了,又来一个,看来bmp图像的编解码也有1G像素大小的限制:

同样打开出错的“grfmt_bmp.cpp”,

这里无法通过设置环境变量进行解决,看来要解决“读取大于1G像素大小的bmp图像”的问题只能通过修改源代码,然后从源代码重新构建库opencv库了。

解决方案

修改并保存源代码

要修改的源代码都在模块“imgcodecs”中,修改后如下图所示:

修改“loadsave.cpp”:

注意:这里不能简单的把“1 << 30”, 修改为 “1 << 32", 因为1默认是32位为有符号整数,符号位占用了1个bit,只剩下31个字节来表示整数大小,进行32位的左移之后,会发生算术溢出。

这里修改为 “size_t(1) << 32”, 和“CV_IO_MAX_IMAGE_PIXELS”保持一致,“size_t”类型不管是64位还是32位的操作系统均定义为无符号整数,如果只是进行32位左移,应该都不是问题。

在vs studio打开源代码给出错误提示:

修改“grfmt_bmp.cpp”:

通过“CMake-GUI”进行项目配置和生成解决方案

 关于从源代码构建opencv4.8.0动态库的详细步骤可以参考另一篇文章:

[C++] 详细教程 - opencv4.8.0安装和验证测试 (Windows + Linux) 

方式一 - 构建“opencv_world”

先把“BUILD_opencv_world”选中,构建opencv_world动态库,然后CMake项目在依赖于opencv_world库:

 打开生成的vs opencv 2022解决方案:

这里发现一个小问题,为什么opencv_world中没有看到修改了源代码的模块“imgcodecs”,为啥呢?会不会导致构建出来的库,修改的代码并不生效?

经过验证,如果采用opencv_world的方式来构建动态库文件,虽然生成的opencv_world vc++项目中看不到“imgcodecs”,“imgcodecs”也模块也会被自动构建,然后添加到opencv_world中去。

opencv的cmake构建过程比较复杂,至于怎么样把“opencv_imgcodecs” 模块添加到opencv_world vc++项目中去,后续有时间再研究一下。

方式二:每个模块单独构建动态库

先把“BUILD_opencv_world”不选中,构建每个模块动态库,然后CMake项目在依赖于代码用的模块(比如opencv_core、opencv_imgcodecs模块)或者依赖所有构建出来的独立模块:

构建生成新的opencv库之后,把“CMakeLists.txt”中的“set(OpenCV_DIR D:/opencv/opencv4.8.0/build)”, 修改为“set(OpenCV_DIR D:/opencv/opencv4.8.0/sources/build/install”), 重新构建和运行测试程序即可。

测试代码:

#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main()
{
    // 大于1G的灰度bmp图像
    Mat img = imread("D:/my_tutorials/SourceCode/big_size_images/scan_camera_img.bmp", cv::IMREAD_COLOR);

    if (img.empty())
    {
        printf("img is not found.");
        return -1;
    }else
    {
        // 打印channels、cols、rows、dims、size等相关属性
        cout << "\nchannels: " << img.channels() << endl;
        cout << "cols: " << img.cols << endl;
        cout << "rows: " << img.rows << endl;
        cout << "depth: " << img.depth() << endl;
        cout << "dims: ";
        for (int i = 0; i < img.dims; i++)
        {
            cout << img.size[i] << " ";
        }
        cout << endl;
        cout << "size: " << img.total() << endl;
    
        return 0;
    }
}

下面是测试程序的运行结果:

  • 38
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老狼IT工作室

你的鼓励将是我创作的最大动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值