起因
遇到問題
在做項目的過程中,想比較 同一幅圖像 的 二值化處理結果 和 人工標註的ground_truth圖 之間的差異。
因爲這兩幅用來比較的圖在生成的時候都是 二值圖像(即像素值非 0 即 255),所以用來求差異圖的代碼段,我想當然地這麼寫:
for i in range(h): for j in range(w): if thresh_pic[i, j, 0] == 0 and label_pic[i, j, 0] == 0: diff_pic[i, j, :] = 0 elif thresh_pic[i, j, 0] == 255 and label_pic[i, j, 0] == 255: diff_pic[i, j, :] = 255 elif thresh_pic[i, j, 0] == 0 and label_pic[i, j, 0] == 255: diff_pic[i, j, 2] = 255 elif thresh_pic[i, j, 0] == 255 and label_pic[i, j, 0] == 0: diff_pic[i, j, 1] = 255 else: logging.error('({},{}): thresh={}, label={}'.format(i, j, thresh_pic[i, j, 0], label_pic[i, j, 0]))
在差異比對效果圖出來之後卻看到了很詭異的現象,圖片中出現了很多黑色麻花,如下圖:
logging打印出來的結果也顯示有很多 既非 0 也非 255 的像素點:
... ERROR:root:(279,535): thresh=254, label=0 ERROR:root:(279,576): thresh=253, label=0 ERROR:root:(279,581): thresh=253, label=0 ...
修改代碼
經過觀察,我發現某些像素點在 存儲爲圖片 之前 像素值 還是 255 或 0,存爲圖片 以後,就會變成了245~255或0~10範圍內的隨機數(在不懂原理的我看來感覺那就是隨機偏移,真實情況其實應該是按照某個算法進行了對應的偏移)了。初步猜測是在 存儲爲圖片時 或 從圖片讀取出來時,部分像素點 發生了 像素值 的 少許偏移。
於是我修改了代碼,考慮到了一個 閾值爲10 的 像素值偏移區間:
for i in range(h): for j in range(w): if thresh_pic[i, j, 0] < 10 and label_pic[i, j, 0] < 10: diff_pic[i, j, :] = 0 elif thresh_pic[i, j, 0] > 245 and label_pic[i, j, 0] > 245: diff_pic[i, j, :] = 255 elif thresh_pic[i, j, 0] < 10 and label_pic[i, j, 0] > 245: diff_pic[i, j, 2] = 255 elif thresh_pic[i, j, 0] > 245 and label_pic[i, j, 0] < 10: diff_pic[i, j, 1] = 255 else: logging.error('({},{}): thresh={}, label={}'.format(i, j, thresh_pic[i, j, 0], label_pic[i, j, 0]))
這次黑色麻花沒有了,logging也沒有打印error信息了:
查找資料
經過上網查找,我發現原來一些細心的前輩們也發現這個問題了,並給出了解答:
Soga,原來是因爲:
- .jpg 是 有損壓縮格式,保存時會 壓縮失真 ( .png 是 無損壓縮格式) 。
那麼好奇心大發作的我又想拿我最愛的妹子圖來進一步探究一下。
實驗
實驗思路
- 將原圖像 複製多份 ,分別 進行 不同輪次 的 循環存儲-讀寫,經過 多輪次 的 循環 後,在 肉眼層面 查看 新圖像 是否明顯較 原圖像 有失真;
- 比較 每一輪 循環存儲-讀寫 後,圖片上 各像素點 的 像素值 發生了哪些 變化(置色方案參見下表)。
像素點的像素值變化 | 置色方案 |
---|---|
不變 | 黑色 |
增加 | 綠色 |
減少 | 紅色 |
實驗效果
原圖像:
100輪 存-讀 之後的圖像:
原圖像 與 第1輪 存-讀 後的 圖像差異:
第1輪 存-讀 後的圖像 與 第2輪 存-讀 後的 圖像差異:
第2輪 存-讀 後的圖像 與 第3輪 存-讀 後的 圖像差異:
第3輪 存-讀 後的圖像 與 第4輪 存-讀 後的 圖像差異:
第4輪 存-讀 後的圖像 與 第5輪 存-讀 後的 圖像差異:
實驗代碼
# coding=utf-8 first_path = './data/first.jpg' second_path = './data/second.jpg' third_path = './data/third.jpg' forth_path = './data/forth.jpg' fifth_path = './data/fifth.jpg' one_hundred_path = './data/one_hundred.jpg' import cv2 import numpy as np import logging # 生成並保存 倆圖像 的 差異圖 def compare(pic1, pic2, dst_path): h, w, c = pic1.shape diff_pic = np.zeros_like(pic1, dtype=np.uint8) for i in range(h): for j in range(w): if pic1[i, j, 0] == pic2[i, j, 0]: # 像素值 不變,該點 置 黑色 diff_pic[i, j, :] = 0 elif pic1[i, j, 0] < pic2[i, j, 0]: # 像素值 增加,該點 置 綠色 diff_pic[i, j, 1] = 255 elif pic1[i, j, 0] > pic2[i, j, 0]: # 像素值 減少,該點 置 紅色 diff_pic[i, j, 2] = 255 else: logging.error('({},{}): pic1={}, pic2={}'.format(i, j, pic1[i, j, 0], pic2[i, j, 0])) cv2.imwrite(dst_path, diff_pic) return diff_pic # 指定輪數 進行 循環 存儲和讀取 def save_and_read_cycle(pic, path, num): for _ in xrange(num): cv2.imwrite(path, pic) pic = cv2.imread(path) return pic # 原圖像 origin_pic = cv2.imread('./data/girl.jpg') # 需要 循環 1次 的圖像 first_pic = origin_pic.copy() first_pic = save_and_read_cycle(first_pic, first_path, 1) # 需要 循環 2次 的圖像 second_pic = origin_pic.copy() second_pic = save_and_read_cycle(second_pic, second_path, 2) # 需要 循環 3次 的圖像 third_pic = origin_pic.copy() third_pic = save_and_read_cycle(third_pic, third_path, 3) # 需要 循環 4次 的圖像 forth_pic = origin_pic.copy() forth_pic = save_and_read_cycle(forth_pic, forth_path, 4) # 需要 循環 5次 的圖像 fifth_pic = origin_pic.copy() fifth_pic = save_and_read_cycle(fifth_pic, fifth_path, 5) # 需要 循環 100次 的圖像 one_hundred_pic = origin_pic.copy() one_hundred_pic = save_and_read_cycle(one_hundred_pic, one_hundred_path, 100) # 求 循環 存儲和讀取 後 該圖片 與 原圖像 差異 ori_fir = compare(origin_pic, first_pic, 'data/ori_fir.jpg') fir_sec = compare(first_pic, second_pic, 'data/fir_sec.jpg') sec_thi = compare(second_pic, third_pic, 'data/sec_thi.jpg') thi_for = compare(third_pic, forth_pic, 'data/thi_for.jpg') for_fif = compare(forth_pic, fifth_pic, 'data/for_fif.jpg') ori_hundred = compare(origin_pic, one_hundred_pic, 'data/ori_hundred.jpg')
實驗結論
- 經過 多輪次 的 循環 後,在 肉眼層面 , 新圖像 較 原圖像 沒有明顯的失真;
- 每一輪 循環存儲-讀寫 後,圖片上 各像素點 的 像素值 發生的 變化 會越來越少;
- .jpg 是有損壓縮格式。
實驗不足與展望
不足之處 |
---|
沒有進一步探究壓縮算法的原理 |
沒有實驗出像素值的偏移區間範圍 |
沒有探究循環讀寫的失真率變化原因 |
沒有製作循環讀寫的失真率變化曲線圖 |
缺少其他圖片進行對比試驗,驗證實驗結論的泛化性 |
沒有在單通道灰度圖像上做實驗 |