CLAHE的实现和研究

CLAHE的实现和研究CLAHE 的实现和研究 CLAHE 算法对于医学图像 特别是医学红外图像的增强效果非常明显 CLAHE nbsp https en wikipedia org wiki Adaptive histogram equalization 中文方面非常好的资料限制对比度自适应直方图均衡化算法原理 实现及效果在 OpenCV 中已经实现了 CLAHE 但是它在使用过程中 存在参数选择的问题 为了从根本上搞明白 我参考了网络上的一些代码主要是来源 http blog csdn net abcd19

CLAHE的实现和研究

CLAHE算法对于医学图像,特别是医学红外图像的增强效果非常明显。
CLAHE  https://en.wikipedia.org/wiki/Adaptive_histogram_equalization
中文方面非常好的资料 限制对比度自适应直方图均衡化算法原理、实现及效果
在OpenCV中已经实现了CLAHE,但是它在使用过程中,存在参数选择的问题。为了从根本上搞明白,我参考了网络上的一些代码
主要是来源 http://blog.csdn.net/abcdg/article/details/
实现了基于OpenCV的CLAHE实现和研究。从最基本的开始做,分别实现HE算法,AHE算法,CLHE算法和CLAHE算法。素材分别采用了手部和手臂的红外图片,同时调用OpenCV生成代码和自己编写代码进行比对。
调用代码和实现效果:
int _tmain(
int argc, _TCHAR * argv[])

{
     //读入灰度的手部图像
    Mat src  = imread( “arm.jpg”, 0);
    Mat dst  = src.clone();
    Mat HT_OpenCV;
    Mat HT_GO;
    Mat AHE_GO;
    Mat CLHE_GO;
    Mat CLAHE_Without_Interpolation;
    Mat CLAHE_OpenCV;
    Mat CLAHE_GO;
    Mat matInter;
     OpenCV HT 方法
    cv : :equalizeHist(src,HT_OpenCV);
     GO HT方法
    HT_GO  = eaualizeHist_GO(src);
     GO AHE方法
    AHE_GO  = aheGO(src);
     GO CLHE方法
    CLHE_GO  = clheGO(src);
     clahe不计算差值
    CLAHE_Without_Interpolation  = claheGoWithoutInterpolation(src);
     OpenCV CLAHE 方法
    Ptr <cv : :CLAHE > clahe  = createCLAHE(); //默认参数
    clahe – >apply(src, CLAHE_OpenCV);
     GO CLAHE方法
    CLAHE_GO  = claheGO(src);
 
     结果显示
    imshow( “原始图像”,src);
    imshow( “OpencvHT”,HT_OpenCV);
    imshow( “GOHT”,HT_GO);
    imshow( “GOAHE”,AHE_GO);
    imshow( “GOCLHE”,CLHE_GO);
    imshow( “GOCLAHE”,CLAHE_GO);
    imshow( “CLAHE_Without_Interpolation”,CLAHE_Without_Interpolation);
    imshow( “OpencvCLAHE”,CLAHE_OpenCV);
    waitKey();
     return  0;
}







































CLAHE的实现和研究
原始图像
CLAHE的实现和研究
GOCLAHE效果
CLAHE的实现和研究
OpenCV CLAHE效果
HE算法: Mat eaualizeHist_GO(Mat src)
{

     int width  = src.cols;
     int height = src.rows;
    Mat HT_GO  = src.clone();
     int tmp[ 256]  ={ 0};
     float C[ 256]  = { 0. 0};
     int total  = width *height;  
     for ( int i = 0 ;i <src.rows;i ++)
    {
         for ( int j = 0;j <src.cols;j ++)
        {
             int index  = src.at <uchar >(i,j);
            tmp[index]  ++;
        }
    }
     //计算累积函数  
     for( int i  =  0;i  <  256 ; i ++){  
         if(i  ==  0)  
            C[i]  =  1.0f  * tmp[i]  / total;  
         else  
            C[i]  = C[i – 1]  +  1.0f  * tmp[i]  / total;  
    }  
     //这里的累积函数分配的方法非常直观高效
     for( int i  =  0;i  < src.rows;i ++){  
         for( int j  =  0;j  < src.cols;j ++){      
             int index  = src.at <uchar >(i,j);
            HT_GO.at <uchar >(i,j)  = C[index]  *  255  ;
        }  
    }  
     return HT_GO;
}





























CLAHE的实现和研究
CLAHE的实现和研究
 
AHE算法:
Mat aheGO(Mat src,
int _step  =  8)

{
    Mat AHE_GO  = src.clone();
     int block  = _step;
     int width  = src.cols;
     int height  = src.rows;
     int width_block  = width /block;  //每个小格子的长和宽
     int height_block  = height /block;
     //存储各个直方图  
     int tmp2[ 8 * 8][ 256]  ={ 0};
     float C2[ 8 * 8][ 256]  = { 0. 0};
     //分块
     int total  = width_block  * height_block; 
     for ( int i = 0;i <block;i ++)
    {
         for ( int j = 0;j <block;j ++)
        {
             int start_x  = i *width_block;
             int end_x  = start_x  + width_block;
             int start_y  = j *height_block;
             int end_y  = start_y  + height_block;
             int num  = i +block *j;  
             //遍历小块,计算直方图
             for( int ii  = start_x ; ii  < end_x ; ii ++)  
            {  
                 for( int jj  = start_y ; jj  < end_y ; jj ++)  
                {  
                     int index  =src.at <uchar >(jj,ii);
                    tmp2[num][index] ++;  
                }  
            } 
             //计算累积分布直方图  
             for( int k  =  0 ; k  <  256 ; k ++)  
            {  
                 if( k  ==  0)  
                    C2[num][k]  =  1.0f  * tmp2[num][k]  / total;  
                 else  
                    C2[num][k]  = C2[num][k – 1]  +  1.0f  * tmp2[num][k]  / total;  
            }  
        }
    }
     //将统计结果写入
     for ( int i = 0;i <block;i ++)
    {
         for ( int j = 0;j <block;j ++)
        {
             int start_x  = i *width_block;
             int end_x  = start_x  + width_block;
             int start_y  = j *height_block;
             int end_y  = start_y  + height_block;
             int num  = i +block *j;  
             //遍历小块,计算直方图
             for( int ii  = start_x ; ii  < end_x ; ii ++)  
            {  
                 for( int jj  = start_y ; jj  < end_y ; jj ++)  
                {  
                     int index  =src.at <uchar >(jj,ii);
                     //结果直接写入AHE_GO中去
                    AHE_GO.at <uchar >(jj,ii)  = C2[num][index]  *  255  ;
                }  
            } 
        }
    }
     return AHE_GO;
}































































CLAHE的实现和研究
CLHE算法:
//这里是在全局直方图加入“限制对比度”方法

Mat clheGO(Mat src, int _step  =  8)
{
     int width  = src.cols;
     int height = src.rows;
    Mat CLHE_GO  = src.clone();
     int tmp[ 256]  ={ 0};
     float C[ 256]  = { 0. 0};
     int total  = width *height;  
     for ( int i = 0 ;i <src.rows;i ++)
    {
         for ( int j = 0;j <src.cols;j ++)
        {
             int index  = src.at <uchar >(i,j);
            tmp[index]  ++;
        }
    }
     /限制对比度计算部分,注意这个地方average的计算不一定科学
     int average  = width  * height  /  255 / 64;  
     int LIMIT  =  4  * average;  
     int steal  =  0;  
     for( int k  =  0 ; k  <  256 ; k ++)  
    {  
         if(tmp[k]  > LIMIT){  
            steal  += tmp[k]  – LIMIT;  
            tmp[k]  = LIMIT;  
        }  
    }  
     int bonus  = steal / 256;  
     //hand out the steals averagely  
     for( int k  =  0 ; k  <  256 ; k ++)  
    {  
        tmp[k]  += bonus;  
    }  
     ///
     //计算累积函数  
     for( int i  =  0;i  <  256 ; i ++){  
         if(i  ==  0)  
            C[i]  =  1.0f  * tmp[i]  / total;  
         else  
            C[i]  = C[i – 1]  +  1.0f  * tmp[i]  / total;  
    }  
     //这里的累积函数分配的方法非常直观高效
     for( int i  =  0;i  < src.rows;i ++){  
         for( int j  =  0;j  < src.cols;j ++){      
             int index  = src.at <uchar >(i,j);
            CLHE_GO.at <uchar >(i,j)  = C[index]  *  255  ;
        }  
    }  
     return CLHE_GO;
}

















































CLAHE的实现和研究
CLAHE不包括插值算法:
Mat claheGoWithoutInterpolation(Mat src, 
int _step  =  8)

{
    Mat CLAHE_GO  = src.clone();
     int block  = _step; //pblock
     int width  = src.cols;
     int height = src.rows;
     int width_block  = width /block;  //每个小格子的长和宽
     int height_block  = height /block;
     //存储各个直方图  
     int tmp2[ 8 * 8][ 256]  ={ 0};
     float C2[ 8 * 8][ 256]  = { 0. 0};
     //分块
     int total  = width_block  * height_block; 
     for ( int i = 0;i <block;i ++)
    {
         for ( int j = 0;j <block;j ++)
        {
             int start_x  = i *width_block;
             int end_x  = start_x  + width_block;
             int start_y  = j *height_block;
             int end_y  = start_y  + height_block;
             int num  = i +block *j;  
             //遍历小块,计算直方图
             for( int ii  = start_x ; ii  < end_x ; ii ++)  
            {  
                 for( int jj  = start_y ; jj  < end_y ; jj ++)  
                {  
                     int index  =src.at <uchar >(jj,ii);
                    tmp2[num][index] ++;  
                }  
            } 
             //裁剪和增加操作,也就是clahe中的cl部分
             //这里的参数 对应《Gem》上面 fCliplimit  = 4  , uiNrBins  = 255
             int average  = width_block  * height_block  /  255;  
             int LIMIT  =  4  * average;  
             int steal  =  0;  
             for( int k  =  0 ; k  <  256 ; k ++)  
            {  
                 if(tmp2[num][k]  > LIMIT){  
                    steal  += tmp2[num][k]  – LIMIT;  
                    tmp2[num][k]  = LIMIT;  
                }  
            }  
             int bonus  = steal / 256;  
             //hand out the steals averagely  
             for( int k  =  0 ; k  <  256 ; k ++)  
            {  
                tmp2[num][k]  += bonus;  
            }  
             //计算累积分布直方图  
             for( int k  =  0 ; k  <  256 ; k ++)  
            {  
                 if( k  ==  0)  
                    C2[num][k]  =  1.0f  * tmp2[num][k]  / total;  
                 else  
                    C2[num][k]  = C2[num][k – 1]  +  1.0f  * tmp2[num][k]  / total;  
            }  
        }
    }
     //计算变换后的像素值  
     //将统计结果写入
     for ( int i = 0;i <block;i ++)
    {
         for ( int j = 0;j <block;j ++)
        {
             int start_x  = i *width_block;
             int end_x  = start_x  + width_block;
             int start_y  = j *height_block;
             int end_y  = start_y  + height_block;
             int num  = i +block *j;  
             //遍历小块,计算直方图
             for( int ii  = start_x ; ii  < end_x ; ii ++)  
            {  
                 for( int jj  = start_y ; jj  < end_y ; jj ++)  
                {  
                     int index  =src.at <uchar >(jj,ii);
                     //结果直接写入AHE_GO中去
                    CLAHE_GO.at <uchar >(jj,ii)  = C2[num][index]  *  255  ;
                }  
            } 
        }
    
     }  
     return CLAHE_GO;
}



















































































CLAHE的实现和研究
CLAHE算法:

Mat claheGO(Mat src,
int _step  =  8)

{
    Mat CLAHE_GO  = src.clone();
     int block  = _step; //pblock
     int width  = src.cols;
     int height = src.rows;
     int width_block  = width /block;  //每个小格子的长和宽
     int height_block  = height /block;
     //存储各个直方图  
     int tmp2[ 8 * 8][ 256]  ={ 0};
     float C2[ 8 * 8][ 256]  = { 0. 0};
     //分块
     int total  = width_block  * height_block; 
     for ( int i = 0;i <block;i ++)
    {
         for ( int j = 0;j <block;j ++)
        {
             int start_x  = i *width_block;
             int end_x  = start_x  + width_block;
             int start_y  = j *height_block;
             int end_y  = start_y  + height_block;
             int num  = i +block *j;  
             //遍历小块,计算直方图
             for( int ii  = start_x ; ii  < end_x ; ii ++)  
            {  
                 for( int jj  = start_y ; jj  < end_y ; jj ++)  
                {  
                     int index  =src.at <uchar >(jj,ii);
                    tmp2[num][index] ++;  
                }  
            } 
             //裁剪和增加操作,也就是clahe中的cl部分
             //这里的参数 对应《Gem》上面 fCliplimit  = 4  , uiNrBins  = 255
             int average  = width_block  * height_block  /  255;  
             //关于参数如何选择,需要进行讨论。不同的结果进行讨论
             //关于全局的时候,这里的这个cl如何算,需要进行讨论 
             int LIMIT  =  40  * average;  
             int steal  =  0;  
             for( int k  =  0 ; k  <  256 ; k ++)  
            {  
                 if(tmp2[num][k]  > LIMIT){  
                    steal  += tmp2[num][k]  – LIMIT;  
                    tmp2[num][k]  = LIMIT;  
                }  
            }  
             int bonus  = steal / 256;  
             //hand out the steals averagely  
             for( int k  =  0 ; k  <  256 ; k ++)  
            {  
                tmp2[num][k]  += bonus;  
            }  
             //计算累积分布直方图  
             for( int k  =  0 ; k  <  256 ; k ++)  
            {  
                 if( k  ==  0)  
                    C2[num][k]  =  1.0f  * tmp2[num][k]  / total;  
                 else  
                    C2[num][k]  = C2[num][k – 1]  +  1.0f  * tmp2[num][k]  / total;  
            }  
        }
    }
     //计算变换后的像素值  
     //根据像素点的位置,选择不同的计算方法  
     for( int  i  =  0 ; i  < width; i ++)  
    {  
         for( int j  =  0 ; j  < height; j ++)  
        {  
             //four coners  
             if(i  < = width_block / 2  && j  < = height_block / 2)  
            {  
                 int num  =  0;  
                CLAHE_GO.at <uchar >(j,i)  = ( int)(C2[num][CLAHE_GO.at <uchar >(j,i)]  *  255);  
            } else  if(i  < = width_block / 2  && j  > = ((block – 1) *height_block  + height_block / 2)){  
                 int num  = block *(block – 1);  
                CLAHE_GO.at <uchar >(j,i)  = ( int)(C2[num][CLAHE_GO.at <uchar >(j,i)]  *  255);  
            } else  if(i  > = ((block – 1) *width_block +width_block / 2)  && j  < = height_block / 2){  
                 int num  = block – 1;  
                CLAHE_GO.at <uchar >(j,i)  = ( int)(C2[num][CLAHE_GO.at <uchar >(j,i)]  *  255);  
            } else  if(i  > = ((block – 1) *width_block +width_block / 2)  && j  > = ((block – 1) *height_block  + height_block / 2)){  
                 int num  = block *block – 1;  
                CLAHE_GO.at <uchar >(j,i)  = ( int)(C2[num][CLAHE_GO.at <uchar >(j,i)]  *  255);  
            }  
             //four edges except coners  
             else  if( i  < = width_block / 2 )  
            {  
                 //线性插值  
                 int num_i  =  0;  
                 int num_j  = (j  – height_block / 2) /height_block;  
                 int num1  = num_j *block  + num_i;  
                 int num2  = num1  + block;  
                 float p  =  (j  – (num_j *height_block +height_block / 2)) /( 1.0f *height_block);  
                 float q  =  1 -p;  
                CLAHE_GO.at <uchar >(j,i)  = ( int)((q *C2[num1][CLAHE_GO.at <uchar >(j,i)] + p *C2[num2][CLAHE_GO.at <uchar >(j,i)]) *  255);  
            } else  if( i  > = ((block – 1) *width_block +width_block / 2)){  
                 //线性插值  
                 int num_i  = block – 1;  
                 int num_j  = (j  – height_block / 2) /height_block;  
                 int num1  = num_j *block  + num_i;  
                 int num2  = num1  + block;  
                 float p  =  (j  – (num_j *height_block +height_block / 2)) /( 1.0f *height_block);  
                 float q  =  1 -p;  
                CLAHE_GO.at <uchar >(j,i)  = ( int)((q *C2[num1][CLAHE_GO.at <uchar >(j,i)] + p *C2[num2][CLAHE_GO.at <uchar >(j,i)]) *  255);  
            } else  if( j  < = height_block / 2 ){  
                 //线性插值  
                 int num_i  = (i  – width_block / 2) /width_block;  
                 int num_j  =  0;  
                 int num1  = num_j *block  + num_i;  
                 int num2  = num1  +  1;  
                 float p  =  (i  – (num_i *width_block +width_block / 2)) /( 1.0f *width_block);  
                 float q  =  1 -p;  
                CLAHE_GO.at <uchar >(j,i)  = ( int)((q *C2[num1][CLAHE_GO.at <uchar >(j,i)] + p *C2[num2][CLAHE_GO.at <uchar >(j,i)]) *  255);  
            } else  if( j  > = ((block – 1) *height_block  + height_block / 2) ){  
                 //线性插值  
                 int num_i  = (i  – width_block / 2) /width_block;  
                 int num_j  = block – 1;  
                 int num1  = num_j *block  + num_i;  
                 int num2  = num1  +  1;  
                 float p  =  (i  – (num_i *width_block +width_block / 2)) /( 1.0f *width_block);  
                 float q  =  1 -p;  
                CLAHE_GO.at <uchar >(j,i)  = ( int)((q *C2[num1][CLAHE_GO.at <uchar >(j,i)] + p *C2[num2][CLAHE_GO.at <uchar >(j,i)]) *  255);  
            }  
             //双线性插值
             else{  
                 int num_i  = (i  – width_block / 2) /width_block;  
                 int num_j  = (j  – height_block / 2) /height_block;  
                 int num1  = num_j *block  + num_i;  
                 int num2  = num1  +  1;  
                 int num3  = num1  + block;  
                 int num4  = num2  + block;  
                 float u  = (i  – (num_i *width_block +width_block / 2)) /( 1.0f *width_block);  
                 float v  = (j  – (num_j *height_block +height_block / 2)) /( 1.0f *height_block);  
                CLAHE_GO.at <uchar >(j,i)  = ( int)((u *v *C2[num4][CLAHE_GO.at <uchar >(j,i)]  +   
                    ( 1 -v) *( 1 -u) *C2[num1][CLAHE_GO.at <uchar >(j,i)]  +  
                    u *( 1 -v) *C2[num2][CLAHE_GO.at <uchar >(j,i)]  +  
                    v *( 1 -u) *C2[num3][CLAHE_GO.at <uchar >(j,i)])  *  255);  
            }  
             //最后这步,类似高斯平滑
            CLAHE_GO.at <uchar >(j,i)  = CLAHE_GO.at <uchar >(j,i)  + (CLAHE_GO.at <uchar >(j,i)  <<  8)  + (CLAHE_GO.at <uchar >(j,i)  <<  16);         
        }  
    }  
   return CLAHE_GO;
}













































































































































原始图像
CLAHE的实现和研究
GOCLAHE效果
CLAHE的实现和研究
OpenCV CLAHE效果
从结果上来看,GOCLAHE方法和OpenCV提供的CLAHE方法是一样的。
再放一组图片
CLAHE的实现和研究
CLAHE的实现和研究
CLAHE的实现和研究
CLAHE的实现和研究
CLAHE的实现和研究
代码实现之后,留下两个问题:
集中在这段代码
            //这里的参数 对应《Gem》上面 fCliplimit  = 4  , uiNrBins  = 255

             int average  = width_block  * height_block  /  255;  
             //关于参数如何选择,需要进行讨论。不同的结果进行讨论
             //关于全局的时候,这里的这个cl如何算,需要进行讨论 
             int LIMIT  =  40  * average;  
             int steal  =  0;  




1、在进行CLAHE中CL的计算,也就是限制对比度的计算的时候,参数的选择缺乏依据。在原始的《GEMS》中提供的参数中,  fCliplimit  = 4  , uiNrBins  = 255. 但是在OpenCV的默认参数中,这里是40.就本例而言,如果从结果上反推,我看10比较好。这里参数的选择缺乏依据;
2、CLHE是可以用来进行全局直方图增强的,那么这个时候,这个 average 如何计算,肯定不是width * height/255,这样就太大了,算出来的LIMIT根本没有办法获得。
但是就实现血管增强的效果而言,这些结果是远远不够的。一般来说,对于CLAHE计算出来的结果,进行Frangi增强或者使用超分辨率增强?结果就是要把血管区域强化出来。
p.s:
arm.jpg 和 hand.jpg
CLAHE的实现和研究
CLAHE的实现和研究
 
目前方向:图像拼接融合、图像识别 联系方式:
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/232328.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • php 死链查询,seo网站死链解决方法 死链查询检测工具

    php 死链查询,seo网站死链解决方法 死链查询检测工具死链是指服务器的地址已经改变了.无法找到当前地址位置,包括协议死链和内容死链两种形式。死链出现的原因有网站服务器设置错误;某文件夹名称修改,路径错误链接变成死链等。我们都知道死链对seo排名的危害是非常大的。死链对网站的危害一、有可能会让搜索引擎降权二、用户体验较差死链检测方法:Xenu死链查询工具今天教大家如何使用Xenu死链接检测工具对网站死链接(什么是网站死链)进行处理,有图有真相,轻松四步…

    2022年7月23日
    16
  • 七、分发脚本xsync报错

    七、分发脚本xsync报错一、报错问题Badownerorpermissionson/home/pcz/.ssh/configBadownerorpermissionson/home/pcz/.ssh/configrsync:connectionunexpectedlyclosed(0bytesreceivedsofar)[sender]rsyncerror:unexplainederror(code255)atio.c(226)[sender=3.1.2]Badow

    2022年5月5日
    47
  • SIFT特征提取分析

    SIFT特征提取分析

    2021年12月2日
    42
  • Hadoop mapreduce过程key 和value分别存什么值

    Hadoop mapreduce过程key 和value分别存什么值Hadoop mapreduce过程key 和value分别存什么值

    2022年4月23日
    53
  • pip的安装与卸载「建议收藏」

    前言我个人的理解是,pip是一种工具,可以帮助用户安装卸载一些需要的安装包,非常的简单实用,类似于yum下面介绍的是使用系统自带的python来安装pip。安装方法一sudoaptinstallpython-pip方法二wgethttps://bootstrap.pypa.io/get-pip.py#科学上网pythonget-pip.pypyth…

    2022年4月5日
    58
  • 遍历Arraylist的三种方法及优缺点简单介绍

    遍历Arraylist的三种方法及优缺点简单介绍集合ArrayList是接口List的一种子类,它的特点是:存储的元素是有序的.底层的数据结构是数组.查询快,增删慢.在众多集合中ArrayList的遍历又是比较特殊的,下面就写一下它的三种遍历方式,代码如下:第一种遍历方式:普通for循环第二种遍历方式:增强for循环第三种遍历方式:迭代器importjava.util.ArrayList;importjava.util.Iterator;/***PACKAGE_NAME*/publicclassDemo.

    2022年7月22日
    8

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号