这个算法是我第一个实现的色调映射算法,也是五种算法中原理最清晰,实现最简单的。(原理见:一种自适应对数映射的高对比度图像显示技术) 这个算法来源于论文《Adaptive Logarithmic Mapping For Displaying High Contrast Scenes》是一篇2003年的古老算法。其实这个算法OpenCV 3.0已经实现了,但是我还是根据自己的理解又实现了一下。 实现该算法共分为四步: 第一步:把场景亮度映射到图像亮度 其实完成这步有很多方法,上述论文中提到了两种方法:一、对于静态图像,使用场景的对数平均值作为初始缩放因子,进行压缩。二、对于需要进行交互式的图像处理,可以使用亮度的对数信息的高斯模糊后的结果,作为压缩结果,范围用户自己调,默认是15%。 但是本项目使用了另一种压缩方法,即归一化。具体步骤如下:首先我们先把提取出来的场景信息进行色彩空间转化,得到亮度值;随后我们对亮度值进行归一化处理; 接下来是第二步,就是运用传说中的自适应函数: 其中Lav是对数平均值,简单来说除以对数平均值,映射效果会更好。为什么会更好,原因在讲基于Retinex的色调映射算法时会讲。 第三步:伽马校正 第四步:色彩空间还原 完整代码如下: 这些步骤没有很明显的前后之分,比如在我的项目中,色彩空间还原就放在了伽马校正的前面。 上面用到的色彩空间转化,是RGB到XYZ,其中Y代表亮度,在映射过程中就使用了亮度这一个变量。色彩空间还原那肯定是XYZ到RGB了。 空间转化矩阵如下: for(int i = 0; i < rows; i++){ for(int j = 0; j < cols; j++){ //通过这么写,不仅可以实现色调映射,还可以实现图像增强 if(strstr(fileName,".hdr")!=NULL) { //提取场景的RGB数据 RGB[0]=result.cols[i*cols*3+j*3+0]; RGB[1]=result.cols[i*cols*3+j*3+1]; RGB[2]=result.cols[i*cols*3+j*3+2]; } else { RGB[2]=(double)re.at<Vec3b>(i,j)[0]; RGB[1]=(double)re.at<Vec3b>(i,j)[1]; RGB[0]=(double)re.at<Vec3b>(i,j)[2]; } //色彩空间转化 //转化成XYZ表示,虽然缩放只缩放Y,但是为了后面进行转化,这里把x,z也求出来; xyz[i][j].x=Mrgb[0][0]*RGB[0]+Mrgb[1][0]*RGB[1]+Mrgb[2][0]*RGB[2]; xyz[i][j].y=Mrgb[0][1]*RGB[0]+Mrgb[1][1]*RGB[1]+Mrgb[2][1]*RGB[2]; xyz[i][j].z=Mrgb[0][2]*RGB[0]+Mrgb[1][2]*RGB[1]+Mrgb[2][2]*RGB[2]; //获取XYZ的比例 double rateX=xyz[i][j].x/(xyz[i][j].x+xyz[i][j].y+xyz[i][j].z); double rateY=xyz[i][j].y/(xyz[i][j].x+xyz[i][j].y+xyz[i][j].z); //归一化 if(strstr(fileName,".hdr")!=NULL) xyz[i][j].y=atan(xyz[i][j].y)*2/PI; else xyz[i][j].y/=255.0; //还原x,y,z的比率 xyz[i][j].x=xyz[i][j].y/rateY*rateX; xyz[i][j].z=xyz[i][j].y/rateY*(1-rateX-rateY); Lmax = max(xyz[i][j].y,Lmax);//取出场景最亮 Lsum += log(xyz[i][j].y+a);//对场景亮度进行对数求和 } }
这个公式看起来很唬人,其实并不难://对数变换,本算法的核心 xyz[i][j].y = 1.0 * log(xyz[i][j].y/Lav + 1) / log(2.0 + 8.0*pow((xyz[i][j].y/Lav / Lmax), log(bias) / log(0.5))) / log10(Lmax + 1);
//伽马校正 RGB[0]=C*pow(RGB[0],r); RGB[1]=C*pow(RGB[1],r); RGB[2]=C*pow(RGB[2],r);
//转化成RGB表示 RGB[0] = Mxyz[0][0]*xyz[i][j].x + Mxyz[1][0]*xyz[i][j].y + Mxyz[2][0]*xyz[i][j].z; RGB[1] = Mxyz[0][1]*xyz[i][j].x + Mxyz[1][1]*xyz[i][j].y + Mxyz[2][1]*xyz[i][j].z; RGB[2] = Mxyz[0][2]*xyz[i][j].x + Mxyz[1][2]*xyz[i][j].y + Mxyz[2][2]*xyz[i][j].z;
Mat Tone_Mapping(char* fileName, double bias, double C, double r)//基于对数变换的HDR { int rows,cols; HDRLoaderResult result; Mat re; //判断图像类型 if(strstr(fileName,".hdr")!=NULL) { //获取图像信息 int ret = HDRLoader::load(fileName, result); rows=result.height; cols=result.width; } else { //加载图像 re = imread(fileName, 1); rows=re.rows; cols=re.cols; } //定义矩阵 Mat out_image (rows,cols,CV_8UC3);//结果图像 Mat img(rows,cols,CV_8UC3);//过程图像 //定义变量 double PI=acos(-1.0); double Ymin=INF,Ymax=-INF,Ysum=0,Yav=0; double Lmin=INF,Lmax=-INF,Lsum=0,Lav=0; for(int i = 0; i < rows; i++){ for(int j = 0; j < cols; j++){ //通过这么写,不仅可以实现色调映射,还可以实现图像增强 if(strstr(fileName,".hdr")!=NULL) { RGB[0]=result.cols[i*cols*3+j*3+0]; RGB[1]=result.cols[i*cols*3+j*3+1]; RGB[2]=result.cols[i*cols*3+j*3+2]; } else { RGB[2]=(double)re.at<Vec3b>(i,j)[0]; RGB[1]=(double)re.at<Vec3b>(i,j)[1]; RGB[0]=(double)re.at<Vec3b>(i,j)[2]; } //转化成XYZ表示,虽然缩放只缩放Y,但是为了后面进行转化,这里把x,z也求出来; xyz[i][j].x=Mrgb[0][0]*RGB[0]+Mrgb[1][0]*RGB[1]+Mrgb[2][0]*RGB[2]; xyz[i][j].y=Mrgb[0][1]*RGB[0]+Mrgb[1][1]*RGB[1]+Mrgb[2][1]*RGB[2]; xyz[i][j].z=Mrgb[0][2]*RGB[0]+Mrgb[1][2]*RGB[1]+Mrgb[2][2]*RGB[2]; //获取XYZ的比例 double rateX=xyz[i][j].x/(xyz[i][j].x+xyz[i][j].y+xyz[i][j].z); double rateY=xyz[i][j].y/(xyz[i][j].x+xyz[i][j].y+xyz[i][j].z); //归一化 if(strstr(fileName,".hdr")!=NULL) xyz[i][j].y=atan(xyz[i][j].y)*2/PI; else xyz[i][j].y/=255.0; //还原x,y,z的比率 xyz[i][j].x=xyz[i][j].y/rateY*rateX; xyz[i][j].z=xyz[i][j].y/rateY*(1-rateX-rateY); Lmax = max(xyz[i][j].y,Lmax);//取出场景最亮 Lsum += log(xyz[i][j].y+a);//对场景亮度进行对数求和 } } Lav = exp(Lsum/(1.0*rows*cols));//求取对数平均值 Lmax=Lmax/Lav; for(int i = 0; i < rows; i++){ for(int j = 0; j < cols; j++){ //记录XYZ空间下x,y,z的比率 double rateX=xyz[i][j].x/(xyz[i][j].x+xyz[i][j].y+xyz[i][j].z); double rateY=xyz[i][j].y/(xyz[i][j].x+xyz[i][j].y+xyz[i][j].z); //对数变换,本算法的核心 xyz[i][j].y = 1.0 * log(xyz[i][j].y/Lav + 1) / log(2.0 + 8.0*pow((xyz[i][j].y/Lav / Lmax), log(bias) / log(0.5))) / log10(Lmax + 1); //还原x,y,z的比率 xyz[i][j].x=xyz[i][j].y/rateY*rateX; xyz[i][j].z=xyz[i][j].y/rateY*(1-rateX-rateY); //转化成RGB表示,色彩空间还原 RGB[0] = Mxyz[0][0]*xyz[i][j].x + Mxyz[1][0]*xyz[i][j].y + Mxyz[2][0]*xyz[i][j].z; RGB[1] = Mxyz[0][1]*xyz[i][j].x + Mxyz[1][1]*xyz[i][j].y + Mxyz[2][1]*xyz[i][j].z; RGB[2] = Mxyz[0][2]*xyz[i][j].x + Mxyz[1][2]*xyz[i][j].y + Mxyz[2][2]*xyz[i][j].z; if (RGB[0] < 0)RGB[0] = 0; if (RGB[0]>1)RGB[0] = 1; if (RGB[1] < 0)RGB[1] = 0; if (RGB[1]>1)RGB[1] = 1; if (RGB[2] < 0)RGB[2] = 0; if (RGB[2]>1)RGB[2] = 1; //伽马校正 RGB[0]=C*pow(RGB[0],r); RGB[1]=C*pow(RGB[1],r); RGB[2]=C*pow(RGB[2],r); if (RGB[0] < 0)RGB[0] = 0; if (RGB[0]>1)RGB[0] = 1; if (RGB[1] < 0)RGB[1] = 0; if (RGB[1]>1)RGB[1] = 1; if (RGB[2] < 0)RGB[2] = 0; if (RGB[2]>1)RGB[2] = 1; //修正补偿 RGB[0] = Transform(RGB[0]); RGB[1] = Transform(RGB[1]); RGB[2] = Transform(RGB[2]); out_image.at<Vec3b>(i, j)[0] = (int)(RGB[2]*255); out_image.at<Vec3b>(i, j)[1] = (int)(RGB[1]*255); out_image.at<Vec3b>(i, j)[2] = (int)(RGB[0]*255); } } return out_image; }
运行效果如下:
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算