天天看點

c++ 提取傅裡葉描述子_Opencv從零開始 - 「進階篇」- HOG特征及其描述子提取目錄概述HOG特征描述子提取HOG+SVM 檢測示例小結未完待續~

✒️ HOG特征是對象識别與模式比對中是一種常見的特征提取算法, 本文主要介紹了HOG特征以及利用其描述子加SVM方法實作檢測功能,具體的内容可以參考下文~

目錄

  • 概述
  • HOG特征描述子提取
  • HOG+SVM 檢測示例
  • 小結

概述

✔️ HOG(Histogram of Oriented Gradient)特征在對象識别與模式比對中是一種常見的特征提取算法,是基于本地像素塊進行特征直方圖提取的一種算法,對象局部的變形與光照影響有很好的穩定性。

HOG應用-行人檢測

✔️ 用HOG特征來來識别人像,通過HOG特征提取+SVM訓練,可以得到很好的效果,Opencv也內建了HOG進行的行人檢測算法。

OpenCV函數

  1. hog = cv2.HOGDescriptor() :建立HOG特征描述;
  2. hog.setSVMDetector(cv.HOGDescriptor_getDefaultPeopleDetector()) :建立HOG+SVM行人檢測器;
  3. 多尺度檢測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()
           
c++ 提取傅裡葉描述子_Opencv從零開始 - 「進階篇」- HOG特征及其描述子提取目錄概述HOG特征描述子提取HOG+SVM 檢測示例小結未完待續~

src

c++ 提取傅裡葉描述子_Opencv從零開始 - 「進階篇」- HOG特征及其描述子提取目錄概述HOG特征描述子提取HOG+SVM 檢測示例小結未完待續~

result

HOG特征描述子提取

提取過程

1. Gamma矯正

✔️ 為了提高檢測器對關照等幹擾因素的魯棒性,需要對圖像進行Gamma矯正,完成對整個圖像的歸一化,調整對比度,降低噪聲影響;

c++ 提取傅裡葉描述子_Opencv從零開始 - 「進階篇」- HOG特征及其描述子提取目錄概述HOG特征描述子提取HOG+SVM 檢測示例小結未完待續~
一般 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)
           

利用公式求取梯度幅值和方向:

c++ 提取傅裡葉描述子_Opencv從零開始 - 「進階篇」- HOG特征及其描述子提取目錄概述HOG特征描述子提取HOG+SVM 檢測示例小結未完待續~

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
c++ 提取傅裡葉描述子_Opencv從零開始 - 「進階篇」- HOG特征及其描述子提取目錄概述HOG特征描述子提取HOG+SVM 檢測示例小結未完待續~
  • 直方圖把180度分為9個bin,每個區間為20度,如果像素落在某個區間,就把該像素的直方圖累計到對應區間的直方圖上
c++ 提取傅裡葉描述子_Opencv從零開始 - 「進階篇」- HOG特征及其描述子提取目錄概述HOG特征描述子提取HOG+SVM 檢測示例小結未完待續~
  • 每個block有4個cell,每個cell有9個向量值,即每個block有36個向量,是以整個視窗有7x15x36=3780個特征描述子。

5. 塊描述子和特征向量歸一化

✔️ 每個block可以得到4個9維的向量,需要再次進行一次歸一化,這樣可以進一步提高泛化能力,同傳使用L2-nrom進行歸一化(還有L1-norm, L1-sqrt,etc.)

c++ 提取傅裡葉描述子_Opencv從零開始 - 「進階篇」- HOG特征及其描述子提取目錄概述HOG特征描述子提取HOG+SVM 檢測示例小結未完待續~

整體流程圖

c++ 提取傅裡葉描述子_Opencv從零開始 - 「進階篇」- HOG特征及其描述子提取目錄概述HOG特征描述子提取HOG+SVM 檢測示例小結未完待續~

HOG+SVM 檢測示例

✔️ 這裡,我們使用前面所了解到HOG知識,結合SVM,進行一個簡單的水表檢測案例。

  1. 使用描述子特征生成樣本資料
  2. 通過SVM進行分類學習與訓練
  3. 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)
           
c++ 提取傅裡葉描述子_Opencv從零開始 - 「進階篇」- HOG特征及其描述子提取目錄概述HOG特征描述子提取HOG+SVM 檢測示例小結未完待續~

src

c++ 提取傅裡葉描述子_Opencv從零開始 - 「進階篇」- HOG特征及其描述子提取目錄概述HOG特征描述子提取HOG+SVM 檢測示例小結未完待續~

result

如上圖,類似于目标檢測中NMS的作用,這裡使用均值的方法獲得最終的框。

小結

✔️ 對于簡單的識别,HOG和SVM的識别效果還是很不錯的,為了提高效果,可以增加正負樣本進行,再次進行訓練。

✔️ SVM不需要GPU,是以在針對一些簡單的識别任務,可以采用這個方法,但是複雜問題,還是建議使用神經網絡,效果會更好!

未完待續~

更多Opencv教程将持續釋出!

歡迎關注喲~❤️❤️❤️

繼續閱讀