天天看點

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

本文分享自華為雲社群《[Python從零到壹] 五十.圖像增強及運算篇之圖像直方圖理論知識和繪制實作-雲社群-華為雲》,作者:eastmount。

一.圖像直方圖理論知識

灰階直方圖是灰階級的函數,描述的是圖像中每種灰階級像素的個數,反映圖像中每種灰階出現的頻率。假設存在一幅6×6像素的圖像,接着統計其1至6灰階級的出現頻率,并繪制如圖1所示的柱狀圖,其中橫坐标表示灰階級,縱坐标表示灰階級出現的頻率[1-2]。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

如果灰階級為0-255(最小值0為黑色,最大值255為白色),同樣可以繪制對應的直方圖,如圖2所示,左邊是一幅灰階圖像(Lena灰階圖),右邊是對應各像素點的灰階級頻率。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

為了讓圖像各灰階級的出現頻數形成固定标準的形式,可以通過歸一化方法對圖像直方圖進行處理,将待處理的原始圖像轉換成相應的标準形式[3]。假設變量r表示圖像中像素灰階級,歸一化處理後會将r限定在下述範圍:

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

在灰階級中,r為0時表示黑色,r為1時表示白色。對于一幅給定圖像,每個像素值位于[0,1]區間之内,接着計算原始圖像的灰階分布,用機率密度函數P®實作。為了更好地進行數字圖像處理,必須引入離散形式。在離散形式下,用rk表示離散灰階級,P(rk)代替P®,并滿足公式(2)。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

公式中,nk為圖像中出現rk這種灰階的像素數,n是圖像中像素總數,是機率論中的頻數,l是灰階級總數(通常l為256級灰階)。接着在直角坐标系中做出rk和P(rk)的關系圖,則成為灰階級的直方圖[4]。

假設存在一幅3×3像素的圖像,其像素值如公式(3)所示,則歸一化直方圖的步驟如下:

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

首先統計各灰階級對應的像素個數。用x數組統計像素點的灰階級,y數組統計具有該灰階級的像素個數。其中,灰階為1的像素共3個,灰階為2的像素共1個,灰階為3的像素共2個,灰階為4的像素共1個,灰階為5的像素共2個。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

接着統計總像素個數,如公式(5)所示。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

最後統計各灰階級的出現機率,通過公式(6)進行計算,其結果如下:

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

繪制的歸一化圖行如圖3所示,橫坐标表示圖像中各個像素點的灰階級,縱坐标表示出現這個灰階級的機率。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

直方圖被廣泛應用于計算機視覺領域,在使用邊緣和顔色确定物體邊界時,通過直方圖能更好地選擇邊界門檻值,進行門檻值化處理。同時,直方圖對物體與背景有較強對比的景物的分割特别有用,可以應用于檢測視訊中場景的變換及圖像中的興趣點。

二.OpenCV繪制直方圖

首先講解使用OpenCV庫繪制直方圖的方法。在OpenCV中可以使用calcHist()函數計算直方圖,計算完成之後采用OpenCV中的繪圖函數,如繪制矩形的rectangle()函數,繪制線段的line()函數來完成。其中,cv2.calcHist()的函數原型及常見六個參數如下:

hist = cv2.calcHist(images, channels, mask, histSize, ranges, accumulate)

  • hist表示直方圖,傳回一個二維數組
  • images表示輸入的原始圖像
  • channels表示指定通道,通道編号需要使用中括号,輸入圖像是灰階圖像時,它的值為[0],彩色圖像則為[0]、[1]、[2],分别表示藍色(B)、綠色(G)、紅色(R)
  • mask表示可選的操作掩碼。如果要統計整幅圖像的直方圖,則該值為None;如果要統計圖像的某一部分直方圖時,需要掩碼來計算
  • histSize表示灰階級的個數,需要使用中括号,比如[256]
  • ranges表示像素值範圍,比如[0, 255]
  • accumulate表示累計疊加辨別,預設為false,如果被設定為true,則直方圖在開始配置設定時不會被清零,該參數允許從多個對象中計算單個直方圖,或者用于實時更新直方圖;多個直方圖的累積結果用于對一組圖像的直方圖計算

接下來的代碼是計算圖像各灰階級的大小、形狀及頻數,接着調用plot()函數繪制直方圖曲線。

# -*- coding: utf-8 -*-
# By:Eastmount
import cv2  
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

#讀取圖像
src = cv2.imread('lena-hd.png')

#計算256灰階級的圖像直方圖
hist = cv2.calcHist([src], [0], None, [256], [0,255])

#輸出直方圖大小、形狀、數量
print(hist.size)
print(hist.shape)
print(hist)

#設定字型
matplotlib.rcParams['font.sans-serif']=['SimHei']

#顯示原始圖像和繪制的直方圖
plt.subplot(121)
plt.imshow(src, 'gray')
plt.axis('off')
plt.title("(a)Lena灰階圖像")

plt.subplot(122)
plt.plot(hist, color='r')
plt.xlabel("x")
plt.ylabel("y")
plt.title("(b)直方圖曲線")
plt.show()
           

上述代碼繪制的“Lena”灰階圖像所對應的直方圖曲線如圖4所示,圖4(a)表示原圖像,圖4(b)表示對應的灰階直方圖曲線。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

同時輸出直方圖的大小、形狀及數量,如下所示:

256
(256L, 1L)
[[7.000e+00]
 [1.000e+00]
 [0.000e+00]
 [6.000e+00]
 [2.000e+00]
 ....
 [1.000e+00]
 [3.000e+00]
 [2.000e+00]
 [1.000e+00]
 [0.000e+00]]
           

彩色圖像調用OpenCV繪制直方圖的算法與灰階圖像一樣,隻是從B、G、R三個放量分别進行計算及繪制,具體代碼如下所示。

# -*- coding: utf-8 -*-
# By:Eastmount
import cv2  
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

#讀取圖像
src = cv2.imread('lena.png')

#轉換為RGB圖像
img_rgb = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)

#計算直方圖
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])

#設定字型
matplotlib.rcParams['font.sans-serif']=['SimHei']

#顯示原始圖像和繪制的直方圖
plt.subplot(121)
plt.imshow(img_rgb, 'gray')
plt.axis('off')
plt.title("(a)Lena原始圖像")
plt.subplot(122)
plt.plot(histb, color='b')
plt.plot(histg, color='g')
plt.plot(histr, color='r')
plt.xlabel("x")
plt.ylabel("y")
plt.title("(b)直方圖曲線")
plt.show()
           

最終繪制的“Lena”彩色圖像及其對應的彩色直方圖曲線如圖5所示,其中圖5(a)表示Lena原始圖像,圖5(b)表示對應的彩色直方圖曲線。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

三.Matplotlib繪制直方圖

Matplotlib是Python強大的資料可視化工具,主要用于繪制各種2D圖形。本小節Python繪制直方圖主要調用matplotlib.pyplot庫中hist()函數實作,它會根據資料源和像素級繪制直方圖。其函數主要包括五個常用的參數,如下所示:

n, bins, patches = plt.hist(arr, bins=50, normed=1, facecolor=‘green’, alpha=0.75)

  • arr表示需要計算直方圖的一維數組
  • bins表示直方圖顯示的柱數,可選項,預設值為10
  • normed表示是否将得到的直方圖進行向量歸一化處理,預設值為0
  • facecolor表示直方圖顔色
  • alpha表示透明度
  • n為傳回值,表示直方圖向量
  • bins為傳回值,表示各個bin的區間範圍
  • patches為傳回值,表示傳回每個bin裡面包含的資料,是一個清單

圖像直方圖的Python實作代碼如下所示,該示例主要是通過matplotlib.pyplot庫中的hist()函數繪制的。注意,讀取的“lena-hd.png”圖像的像素為二維數組,而hist()函數的資料源必須是一維數組,通常需要通過函數ravel()拉直圖像。

# -*- coding: utf-8 -*-
# By:Eastmount
import cv2  
import numpy as np
import matplotlib.pyplot as plt

#讀取圖像
src = cv2.imread('lena-hd.png')

#繪制直方圖
plt.hist(src.ravel(), 256)
plt.xlabel("x")
plt.ylabel("y")
plt.show()

#顯示原始圖像
cv2.imshow("src", src)
cv2.waitKey(0)
cv2.destroyAllWindows()
           

讀取顯示的“lena”灰階圖像如圖6所示。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

最終的灰階直方圖如圖7所示,它将Lena圖256級灰階和各個灰階級的頻數繪制出來,其中x軸表示圖像的256級灰階,y軸表示各個灰階級的頻數。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

如果調用下列函數,則繪制的直方圖是經過标準化處理,并且顔色為綠色、透明度為0.75的直方圖,如圖8所示。

  • plt.hist(src.ravel(), bins=256, density=1, facecolor=‘green’, alpha=0.75)
Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

彩色直方圖是高維直方圖的特例,它統計彩色圖檔RGB各分量出現的頻率,即彩色機率分布資訊。彩色圖檔的直方圖和灰階直方圖一樣,隻是分别畫出三個通道的直方圖,然後再進行疊加,其代碼如下所示。Lena彩色原始圖像如圖9所示。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作
# -*- coding: utf-8 -*-
# By:Eastmount
import cv2  
import numpy as np
import matplotlib.pyplot as plt

#讀取圖像
src = cv2.imread('Lena.png')

#擷取BGR三個通道的像素值
b, g, r = cv2.split(src)

#繪制直方圖
plt.figure("Lena")
#藍色分量
plt.hist(b.ravel(), bins=256, density=1, facecolor='b', edgecolor='b', alpha=0.75)
#綠色分量
plt.hist(g.ravel(), bins=256, density=1, facecolor='g', edgecolor='g', alpha=0.75)
#紅色分量
plt.hist(r.ravel(), bins=256, density=1, facecolor='r', edgecolor='r', alpha=0.75)
plt.xlabel("x")
plt.ylabel("y")
plt.show()

#顯示原始圖像
cv2.imshow("src", src)
cv2.waitKey(0)
cv2.destroyAllWindows()
           

繪制的彩色直方圖如圖10所示,包括紅色、綠色、藍色三種對比。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

如果希望将三個顔色分量的柱狀圖分開繪制并進行對比,則使用下面的代碼實作,調用plt.figure(figsize=(8, 6))函數繪制視窗,以及plt.subplot()函數分别繪制4個子圖。

# -*- coding: utf-8 -*-
# By:Eastmount
import cv2  
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

#讀取圖像
src = cv2.imread('lena.png')

#轉換為RGB圖像
img_rgb = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)

#擷取BGR三個通道的像素值
b, g, r = cv2.split(src)
print(r,g,b)

plt.figure(figsize=(8, 6))

#設定字型
matplotlib.rcParams['font.sans-serif']=['SimHei']

#原始圖像
plt.subplot(221)
plt.imshow(img_rgb)
plt.axis('off')
plt.title("(a)原圖像")

#繪制藍色分量直方圖
plt.subplot(222)
plt.hist(b.ravel(), bins=256, density=1, facecolor='b', edgecolor='b', alpha=0.75)
plt.xlabel("x")
plt.ylabel("y")
plt.title("(b)藍色分量直方圖")

#繪制綠色分量直方圖
plt.subplot(223)
plt.hist(g.ravel(), bins=256, density=1, facecolor='g', edgecolor='g', alpha=0.75)
plt.xlabel("x")
plt.ylabel("y")
plt.title("(c)綠色分量直方圖")

#繪制紅色分量直方圖
plt.subplot(224)
plt.hist(r.ravel(), bins=256, density=1, facecolor='r', edgecolor='r', alpha=0.75)
plt.xlabel("x")
plt.ylabel("y")
plt.title("(d)紅色分量直方圖")
plt.show()
           

最終輸出的圖形如圖11所示,,圖11(a)表示原圖像,圖11(b)表示藍色分量直方圖,圖11©表示綠色分量直方圖,圖11(d)表示紅色分類直方圖。

Python從0到1丨帶你了解圖像直方圖理論知識和繪制實作

四.總結

本文主要講解圖像直方圖理論知識以及直方圖繪制方法,并且包括Matplotlib和OpenCV兩種統計及繪制方法。灰階直方圖是灰階級的函數,描述的是圖像中每種灰階級像素的個數,反映圖像中每種灰階出現的頻率。這篇文章的知識點将為後續圖像處理和圖像運算對比提供支撐。

參考文獻:

  • [1] 岡薩雷斯. 數字圖像處理(第3版)[M]. 北京:電子工業出版社, 2013.
  • [2] 張恒博, 歐宗瑛. 一種基于色彩和灰階直方圖的圖像檢索方法[J]. 計算機工程, 2004.
  • [3] Eastmount. [數字圖像處理] 四.MFC對話框繪制灰階直方圖[EB/OL]. (2015-05-31). https://blog.csdn.net/eastmount/article/details/46237463.
  • [4] 阮秋琦. 數字圖像處理學(第3版)[M]. 北京:電子工業出版社, 2008.
  • [5] Eastmount. [Python圖像處理] 十一.灰階直方圖概念及OpenCV繪制直方圖[EB/OL]. (2018-11-06). https://blog.csdn.net/Eastmount/article/details/83758402.

關注#華為雲開發者聯盟# 點選下方,第一時間了解華為雲新鮮技術~

華為雲部落格_大資料部落格_AI部落格_雲計算部落格_開發者中心-華為雲