暗通道先验去雾实现过程分析
? ? ? ? 經典去霧算法-何凱明09年提出暗通道先驗去霧(Single Image Haze Removal Using Dark Channel Prior)
? ? ? ? 暗通道去霧公式:I(x) = f(x)*t(x) + (1 – t(x))*A
? ? ? ??I(x)為待去霧圖像,f(x)為去霧后圖像,t(x)為透射率(0,1),A為大氣光成分。
? ? ? ??根據公式,去霧算法可解釋為:有霧時,相機獲取到的圖像為兩部分組成,一部分為被拍攝物體發射光線穿過霧霾后的光線,另一部分為大氣光被霧霾反射后的光線。被拍攝物體發射光線f(x)通過霧霾,霧霾透射率為t(x),那么被攝物體光線到達相機后值為?f(x)*t(x)。原始大氣光值為A,大氣光的方向可看做與被拍攝物體光線完全相反,大氣光一部分穿過霧霾,一部分被霧霾反射,被反射后的光線值即為A - A*t(x))。
? ? ? ??整個去霧流程如下圖:
? ? ? ??有了算法模型,接下來對各個部分進行細化實現,首先計算透射率。暗通道先驗法指出,根據大量圖像統計,無霧圖像RGB通道總有一個通道值趨近于0,也就是說在該像素上透射率t(x)趨近于1,f(x)與I(x)幾乎相等。而有霧圖像,由于霧霾反射的大氣光干擾,RGB通道上的最低值,也就是被反射后的大氣光值,通過這個值即可計算出該像素上的透射率,當然實際情況下需要考慮其他因素造成的干擾,一般情況下需要對原始圖像通過RGB最低值計算的灰度圖進行最低值濾波,根據某個像素相鄰范圍的最低值計算透射率。
? ? ? ??整個計算透射率過程分為三個步驟:第一步為原始圖像通過RGB最低值轉灰度圖;第二步對灰度圖進行最低通道濾波,濾波器半徑可選,取濾波器內最低值作為中心像素值;第三部對低通濾波后的灰度圖進行高斯低通濾波,主要作用為平滑圖像,讓邊緣過度平緩。
? ? ? ??透射率計算流程如下:
? ? ? ??計算大氣光成分值則比較簡單,大氣光成分A可以看做一個獨立的RGB像素,包含三個值。一般是對原始圖像RGB通道統計0~255概率分布,分別從各個通道最大值開始取占比大于萬分之一的值作為A中對應值。
? ? ? ??完成透射率和大氣光成分計算后,接下來就是根據公式計算去霧圖像。實際上還需要計算透射率圖像,透射率圖像通過高斯低通濾波后圖像進行反轉得到,在實際應用開發上,這一步可以省略,直接通過濾波后灰度圖實際上可以看做霧霾反射率圖T(x),那么透射率即t(x)=1-T(x)。去霧處理后的圖像一般會比較暗,可以通過自動對比度或自動色階進行優化。
? ? ? ??基于暗通道的去霧對于濃度不均勻的霧霾去霧效果比較好,但計算透射率過程由于最低值濾波和高斯低通濾波隨著卷積核增大,消耗計算量也越大,處理單張影像還好,如果用于批量處理影像,顯然不太實用。當然在實際應用中,可以考慮減少濾波核半徑,犧牲一些效果,提升處理速度。把最低值通濾波和高斯低通濾波核半徑都設置為1進行測試。
測試圖像:
去霧效果圖:
附上代碼,java版的實現:
package tools;import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List;public class AutoHazeRemoval {//自動去霧public BufferedImage hazeRemoval(BufferedImage image) {BufferedImage tempImage = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());BufferedImage grayImage = new ImageGray().transferGrayImageByLowest(image);grayImage = new LowestFilter().lowestFilter(grayImage, 1);grayImage = new GaussianBlur().gaussBlur(grayImage, 1, 1);int[] A = extractA(image);for (int i = 0; i < image.getWidth(); i++) {for (int j = 0; j < image.getHeight(); j++) {int rgb = image.getRGB(i, j);double R = (rgb >> 16) & 0xff;double G = (rgb >> 8) & 0xff;double B = rgb & 0xff;//計算透射率int transmissionValue = grayImage.getRGB(i, j) & 0xff;double transmission = 0.95 * (double)transmissionValue / 255;R = (R - A[0] * transmission) / (1 - transmission); G = (G - A[1] * transmission) / (1 - transmission); B = (B - A[2] * transmission) / (1 - transmission); rgb = (255 & 0xff) << 24 | (clamp((int)R) & 0xff) << 16| (clamp((int)G) & 0xff) << 8 | (clamp((int)B) & 0xff);tempImage.setRGB(i, j, rgb);}}return tempImage;}/*** 通過RGB在0~255頻率分布計算全局大氣光成分* * @param image* @return*/public int[] extractA(BufferedImage image) {List<Integer[]> list = generateBinary(image);int[] result = new int[3];result[0] = getValue(image,list.get(0));result[1] = getValue(image,list.get(1));result[2] = getValue(image,list.get(2));return result;}private int getValue(BufferedImage image,Integer[] list) {double temp = 0.0;int result = 0;for(int i = 255; i > 0; i--) {double num = list[i] / (double)(image.getWidth()*image.getHeight());temp += num;if (temp >= 0.0001) {result = i;break;}}return result;}/*** 圖像二值化 計算圖像RGB值從0~255之間分布* * @return*/public List<Integer[]> generateBinary(BufferedImage image) {List<Integer[]> list = new ArrayList<>();Integer[] rlist = new Integer[256];Integer[] glist = new Integer[256];Integer[] blist = new Integer[256];// 通過循環,往集合里面填充0~255個位置,初始值都為0for (int i = 0; i < 256; i++) {rlist[i] = 0;glist[i] = 0;blist[i] = 0;}for (int i = 0; i < image.getWidth(); i++) {for (int j = 0; j < image.getHeight(); j++) {int rgb = image.getRGB(i, j);int r = (rgb >> 16) & 0xff;int g = (rgb >> 8) & 0xff;int b = rgb & 0xff;rlist[r] = rlist[r] + 1;glist[g] = rlist[g] + 1;blist[b] = rlist[b] + 1;}}list.add(rlist);list.add(glist);list.add(blist);return list;}// 判斷a,r,g,b值,大于256返回256,小于0則返回0,0到256之間則直接返回原始值private int clamp(int rgb) {if (rgb > 255)return 255;if (rgb < 0)return 0;return rgb;} }?
總結
以上是生活随笔為你收集整理的暗通道先验去雾实现过程分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python离线安装国内镜像OpenCV
- 下一篇: 无人机图像处理工具-亮度、对比度、饱和度