直方图均衡化的 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)直方图均衡化的不足:具体增强效果不易控制,处理的结果总是得到全局均衡化的直方图。

你可能感兴趣的:(opencv)