直方圖
什么是直方圖呢?
直方圖是對數(shù)據(jù)集合統(tǒng)計,并將統(tǒng)計結(jié)果分布于一系列定義的bins中。這里的數(shù)據(jù)不僅僅是灰度值,統(tǒng)計數(shù)據(jù)可能是任何能夠有效描述圖像的特征,例如顏色、梯度/邊緣、形狀、紋理、局部特征點、視覺詞匯等。
灰度直方圖
通過直方圖你可以對整幅圖像的灰度分布有一個整體的了解。直方圖的 x 軸是灰度值(0 到 255),y 軸是圖片中具有同一個灰度值的點的數(shù)目。
灰度直方圖其實就是對圖像的另一種解釋。通過直方圖我們可以對圖像的對比度,亮度,灰度分布等有一個直觀的認(rèn)識。幾乎所有的圖像處理軟件都提供了直方圖分析功能。在這里,直方圖是根據(jù)灰度圖像繪制的,而不是彩色圖像。直方圖的左邊區(qū)域像是了暗一點的像素數(shù)量,右側(cè)顯示了亮一點的像素的數(shù)量。

使用OpenCV 統(tǒng)計直方圖函數(shù) cv2.calcHist 可以幫助我們統(tǒng)計一幅圖像的直方圖。
cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
參數(shù)意義如下:
images: 原圖像(圖像格式為 uint8 或 ?oat32)。當(dāng)傳入函數(shù)時應(yīng)該用中括號 [] 括起來,例如:[img]。
channels: 同樣需要用中括號括起來,它會告訴函數(shù)我們要統(tǒng)計那幅圖像的直方圖。如果輸入圖像是灰度圖,它的值就是 [0];如果是彩色圖像的話,傳入的參數(shù)可以是 [0],[1],[2] 它們分別對應(yīng)著通道 B,G,R。
mask: 掩模圖像。要統(tǒng)計整幅圖像的直方圖就把它設(shè)為 None。但是如果你想統(tǒng)計圖像某一部分的直方圖的話,你就需要制作一個掩模圖像,并使用它。(后邊有例子)
histSize:BIN 的數(shù)目。也應(yīng)該用中括號括起來,例如:[256]。
ranges: 像素值范圍,通常為 [0,256]
例子:以灰度格式加載一幅圖像并統(tǒng)計圖像的直方圖
import cv2
import matplotlib.pyplot as plt
import numpy as np
def img_show(name,img):
"""matplotlib圖像顯示函數(shù)
name:字符串,圖像標(biāo)題
img:numpy.ndarray,圖像
"""
if len(img.shape) == 3:
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
plt.imshow(img,'gray')
#plt.xticks([])
#plt.yticks([])
plt.xlabel(name,fontproperties='FangSong',fontsize=12)
if __name__=="__main__":
img1 = cv2.imread("cute-dog.jpg",0)
#別忘了中括號[img1],[0],None,[256],[0,256],只有mask沒有中括號
hist = cv2.calcHist([img1],[0],None,[256],[0,256])
plt.figure(figsize=(12,8),dpi=80)
plt.subplot(121)
img_show('原圖',img1)
plt.subplot(122)
plt.plot(hist)
plt.xlim([0,256])
plt.ylim([0,800000])
plt.xlabel('直方圖',fontproperties='FangSong',fontsize=12)

使用 Matplotlib Matplotlib 中有直方圖繪制函數(shù):matplotlib.pyplot.hist()它可以直接統(tǒng)計并繪制直方圖。
img1 = cv2.imread("cute-dog.jpg",0)
plt.figure(figsize=(12,8),dpi=80)
plt.subplot(121)
img_show('原圖',img1)
plt.subplot(122)
plt.hist(img1.ravel(),256,[0,256])
plt.xlim([0,256])
plt.xlabel('直方圖',fontproperties='FangSong',fontsize=12)

例子:繪制BGR通道直方圖
img1 = cv2.imread("cute-dog.jpg")
color = ('b','g','r')
# 對一個列表或數(shù)組既要遍歷索引又要遍歷元素時
# 使用內(nèi)置enumerrate函數(shù)會有更加直接,優(yōu)美的做法
# enumerate會將數(shù)組或列表組成一個索引序列。
# 使我們再獲取索引和索引內(nèi)容的時候更加方便
for i,col in enumerate(color):
print(i)
print(col)
hist = cv2.calcHist([img1],[i],None,[256],[0,256])
plt.plot(hist,color=col)
plt.xlim([0,256])
plt.ylim([0,800000])
plt.show()

img1 = cv2.imread("cute-dog.jpg")
color = ('b','g','r')
for i,col in enumerate(color):
plt.hist(img1[:,:,i].ravel(),256,[0,256],color=col,alpha=1)
plt.xlim([0,256])
plt.ylim([0,800000])
plt.show()

使用掩模
要統(tǒng)計圖像某個局部區(qū)域的直方圖只需要構(gòu)建一副掩模圖像。將要統(tǒng)計的部分設(shè)置成白色,其余部分為黑色,就構(gòu)成了一副掩模圖像。然后把這個掩模圖像傳給函數(shù)就可以了。
例子:統(tǒng)計圖像某個局部區(qū)域的直方圖
img1 = cv2.imread("cute-dog.jpg")
img = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
# 創(chuàng)建掩膜
mask = np.zeros(img.shape[:2],np.uint8)
mask[1000:4000,100:3200] = 255
img_mask = cv2.bitwise_and(img,img,mask =mask)
# 創(chuàng)建一個掩膜和一個無掩膜的直方圖
hist_full = cv2.calcHist([img],[0],None,[256],[0,256])
hist_mask = cv2.calcHist([img_mask],[0],mask,[256],[0,256])
plt.figure(figsize=(10,8),dpi=80)
plt.subplot(221)
plt.imshow(img,'gray')
plt.subplot(222)
plt.imshow(mask,'gray')
plt.subplot(223)
plt.imshow(img_mask,'gray')
plt.subplot(224)
plt.plot(hist_full,label='original')
plt.plot(hist_mask,label='img_mask')
plt.legend()
plt.xlim([0,256])
plt.ylim([0,800000])

例子
img1 = cv2.imread("empire.jpg")
chans = cv2.split(img1)
colors = ('b','g','r')
plt.figure(figsize=(15,5),dpi=150)
plt.subplot(121)
img_show('原圖',img1)
plt.subplot(122)
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.xlabel('彩色直方圖',fontproperties='FangSong',fontsize=12)

img1 = cv2.imread("empire.jpg")
chans = cv2.split(img1)
colors = ('b','g','r')
plt.figure(figsize=(18,5),dpi=80)
plt.subplot(131)
hist =cv2.calcHist([chans[1],chans[0]],[0,1],None,[32,32],[0,256,0,256])
plt.title('G和B的二維彩色直方圖',fontproperties='FangSong',fontsize=12)
plt.colorbar(plt.imshow(hist,interpolation= 'nearest'))
plt.subplot(132)
hist =cv2.calcHist([chans[1],chans[2]],[0,1],None,[32,32],[0,256,0,256])
plt.title('G和R的二維彩色直方圖',fontproperties='FangSong',fontsize=12)
plt.colorbar(plt.imshow(hist,interpolation= 'nearest'))
plt.subplot(133)
hist =cv2.calcHist([chans[0],chans[2]],[0,1],None,[32,32],[0,256,0,256])
plt.title('B和R的二維彩色直方圖',fontproperties='FangSong',fontsize=12)
plt.colorbar(plt.imshow(hist,interpolation= 'nearest'))
print('二維直方圖形狀:%s,和%s'%(hist.shape,hist.flatten().shape[0]))

第一個是綠色和藍(lán)色通道的二維顏色直方圖,第二個是綠色和紅色,第三個是藍(lán)色和紅色。
藍(lán)色陰影表示低像素計數(shù),而黃色陰影表示高像素計數(shù)(即2D直方圖中的峰值)。
參考資料:
網(wǎng)址:Matplotlib基礎(chǔ)教程
書籍:《數(shù)字圖像處理》《OpenCV-Python-Toturial-中文版》