数字图像处理(三)直方图均衡

在数字图像处理中,直方图均衡是调整图像亮度,对比度,图像增晰等操作中常用的做法。对于图像中各个不同的颜色进行直方图统计,采取统计数据对于颜色进行重新映射,从而达到调整对比度,图像增晰的目的。

1.数字图像的直方图

直方图是指对于整幅图像所有的像素点颜色进行统计,从而得到不同颜色的统计数据,将该数据以直方图的形式画出,即为图像的直方图。

例如,对于下面的这两幅图像:

其所对应的直方图为:

由图可见,高对比度的图像颜色分布较广,低对比度图像颜色集中分布在某一区域。第二张图像中的像素值集中分布在一个颜色上,造成图像模糊。

使用图像直方图可以清晰地看出图像的颜色分布情况,从而对于如何进行图形的增晰,提高对比度有了下一步的指导作用

2.直方图均衡

直方图均衡,顾名思义,就是在图像直方图上进行图像颜色的均衡。具体来说就是将之前分布过于集中的颜色在直方图上分散开来,让其映射到应当占据的像素值上。具体方法如下(以3位8色为例):

假设一张64*64像素(M=64,NJ=64,MN=4096),3位(L=8)的图像来说,其颜色空间为[0,L-1],即[0,7]。对整张图像的像素值进行统计,得到统计数据如下:

rk nk pr(rk)=nk/MN
r0=0 790 0.19
r1=1 1023 0.25
r2=2 850 0.21
r3=3 656 0.16
r4=4 329 0.08
r5=5 245 0.06
r6=6 122 0.03
r7=7 81 0.02

直方图均衡变换公式为:sk=T(rk)=(L1)j=0kpr(rj)=L1MNj=0knj

对于该直方图:s0=T(r0)=7j=00pr(rj)=7pr(r0)=1.33

类似的,有:s1=T(r1)=7j=01pr(rj)=7pr(r0)+7pr(r1)=3.08

以及s1=4.55,s3=5.67,s4=6.23,s5=6.65,s6=6.86,s7=7.00

将所有的s值近似为最接近的整数(四舍五入) (1)s0=1.331       s1=3.083       s2=4.555    s3=5.676

(2)s4=6.236       s5=6.657       s6=6.867    s7=7.007

将原有图像通过sk=T(rk)映射到新图像中,得到的结果即为直方图均衡的结果。

以上图中的长城为例,对于第二张图进行直方图均衡,得到的结果与原图对比如下:

原图像整体偏暗,经过直方图均衡之后亮度提升,对比度提升,长城的细节纹理更加清晰。

3.目标直方图均衡

以一幅图像的直方图为标准,对待均衡的图像按照该标准进行直方图均衡。方法如下:

首先对于待配准图像计算其均衡变换:sk=T(rk)=(L1)j=0kpr(rj)    k=0,1,2...L1

计算出标准图像的直方图均衡变换:G(zp)=(L1)i=0qpz(zi)

对于每一个待配准图像颜色rk,有sk与其对应;对于该sk,找到与其最近的G(zq),再将其反变换到颜色zq,可以建立从rkzq的映射,从而将待配准的图像以标准图像为模板进行直方图均衡。

还是以上面的长城图像为例,以原图为目标进行直方图均衡,得到结果对比如下:

用来匹配的原图为:

可以看出,相比于上面的直方图均衡,目标直方图均衡在整体的色彩上更加偏向于用于做为目标的标准图,观感上与原图更加接近。

4.局部直方图均衡

顾名思义,局部直方图是在图像的每一个小图像块例如33,5577大小的局部进行直方图均衡的操作,算法部分类似于直方图均衡,在此不再赘述,得到的效果如下:

附录

参考文献:

[1]数字图像处理[M]:第三版/(美)拉斐尔·C·冈萨雷斯(Rafael C.Gonzalez),(美)理查德·E·伍兹(Richard E. Woods)著;阮秋琦等译,—北京:电子工业出版社,2017.5

源码:

1.直方图统计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import mlab
from matplotlib import rcParams
def graybar(img,win_num,img_name,flag):
num=np.zeros((256))
img_shape=img.shape
if flag:
for i in range(img_shape[0]):
for j in range(img_shape[1]):
num[img[i][j][0]]+=1
else:
for i in range(img_shape[0]):
for j in range(img_shape[1]):
if img[i][j][0]==0:
img[i][j][0]=255
num[img[i][j][0]]+=1
fig=plt.figure(win_num)
rects=plt.bar(range(256),num,0.2)
plt.title('gray_bar_'+img_name)
plt.show()
def flag(str):
if '1' in str:
return 0
return 1
filename=['citywall.bmp','citywall1.bmp','citywall2.bmp','elain.bmp','elain1.bmp','elain2.bmp','elain3.bmp','lena.bmp','lena1.bmp','lena2.bmp','lena3.bmp','woman.BMP','woman1.bmp','woman2.bmp']
for i in filename:
img=cv.imread(i)
graybar(img,0,i,flag(i))

2.直方图均衡:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import mlab
from matplotlib import rcParams
def graybar_trans(img,flag):
num=np.zeros((256))
img_shape=img.shape
if flag:
for i in range(img_shape[0]):
for j in range(img_shape[1]):
num[img[i][j][0]]+=1
else:
for i in range(img_shape[0]):
for j in range(img_shape[1]):
if img[i][j][0]==0:
img[i][j][0]=255
num[img[i][j][0]]+=1
fre=num/np.sum(num)
trans=np.zeros((256))
trans[0]=fre[0]*256
for i in range(256):
if i:
trans[i]=trans[i-1]+fre[i]*255
trans=trans.astype(np.uint8)
new_img=np.zeros((img_shape[0],img_shape[1]))
for i in range(img_shape[0]):
for j in range(img_shape[1]):
new_img[i][j]=trans[img[i][j][0]]
return new_img.astype(np.uint8)
def flag(str):
if '1' in str:
return 0
return 1
filename=['citywall.bmp','citywall1.bmp','citywall2.bmp','elain.bmp','elain1.bmp','elain2.bmp','elain3.bmp','lena.bmp','lena1.bmp','lena2.bmp','lena3.bmp','woman.BMP','woman1.bmp','woman2.bmp']
for i in filename:
img=cv.imread(i)
result=graybar_trans(img,flag(i))
# cv.namedWindow(i,cv.WINDOW_FREERATIO)
# cv.imshow(i,result)
# cv.waitKey(0)
cv.imwrite('bar_'+i,result)

3.目标直方图均衡:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import mlab
from matplotlib import rcParams
def find(a,x):
shape_tmp=a.shape
for i in range(shape_tmp[0]):
if x-a[i]<=0:
return i
return shape_tmp[0]-1
def graybar_trans(img,cnt,flag):
file_tem=['citywall.bmp','elain.bmp','lena.bmp','woman.bmp']
img_tem=cv.imread(file_tem[cnt])
num,num_temp=np.zeros((256)),np.zeros((256))
img_shape=img.shape
if flag:
for i in range(img_shape[0]):
for j in range(img_shape[1]):
num[img[i][j][0]]+=1
num_temp[img_tem[i][j][0]]+=1
else:
for i in range(img_shape[0]):
for j in range(img_shape[1]):
if img[i][j][0]==0:
img[i][j][0]=255
num[img[i][j][0]]+=1
num_temp[img_tem[i][j][0]]+=1
fre=num/np.sum(num)
fre_temp=num_temp/np.sum(num_temp)
z,z_temp=np.zeros((256)),np.zeros((256))
z[0],z_temp[0]=fre[0]*256,fre_temp[0]*256
for i in range(256):
if i:
z[i]=z[i-1]+fre[i]*255
z_temp[i]=z_temp[i-1]+fre_temp[i]*255
trans=np.zeros((256))
for i in range(256):
trans[i]=find(z_temp,z[i])
trans=trans.astype(np.uint8)
new_img=np.zeros((img_shape[0],img_shape[1]))
for i in range(img_shape[0]):
for j in range(img_shape[1]):
new_img[i][j]=trans[img[i][j][0]]
return new_img.astype(np.uint8)
def cnt(str):
if 'citywall' in str:
return 0
if 'elain' in str:
return 1
if 'lena' in str:
return 2
if 'woman' in str:
return 3
def flag(str):
if '1' in str:
return 0
return 1
filename=['citywall.bmp','citywall1.bmp','citywall2.bmp','elain.bmp','elain1.bmp','elain2.bmp','elain3.bmp','lena.bmp','lena1.bmp','lena2.bmp','lena3.bmp','woman.BMP','woman1.bmp','woman2.bmp']
for i in filename:
img=cv.imread(i)
result=graybar_trans(img,cnt(i),flag(i))
# cv.namedWindow(i,cv.WINDOW_FREERATIO)
# cv.imshow(i,result)
# cv.waitKey(0)
cv.imwrite("aim_bar_"+i,result)

4.局部直方图均衡:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import cv2 as cv
import numpy as np

def trans(imname):
img=cv.imread(imname)
shape=img.shape
cnt_i=shape[0]//7+1
cnt_j=shape[0]//7+1
result=np.zeros((shape[0],shape[1]))
for i in range(cnt_i):
for j in range(cnt_j):
num=np.zeros(256)
for point_i in range(i*7,i*7+7):
for point_j in range(j*7,j*7+7):
if point_i<shape[0] and point_j<shape[1]:
num[img[point_i][point_j][0]]+=1
fre=num/np.sum(num)
T=np.zeros((256))
T[0]=fre[0]*256
for k in range(256):
if k:
T[k]=T[k-1]+fre[k]*255
for point_i in range(i*7,i*7+7):
for point_j in range(j*7,j*7+7):
if point_i<shape[0] and point_j<shape[1]:
result[point_i][point_j]=T[img[point_i][point_j][0]]
result=result.astype(np.uint8)
cv.imwrite("trans_"+imname,result)

trans('lena.bmp')
trans('elain.bmp')