直方图均衡化算法原理与实现

工作后,对原来学习的一些基本图像处理算法有了一些新的认识,比如Canny 算法,直方图均衡化算法等,今天就来说说直方图均衡化算法。


直方图均衡化原理

我们知道提高图像对比度的变换函数 f ( x ) f(x) f(x)需要满足一下条件:

  1. f ( x ) f(x) f(x) 0 < = x < = L − 1 0<=x<=L-1 0<=x<=L1上单调递增(不要求严格单调递增),其中L表示灰度级(L=256)
  2. f ( x ) f(x) f(x)的范围是 [ 0 , L − 1 ] [0,L-1] [0,L1]

我们知道当图像直方图完全均匀分布的时候,此时图像的熵是最大的(随机变量每个值的概率都相同时,概率最大),图像对比度是最大的。所以,理想情况下,图像经过变换函数 f ( x ) f(x) f(x)变换后,直方图能够均匀分布,此时对比度是最大的。

那问题来了?怎样的变换函数具有如此神奇的功能呢?[1]P74中给出了答案。
在图像处理中,有一个重要的函数,能够满足上面的条件:
y = f ( x ) = ( L − 1 ) ∫ 0 x p x ( t ) d t y=f(x)=(L-1)\int_0^xp_x(t){\rm d}t y=f(x)=(L1)0xpx(t)dt
英文原版中P145中的表述为:
这里写图片描述
其中 p x ( x ) p_x(x) px(x)表示概率密度函数,在离散的图像中,表示直方图的每个灰度级的概率(在图像中,灰度级就可以看成是一个随机变量,而直方图就是该随机变量的概率密度函数),由概率论的知识,我们可以知道,变换函数 f ( x ) f(x) f(x)其实就是连续型随机变量x的分布函数,表示的是函数下方的面积。

这里写图片描述

分布函数的两个性质:1.单调不减 2.值域为[0,1],我们可以知道f(x)满足条件1和2

有人可能会有这个疑问?图像是离散的,为什么可以用连续的来表示呢?从数学角度来看,离散是连续的一种特例(图像就是一个很好的例子)。

下面我们证明变换后的直方图是均匀的。
由概率论知识,变换后的概率密度:
p y ( y ) = p x ( x ) ∣ ( f − 1 ( y ) ) ′ ∣ p_y(y)=p_x(x)|(f^{-1}(y))^\prime| py(y)=px(x)(f1(y))
由变上限函数求导法则可知 f ( x ) ′ = ( L − 1 ) p x ( x ) f(x)^\prime =(L-1)p_x(x) f(x)=(L1)px(x)
反函数的导数等于原函数导数的倒数,所以
( f − 1 ( y ) ) ′ = 1 ( L − 1 ) p x ( x ) (f^{-1}(y))^\prime = {1 \over (L-1)p_x(x)} (f1(y))=(L1)px(x)1
所以
p y ( y ) = 1 L − 1 p_y(y)={1 \over L-1} py(y)=L11

看到了吧,变换后的概率密度函数是一个均匀分布,对于图像来说,就是每个灰度级概率都是相等的,达到了我们的目的。
下面我们需要将这个变换函数转换为图像中的表达,图像中,我们可以知道,可以使用求和代替积分,差分代替微分,所以上述的变换函数就是:
y = f ( x ) = ( L − 1 ) ∑ 0 x i h ( x i ) w × h y=f(x)=(L-1)\sum_0^{x_i}{h(x_i) \over w \times h} y=f(x)=(L1)0xiw×hh(xi)

其中 h ( x i ) h(x_i) h(xi)表示直方图中每个灰度级像素的个数, w w w h h h分别表示图像的宽和高。


直方图均衡化算法实现

根据上面的推导,算法实现如下:

//不支持OpenCV的ROI
void GetHistogram(const Mat &image, int *histogram)
{
	memset(histogram, 0, 256 * sizeof(int));

	//计算直方图
	int pixelCount = image.cols*image.rows;
	uchar *imageData = image.data;
	for (int i = 0; i <= pixelCount - 1; ++i)
	{
		int gray = imageData[i];
		histogram[gray]++;
	}
}

void EqualizeHistogram(const Mat &srcImage, Mat &dstImage)
{
	CV_Assert(srcImage.type() == CV_8UC1);
	dstImage.create(srcImage.size(), srcImage.type());

	// 计算直方图
	int histogram[256];
	GetHistogram(srcImage, histogram);

	// 计算分布函数(也就是变换函数f(x))
	int numberOfPixel = srcImage.rows*srcImage.cols;
	int LUT[256];
	LUT[0] = 1.0*histogram[0] / numberOfPixel*255;
	int sum = histogram[0];
	for (int i = 1; i <= 255; ++i)
	{
		sum += histogram[i];

		LUT[i] = 1.0*sum / numberOfPixel * 255;
	}

	// 灰度变换
	uchar *dataOfSrc = srcImage.data;
	uchar *dataOfDst = dstImage.data;
	for (int i = 0; i <= numberOfPixel - 1; ++i)
		dataOfDst[i] = LUT[dataOfSrc[i]];
}

原图
这里写图片描述

测试结果:

这里写图片描述
从直方图均衡化算法中,可以看出,看似简单的几个算法步骤,背后蕴藏了很多数学理论知识,这大概就是数学的魅力吧。

完整工程见github项目:QQImageProcess_OpenCV
其中直方图均衡化算法的实现在 Src/ImageProcess/GrayTransformation.h中的EqualizeHistogram函数中。
同时自己也实现了一个不依赖于OpenCV的直方图均衡化算法,见github项目:QQImageProcess,直方图均衡化算法的具体实现在Src/ImageProcess/GrayTransformation.h中的EqualizeHistogram()函数中。


2016-9-3 10:41:39


参考文献

  1. 《数字图像处理》第3版,冈萨雷斯

非常感谢您的阅读,如果您觉得这篇文章对您有帮助,欢迎扫码进行赞赏。
这里写图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值