這是數(shù)字圖像處理課的大作業(yè),完成于 2013/06/17,需要調(diào)用 openCV 庫,完整源碼和報(bào)告如下:
?

1 #include <cv.h> 2 #include <highgui.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <math.h> 6 #include <assert.h> 7 #include < string > 8 9 /* 灰度級結(jié)點(diǎn) */ 10 typedef struct { 11 int pixels; // 灰度級對應(yīng)像素個數(shù) 12 float rate; // 像素比例 13 float accuRate; // 累計(jì)像素比例 14 int map; // 到均衡化后的灰度級的映射 15 } levNode; 16 17 void histeqGray(IplImage* pGray, int levels, int argc); 18 IplImage* histImage(IplImage* pSrc, int histWidth, int histHeight, int nScale); 19 20 int main( int argc, char * argv[]) 21 { 22 int levels; 23 std:: string imgName, inTmp; 24 if (argc == 3 ) { 25 levels = atoi(argv[ 1 ]); 26 imgName = argv[ 2 ]; 27 } 28 else if (argc == 2 ) 29 imgName = argv[ 1 ]; 30 else { 31 printf( " usage: histeq [levels] image_name \n " ); 32 return - 1 ; 33 } 34 35 IplImage* pSrc = cvLoadImage(imgName.c_str(), CV_LOAD_IMAGE_UNCHANGED); 36 int channel = pSrc-> nChannels; 37 38 IplImage* pChnl[ 4 ] = { NULL }; 39 40 for ( int i = 0 ; i < channel; ++ i) 41 pChnl[i] = cvCreateImage(cvGetSize(pSrc), pSrc->depth, 1 ); 42 43 cvSplit(pSrc, pChnl[ 0 ], pChnl[ 1 ], pChnl[ 2 ], pChnl[ 3 ]); 44 45 for ( int i = 0 ; i < channel; ++ i) 46 histeqGray(pChnl[i], levels, argc); 47 48 IplImage* pEql = cvCreateImage(cvGetSize(pSrc), pChnl[ 0 ]->depth, pSrc-> nChannels); 49 50 cvMerge(pChnl[ 0 ], pChnl[ 1 ], pChnl[ 2 ], pChnl[ 3 ], pEql); 51 52 inTmp = imgName + " _Eql.jpg " ; 53 cvSaveImage(inTmp.c_str(), pEql); 54 55 // cvNamedWindow(imgName.c_str(), CV_WINDOW_AUTOSIZE); 56 cvShowImage(imgName.c_str(), pSrc); 57 // cvNamedWindow(inTmp.c_str(), CV_WINDOW_AUTOSIZE); 58 cvShowImage(inTmp.c_str(), pEql); 59 60 IplImage* pSrcGray = cvCreateImage(cvGetSize(pSrc), IPL_DEPTH_8U, 1 ); 61 if (pSrc->nChannels == 3 ) 62 cvCvtColor(pSrc, pSrcGray, CV_BGR2GRAY); 63 else 64 cvCopyImage(pSrc, pSrcGray); 65 IplImage* pEqlGray = cvCreateImage(cvGetSize(pEql), IPL_DEPTH_8U, 1 ); 66 if (pSrc->nChannels == 3 ) 67 cvCvtColor(pEql, pEqlGray, CV_BGR2GRAY); 68 else 69 cvCopyImage(pEql, pEqlGray); 70 imgName += " _Hist.jpg " ; 71 inTmp += " _Hist.jpg " ; 72 int nScale = 2 ; 73 int histWidth = /* pSrc->width * nScale */ 256 * nScale; 74 int histHeight = /* pSrc->height */ 128 ; 75 IplImage* pSrcGrayHist = histImage(pSrcGray, histWidth, histHeight, nScale); 76 IplImage* pEqlGrayHist = histImage(pEqlGray, histWidth, histHeight, nScale); 77 cvSaveImage(imgName.c_str(), pSrcGrayHist); 78 cvSaveImage(inTmp.c_str(), pEqlGrayHist); 79 cvShowImage(imgName.c_str(), pSrcGrayHist); 80 cvShowImage(inTmp.c_str(), pEqlGrayHist); 81 82 cvWaitKey(); 83 84 cvReleaseImage(& pEql); 85 cvReleaseImage(& pEqlGray); 86 for ( int i = 0 ; i < channel; ++ i) 87 cvReleaseImage(& pChnl[i]); 88 cvReleaseImage(& pSrc); 89 cvReleaseImage(& pSrcGray); 90 91 return 0 ; 92 } 93 94 /* 95 * 直方圖均衡化函數(shù) 96 * pGray為輸入的灰度圖 97 * levels為均衡化的灰度級 98 */ 99 void histeqGray(IplImage* pGray, int levels, int argc) 100 { 101 int depth = pGray-> depth; 102 printf( " %d \n " , depth); 103 int width = pGray-> width; 104 int height = pGray-> height; 105 int sumPixels = width * height; // 總像素?cái)?shù) 106 printf( " %d \n " , sumPixels); 107 int values = static_cast< int >(pow(( float ) 2 , depth)); // 根據(jù)圖像深度計(jì)算像素取值范圍 108 if (argc == 2 ) levels = values; 109 printf( " %d \n " , levels); 110 111 int outDepth; 112 /* if (levels <= 2) 113 outDepth = 1; 114 else */ if (levels <= 256 ) 115 outDepth = 8 ; 116 else if (levels <= 65536 ) 117 outDepth = 16 ; 118 119 assert(levels <= values); 120 int intervals = values / levels; // 根據(jù)像素取值范圍和灰度級求每個灰度級的像素間隔 121 levNode* levNodes = (levNode*)calloc(levels, sizeof (levNode)); // 生成灰度結(jié)點(diǎn) 122 // for (int lev = 0; lev < levels; ++lev) printf("%d \n", levNodes[lev].pixels); 123 // char* pValues = pGray->imageData; 124 125 /* 統(tǒng)計(jì)每個灰度級的像素個數(shù) */ 126 for ( int y = 0 ; y < height; ++ y) 127 for ( int x = 0 ; x < width; ++ x) { 128 CvScalar scal = cvGet2D(pGray, y, x); 129 int val = ( int )scal.val[ 0 ]; 130 // printf("%d \n", val); 131 for ( int lev = 0 ; lev < levels; ++ lev) { 132 if ( val >= intervals*lev && val < intervals*(lev+ 1 )) { 133 ++levNodes[lev].pixels; break ; 134 } 135 } 136 } 137 138 int sum = 0 ; 139 for ( int lev = 0 ; lev < levels; ++ lev) 140 sum += levNodes[lev].pixels; 141 printf( " %d \n " , sum); 142 143 /* 計(jì)算每個灰度級像素比例和累計(jì)比例 */ 144 levNodes[ 0 ].accuRate = levNodes[ 0 ].rate = levNodes[ 0 ].pixels / ( float )sumPixels; 145 levNodes[ 0 ].map = ( int )(levNodes[ 0 ].accuRate * (levels - 1 ) + 0.5 ); 146 printf( " %d \n " , levNodes[ 0 ].pixels); 147 for ( int lev = 1 ; lev < levels; ++ lev) { 148 levNodes[lev].rate = levNodes[lev].pixels / ( float )sumPixels; 149 levNodes[lev].accuRate = levNodes[lev- 1 ].accuRate + levNodes[lev].rate; 150 levNodes[lev].map = ( int )(levNodes[lev].accuRate * (levels - 1 ) + 0.5 ); 151 } 152 printf( " %f \n " , levNodes[levels- 1 ].accuRate); 153 154 /* 生成均衡化后的圖像 */ 155 for ( int y = 0 ; y < height; ++ y) 156 for ( int x = 0 ; x < width; ++ x) { 157 CvScalar scal = cvGet2D(pGray, y, x); 158 int val = ( int )scal.val[ 0 ]; 159 // printf("%d \n", val); 160 for ( int lev = 0 ; lev < levels; ++ lev) { 161 if (val >= intervals*lev && val < intervals*(lev+ 1 )) { 162 scal.val[ 0 ] = levNodes[lev].map; 163 // printf("%f \n", scal.val[0]); 164 cvSet2D(pGray, y, x, scal); 165 break ; 166 } 167 } 168 } 169 pGray->depth = outDepth; 170 171 free(levNodes); 172 } 173 174 /* 175 * 繪制直方圖函數(shù) 176 */ 177 IplImage* histImage(IplImage* pSrc, int histWidth, int histHeight, int nScale) 178 { 179 int histSize = static_cast< int >(pow(( float ) 2 , pSrc-> depth)); 180 CvHistogram* pHist = cvCreateHist( /* pSrc->nChannels */ 1 , & histSize, CV_HIST_ARRAY); 181 cvCalcHist(& pSrc, pHist); 182 183 IplImage* pHistImg = cvCreateImage(cvSize(histWidth, histHeight), IPL_DEPTH_8U, 1 ); 184 cvRectangle(pHistImg, cvPoint( 0 , 0 ), cvPoint(pHistImg->width,pHistImg->height), CV_RGB( 255 , 255 , 255 ), CV_FILLED); 185 186 float histMaxVal = 0 ; 187 cvGetMinMaxHistValue(pHist, 0 , & histMaxVal); 188 189 for ( int i = 0 ; i < histSize; i++ ) 190 { 191 float histValue= cvQueryHistValue_1D(pHist, i); // 像素為i的直方塊大小 192 int nRealHeight = cvRound((histValue / histMaxVal) * histHeight); // 要繪制的高度 193 cvRectangle(pHistImg, 194 cvPoint(i*nScale, histHeight - 1 ), 195 cvPoint((i + 1 )*nScale - 1 , histHeight - nRealHeight), 196 cvScalar(i), 197 CV_FILLED 198 ); 199 } 200 // cvFillConvexPoly 201 202 cvReleaseHist(& pHist); 203 return pHistImg; 204 }
?
一、直方圖均衡化概述
直方圖均衡化是一種圖像增強(qiáng)方法,其基本思想是把給定圖像的直方圖分布改造成均勻分布的直方圖,從而增加象素灰度值的動態(tài)范圍,達(dá)到增強(qiáng)圖像整體對比度的效果。由信息學(xué)的理論來解釋,具有最大熵(信息量)的圖像為均衡化圖像。
直方圖均衡化可表示為:
,t為某個象素變換后的灰度級,s為該象素變換前的灰度級。
該灰度變換函數(shù)應(yīng)滿足如下兩個條件:
條件1:保證原圖各灰度級在變換后仍保持從黑到白(或從白到黑)的排列順序;
條件2:保證變換前后灰度值動態(tài)范圍的一致性。
可以證明累積分布函數(shù)(cumulative distribution function CDF)滿足上述兩個條件并能將s的分布轉(zhuǎn)換為t的均勻分布。
事實(shí)上,s的CDF就是原始圖的累積直方圖,即:
根據(jù)這個公式,可以直接算出直方圖均衡化后各象素的灰度值。
需要取整,以滿足數(shù)字圖象的要求。
?
二、算法步驟
步驟 |
運(yùn)算 |
1 |
|
2 |
|
3 |
計(jì)算原始直方圖(像素比例) |
4 |
|
5 |
|
6 |
|
7 |
計(jì)算新的直方圖 |
三、算法測試
1、灰度圖
2、彩色圖
?
四、結(jié)果分析
(1)對于灰度圖和彩色圖,算法結(jié)果都不錯,直方圖顯示像素分布很廣、很平均。
(2)直方圖均衡化的優(yōu)點(diǎn):自動增強(qiáng)整個圖像的對比度。
(3)直方圖均衡化的不足:具體增強(qiáng)效果不易控制,處理的結(jié)果總是得到全局均衡化的直方圖。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
