直方图均衡化的作用是图像增强。
(就是想使得图像的直方图分布尽可能均匀,理想情况各个灰度值的对应的像素点个数相同)
有两个问题比较难懂,一是为什么要选用累积分布函数,二是为什么使用累积分布函数处理后像素值会均匀分布。
第一个问题。均衡化过程中,必须要保证两个条件:①像素无论怎么映射,一定要保证原来的大小关系不变,较亮的区域,依旧是较亮的,较暗依旧暗,只是对比度增大,绝对不能明暗颠倒;②如果是八位图像,那么像素映射函数的值域应在0和255之间的,不能越界。综合以上两个条件,累积分布函数是个好的选择,因为累积分布函数是单调增函数(控制大小关系),并且值域是0到1(控制越界问题),所以直方图均衡化中使用的是累积分布函数。
第二个问题。累积分布函数具有一些好的性质,那么如何运用累积分布函数使得直方图均衡化?比较概率分布函数和累积分布函数,前者的二维图像是参差不齐的,后者是单调递增的。直方图均衡化过程中,映射方法是
其中,n是图像中像素的总和,是当前灰度级的像素个数,L是图像中可能的灰度级总数。
通过上面的公式,直方图均衡化的步骤是:
(1)、我们求出图像的直方图。
(2)、计算出图像的累计直方图,归一化的累计直方图(即每一项变成概率)。
(3)、得到每个灰度值所对应的累计直方图中的概率后,用概率乘上想要将图像映射到的像素区域最大值(例如,8位图像,可以映射到255)。
(4)、即得到了直方图均衡化后的图像。彩色图像也是一样的,只要分别对RGB层进行上述处理即可。
来看看通过上述公式怎样实现的拉伸。假设有如下图像:
得图像的统计信息如下图所示,并根据统计信息完成灰度值映射:
即变换后的灰度值=原灰度值对应的累积分布概率*255(假设是8位图像,像素值得取值为【0,255】)
映射后的图像如下所示:
以上就是直方图映射均衡化的步骤。
关于为什么用累积分布还有下面的解释:
原图
均衡化后
python代码实现:
#-*- coding:utf-8 -*-
from PIL import Image
from pylab import *
from numpy import *
import matplotlib.pyplot as plt
#计算直方图
def computeHist(img):
m,n=shape(img)
hist=[0]*256
for i in range(0,m):
for j in range(0,n):
hist[img[i,j]]+=1
return hist
#计算累积直方图
def computeCDF(hist,img):
m,n=shape(img)
p=[0]*256
for i in range(0,256):
p[i]=float(hist[i])/(m*n)
return p
#将累计直方图改为概率模式
def histeq(p):
c=[0]*256
for i in range(0,256):
for j in range(0,i):
c[i]+=p[j]
return c
#将原图中各个像素点通过累积概率转变为直方图均衡化后的图像
def transToHistEq(c,img):
image=img.copy()
m,n=shape(img)
for i in range(0,m):
for j in range(0,n):
image[i,j]=int(c[image[i,j]]*256)
return image
#读取图片,并转为灰度图像
im=array(Image.open("E:/source.jpg").convert('L'))
hist=computeHist(im)
p=computeCDF(hist,im)
c=histeq(p)
img=transToHistEq(c,im)
#array()的方向操作,使得后面能进行画图
img=Image.fromarray(img)
#原图
figure()
gray()
imshow(im)
axis('off')
#均衡化后的图
figure()
gray()
imshow(img)
axis('off')
show()