python+opencv3.4.0 實現HOG+SVM行人檢測

參照opencv官網例程寫了一個基於python的行人檢測程序,實現了和自帶檢測器基本一致的檢測效果。

網址 :https://docs.opencv.org/3.4.0/d5/d77/train_HOG_8cpp-example.html

opencv版本:3.4.0

訓練集和opencv官方用了同一個,可以從http://pascal.inrialpes.fr/data/human/下載,在網頁的最下方“here(970MB處)”,用迅雷下載比較快(500kB/s)。訓練集文件比較亂,需要仔細閱讀下載首頁的文字介紹。注意pos文件夾下的png圖片屬性,它們用opencv無法直接打開,linux系統下也無法顯示,需要用matlab讀取圖片->保存才行,很奇怪的操作。

代碼如下,儘可能與opencv官方例程保持一致,但省略了很多不是很關鍵的東西。訓練一次大概需要十幾分鍾

import cv2
import numpy as np
import random


def 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 -= 1
        if amout <= 0: # 控制讀取圖片的數量
            break
    return 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 = wsize
    for 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_lst


def 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 np

hog = 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()


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