LoginSignup
8
5

More than 1 year has passed since last update.

OpenCVのカスケード分類器を自作して自動車検出を行う

Posted at

1. はじめに

OpenCVでは、人の顔や目等を検出するカスケード分類器があらかじめ用意されており、
それを用いると簡単に画像上の人の顔や目等の検出が可能です。

またOpenCVで用意されている実行ファイルとサンプル画像を用意することで、
任意の対象のカスケード分類器が作成できるとのことなので、
今回はCIFAR10の自動車画像を用いて、自動車検出を試みました。

*カスケード型識別器(Cascade detector)は、複数の強識別器を連結した識別器です。
カスケード型識別器では、各強識別器により順番に判別を行います。
(cascade:(連続した)小さな滝)

2. 前準備

2-1. 実行ファイルの用意

まずOpenCVの公式ページから、必要なファイルのダウンロードを行います。

OpenCV公式サイト

カスケード分類器の作成に必要なツールは、3系にのみ含まれており、
4系には含まれていないため、今回は以下のバージョンをダウンロードしました。

image.png

ダウンロードしたファイルをダウンロードフォルダに展開すると、
以下のフォルダに必要なファイルが格納されています。

C:\Users\USERNAME\Downloads\opencv\build\x64\vc15\bin

image.png

必要なファイルは以下の3つです。

・opencv_createsamples.exe
・opencv_traincascade.exe
・opencv_world3416.dll

今回はデスクトップ下にopencv-cascadeのフォルダを作成し、
そこに必要なファイルをコピーしました。

C:\Users\USERNAME\Desktop\opencv\cascade
image.png

また同じ階層に以下の3つのフォルダを作成しています。
cascade - 作成されたカスケード分類器が保存される
ng - NG画像を保存する。
ok - OK画像を保存する。

2-2. 画像の用意

OK画像としては、CIFAR10の画像より、自動車画像を5000枚用意しました。
NG画像としては、CIFAR10の画像より、自動車画像以外を3000枚用意しました。

まず以下のリンクより、「CIFAR-10 python version」をダウンロードします。

CIFAR10

ダウンロードしたファイルを解凍すると、data_batch_1からdata_batch_5までが格納されています。
そのファイルからOK画像とNG画像の生成は以下のコードによって実行しました。

import numpy as np
import cv2

def unpickle(file):
    import pickle
    with open(file, 'rb') as fo:
        dict = pickle.load(fo, encoding='bytes')
    return dict

def preprocess(dict):
    image = dict[b'data']
    image = image.reshape(-1,32,32,3,order ='F') #order指定で高い次元から埋める
    image = image.transpose(0,2,1,3) #列番号0123 → 0213に変更
    label = dict[b'labels']
    return image,label

X_train, Y_train = preprocess(unpickle("cifar10/data_batch_1"))
for i in range(4):
    X, Y =preprocess(unpickle("cifar10/data_batch_"+ str(i+2)))
    X_train = np.concatenate([X_train,X])
    Y_train = np.concatenate([Y_train,Y])

X_train = np.array(X_train)
Y_train = np.array(Y_train)

X_car = X_train[Y_train == 1] #carのlabelは1
X_notcar = X_train[Y_train != 1]

#ok画像
for i in range(5000):
    filename = 'C:/Users/USERNAME/Desktop/opencv/cascade/ok/{:0>4d}.jpg'.format(i)    
    cv2.imwrite(filename, X_car[i])

#ng画像
for i in range(3000):
    filename = 'C:/Users/USERNAME/Desktop/opencv/cascade/ng/{:0>4d}.jpg'.format(i)    
    cv2.imwrite(filename, X_notcar[i])

okフォルダ
image.png

ngフォルダ
image.png

2-3. 画像リストファイルの作成

次に、画像リストファイルを作成します。

まずコマンドプロンプトで、okフォルダに移動し、以下のコマンドを実行します。

dir *.jpg /b > poslist.txt

poslistはファイル名と正解の対象物の数と位置を記入する必要があります。

poslist.txt
0000.jpg 1 0 0 32 32 ←(ファイル名/対象物の数/x軸開始位置/Y軸開始位置/幅/高さ)
0001.jpg 1 0 0 32 32
0002.jpg 1 0 0 32 32

次にコマンドプロンプトで、ngフォルダに移動し、以下のコマンドを実行します。

dir *.jpg /b > neglist.txt

neglistはフルパスで記載します。

neglist.txt
C:\Users\USERNAME\Desktop\opencv\cascade\ng\0000.jpg
C:\Users\USERNAME\Desktop\opencv\cascade\ng\0001.jpg
C:\Users\USERNAME\Desktop\opencv\cascade\ng\0002.jpg

2-4. ベクトルファイルの作成

最後に、poslistよりベクトルファイルを作成します。
コマンドラインで、desktop-opencv-cascadeファルダに移動し、
以下のコマンドを実行します。

opencv_createsamples -info ./ok/poslist.txt -vec ./ok/positive.vec -num 5000 -w 32 -h 32 -show
オプション名 用途 パラメータ
-info 正解画像リストファイル ok/poslist.txt
-vec ベクトルファイル保存場所 ok/positive.vec
-num 画像の数 5000
-w ベクトルの幅 32
-h ベクトルの高さ 32
-show 画像ファイルを見れる -

今回は画像ファイルを5000枚分用意しましたが、
1枚の画像ファイルを複製して学習ファイルを作成する方法も用意されています。

opencv_createsamples.exe -img ./ok/0000.jpg -num 1000 -vec ./ok/positive.vec -show
オプション名 用途 パラメータ
-img 正解画像 ok/0000.jpg
-vec ベクトルファイル保存場所 ok/positive.vec
-num 画像の数 1000
-show 画像ファイルを見れる -

用意するファイルは以上です。

3 カスケード分類器の作成

カスケード分類器の作成を行います。
コマンドラインで、desktop-opencv-cascadeファルダに移動し、
以下のコマンドを実行します。

opencv_traincascade.exe -data cascade -vec ok/positive.vec -bg ng/neglist.txt -numPos 4000 -numNeg 3000 -w 32 -h 32
オプション名 用途 パラメータ
-data 作成されたカスケード分類器が保存されるファルダ cascade
-vec ベクトルファイル ok/positive.vec
-numPos 正解画像数 4000
-numNeg 不正解画像数 3000
-w ベクトルの幅 32
-h ベクトルの高さ 32

正解画像数は用意した画像するより少なくすると良いとのことで、
今回は4000枚を指定しました。

正常に実行されると以下の表示が出ます。

cascadeDirName: cascade
vecFileName: ok/positive.vec
bgFileName: ng/neglist.txt
numPos: 4000
numNeg: 3000
numStages: 20
precalcValBufSize[Mb] : 1024
precalcIdxBufSize[Mb] : 1024
acceptanceRatioBreakValue : -1
stageType: BOOST
featureType: HAAR
sampleWidth: 32
sampleHeight: 32
boostType: GAB
minHitRate: 0.995
maxFalseAlarmRate: 0.5
weightTrimRate: 0.95
maxDepth: 1
maxWeakCount: 100
mode: BASIC
Number of unique features given windowSize [32,32] : 510112

===== TRAINING 0-stage =====
<BEGIN
POS count : consumed   4000 : 4000
NEG count : acceptanceRatio    3000 : 1
Precalculation time: 66.801
+----+---------+---------+
|  N |    HR   |    FA   |
+----+---------+---------+
|   1|        1|        1|
+----+---------+---------+
|   2|        1|        1|
+----+---------+---------+
|   3|        1|        1|
+----+---------+---------+
|   4|  0.99625| 0.879667|
+----+---------+---------+
|   5|   0.9955| 0.848333|

・・・
・・・
===== TRAINING 10-stage =====
<BEGIN
POS count : consumed   4000 : 4206
NEG count : acceptanceRatio    0 : 0
Required leaf false alarm rate achieved. Branch training terminated.

4. 結果の確認

上段に自動車、下段にそれ以外を並べた画像を用意しました。

test.png

この画像に対し、カスケード分類器を使って自動車の検出を行います。

import cv2

try:
    img = cv2.imread('C:/temp/test.png')

    if img is None:
        print('ファイルを読み込めません')
        import sys
        sys.exit()

    cascade = cv2.CascadeClassifier(
        r'C:/Users/USERNAME/Desktop/opencv4/cascade/cascade/cascade.xml')
    carrect = cascade.detectMultiScale(img)

    if len(carrect) > 0:
        for rect in carrect:
            print(rect)
            cv2.rectangle(img, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]),
                (0, 0, 255), thickness=2)
    else:
        print('no object')

    cv2.imwrite('c:/temp/dobj.png', img)
    cv2.imshow('img', img)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

except:
    import sys
    print("Error:", sys.exc_info()[0])
    print(sys.exc_info()[1])
    import traceback
    print(traceback.format_tb(sys.exc_info()[2]))

結果は以下の通りです。

dobj.jpg

うまくいっていないことが分かります。。。

5. エラーコード集

自分が出会ったエラーコードを列挙します。

前に作成されたカスケード分類器が、保存先のフォルダに入っていると以下の警告が出ます。
前回と違う設定となる場合は、一度保存先のフォルダ内のファイルを削除してください。

Training parameters are pre-loaded from the parameter file in data folder!
Please empty this folder if you want to use a NEW set of training parameters.

ベクトルファイルの幅/高さとカスケード分類器作成ファイル実行時に指定する幅/高さが異なると
以下のエラーがでます。

OpenCV: terminate handler is called! The last OpenCV error is:
OpenCV(3.4.16) Error: Assertion failed (_img.rows * _img.cols == vecSize) in CvCascadeImageReader::PosReader::get, file C:\build\3_4_winpack-build-win64-vc15\opencv\apps\traincascade\imagestorage.cpp, line 153

カスケードファイル実行時に、ベクトルファイルの画像数より多い画像数を指定すると
以下のエラーが出ます。

OpenCV: terminate handler is called! The last OpenCV error is:
OpenCV(3.4.16) Error: Bad argument (> Can not get new positive sample. The most possible reason is insufficient count of samples in given vec-file.
> ) in CvCascadeImageReader::PosReader::get, file C:\build\3_4_winpack-build-win64-vc15\opencv\apps\traincascade\imagestorage.cpp, line 158

6. まとめ

今回は結果がうまくいきません出来したが、やり方の備忘録として記録しました。
もう少し結果改善出来たらまた更新しようと思います。

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5