OpenCV影象處理-HOG特徵和應用
HOG特徵和應用
概述
✔️ HOG(Histogram of Oriented Gradient)特徵在物件識別與模式匹配中是一種常見的特徵提取演算法,是基於本地畫素塊進行特徵直方圖提取的一種演算法,物件區域性的變形與光照影響有很好的穩定性。
HOG應用-行人檢測
✔️ 用HOG特徵來來識別人像,透過HOG特徵提取+SVM訓練,可以得到很好的效果,Opencv也集成了HOG進行的行人檢測演算法。
OpenCV函式
hog = cv2。HOGDescriptor()
:建立HOG特徵描述;
hog。setSVMDetector(cv。HOGDescriptor_getDefaultPeopleDetector())
:建立HOG+SVM行人檢測器;
多尺度檢測API:
rects, weights = hog。detectMultiScale(img, foundLocations,
hitThreshold = 0,
winStride, padding,
scale = 1。05,
finalThreshold = 2。0,
useMeanshiftGrouping = false)
輸入
Img ——> 表示輸入影象;
foundLocations ——> 表示發現物件矩形框;
hitThreshold ——> 表示SVM距離度量(特徵與SVM分類超平面之間距離),預設0表示;
winStride ——> 表示視窗步長;
padding ——> 表示填充;
scale ——> 表示尺度空間;
finalThreshold ——> 最終閾值,預設為2。0;
useMeanshiftGrouping ——> 不建議使用,速度太慢;
PS:其中視窗步長與Scale對結果影響最大,特別是Scale,小的尺度變化有利於檢出低解析度物件,同時也會導致FP發生,高的可以避免FP但是會產生FN(物件漏檢)。
行人檢測程式碼示例
import
cv2
as
cv
src
=
cv
。
imread
(
“people。png”
)
cv
。
imshow
(
“input”
,
src
)
# hog特徵描述
hog
=
cv
。
HOGDescriptor
()
# 建立SVM檢測器
hog
。
setSVMDetector
(
cv
。
HOGDescriptor_getDefaultPeopleDetector
())
# 檢測行人
(
rects
,
weights
)
=
hog
。
detectMultiScale
(
src
,
winStride
=
(
4
,
4
),
padding
=
(
8
,
8
),
scale
=
1。25
,
useMeanshiftGrouping
=
False
)
for
(
x
,
y
,
w
,
h
)
in
rects
:
cv
。
rectangle
(
src
,
(
x
,
y
),
(
x
+
w
,
y
+
h
),
(
0
,
255
,
0
),
2
)
cv
。
imshow
(
“hog-people”
,
src
)
cv
。
waitKey
(
0
)
cv
。
destroyAllWindows
()
原圖
結果圖
HOG特徵描述子提取
提取過程
1. Gamma矯正
✔️ 為了提高檢測器對關照等干擾因素的魯棒性,需要對影象進行Gamma矯正,完成對整個影象的歸一化,調整對比度,降低噪聲影響;
一般 r=1/2
2. 灰度化
3. 計算影象XY梯度和方向
使用sobel可以出水平和垂直方向的梯度:
gx = cv2。Sobel(img, cv2。CV_32F, 1, 0, ksize=1)
gy = cv2。Sobel(img, cv2。CV_32F, 0, 1, ksize=1)
利用公式求取梯度幅值和方向:
# Opencv中使用:
mag, angle = cv2。cartToPolar(gx, gy, angleInDegrees=True)
4. 8x8網格方向梯度權重直方圖統計
✔️ 流程:首先將影象劃分成若干個塊(Block),每個塊又由若干個細胞單元(cell)組成,細胞單元由更小的單位畫素(Pixel)組成,然後在每個細胞單元中對內部的所有畫素的梯度方向進行統計。
預設HOG的描述子視窗為64x128, 視窗移動步長為 8x8
每個視窗的cell為8x8,每個block由4個cell組成,block移動步長為一個cell,因此可以得到7x15個block
HOG
直方圖把180度分為9個bin,每個區間為20度,如果畫素落在某個區間,就把該畫素的直方圖累計到對應區間的直方圖上
直方圖統計
每個block有4個cell,每個cell有9個向量值,即每個block有36個向量,所以整個視窗有7x15x36=3780個特徵描述子。
5. 塊描述子和特徵向量歸一化
✔️ 每個block可以得到4個9維的向量,需要再次進行一次歸一化,這樣可以進一步提高泛化能力,同傳使用L2-nrom進行歸一化(還有L1-norm, L1-sqrt,etc。)
整體流程圖
HOG提取流程
HOG+SVM 檢測示例
✔️ 這裡,我們使用前面所瞭解到HOG知識,結合SVM,進行一個簡單的水錶檢測案例。
使用描述子特徵生成樣本資料
透過SVM進行分類學習與訓練
load模型,進行預測結果
資料生成
# 把目標圖放在64x128的灰色圖片中間,方便計算描述子
def
get_hog_descriptor
(
image
):
hog
=
cv
。
HOGDescriptor
()
h
,
w
=
image
。
shape
[:
2
]
rate
=
64
/
w
image
=
cv
。
resize
(
image
,
(
64
,
np
。
int
(
rate
*
h
)))
gray
=
cv
。
cvtColor
(
image
,
cv
。
COLOR_BGR2GRAY
)
bg
=
np
。
zeros
((
128
,
64
),
dtype
=
np
。
uint8
)
bg
[:,:]
=
127
h
,
w
=
gray
。
shape
dy
=
(
128
-
h
)
//
2
bg
[
dy
:
h
+
dy
,:]
=
gray
descriptors
=
hog
。
compute
(
bg
,
winStride
=
(
8
,
8
),
padding
=
(
0
,
0
))
return
descriptors
def
get_data
(
train_data
,
labels
,
path
,
lableType
):
for
file_name
in
os
。
listdir
(
path
):
img_dir
=
os
。
path
。
join
(
path
,
file_name
)
img
=
cv
。
imread
(
img_dir
)
hog_desc
=
get_hog_descriptor
(
img
)
one_fv
=
np
。
zeros
([
len
(
hog_desc
)],
dtype
=
np
。
float32
)
for
i
in
range
(
len
(
hog_desc
)):
one_fv
[
i
]
=
hog_desc
[
i
][
0
]
train_data
。
append
(
one_fv
)
labels
。
append
(
lableType
)
return
train_data
,
labels
def
get_dataset
(
pdir
,
ndir
):
train_data
=
[]
labels
=
[]
# 獲取正樣本
train_data
,
labels
=
get_data
(
train_data
,
labels
,
pdir
,
lableType
=
1
)
# 獲取負樣本
train_data
,
labels
=
get_data
(
train_data
,
labels
,
ndir
,
lableType
=-
1
)
return
np
。
array
(
train_data
,
dtype
=
np
。
float32
),
np
。
array
(
labels
,
dtype
=
np
。
int32
)
if
__name__
==
‘__main__’
:
# train_data的shape為(n, 3780), labels(n,)
# n為樣本數
train_data
,
labels
=
get_dataset
(
“pdir/”
,
“ndir/”
)
構建SVM訓練器
✔️ Opencv中SVM有線性分類器和非線性的徑向分類器。
這裡使用線性分類器:
svm。train(trainData, cv。ml。ROW_SAMPLE, responses)
Sample ——> 表示訓練樣本資料/HOG特徵資料
Layout ——> 有兩種組織方式ROW_SAMPLE與COL_SAMPLE
Responses ——> 每個輸入樣本的標籤
訓練程式碼
def
svm_train
(
pdir
,
ndir
):
# 建立SVM
svm
=
cv
。
ml
。
SVM_create
()
# 設定相應的SVM引數
svm
。
setKernel
(
cv
。
ml
。
SVM_LINEAR
)
svm
。
setType
(
cv
。
ml
。
SVM_C_SVC
)
svm
。
setC
(
2。67
)
svm
。
setGamma
(
5。383
)
# 獲取正負樣本和labels
trainData
,
responses
=
get_dataset
(
pdir
,
ndir
)
# reshape (n,)——>(n,1)
responses
=
np
。
reshape
(
responses
,
[
-
1
,
1
])
# 訓練
svm
。
train
(
trainData
,
cv
。
ml
。
ROW_SAMPLE
,
responses
)
svm
。
save
(
‘svm_data。dat’
)
預測目標
import cv2 as cv
import numpy as np
image = cv。imread(“test_01。jpg”)
# 原圖太大,降低原圖解析度
test_img = cv。resize(image, (0, 0), fx=0。2, fy=0。2)
# 灰度
gray = cv。cvtColor(test_img, cv。COLOR_BGR2GRAY)
# 獲取大小
h, w = test_img。shape[:2]
# 載入訓練好的模型
svm = cv。ml。SVM_load(‘。。/code_104/svm_data。dat’)
# 為了篩選框,記錄框座標總和以及框的個數,為了最後求出所有候選框的均值框
sum_x = 0
sum_y = 0
count = 0
# 建立hog特徵描述子函式
hog = cv。HOGDescriptor()
# 為了加快計算,視窗滑動的步長為4,一個cell是8個畫素
for row in range(64, h-64, 4):
for col in range(32, w-32, 4):
win_roi = gray[row-64:row+64,col-32:col+32]
hog_desc = hog。compute(win_roi, winStride=(8, 8), padding=(0, 0))
one_fv = np。zeros([len(hog_desc)], dtype=np。float32)
for i in range(len(hog_desc)):
one_fv[i] = hog_desc[i][0]
one_fv = one_fv。reshape(-1, len(hog_desc))
# 預測
result = svm。predict(one_fv)[1]
# 統計正樣本
if result[0][0] > 0:
sum_x += (col-32)
sum_y += (row-64)
count += 1
# 畫出所有框
cv。rectangle(test_img, (col-32, row-64), (col+32, row+64), (0, 233, 255), 1, 8, 0)
# 求取均值框
x = sum_x // count
y = sum_y // count
# 畫出均值框
cv。rectangle(test_img, (x, y), (x+64, y+128), (0, 0, 255), 2, 8, 0)
原圖
結果圖(紅色框為最終均值框,黃色為檢測到的所有框)
如上圖,類似於目標檢測中NMS的作用,這裡使用均值的方法獲得最終的框。
小結
✔️ 對於簡單的識別,HOG和SVM的識別效果還是很不錯的,為了提高效果,可以增加正負樣本進行,再次進行訓練。
✔️ SVM不需要GPU,所以在針對一些簡單的識別任務,可以採用這個方法,但是複雜問題,還是建議使用神經網路,效果會更好!
——————————————————————可愛の分割線——————————————————————
更多Opencv教程可以 Follow github的opencv教程,
中文&English
歡迎Star
❤️❤️❤️
參考
- Histogram of Oriented Gradients
- 方向梯度直方圖(HOG)