直方圖及直方圖均值化的理論,實現及展示
直方圖:
首先,我們來看看什么是直方圖:
理論概念:
在圖像處理中,經常用到直方圖,如顏色直方圖、灰度直方圖等。 圖像的灰度直方圖就描述了圖像中灰度分布情況,能夠很直觀的展示出圖像中各個灰度級所 占的多少。 圖像的灰度直方圖是灰度級的函數,描述的是圖像中具有該灰度級的像素的個數:其中,橫 坐標是灰度級,縱坐標是該灰度級出現的頻率。
意義:
? 直方圖反映了圖像中的灰度分布規律。它描述每個灰度級具有的像素個數,但不包含 這些像素在圖像中的位置信息。
? 任何一幅特定的圖像都有唯一的直方圖與之對應,但不同的圖像可以有相同的直方圖。
? 如果一幅圖像有兩個不相連的區域組成,并且每個區域的直方圖已知,則整幅圖像的 直方圖是該兩個區域的直方圖之和
代碼實現:
import cv2
import numpy as np
from matplotlib import pyplot as plt'''
calcHist—計算圖像直方圖
函數原型:calcHist(images, channels, mask, histSize, ranges, hist=None, accumulate=None)
images:圖像矩陣,例如:[image]
channels:通道數,例如:0
mask:掩膜,一般為:None
histSize:直方圖大小,一般等于灰度級數
ranges:橫軸范圍
'''
'''
# 灰度圖像直方圖
# 獲取灰度圖像
img = cv2.imread("lenna.png", 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#cv2.imshow("image_gray", gray)# 灰度圖像的直方圖,方法一
plt.figure()
plt.hist(gray.ravel(), 256)
plt.show()'''
'''
# 灰度圖像的直方圖, 方法二
hist = cv2.calcHist([gray],[0],None,[256],[0,256])
plt.figure()#新建一個圖像
plt.title("Grayscale Histogram")
plt.xlabel("Bins")#X軸標簽
plt.ylabel("# of Pixels")#Y軸標簽
plt.plot(hist)
plt.xlim([0,256])#設置x坐標軸范圍
plt.show()
'''#彩色圖像直方圖
image = cv2.imread("lenna.png")
cv2.imshow("Original",image)
#cv2.waitKey(0)chans = cv2.split(image)
colors = ("b","g","r")
plt.figure()
plt.title("Flattened Color Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")for (chan,color) in zip(chans,colors):hist = cv2.calcHist([chan],[0],None,[256],[0,256])plt.plot(hist,color = color)plt.xlim([0,256])
plt.show()
運行結果:
彩色:
灰色:
直方圖均值化
由上面的輸出結果,我們很容易發現,像素的分布是不均勻的,我們需要將像素的分布變得均勻,這就用到了直方圖均值化。
概念:
直方圖均衡化是將原圖像的直方圖通過變換函數變為均勻的直方圖,然后按均勻直方圖修改原 圖像,從而獲得一幅灰度分布均勻的新圖像。
直方圖均衡化的作用是圖像增強。
步驟:
為了將原圖像的亮度范圍進行擴展,需要一個映射函數,將原圖像的像素值均衡映射到新直 方圖中,這個映射函數有兩個條件: (1)為了不打亂原有的順序,映射后亮、暗的大小關系不能改變, (2) 映射后必須在原有的范圍內,比如(0-255)
步驟:
- 依次掃描原始灰度圖像的每一個像素,計算出圖像的灰度直方圖H
- 計算灰度直方圖的累加直方圖
- 根據累加直方圖和直方圖均衡化原理得到輸入與輸出之間的映射關系。
- 最后根據映射關系得到結果:dst(x,y) = H’(src(x,y))進行圖像變換
理論公式:
1.對于輸入圖像的任意一個像素p, p∈[0,255], 總能在輸出圖像里有對應的像素q, q∈[0,255] 使得下面等式成 立(輸入和輸出的像素總量相等):
2.其中,輸出圖像每個灰度級的個數:
3.代入累加直方圖公式:
(因為k是從0開始的,所以是乘(q+1),H和W分別為圖像像素長和寬)
最后用一張圖來講解一下:
如左邊黃色55圖像中,像素值從0-9。
為其建立一個矩陣,pix值從0-9。
Ni值為該像素值存在的個數,如在55的圖像中,0像素有3個,Ni值為3。
Pi值為該像素值的概率,
sunmPi值就是之前Pi的總和,也可以看作sumPi=Pi+sumP(i-1),比如sumP0=P0=0.12,sumP1=sumP0+P1
最后的結果就是sumPi×255-1(注意:圖中忘記減一了)*,將這個結果的數值替換原像素數值。
代碼實現:
import cv2
import numpy as np
from matplotlib import pyplot as plt'''
equalizeHist—直方圖均衡化
函數原型: equalizeHist(src, dst=None)
src:圖像矩陣(單通道圖像)
dst:默認即可
'''# # 獲取灰度圖像
# img = cv2.imread("lenna.png", 1)
# gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# #cv2.imshow("image_gray", gray)
#
# # 灰度圖像直方圖均衡化
# dst = cv2.equalizeHist(gray)
#
# # 直方圖
# hist = cv2.calcHist([dst],[0],None,[256],[0,256])
#
# plt.figure()
# plt.hist(dst.ravel(), 256)
# plt.show()
#
# cv2.imshow("Histogram Equalization", np.hstack([gray, dst]))
# cv2.waitKey(0)# 彩色圖像直方圖均衡化
img = cv2.imread("lenna.png", 1)
cv2.imshow("src", img)# 彩色圖像均衡化,需要分解通道 對每一個通道均衡化
(b, g, r) = cv2.split(img)
bH = cv2.equalizeHist(b)
gH = cv2.equalizeHist(g)
rH = cv2.equalizeHist(r)
# 合并每一個通道
result = cv2.merge((bH, gH, rH))
cv2.imshow("dst_rgb", result)cv2.waitKey(0)chans = cv2.split(result)
colors = ("b","g","r")
plt.figure()
plt.title("Flattened Color Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")for (chan,color) in zip(chans,colors):hist = cv2.calcHist([chan],[0],None,[256],[0,256])plt.plot(hist,color = color)plt.xlim([0,256])
plt.show()
運行結果:
彩色:
灰色:
結果對比很明顯像素值變的均勻很多,而且明顯彩色圖像變得更亮了一些。