opencv的cv2.imwrite()函数写图像之后,再次读取,其像素值不相等的bug

转载于大佬:[原链接](https://blog.csdn.net/oukohou/article/details/82378552)

1. 今天刚发现的一个小bug:

用opencv的imread()函数读取一张图像之后,将其保存为’.jpeg’后缀的图像,然后再次读取刚刚保存的图像,会发现两次读取的图像,其像素值不相等?

2. Bug复现:

复现代码如下:

# -*- coding: utf-8 -*-

__author__ = 'kohou.wang'
__time__ = '18-9-3'

# If this runs wrong, don't ask me, I don't know why;
# If this runs right, thank god, and I don't know why.
# Maybe the answer, my friend, is blowing in the wind.

import cv2
import matplotlib.pyplot as plt

if __name__ == "__main__":
    img1 = cv2.imread('temp.jpeg')  # 读取图像
    cv2.imwrite('temp1.jpeg', img1)  # 保存图像
    img2 = cv2.imread('temp1.jpeg')  # 读取图像
    
    # 打印两次图像以作对比
    plt.subplot(1, 2, 1)
    plt.imshow(img1)
    plt.title('origin')
    plt.subplot(1, 2, 2)
    plt.imshow(img2)
    plt.title('after')
    plt.show()

 
 
 
 

    OK,然后得到的结果是什么样的呢:

    对比图

    看起来好像差不多,但是具体的矩阵对比为:

    img1的像素值

    img2的像素值

    可以看到,其矩阵值在RGB通道上总是会有那么几个像素值的差异。

    3. But why?

    这是为什么呢?一张图像,读取、保存、再读取,两次读取的结果不应该是一致的么?

    try 1:

    猜测会不会是opencv 的imwrite函数的问题呢?其在写入图像的时候会不会有什么不为人知的小秘密?
    于是采用了PIL中的Image进行同样的测试,发现结果一样:

    • 依然会有一定像素值的差异。

    然而,一个不经意的小细节给了我们提示:
    两个图像的类不同
    debug的时候发现,用于两次读取的图像类不同!最开始用的是PIL.Image.IMage类,但读取保存后的.jpeg图像时,用的是PIL.JpegImagePlugin.JpegImageFile类!好了,这时我们就隐约猜到了,应该与保存的图像格式相关。

    try 2:

    于是我们把保存的图像格式变为.png,再次测试:
    PNG_result
    得到的图像对比是这样,看起来还是差不多,那么像素值呢?
    img1_png
    img2_png
    哇哦哇哦哇哦,nothing different!两次读取的结果完全一致!

    4. Conclusion

    所以问题应该在于,.jpeg后缀的图像,其由于jpeg图像本身的编解码问题,写入时的编码与读取时的解码所得不能完美互为逆操作,从而导致的每次写入之后,读取的值都不同。
    写到这里,搜了搜关键字”jpeg编解码 有损“的结果 ,也确实如此。Jpeg是一种有损压缩,而png是无损压缩。
    到这里,也就解决了这个其实算不上bug的bug:

    • 由于图像编解码算法的原因,注定了jpeg图像的写入、读取结果会不一致,而png图像则完全一致。

    这应该算是一个小tip吧,对于不是专业搞图像的、但又要日常与图像打交道的我来说,对于图像的存取,以后还是都保存为png格式为妙。

    以上,你的赞是我最大的动力!


    Scan and we’ll see.
    oukohou

    • 3
      点赞
    • 4
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值