自适应直方图均衡化 AHE

  直方图均衡化之后,第二个实验是自适应直方图均衡化。

 

  由于图片明暗分布的问题,对一张图片进行全局的直方图均衡化可能导致明部或者暗部的细节丢失。为了优化均衡化效果,我们对可以对不同区域进行直方图均衡化以获得更加合适的效果。

  要实现区域直方图均衡化有两种方法:

    1、将图片均分成几片区域各自进行直方图均衡化;

    2、对于一个特定像素,去其领域构建S(r)映射函数并将结果影射到该像素中;

  两种方法各有其缺点。前者会使得图片出线许多区块(亮度不均),后者计算量庞大(对每个像素的领域都要进行一次直方图均衡化)。

  在《计算机视觉——算法与应用》一书中提到方法二可以进行加速但没给出实现方法或相关论文。而对于方法一,在对各区块进行线性插值之后,可以削弱图片的区块效应。这种方法,叫做:自适应直方图均衡化(Adaptive histogram equlization,简称AHE。)

 

线性插值方法:(翻译自维基百科)

 

 2、通过插值加快计算速度

        如上所述的直接的自适应直方图,不管是否带有对比度限制,都需要对图像中的每个像素计算器领域直方图以及对应的变换函数,这使得算法及其耗时。

        而插值使得上述算法效率上有极大的提升,并且质量上没有下降。首先,将图像均匀分成等份矩形大小,如下图的右侧部分所示(8行8列64个块是常用的选择)。然后计算个块的直方图、CDF以及对应的变换函数。这个变换函数对于块的中心像素(下图左侧部分的黑色小方块)是完全符合原始定义的。而其他的像素通过哪些于其临近的四个块的变换函数插值获取。位于图中蓝色阴影部分的像素采用双线性查插值,而位于便于边缘的(绿色阴影)部分采用线性插值,角点处(红色阴影处)直接使用块所在的变换函数。

       Clahe-tileinterpol.svg
 
     这样的过程极大的降低了变换函数需要计算的次数,只是增加了一些双线性插值的计算量。
 
 
 
以下是实现结果:
 
HE

 

AHE

 

可见对于HE,AHE明暗对比更好。

 

本次试验中AHE还是出现了明显的区块效应,这与网络上的实验结果有出入。不知道是算法缺陷,亦是本人实现方法有问题,现给出代码希望各位路过大牛指点迷津。

#include <iostream>
#include <string>
#include <io.h>
#include <opencv2/opencv.hpp>
#include<math.h>
#include<cv.h>

using namespace std;
using namespace cv;

void main(int argc, char** argv)
{
    //变量定义
        //图片类
        IplImage *src , *mid, *dst;
        IplImage *histframesrc , *histframedst;
        CvSize     size,size1;
        //直方图类
        CvHistogram *histsrc[5][5] , *histdst;
        int scale;
        float histmin,histmax;

        int bins=256;
        float range[]={0,255};
        float *ranges[]={range};
        //杂家
        int i,j,m,n;                                //循环变量……而已 
        float s,t;                                        //线插用
        int pixcolor;                                    //中间变量……还得在前面定义,烦
        float sr[5][5][256];                                //S(r)映射函数
    //---------------------------------------------------------------
        //载入图像转换为灰度图
        src=cvLoadImage("22.jpg");
        size=cvGetSize(src);
        mid=cvCreateImage(size,8,1);
        dst=cvCreateImage(size,8,1);
        cvCvtColor(src,mid,CV_BGR2GRAY);
        //子区域大小
        size1.width=size.width/5;
        size1.height=size.height/5;
        size.width=size1.width*5;
        size.height=size1.height*5;
        //计算各个子区域直方图,归一到255
        for(m=0;m<=4;m+=1)
        {
            for(n=0;n<=4;n+=1)
            {
                cvSetImageROI(mid,Rect(m*size1.width,n*size1.height,size1.width,size1.height));
                histsrc[m][n]=cvCreateHist(1,&bins,CV_HIST_ARRAY,ranges,1);
                cvCalcHist(&mid,histsrc[m][n],0,0);
                cvNormalizeHist(histsrc[m][n],255);
            }
        }
        //少年,做事要有头有尾,记得resetROI
        cvResetImageROI(mid);
        
        
        //计算各直方图的s(r)映射函数
        for(m=0;m<=4;m+=1)
        {
            for(n=0;n<=4;n+=1)
            {
                sr[m][n][0]=cvQueryHistValue_1D(histsrc[m][n],0);
                for(i=1;i<=255;i++)sr[m][n][i]=sr[m][n][i-1]+cvQueryHistValue_1D(histsrc[m][n],i);
            }
        }
        
        

        //查找表线插…………WOW!核心!
        CvScalar pixel;
    
        
        for(j=0;j<size.height;j+=1)
        {
            for(i=0;i<size.width;i+=1)
            {
                
                pixel=cvGet2D(mid,j,i);                                            //提取像素
                pixcolor=pixel.val[0];
                m=cvRound(i/size1.width),n=cvRound(j/size1.height);                //M.N所在区域
                s=(i/float(size1.width)-(2*m-1)/2.0),t=(j/float(size1.height)-(2*n-1)/2.0);        //S.T.线性内插系数
                    
                if((m==0&&n==0)||(m==5&&n== 5))pixel.val[0]=sr[m][n][pixcolor];                        //分情况线性内插
                else 
                    if(m==0)pixel.val[0]=t*sr[m][n-1][pixcolor]+(1-t)*sr[m][n][pixcolor];
                else 
                    if(m==5&&n!=5)pixel.val[0]=t*sr[4][n-1][pixcolor]+(1-t)*sr[4][n][pixcolor];
                else
                    if(n==0)pixel.val[0]=s*sr[m-1][n][pixcolor]+(1-s)*sr[m][n][pixcolor];
                else
                    if(n==5&&m!=5)pixel.val[0]=s*sr[m-1][4][pixcolor]+(1-s)*sr[m][4][pixcolor];
                else
                    pixel.val[0]=s*t*sr[m-1][n-1][pixcolor] + s*(1-t)*sr[m-1][n][pixcolor] + (1-s)*t*sr[m][n-1][pixcolor] + (1-s)*(1-t)*sr[m][n][pixcolor];
                
    

                    cvSet2D(dst,j,i,pixel);

                    
            }
        }

        cvShowImage("av",dst);



         for(;;)if(cvWaitKey(0)==27)break;


}

 

 

下一步实验计划是实现CHE或者是CLAHE,但是网络上关于这方面的资料少。如果实现不能,我将转战线性滤波。

posted @ 2013-05-11 20:47  Pony_s  阅读(8109)  评论(1编辑  收藏  举报