F. Drago等人在《Adaptive Logarithmic Mapping For Displaying High Contrast Scenes》中提到了一种HDR增强算法,本文尝试对其进行复现。
在这个亮度映射解决方案中的对数关系使用Stockham提出的转换方式作为图像处理的基础,Stockham使用如下公式作为低照度图像增强的方法:
其中,Ld为输出的亮度值,Lw为输入的亮度值,Lmax为最大的亮度值。
我曾以YUV简单实现这个算法逻辑:
YUV颜色空间介绍,低照度图像增强算法。
由于该算法应用于场景照明特征不明显的情况下,所以需要对 RGB 和 CIE XYZ 进行相互转换,以便提取出Y应用于该算法。之后在Stockham的基础上乘以一个用户可设置的曝光因子。
附:Yxy、XYZ空间介绍及与RGB的相互转换公式
最终得出的增强公式为:
其中,Ld为输出的亮度值,Lw为输入的亮度值,Lmax为最大的亮度值。L dmax是设定的一个比例因子,根据不同的显示器需要进行调整,CRT显示器可以取100,也可以取到更大值,此处默认设置为100。
b可以理解为增强强度的参数。
b的值在0.7到0.9之间对于生成感知良好的图像最有用。与b=1.0的图像相比,b=0.85的图像亮度大约是两倍,b=0.7的图像亮度大约是三倍。这影响了图像的真实性,即使由于偏差值的减少而增加的对比度自然会导致更亮的图像。原作者提出0.85作为默认参数设置。
最后再对其做一个校正,以补偿观看环境。此处实现先做ITU-R BT.709gamma校正,原作者给出了一个调整后的校正,实际可根据场景以下面的公式进行调整。
实现:
#include <iostream>
#include <opencv2/opencv.hpp>
#include "detect.h"
#include "enhance.h"
using namespace std;
using namespace cv;
double Transform(double x)
{
if (x <= 0.018)return x * 4.5;
return 1.099*pow(x,0.45) - 0.099;
}
struct zxy {
double x, y, z;
}s[2500][2500];
//sR, sG and sB (Standard RGB) input range = 0 ÷ 255
//X, Y and Z output refer to a D65/2° standard illuminant.
double rgbValueTrans(double value)
{
double output;
if(value > 0.04045)
{
output = pow( ((value + 0.055)/1.055),2.4);
}
else
{
output = value / 12.92;
}
output = output * 100 ;
return output;
}
//X, Y and Z input refer to a D65/2° standard illuminant.
//sR, sG and sB (standard RGB) output range = 0 ÷ 255
double xyzValueTrans(double value)
{
double output;
if(value > 0.0031308)
{
output = 1.055 * pow(value,1/2.4) - 0.055;
}
else
{
output = value * 12.92;
}
if (output < 0)output = 0; if (output>1)output = 1;
output = output * 255 ;
return output;
}
int test(cv::Mat input_img, cv::Mat out_img) {
int rows = input_img.rows;
int cols = input_img.cols;
double r, g, b;
double lwmax = -1.0, base = 0.73;
int Ldmax = 100;
//1.BGR转XYZ
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
b = rgbValueTrans( (double)input_img.at<Vec3b>(i, j)[0] / 255.0 );
g = rgbValueTrans( (double)input_img.at<Vec3b>(i, j)[1] / 255.0 );
r = rgbValueTrans( (double)input_img.at<Vec3b>(i, j)[2] / 255.0 );
s[i][j].x = (0.4124*r + 0.3576*g + 0.1805*b);
s[i][j].y = (0.2126*r + 0.7152*g + 0.0722*b);
s[i][j].z = (0.0193*r + 0.1192*g + 0.9505*b);
lwmax = max(lwmax, s[i][j].y);
}
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
//XYZ转Yxy
double xx = s[i][j].x / (s[i][j].x + s[i][j].y + s[i][j].z);
double yy = s[i][j].y / (s[i][j].x + s[i][j].y + s[i][j].z);
double tp = s[i][j].y;
s[i][j].y = 0.01 * Ldmax* log(s[i][j].y + 1) / log(2 + 8.0*pow((s[i][j].y / lwmax), log(base) / log(0.5))) / log10(lwmax + 1);
s[i][j].y = Transform(s[i][j].y);
double x = s[i][j].y / yy*xx;
double y = s[i][j].y;
double z = s[i][j].y / yy*(1 - xx - yy);
r = 3.2410*x - 1.5374*y - 0.4986*z;
g = -0.9692*x + 1.8760*y + 0.0416*z;
b = 0.0556*x - 0.2040*y + 1.0570*z;
r = xyzValueTrans(r);
g = xyzValueTrans(g);
b = xyzValueTrans(b);
out_img.at<Vec3b>(i, j)[0] = int(b);
out_img.at<Vec3b>(i, j)[1] = int(g);
out_img.at<Vec3b>(i, j)[2] = int(r);
}
}
return 0;
}
int main()
{
Mat src = imread("/home/seeking/Documents/0706/1.png");
Mat src1, dst;
resize(src,src1,Size(0,0),0.5,0.5);
dst = src1.clone();
imshow("src1",src1);
test(src1, dst);
imshow("dst",dst);
//Enhance gamma = Enhance();
//Mat output = gamma.gammaWithParameter(src1,2.2);
//imshow("gamma",output);
waitKey(0);
}
HDR介绍
高动态范围(High-Dynamic Range,简称HDR),又称宽动态范围技术,是在非常强烈的对比下让摄像机看到影像的特色而运用的一种技术。 当在强光源(日光、灯具或反光等)照射下的高亮度区域及阴影、逆光等相对亮度较低的区域在图像中同时存在时,摄像机输出的图像会出现明亮区域因曝光过度成为白色,而黑暗区域因曝光不足成为黑色,严重影响图像质量。摄像机在同一场景中对最亮区域及较暗区域的表现是存在局限的,这种局限就是通常所讲的“动态范围“。HDR图片是使用多张不同曝光的图片,然后再用软件组合成一张图片。它的优势是最终你可以得到一张无论在阴影部分还是高光部分都有细节的图片。在正常的摄影当中,或许你只能选择两者之一。