在人像美顏的拍照界面,往往由於光線和環境因素的影響,導致我們拍出的照片質量較差,包括噪聲多,曝光度過低等等問題,因此,大多數相機應用都會在Camera界面添加一定的圖片預處理,比如自動亮度對比度調整,降噪,調色等等。今天我們要說的是自動亮度對比度和調色問題。
對於自動亮度對比度的調節,主要分爲了傳統算法和基於深度學習的智能算法兩類,我們先對目前算法做一個概述:
傳統算法:
傳統的算法一般主要以視網膜增強算法(Retinex)、基於局部直方圖信息的增強算法爲主,對應的論文舉例如下:
視網膜增強算法舉例:
Multi-Scale Retinex for Color Image Enhancement
A Multiscale Retinex for Bridging the Gap Between Color Images and the Human Observation of Scenes
基於局部直方圖信息增強算法舉例:
Real-Time Adaptive Contrast Enhancement
除了上述兩種方法思路之外,2012年微軟研究院出了一篇個人認爲效果還不錯的論文:
Automatic Exposure Correction of Consumer Photographs
據說微軟的“微軟自拍app”用的就是這個算法,對應效果如下:
傳統算法對於這個問題的研究已經有幾十年的時間,這期間積累了大量的論文資料,這裏我們不在仔細說明,由於傳統算法始終無法對各種情況和各種圖像內容進行自動分析和自適應,因此,效果上依然存在很大問題。
AI算法:
基於深度學習的自動亮度對比度調節算法是近些年的熱點,2019年最新的論文是騰訊的一篇論文:
Underexposed Photo Enhancement using Deep Illumination Estimation
論文的測試DEMO github地址:https://github.com/wangruixing/DeepUPE
對應效果如下:
論文參考了谷歌HDRNet中的部分原理,比如雙邊濾波網格部分等等,如下圖所示:
這篇論文效果很驚豔,但是目前爲止,本人還沒有跑通DEMO,實際測試中存在各種問題,github中也沒有朋友復現出來,很可惜。
上述總結,只是本人挑選的一部分內容所做的概述,下面來看本文重點內容。
本文目的是圖像自動亮度對比度和調色算法,我們給出兩種可行思路:
1,美圖自動亮度對比度調節算法
該算法來自美圖秀秀專利信息,算法流程如下:
①根據直方圖分佈信息,將圖像劃分爲以下幾類:高光類,高光-中間調類,中間調類,中間調-陰影類,陰影類,高光-陰影類,直方圖表示舉例如下:
陰影類 高光-中間調類 高光類
陰影-中間調類 高光-陰影類 中間調類
②準備樣本數據集,人工將數據集按照①中六類標準劃分類別;
③指定亮度對比度調節公式,如下所示:
C=(100+contrast)/100, contrast範圍[-100,100]
brightness+=128
cTable[i] = max(0,min(255,((i-128)*c+brightness+0.5)))
對於灰度級Gray的像素,亮度對比度調節後的結果值爲:cTable[Gray]
④使用③中公式,將樣本集中的樣本進行人工亮度對比度調節,默認調到人工視覺最佳爲止,然後記錄對應的亮度對比度數值,存入數據庫中,至此完成數據樣本準備工作;
⑤構建一個簡單的CNN分類網絡,輸入任意一張圖像,輸出對應的分類標籤,共6類,對應①中6種情況;
⑥使用CNN對數據樣本進行訓練,根據訓練結果進行驗證,並計算樣本集種的誤差,然後調節網絡參數,調節數據集中被錯分的圖像亮度對比度數值並檢測標籤是否錯分,再次訓練,依次反覆,知道精確度小於某個閾值爲止;
⑦使用訓練模型對任意一張輸入照片進行分類,根據分類找到對應類別數據集,使用直方圖相似性來尋找數據集中最相似的樣本,並找到該樣本對應的亮度對比度數值;
⑧根據亮度對比度數值對輸入照片進行亮度對比度調節,得到最終效果圖。
上述過程就是美圖的自動亮度對比度調節方案,理論上先分類,然後找到樣本中直方圖分佈與其最接近的樣本,按照樣本的亮度對比度數值對輸入照片進行調節,算法是可行的,而且,分類網絡的速度是比較快的。由於樣本問題,本人只進行了理論分析,給大家提供一種思路,當然,這種方法只能對圖像進行亮度對比度調節,不能進行顏色調節。
2,端到端網絡訓練
本人使用了另一種方法,直接使用CNN對樣本數據進行訓練,得到最終的調節效果。本人使用Photo lemur3軟件來得到樣本數據,這個軟件是國外一款基於深度學習算法開發的圖像自動調節軟件,可以對圖像進行亮度對比度和調色處理。本人算法如下:
①使用Photolemur 3對300張樣本進行處理,得到300張樣本數據集,當然數據集比較少,這裏僅做可行性測試;
②使用UNet網絡對圖像進行端到端的訓練,使用adam優化器,mse loss,迭代500次,效果如圖所示:
Unet的網絡模型如下:
def unet(input_size = (256,256,3)):
inputs = Input(input_size)
conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(inputs)
conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1)
pool1 = MaxPooling2D(pool_size=(2, 2))(conv1)
conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool1)
conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv2)
pool2 = MaxPooling2D(pool_size=(2, 2))(conv2)
conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool2)
conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv3)
pool3 = MaxPooling2D(pool_size=(2, 2))(conv3)
conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool3)
conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv4)
drop4 = Dropout(0.5)(conv4)
pool4 = MaxPooling2D(pool_size=(2, 2))(drop4)
conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool4)
conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv5)
drop5 = Dropout(0.5)(conv5)
up6 = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(drop5))
merge6 = merge([drop4,up6], mode = 'concat', concat_axis = 3)
conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge6)
conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv6)
up7 = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv6))
merge7 = merge([conv3,up7], mode = 'concat', concat_axis = 3)
conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge7)
conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv7)
up8 = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv7))
merge8 = merge([conv2,up8], mode = 'concat', concat_axis = 3)
conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge8)
conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv8)
up9 = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv8))
merge9 = merge([conv1,up9], mode = 'concat', concat_axis = 3)
conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge9)
conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
conv9 = Conv2D(2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
res = Conv2D(3, (1, 1), activation=None)(conv9)
model = Model(input = inputs, output = res)
#model.compile(optimizer = Adam(lr = 1e-4), loss = 'mse', metrics = ['accuracy'])
return model
上述方法思路比較簡單,但是如果直接使用上述思路,雖然可以得到一定的效果,實時處理確實非常困難,正如HDRNet一樣,也比較難以實時處理,尤其是不適用GPU的情況下實時處理。如何在CPU模式下,應用到camera實時預覽中去,本人做了如下修改:
①優化網絡模型,修改Unet,爲了減少計算量和複雜度,可以減少通道數,減少網絡層數,使用深度可分離卷積替代普通卷積等,最終本人的模型大小爲9K,參數600多個,速度在PC端CPU模式下,使用MNN推理引擎,達到3ms,在終端android手機上測試可達到7.5ms,基本滿足實時處理的需要,當然,圖像大小爲256×256;
②如何將256×256的小圖效果轉換爲原圖效果,這裏我們可以直接輸入原圖來得到效果圖,但是這種方式速度太慢,無法達到實時需求,因此,本人使用了顏色遷移算法,直接將小圖顏色遷移到大圖上,爲此完成了此項工作。
上述兩種思路就是本文的內容,拋磚引玉吧!