这是数字图像处理课的大作业,完成于 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 }
一、直方图均衡化概述
直方图均衡化是一种图像增强方法,其基本思想是把给定图像的直方图分布改造成均匀分布的直方图,从而增加象素灰度值的动态范围,达到增强图像整体对比度的效果。由信息学的理论来解释,具有最大熵(信息量)的图像为均衡化图像。
直方图均衡化可表示为:,t为某个象素变换后的灰度级,s为该象素变换前的灰度级。
该灰度变换函数应满足如下两个条件:
条件1:保证原图各灰度级在变换后仍保持从黑到白(或从白到黑)的排列顺序;
条件2:保证变换前后灰度值动态范围的一致性。
可以证明累积分布函数(cumulative distribution function CDF)满足上述两个条件并能将s的分布转换为t的均匀分布。
事实上,s的CDF就是原始图的累积直方图,即:
根据这个公式,可以直接算出直方图均衡化后各象素的灰度值。需要取整,以满足数字图象的要求。
二、算法步骤
步骤 |
运算 |
1 |
|
2 |
|
3 |
计算原始直方图(像素比例) |
4 |
|
5 |
|
6 |
|
7 |
计算新的直方图 |
三、算法测试
1、灰度图
2、彩色图
四、结果分析
(1)对于灰度图和彩色图,算法结果都不错,直方图显示像素分布很广、很平均。
(2)直方图均衡化的优点:自动增强整个图像的对比度。
(3)直方图均衡化的不足:具体增强效果不易控制,处理的结果总是得到全局均衡化的直方图。