天天看點

【python opencv】直方圖查找、繪制和分析

那麼直方圖是什麼?您可以将直方圖視為圖形或繪圖,進而可以總體了解圖像的強度分布。它是在X軸上具有像素值(不總是從0到255的範圍),在Y軸上具有圖像中相應像素數的圖。

這隻是了解圖像的另一種方式。通過檢視圖像的直方圖,您可以直覺地了解該圖像的對比度,亮度,強度分布等。當今幾乎所有圖像處理工具都提供直方圖功能。以下是劍橋彩色網站的圖檔,我建議您通路該網站以擷取更多詳細資訊。

【python opencv】直方圖查找、繪制和分析
您可以看到圖像及其直方圖。(請記住,此直方圖是針對灰階圖像而非彩色圖像繪制的)。直方圖的左側區域顯示圖像中較暗像素的數量,而右側區域則顯示明亮像素的數量。從直方圖中,您可以看到暗區域多于亮區域,而中間調的數量(中間值的像素值,例如127附近)則非常少。

尋找直方圖

現在我們有了一個關于直方圖的想法,我們可以研究如何找到它。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]])

  1. images:它是uint8或float32類型的源圖像。它應該放在方括号中,即“ [img]”。
  2. channels:也以方括号給出。它是我們計算直方圖的通道的索引。例如,如果輸入為灰階圖像,則其值為[0]。對于彩色圖像,您可以傳遞[0],[1]或[2]分别計算藍色,綠色或紅色通道的直方圖。
  3. mask:圖像掩碼。為了找到完整圖像的直方圖,将其指定為“無”。但是,如果要查找圖像特定區域的直方圖,則必須為此建立一個掩碼圖像并将其作為掩碼。(我将在後面顯示一個示例。)
  4. histSize:這表示我們的BIN計數。需要放在方括号中。對于全尺寸,我們通過[256]。
  5. 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()      
【python opencv】直方圖查找、繪制和分析

您可以從上圖中得出,藍色在圖像中具有一些高值域(顯然這應該是由于天空)

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()      

檢視結果。在直方圖中,藍線表示完整圖像的直方圖,綠線表示掩碼區域的直方圖。