啥叫直方圖
直方圖簡單來說就是圖像中每個像素值的個數統計,比如一副灰度圖中像素值為0的有多少個,1的有多少個...
在計算直方圖之前,先了解幾個術語:
- dims:要計算的通道數,對於灰度圖dims=1,普通彩色圖dims=3
- range:要計算的像素值范圍,一般為[0,255]
- bins:子區段數目,如果我們統計0~255每個像素值,bins=256;如果划分區間,比如0~15,16~31...240~255這樣16個區間,bins=16
計算直方圖
OpenCV和Numpy中提供了計算直方圖的函數,我們對比一下他們的性能
OpenCV中直方圖計算
在OpenCV中使用 cv2.calcHist(images,channels,mask,histSize,ranges) 計算,其中:
- 參數1:要計算的原圖,以方括號的傳入,如:[img]
- 參數2:類似前面提到的dims,灰度圖寫[0]就行,彩色圖B、G、R分別傳入[0]、[1]、[2]
- 參數3:要計算的區域,計算整幅圖的話,寫None
- 參數4:前面提到的bins
- 參數5:前面提到的range
import cv2 import numpy as np import matplotlib.pyplot as plt
start = cv2.getTickCount() img = cv2.imread('hist.jpg', 0) hist = cv2.calcHist([img], [0], None, [256], [0, 256]) end = cv2.getTickCount() print((end - start) / cv2.getTickFrequency())
這里我使用到了評估代碼運行時間,且並沒有給出最初最后print()函數輸出的結果,其實這個結果一直在變化。這里簡單的介紹一下獲取代碼運行時間的兩種方式
第一種:
import cv2 start = cv2.getTickCount() # 這里寫測試代碼... end = cv2.getTickCount() print((end - start) / cv2.getTickFrequency())
這段代碼就是用來測量程序運行時間(單位:s),其中cv2.getTickCount()函數得到電腦啟動以來的時鍾周期數,cv2.getTickFrequency()返回你電腦的主頻,前后相減再除以主頻就是你代碼的運行時間。我這里就是使用的第一種
第二種:使用Python中的time模塊計時:
import time start = time.clock() # 這里寫測試代碼... end = time.clock() print(end - start)
Numpy中直方圖計算
也可用Numpy的函數計算,使用 ravel() 函數 將二維矩陣展平變成一維數組,
hist, bins = np.histogram(img.ravel(), 256, [0, 256])
還有一種更高效的的方式:
hist = np.bincount(img.ravel(), minlength=256)
繪制直方圖
其實Matplotlib自帶了一個計算並繪制直方圖的功能,不需要用到上面的函數
plt.hist(img.ravel(), 256, [0, 256])
plt.show()
從直方圖上可以看出圖片的像素點集中子150附近,圖片偏灰白,效果不好。接下來我們來看看如何來改善它。
當然,我們也可以用前面計算出來的結果繪制:
plt.plot(hist)
plt.show()
直方圖的均衡化
一幅效果好的圖像通常在直方圖上的分布比較均勻,直方圖均衡化就是用來改善圖像的全局亮度和對比度的。
OpenCV中用cv2.equalizeHist()實現均衡化,我們把兩張圖片並排顯示一下,對比一下:
equ = cv2.equalizeHist(img) cv2.imshow('equalization', np.hstack((img, equ))) # 並排顯示 cv2.waitKey(0)
均衡后的直方圖:
plt.hist(equ.ravel(), 256, [0, 256])
plt.show()
可以看出均衡后的直方圖明顯好於原圖。均衡后的圖片的亮度和對比度效果明顯好於原圖
自適應均衡化
直方圖均衡化是應用於整幅圖片的,那是不是所有圖片都適合?會不會出現什么問題?看下圖:
很明顯,因為全局調整亮度和對比度的原因,臉部太亮,大部分細節都丟失了。
自適應均衡化就是解決這一問題的:它在每一個小區域內(默認8x8)進行直方圖均衡化。當然,如果有噪點的話,噪點會被放大,需要對小區域的對比度進行了限制,所以這個算法全稱叫:對比度受限的自適應直方圖均衡化 CLAHE (Contrast Limited Adaptive Histogram Equalization)
# 自適應均衡化,參數可選 clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) cl1 = clahe.apply(img) cv2.imshow('equalization', np.hstack((equ, cl1))) # 並排顯示 cv2.waitKey(0)
效果比均衡化效果要好,至少臉部的細節保留了
使用掩膜
要統計圖像某個局部區域的直方圖只需要構建一副掩膜圖像。將要統計的部分設置為白色,其余部分為黑色,就構成了一副掩膜圖像。然后把這個掩膜圖像傳給函數就可以了。
img = cv2.imread('home.jpg', 0) # 創建一個掩膜 mask = np.zeros(img.shape[:2], np.uint8) # 取彩色通道的長寬 mask[0 : 200, 0 : 200] = 255 masked_img = cv2.bitwise_and(img, img, mask = mask) Original_Hist = cv2.calcHist([img], [0], None, [256], [0, 256]) masked_img_Hist = cv2.calcHist([img], [0], mask, [256], [0, 256]) plt.subplot(231), plt.imshow(img, 'gray') plt.subplot(232), plt.imshow(mask, 'gray') plt.subplot(233), plt.imshow(masked_img, 'gray') plt.subplot(234), plt.plot(Original_Hist) plt.subplot(235), plt.plot(masked_img_Hist) plt.subplot(236), plt.plot(Original_Hist), plt.plot(masked_img_Hist) plt.show()
其中在混合的那副直方圖中,藍色為原圖的直方圖,橙色為進行掩膜之后的直方圖。