【图像处理】一种低光照图像的亮度提升方法(Adaptive Local Tone Mapping Based on Retinex for High Dynamic Range Images)



【fishing-pan:https://blog.csdn.net/u013921430 转载请注明出处】

前言

  在实际的拍照过程中,常常会遇到,光线不足的情况。这时候单反用户一般会调大感光度,调大光圈,以让照片整体更清晰,更亮。那么如果照片已经被拍的很暗了,怎么办呢?这时候我们可以利用算法来提升图像整体的光照情况,让图像更清晰。

  2013年这篇《Adaptive Local Tone Mapping Based on Retinex for High Dynamic Range Images》发表在了IEEE上,如题目所说,文章提到将高动态图像在低动态范围显示设备上进行显式时,会面临信息丢失的问题。因此结合传统的CENTER/SURROUND RETINEX 技术提出了全局自适应和局部自适应的HDR实现过程,对HDR image 进行色调映射。而文中的全局自适应方法对于低照度图像具有很好的照度提升效果。作者将他的Matlab脚本上传到了Github,有兴趣的可以点击这里去查看。

全局自适应原理

  全局自适应方法的原理很简单,就是两个公式;





L


g



(


x


,


y


)


=




l


o


g


(



L


w



(


x


,


y


)


/




L


w



ˉ



+


1


)




l


o


g


(



L



w


m


a


x




/




L


w



ˉ



+


1


)





L_{g}(x,y)=\frac{log(L_{w}(x,y)/\bar{L_{w}}+1)}{log(L_{wmax}/\bar{L_{w}}+1)}



  上述式子中,





L


g



(


x


,


y


)



L_{g}(x,y)


代表全局自适应处理的输出结果;





L


w



(


x


,


y


)



L_{w}(x,y)


表示输入图像的亮度值;





L



w


m


a


x





L_{wmax}


表示输入图像亮度的最大值;






L


w



ˉ




\bar{L_{w}}


表示输入图像的亮度值的对数平均值;由以下公式求得;






L


w



ˉ



=


e


x


p



(



1



m





n







l


o


g


(


σ


+



L


w



(


x


,


y


)


)


)




\bar{L_{w}}=exp\left ( \frac{1}{m*n}\sum log(\sigma +L_{w}(x,y))\right )



  其中,




m





n



m*n


代表图像的尺寸。




σ



\sigma


是一个很小的值,为了防止遇到图像中亮度为0的黑点的情况;

  文中提到“随着对数均值趋近于高值,函数的形状从对数趋势转换为线性趋势,因此对低对数均值的图像具有更好的提升效果。”这个方法利用到了图像的对数均值,这就是自适应的体现。

  我在Matlab中分别简单的画了一下曲线,发现在对数均值比较大的时候曲线确实非常接近直线。
在这里插入图片描述

代码&结果

   我根据自己的理解以及作者的Matlab脚本,分别利用OpenCV实现了全局自适应方法。两者有很多细节不一样,结果也有一些差异;
   首先是作者的matlab 代码;

function outval = ALTM_Retinex(I)
II = im2double(I);
Ir=double(II(:,:,1)); Ig=double(II(:,:,2)); Ib=double(II(:,:,3));
% Global Adaptation
Lw = 0.299 * Ir + 0.587 * Ig + 0.114 * Ib;% input world luminance values
Lwmax = max(max(Lw));% the maximum luminance value
[m, n] = size(Lw);
Lwaver = exp(sum(sum(log(0.001 + Lw))) / (m * n));% log-average luminance
Lg = log(Lw / Lwaver + 1) / log(Lwmax / Lwaver + 1);
gain = Lg ./ Lw;
gain(find(Lw == 0)) = 0;
outval = cat(3, gain .* Ir, gain .* Ig, gain .* Ib);
figure;
imshow(outval)

   接着是我参考作者提供的脚本实现的C++ 代码

//-------------------------------
//函数名:adaptHDR;参照作者脚本实现
//函数功能:全局自适应光照度提升
//参数:Mat &scr,输入图像
//参数:Mat &dst,输出图像
//------------------------------
bool adaptHDR(Mat &scr, Mat &dst)
{

	if (!scr.data)  //判断图像是否被正确读取;
	{
		cerr << "输入图像有误"<<endl;
		return false;
	}

	int row = scr.rows;
	int col = scr.cols;


	Mat ycc;                        //转换空间到YUV;
	cvtColor(scr, ycc, COLOR_RGB2YUV);

	vector<Mat> channels(3);        //分离通道,取channels[0];
	split(ycc, channels);


	Mat Luminance(row, col, CV_32FC1);
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			Luminance.at<float>(i, j) =(float)channels[0].at<uchar>(i, j)/ 255;
		}
	}


	double log_Ave = 0;
	double sum = 0;
	for (int i = 0; i < row; i++)                 //求对数均值
	{
		for (int j = 0; j < col; j++)
		{
			sum += log(0.001 + Luminance.at<float>(i, j));
		}
	}
	log_Ave = exp(sum / (row*col));

	double MaxValue, MinValue;      //获取亮度最大值为MaxValue;
	minMaxLoc(Luminance, &MinValue, &MaxValue);

	Mat hdr_L (row,col,CV_32FC1);
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			hdr_L.at<float>(i, j) = log(1 + Luminance.at<float>(i, j) / log_Ave) / log(1 + MaxValue / log_Ave);


			if (channels[0].at<uchar>(i, j) == 0)   //对应作者代码中的gain = Lg ./ Lw;gain(find(Lw == 0)) = 0; 
			{
				hdr_L.at<float>(i, j) = 0;
			}
			else
			{
				hdr_L.at<float>(i, j) /= Luminance.at<float>(i, j);
			}
		
		}
	}

	vector<Mat> rgb_channels;        //分别对RGB三个通道进行提升
	split(scr, rgb_channels);
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			int r = rgb_channels[0].at<uchar>(i, j) *hdr_L.at<float>(i, j); if ( r> 255){r = 255; }
			rgb_channels[0].at<uchar>(i, j) = r;

			int g = rgb_channels[1].at<uchar>(i, j) *hdr_L.at<float>(i, j); if (g> 255){ g = 255; }
			rgb_channels[1].at<uchar>(i, j) = g;

			int b = rgb_channels[2].at<uchar>(i, j) *hdr_L.at<float>(i, j); if (b> 255){ b = 255; }
			rgb_channels[2].at<uchar>(i, j) = b;
		}
	}
	merge(rgb_channels, dst); 

	return true;
}

  最后是我自己根据自己的理解实现代码

//-------------------------------
//函数名:my_AdaptHDR;自己理解实现
//函数功能:全局自适应光照度提升
//参数:Mat &scr,输入图像
//参数:Mat &dst,输出图像
//------------------------------

bool my_AdaptHDR(Mat &scr, Mat &dst)
{

	if (!scr.data)  //判断图像是否被正确读取;
	{
		cerr << "输入图像有误" << endl;
		return false;
	}

	int row = scr.rows;
	int col = scr.cols;


	Mat ycc;                        //转换空间到YUV;
	cvtColor(scr, ycc, COLOR_RGB2YUV);

	vector<Mat> channels(3);        //分离通道,取channels[0];
	split(ycc, channels);


	double log_Ave = 0;
	double sum = 0;
	for (int i = 0; i < row; i++)   //求对数均值
	{
		for (int j = 0; j < col; j++)
		{
			sum += log(0.001 + channels[0].at<uchar>(i, j));
		}
	}
	log_Ave = exp(sum / (row*col));


	double MaxValue, MinValue;      //获取亮度最大值为MaxValue;
	minMaxLoc(channels[0], &MinValue, &MaxValue);

	Mat hdr_L(row, col, CV_32FC1);
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			hdr_L.at<float>(i, j) = log(1 + channels[0].at<uchar>(i, j) / log_Ave) / log(1 + MaxValue / log_Ave);
		}
	}

	double L_MaxValue, L_MinValue;            //获取亮度最大值为MaxValue;
	minMaxLoc(hdr_L, &L_MinValue, &L_MaxValue);
	
	for (int i = 0; i < row; i++)            //对亮度通道进行提升;
	{
		for (int j = 0; j < col; j++)
		{
			channels[0].at<uchar>(i, j) = floor(0.5 + 255 * (hdr_L.at<float>(i, j) - L_MinValue) / (L_MaxValue - L_MinValue));
		}
	}

	merge(channels, ycc);
	cvtColor(ycc, dst, COLOR_YUV2RGB);

	return true;

}

   两种思路的区别主要在于,我的方法是先把图像重RGB空间转换到YUV空间,然后对亮度进行提升,然后转换回RGB空间。这样做有一个问题,只提升了图像的亮度,而没有提高色度和浓度,图像会显得很亮,但是色彩不够鲜艳。而从作者提供的代码的思路来看,他用新得到的图像亮度除以原始图像亮度,得到一个亮度增益,然后将这个增益附加在每个颜色通道上。这样做三通道可以得到同样程度的亮度增强,但是这种方法可能会让图像色彩过饱和。

  来看看结果;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

   到底谁的思路好,我觉得我还是尊重作者的思路。但是也许有些情况下,我的方法会取得更好的效果,也可能存在更好的思路。但是最主要的方法还是在上面的两个公式中。而作者在文中主要介绍的是局部的映射方法,全局自适应是为后面做局部自适应进行铺垫的,当然,这篇博客中,不会讲到局部的方法了。

   对于低照度图像的计处理,我并不在行,这也是第一次接触,有些原理上面的东西还没摸清楚,只是觉得有意思就去实现了一下,有些不足的地方,还请大家指针(指正)。

参考

  1. Ahn H, Keum B, Kim D, et al. Adaptive local tone mapping based on retinex for high dynamic range images[C]//Consumer Electronics
    (ICCE), 2013 IEEE International Conference on. IEEE, 2013:
    153-156.

  2. https://github.com/IsaacChanghau/OptimizedImageEnhance/tree/master/matlab/ALTMRetinex