这是数字图像处理课的大作业,完成于 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)直方图均衡化的不足:具体增强效果不易控制,处理的结果总是得到全局均衡化的直方图。

