[171110] 基於 Python OpenCV 復現引導濾波(Guided Filter)和基於暗通道先驗單幅圖像去霧(Haze Removal)
原創內容,禁止轉載
導讀:這是 Python OpenCV 圖像處理 專欄的第10篇文章。主要是使用 Python 和 OpenCV 復現何凱明大神的 《Guided Image Filtering》和 《Single Image Haze Removal Using Dark Channel Prior》這兩篇論文。關於 Python OpenCV 處理的基礎知識,可以參考這篇文章 [171102] Python3 OpenCV3 圖像處理基礎(Python3 + Numpy + Matplotlib + OpenCV3 + ...)。更多文章,請查看 [171101] Python OpenCV 圖像處理專欄目錄。
同志們好,我又來出來蹦躂了
昨天挖了個坑,說要總結下實現圖像對比度增強處理的方法。發現涉及的方法太廣,坑太大了,趕緊爬出來跑了。。。
額言歸正傳,實現對比度增強,其實就是抑制不感興趣的,強化感興趣的。廣義上,各種去噪、邊緣檢測、(對比度受限)自適應直方圖均衡化等,都可以稱為對比度增強。而保邊去噪這個大類作為圖像對比度增強中的重要組成部分,當然也很引人注意。
除了在 [171108] 基於 Python OpenCV 圖像處理的特殊濾鏡(鉛筆畫、風格化、細節增強等) 里提到的細節增強(cv2.detailEnhance)和邊緣保持濾波(cv2.edgePreservingFilter),還有個經典的雙邊濾波(cv2.bilateralFilter)。今天要說的是何凱明大神(以下簡稱 「何」)在2010年提出的引導濾波,雖然已經被OpenCV收錄(cv2.ximgproc.createGuidedFilter),但是復現以下體會何的簡潔而優雅的思路。
論文主頁給出的示例,直觀的感覺是:使用引導信號(I)調製另一個被引導信號(p)生成結果信號(q),使其在q擁有兩個信號的特性。如使用平滑的信號引導另一個非平滑的信號,產生的結果類似於保邊去噪。
推導細節的話,大家直接去看原文吧。這裡直接使用結論,如圖:
何後來又給出了 "Fast" 版的演算法,也就是首先對圖像進行降採樣,處理後再拓展回去。
這裡將要給出第一個演算法的 Python 版本Python實現。
其中,求均值使用的是 OpenCV 的 cv2.boxFilter:
cv2.boxFilter(src, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]])@brief 均值濾波,也就是滑動窗遍歷時,使用均值作為錨點處的值。@param src: 輸入圖像 @param dst: 輸出圖像(與輸入等大小)@param ddepth: 輸出圖像的位深(-1代表使用原圖像的位深,一般使用 cv2.CV_64F)@param ksize: 濾波器尺寸 ksize=(w,h)...其他參數一般默認就行,不解釋了。
同時需要注意, Python3 中的除法有兩種,整除(//)和浮點除(/),得到的結果分別是 int 型 和 float(64) 型。在進行數學運算時,特別要留心數據精度,一方面防止溢出,另一方便防止類型不匹配。
OK,根據上圖 Algorithm1 中給出的演算法,使用 Python 實現之:
def guidedFilter(I, p, sz=60, eps=1E-6): """計算引導濾波 """ ## 轉換數據精度,其實本函數中有隱式轉換的,可以省略,但這裡明確下。 img_I = I.astype(np.float64) img_p = p.astype(np.float64) ## 定義 lambda 表達式,一會可以少些不少字,哈哈哈哈。。 fmean = lambda img: cv2.boxFilter(img, cv2.CV_64F, (sz, sz)) ## (1) mean_I = fmean(img_I) mean_p = fmean(img_p) corr_I = fmean(img_I * img_I) corr_Ip = fmean(img_I * img_p) ## (2) var_I = corr_I - mean_I * mean_I conv_Ip = corr_Ip - mean_I * mean_p ## (3) a = conv_Ip/(var_I+eps) b = mean_p - a*mean_I ## (4) mean_a = fmean(a) mean_b = fmean(b) ## (5) q = mean_a * I + mean_b return q
Ok, 然後這就實現了 引導濾波。效果還不錯:
再次說明的是,輸入 I, p 最好歸一化。我歸一化之後,送入 guidedFilter 進行計算,得到的 q 範圍是 (-0.17218250812 1.55670020906)。然後也歸一化,並映射到 [0~255]。
上面復現的是引導濾波結果,在基於暗通道先驗圖像去霧時,引導濾波是為了用來矯正 atmospheric transmission(大氣層透射係數)的。
基於暗通道先驗去霧,是何經過統計大量圖像的通道最小後發現的一個規律:即沒有天空的沒有霧的圖像中,不同位置像素總有一個通道是較暗的,構成暗通道圖。合理的解釋是:(1)陰影的通道部分比較暗,(2)彩色區域總有通道較暗,(3) 暗物體都暗。而有霧的場景下拍攝的圖像,由於固體或氣體小顆粒的散射或折射等,會增加圖像「暗通道」的亮度。根據模型計算出大氣透射(散射/投射/折射等這幾個概念在這裡的作用應該差不多,我瞎猜的。具體請參閱原文)係數分布圖,然後用來校正(或矯正,具體是哪個大家明白就行,我也分不清)有霧的圖像,得到「去霧圖像」。
聲明,下面這段是抄的公式,具體細節請查閱原文。這裡只是我的筆記本(霧
有霧圖像的模型:
暗通道計算:
最小化有霧圖像滑動窗區域塊:
由於通道獨立,推導得到:
根據暗通道先驗知識,去霧後暗通道近似為0:
且平均值 大於0,故:
反代入上上式,得到去霧圖像的 transmission 圖:
其實,實際上「無霧」的晴天也是有粉塵顆粒水蒸氣的,所以再校正下:
OK, 這樣就由暗通道先驗知識,使用暗通道得到 transmission 圖。再使用 GuidedFilter 校正下。
最後,由 transmission、大氣參數A 和 有霧原圖,得到去霧圖:
由所有公式可以看出,重要的是得到校正的 transmission 圖。 而 transmission 圖的精度又跟暗通道圖有關係。(其實由於我在在 Python 中沒有像原文那樣,逐個區塊取最小值等操作,所以最終結果圖不理想。但作為演示,體驗下該演算法的有效性,也是可以接受的,起碼我接受了)
結果如圖:
OK,又到了大家都喜歡的代碼環節了,哈哈哈,獻醜了:
#2017.11.11 00:04:35 我該去鍛煉了,待續。。。。
參考:
- [Single Image Haze Removal](http://kaiminghe.com/cvpr09/index.html)
- [Guided Image Filtering](http://kaiminghe.com/eccv10/index.html)
推薦閱讀:
※OpenCV人臉識別之二:模型訓練
※OpenCV AdaBoost + Haar目標檢測技術內幕
※OpenCV機器學習——樸素貝葉斯NBC
※[1]OpenCV4Android機器視覺應用入門-JNI編寫HelloWorld
※人臉識別博客匯總以及一些學習建議