直方图均衡化是一种增强图像对比度常用的一种方式,主要思想是将图像的直方图分布映射为近似均匀分布,从而增强图像的对比度。原始图像由于其灰度分布可能集中在较窄的区间,造成图像不够清晰。例如,过曝光图像的灰度级集中在高亮度范围内,而曝光不足将使图像灰度级集中在低亮度范围内。采用直方图均衡化,可以把原始图像的直方图变换为均匀分布(均衡)的形式,这样就增加了像素之间灰度值差别的动态范围,从而达到增强图像整体对比度的效果。
直方图均衡化原理
原理介绍
对于一幅图像,我们用
r
r
r 表示待处理的图像图像灰度级,假设
r
r
r 的取值区间是
[
0
,
L
−
1
]
[0, L-1]
[0,L−1],
r
r
r 代表图像的256个灰度级。
r
r
r满足如下关系式:
s
=
T
(
r
)
,
0
<
r
<
L
−
1
s= T(r), 0<r<L-1
s=T(r),0<r<L−1
其满足以下条件:
(a)
T
(
r
)
T(r)
T(r)在区间
0
<
r
<
L
−
1
0<r<L-1
0<r<L−1 为单调递增函数
(b) 当
0
<
L
−
1
0<L-1
0<L−1 时,
0
<
T
(
r
)
<
L
−
1
0<T(r)<L-1
0<T(r)<L−1
条件(a)中要求
T
(
r
)
T(r)
T(r)为单调递增函数是为了保证输出灰度值不少于相应的输入灰度值,防止灰度反变换时产生人为缺陷。条件(b)保证输入灰度的范围和输出灰度的范围相同。
直方图均衡化的目的就是求
T
(
r
)
T(r)
T(r)的函数表达式。
对于连续的情况:
s
=
T
(
r
)
=
∫
0
r
p
r
(
r
)
d
r
s=T(r)=\int_0^r {p_r(r)} \,{\rm d}r
s=T(r)=∫0rpr(r)dr
对于离散的情况:
s
k
=
T
(
r
k
)
=
∑
i
=
0
k
p
r
(
r
i
)
=
∑
i
=
0
k
n
i
N
s_k=T(r_k)=\sum_{i=0}^kp_r(r_i)=\sum_{i=0}^k\frac{n_i}{N}
sk=T(rk)=i=0∑kpr(ri)=i=0∑kNni
计算步骤
① 统计每个灰度级像素个数
② 计算累积概率密度函数
③ 根据累积概率密度求灰度映射表
④ 根据映射表计算映射后的灰度值
代码实现
代码实现如下:
// src:src image data
// dst:dst image data
// height: image height
// width: image width
void equalizeHist(unsigned char* src, unsigned char*dst, int &height, int &width)
{
const int hist_sz = 256;
int hist[hist_sz] = { 0, };
int lut[hist_sz];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
hist[src[i*width + j]]++;
}
int i = 0;
while (!hist[i])
i++;
int total = width * height;
if (hist[i] == total)
{
//原始图像只有一个灰度级
std::for_each(dst, dst + total, [i](unsigned char& value) { value = i; });
return;
}
float scale = (hist_sz - 1.f) / (total - hist[i]);
int sum = 0;
//将第一个灰度级置为0
for (lut[i++] = 0; i < hist_sz; i++)
{
sum += hist[i];
lut[i] = static_cast<unsigned char>(sum * scale + 0.5f);
}
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
dst[y * width + x] = static_cast<unsigned char>(lut[src[y * width + x]]);
}
}
}
处理结果:
处理的结果和opencv函数接口cv::equalizeHist的结果一致。
参考文献:
- Rafael C. Gonzalez, Richard E. Woods,Digital Image Processing (Third Edition)
- 直方图均衡化博客
感兴趣的同学可以了解下Cuda实现直方图均衡化 ,有具体的代码实现。