gamma原理及快速实现算法(C/C++)

参考

http://www.cambridgeincolour.com/tutorials/gamma-correction.htm

http://en.wikipedia.org/wiki/Gamma_correction

论文Gamma矫正的快速算法以及其C语言实现

一、什么是Gamma校正

Gamma校正是对输入图像灰度值进行的非线性操作,使输出图像灰度值与输入图像灰度值呈指数关系:

[2]   A是一个常数,通常取1,这个指数即为Gamma

经过Gamma校正后的输入和输出图像灰度值关系如图1所示:横坐标是输入灰度值,纵坐标是输出灰度值,蓝色曲线是gamma值小于1时的输入输出关系,红色曲线是gamma值大于1时的输入输出关系。可以观察到,当gamma值小于1时(蓝色曲线),图像的整体亮度值得到提升,同时低灰度处的对比度得到增加,更利于分辩低灰度值时的图像细节。

图一Gamma校正后的输入和输出图像灰度值关系图

上图是不同gamma值对应图像亮度的变化

二、为什么进行Gamma校正?

1. 人眼对外界光源的感光值与输入光强不是呈线性关系的,而是呈指数型关系的。在低照度下,人眼更容易分辨出亮度的变化,随着照度的增加,人眼不易分辨出亮度的变化。而摄像机感光与输入光强呈线性关系。如图2所示:

图2 人眼和摄像机的感光与实际输入光强的关系[1]。

为方便人眼辨识图像,需要将摄像机采集的图像进行gamma校正。

2. 为能更有效的保存图像亮度信息,需进行Gamma校正。

未经gamma校正和经过gamma校正保存图像信息如图3所示:

图3 未经gamma校正和经过gamma校正保存图像信息.

可以观察到,未经gamma校正的情况下,低灰度时,有较大范围的灰度值被保存成同一个值,造成信息丢失;同时高灰度值时,很多比较接近的灰度值却被保存成不同的值,造成空间浪费。经过gamma校正后,图像的信息更加逼近原图的信息从而改善了存储的有效性和效率。

3.gamma叠加的影响

如图所示对于一个标准的伽玛编码的图像文件(— ),改变显示的γ(— )将因此具有以下的总体影响(— )的图像上。

三、Gamma矫正的原理

假设图像中有一个像素,值是200,那么对这个像素进行校正必须执行如下步骤。

1.归一化:将像素值转换为 0~1 之间的实数。 算法如下: ( i + 0. 5)/256 这里包含1个除法和1个加法操作。对于像素 A 而言 ,其对应的归一化值为 0. 783203。

2.预补偿:根据公式 ,求出像素归一化后的 数据以 1/gamma 为指数的对应值。这一步包含一个 求指数运算。若 gamma 值为 2. 2 , 则 1/gamma 为 0. 454545 ,对归一化后的 A 值进行预补偿的结果就 是 0. 783203^0. 454545 = 0. 894872。

3.反归一化:将经过预补偿的实数值反变换为 0 ~ 255 之间的整数值。具体算法为: f*256 - 0. 5 此步骤包含一个乘法和一个减法运算。续前 例 ,将 A 的预补偿结果 0. 894872 代入上式 ,得到 A 预补偿后对应的像素值为 228 ,这个 228 就是最后送 入显示器的数据。

如上所述如果直接按公式编程的话,假设图像的分辨率为800*600,对它进行gamma校正,需要执行48万个浮点数乘法、除法和指数运算。效率太低,根本达不到实时的效果。

针对上述情况,提出了一种快速算法,如果能够确知图像的像素取值范围 ,例如 , 0~255之间的整数 ,则图像中任何一个像素值只能 是 0 到 255 这 256 个整数中的某一个;在 gamma值 已知的情况下 ,0~255 之间的任一整数 ,经过“归一 化、预补偿、反归一化”操作后,所对应的结果是唯一的 ,并且也落在 0~255 这个范围内。如前例 ,已知 gamma 值为 2. 2 ,像素 A 的原始值是 200 ,就可求得 经 gamma 校正后 A 对应的预补偿值为 228。基于上述原理 ,我们只需为 0~255 之间的每个整数执行一次预补偿操作 ,将其对应的预补偿值存入一个预先建立的 gamma 校正查找表(LUT:Look Up Table) ,就可以使用该表对任何像素值在 0~255 之 间的图像进行 gamma 校正。

C语言程序:

#include <math.h>

typedef unsigned char UNIT8; //用 8 位无符号数表示 0~255 之间的整数
UNIT8 g_GammaLUT[256];//全局数组:包含256个元素的gamma校正查找表
//Buildtable()函数对0-255执行如下操作:
//①归一化、预补偿、反归一化;
//②将结果存入 gamma 查找表。
//从公式得fPrecompensation=1/gamma
void BuildTable(float fPrecompensation )
{
	int i;
	float f;
	for( i=0;i<256;i++)
	{
		f=(i+0.5F)/256;//归一化
		f=(float)pow(f,fPrecompensation);
		g_GammaLUT[i]=(UNIT8)(f*256-0.5F);//反归一化
	}
}

void GammaCorrectiom(UNIT8 src[],int iWidth,int iHeight,float fGamma,UNIT8 Dst[])
{
	int iCols,iRows;
	BuildTable(1/fGamma);//gamma校正查找表初始化
	//对图像的每个像素进行查找表矫正
	for(iRows=0;iRows<iHeight;iRows++)
	{
		for(iCols=0;iCols<iWidth;iCols++)
		{
			Dst[iRows*iWidth+iCols]=g_GammaLUT[src[iRows*iWidth+iCols]];
		}
	}
}

四、利用OpenCV实现的Gamma校正

#include <opencv\cv.h>
#include <opencv\highgui.h>
#include <iostream>

using namespace cv; //下面的所有cv相关类型不用加上前缀了

int main(int argc, char* argv[])
{
	Mat img = imread(argv[1]);
	Mat& src=img;
	Mat& MyGammaCorrection(Mat& src, float fGamma);
	if(!img.data)
		return -1;
	float fGamma=1/2.2;
	MyGammaCorrection(img, fGamma);
	namedWindow("dst", CV_WINDOW_AUTOSIZE);
	imshow("dst",src);
	waitKey(0);
	return 0;
}

Mat& MyGammaCorrection(Mat& src, float fGamma)
{
    CV_Assert(src.data);  //若括号中的表达式为false,则返回一个错误的信息。

    // accept only char type matrices
    CV_Assert(src.depth() != sizeof(uchar));
    // build look up table
    unsigned char lut[256];
    for( int i = 0; i < 256; i++ )
    {
        lut[i] = pow((float)(i/255.0), fGamma) * 255.0;
    }
    //先归一化,i/255,然后进行预补偿(i/255)^fGamma,最后进行反归一化(i/255)^fGamma*255

    const int channels = src.channels();
    switch(channels)
    {
        case 1:
            {
                //运用迭代器访问矩阵元素
                MatIterator_<uchar> it, end;
                for( it = src.begin<uchar>(), end = src.end<uchar>(); it != end; it++ )
                    //*it = pow((float)(((*it))/255.0), fGamma) * 255.0;
                    *it = lut[(*it)];  

                break;
            }
        case 3:
            {  

                MatIterator_<Vec3b> it, end;
                for( it = src.begin<Vec3b>(), end = src.end<Vec3b>(); it != end; it++ )
                {
                    //(*it)[0] = pow((float)(((*it)[0])/255.0), fGamma) * 255.0;
                    //(*it)[1] = pow((float)(((*it)[1])/255.0), fGamma) * 255.0;
                    //(*it)[2] = pow((float)(((*it)[2])/255.0), fGamma) * 255.0;
                    (*it)[0] = lut[((*it)[0])];
                    (*it)[1] = lut[((*it)[1])];
                    (*it)[2] = lut[((*it)[2])];
                }  

                break;  

            }
    }  

    return src;
}  

实现结果

试验结果

原图

fGamma=2.2

fGamma=2.2

gamma原理及快速实现算法(C/C++)

时间: 2024-10-10 22:13:03

gamma原理及快速实现算法(C/C++)的相关文章

[转]快速平方根算法

在3D图形编程中,经常要求平方根或平方根的倒数,例如:求向量的长度或将向量归一化.C数学函数库中的sqrt具有理想的精度,但对于3D游戏程式来说速度太慢.我们希望能够在保证足够的精度的同时,进一步提高速度. Carmack在QUAKE3中使用了下面的算法,它第一次在公众场合出现的时候,几乎震住了所有的人.据说该算法其实并不是Carmack发明的,它真正的作者是Nvidia的Gary Tarolli(未经证实). // // 计算参数x的平方根的倒数 // float InvSqrt (float

Java中Map相关的快速查找算法与唯一性(转载)

原文地址:http://blog.csdn.net/chuyuqing/article/details/19629229 在对<Set和hashCode()>的一篇原创文章写完后,由于对自己的一些论断产生了模糊和怀疑,因此又对Set进行了一些研究,形成本篇. 在Set的使用场景中,我们不外乎看中了她存储数据的唯一性,即不能存储重复值,这在某些应用场合下是很必要的一个特性.那么从更深一层来考虑,Set究竟如何使数据不重复的呢?从另一个层面来考虑,她又如何确保在验证数据是否重复过程中的快速性呢?假

快速乘法/快速幂 算法

快速幂算法可以说是ACM一类竞赛中必不可少,并且也是非常基础的一类算法,鉴于我一直学的比较零散,所以今天用这个帖子总结一下 快速乘法通常有两类应用:一.整数的运算,计算(a*b) mod c  二.矩阵快速乘法 一.整数运算:(快速乘法.快速幂) 先说明一下基本的数学常识: (a*b) mod c == ( (a mod c) * (b mod c) ) mod c //这最后一个mod c 是为了保证结果不超过c 对于2进制,2n可用1后接n个0来表示.对于8进制,可用公式 i+3*j ==

JAVA算法4——连通性问题之路径压缩的加权快速合并算法

能否找到一个保证线性时间性能的算法,这个问题非常难.还有一些改进加权快速合并算法的简单方法.理想情况下,我们希望每个结点直接连到其树根,但又不想像快速合并算法那样改变大量连线.我们可以简单地把所检查的所有结点连到根上,从而接近理想情况.我们可以很容易地实现此方法,方法名为压缩路径,在合并操作时,经过每条路径就加一条连线,也就是把一路上遇到的对应于每个顶点的id数组值都设为连到树根上.净结果就是几乎完全把树变平坦了,逼近快速查找法所获得的理想状态. 还有其他许多方法来实现路径压缩下面程序实现路径压

高维数据的快速最近邻算法FLANN

1.     简介 在计算机视觉和机器学习中,对于一个高维特征,找到训练数据中的最近邻计算代价是昂贵的.对于高维特征,目前来说最有效的方法是 the randomized k-d forest和the priority search k-means tree,而对于二值特征的匹配 multiple hierarchical clusteringtrees则比LSH方法更加有效. 目前来说,fast library for approximate nearest neighbors (FLANN)

Atitti 文本分类 &#160;以及 垃圾邮件 判断原理 以及贝叶斯算法的应用解决方案

Atitti 文本分类  以及 垃圾邮件 判断原理 以及贝叶斯算法的应用解决方案 1.1. 七.什么是贝叶斯过滤器?1 1.2. 八.建立历史资料库2 1.3. 十.联合概率的计算3 1.4. 十一.最终的计算公式3 1.5. .这时我们还需要一个用于比较的门槛值.Paul Graham的门槛值是0.9,概率大于0.9,4 1.1. 七.什么是贝叶斯过滤器? 垃圾邮件是一种令人头痛的顽症,困扰着所有的互联网用户. 正确识别垃圾邮件的技术难度非常大.传统的垃圾邮件过滤方法,主要有"关键词法&quo

快速模糊算法

前段时间在网上看到一个快速模糊算法,性能很不错. 源博客: http://www.lellansin.com/super-fast-blur-%E6%A8%A1%E7%B3%8A%E7%AE%97%E6%B3%95.html 博主对其进行了简单的bug修正以及性能优化. 在博主机子上使用该算法对一张5000x3000的图片进行模糊处理,仅需500-600毫秒,速度非常快. 代码如下: /* * Super Fast Blur v1.1+ * Original author: Mario Klin

稀疏矩阵的普通转置与快速转置算法

稀疏矩阵的普通转置与快速转置算法 一般来说,对于系数矩阵,我们使用三元组来存储.即就是将矩阵的所有非零元素的三元组存放在一个顺序表中,如图所示: 注意一个转置的前提:该顺序表是排好序的,即行优先,列其次. 一.普通转置 这种算法比较简单,也很容易想到: 算法思想: 对M.data从头至尾扫描: ?第一次扫描时,将M.data中列号为1的三元组赋值到T.data中 ?第二次扫描时,将M.data中列号为2的三元组赋值到T.data中 ?依此类推,直至将M.data所有三元组赋值到T.data中 代

hdu oj 1061 Rightmost Digit (快速幂算法)

这里首先要讲解一下快速幂算法: 快速幂取模算法 在网站上一直没有找到有关于快速幂算法的一个详细的描述和解释,这里,我给出快速幂算法的完整解释,用的是C语言,不同语言的读者只好换个位啦,毕竟读C的人较多~ 所谓的快速幂,实际上是快速幂取模的缩写,简单的说,就是快速的求一个幂式的模(余).在程序设计过程中,经常要去求一些大数对于某个数的余数,为了得到更快.计算范围更大的算法,产生了快速幂取模算法.[有读者反映在讲快速幂部分时有点含糊,所以在这里对本文进行了修改,作了更详细的补充,争取让更多的读者一目