那麼直方圖是什麼?您可以将直方圖視為圖形或繪圖,進而可以總體了解圖像的強度分布。它是在X軸上具有像素值(不總是從0到255的範圍),在Y軸上具有圖像中相應像素數的圖。
這隻是了解圖像的另一種方式。通過檢視圖像的直方圖,您可以直覺地了解該圖像的對比度,亮度,強度分布等。當今幾乎所有圖像處理工具都提供直方圖功能。以下是劍橋彩色網站的圖檔,我建議您通路該網站以擷取更多詳細資訊。

尋找直方圖
現在我們有了一個關于直方圖的想法,我們可以研究如何找到它。OpenCV和Numpy都為此内置了功能。在使用這些功能之前,我們需要了解一些與直方圖有關的術語。
BINS:上面的直方圖顯示每個像素值的像素數,即從0到255。即,您需要256個值來顯示上面的直方圖。但是考慮一下,如果您不需要分别找到所有像素值的像素數,而是找到像素值間隔中的像素數怎麼辦? 例如,您需要找到介于0到15之間的像素數,然後找到16到31之間,...,240到255之間的像素數。隻需要16個值即可表示直方圖。這就是在OpenCV教程中有關直方圖的示例中顯示的内容。
是以,您要做的就是将整個直方圖分成16個子部分,每個子部分的值就是其中所有像素數的總和。 每個子部分都稱為“ BIN”。在第一種情況下,bin的數量為256個(每個像素一個),而在第二種情況下,bin的數量僅為16個。BINS由OpenCV文檔中的histSize術語表示。
DIMS:這是我們為其收集資料的參數的數量。在這種情況下,我們僅收集關于強度值的一件事的資料。是以這裡是1。
RANGE:這是您要測量的強度值的範圍。通常,它是
[0,256]
,即所有強度值。
1. OpenCV中的直方圖計算
是以,現在我們使用cv.calcHist()函數查找直方圖。讓我們熟悉一下該函數及其參數:
cv.calcHist(images,channels,mask,histSize,ranges [,hist [,accumulate]])
- images:它是uint8或float32類型的源圖像。它應該放在方括号中,即“ [img]”。
- channels:也以方括号給出。它是我們計算直方圖的通道的索引。例如,如果輸入為灰階圖像,則其值為[0]。對于彩色圖像,您可以傳遞[0],[1]或[2]分别計算藍色,綠色或紅色通道的直方圖。
- mask:圖像掩碼。為了找到完整圖像的直方圖,将其指定為“無”。但是,如果要查找圖像特定區域的直方圖,則必須為此建立一個掩碼圖像并将其作為掩碼。(我将在後面顯示一個示例。)
- histSize:這表示我們的BIN計數。需要放在方括号中。對于全尺寸,我們通過[256]。
- ranges:這是我們的RANGE。通常為[0,256]。
是以,讓我們從示例圖像開始。隻需以灰階模式加載圖像并找到其完整直方圖即可。
img = cv.imread('home.jpg',0)
hist = cv.calcHist([img],[0],None,[256],[0,256])
hist是256x1的數組,每個值對應于該圖像中具有相應像素值的像素數。
2. numpy的直方圖計算
Numpy還為您提供了一個函數np.histogram()。是以,除了calcHist()函數外,您可以嘗試下面的代碼:
hist,bins = np.histogram(img.ravel(),256,[0,256])
hist與我們之前計算的相同。但是bin将具有257個元素,因為Numpy計算出bin的範圍為
0-0.99
、
1-1.99
2-2.99
等。是以最終範圍為
255-255.99
。為了表示這一點,他們還在最後添加了256。但是我們不需要256。最多255就足夠了。
- 另外 Numpy還有另一個函數np.bincount(),它比np.histogram()快10倍左右。是以,對于一維直方圖,您可以更好地嘗試一下。不要忘記在np.bincount中設定minlength = 256。例如,
hist = np.bincount(img.ravel(),minlength = 256)
注意 OpenCV函數比np.histogram()快大約40倍。是以,盡可能使用OpenCV函數。
現在我們應該繪制直方圖,但是怎麼繪制?
繪制直方圖
有兩種方法, 1. 簡短的方法:使用Matplotlib繪圖功能 2. 稍長的方法:使用OpenCV繪圖功能
1. 使用Matplotlib
Matplotlib帶有直方圖繪圖功能:
matplotlib.pyplot.hist()
它直接找到直方圖并将其繪制。您無需使用calcHist()或np.histogram()函數來查找直方圖。請參見下面的代碼:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg',0)
plt.hist(img.ravel(),256,[0,256]); plt.show()
結果:
或者,您可以使用matplotlib的法線圖,這對于BGR圖是很好的。為此,您需要首先找到直方圖資料。試試下面的代碼:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('home.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.show()
您可以從上圖中得出,藍色在圖像中具有一些高值域(顯然這應該是由于天空)
2. 使用 OpenCV
好吧,在這裡您可以調整直方圖的值及其bin值,使其看起來像x,y坐标,以便您可以使用cv.line()或cv.polyline()函數繪制它以生成與上述相同的圖像。OpenCV-Python2官方示例已經提供了此功能。檢查示例/python/hist.py中的代碼。
掩碼的應用
我們使用了
cv.calcHist()
來查找整個圖像的直方圖。如果你想找到圖像某些區域的直方圖呢?隻需建立一個掩碼圖像,在你要找到直方圖為白色,否則黑色。然後把這個作為掩碼傳遞。
img = cv.imread('home.jpg',0)
# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv.bitwise_and(img,img,mask = mask)
# 計算掩碼區域和非掩碼區域的直方圖
# 檢查作為掩碼的第三個參數
hist_full = cv.calcHist([img],[0],None,[256],[0,256])
hist_mask = cv.calcHist([img],[0],mask,[256],[0,256])
plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask,'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0,256])
plt.show()
檢視結果。在直方圖中,藍線表示完整圖像的直方圖,綠線表示掩碼區域的直方圖。