摘要:本篇文章主要講解灰度直方圖的基本概念,Python調用OpenCV實現繪制圖像直方圖。
本文分享自華為雲社區《[Python圖像處理] 十一.灰度直方圖概念及OpenCV繪制直方圖》,作者:eastmount。
灰度直方圖(histogram)是灰度級的函數,描述的是圖像中每種灰度級像素的個數,反映圖像中每種灰度出現的頻率。橫坐標是灰度級,縱坐標是灰度級出現的頻率。
對於連續圖像,平滑地從中心的高灰度級變化到邊緣的低灰度級。直方圖定義為:
其中A(D)為阈值面積函數:為一幅連續圖像中被具有灰度級D的所有輪廓線所包圍的面積。對於離散函數,固定ΔD為1,則:H(D)=A(D)-A(D+1)。
色彩直方圖是高維直方圖的特例,它統計色彩的出現頻率,即色彩概率分布信息。通常這需要一定的量化過程,將色彩分成若干互不重疊的種類。一般不直接在RGB色彩空間中統計,而是在將亮度分離出來後,對代表色彩部分的信息進行統計,如在HSI空間的HS子空間、YUV空間的UV子空間,以及其它反映人類視覺特點的彩色空間表示中進行。
直方圖的計算方法如下:
依據定義,若圖像具有L(通常L=256,即8位灰度級)級灰度,則大小為MxN的灰度圖像f(x,y)的灰度直方圖hist[0…L-1]可用如下計算獲得。
1、初始化 hist[k]=0; k=0,…,L-1
2、統計 hist[f(x,y)]++; x=0,…,M-1, y =0,…,N-1
3、歸一化 hist[f(x,y)]/=M*N
那麼說了這麼多,直方圖究竟有什麼作用呢?
在使用輪廓線確定物體邊界時,通過直方圖更好的選擇邊界阈值,進行阈值化處理;對物體與背景有較強對比的景物的分割特別有用;簡單物體的面積和綜合光密度IOD可以通過圖像的直方圖求得。
在直方圖中,橫坐標表示圖像中各個像素點的灰度級,縱坐標表示具有該灰度級的像素個數。
假設存在一個3*3的圖像,如下圖所示,x數組統計的是像素點的灰度級,y數組統計的是具有該灰度級的像素個數。其中,灰度為1的像素共3個,灰度為2的像素共1個,灰度為3的像素共2個,灰度為4的像素共1個,灰度為5的像素共2個。
x = [1, 2, 3, 4, 5]
y = [3, 1, 2, 1, 2]
繪制的折線圖如下所示:
繪制的直方圖如下所示:
如果灰度級為0-255(最小值0黑色,最大值255白色),同樣可以繪制對應的直方圖,下圖是三張圖片拼接而成及其對應的直方圖。
該直方圖的橫坐標表示圖像中各個像素點的灰度級,縱坐標表示出現這個灰度級的概率。其計算方法如下:
(1) 先計算灰度級及對應像素的個數
x = [1, 2, 3, 4, 5]
t = [3, 1, 2, 1, 2]
(2) 統計總的像素個數
n = (3 + 1 + 2 + 1 +2) = 9
(3) 統計各個灰度級的出現概率
y = t / n = [3/9, 1/9, 2/9, 1/9, 2/9]
主要調用matplotlib的子庫pyplot實現,它提供了類似於Matlab的繪圖框架,matplotlib是非常強大基礎的一個Python繪圖包。Provides a Matlab-like plotting framework. 導入代碼如下:
import matplotlib.pyplot as plt
其中繪制直方圖主要調用hist函數實現,它根據數據源和像素級繪制直方圖。函數原型如下:
hist(數據源, 像素級)
參數:
數據源必須是一維數組,通常需要通過函數ravel()拉直圖像
像素級一般是256,表示[0, 255]
函數ravel()將多維數組降為一維數組,格式為:
一維數組 = 多維數組.ravel()
#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
src = cv2.imread('test01.jpg')
cv2.imshow("src", src)
cv2.waitKey(0)
cv2.destroyAllWindows()
plt.hist(src.ravel(), 256)
plt.show()
輸出結果如下所示:
前面講解調用matplotlib庫繪制直方圖,接下來講解使用OpenCV統計繪制直方圖的例子。
直方圖橫坐標:圖像中各個像素點的灰度級
直方圖縱坐標:具有該灰度級的像素個數
主要調用函數calcHist()實現:
hist = cv2.calcHist(images, channels, mask, histSize, ranges, accumulate)
參數:
首先計算圖像灰度級的基本大小、形狀及內容。
#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
src = cv2.imread('test01.jpg')
#參數:原圖像 通道[0]-B 掩碼 BINS為256 像素范圍0-255
hist = cv2.calcHist([src], [0], None, [256], [0,255])
print(type(hist))
print(hist.size)
print(hist.shape)
print(hist)
輸出結果如下所示:
下面是繪制圖像的代碼,首先補充一些matplotlib庫繪制圖像代碼,也推薦我的文章。[Python數據挖掘課程] 六.Numpy、Pandas和Matplotlib包基礎知識
#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
#繪制sin函數曲線
x1 = np.arange(0, 6, 0.1)
y1 = np.sin(x1)
plt.plot(x1, y1)
#繪制坐標點折現
x2 = [0, 1, 2, 3, 4, 5, 6]
y2 = [0.3, 0.4, 2.5, 3.4, 4, 5.8, 7.2]
plt.plot(x2, y2)
#省略有規則遞增的x2參數
y3 = [0, 0.5, 1.5, 2.4, 4.6, 8]
plt.plot(y3, color="r")
plt.show()
輸出結果有三條線,如下所示:
最後給出調用calcHist()計算B、G、R灰度級並繪制圖形的代碼。
#encoding:utf-8
import cv2
import numpy as np
import matplotlib.pyplot as plt
src = cv2.imread('test01.jpg')
histb = cv2.calcHist([src], [0], None, [256], [0,255])
histg = cv2.calcHist([src], [1], None, [256], [0,255])
histr = cv2.calcHist([src], [2], None, [256], [0,255])
cv2.imshow("src", src)
cv2.waitKey(0)
cv2.destroyAllWindows()
plt.plot(histb, color='b')
plt.plot(histg, color='g')
plt.plot(histr, color='r')
plt.show()
輸出結果如下圖所示:
點擊關注,第一時間了解華為雲新鮮技術~