基本思想
如果一副图像的像素占有很多的灰度级而且分布均匀,那么这样的图像往往有高对比度和多变的灰度色调。直方图均衡化就是一种能仅靠输入图像直方图信息自动达到这种效果的变换函数。它的基本思想是对图像中像素个数多的灰度级进行展宽,而对图像中像素个数少的灰度进行压缩,从而扩展像元取值的动态范围,提高了对比度和灰度色调的变化,使图像更加清晰。
直方图均衡化处理的“中心思想”是把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内的均匀分布。直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致相同。直方图均衡化就是把给定图像的直方图分布改变成“均匀”分布直方图分布。
基本流程
直白地理解直方图均衡化:上述公式只是从数学的角度阐述了直方图均衡化的计算方法,没有深入的解释数学公式背后的含义。简单来说,直方图均衡化是要找到一个对应函数s=F(r),r为输入信号图像,s为输出信号图像。我们的目的就是寻找F的表达。想象一下,一个未处理的图像,其直方图是位于灰度值的一个狭小区域,用俗话说就是直方图很窄。我们要把它变成一个直方图很宽的图像,那么,这需要一个拉伸函数。显而易见,这个函数必须保证,图像中原有像素的大小顺序不能变,否则会要改变原图像中表达的内容。这时候我们想到了累积分布函数。为了方便理解,反过来讨论下为什么找累积分布函数。首先,一般来说灰度图像像素是在0-255之间的取值,那么如果要把像素尽可能的分布于0-255之间,在分布的时候,要考虑到原本图像的灰度值在某个灰度区间内的密度不同。举个例子,图像r原本像素分布在100-150之间,且140-150集中了原先像素的绝大部分像素值,这个时候,如果让原来的图像按线性函数映射到0-255是行不通的,线性函数并不能表达原来像素值的密度分布情况。然而,累积分布函数是与原来的像素在某个特定区间内分布情况有着很强的关联性。如果对原来的像素做累积,发现在140-150间的像素占了影响累积函数变化的绝大部分。累积函数只是一个表达原本像素在区间内的分布规律,它很好的表达了原图像中各个像素在灰度值为100-150的区间内的分布情况。这时候我们用这个分布情况来乘以255,就把这个分布情况拉伸至了0-255区间。如果把这个乘积作为像素值形成新的图像s,就会发现原本窄的直方图就会被拉伸之至0-255,同时也很好的保留了原本图像中的像素值分布在灰度上的分布规律。以上的过程就是从输入图像r到输出图像s的过程,这个累积函数就是我们要找的映射函数F。
举例来看
直方图均衡化过程中,映射方法是
其中,n是图像中像素的总和,nk 是当前灰度级的像素个数,L是图像中可能的灰度级总数。
假设存在下述图像
得图像的统计信息如下图所示,并根据统计信息完成灰度值映射:
映射后的图像如下所示:
算法片段
直方图均衡化算法分为三个步骤,
- 第一步是统计直方图每个灰度级出现的次数,
- 第二步是累计归一化的直方图,
- 第三步是计算新的像素值。
1. 统计直方图每个灰度级出现的次数
for(i=0;i<height;i++){ for(j=0;j<width;j++){ n[s[i][j]]++; } } for(i=0;i<L;i++){ p[i]=n[i]/(width*height); }
这里,n[i]表示的是灰度级为i的像素的个数,L表示的是最大灰度级,width和height分别表示的是原始图像的宽度和高度,所以,p[i]表示的就是灰度级为i的像素在整幅图像中出现的概率(其实就是p[]这个数组存储的就是这幅图像的归一化之后的直方图)。
2. 累计归一化的直方图
for(i=0;i<=L;i++){ for(j=0;j<=i;j++){ c[i]+=p[j]; } }
c[]这个数组存储的就是累计的归一化直方图
3. 计算新的像素值
//找出像素的最大值和最小值。 max=min=s[0][0]; for(i=0;i<height;i++){ for(j=0;j<width;j++){ if(max<s[i][j]){ max=s[i][j]; }else if(min>s[i][j]){ min=s[i][j]; } } } //t[][]就是最终直方图均衡化之后的结果。 for(i=0;i<height;i++){ for(j=0;j<width;j++){ t[i][j]=c[s[i][j]]*(max-min)+min; } }
注: 对于彩色的图片来说,直方图均衡化一般不能直接对R、G、B三个分量分别进行上述的操作,而要将RGB转换成HSV来对V分量进行直方图均衡化的操作。
优缺点
Ref
max=min=s[0][0];
for(i=0;i<height;i++){
for(j=0;j<width;j++){
if(max<s[i][j]){
max=s[i][j];
}else if(min>s[i][j]){
min=s[i][j];
}
}
}