OpenCV imread读取jpg图像的一个大坑

在OpenCV版本[3.0.0,3.4.1]内,使用imread读取jpg图像的像素值与[2.4.9,2.4.13.6]版本结果不同,对CNN计算造成影响。此差异源于JPEG解码器版本变化,仅在3.4.2及之后版本得到修正。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OpenCV imread读取jpg图像的一个大坑

https://www.cnblogs.com/zjutzz/p/10543935.html

长话短说

版本区间[OpenCV3.0.0, OpenCV3.4.1]内的OpenCV,(至少在windows下,使用官方提供的预编译版本),imread读取jpg图片后的像素值,和版本区间[OpenCV2.4.9, OpenCV2.4.13.6]结果不一样,虽然imshow看到的结果差不多。

从编码解码角度考虑并没有谁对谁错,但是要注意到,imread结果的不同对于CNN框架前向计算结果的影响。(妈的,因为这个difference,周末被迫加班,对OpenCV多了一份不信任)

关键是,这个不同,在opencv官方changelog中找不到!

啰嗦版本

OpenCV的imread并没有自己去实现编码解码,而是根据图像类型(jpg, png等)去调用相应的编码解码器(libjpeg,libpng等)。

因为不是研究传统DIP的图像编码解码的,以前没有注意到opencv的不同版本中jpg图像解码器的不同。

但是最近项目中我负责维护的CNN前向计算框架计算结果不太对,表现为SSD网络输出的loc信息,小数点后面几位对不上。明明一样的图,CNN前向代码我仅仅是拆分layer到文件,没有修改逻辑。为何结果不对??

尝试逐层打印结果,第一步应该确认网络输入,结果发现imread后再去查看图像像素值,同一张图结果不一样。imshow则看不出来区别

一开始因为CMake用了最新版,但是CMakeLists.txt里配置出了点问题,发现读取结果不对的情况,是使用了配置的Windows下Caffe的依赖包里的OpenCV,也就是opencv3.1.0。

于是手动从官方下载了opencv3.1.0的windows预编译版本,发现确实,结果仍然和opencv249不对。我这里以opencv249结果算作正确,尝试了一个下午,发现opencv3.0.0到opencv3.4.1(包含)这系列版本的imread都不对。无语了:

问题的原因,通过打印每个opencv版本的图片解码信息cv::getBuildInformation()来查看:

#include <stdio.h>
#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main() {

    std::string im_pth = "F:/xx/work/20190315/1.jpg";
    cv::Mat src_image = cv::imread(im_pth);
    IplImage* shadow_image = cvLoadImage(im_pth.c_str(), CV_LOAD_IMAGE_COLOR);

    std::cout << cv::getBuildInformation() << std::endl << std::endl << std::endl;

    for (int i = 0; i < shadow_image->height * shadow_image->width * 3; i++){
        fprintf(flog, "%f\n", (float)(unsigned char)(shadow_image->imageData[i]));
        if (i == 0) {
            // 158 is correct
            // 192 is wrong
            printf("%u\n", (unsigned int)(unsigned char)(shadow_image->imageData[i]));
        }
    }
    fclose(flog);
    printf("-------------------end-------------------\n");

    return 0;
}

1.jpg

我这里的情况,打印上面这张jpg图片的第一个像素值,opencv249打印出来是158(认为是正确,因为板子上跑的版本就是这个结果,是正确的标准,DSP优化库要以这个为标准的),而opencv310打印出来是192。

各个opencv版本的Media IO信息:

//-------------------
opencv 3.0.0 (wrong)

  Media I/O:
    ZLib:                        build (ver 1.2.8)
    JPEG:                        build (ver 90)
    WEBP:                        build (ver 0.3.1)
    PNG:                         build (ver 1.5.12)
    TIFF:                        build (ver 42 - 4.0.2)
    JPEG 2000:                   build (ver 1.900.1)
    OpenEXR:                     build (ver 1.7.1)
    GDAL:                        NO

//------------------
opencv 3.4.1 (wrong)

 Media I/O:
   ZLib:                        build (ver 1.2.11)
   JPEG:                        build (ver 90)
   WEBP:                        build (ver encoder: 0x020e)
   PNG:                         build (ver 1.6.34)
   TIFF:                        build (ver 42 - 4.0.9)
   JPEG 2000:                   build (ver 1.900.1)
   OpenEXR:                     build (ver 1.7.1)

//------------------
opencv 3.4.2 (correct)

  Media I/O:
    ZLib:                        build (ver 1.2.11)
    JPEG:                        build-libjpeg-turbo (ver 1.5.3-62)
    WEBP:                        build (ver encoder: 0x020e)
    PNG:                         build (ver 1.6.34)
    TIFF:                        build (ver 42 - 4.0.9)
    JPEG 2000:                   build (ver 1.900.1)
    OpenEXR:                     build (ver 1.7.1)
    HDR:                         YES
    SUNRASTER:                   YES
    PXM:                         YES

//------------------
opencv 2.4.9 (correct)

  Media I/O:
    ZLib:                        build (ver 1.2.7)
    JPEG:                        build (ver 62)
    PNG:                         build (ver 1.5.12)
    TIFF:                        build (ver 42 - 4.0.2)
    JPEG 2000:                   build (ver 1.900.1)
    OpenEXR:                     build (ver 1.7.1)


//------------------
opencv 2.4.13.6 (correct)

 Media I/O:
   ZLib:                        build (ver 1.2.7)
   JPEG:                        build (ver 62)
   PNG:                         build (ver 1.5.27)
   TIFF:                        build (ver 42 - 4.0.2)
   JPEG 2000:                   build (ver 1.900.1)
   OpenEXR:                     build (ver 1.7.1)

可以看出:opencv2.4.x系列,JPEG都是使用ver62,这个应该是libjpeg库版本信息;opencv3.4.2开始,用libjpeg-turbo,这个结果和2.4.x系列一致;只有坑爹的opencv3.0.0到opencv3.4.1系列用的ver90鬼东西结果不对

references

Opencv3.0.0‘s bug of imread function

Problem caused by the change from libjpeg to libjpeg-turbo

关于JPG解码的坑

Loading jpg files gives different results in 3.4.0 and 3.4.2

Greatness is never a given, it must be earned.

<think>好的,我现在需要回答用户关于C++ OpenCVimread函数读取图像通道数的问题。首先,我需要回忆一下OpenCVimread函数的基本用法和相关参数。 记得imread函数的第一个参数是文件路径,第二个参数是读取模式,比如是否以彩色、灰度或者包含alpha通道的方式读取。这个读取模式会影响图像的通道数。用户可能想知道不同的模式参数如何影响结果。 接着,我应该考虑不同的标志选项,比如IMREAD_COLOR、IMREAD_GRAYSCALE和IMREAD_UNCHANGED。这三个是最常用的。IMREAD_COLOR会强制将图像转换为3通道的BGR格式,而IMREAD_GRAYSCALE则转换为单通道的灰度图像IMREAD_UNCHANGED则会保留图像原有的通道数,包括可能的alpha通道。 然后,我需要验证这些信息是否正确。比如,对于JPEG图像,通常读取的是3通道,而PNG如果有透明通道的话,使用IMREAD_UNCHANGED会得到4通道。这点需要确认是否正确,可能查阅OpenCV的官方文档或可靠的资料。 另外,用户提供的引用中有提到一些代码示例,比如引用[4]中关于Mat对象的说明,图像作为矩阵存储,通道数对应于矩阵的通道数。这可以支持我的回答,说明通道数与读取方式有关。 还需要注意OpenCV中颜色通道的顺序是BGR而不是RGB,这可能也需要提及,虽然用户的问题主要关注通道数量,但这点对后续处理有影响,可以适当补充。 然后,我应该组织回答的结构:先说明imread的基本用法,然后详细解释各个标志对应的通道数,再给出示例代码,最后总结不同情况下的通道数。同时,需要引用用户提供的参考资料中的相关内容,例如引用[4]中的Mat对象部分。 另外,用户要求生成相关问题,我需要根据主题提出几个相关的问题,比如如何检查通道数、转换颜色空间等。 最后,确保回答符合格式要求:使用中文,行内公式用$...$,独立公式用$$...$$,但在这个问题中可能公式较少,主要是代码和文字说明。代码需要用正确的语法,可能用户提供的代码中有C++的示例,可以借鉴。</think>在C++ OpenCV中,`imread`函数读取图像时的通道数取决于第二个参数`flags`的取值。以下是三种典型情况的说明: ### 通道数规则 1. **默认模式** `IMREAD_COLOR` 若未指定参数或使用`cv::IMREAD_COLOR`,图像会被强制转换为**3通道BGR格式**(即使原图是单通道灰度图)[^4]。 2. **灰度模式** `IMREAD_GRAYSCALE` 使用`cv::IMREAD_GRAYSCALE`时,图像会被转换为**单通道灰度图**,通道数为1。 3. **保留原通道** `IMREAD_UNCHANGED` 使用`cv::IMREAD_UNCHANGED`时,会保留原始通道数,例如: - JPEG图像通常为3通道 - 带透明通道的PNG图像为4通道 - 单通道TIFF图像保持为1通道 ### 代码示例 ```cpp #include <opencv2/opencv.hpp> int main() { // 读取为3通道BGR图像 cv::Mat img_color = cv::imread("image.jpg", cv::IMREAD_COLOR); std::cout << "Color channels: " << img_color.channels() << std::endl; // 输出3 // 读取为单通道灰度图像 cv::Mat img_gray = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE); std::cout << "Gray channels: " << img_gray.channels() << std::endl; // 输出1 // 保留原始通道数 cv::Mat img_unchanged = cv::imread("image.png", cv::IMREAD_UNCHANGED); std::cout << "Unchanged channels: " << img_unchanged.channels() << std::endl; // 可能输出4 return 0; } ``` ### 关键特性 - OpenCV默认使用**BGR颜色空间**而非RGB,进行颜色操作时需注意通道顺序 - 可通过`Mat::channels()`方法获取通道数 - 通道数直接影响矩阵维度,例如单通道图像的矩阵尺寸为$height \times width$,三通道图像则为$height \times width \times 3$[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值