這裡介紹如何在 Python 中以 OpenCV 與 matplotlib 等工具,統計影像像素值的分佈,並畫出直方圖。
在開發影像處理的程式時,我們時常會需要觀察影像像素值的分佈與特性,以便選用適合的演算法、制定門檻值、設計出適合的影像處理流程。
在 Python 中若要觀察影像亮度分佈,可以使用 OpenCV 與 matplotlib 等工具,以下是使用教學。
像素值分佈直方圖
OpenCV 的 calcHist
函數可用來計算直方圖的數值,其語法如下:
cv2.calcHist(影像, 通道, 遮罩, 區間數量, 數值範圍)
以下是各個參數的意義與使用說明:
- 影像:影像的來源,其型別可以是
uint8
或float32
,變數必須放在中括號當中,例如:[img]
。 - 通道:指定影像的通道(channel),同樣必須放在中括號當中。若為灰階影像,則通道就要指定為
[0]
,若為彩色影像則可用[0]
、[1]
或[2]
指定 藍色、綠色或紅色的通道。 - 遮罩:以遮罩指定要納入計算的圖形區域,若指定為
None
則會計算整張圖形的所有像素。 - 區間數量:指定直方圖分隔區間的數量(bins),也就是圖形畫出來要有幾條長方形。
- 數值範圍:指定要計算的像素值範圍,通常都是設為
[0,256]
(計算所有的像素值)。
以下是一個簡單的範例:
import cv2 import numpy as np import matplotlib.pyplot as plt # 讀取圖檔 img = cv2.imread('image.jpg') # 轉為灰階圖片 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 計算直方圖每個 bin 的數值 hist = cv2.calcHist([gray], [0], None, [256], [0, 256]) # 畫出直方圖 plt.bar(range(1,257), hist) plt.show()
我拿這張照片作為測試的範例:
執行的結果會像這樣:
通常像這種每個區間只包含一個數值的直方圖,我們可以改用普通的線條來表示:
# 畫出分佈圖
plt.plot(hist)
畫出來的結果會像這樣:
matplotlib 的 pyplot.hist
函數也可以用來統計並繪製直方圖,只不過圖形在傳入時,要先使用 ravel
將所有的像素資料轉為一維的陣列,以下是一個簡單的範例:
import cv2 import numpy as np import matplotlib.pyplot as plt # 讀取圖檔 img = cv2.imread('image.jpg') # 轉為灰階圖片 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 畫出直方圖 plt.hist(gray.ravel(), 256, [0, 256]) plt.show()
畫出來的結果會像這樣:
對於彩色的圖片,可以用 OpenCV 的 calcHist
函數分別計算統計值,並畫出 RGB 三種顏色的分佈圖:
import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread('image.jpg') # 畫出 RGB 三種顏色的分佈圖 color = ('b','g','r') for i, col in enumerate(color): histr = cv2.calcHist([img],[i],None,[256],[0, 256]) plt.plot(histr, color = col) plt.xlim([0, 256]) plt.show()
圖形遮罩
若要計算圖形中部份區域的像素值分佈,可以使用 OpenCV 的 calcHist
配合圖形遮罩,以下的範例我們建立一個簡單的方框遮罩,將圖片中央的區塊取出來,計算像素值分佈:
import cv2 import numpy as np import matplotlib.pyplot as plt # 讀取圖檔 img = cv2.imread('image.jpg') # 轉為灰階圖片 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 建立圖形遮罩 mask = np.zeros(gray.shape, np.uint8) mask[300:780, 300:1620] = 255 # 計算套用遮罩後的圖形 masked_gray = cv2.bitwise_and(gray, gray, mask = mask) # 以原圖計算直方圖 hist_full = cv2.calcHist([img], [0], None, [256], [0, 256]) # 以套用遮罩後的圖計算直方圖 hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256]) # 繪製結果 plt.subplot(221), plt.imshow(gray, 'gray') plt.subplot(222), plt.imshow(mask, 'gray') plt.subplot(223), plt.imshow(masked_gray, 'gray') plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask) plt.xlim([0,256]) plt.show()
遮罩其實就是一張大小跟原圖一樣的圖片,其中白色的部份代表選取區域,而黑色的部份則代表排除區域。此程式執行的結果如下:
參考資料:OpenCV
路人乙
可以請問版主怎麼把圖片和histogram結合在一起呢
G. T. Wang
其實我是偷懶,直接用 GIMP 做的。 😀