摘要
本文记录机器视觉学习中常用的图像空间域增强的原理和实现,作为自己后续参考的文献,也给看到本文的同学参考。
直方图均衡化原理
1)基础知识
对于图像,我们可将其看成带有像素值的数字图像,而每一个像素值为一个灰度级,且数字图像的灰度级范围为[0,L-1](L最大为256),同时其为一个离散的图像,我们可以将离散的数值从整体角度看成一个离散函数,故有
h
(
r
k
)
=
n
k
(
r
k
)
h\begin{pmatrix} r_k\end{pmatrix} = n_k\begin{pmatrix} r_k\end{pmatrix}
h(rk)=nk(rk)
其中
r
k
r_k
rk为第k级灰度值,
n
k
n_k
nk为图像中灰度级为
r
k
r_k
rk的像素个数,进行归一化处理,求
r
k
r_k
rk的发生概率:
p
(
r
k
)
=
n
k
/
n
p\begin{pmatrix} r_k\end{pmatrix} = n_k/n
p(rk)=nk/n
其中,n为图像中像素的总数,而
p
(
r
k
)
p\begin{pmatrix} r_k\end{pmatrix}
p(rk)为灰度级
r
k
r_k
rk发生的概率估计值,进一步推理出,图像中所有灰度级的概率估计值就可以构成灰度级的概率密度函数
p
r
(
r
k
)
p_r\begin{pmatrix} r_k\end{pmatrix}
pr(rk)。
2)直方图均衡化原理
在真正说直方图原理之前,要先明白直方图均衡化的目的是什么?直方图均衡化本质上就是将图像中的灰度级数目进行均衡化的分布,即为灰度级数目不集中在部分的灰度级之上,当图像直方图完全的均匀分布后,此时的图像的熵是最大的(即每个灰度级的概率分布都是均衡的),图像的对比度也是最大的,进而增强了图像的可观察度。
那我们又如何实现图像的灰度级均匀的分布呢? 答案是变换函数,通过对灰度级的变换实现对原直方图的变换。
在这里我们假设找到一个变换函数
T
(
r
)
T\begin{pmatrix} r\end{pmatrix}
T(r),实现:
S
=
T
(
r
)
S=T\begin{pmatrix}r\end{pmatrix}
S=T(r),但需要满足如下的条件:
(1)灰度级范围为:
0
≤
r
≤
1
0\leq r \leq1
0≤r≤1
(2)变换函数
T
(
r
)
T\begin{pmatrix}r\end{pmatrix}
T(r)在区间
0
≤
r
≤
1
0\leq r \leq1
0≤r≤1上单调递增
(3)当
0
≤
r
≤
1
0\leq r \leq1
0≤r≤1时,
T
(
r
)
T\begin{pmatrix}r\end{pmatrix}
T(r)范围为
0
≤
r
≤
1
0\leq r \leq1
0≤r≤1
注意:
(1)可能看到这里的小伙伴会有一个疑问?为啥灰度级的范围不是最大为255呢?大家可以把这理解成理论知识或者理解成灰度级的取值都在1范围内,实际应用时灰度级的范围还应该是
[
0
,
255
]
\begin{bmatrix}0,255\end{bmatrix}
[0,255]
(2)关于
T
(
r
)
T\begin{pmatrix}r\end{pmatrix}
T(r)范围是因为初始变换为灰度级概率密度累加计算,因此最大值不会超过1。
2)直方图均衡化推理求解
在原理中,已经介绍初始的变换为灰度级概率密度函数的累加过程(即为求解累加分布函数
T
(
r
)
T\begin{pmatrix}r\end{pmatrix}
T(r)):
S
=
T
(
r
)
=
∫
1
r
p
r
(
x
)
d
x
S=T\begin{pmatrix}r\end{pmatrix}=\int_1^rp_r\begin{pmatrix} x\end{pmatrix}dx
S=T(r)=∫1rpr(x)dx
其中,
p
r
(
x
)
p_r\begin{pmatrix} x\end{pmatrix}
pr(x)表示灰度级概率密度函数,该累加分布函数(CDF)需要满足如下条件:
(1)灰度级范围为:
0
≤
r
≤
L
−
1
0\leq r \leq L-1
0≤r≤L−1
(2)变换函数
T
(
r
)
T\begin{pmatrix}r\end{pmatrix}
T(r)在区间
0
≤
r
≤
L
−
1
0\leq r \leq L-1
0≤r≤L−1上单调递增
(3)当
0
≤
r
≤
L
−
1
0\leq r \leq L-1
0≤r≤L−1时,
T
(
r
)
T\begin{pmatrix}r\end{pmatrix}
T(r)范围为
0
≤
r
≤
L
−
1
0\leq r \leq L-1
0≤r≤L−1
在此基础上再进一步进行函数变换,寻找变换函数实现图像的直方图均衡分布,在这里我就直接给出如下公式:
S
=
T
(
r
)
=
(
L
−
1
)
∫
0
r
p
r
(
x
)
d
x
(
1
)
S=T\begin{pmatrix}r\end{pmatrix}=\begin{pmatrix}L-1\end{pmatrix}\int_0^rp_r\begin{pmatrix} x\end{pmatrix}dx \begin{pmatrix} 1\end{pmatrix}
S=T(r)=(L−1)∫0rpr(x)dx(1)
对于该公式是如何得到的?我只能说是之前的研究员经过大量实验获得的,大家也可以尝试如下一个公式,个人观点均可以:
S
=
T
(
r
)
=
(
(
L
−
1
)
∫
0
r
p
r
(
x
)
d
x
+
0.5
)
(
2
)
S=T\begin{pmatrix}r\end{pmatrix}=\begin{pmatrix} \begin{pmatrix}L-1\end{pmatrix}\int_0^rp_r\begin{pmatrix} x\end{pmatrix}dx+0.5\end{pmatrix} \begin{pmatrix} 2\end{pmatrix}
S=T(r)=((L−1)∫0rpr(x)dx+0.5)(2)
注意:该公式没有附贴代码,大家可自己修改
接下来,对上述的 公式
(
1
)
\begin{pmatrix} 1\end{pmatrix}
(1)进行离散形式变换,因为是对灰度级概率密度函数进行积分,而积分的本质可看成为累加,故可变换如下形式:
S
=
T
(
r
)
=
(
L
−
1
)
∑
0
r
p
r
(
x
)
S=T\begin{pmatrix}r\end{pmatrix}=\begin{pmatrix}L-1\end{pmatrix}\sum_0^rp_r\begin{pmatrix} x\end{pmatrix}
S=T(r)=(L−1)0∑rpr(x)
其中,
r
r
r的范围为
0
≤
r
≤
L
−
1
0\leq r \leq L-1
0≤r≤L−1。
直方图均衡化实现
(1)统计图像中灰度级的数目
(2)计算灰度级的累加分布概率
(3)通过变换函数,获变换后的灰度级的概率密度
(4)遍布原图,修改原图像
直方图均衡化代码
# include <iostream>
#include <opencv.hpp>
using namespace cv;
using namespace std;
void GetHistogram(const Mat& srcImage, int* histogram);
int main() {
Mat srcImage; //存放读取的原图
Mat dstImage; //直方均衡的图像
srcImage = imread("d://1.jpg", 0); //1RGB 0灰色
if (!srcImage.data)
cout << "图片加载失败,请检查文件是否存在!" << endl;
else
cout << "图片加载成功!" << endl;
CV_Assert(srcImage.type() == CV_8UC1); //判断图像是否为灰度图片,非灰色报错
dstImage.create(srcImage.size(), srcImage.type()); //设置目标图与原图大小和类型相同
// 计算直方图,即为每个灰度级在图像中的数目
int histogram[256]; //存放为每一个灰度级在图像中的数目
GetHistogram(srcImage, histogram);
//计算累积分布函数,变换函数
int pixelSrcImage = srcImage.cols * srcImage.rows;
int Lut[256]; //分布函数
Lut[0] = (1.0 * histogram[0] / pixelSrcImage) * 255;
int sum = histogram[0];
for (int i = 1; i < 256; ++i) {
sum += histogram[i];
Lut[i] = (1.0 * sum / pixelSrcImage) * 255;
}
//灰度变换
uchar* srcImageDate = srcImage.data;
uchar* dstImageDate = dstImage.data;
for (int i = 0; i < pixelSrcImage; ++i) {
dstImageDate[i] = Lut[srcImageDate[i]];
}
imshow("原始图像", srcImage);
imshow("直方图均衡后图像", dstImage);
//保持等待状态
waitKey(0);//括号里可以填任意正整数,意味着,图像显示的毫秒时间
return 0;
}
void GetHistogram(const Mat& srcImage, int* histogram) {
memset(histogram, 0, 256 * sizeof(int)); //设置histogram数组初始全为0
//建立直方图, 统计图像中每一灰度级数
int pixelCount = srcImage.cols * srcImage.rows;
uchar *srcImageDate = srcImage.data;
for (int i = 0; i < pixelCount; ++i) {
int grayDate = srcImageDate[i];
histogram[grayDate]++;
}
}
实验结果
如有错误!敬请大家批评指责!
一起学习!一起进步!