【影像辨識】訓練貓臉偵測器

作者:曾成訓(CH.TSENG)

OpenCV 使用的 Object detection 技術稱為 Cascade Classifier for Object Detection ,是一種屬於 boosted cascade of weak classifiers 的方法,也就是將數個弱分類器串聯起來再得出最佳的分類結果。其實最早整合到 OpenCV 並支援的分類特徵是哈爾特徵(Haar-like features),後來加入了 LBP ( Local Binary Pattern)以及 HOG( Histogram Of Gradient),不過可惜的是 HOG 在 3.x 後由於某些技術問題被取消了。

Boosting 的中心思想在於「三個臭皮匠勝過一個諸葛亮」,主要是將大量的弱分類器(分類效果僅比隨機好一點)逐步訓練成一個較強的分類器,透過對每個弱分類器分類錯誤的部份持續投入學習,最後形成一個超強的分類器。

舉例來說,我們熟知的 Spam mails 檢測,就是一種 Boosting 的概念,拆開,每一條 spam check rule 都是弱分類器,僅對一小部份的垃圾郵件有效,但是把數以百計的 rules 串連起來,便能打造一個滴水不漏的垃圾郵件防堵系統!

在機器學習中,有很多分類器便應用了 Boosting 的方法,例如 AdaBoost(Adaptive Boosting)、Gradient Tree Boosting、XGBoost 等, 而 OpenCV 內建的 Cascade Classifier for Object Detection,正是應用了AdaBoost。

新版 OpenCV 對於 Cascade Classifier 的支援

可惜的是,OpenCV 4.x 版後不再支援 Cascade Classifier 的訓練,因此目前在 4.x 版的 OpenCV 程式碼中,無法看見諸如 opencv_createsamples、opencv_traincascade 等程式,官方的說法是近幾年流行的 ML、DL 效果更佳,鑒於使用者日少,決定不再包入相關的訓練程式在原始碼中。

不過事實上目前還是有相當多的使用者對於 cascade classifier 有強烈的需求,因為它使用方便、偵測速度快,常讓人包容誤報率高和訓練複雜的缺點,因此有耳聞官方可能會在下一版本的 OpenCV 中再度支援。

訓練貓臉偵測器

我撰寫了一套工具程式可快速方便地進行 Cascade Classifier 的訓練,以下以貓臉偵測示範如何使用這個工具製作自己的 Cascade Classifier。

  • 搜集相片及標記

1. 先從網路上下載一些貓的相片,本例中我下載了 126 張。

(圖片來源:曾成訓提供)

2. 使用 labelImg 進行標記:由於貓臉區域不像人臉那麼明確,因此在框選時我選擇從兩眼外側(不包含耳朵)開始直到下巴的區域。

(圖片來源:曾成訓提供)

  • 準備 dataset

此步驟將產生訓練時需要的positives(正向圖片檔,即標記的貓臉)以及 negatives(負向圖片檔,即沒有貓臉的圖片)。

1. 所有相片及標記好的 label 檔分別置於 images 及 labels 的資料夾中。

2. 執行 1_labels_to_pos_neg_imgs.py:此程式的目的是將所有相片中的標記框取出,另外存到一個folder 下,這些圖片稱為 positives,圖片中不含標記框的其它區域則存到 neg_bg 資料夾中,這些與貓臉無關的圖片稱為 negatives。1_labels_to_pos_neg_imgs.py 的參數如下,您只要修改參數的內容即可:

查閱參數

#標記檔的path

xmlFolder = “H:\\working\\cascade_cat_face\\voc_dataset\\labels”

#圖片檔的path

imgFolder = “H:\\working\\cascade_cat_face\\voc_dataset\\images”

#要取出的標記名稱(class name)

labelName = “catface”

#專案目錄,所有產生的檔案或目錄皆會存於此

projFolder = “H:\\working\\cascade_cat_face\\cascade_training”

#訓練的圖片大小(建議不要太大)

outputSize = (54, 45)

#產生的訓練圖片類型

imageKeepType = “jpg”

#去除標記區域的圖片,是否要作為negative圖片?

generateNegativeSource = True

執行成功後,會產生以下兩個目錄:positives(正向圖片),含有貓臉標記的圖片;neg_bg(負向圖片),不含貓臉標記的圖片(我把有貓臉的區域以黑色色塊取代,就能作為負面圖片來使用)。

positives(圖片來源:曾成訓提供)

negatives(圖片來源:曾成訓提供)

3. 執行 2_generate-negatives.py:本程式使用 sliding window 的方式,將 neg_bg 資料夾下的相片切裁為指定大小的 negatives 圖片,並產生一個 negatives.info 檔案。

查閱參數

#專案目錄,所有產生的檔案或目錄皆會存於此

projFolder = “H:\\working\\cascade_cat_face\\cascade_training”

#sliding window移動距離

movePixels = 80

#sliding window時圖片依次的縮小比例

resizeScale = 0.5

#裁切出的圖片大小

negSize = (54, 45)

#載切後儲存的圖片格式

imageKeepType = “jpg”

#neg_bg folder下的圖片要不要先縮小為指定尺寸? 0–> keep the same

resize_org_w = 0

#要產生多少負向的圖片?

imagesCount = 8000

negatives 資料夾內容:

(圖片來源:曾成訓提供)

negatives.info檔案內容:

H:\working\cascade_cat_face\cascade_training\negatives\1580366317.81161742.jpg

H:\working\cascade_cat_face\cascade_training\negatives\1580366317.81961253.jpg

H:\working\cascade_cat_face\cascade_training\negatives\1580366317.83462724.jpg

H:\working\cascade_cat_face\cascade_training\negatives\1580366317.84162245.jpg

H:\working\cascade_cat_face\cascade_training\negatives\1580366317.8586116.jpg

H:\working\cascade_cat_face\cascade_training\negatives\1580366317.86569797.jpg

4. 執行 3_augmentation.py:本程式使用 augmentation 強化資料的方式產生更多正向圖片,新增加的圖片將放置於 aug_positives 資料夾下。

查閱參數

#專案目錄,所有產生的檔案或目錄皆會存於此

projFolder = “H:\\working\\cascade_cat_face\\cascade_training”

#產生的正向圖片大小

outputSize = (54, 45)

#產生的圖片格式

imageKeepType = “jpg”

#每一個正向圖片要產生出幾張新圖片?

numAugment = 3

#Augmentation的設定

aug_whitening = False

aug_rotation = 16

aug_w_shift = 0.1

aug_h_shift = 0.1

aug_shear = 0.1

aug_zoom = 0.05

aug_h_flip = True

aug_v_flip = False

aug_fillmode = “nearest”

產生的圖片如下,原本僅有 243 張,但透過 augmentation 增加了 929 張。

(圖片來源:曾成訓提供)

5. 執行 4_add_aug_positives_to_list.py:將前一步所產生的圖片放到 positives.info 檔案,其內容截錄如下。

positives/aug__0_4419.jpg 1 0 0 54 45

positives/aug__0_584.jpg 1 0 0 54 45

positives/aug__0_5614.jpg 1 0 0 54 45

positives/aug__0_402.jpg 1 0 0 54 45

positives/aug__0_7285.jpg 1 0 0 54 45

positives/aug__0_710.jpg 1 0 0 54 45

positives/aug__0_4390.jpg 1 0 0 54 45

最終待訓練用的資料夾及其下檔案如下:

(圖片來源:曾成訓提供)

  • 產生訓練用的 VEC 檔

由於 Cascade Classifier 不能直接讀取圖片檔,我們必須轉為 VEC 檔才能開始訓練。

1. 進入專案目錄:H:\working\cascade_cat_face\cascade_training

cd H:\working\cascade_cat_face\cascade_training

2. 執行 opencv_createsamples.exe

H:\opencv\build\x64\vc15\bin\opencv_createsamples.exe -info positives.info -vec samples.vec -w 54 -h 45 -num 1790

若出現如下方的 error message,表示某個圖檔有問題,建議直接從 positives.info 列表中刪除該檔案,例如下方第 229 行的圖檔有問題,直接刪除該行。

(圖片來源:曾成訓提供)

再執行一次 opencv_createsamples.exe 便可成功轉換,最後是如下畫面:

(圖片來源:曾成訓提供)

  • 開始訓練

1. 進入專案目錄:H:\working\cascade_cat_face\cascade_training

cd H:\working\cascade_cat_face\cascade_training

2. 執行下方的指令:

H:\opencv\build\x64\vc15\bin\opencv_traincascade.exe -data H:\working\cascade_cat_face\cascade_training -vec samples.vec -bg negatives.info -numPos 1700 -numNeg 7000 -numStages 8 -minHitRate 0.995 -maxFalseAlarmRate 0.3 -w 54 -h 45 -featureType LBP

(圖片來源:曾成訓提供)

3. 執行重點

(1)訓練 stange 的數目由 -numStages 指定。

(2)HR(Hit Rate,偵測到物件)、FA(False Alarm,錯誤偵測到物件,即誤判)此兩個值分別由 -minHitRate 和 -maxFalseAlarmRate 指定,訓練結果滿足這兩個值表示該 Stage 結束。

(3)-featureType可指定為HAAR 或 LBP,目前比較流行的是 LBP,訓練速度較快且偵測效果不亞於 HAAR。

(圖片來源:曾成訓提供)

(圖片來源:曾成訓提供)

(圖片來源:曾成訓提供)

上圖中可看出訓練時間相當冗長,但如果將 -featureType 改為 HAAR,則訓練時間會比 LBP 要多出好幾倍!

訓練結果(LBP)

這次測試總共標記了 126 張相片(約有 241 張貓臉正樣本),另外透過資料強化擴增到 1,794 張,再搭配 7,059 張負樣本進行了 OpenCV Cascade Classifier 的 LBP 訓練,最後訓練了 7 個階段,總訓練時間為 5 小時 36 分 41 秒。

我另外找了 11 張相片作為 test 圖片,使用訓練完成所產生的 cascade xml 測試其效果如下,若能繼續增加更多的貓臉正樣本,應該可提高辨識的效果。

  • 效果還不錯

(圖片來源:曾成訓提供)

(圖片來源:曾成訓提供)

(圖片來源:曾成訓提供)

  • 只偵測出部分

(圖片來源:曾成訓提供)

(圖片來源:曾成訓提供)

  • Bounding box 大小位置有待加強

(圖片來源:曾成訓提供)

(圖片來源:曾成訓提供)

  • False Alarm

(圖片來源:曾成訓提供)

(本文經作者同意轉載自 CH.TSENG 部落格原文連結;責任編輯:賴佩萱)

曾 成訓

Author: 曾 成訓

人到中年就像沒對準的描圖紙,一點一點的錯開,我只能當個Maker來使它復位。

Share This Post On

1 Comment

  1. 老師您好:
    拜讀您的貓臉辨識器,有依循您的只是做執行,我是用spyder3,
    導入第一支1_labels_to_pos_neg_imgs.py檔後出現以下錯誤訊息:

    File “C:/Users/chun chieh/Desktop/cascade_opencv_train-master/1_labels_to_pos_neg_imgs.py”, line 148, in
    saveROI(saveROIsPath, imgFolder, xmlfile, labelName, generateNegativeSource)

    File “C:/Users/chun chieh/Desktop/cascade_opencv_train-master/1_labels_to_pos_neg_imgs.py”, line 78, in saveROI
    image2 = image.copy()

    AttributeError: ‘NoneType’ object has no attribute ‘copy’

    可否煩請老師解惑,感恩老師指教
    我的EMAIL: ada6658@gmail.com

    Post a Reply

Submit a Comment

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *