写真から特定の種類の物体を検出して切り出したい。(車とか魚とか人とか..)
OpenCVでできそうか調べる。
OpenCVでの物体検出の方法の確認
参考:https://www.tech-tech.xyz/haar-cascade.html
3パターンほどやり方はあるようで、
-
テンプレートマッチング(パターンマッチング)
画像をそのまま比較して、類似度が高い部分を抽出してくれる。画像の回転やスケール変化に弱いらしい。類似度も返してくれるのでそこから判断するしかないか。 -
特徴点抽出
テンプレートパターンと違って、サイズ違いや回転したものも抽出出来る。1と同様”それらしきもの”の抽出は難しい。コインとか特定の絵画とか、微妙な違いとかが存在しない固有のものの抽出にならこれでできそう。 -
カスケード分類器を利用
事前に数百の学習用画像ファイルを読み込んで学習したカスケードファイルを用いて、画像の中からそれらしきものを抽出する。顔や人、上半身、目とか。配布済みのファイルもあるので、顔などならなら学習なしで使える。
カスケードファイル: https://github.com/opencv/opencv/tree/master/data/haarcascades
参考:http://opencv.jp/opencv-2.2/c/objdetect_cascade_classification.html
自分がやりたい、多少曖昧なものの検出はカスケード分類器が必要な様子。
画像が特定されているならテンプレートパターン。抽出対象のオブジェクトが特定されているなら特徴点抽出、それ以外はカスケード分類器という感じでしょうか。
※ ちなみに、文字認識(OCR)は「Tesseract」というものを使うのが定番らしい。。
カスケード分類器の作成
参考:https://www.pro-s.co.jp/blog/system/opencv/6202
ほぼ参考先に書いてある内容でいけそう..数が必要な学習用のファイルもユーティリティツールで増やすことが可能な様子。※精度はよくわからないけど...
opencv_createsamples -img ./pos/1/dist/spoon1.png -vec ./vec/1.vec -num 10 -bgthresh 0
opencv_createsamples -img ./pos/1/src/spoon1.png -vec ./vec/1.vec -num 500 -bgthresh 0 -maxidev 40 -maxxangle 0.5 -maxyangle 1.5 -maxzangle 1.5
opencv_traincascade 実行中に 学習中に以下のような出力が各ステージで出力されるのだけど、それぞれどういう意味かよくわからん。
opencv_traincascade -data ./cascade -vec ./vec/spoon1.vec -bg ./neg/jpg/nglist2.t
xt -numPos 50 -numNeg 50 -stagenumStages 12
===== TRAINING 0-stage =====
<BEGIN
POS count : consumed 200 : 200
NEG count : acceptanceRatio 1000 : 1
Precalculation time: 1
+----+---------+---------+
| N | HR | FA |
+----+---------+---------+
| 1| 1| 1|
+----+---------+---------+
| 2| 1| 0.016|
+----+---------+---------+
END>
Training until now has taken 0 days 0 hours 0 minutes 3 seconds.
遭遇したエラー1
入力ファイルを用意してopencv_traincascade ...
を実行したら以下のエラーが発生。
Train dataset for temp stage can not be filled. Branch training terminated.
私の場合は、ネガティブファイルのファイル名だけで、パスがたりなかったのでだめでした。ファイルに実行箇所からの相対パスも挿入して修正.
ls -v | grep jpg > nglist.txt
# 行頭に./neg/jpg/を追加
sed "s#^#./neg/jpg/#" nglist.txt >nglist2.txt
遭遇したエラー2
Required leaf false alarm rate achieved. Branch training terminated.
うーん、これという答えはないけどある程度ステージが進んでデフォルトの20回を達成できないという所が問題なので、ステージの指定をクリア可能な所まで下げて実施することにした。
サンプル数の増減でクリアできるステージ数もかわるので、そこも影響を受けてそう。
-numStages
デフォルト20。10ぐらいにしとく。
何回も出る。これは意味を理解しないとだみだ..
https://taktak.jp/2016/08/26/1618
遭遇したエラー3
エラーではないのだけどトレーニング opencv_traincascade
が全く進まない...
引数 -numNegを省略しちゃったら大分おそくなってしまった。厳密に何をしてくれているのかわからないけど、デフォルト1000
遭遇したエラー4
HOG cascade is not supported in 3.0 in function 'read'
traingのfeatureTypeでHOGを指定してカスケード分類器を作成したのだが..いざopenCVで読み込んで利用しようとしたら上記のエラー。HOGは対応しなくなったそうだ。
実行用スクリプト
import cv2
def main():
# 入力画像の読み込み(テスト用画像ファイル)
img = cv2.imread("検出対象ファイル名")
# カスケード型識別器(自作した分類器)
cascade = cv2.CascadeClassifier("./cascade/cascade.xml") #
# face→ballに変更(そのままでもいいですけど)
objects = cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors=3, minSize=(0, 0))
# 顔領域を赤色の矩形で囲む
for (x, y, w, h) in objects:
cv2.rectangle(img, (x, y), (x + w, y+h), (0,0,200), 3)
# 結果画像を保存
cv2.imwrite("result.png",img)
main()
しかし検出されない..
入力データがよくないのかな...
画像を増やす
● 元のイメージファイルと背景画像を組み合わせてサンプル画像を生成する
opencv_createsamples -info ./pos/info/1.txt -img ./pos/spoon1.png -bg ./neg/jpg/nglist.txt -num 10 -w 24 -h 24 -show -bgthresh 200
※-infoで指定した場所に生成されたファイルリストが出力。複数作って一つのファイルにマージして、vector作成のインプットにする。
※この時nglistの中身はファイル名だけで大丈夫。ファイルのパスは不要。
⇒【注意】出力ファイルリスト内に対象の物体がある位置を記載しないといけないのだが、そこは自動調整してくれなくて、常に画像全体のサイズが指定されてしまう。この方法で行く場合は背景と入力のサイズを全く同じにして用意しておかないといけない..
※パラメータ:bgcolorはサンプルで作成した画像の背景色
※ パラメータ:bgthreshはその値以下の切り落とす。数値を渡すので、0-255と思われる。200とかにすると明るい部分しか抽出されないというイメージ。明暗を逆に抽出したい場合は 「-inv」パラメータで反転させる...のかな?
●ファイルリストからベクターファイルを作成する
opencv_createsamples -info ./pos/2/dist/poslist.txt -vec ./vec/3.vec -num 1700
●トレーニング
opencv_traincascade -data ./cascade_harrlike/3/ -vec ./vec/3.vec -bg ./neg/jpg/nglist2.txt -numPos 1500 -numNeg 1500 -numStages 10 -precalcValBufSize 2024 -precalcIdxBufSize 2024
まだ検出されない..
ここからはおそらく入力データをちょっと、ちゃんと揃えてやるしかないのかなという印象...続く。。