zoukankan      html  css  js  c++  java
  • Gamma原理及快速实现算法(C/C++)(转)

    源:Gamma原理及快速实现算法(C/C++)

    原文:http://blog.csdn.net/lxy201700/article/details/24929013

    参考

    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所示:

     

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

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

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

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

     

     

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

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

    3.gamma叠加的影响

     

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

     

    三、Gamma矫正的原理

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

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

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

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

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

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

  • 相关阅读:
    C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!
    Java8的新特性以及与C#的比较
    点旋转坐标变换
    vc++返回模块路径
    为什么不要 "lock(this)" ? lock object 并是readonly(转载)
    《黄帝内经》要义
    C++多线程编程简单实例
    c++ 获取文件大小
    c# 获取文件夹大小
    自动驾驶仿真工具的下载与安装
  • 原文地址:https://www.cnblogs.com/LittleTiger/p/4977551.html
Copyright © 2011-2022 走看看