opencv的cv2.imwrite()函數寫圖像之後,再次讀取,其像素值不相等的bug
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,然後得到的結果是什麼樣的呢:
看起來好像差不多,但是具體的矩陣對比爲:
可以看到,其矩陣值在RGB通道上總是會有那麼幾個像素值的差異。
3. But why?
這是爲什麼呢?一張圖像,讀取、保存、再讀取,兩次讀取的結果不應該是一致的麼?
try 1:
猜測會不會是opencv 的imwrite函數的問題呢?其在寫入圖像的時候會不會有什麼不爲人知的小祕密?
於是採用了PIL中的Image進行同樣的測試,發現結果一樣:
- 依然會有一定像素值的差異。
然而,一個不經意的小細節給了我們提示:
debug的時候發現,用於兩次讀取的圖像類不同!最開始用的是PIL.Image.IMage類,但讀取保存後的.jpeg圖像時,用的是PIL.JpegImagePlugin.JpegImageFile類!好了,這時我們就隱約猜到了,應該與保存的圖像格式相關。
try 2:
於是我們把保存的圖像格式變爲.png,再次測試:
得到的圖像對比是這樣,看起來還是差不多,那麼像素值呢?
哇哦哇哦哇哦,nothing different!兩次讀取的結果完全一致!
4. Conclusion
所以問題應該在於,.jpeg後綴的圖像,其由於jpeg圖像本身的編解碼問題,寫入時的編碼與讀取時的解碼所得不能完美互爲逆操作,從而導致的每次寫入之後,讀取的值都不同。
寫到這裏,搜了搜關鍵字”jpeg編解碼 有損“的結果 ,也確實如此。Jpeg是一種有損壓縮,而png是無損壓縮。
到這裏,也就解決了這個其實算不上bug的bug:
- 由於圖像編解碼算法的原因,註定了jpeg圖像的寫入、讀取結果會不一致,而png圖像則完全一致。
這應該算是一個小tip吧,對於不是專業搞圖像的、但又要日常與圖像打交道的我來說,對於圖像的存取,以後還是都保存爲png格式爲妙。
以上,你的贊是我最大的動力!
Scan and we’ll see.