✒️ HOG特征是對象識别與模式比對中是一種常見的特征提取算法, 本文主要介紹了HOG特征以及利用其描述子加SVM方法實作檢測功能,具體的内容可以參考下文~
目錄
- 概述
- HOG特征描述子提取
- HOG+SVM 檢測示例
- 小結
概述
✔️ 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 cvsrc = 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()
src
result
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
- 直方圖把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+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 descriptorsdef 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, labelsdef 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 cvimport numpy as npimage = 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 = 0sum_y = 0count = 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 // county = sum_y // count# 畫出均值框cv.rectangle(test_img, (x, y), (x+64, y+128), (0, 0, 255), 2, 8, 0)
src
result
如上圖,類似于目标檢測中NMS的作用,這裡使用均值的方法獲得最終的框。
小結
✔️ 對于簡單的識别,HOG和SVM的識别效果還是很不錯的,為了提高效果,可以增加正負樣本進行,再次進行訓練。
✔️ SVM不需要GPU,是以在針對一些簡單的識别任務,可以采用這個方法,但是複雜問題,還是建議使用神經網絡,效果會更好!
未完待續~
更多Opencv教程将持續釋出!
歡迎關注喲~❤️❤️❤️