G-channel
之前研究低光图像增强时,看到一篇博客,里面介绍了一种方法,没有说明出处,也没有说明方法的名字,这里暂时叫做 G-channel 算法。
博客地址:低照度图像增强(附步骤及源码)_低照度图像增强算法_grafx的博客-CSDN博客
算法过程
博客里面简单说明了算法的执行过程,这里重复如下:
- 将绿色通道反色后作为系数值,分别与各个通道相乘,得到新图层;
- 将新图层与原图做一次滤色混合,f(a,b)=1-(1-a)*(1-b);
为了了解算法原理,经过查找,找到了几篇博客:
- 基于photoshop滤色混合模式的图片亮度改变_悍然的博客-CSDN博客
- 正片叠底(Multiply)和滤色(Screen)是两种基本的混合模式_滤色和正片叠底_e财富800的博客-CSDN博客
- Photoshop图层混合模式的计算公式_pizi0475的博客-CSDN博客
从这几篇博客里,可以将算法过程归纳为Photoshop的2个处理步骤:
- Multiply正片叠底:C=(A×B)/255,A为基色,B为混合色,会使得图像C整体亮度更暗
- Screen滤色:C=255-(A反相×B反相)/255,与上面相反,会使得图像C整体亮度更亮
算法实现
博客中给出了opencv的实现代码,下面使用matlab进行了仿真,代码如下所示:
close all;clear;clc;
src = imread('8.bmp');
im = double(src);
r = im(:,:,1);
g = im(:,:,2);
b = im(:,:,3);
g_alpha = 255 - g;
r1 = r .* g_alpha / 255;
g1 = g .* g_alpha / 255;
b1 = b .* g_alpha / 255;
r = 255 - (255 - r) .* (255 - r1) / 255;
g = 255 - (255 - g) .* (255 - g1) / 255;
b = 255 - (255 - b) .* (255 - b1) / 255;
dst = cat(3, r, g, b);
dst = uint8(round(dst));
imshow([src, dst])
由于找不到博客里测试图像的原图,这里换了另外一张测试图像,结果如下,这里执行2次算法,可以看到结果与博客里面的结果还是很相似的,说明仿真没有问题。
为了方便分析,这里对代码做了调整(原始代码,数据范围为[0,255],调整后的代码,数据范围归一化到[0,1]),如下所示,与原始结果是一致:
close all;clear;clc;
src = imread('8.bmp');
im = double(src)/255;
r = im(:,:,1);
g = im(:,:,2);
b = im(:,:,3);
g_alpha = 1 - g;
r1 = r .* g_alpha;
g1 = g .* g_alpha;
b1 = b .* g_alpha;
r = 1 - (1 - r) .* (1 - r1);
g = 1 - (1 - g) .* (1 - g1);
b = 1 - (1 - b) .* (1 - b1);
dst = cat(3, r, g, b);
dst = uint8(round(dst*255));
imshow([src, dst])
原理分析
下面就个人理解来对算法原理做个简单的分析。
步骤一:Multiply正片叠底
正片叠底公式为:C=(A×B)/255,数据范围都为[0,255],这里全部都归一化到[0,1],则公式变为:C=A×B。
任何一个数乘以一个小于1的数以后,都会变小,即C<A(C=A×B),因而图像整体会变暗。
这步里面的B为G通道取反,以G通道进行说明,处理后为:g1=g×(1-g)。原始图像中本来很亮的像素值gh,取反之后变为1-gh,变得很小,处理后会乘以一个很小的小数,使得很亮的像素值gh降低的很多;而原本很暗的像素值gl,取反之后1-gl,变得很大,处理后会乘以一个很大的小数,使得很暗的像素值gl降低的很少。最终的处理结果是图像中亮的像素变很暗,而暗的像素稍微变暗,因而图像会整体变暗。
我们换一个方式来看,公式是一个二次函数,开口朝下,对称轴为0.5,超过0.5部分,单调递减,即原始亮度越亮,处理后亮度越暗,如下所示为其函数关系图:
下面是算法实际仿真结果,如下所示:
close all;clear;clc;
src = imread('8.bmp');
im = double(src)/255;
r = im(:,:,1);
g = im(:,:,2);
b = im(:,:,3);
g_alpha = 1 - g;
r1 = r .* g_alpha;
g1 = g .* g_alpha;
b1 = b .* g_alpha;
dst = cat(3, r1, g1, b1);
dst = uint8(round(dst*255));
imshow([src, dst])
可以看到,图像整体亮度变暗,且图中白色的杯盘变得很暗,与上面的分析很吻合。
步骤二:Screen滤色
同样,归一化到[0,1]后,Screen滤色公式为:C=1-(1-A)×(1-B)。如果用来做图像增强,则需要A和B有很强的相关性;如果是为了实现某种效果,则B跟这种效果密切相关(比如让某个区域更亮)。为了简化说明,这里让B=A,同样以G通道进行说明:g2=1-(1-g)×(1-g),这里先单独看Screen滤色。从第一步的分析可知,(1-g)可以使图像中亮的像素值降低的更多,然后取反,这样会使得图像中亮的像素增加的更多,暗的像素值增加的很少,图像整体亮度提升。
我们再来看看g2这个函数,它是一个二次函数,开口朝下,对称轴为1,[0,1]单调递增,如下所示:
close all;clear;clc;
src = imread('8.bmp');
im = double(src)/255;
r = im(:,:,1);
g = im(:,:,2);
b = im(:,:,3);
g_alpha = 1 - g;
r2 = 1 - (1- r) .* g_alpha;
g2 = 1 - (1- g) .* g_alpha;
b2 = 1 - (1- b) .* g_alpha;
dst = cat(3, r2, g2, b2);
dst = uint8(round(dst*255));
figure, imshow([src, dst])
可以看到,图像整体亮度变亮,且图中白色的杯盘变得更亮,与上面的分析很吻合。
这样就会出现一个问题,图像中原本比较亮的像素值处理后会更亮,容易出现饱和,为了避免出现这种问题,就需要结合步骤一里面的Multiply正片叠底,先对亮的像素值进行抑制再来提升亮度,从而可以减小出现饱和的风险。
下面我们结合步骤一Multiply正片叠底一起看,对G通道,g3=1-(1-g)×(1-g1)=1-(1-g)×(1-g×(1-g)),它是一个3次函数,如下所示:
可以看到,最终的曲线g3,相比于Screen滤色g2,在亮的像素增强上进行了抑制。其最终增强效果如文章开头所示。