作者:曾成訓(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 部落格、原文連結;責任編輯:賴佩萱)
- 【模型訓練】訓練馬賽克消除器 - 2020/04/27
- 【AI模型訓練】真假分不清!訓練假臉產生器 - 2020/04/13
- 【AI防疫DIY】臉部辨識+口罩偵測+紅外線測溫 - 2020/03/23
訂閱MakerPRO知識充電報
與40000位開發者一同掌握科技創新的技術資訊!
2020/03/01
老師您好:
拜讀您的貓臉辨識器,有依循您的只是做執行,我是用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