其實實現了半個月了,不過一直沒更新,囧。
上次講到對比度限制的直方圖均衡化,糾結了一段時間。
不知道為什么思維總是會想到改變圖像的像素值,其實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上實現。