【算法随记四】自动色阶、对比度、直方图均衡等算法的一些小改进。
自动色阶、自动对比度以及直方图均衡这三个算法虽然很普通,也很简单,但是在实际应用中有着非常高的使用率,特别是在修图中,很多设计师打开一幅图,首先的的操作就是Shift+Ctrl+L(自动色阶)。在原理实现上,他们都属于基于直方图统计方面的算法,执行效率都非常之高。我在调整图像- 自动对比度、自动色阶算法一文中对他们的过程进行了详细的分析和解读,这里不在详述。
但是有的时候我们发现自动色阶或对比度等等会过调整或欠调整,在某个软件(不记得是那个了)中我发现了对他们的一些改进方式,其核心改进如下:
普通的方式:
for (int Y = 0; Y < 256; Y++) { if (Y < Min) Table[Y] = 0; else if (Y > Max) Table[Y] = 255; else Table[Y] = IM_ClampToByte((float)(Y - Min) / (Max - Min) * 255); }
改进后的方式:
float Avg = 0, Mean = 0, Sum = 0; for (int Y = 0; Y < 256; Y++) { Sum += Histgram[Y]; Avg += Y * Histgram[Y]; } Mean = Avg / Sum; float Gamma = log(0.5f) / log((float)(Mean - Min) / (Max - Min)); if (Gamma < 0.1f) Gamma = 0.1f; else if (Gamma > 10) Gamma = 10; for (int Y = 0; Y < 256; Y++) { if (Y < Min) Table[Y] = 0; else if (Y > Max) Table[Y] = 255; else Table[Y] = IM_ClampToByte(pow((float)(Y - Min) / (Max - Min), Gamma) * 255); }
其中的Max和Min的意思请参考其他的文章。
改进后的查找表考虑到全图的一个平均值信息,根据这个平局值来决定调整的一个Gamma值,相当于他同时结合了Gamma校正和自动色阶的思想,普通的自动色阶对应Gamma=1,还是拿一些我常用的测试图举例吧。
原图 标准的自动色阶 改进后的自动色阶
似乎改进后的更为合理。
对于直方图均衡化的改进,我在ImageJ的代码中找到这样一段话:
// Changes the tone curves of images. // It should bring up the detail in the flat regions of your image. // Histogram Equalization can enhance meaningless detail and hide important but small high-contrast features. This method uses a // similar algorithm, but uses the square root of the histogram values, so its effects are less extreme. Hold the alt key down // to use the standard histogram equalization algorithm. This code was contributed by Richard Kirk (rak@cre.canon.co.uk). // ImageJ\source\ij\plugin\ContrastEnhancer.java
他的核心代码修改如下:
private void equalize(ImageProcessor ip, int[] histogram) { ip.resetRoi(); if (ip instanceof ShortProcessor) { // Short max = 65535; range = 65535; } else { //bytes max = 255; range = 255; } double sum; sum = getWeightedValue(histogram, 0); for (int i=1; i<max; i++) sum += 2 * getWeightedValue(histogram, i); sum += getWeightedValue(histogram, max); double scale = range/sum; int[] lut = new int[range+1]; lut[0] = 0; sum = getWeightedValue(histogram, 0); for (int i=1; i<max; i++) { double delta = getWeightedValue(histogram, i); sum += delta; lut[i] = (int)Math.round(sum*scale); sum += delta; } lut[max] = max; applyTable(ip, lut); }
private double getWeightedValue(int[] histogram, int i) { int h = histogram[i]; if (h<2 || classicEqualization) return (double)h; return Math.sqrt((double)(h)); }
他这里主要是对直方图的数据进行了开根号,这样可以减少有些色阶特别集中的直方图对修正后的图像的影响,他可以解决普通的直方图均衡化有时候处理起来图像变换太大的问题,我们同样以上述两幅图来做示范。
原图 标准的直方图均衡化 改进后的直方图均衡化
可以明显的看到,标准的直方图均衡化对两幅图的处理都太过分,而改进后的则效果要和谐和自然很多。
这种改进还可以嵌入到很多其他的和直方图有关的算法中,比如CLAHE等等,也可以有效的改进他们的效果。
本人的SSE算法优化合集DEMO: https://files.cnblogs.com/files/Imageshop/SSE_Optimization_Demo.rar。