OpenCV:LBP和HOG特徵算子

日萌社

人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度學習實戰(不定時更新)


4.5 LBP和HOG特徵算子

學習目標

  1. 瞭解LBP特徵的原理
  2. 瞭解LBP的改進算法:圓形LBP,旋轉LBP和等價模式
  3. 瞭解HOG算法的原理
  4. 熟悉灰度圖像的γ變換
  5. 瞭解HOG特徵的提取流程
  6. 瞭解LBP特徵的提取方法
  7. 瞭解HOG特徵的提取方法

1.LBP算法

LBP(Local Binary Pattern)指局部二值模式,是一種用來描述圖像局部特徵的算子,LBP特徵具有灰度不變性和旋轉不變性等顯著優點。它是由T. Ojala, M.Pietikäinen, 和 D. Harwood在1994年提出,由於LBP特徵計算簡單、效果較好,因此LBP特徵在計算機視覺的許多領域都得到了廣泛的應用。

1.1 LBP特徵描述

原始的LBP算子定義爲在$33$的窗口內,以窗口中心像素爲閾值,將相鄰的8個像素的灰度值與其進行比較,若周圍像素值大於中心像素值,則該像素點的位置被標記爲1,否則爲0。這樣,$33$鄰域內的8個點經比較可產生8位二進制數(通常轉換爲十進制數即LBP碼,共256種),即得到該窗口中心像素點的LBP值,並用這個值來反映該區域的紋理信息。如下圖所示:

LBP算子利用了周圍點與該點的關係對該點進行量化。量化後可以更有效地消除光照對圖像的影響。只要光照的變化不足以改變兩個點像素值之間的大小關係,那麼LBP算子的值不會發生變化,所以一定程度上,基於LBP的識別算法解決了光照變化的問題,但是當圖像光照變化不均勻時,各像素間的大小關係被破壞,對應的LBP模式也就發生了變化。

原始的LBP提出後,研究人員不斷對其提出了各種改進和優化。

1.2 圓形LBP算子

原始LBP特徵使用的是固定鄰域內的灰度值,當圖像的尺度發生變化時,LBP特徵的編碼將會發生變換,LBP特徵將不能正確的反映像素點周圍的紋理信息,因此研究人員對其進行了改進。基本的 LBP 算子的最大缺陷在於它只覆蓋了一個固定半徑範圍內的小區域,只侷限在3*3的鄰域內,對於較大圖像大尺度的結構不能很好的提取需要的紋理特徵,因此研究者們對LBP算子進行了擴展。

新的LBP算子$LBP_{p}^{R}$計算不同半徑鄰域大小和不同像素點數的特徵值,其中P表示周圍像素點個數,R表示鄰域半徑,同時把原來的方形鄰域擴展到了圓形,下圖給出了三種擴展後的LBP例子,其中,R可以是小數:

對於沒有落到整數位置的點,根據軌道內離其最近的兩個整數位置像素灰度值,利用雙線性插值的方法可以計算它的灰度值。

該算子的計算公式與原始的LBP描述算子計算方法相同,區別在鄰域的選擇上。

1.3 旋轉不變LBP特徵

從 LBP 的定義可以看出,LBP算子不是旋轉不變的。圖像的旋轉就會得到不同的 LBP值。所以 Maenpaa等人又將 LBP算子進行了擴展,提出了具有旋轉不變性的 LBP 算子,即不斷旋轉圓形鄰域得到一系列初始定義的 LBP值,取其最小值作爲該鄰域的 LBP 值。即:

上圖給出了求取旋轉不變的 LBP 的過程示意圖,算子下方的數字表示該算子對應的 LBP值,圖中所示的 8 種 LBP模式,經過旋轉不變的處理,最終得到的具有旋轉不變性的 LBP值爲 15。也就是說,圖中的 8種 LBP 模式對應的旋轉不變的 LBP模式都是 00001111。

1.4 Uniform Pattern LBP特徵

Uniform Pattern,也被稱爲等價模式或均勻模式,由於一個LBP特徵有多種不同的二進制形式,對於半徑爲R的圓形區域內含有P個採樣點的LBP算子將會產生$2^P$種模式。很顯然,隨着鄰域集內採樣點數的增加,二進制模式的種類是以指數形式增加的。例如:5×5鄰域內20個採樣點,有2^20=1,048,576種二進制模式。這麼多的二進制模式不利於紋理的提取、分類、識別及存取。例如,將LBP算子用於紋理分類或人臉識別時,常採用LBP模式的統計直方圖來表達圖像的信息,而較多的模式種類將使得數據量過大,且直方圖過於稀疏。因此,需要對原始的LBP模式進行降維,使得數據量減少的情況下能最好的表示圖像的信息。

爲了解決二進制模式過多的問題,提高統計性,Ojala提出了採用一種“等價模式”(Uniform Pattern)來對LBP算子的模式種類進行降維。Ojala等認爲,在實際圖像中,絕大多數LBP模式最多隻包含兩次從1到0或從0到1的跳變。因此,Ojala將“等價模式”定義爲:當某個LBP所對應的循環二進制數從0到1或從1到0最多有兩次跳變時,該LBP所對應的二進制就稱爲一個等價模式類。如00000000(0次跳變),00000111(只含一次從0到1的跳變),10001111(先由1跳到0,再由0跳到1,共兩次跳變)都是等價模式類。除等價模式類以外的模式都歸爲另一類,稱爲混合模式類,例如10010111(共四次跳變)。

下圖所示的LBP值屬於等價模式類:

通過這樣的改進,二進制模式的種類大大減少,而不會丟失任何信息。模式數量由原來的$2^P$種減少爲 P ( P-1)+2種,其中P表示鄰域集內的採樣點數。對於3×3鄰域內8個採樣點來說,二進制模式由原始的256種減少爲58種,即:它把值分爲59類,58個uniform pattern爲一類,其它的所有值爲第59類。這樣直方圖從原來的256維變成59維。這使得特徵向量的維數更少,並且可以減少高頻噪聲帶來的影響。

1.5 實現

在OpenCV中實現了LBP特徵的計算,但沒有提供一個單獨的計算LBP特徵的接口。所以我們使用skimage給大家演示該算法。

skimage即是Scikit-Image。基於python腳本語言開發的數字圖片處理包,scikit-image是基於scipy的一款圖像處理包,它將圖片作爲numpy數組進行處理。安裝方法:

pip install scikit-image

skimage包的全稱是scikit-image SciKit (toolkit for SciPy) ,它對scipy.ndimage進行了擴展,提供了更多的圖片處理功能。它是由python語言編寫的,由scipy 社區開發和維護。skimage包由許多的子模塊組成,各個子模塊提供不同的功能。其中feature模塊進行特徵檢測與提取。

使用的API是:

skimage.feature.local_binary_pattern(image, P, R, method=‘default’)

參數:

  • image: 輸入的灰度圖像

  • P,R: 進行LBP算子計算時的半徑和像素點數

  • method: 算法類型:{‘default’, ‘ror’, ‘nri-uniform’, ‘var’}

    default: "默認",原始的LBP特徵
    

    ror: 圓形LBP算子

    nri-uniform: 等價LBP算子

    var:旋轉不變LBP算子

示例:

我們在下圖中提取LBP特徵:

代碼如下所示:

import cv2 as cv
from skimage.feature import local_binary_pattern
import matplotlib.pyplot as plt
# 1.讀取圖像
img = cv.imread("face.jpeg")
face = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 2.特徵提取
# 2.0 需要的參數
# LBP算法中範圍半徑的取值
radius = 1  
# 領域像素點數
n_points = 8 * radius 

# 2.1 原始LBP特徵
lbp = local_binary_pattern(face, 8, 1)

# 2.2 圓形LBP特徵
clbp = local_binary_pattern(face,n_points,radius,method="ror")

# 2.3 旋轉不變LBP特徵
varlbp = local_binary_pattern(face,n_points,radius,method="var")

# 2.4 等價特徵
uniformlbp = local_binary_pattern(face,n_points,radius,method="nri-uniform")

fig,axes=plt.subplots(nrows=2,ncols=2,figsize=(10,8))
axes[0,0].imshow(lbp,'gray')
axes[0,0].set_title("原始的LBP特徵")
axes[0,0].set_xticks([])
axes[0,0].set_yticks([])
axes[0,1].imshow(clbp,'gray')
axes[0,1].set_title("圓形LBP特徵")
axes[0,1].set_xticks([])
axes[0,1].set_yticks([])
axes[1,0].imshow(varlbp,'gray')
axes[1,0].set_title("旋轉不變LBP特徵")
axes[1,0].set_xticks([])
axes[1,0].set_yticks([])
axes[1,1].imshow(uniformlbp,"gray")
axes[1,1].set_title("等價特徵")
axes[1,1].set_xticks([])
axes[1,1].set_yticks([])
plt.show()

檢測結果如下所示:

2.HOG算法

HOG(Histogram of Oriented Gridients的簡寫)特徵檢測算法,最早是由法國研究員Dalal等在CVPR-2005上提出來的,一種解決人體目標檢測的圖像描述子,是一種用於表徵圖像局部梯度方向和梯度強度分佈特性的描述符。其主要思想是:在邊緣具體位置未知的情況下,邊緣方向的分佈也可以很好的表示行人目標的外形輪廓。

2.1 特徵提取流程

HOG的主要思想是:在一副圖像中,局部目標的表象和形狀(appearance and shape)能夠被梯度或邊緣的方向密度分佈(即梯度的統計信息,而梯度主要位於邊緣的地方)很好地描述。

HOG特徵檢測算法的幾個步驟:顏色空間歸一化—>梯度計算—>梯度方向直方圖—>重疊塊直方圖歸一化—>HOG特徵。如下圖所示:

整體流程簡單描述如下:

  1. 將輸入圖像(你要檢測的目標或者掃描窗口)灰度化,即將彩色圖轉換爲灰度圖
  2. 顏色空間歸一化:採用Gamma校正法對輸入圖像進行顏色空間的標準化(歸一化),目的是調節圖像的對比度,降低圖像局部的陰影和光照變化所造成的影響,同時可以抑制噪音的干擾
  3. 梯度計算:計算圖像每個像素的梯度(包括大小和方向);主要是爲了捕獲輪廓信息,同時進一步弱化光照的干擾
  4. 梯度方向直方圖:將圖像劃分成小cells(例如8*8像素/cell), 統計每個cell的梯度直方圖(不同梯度的個數),即可形成每個cell的描述符
  5. 重疊直方圖歸一化:將每幾個cell組成一個block(例如3*3個cell/block),一個block內所有cell的特徵descriptor串聯起來便得到該block的HOG特徵描述符。
  6. HOG特徵:將圖像image內的所有block的HOG特徵描述符串聯起來就可以得到該image(你要檢測的目標)的HOG特徵描述符,就得到最終的可供分類使用的特徵向量了

下面我們詳細介紹每一步驟的內容:

2.2 顏色空間歸一化

爲了減少光照因素的影響,首先需要將整個圖像進行規範化(歸一化)。在圖像的紋理強度中,局部的表層曝光貢獻的比重較大,所以,這種壓縮處理能夠有效地降低圖像局部的陰影和光照變化。因爲顏色信息作用不大,通常先轉化爲灰度圖,然後在進行伽馬校正。

伽馬校正能夠有效的降低圖像的局部陰影和光照所帶來的的影響,從而降低算法對光照的敏感度,增強算法的魯棒性。

伽馬校正使用下式所得:

從上圖中可以看出:

  1. 當γ<1時,如上圖中虛線所示,在低灰度值區域內,動態範圍變大(當x在[0,0.2]時,y的範圍是[0,0.5]),進而圖像的對比度增強;在高灰度值區域內,動態範圍變小(當x在[0.8,1]時,y的範圍是[0.9,1]),圖像的對比度降低;同時,圖像整體的灰度值變大。
  2. 當γ>1時,如上圖中實線所示,在低灰度值區域內,動態範圍變小(當x在[0,0.5]時,y的範圍是[0,0.2]),進而圖像的對比度降低;在高灰度值區域內,動態範圍變大,圖像的對比度增強;同時,圖像整體的灰度值變小。

下圖中左圖是原圖,中圖是γ=1/2.2的校正結果,右圖是γ=2.2的校正結果。

在HOG特徵提取中,γ一般取0.5,此時,圖像的灰度值被拉伸,且灰度越低,拉伸的幅度越大,也就是說,對於光照較暗的圖像處理較好,能較大程度提升他們的亮度。

2.3 圖像梯度計算

邊緣是由圖像局部特徵包括灰度、顏色和紋理的突變導致的。一幅圖像中相鄰的像素點之間變化比較少,區域變化比較平坦,則梯度幅值就會比較小,反之,則梯度幅值就會比較大。梯度在圖像中對應的就是其一階導數,所以圖像梯度計算利用一階微分求導處理,不僅能夠捕獲輪廓、人影以及一些紋理信息,還能進一步弱化光照的影響。Dalal研究了很多算子,如下表所示:

2.4 梯度直方圖計算

Dalal的研究結果表明,梯度方向爲無符號且通道數爲9時得到最好的檢測效果,此時一個梯度方向的一個通道爲180/9=20°,代表的是角度0,20,40,60.....160。梯度方向矩陣中可以看到角度是0-180度,不是0-360度,這種被稱之爲"無符號"梯度("unsigned" gradients)因爲一個梯度和它的負數是用同一個數字表示的,也就是說一個梯度的方向以及它旋轉180度之後的方向被認爲是一樣的,如下圖所示:

我們將上一步得到的梯度方向投影到9個通道中,並將梯度幅值作爲投影時的權值。

在進行梯度方向投影處理時採用加權的方式,確定某個通道的權值,如下所示:

還有一個細節是,如果角度大於在(160,180)之間時,角度0和180是相等的,所以,角度爲165時將其按照幅值比例投影到0和160兩個通道中:(180-165)/(165-160)

遍歷整個cell中的所有像素點,便可以得到該cell單元的梯度方向直方圖:

2.5 重疊塊直方圖歸一化

由於圖像的局部曝光度以及前景與背景之間的對比度存在多樣化的情況,所以梯度值的變化範圍非常廣,引進歸一化的直方圖對於檢測結果的提高有着非常重要的作用。

在上一步中我們在每一個cell單元中創建了梯度方向直方圖,在這一步中我們將在block塊中進行梯度直方圖的歸一化,每一個block是由2*2=4個cell單元構成,如下圖所示:

在每個塊中梯度直方圖應該是4*9=36維的。

在解釋直方圖是如何進行歸一化之前,讓我們看看長度爲3的向量是如何進行歸一化的:假設一個像素向量[128,64,32],向量的長度則爲:sqrt{128^2 + 64^2 + 32^2} = 146.64 這也被稱爲向量的L2範數。將向量的每一個元素除以146.64得到歸一化向量[0.87,0.43,0.22]。

我們將一個block塊中的梯度直方圖串聯成一個36*1維向量,並進行歸一化,就得到了該block塊內的特徵,因爲block塊之間是有重疊的,也就是說每個cell單元中的特徵會多次出現在不同的block塊中。

2.6 收集HOG特徵

上一步中我們得到一個block塊的歸一化後的梯度方向直方圖,現在我們只需遍歷檢測圖像中所有的塊便可以得到整個圖像的梯度方向直方圖,這就是我們要求解的HOG特徵向量。

從上圖中,我們可以發現直方圖的主要方向捕捉了人的外形,尤其是軀幹和腿的部位。我們得到歸一化的HOG特徵之後,就可以使用分類器對行人進行檢測,比如使用支持向量機SVM進行人與背景的分類,如下圖所示:

2.7 HOG特徵的優缺點

HOG特徵具有以下優點:

  • HOG表示的是邊緣的結構特徵,因此可以描述局部的形狀信息
  • 位置和方向的量化在一定程度上可以一直平移和旋轉帶來的影響
  • 採取局部區域歸一化直方圖,可以部分抵消光照變換帶來的影響

它也有不少缺點:

  • 描述子生成冗長,維數較高,導致速度慢,實時性差
  • 很難處理遮擋問題
  • 由於梯度的性質,對噪聲非常敏感

2.2 實現

OpenCV提供了計算HOG特徵的API,實現HOG特徵提取的流程是:

  1. 實例化HOG特徵提取算子,使用的API是:
hog = cv2.HOGDescriptor(winSize,blockSize,blockStride,cellSize,nbins)

參數:

  • winSize: 檢測窗口的大小
  • blockSize: block塊的大小
  • blockStride: block塊的滑動步長
  • cellSize: cell單元的大小
  • Nbins:統計梯度的方向的數目,一般取爲9,即在一個cell單元中計算9個方向的梯度直方圖

返回:

  • hog: 實例化後的Hog特徵檢測對象

  • 搜索整個圖像,計算圖像的HOG特徵,調用:

hogDes = hog.compute(img, winStride, padding)

參數:

  • img: 輸入圖像
  • winStrise:檢測窗口的滑動步長
  • padding:填充,在圖像的周圍填充點對邊界進行處理。

返回:

  • hogDes: 整幅圖像的HOG特徵描述符,當padding爲默認的(0,0)時,特徵向量的維數:[(img_size - window_size) / window_stride +1 )]*(每個檢測窗口中的特徵維數)。

示例:

我們計算下圖的hog特徵:

代碼如下所示:

import cv2 as cv 
import numpy as np
import matplotlib.pyplot as plt

# 1.讀取圖像
img = cv.imread('xingren.jpeg')
gray= cv.cvtColor(img,cv.COLOR_BGR2GRAY)

# 2.Hog特徵提取
# 2.1 參數設置
winSize = (64,128)
blockSize = (16,16)
blockStride = (8,8)
cellSize = (8,8)
nbins = 9

# 2.2 實例化hog對象
hog = cv.HOGDescriptor(winSize,blockSize,blockStride,cellSize,nbins)

# 2.3 計算Hog特徵描述符
hogDes = hog.compute(img,winStride=(8,8))

# 2.4 輸出描述符的大小
print(hogDes.size)

總結

  1. LBP算法:

    原始LBP特徵:在3∗3的窗口內,以窗口中心像素爲閾值,將相鄰的8個像素的灰度值與其進行比較,若周圍像素值大於中心像素值,則該像素點的位置被標記爲1,否則爲0。這樣,3∗3鄰域內的8個點經比較可產生8位二進制數,即LBP值。

    圓形LBP算子:計算不同半徑鄰域大小和不同像素點數的特徵值

    旋轉不變LBP算子:不斷旋轉圓形鄰域得到一系列初始定義的 LBP值,取其最小值作爲該鄰域的 LBP 值

    Uniform Pattern LBP特徵:當某個LBP所對應的循環二進制數從0到1或從1到0最多有兩次跳變時,該LBP所對應的二進制就稱爲一個等價模式類。。除等價模式類以外的模式都歸爲另一類,稱爲混合模式類。

    API:

    Skiimage.feature.Local_binary_pattern()

  2. HOG算法

    思想:在一副圖像中,局部目標的表象和形狀(appearance and shape)能夠利用梯度或邊緣的方向密度分佈來描述。

    HOG特徵檢測算法的步驟:

    顏色空間歸一化—>梯度計算—>梯度方向直方圖—>重疊塊直方圖歸一化—>HOG特徵

    簡單描述如下:

    1)將輸入圖像灰度化,即將彩色圖轉換爲灰度圖

    2)顏色空間歸一化:採用Gamma校正法對輸入圖像進行顏色空間的標準化(歸一化),目的是調節圖像的對比度,降低圖像局部的陰影和光照變化所造成的影響,同時可以抑制噪音的干擾

    3)梯度計算:計算圖像每個像素的梯度(包括大小和方向);主要是爲了捕獲輪廓信息,同時進一步弱化光照的干擾

    4)梯度方向直方圖:將圖像劃分成小cells(例如6*6像素/cell), 統計每個cell的梯度直方圖(不同梯度的個數),即可形成每個cell的描述符

    5)重疊直方圖歸一化:將每幾個cell組成一個block(例如3*3個cell/block),一個block內所有cell的特徵descriptor串聯起來便得到該block的HOG特徵描述符。

    6)HOG特徵:將圖像image內的所有block的HOG特徵描述符串聯起來就可以得到該image的HOG特徵描述符,就得到最終的可供分類使用的特徵向量了。

API:

1)實例化HOG對象:

hog = cv.HOGDescriptor()

2) 計算HOG特徵描述符

hogdes = hog.Compute()


In [1]:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
from skimage.feature import local_binary_pattern

LBP

In [2]:

# 1 讀取圖片
face = cv.imread("./image/face.jpeg",0)

In [3]:

# 2 特徵提取
# 2.0 參數設置
r = 1
points = 8*r

In [4]:

# 2.1 原始的LBP
lbp = local_binary_pattern(face,8,1)

In [5]:

plt.imshow(lbp,"gray")

Out[5]:

<matplotlib.image.AxesImage at 0x1293824d0>

In [6]:

# 2.2 圓形LBP
clbp = local_binary_pattern(face,points,r,method="ror")

In [7]:

plt.imshow(clbp,"gray")

Out[7]:

<matplotlib.image.AxesImage at 0x129720a90>

In [8]:

# 2.3 旋轉不變LBP
varlbp = local_binary_pattern(face,points,r,method="var")

In [9]:

plt.imshow(varlbp,"gray")

Out[9]:

<matplotlib.image.AxesImage at 0x129829d10>

In [10]:

# 2.4 等價LBP
uniformlbp = local_binary_pattern(face,points,r,method="nri_uniform")

In [11]:

np.max(uniformlbp)

Out[11]:

58.0

In [12]:

plt.imshow(uniformlbp,"gray")

Out[12]:

<matplotlib.image.AxesImage at 0x129928490>

HOG

In [13]:

# 1 讀取圖像
img = cv.imread("./image/xingren.jpeg",0)

In [15]:

img.shape

Out[15]:

(256, 128)

In [16]:

# 2 特徵提取

In [17]:

# 2。0 參數設置
winsize = (64,128)
blocksize =(16,16)
bloskstride = (8,8)
cellsize = (8,8)
nbins =9 

In [18]:

# 2.1 實例化
hog = cv.HOGDescriptor(winsize,blocksize,bloskstride,cellsize,nbins)

In [21]:

# 2.2 計算描述符
hogdes = hog.compute(img,(8,8))

In [22]:

# 2.3 大小
hogdes.size

Out[22]:

578340

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章