直方圖均衡化是對於一幅圖像,其具有多個灰度等級的像素,我們盡可能讓這些灰度等級出現頻率的概率密度函數趨近於常數。這么做的意義在哪里?當一幅圖像比較暗的時候,灰度等級絕大部分處於低等級的狀態,那么由於我們使灰度等級頻率的概率密度函數盡可能趨向於常數,即盡可能保證在各個灰度等級出現頻率一樣,我們認為此時應該有更高的對比度,展示的細節更加細膩。在圖像處於過亮的情況下也可以得出相近結論。
舉個例子,來自於《數字圖像處理》第三章
有大小為64 * 64 像素的3比特數字圖像的灰度分布和直方圖值如下:
下面我們計算原本的灰度等級各自映射到哪個等級。
\(s_0 = 7\Sigma_{j=0}^0p_r(r_0) = 7 * p_r(r_0) = 1.33\)
\(s_1 = 7\Sigma_{j=0}^1p_r(r_0) = 7 * p_r(r_0) + 7 * p_r(r_1) = 3.08\)
則\(s_0\)四舍五入得1(實際計算機中灰度等級只能是離散的)
\(s_1\)四舍五入得3。
其他每個灰度等級的映射都類似,可以分別計算出均衡化后每個原灰度等級應該映射到的新的灰度等級。
Lena的灰度圖
Lena灰度圖做直方圖均衡化后
Lena的彩色圖
Lena彩色圖做直方圖均衡化后
對於彩色圖像的直方圖均衡化,可以考慮使用R,G,B三個通道分別均衡化,然后將三個通道合在一起。但這樣有可能會改變色調。
下面是直方圖均衡化的python實現,依賴PIL包
def his_equ(img, outfile,level=256,mode='RGB'):
'''
:param img: Image.open打開的文件句柄
:param outfile: 輸出文件的文件名
:param level:灰度等級,彩色圖是每個通道對應的等級數
:param mode:'rgb'為彩色模式,'gray'為灰度圖
:return: 按照輸出文件路徑保存均衡化之后的圖片
'''
if mode == 'RGB' or mode == 'rgb':
r, g, b = [], [], []
width, height = img.size[0], img.size[1]
sum_pix = width * height
pix = img.load()
for x in range(width):
for y in range(height):
r.append(pix[x, y][0])
g.append(pix[x, y][1])
b.append(pix[x, y][2])
r_c = dict(Counter(r))
g_c = dict(Counter(g))
b_c = dict(Counter(b))
r_p,g_p,b_p = [],[],[]
for i in range(level):
if r_c.has_key(i):
r_p.append(float(r_c[i]) / sum_pix)
else:
r_p.append(0)
if g_c.has_key(i):
g_p.append(float(g_c[i])/sum_pix)
else:
g_p.append(0)
if b_c.has_key(i):
b_p.append(float(b_c[i])/sum_pix)
else:
b_p.append(0)
temp_r,temp_g,temp_b = 0,0,0
for i in range(level):
temp_r += r_p[i]
r_p[i] = int(temp_r * (level-1))
temp_b += b_p[i]
b_p[i] = int(temp_b *(level-1))
temp_g += g_p[i]
g_p[i] = int(temp_g*(level -1))
new_photo = Image.new('RGB',(width,height))
for x in range(width):
for y in range(height):
new_photo.putpixel((x,y),(r_p[pix[x,y][0]],g_p[pix[x,y][1]],b_p[pix[x,y][2]]))
new_photo.save(outfile)
elif mode == 'gray' or mode == 'GRAY':
width, height = img.size[0], img.size[1]
sum_pix = width * height
pix = img.load()
pb = []
for x in range(width):
for y in range(height):
pb.append(pix[x,y])
pc = dict(Counter(pb))
pb = []
for i in range(level):
if pc.has_key(i):
pb.append(float(pc[i]) / sum_pix)
else:
pb.append(0)
temp = 0
for i in range(level):
temp += pb[i]
pb[i] = int(temp * (level-1))
new_photo = Image.new('L',(width,height))
for x in range(width):
for y in range(height):
new_photo.putpixel((x,y),pb[pix[x,y]])
new_photo.save(outfile)
if __name__ == '__main__':
ppp = Image.open('Lena.jpg','r')
his_equ(ppp,'lena_his.jpg')