基于python的HOG+SVM目标检测算法实现
目錄
- 一、場景需求解讀
- 二、HOG算法簡介
- 三、SVM算法簡介
- 四、基于HOG的目標檢測算法訓練流程
- 五、目標檢測代碼實現
- 六、非極大值抑制(NMS)簡介及代碼實現
- 七、NMS效果展示與分析
- 八、思維擴展
- 參考資料
- 注意事項
一、場景需求解讀
??目標檢測是一個很常見的計算機視覺任務,它在現實場景中具有很多的應用。隨著深度學習技術的快速發展,當前主流的目標檢測算法主要分為單階段和雙階段,代表性的算法包括SSD和Faster-rcnn等;除此之外也可以分為基于Anchors和Anchors free的算法,盡管這些算法都能取得較高的精度,但是它們都需要依賴GPU和大量的訓練樣本,另外,這些算法的運行速度都比較慢,一般都是在GPU上面能獲得近似實時的速度。對于現實生活中的一些場景而言,它們可能對算法的速度和成本有著較高的要求,但是檢測任務又相對來講比較簡單,對于這種情況而言,傳統的基于HOG+SVM的檢測算法仍然具有較大的用武之地。下面展示了一個案例。
二、HOG算法簡介
??HOG是一種在計算機視覺和圖像處理中用來進行物體檢測的描述子。通過計算和統計局部區域的梯度方向直方圖來構成特征。Hog特征結合SVM分類器已經被廣泛應用于圖像識別中,尤其在行人檢測中獲得了極大的成功。
主要思想:在一幅圖像中,局部目標的表象和形狀能夠利用梯度或邊緣的方向密度分布來進行描述。其本質是梯度的統計信息,而梯度主要存在于邊緣所在的地方。
算法優點:與其他的特征描述方法相比,HOG具有較多優點。由于HOG是在圖像的局部方格單元上進行操作的,所以它對圖像的幾何和光學形變都能保持很好的不變性,這兩種形變只會出現在更大的空間領域上。其次,在粗的空域抽樣、精細的方向抽樣以及較強的局部光學歸一化等條件下,只要行人大體上能夠保持直立的姿勢,可以容許行人有一些細微的肢體動作,這些細微的動作可以被忽略而不影響檢測效果。因此HOG特征特別適合于做圖像中的人體檢測。
實現流程:
- 步驟1-讀取待檢測的圖片;
- 步驟2-將輸入圖像灰度化(將輸入的彩色圖像的r,g,b值通過特定公式轉換為灰度值);
- 步驟3-采用Gamma校正法對輸入圖像進行顏色空間的標準化(歸一化);
- 步驟4-計算圖像中每個像素的梯度值(包括大小和方向),捕獲輪廓信息;
- 步驟5-統計每個cell內的梯度直方圖(不同梯度的個數),形成每個cell的特征描述子;
- 步驟6-將每幾個cell組成一個block(以3*3為例),一個block內所有cell的特征串聯起來得到該block的HOG特征描述子;
- 步驟7-將圖像image內所有block塊的HOG特征描述子串聯起來得到該image(檢測目標)的HOG特征描述子,這就是最終分類的特征向量。
三、SVM算法簡介
??支持向量機(support vector machines, SVM)是一種二分類模型,它的基本模型是定義在特征空間上的間隔最大的線性分類器,間隔最大使它有別于感知機;SVM還包括核技巧,這使它成為實質上的非線性分類器。SVM的的學習策略就是間隔最大化,可形式化為一個求解凸二次規劃的問題,也等價于正則化的合頁損失函數的最小化問題。SVM的的學習算法就是求解凸二次規劃的最優化算法。具體的算法實現原理請參考該博客。
四、基于HOG的目標檢測算法訓練流程
-
步驟1-從訓練數據集中獲取P個正樣本塊,并計算這P個正樣本塊的HOG特征描述子;
-
步驟2-從訓練數據集中獲取N個負樣本塊,并計算這N個負樣本塊的HOG特征描述子,其中N>>P;
-
步驟3-在這些正樣本和負樣本塊上面訓練一個SVM分類器模型;
-
步驟4-應用hard-negative-mining。對于負面訓練集中的每個圖像和每個可能的圖像比例,在圖像上面應用滑動窗口。在每個窗口中計算相應的HOG特征描述符并應用分類器。如果您的分類器(錯誤地)將給定窗口分類為一個對象(它將絕對存在誤報),記錄與誤報補丁相關的特征向量以及分類的概率。這種方法被稱為hard-negative-mining。具體效果如下圖所示:
-
步驟5-首先獲取使用hard-negative-mining技術獲取到的錯誤的正樣本塊,然后按照概率值對它們進行排序;接著使用這些樣本塊重新訓練分類器模型;
-
步驟6-將訓練好的模型應用到測試圖片中;
-
步驟7-對預測的結果使用NMS去除冗余的BB。
五、目標檢測代碼實現
訓練代碼如下所示,具體的訓練數據集從該鏈接下載,最終將會獲得一個訓練好的分類模型。
import cv2 import numpy as np import randomdef load_images(dirname, amout = 9999):img_list = []file = open(dirname)img_name = file.readline()while img_name != '': # 文件尾img_name = dirname.rsplit(r'/', 1)[0] + r'/' + img_name.split('/', 1)[1].strip('\n')img_list.append(cv2.imread(img_name))img_name = file.readline()amout -= 1if amout <= 0: # 控制讀取圖片的數量breakreturn img_list# 從每一張沒有人的原始圖片中隨機裁出10張64*128的圖片作為負樣本 def sample_neg(full_neg_lst, neg_list, size):random.seed(1)width, height = size[1], size[0]for i in range(len(full_neg_lst)):for j in range(10):y = int(random.random() * (len(full_neg_lst[i]) - height))x = int(random.random() * (len(full_neg_lst[i][0]) - width))neg_list.append(full_neg_lst[i][y:y + height, x:x + width])return neg_list# wsize: 處理圖片大小,通常64*128; 輸入圖片尺寸>= wsize def computeHOGs(img_lst, gradient_lst, wsize=(128, 64)):hog = cv2.HOGDescriptor()# hog.winSize = wsizefor i in range(len(img_lst)):if img_lst[i].shape[1] >= wsize[1] and img_lst[i].shape[0] >= wsize[0]:roi = img_lst[i][(img_lst[i].shape[0] - wsize[0]) // 2: (img_lst[i].shape[0] - wsize[0]) // 2 + wsize[0], \(img_lst[i].shape[1] - wsize[1]) // 2: (img_lst[i].shape[1] - wsize[1]) // 2 + wsize[1]]gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)gradient_lst.append(hog.compute(gray))# return gradient_lstdef get_svm_detector(svm):sv = svm.getSupportVectors()rho, _, _ = svm.getDecisionFunction(0)sv = np.transpose(sv)return np.append(sv, [[-rho]], 0)# 主程序 # 第一步:計算HOG特征 neg_list = [] pos_list = [] gradient_lst = [] labels = [] hard_neg_list = [] svm = cv2.ml.SVM_create() pos_list = load_images(r'G:/python_project/INRIAPerson/96X160H96/Train/pos.lst') full_neg_lst = load_images(r'G:/python_project/INRIAPerson/train_64x128_H96/neg.lst') sample_neg(full_neg_lst, neg_list, [128, 64]) print(len(neg_list)) computeHOGs(pos_list, gradient_lst) [labels.append(+1) for _ in range(len(pos_list))] computeHOGs(neg_list, gradient_lst) [labels.append(-1) for _ in range(len(neg_list))]# 第二步:訓練SVM svm.setCoef0(0) svm.setCoef0(0.0) svm.setDegree(3) criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 1000, 1e-3) svm.setTermCriteria(criteria) svm.setGamma(0) svm.setKernel(cv2.ml.SVM_LINEAR) svm.setNu(0.5) svm.setP(0.1) # for EPSILON_SVR, epsilon in loss function? svm.setC(0.01) # From paper, soft classifier svm.setType(cv2.ml.SVM_EPS_SVR) # C_SVC # EPSILON_SVR # may be also NU_SVR # do regression task svm.train(np.array(gradient_lst), cv2.ml.ROW_SAMPLE, np.array(labels))# 第三步:加入識別錯誤的樣本,進行第二輪訓練 # 參考 http://masikkk.com/article/SVM-HOG-HardExample/ hog = cv2.HOGDescriptor() hard_neg_list.clear() hog.setSVMDetector(get_svm_detector(svm)) for i in range(len(full_neg_lst)):rects, wei = hog.detectMultiScale(full_neg_lst[i], winStride=(4, 4),padding=(8, 8), scale=1.05)for (x,y,w,h) in rects:hardExample = full_neg_lst[i][y:y+h, x:x+w]hard_neg_list.append(cv2.resize(hardExample,(64,128))) computeHOGs(hard_neg_list, gradient_lst) [labels.append(-1) for _ in range(len(hard_neg_list))] svm.train(np.array(gradient_lst), cv2.ml.ROW_SAMPLE, np.array(labels))# 第四步:保存訓練結果 hog.setSVMDetector(get_svm_detector(svm)) hog.save('myHogDector.bin')測試代碼如下所示。
import cv2 import numpy as nphog = cv2.HOGDescriptor() hog.load('myHogDector.bin') cap = cv2.VideoCapture(0) while True:ok, img = cap.read()rects, wei = hog.detectMultiScale(img, winStride=(4, 4),padding=(8, 8), scale=1.05)for (x, y, w, h) in rects:cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)cv2.imshow('a', img)if cv2.waitKey(1)&0xff == 27: # esc鍵break cv2.destroyAllWindows()六、非極大值抑制(NMS)簡介及代碼實現
??對于目標檢測算法而言,通常檢測出的結果中會存在一些重復或者冗余的情況,即輸出了多個可能是人臉的BB,那么我們通常都需要使用NMS技術來獲得一個最準確的BB,下圖展示了一個實例。
NMS的原理-NMS的本質是搜索局部極大值,抑制非極大值元素。
NMS的作用-當算法對一個目標產生了多個候選框的時候,選擇 score 最高的框,并抑制其他對于改目標的候選框。
NMS的應用場景-一幅圖中有多個目標(如果只有一個目標,那么直接取 score 最高的候選框即可)。
NMS的輸入-算法對一幅圖產生的所有的候選框,以及每個框對應的 score (可以用一個 5 維數組 dets 表示,前 4 維表示四個角的坐標,第 5 維表示分數),閾值 thresh。
NMS的輸出-正確的候選框組(dets 的一個子集)。
七、NMS效果展示與分析
??上圖展示了NMS算法的處理效果。上面分別展示了3個不同測試圖片上的測試效果,紅色的邊界框表示原始的進行NMS處理之前的效果,綠色的邊界框表示進行NMS處理之后的效果,通過上面的結果我們可以發現NMS算法可以很好的抑制掉那些重復的邊界框,最終找到一個最準確的邊界框。
八、思維擴展
??上面僅僅展示了一種NMS算法,如果你進行實際的測試之后你可能會發現這個算法的處理速度比較慢,并不能滿足你的性能要求。那你聰明的你肯定想要了一種可以用來進行算法加速的思路,那就是是使用numpy和cython進行算法加速,下面展示了一個加速版本的NMS算法實現。
# -------------------------------------------------------- # Fast R-CNN # Copyright (c) 2015 Microsoft # Licensed under The MIT License [see LICENSE for details] # Written by Ross Girshick # --------------------------------------------------------import numpy as np cimport numpy as npcdef inline np.float32_t max(np.float32_t a, np.float32_t b):return a if a >= b else bcdef inline np.float32_t min(np.float32_t a, np.float32_t b):return a if a <= b else bdef cpu_nms(np.ndarray[np.float32_t, ndim=2] dets, np.float thresh):cdef np.ndarray[np.float32_t, ndim=1] x1 = dets[:, 0]cdef np.ndarray[np.float32_t, ndim=1] y1 = dets[:, 1]cdef np.ndarray[np.float32_t, ndim=1] x2 = dets[:, 2]cdef np.ndarray[np.float32_t, ndim=1] y2 = dets[:, 3]cdef np.ndarray[np.float32_t, ndim=1] scores = dets[:, 4]cdef np.ndarray[np.float32_t, ndim=1] areas = (x2 - x1 + 1) * (y2 - y1 + 1)cdef np.ndarray[np.int_t, ndim=1] order = scores.argsort()[::-1]cdef int ndets = dets.shape[0]cdef np.ndarray[np.int_t, ndim=1] suppressed = \np.zeros((ndets), dtype=np.int)# nominal indicescdef int _i, _j# sorted indicescdef int i, j# temp variables for box i's (the box currently under consideration)cdef np.float32_t ix1, iy1, ix2, iy2, iarea# variables for computing overlap with box j (lower scoring box)cdef np.float32_t xx1, yy1, xx2, yy2cdef np.float32_t w, hcdef np.float32_t inter, ovrkeep = []for _i in range(ndets):i = order[_i]if suppressed[i] == 1:continuekeep.append(i)ix1 = x1[i]iy1 = y1[i]ix2 = x2[i]iy2 = y2[i]iarea = areas[i]for _j in range(_i + 1, ndets):j = order[_j]if suppressed[j] == 1:continuexx1 = max(ix1, x1[j])yy1 = max(iy1, y1[j])xx2 = min(ix2, x2[j])yy2 = min(iy2, y2[j])w = max(0.0, xx2 - xx1 + 1)h = max(0.0, yy2 - yy1 + 1)inter = w * hovr = inter / (iarea + areas[j] - inter)if ovr >= thresh:suppressed[j] = 1return keep參考資料
[1] 參考鏈接1
[2] 參考鏈接2
注意事項
[1] 該博客是本人原創博客,如果您對該博客感興趣,想要轉載該博客,請與我聯系(qq郵箱:1575262785@qq.com),我會在第一時間回復大家,謝謝大家的關注.
[2] 由于個人能力有限,該博客可能存在很多的問題,希望大家能夠提出改進意見。
[3] 如果您在閱讀本博客時遇到不理解的地方,希望您可以聯系我,我會及時的回復您,和您交流想法和意見,謝謝。
[4] 本文測試的圖片可以通過該鏈接進行下載。網盤鏈接- 提取碼:e31o。
[5] 本人業余時間承接各種本科畢設設計和各種小項目,包括圖像處理(數據挖掘、機器學習、深度學習等)、matlab仿真、python算法及仿真等,有需要的請加QQ:1575262785詳聊!!!
總結
以上是生活随笔為你收集整理的基于python的HOG+SVM目标检测算法实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一号专车如何评价司机
- 下一篇: Python+Opencv实现模板匹配