直方图均衡算法及结合自动色阶的改进

        直方图均衡,顾名思义,是处理影像灰度在统计直方图上分布问题,是一种图像增强算法。以图像某灰度值分布概率以及低于该值得所有值得分布概率之和p与该图像所有灰度级别L的乘积作为该灰度值均衡后的值。

        直方图均衡公式:S = L * \sum p(i)

        以8位灰度图像的直方图均衡为例,第一步是统计图像所有灰度值在0~255分布概率;第二步通过从低到高累加各个灰度级别概率p(i),直方均衡公式p(i)*255计算出对应值,生成查找表;第三部通过原图对应查找表生成直方均衡后图形。

        统计概率分布和直方均衡计算:

private Map<Integer, Double> getPDF(BufferedImage image) {
		Map<Integer, Double> map = new HashMap<>();
		int width = image.getWidth();
		int height = image.getHeight();
		double totalPixel = 3 * width * height;
		for (int i = 0; i < 256; i++) {
			map.put(i, 0.0);// 通过循环,往集合里面填充0~255个位置,初始值都为0
		}
		//分别统计图像上0~255上分布总数
		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;
				map.put(r, map.get(r) + 1);
				map.put(g, map.get(g) + 1);
				map.put(b, map.get(b) + 1);
			}
		}
直方均衡计算,生成查找表:
private void getTable(Map<Integer, Double> map) {
		double param = 0.0;
		for(int i = 0;i < 256;i++) {
			param += map.get(i);
			table[i] = (int)Math.round(param * 255);
		}
	}

 测试图像:

分布概率直方图:

直方均衡后:

直方均衡后概率分布:

        从图像和概率直方图分布看,原图的灰度峰值在50左右,且大部分都分布在较小灰度值上,经过直方图均衡后,图像变亮,且灰度值在0~255上分布比较均匀。这是传统直方图均衡实现方式,但其中存在缺陷,由于中间部分灰度因为占据较小概率分布,有可能被其附近较大概率的灰度淹没。例如,灰度值为100的统计概率为50%,那么均衡后值为128,而110处概率分布为50.1%,那么均衡计算后值仍为128,原图中100~110之间灰度也就被淹没。从测试图像细节中也可以发现这个问题,如下所示:

原图部分细节:

        从图像上可以看到三叉路口存在较明显的亮度差异形成的边界线。那么再看直方均衡后图像细节。

        由于直方均衡的作用,部分灰度级被淹没,三叉路口路面边界线已经丢失,整个路面也完全变为白色,丢失了大量细节。目前直方图均衡算法的改进也有很多,一种是基于HSI色彩空间。由于RGB色彩空间灰度值为离散整数值,在计算过程中容易丢失精度。将图像从RGB色彩空间转换为HSI色彩空间,分离出来的亮度I值为连续变换值,在直方均衡过程中更容易保留更多细节。那么首先要将RGB转换为HSI。

RGB转HSI代码:

public Double[] rgb2HSI(int[] rgb) {
		double sum = rgb[0] + rgb[1] + rgb[2];
		double r = rgb[0] / sum;
		double g = rgb[1] / sum;
		double b = rgb[2] / sum;
		double s1 = 0.5 * ((r - g) + (r - b));
		double s2 = Math.pow((r - g), 2) + (r - b) * (g - b);
		double s3 = s1 / Math.sqrt(s2);
		double h = 0.0;
		if (b <= g) {
			h = Math.acos(s3);
		} else if (b > g) {
			h = 2 * Math.PI - Math.acos(s3);
		}
		double s = 1 - 3 * Math.min(r, Math.min(g, b));
		double H = ((h * 180.0) / Math.PI);
		double S = s * 100.0;
		double I = sum / 3;
		return new Double[] { H, S, I };
	}

HSI转换RGB代码:

public Integer[] hsi2RGB(Double[] hsi) {
		double h = (hsi[0] * Math.PI) / 180.0;
		double s = hsi[1] / 100.0;
		double i = hsi[2] / 255.0;
		double x = i * (1 - s);
		double y = i * (1 + (s * Math.cos(h)) / Math.cos(Math.PI / 3.0 - h));
		double z = 3 * i - (x + y);
		double r = 0, g = 0, b = 0;
		if (h < ((2 * Math.PI) / 3)) {
			b = x;
			r = y;
			g = z;
		} else if (h >= ((2 * Math.PI) / 3) && h < ((4 * Math.PI) / 3)) {
			h = h - ((2 * Math.PI) / 3.0);
			x = i * (1 - s);
			y = i * (1 + (s * Math.cos(h)) / Math.cos(Math.PI / 3.0 - h));
			z = 3 * i - (x + y);
			r = x;
			g = y;
			b = z;
		} else if (h >= ((4 * Math.PI) / 3) && h < ((2 * Math.PI))) {
			h = h - ((4 * Math.PI) / 3.0);
			x = i * (1 - s);
			y = i * (1 + (s * Math.cos(h)) / Math.cos(Math.PI / 3.0 - h));
			z = 3 * i - (x + y);
			g = x;
			b = y;
			r = z;
		}
		int red = (int) Math.round((r * 255));
		int green = (int) Math.round((g * 255));
		int blue = (int) Math.round((b * 255));
		return new Integer[] { red, green, blue};
	}

       

        尽管HSI色彩空间可以解决直方均衡中灰度级丢失问题,但HSI与RGB色彩空间的转换需要耗费大量计算,导致图像处理过程比较缓慢。下面就介绍一下博主自己想到的一种解决办法。

        虽然使用传统直方均衡容易丢失部分灰度级,但仍然可以采用传统方式进行直方均衡计算,将直方均衡计算后图像与原图进行均值融合,便可以恢复部分丢失的灰度值。尽管该方法不能完全恢复丢失的灰度级别,但相对于传统直方均衡,已经有了很大改善。处理流程为:对原图分别进行直方均衡和自动色阶,将计算后的图像进行均值融合,得到最终图像。

效果图:

        相对于传统直方均衡,该方法计算后图像稍暗,但可以保留更多细节。再看细节部分。

        从放大后的细节上看,三叉路口处的边界线仍然清晰可辨。路面也没有完全变为白色。

        实际上,编程实现还可以对算法进一步优化,由于自动色阶算法同样需要概率分布计算,那么直方图均衡和自动色阶的概率分布计算可以一起做!然后根据概率分布分别计算直方图均衡和自动色阶查找表,之后取出原图每个像素,利用查找表计算直方图均衡和自动色阶对应的值,对两个值求均值即可。

实现代码:

public BufferedImage histogramEqualization(BufferedImage image) {
		BufferedImage resultImage = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
		Map<Integer, Double> map = getPDF(image);
		getTable(map);  //初始化直方图均衡查找表
		// 自动色阶前裁切掉最暗和最亮部分占比较少的值
		int min = getMin(map, 0.0001);
		int max = getMax(map, 0.0001);
		int[] arr = new int[] { min, max };
		getContrastByMaxMin(arr);    // 初始化自动色阶查找表
		int width = image.getWidth();
		int height = image.getHeight();
		for (int i = 0; i < width; i++) {
			for (int j = 0; j < height; j++) {
				int rgb = image.getRGB(i, j);
				int R = (rgb >> 16) & 0xff;
				int G = (rgb >> 8) & 0xff;
				int B = rgb & 0xff;
				double hisR = histogramtTable[R];
				double hisG = histogramtTable[G];
				double hisB = histogramtTable[B];
				double contrastR = autoContrastTable[R];
				double contrastG = autoContrastTable[G];
				double contrastB = autoContrastTable[B];
				R = (int) Math.round((hisR + contrastR) / 2);
				G = (int) Math.round((hisG + contrastG) / 2);
				B = (int) Math.round((hisB + contrastB) / 2);
				rgb = (255 & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
				resultImage.setRGB(i, j, rgb);
			}
		}
		return resultImage;
	}

      

 

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

TheMatrixs

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

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

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

打赏作者

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

抵扣说明:

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

余额充值