对比度限制直方图均衡化CLHE
其实实现了半个月了,不过一直没更新,囧。
上次讲到对比度限制的直方图均衡化,纠结了一段时间。
不知道为什么思维总是会想到改变图像的像素值,其实CLHE的目的是获取一个不那么陡峭的转换的映射函数,所以操作只要在直方图层面完成就行了。
确定阀值,切割直方图,将大于阀值的面积平均分到各个bins(之前就在纠结这里……),得到一个CL的直方图之后再求映射函数,并不用对原图进行操作。
代码:
这里的实现是多出来的面积按平局分布到各个bins,其实可以用高斯分布,效果可能会更好。
//核心部分 /* //限制对比度 { //获取最大值 cvGetMinMaxHistValue(histo_src,&histmin,&histmax); thresh*=histmax; cout<<thresh<<"\n"; //遍历get顶部面积 S=0; for(i=1;i<=255;i++) { if(cvQueryHistValue_1D(histo_src,i)>thresh) { S+=(cvQueryHistValue_1D(histo_src,i)-thresh); cvSetReal1D(histo_src->bins,i,thresh); } } S/=255; //遍历+平均面积; for(i=1;i<=255;i++) { cvSetReal1D(histo_src->bins, i, S+cvQueryHistValue_1D(histo_src,i) ); } } */ //程序部分--------------------------------------------------------------------------------------------- //滑块调节CLHE #include <iostream> #include <string> #include <io.h> #include <opencv2/opencv.hpp> #include<math.h> #include<cv.h> using namespace std; using namespace cv; //全局变量2个 int position; float thresh; //好吧,全部都是全局变量 //变量定义 //图片类 IplImage *src , *dst; IplImage *histframesrc , *histframedst; CvSize size; //直方图类 CvHistogram *histo_src , *histo_dst; int scale; float histmin,histmax; int bins=256; float range[]={0,255}; float *ranges[]={range}; //杂家 int i,j,k,m,n; //循环变量……而已 float s_r[256]; //S(r)映射函数 float S; //顶部面积 void on_trackbar(int position) { thresh=float(position)/100; cout<<thresh<<"\t"; cvCalcHist(&src,histo_src,0,0); //计算直方图 cvNormalizeHist(histo_src,255); //归一 //限制对比度 { //获取最大值 cvGetMinMaxHistValue(histo_src,&histmin,&histmax); thresh*=histmax; cout<<thresh<<"\n"; //遍历get顶部面积 S=0; for(i=1;i<=255;i++) { if(cvQueryHistValue_1D(histo_src,i)>thresh) { S+=(cvQueryHistValue_1D(histo_src,i)-thresh); cvSetReal1D(histo_src->bins,i,thresh); } } S/=255; //遍历+平均面积; for(i=1;i<=255;i++) { cvSetReal1D(histo_src->bins, i, S+cvQueryHistValue_1D(histo_src,i) ); } } //////////////////////////////// //累加,求S(r)映射 s_r[0]=cvQueryHistValue_1D(histo_src,0); for(i=1;i<=255;i++) { s_r[i]=s_r[i-1]+cvQueryHistValue_1D(histo_src,i); } //遍历图像并由sr关系进行直方图均衡化啦 CvScalar s; for(m=0;m<size.height;m++) { for(n=0;n<size.width;n++) { s=cvGet2D(src,m,n); i=s.val[0];//得到像素值 i=s_r[i];//得到映射值 s.val[0]=i;//设置像素通道值 cvSet2D(dst,m,n,s); } } //SHOWOFF一下啦 cvSmooth(dst,dst,CV_GAUSSIAN ); cvShowImage("dst",dst); //计算dst直方图 cvCalcHist(&dst,histo_dst,0,0); cvNormalizeHist(histo_dst,255); scale=2; //画出src和dst的直方图 histframesrc=cvCreateImage( cvSize(bins*scale,256),8,1); histframedst=cvCreateImage( cvSize(bins*scale,256),8,1); //src的 cvGetMinMaxHistValue(histo_src , &histmin , &histmax , 0 , 0 ); for(int i = 0; i < bins; i++) { /** 获得直方图中的统计次数,计算显示在图像中的高度 */ float bin_val = cvGetReal1D(histo_src->bins,i); bin_val=bin_val*255/histmax; s.val[0]=cvRound(bin_val); cvRectangle(histframesrc,cvPoint(i*scale,256),cvPoint((i+1)*scale,256-bin_val),s,-1,8,0); } cvShowImage("src's hisogram",histframesrc); //dst的 cvGetMinMaxHistValue(histo_dst , &histmin , &histmax , 0 , 0 ); for(int i = 0; i < bins; i++) { /** 获得直方图中的统计次数,计算显示在图像中的高度 */ float bin_val = cvGetReal1D(histo_dst->bins,i); bin_val=bin_val*255/histmax; s.val[0]=cvRound(bin_val); cvRectangle(histframedst,cvPoint(i*scale,256),cvPoint((i+1)*scale,256-bin_val),s,-1,8,0); } cvShowImage("dst's hisogram",histframedst); } void main(int argc, char** argv) { src=cvLoadImage("11.jpg",0); //图片变量初始化 cvShowImage("src",src); size=cvGetSize(src); dst=cvCreateImage(size,8,1); histo_src=cvCreateHist(1,&bins,CV_HIST_ARRAY,ranges); //直方图初始化 histo_dst=cvCreateHist(1,&bins,CV_HIST_ARRAY,ranges); cvNamedWindow("dst"); cvCreateTrackbar("\%","dst",&position,100,on_trackbar);//创建滑动条 on_trackbar(position);//回调滑动条 for(;;)if(cvWaitKey(0)==27)break; ///////////////////////////////////// }
实现结果:
阀值=0.14
阀值=0.91
在CLHE之后对图像进行了高斯平滑,所以直方图会比较奇怪。
对比结果可以得出CLHE之后的直方图并没有平均布满整个空间,从而达到了对比度限制的目的。
另:
1、关于上次的AHE,因为试用了区块加速,所以不可避免的出现了区块效应,实际中的AHE需要对每个像素周围MxN范围内外的图像求映射,并按特定比例相加,耗时慢。
2、接下来可能会实现SIFT或者是霍夫变换,前者掌握的比较早,但是后者实现更加简单,应该会先做后者。尽量在Matlab上实现。