直方圖均衡化的 C++ 實現(基於 openCV)


這是數字圖像處理課的大作業,完成於 2013/06/17,需要調用 openCV 庫,完整源碼和報告如下:

 

  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 /* 灰度級結點 */
 10 typedef struct {
 11     int pixels;        // 灰度級對應像素個數
 12     float rate;        // 像素比例
 13     float accuRate;    // 累計像素比例
 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 * 直方圖均衡化函數 
 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;        // 總像素數
106     printf("%d \n", sumPixels);
107     int values = static_cast<int>(pow((float)2, depth)); // 根據圖像深度計算像素取值范圍
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; // 根據像素取值范圍和灰度級求每個灰度級的像素間隔
121     levNode* levNodes = (levNode*)calloc(levels, sizeof(levNode)); // 生成灰度結點
122     //for (int lev = 0; lev < levels; ++lev) printf("%d \n", levNodes[lev].pixels);
123     //char* pValues = pGray->imageData;
124 
125     /* 統計每個灰度級的像素個數 */
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     /* 計算每個灰度級像素比例和累計比例 */
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 * 繪制直方圖函數
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 }
View Code

 

一、直方圖均衡化概述

直方圖均衡化是一種圖像增強方法,其基本思想是把給定圖像的直方圖分布改造成均勻分布的直方圖,從而增加象素灰度值的動態范圍,達到增強圖像整體對比度的效果。由信息學的理論來解釋,具有最大熵(信息量)的圖像為均衡化圖像。

直方圖均衡化可表示為:clip_image002,t為某個象素變換后的灰度級,s為該象素變換前的灰度級。

該灰度變換函數應滿足如下兩個條件:

1)f(s)在clip_image004范圍內是單值遞增函數;

2)對clip_image006

條件1:保證原圖各灰度級在變換后仍保持從黑到白(或從白到黑)的排列順序;

條件2:保證變換前后灰度值動態范圍的一致性。

可以證明累積分布函數(cumulative distribution function CDF)滿足上述兩個條件並能將s的分布轉換為t的均勻分布。

事實上,s的CDF就是原始圖的累積直方圖,即:

clip_image008

其中clip_image010clip_image012、k=0,1,…,L-1

根據這個公式,可以直接算出直方圖均衡化后各象素的灰度值。clip_image014需要取整,以滿足數字圖象的要求。

 

二、算法步驟

步驟

運算

1

列出原始圖灰度級 clip_image016

2

統計原始直方圖各灰度級象素數 clip_image018

3

計算原始直方圖(像素比例)

4

計算累積直方圖 clip_image020(累計像素比例)

5

取整clip_image022

6

確定映射對應關系( clip_image016[1]clip_image020[1]

7

計算新的直方圖

三、算法測試

1、灰度圖

clip_image024clip_image026

clip_image028

clip_image030

2、彩色圖

clip_image032clip_image034

clip_image036

clip_image038

 

四、結果分析

(1)對於灰度圖和彩色圖,算法結果都不錯,直方圖顯示像素分布很廣、很平均。

(2)直方圖均衡化的優點:自動增強整個圖像的對比度。

(3)直方圖均衡化的不足:具體增強效果不易控制,處理的結果總是得到全局均衡化的直方圖。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM