1. 程式人生 > >使用opencv訓練cascade分類器進行目標檢測

使用opencv訓練cascade分類器進行目標檢測

文章目錄

0.建立訓練目錄

資料夾: train/

1.建立正負樣本

新建3個資料夾:train/pos/、train/neg/、train/xml/

pos資料夾
放置正樣本,尺寸要一致:如 20

20 20*20 (一般用於Haar特徵), 24 24 24*24 (LBP特徵)

neg資料夾
放置負樣本,正樣本的尺寸要保證不大於負樣本的尺寸

xml資料夾
級聯分類器xml檔案的輸出目錄

2.生成正負樣本的txt檔案

生成正樣本txt檔案 train/pos/pos.txt:

內容:圖片名 類別編號 左上角x 左上角y 右下角x 右下角y
pos_image1.png 1 0 0 30 30
pos_image2.png 1 0 0 30 30
… …

生成負樣本txt檔案 train/neg/neg.txt:

內容:圖片路徑名
neg/neg_image1.png
neg/neg_image2.png
… …

import os

def _get_directory_files(path, fileType, filePaths)
: if not os.path.exists(path): return files = os.listdir(path) for f in files: npath = path + '/' + f if (os.path.isfile(npath)): if (os.path.splitext(npath)[1] == fileType): filePaths.append(f) if (os.path.isdir(npath)): if (f[0] == '.'): pass else: _get_directory_files(npath, fileType, filePaths) return def _write_txt(txt_path,img_dir,is_pos,img_size,neg_dir='neg'): imgpaths=[] _get_directory_files(img_dir,'.png',imgpaths) with open(txt_path, "a") as f: for img_p in imgpaths: if is_pos: f.write('%s 1 0 0 %d %d\n' % (img_p,img_size[0],img_size[1])) else: f.write('%s/%s\n' % (neg_dir, img_p)) return if __name__ == "__main__": txt_pos = './train/pos/pos.txt' txt_neg = './train/neg/neg.txt' pos_img_dir = './train/pos' neg_img_dir = './train/neg' _write_txt(txt_pos,pos_img_dir,True,(30,30)) _write_txt(txt_neg,neg_img_dir,False,(30,30))

3.生成 pos.vec描述檔案

在 train/ 目錄下,執行命令:

opencv_createsamples -vec pos.vec -info pos/pos.txt -bg neg/neg.txt -num 10000 -w 30 -h 30

命令引數如下:

  • info 輸入正樣本描述檔案,預設NULL

  • img 輸入影象檔名,預設NULL

  • bg 負樣本描述檔案,檔案中包含一系列的被隨機選作物體背景的影象檔名,預設NULL

  • num 生成正樣本的數目,預設1000

  • bgcolor 背景顏色,表示透明顏色,預設0

  • bgthresh 顏色容差,所有處於bgcolor-bgthresh和bgcolor+bgthresh之間的畫素被置為透明畫素,也就是將白噪聲加到前景影象上,預設80

  • inv 前景影象顏色翻轉標誌,如果指定顏色翻轉,預設0(不翻轉)

  • randinv 如果指定顏色將隨機翻轉,預設0

  • maxidev 前景影象中畫素的亮度梯度最大值,預設40

  • maxxangle X軸最大旋轉角度,以弧度為單位,預設1.1

  • maxyangle Y軸最大旋轉角度,以弧度為單位,預設1.1

  • maxzangle Z軸最大旋轉角度,以弧度為單位,預設0.5
    輸入影象沿著三個軸進行旋轉,旋轉角度由上述3個值限定。

  • show 如果指定,每個樣本都將被顯示,按下Esc鍵,程式將繼續建立樣本而不在顯示,預設為0(不顯示)

  • scale 顯示影象的縮放比例,預設4.0

  • w 輸出樣本寬度,預設24

  • h 輸出樣本高度,預設24

  • vec 輸出用於訓練的.vec檔案,預設NULL

4.訓練cascade分類器

在 train/ 目錄下,執行命令:

opencv_traincascade -data xml -vec pos.vec -bg neg/neg.txt -numPos 8000 -numNeg 16000 -numStages 20 -featureType LBP -w 30 -h 30

命令引數如下:

  • data 目錄名xml,存放訓練好的分類器,如果不存在訓練程式自行建立

  • vec pos.vec檔案,由opencv_createsamples生成

  • bg 負樣本描述檔案, neg/neg.txt

  • numPos 每級分類器訓練時所用到的正樣本數目。
    應當注意,這個數值一定要比正樣本時的總數少,不然會報can not get new positive sample.理由:minHitRate:影響每個強分類器閾值,當設定為0.95時如果正訓練樣本個數為10000個,那麼其中的500個就很可能背叛別為負樣本,第二次選擇的時候必須多選擇後面的500個,按照這種規律我們為後面的每級多增加numPos*minHitRate個正樣本,根據訓練的級數可以得到如下公式:
    n u m P o s + n u m S t a g e s 1 n u m P o s 1 m i n H i t R a t e < = numPos+(numStages-1)*numPos*(1-minHitRate)<=準備的訓練樣本

  • numNeg 每級分類器訓練時所用到的負樣本數目,可以大於-bg指定的圖片數目

  • numStages 訓練分類器的級數,預設20級,一般在14-25層之間均可。
    如果層數過多,分類器的fals alarm就更小,但是產生級聯分類器的時間更長,分類器的hitrate就更小,檢測速度就慢。如果正負樣本較少,層數沒必要設定很多。

  • precalcValBufSize 快取大小,用於儲存預先計算的特徵值,單位MB

  • precalcIdxBufSize 快取大小,用於儲存預先計算的特徵索引,單位MB

  • baseFormatSave 僅在使用Haar特徵時有效,如果指定,級聯分類器將以老格式儲存

  • stageType 級聯型別,{ CC_BOOST }

  • featureType 特徵型別,目前只支援LBP、HOG、Haar三種特徵。但是HAAR訓練非常非常的慢,而LBP則相對快很多,因為HAAR需要浮點運算,精度自然比LBP更高,但是LBP的效果也基本能達到HAAR的效果,推薦使用LBP。

  • w,h 訓練樣本的尺寸,必須跟使用opencv_createsamples建立的訓練樣本尺寸保持一致,並且-w和-h的比例必須符合真實目標的比例.

  • bt Boosted分類器型別,{DAB-discrete Adaboost, RAB-RealAdaboost, LB-LogiBoost, GAB-Gentle Adaboost}

  • minHitRate 分類器的每一級希望得到的最小檢測率,總的最大檢測率大約為min_hit_rate^number_of_stages

  • maxFalseAlarmRate 分類器的每一級希望得到的最大誤檢率,總的誤檢率大約為max_false_rate^number_of_stages

  • weightTrimRate Specifies whether trimming should beused and its weight. 一個還不錯的數值是0.95

  • maxDepth 弱分類器的最大深度,一個不錯數值是1,二叉樹

  • maxWeightCount 每一級中弱分類器的最大數目

  • mode 訓練過程使用的Haar特徵型別,有BASIC/CORE/ALL三種特徵組合待選的,預設情況為BASIC,三種情況下對應的特徵選取分別如下:

5.目標檢測

detectMultiScale()函式引數:

cv2.CascadeClassifier.detectMultiScale(image[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize]]]]]) → objects

cv2.CascadeClassifier.detectMultiScale(image, rejectLevels, levelWeights[, scaleFactor[, minNeighbors[, flags[, minSize[, maxSize[, outputRejectLevels]]]]]]) → objects

引數如下:

  • image: Matrix of the type CV_8U containing an image where objects are detected. 灰度圖

  • objects:Vector of rectangles where each rectangle contains the detected object.

  • scaleFactor:Parameter specifying how much the image size is reduced at each image scale. 影象尺度引數,預設1.1

  • minNeighbors:Parameter specifying how many neighbors each candidate rectangle should have to retain it. 為每一個級聯矩形應該保留的臨近個數,預設為3,即至少有3次檢測到目標,才認為是目標。

  • flags:Parameter with the same meaning for an old cascade as in the function cvHaarDetectObjects. It is not used for a new cascade.
    CV_HAAR_DO_CANNY_PRUNING,利用邊緣檢測來排除一些邊緣很少或者很多的影象區域
    CV_HAAR_SCALE_IMAGE,按正常比例檢測
    CV_HAAR_FIND_GIGGEST_OBJECT,只檢測最大的物體
    CV_HAAR_DO_ROUGH_SEARCH,只做粗略檢測,預設值為0

  • minSize – Minimum possible object size. Objects smaller than that are ignored.

  • maxSize – Maximum possible object size. Objects larger than that are ignored.

import cv2, time
import numpy as np
import os.path

def get_hw_by_short_size(im_height, im_width, resize):
    short_size, max_size = resize
    im_size_min = np.min([im_height, im_width])
    im_size_max = np.max([im_height, im_width])
    scale = (short_size + 0.0) / im_size_min
    if scale * im_size_max > max_size:
        scale = (max_size + 0.0) / im_size_max

    resized_height, resized_width = int(round(im_height * scale)), int(
        round(im_width * scale))
    return resized_height, resized_width


class car_detector:

    def __init__(self, cascade_file, max_detect_hw=(400, 600)):
        if not os.path.isfile(cascade_file):
            raise RuntimeError("%s: not found" % cascade_file)

        self._cascade = cv2.CascadeClassifier(cascade_file)
        self._max_detect_hw = max_detect_hw

    def detect_image(self, image):
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        gray = cv2.equalizeHist(gray)
        cars = self._cascade.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=15, minSize=(60, 60))

        for (x, y, w, h) in cars:
            cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)

        return image

    def detect_video(self, video_path, start_frame, end_frame, ):

        cap = cv2.VideoCapture(video_path)
        cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
        org_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        org_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        h, w = get_hw_by_short_size(org_h, org_w, self._max_detect_hw)

        while (start_frame < end_frame):
            start_frame += 1
            ret, image = cap.read()
            if not ret: return

            resized_img = cv2.resize(image, (w, h), interpolation=cv2.INTER_CUBIC)
            result = self.detect_image(resized_img)
            cv2.imshow("Detect", result)
            cv2.waitKey(1)

if __name__ == "__main__":  
    car_cascade_lbp_21 = './train/xml/cascade.xml'
    video_path = "./test.mp4"
    start_frame = 0
    end_frame = 300
 
    detect = car_detector(car_cascade_lbp_21) 
    detect.detect_video(video_path, start_frame, end_frame)

總結

車輛檢測,在訓練階段:

HOG特徵
正樣本尺寸 30 30 30*30 ,訓練速度非常快,結果不收斂。
正樣本尺寸 64 64 64*64 ,訓練速度較快,結構收斂。
但是, OpenCV 3.x 中, CascadeClassifier方法不支援 HOG特徵。

HAAR特徵
正樣本尺寸 20 20 20*20 ,訓練速度非常慢,結果不收斂。

LBP特徵
正樣本尺寸 30 30 30*30 ,訓練速度較快,結果收斂。