暗通道先验去雾实现过程分析

        经典去雾算法-何凯明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个位置,初始值都为0
		for (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;
	}
}

 

  • 5
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TheMatrixs

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值