这是一篇2010年比较古老的文章了,是在QQ群里一位群友提到的,无聊下载看了下,其实也没有啥高深的理论,抽空实现了下,虽然不高大上,还是花了点时间和心思优化了代码,既然这样,就顺便分享下优化的思路和经历。
其中I(i,j)为输入图像,O(i,j)为输出图像,γ为控制参数,当γ大于1时,图像整体变亮,当γ小于1大于0时,图像整体变暗,γ小于0算法无意义。
这个算法对于图像整体偏暗或整体偏亮时,通过调节参数γ可以获得较为满意的效果,但是如果图像中同时存在欠曝或过曝的区域,同一个参数就无法同时满意的效果了,因此,可引入一种γ随图像局部区域信息变化的算法来获取更为满意的效果,一种常用的形式如下:
其中的mask获取方式为:先对原图进行反色处理,然后进行一定半径的高斯模糊。
这样做的道理如下:如果mask的值大于128,说明那个点是个暗像素同时周边也是暗像素,因此γ值需要小于0以便将其增亮,mask值小于128,对应的说明当前点是个较亮的像素,且周边像素也较亮,mask值为128则不产生任何变化,同时,mask值离128越远,校正的量就越大,并且还有个特点就是纯白色和纯黑色不会有任何变化(这其实也是会产生问题的)。
如下图所示,直观的反应了不同的mask值的映射结果。
简单写一段测试代码,看看这个的效果如何:
int IM_LocalExponentialCorrection(unsigned char *Src, unsigned char *Dest, int Width, int Height, int Stride) { unsigned char *Mask = (unsigned char *)malloc(Height * Stride * sizeof(unsigned char)); IM_Invert(Src, Mask, Width, Height, Stride); // Invert Intensity IM_ExpBlur(Mask, Mask, Width, Height, Stride, 20); // Blur for (int Y = 0; Y < Height; Y++) { unsigned char *LinePS = Src + Y * Stride; unsigned char *LinePD = Dest + Y * Stride; unsigned char *LinePM = Mask + Y * Stride; for (int X = 0; X < Width; X++) { LinePD[0] = IM_ClampToByte(255 * pow(LinePS[0] * IM_INV255, pow(2, (128 - LinePM[0]) / 128.0f))); // Moroney论文的公式 LinePD[1] = IM_ClampToByte(255 * pow(LinePS[1] * IM_INV255, pow(2, (128 - LinePM[1]) / 128.0f))); LinePD[2] = IM_ClampToByte(255 * pow(LinePS[2] * IM_INV255, pow(2, (128 - LinePM[2]) / 128.0f))); LinePS += 3; LinePD += 3; LinePM += 3; } } free(Mask); return IM_STATUS_OK; }
基本按照论文的公式写的代码,未做优化,测试两张图片看看。
原图1 Moroney论文的结果
似乎效果还不错。
第一,高斯模糊的mask使用双边滤波来代替,因为双边滤波的保边特性,这样可以减少处理后的halo瑕疵。这没啥好说的。
第二,常数2使用变量α代替,并且是和图像内容相关的,具体算式如下:
当图像的整体平均值小于128时,使用计算,当平均值大于128时,使用
计算,论文作者给出了这样做的理由:对于低对比度的图像,应该需要较强烈的校正,因此α值应该偏大,而对于有较好对比度的图,