zoukankan      html  css  js  c++  java
  • 灰度图像--图像增强 直方图均衡化(Histogram equalization)

    灰度图像--图像增强 直方图均衡化(Histogram equalization)

    转载请标明本文出处:http://blog.csdn.net/tonyshengtan,欢迎大家转载,发现博客被某些论坛转载后,图像无法正常显示,无法正常表达本人观点,对此表示很不满意。有些网站转载了我的博文,很开心的是自己写的东西被更多人看到了,但不开心的是这段话被去掉了,也没标明转载来源,虽然这并没有版权保护,但感觉还是不太好,出于尊重文章作者的劳动,转载请标明出处!!!!

    文章代码已托管,欢迎共同开发:https://github.com/Tony-Tan/DIPpro

    开篇废话

     
            废话开始,图像处理这些代码已经有三千多行了,不多,但是感觉多加练习以后对算法理解和写代码的能力上都有很大提高,毕竟对于算法来说想明白了一定要用一下才会真正掌握,但不能靠记忆去记住一个算法,这就需要我们懒人的天性,不愿意记住完整的公式,更愿意记住一个简单的起始,通过自己的理解和数学推导算法,这是个很不错的 方法,而写代码属于一种技术工作,熟能生巧,要多加练习,并且也要思考其中的技术细节,总之,做一切事情思考下还是不错的。
           废话完成,说说直方图均衡,在冈萨雷斯的书里面直方图均衡化在第三章提出,因为之前想按照书上目录上的顺序来写着一些列的博客,后来发现还是自己总结下的学习思路,按照自己理解的知识网络来走,所以刚要写直方图均衡的时候就是转向自己的节奏开始按照二值图像,灰度图像,彩色图像的知识结构介绍。
           直方图均衡的目的和前面灰度变换一样,为了增强对比度,使图像的灰度分布在整个灰度范围内更加均衡,其中直方图需要来解释下,直方图是个统计概念,比如我们有十种颜色的球,每种颜色的球有不同的数量,假设颜色分布为a0~a9,数量为n(x)(x取值为a0~a9)那么直方图就是以a0~a9为横坐标,n为纵坐标的统计直方图:
           如果将上图中的数据归一化(每个分量除以球数总和),也就是使得各分量总和为1,各个分量就表示这种颜色的球出现的频率,就得到频率直方图。
           如果将各种颜色换成各灰度值,球的个数等效的换成具有该灰度的像素数量,或者换成该灰度出现的频率,就成了图像的直方图,对于彩色图像和灰度图像,直方图具有重要的统计意义,而对于二值图像来说,该意义不大,因为二值图像就两个灰度,所以其只能反映黑白面积比例。
           直方图均衡的目的是为了使灰度分布的更广泛,从而来拉伸对比度:
           事实表明当灰度的直方图范围从上面的左边变换成右边后,图像对比度得到提升,也就达到了我们增强图像的目的--更便于观察,更容易区分不同灰度间细节。
     

    数学原理

     
         直方图变换的最终操作和前面提到的灰度操作是一样的,即:
          这是一种从灰度到灰度的映射,并且该映射与前面伽马变换对数变换的映射不同的是,它不具有确定的表达公式,而是根据原始图像的灰度分布不同而“自适应”的产生映射,并且必须具有以下两个性质:
    1. 该函数必须单调递增(因为要从s反射回r所以该函数必须是严格的单调递增,即r和s为一对一的关系)
    2. 0<=r<=L-1时,必须满足0<=s<=L-1
          这是两点约束,即我们找到的T必须使得上面成立,而且还要达到均衡直方图的目的。即完成下面的转化:
    上面的是直方图的原始分布和目标分布,下面是响应的T。
    推导过程:
           先提出一个概率密度函数pdf的概念,就是每个灰度值对应出现的概率,用p表示,pr(r)表示原始灰度r出现的概率,其计算是用灰度值为r的像素总个数除以图像的像素总个数。同理变换后的ps(s)表示变换后灰度为s的像素的概率。
    1. r到s是一对一的映射,所以若r0映射到s0那么pr(r0)=ps(s0)这个式子是因为像素个数不会改变,只是对应的灰度值改变了,这个就是我们接下来要用到的最基本的原理。
    2. 我们的目标灰度分布是均匀分布,也就是上图右上的概率分布图,因为绿色部分面积必然为1,所以,ps的目标分布为:                                          
    根据上述的基本原理和假设存在:
    根据积分定理,w为积分假变量,那么:
    所以:
    将上面离散化:
    上面为大概的公式推导,如有不严谨之处还请指出。
    所以我们将按照上面得出的结论进行编程:
     
     

    代码

     
    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. /******************************************************************************************** 
    2.  直方图基本操作 
    3.  *******************************************************************************************/  
    4. void InitMappingTable(void * arry,int size,int Data_type){  
    5.     if(Data_type==TABLE_INT)  
    6.         for(int i=0;i<size;i++)  
    7.             ((int*)arry)[i]=0;  
    8.     else if(Data_type==TABLE_CHAR)  
    9.         for(int i=0;i<size;i++)  
    10.             ((char*)arry)[i]=0;  
    11.     else if(Data_type==TABLE_DOUBLE)  
    12.         for(int i=0;i<size;i++)  
    13.             ((double*)arry)[i]=0;  
    14.       
    15. }  
    16. void InitHistogram(int *hist){  
    17.     for(int i=0;i<GRAY_LEVEL;i++)  
    18.         hist[i]=0;  
    19. }  
    20.   
    21. void setHistogram(double *src,int *hist,int width,int height){  
    22.     InitHistogram(hist);  
    23.     for(int j=0;j<height;j++)  
    24.         for(int i=0;i<width;i++){  
    25.             int tempv=src[j*width+i];  
    26.             hist[tempv]++;  
    27.         }  
    28. }  
    29. int findHistogramMax(int *hist){  
    30.     for(int i=GRAY_LEVEL-1;i>=0;i--){  
    31.         if(hist[i]!=0)  
    32.             return i;  
    33.     }  
    34.     return -1;  
    35.   
    36. }  
    37. int findHistogramMin(int *hist){  
    38.     for(int i=0;i<GRAY_LEVEL;i++){  
    39.         if(hist[i]!=0)  
    40.             return i;  
    41.     }  
    42.     return -1;  
    43. }  
    44. void fillMaptable(double * map){  
    45.       
    46.     for(int i=1;i<GRAY_LEVEL;i++){  
    47.         if(map[i]==0)  
    48.             map[i]=map[i-1];  
    49.       
    50.     }  
    51.   
    52. }  
    53. /******************************************************************************************** 
    54.  直方图均衡 
    55.  *******************************************************************************************/  
    56. //均衡直方图,将原图直方图,经过公式得到目标直方图  
    57. void EqualizationHist(int *src_hist,double *dst_map){  
    58.     int temphist[GRAY_LEVEL];  
    59.     InitHistogram(temphist);  
    60.     int max=findHistogramMax(src_hist);  
    61.     int min=findHistogramMin(src_hist);  
    62.     temphist[min]=src_hist[min];  
    63.     for(int i=min+1;i<=max;i++)  
    64.         temphist[i]=temphist[i-1]+src_hist[i];  
    65.     for(int i=min;i<=max;i++)  
    66.         temphist[i]-=temphist[min];  
    67.     int total=temphist[max];  
    68.     for(int i=min;i<=max;i++){  
    69.         dst_map[i]=((double)GRAY_LEVEL-1.0)*temphist[i]/total;  
    70.     }  
    71.       
    72. }  
    73. //直方图均很,用输入图像得到输出图像  
    74. void HistogramEqualization(double *src,double *dst,int width,int height){  
    75.     int hist[GRAY_LEVEL];  
    76.     setHistogram(src, hist, width, height);  
    77.     double GrayMappingTable[GRAY_LEVEL];  
    78.     InitMappingTable(GrayMappingTable,GRAY_LEVEL,TABLE_DOUBLE);  
    79.     EqualizationHist(hist, GrayMappingTable);  
    80.     for(int i=0;i<width;i++)  
    81.         for(int j=0;j<height;j++)  
    82.             dst[j*width+i]=GrayMappingTable[(int)src[j*width+i]];  
    83.       
    84. }  


     

    结果

     
    原图:
    原图直方图:
    直方图均衡后图片:
    直方图均衡后直方图:
     

    总结

     
            上面给出的结果为经典结果,很多文章都使用的这幅图片,值得解释的是,虽然我们从r到s的映射是一对一的,但r和s是离散的整数,如果s被映射到非整数,将就近取整,所以有些灰度值会被合并。
            直方图运算速度快,效果好,应用范围很广,故总结如上。
            待续。。。
     
     
     
     
     
    直方图均衡化算法实现
    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://qianqing13579.blog.51cto.com/5255432/1559162

    直方图

    直方图(histogram)是灰度级的函数,它表示图像中具有每种灰度级的像素的个数,

    反映原图中各种灰度值分布的情况。

    如下图所示,灰度直方图的横坐标是灰度级,纵坐标是该灰度级出现的频率,是图像的最基本的统计特征。

    上面的是标准直方图

    灰度统计累计直方图:

                      H(k)= ∑ni(i<=k)

    累积直方图中第k列的高度是图像中所有灰度值<=k的像素的个数

    灰度直方图的求取算法实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void GetHistogram(BYTE *image_Src, int width, int height, unsigned long *histogram)
    {
        int pixelCount = width*height;//imageSize->pixelCount
        memset(histogram, 0, 256*4);//注意最后一个参数是数组的大小(单位是字节)
        for (int i = 0; i <= pixelCount - 1; ++i)
        {
            int gray = image_Src[0];
            histogram[gray]++;
            //下一个像素
            image_Src+=1;
        }
    }

    直方图的特点

    直方图具有很多的优点,直方图能反映图像的概貌,

    • 图像中有几类目标,目标和背景的分布如何;

    • 通过直方图可以直接计算图像中的最大亮度、最小亮度、平均亮度、对比度以及中间亮度等。

    • 使用直方图可以完成图像分割、目标检索等。因为不同的目标具有不同的颜色分布。使用归一化直方图作目标匹配,还不易受到目标翻转和目标大小变化的影响。

    • 在图像查询的系统中,直方图有很大的应用,用它存储目标的特征占有空间小,且执行速度快。

    • 其缺点:因其没有记录位置信息,不同的图像会具有相同或相近的直方图。一幅图像旋转、翻转后的直方图是相同的;放大、缩小后的直方是相近的。

    直方图均衡化

    原理

    为了增强图像整体的对比效果,增加灰度值的动态范围

    由图像点运算可知,图像增强的公式可以表示为

    G(x,y)=F(g(x,y))

    这里,由于要增强对比效果,所以
    这里假设原图灰度级范围位为采用归一化的[0,1]

    • F()在整个灰度级范围[0,1]内是递增函数(因为要增强对比)

    • F()的值域也是[0,1]

    可以证明累计分布函数满足上面的变换函数要求,而图像中,这个累计分布函数,就是原始图像的累计直方图。
    F()通常都是作为查找表LUT出现的,因为是离散的,所以累计直方图也是可以作为LUT出现

    实际上:累计分布直方图作为图像变换函数(本质为LUT)。从而将增强了原图像的对比效果。而通常图像变换函数是离散的,通过LUT实现,所以通过累计分布直方图作就可以实现直方图均衡化。

    也可以用熵的原理来解释均衡化原理:

    熵(Entropy):是信息量的度量,其定义为:

                                    wKioL1QoBbWzyk1IAAAcYO8-5EQ815.jpg

    其中, pi是符号 i出现的概率。  在图像中,wKiom1QoBeaT19-8AAAei2E3AtA404.jpgpr是灰度级r 出现的概率。

    可以证明,当p0=p1=p2=…=p255=1/256时,H取最大值,即图像信息量最大。  

    根据熵理论可知,直方图中,当H[0],H[1]…,H[n-1]相等时,图像信息量最大-》均衡化的目的是使每个Hi都相等,即把原始图的直方图变换为均匀分布的形式,这样就增加了象素值的范围,增强了图像的对比效果。

    对于熵这个概念,我也是费了好大的劲才大概能够明白他的意思:

    熵越大,系统的不确定性越大,系统越混乱,从而信息量也就越大。

       对于单个信息,可以理解为发生的概率越小  ,熵越大    

             对于一个系统,如果系统由多个部分组成,则可以理解为各个部分发生概率基本相等时熵最大

    关于熵的概念,大家可以参考吴军老师的《数学之美》中关于熵的解释,比较通俗易懂。

    直方图均衡化算法实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    void GetHistogramEqualize(BYTE *image_Src,BYTE *image_Dst, int width, int height)
    {
        //-------step 1.求灰度直方图
        unsigned long historgam[256];
        GetHistogram(image_Src, width, height, historgam);
     
        //------step 2.求累计分布直方图(灰度变换函数,LUT)
        //累计分布直方图符合增强对比度函数的要求
        int LUT[256];
        LUT[0] = historgam[0];
        int sum = historgam[0];
        for (int i = 1; i <= 255; ++i)
        {
            sum += historgam[i];
            LUT[i] =255* sum / (width*height);
        }
     
        //----step 3.对原图像做图像增强
        int pixelCount = width*height;
        for (int i = 0; i <= pixelCount - 1; ++i)
        {
            int gray = image_Src[i];
            image_Dst[i] = LUT[gray];
             
        }
    }

    效果图(借助了Opencv来显示图片)

    wKioL1QoA3KABweyAAXY_lr1bhE981.jpg

  • 相关阅读:
    请编程遍历页面上所有TextBox控件并给它赋值为string.Empty?
    AJAX基本应用
    EasyNVR录像开启后,无录像文件生成,如何解决?
    EasyNVR近期功能点优化及问题更新调整
    Linux下测试新版EasyNVR采用WebRTC播放起播较慢优化过程
    EasyNVR添加新用户无法查看历史录像问题原因分析
    EasyNVR查看直播视频流显示黑屏原因排查
    EasyNVR播hls格式视频无法全屏自适应播放如何调节?
    EasyNVR分屏切换时视频源丢失问题的优化分享
    EasyNVR通道设置中水印无法回显以及显示图片异常的问题优化
  • 原文地址:https://www.cnblogs.com/timssd/p/5714071.html
Copyright © 2011-2022 走看看