こんにちは!アイデミー研修生の川内と申します。
突然ですが、みなさんカワウソってご存知ですか?? 可愛いですね〜〜。
よく犬と猫どっち派とか聞かれますが僕は断然カワウソ派です。
OpenCVというのを使うとデフォルトで作成されているモデルを用いて人間の顔が検出することが出来ます。OpenCVについては下記のリンクをご覧ください。
機械学習のためのOpenCV入門
OpenCVで物体検出器を作成① 基礎知識【開発会社プロフェッサ】
人の顔の画像の特徴量を抽出することにより学習するのですが、学習させるモデルにおいてはHaar-like特徴というのを用いています。Haar-like特徴は、簡単に言うと画像の明暗差により特徴を捉えます。例えば人間顔で言えば目は黒く、目元は明るいといった特徴をたくさん取ることで、人間の顔の特徴全体を捉える感じです。
Haar-likeについて(英語で書かれています)
Face Detection using Haar Cascades — OpenCV 3.0.0-dev documentation
和訳されたサイトもありました。
Haar Cascadesを使った顔検出 — OpenCV-Python Tutorials 1 documentation
ものは試しで早速やってみましょう
社長の石川です。良い顔していますね。
import cv2
cascade_path = "/usr/local/opt/opencv/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml"
image_path = "sample.jpg"
color = (255, 255, 255)
image = cv2.imread(image_path)
cascade = cv2.CascadeClassifier(cascade_path)
facerect = cascade.detectMultiScale(image, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1))
if len(facerect) > 0:
for rect in facerect:
cv2.rectangle(image, tuple(rect[0:2]),tuple(rect[0:2]+rect[2:4]), color, thickness=2)
cv2.imwrite("face_detected.jpg", image)
さて結果がこちらになります。
うまく出来ていますね。こんな感じでカワウソの画像を認識して顔を四角で囲ってみたくなりました。誰も使いません。世界一いらない人工知能と言っても過言ではないでしょう。ただ、カワウソが好きという理由だけで作ってみようと思い立ちました。下記のリンクの方が猫の顔検出モデルを作成されていたのでそれを参考に作ってみるという方針を立てました。画像さえ集められればモデル構築出来そうということが分かりました。
ねこと画像処理 part 2 – 猫検出 (モデル配布) « Rest Term
画像収集
さて、方針は決まって後は画像を集めるのですがここが一番の肝です。今回はFlickrという画像共有サイトのAPIを利用して画像を集めました。利用方法については下記を参考にしました。
Flickr APIを使って画像ファイルをダウンロードする
本来であれば正解データ7000枚、不正解データ3000枚ほど必要なのですがflickr APIではカワウソの画像400枚ほどしか集められませんでした。こうやればたくさん集められるよ!!というのを知っている方がいらっしゃれば教えてくださいm(_ _)m。不正解データは同様にしてパンダの画像を集めました。
正解データの例
不正解データの例
以下に画像取得の時に利用したスクリプトを載せておきます
import os
import time
import traceback
import flickrapi
from urllib.request import urlretrieve
import sys
from retry import retry
flickr_api_key = ""
secret_key = ""
keyword = sys.argv[1]
@retry()
def get_photos(url, filepath):
urlretrieve(url, filepath)
time.sleep(1)
if __name__ == '__main__':
flicker = flickrapi.FlickrAPI(flickr_api_key, secret_key, format='parsed-json')
response = flicker.photos.search(
text=keyword,
per_page=1000,
media='photos',
sort='relevance',
safe_search=1,
extras='url_n,license'
)
photos = response['photos']
try:
if not os.path.exists('./image-data/' + keyword):
os.mkdir('./image-data/' + keyword)
for photo in photos['photo']:
try:
url_q = photo['url_n']
filepath = './image-data/' + keyword + '/' + keyword + "-" + photo['id'] + '.jpg'
get_photos(url_q, filepath)
except KeyError:
print("error!!!")
except Exception as e:
traceback.print_exc()
正解データのラベル付け
さて、なんとか画像を集めることが出来たのですが、正解データの画像のどの座標の位置にカワウソの顔があるかを指定していかなければなりません。githubにブラウザで画像データの座標を記録出来るプログラムを作られてる方がいたのでそちらを利用させていただくことにしました。 github.com
こんな感じで地道にカワウソの顔を囲っていきます。
画像が赤い四角形で囲まれた状態でNEXTボタンを押すと正解データに座標と共に分類され、何もない状態で押すと不正解データに分類されます。
しかし、カワウソの画像でも横顔のデータなどは正解データとしては不適切でそういった画像の時はSKIPボタンでどちらにも分類されないようにしました。集めたカワウソの画像の中には横顔だったり、二次元のキャラクターのカワウソも含まれていたので正解データに分類された画像は5割程となりました。
正解データとはならない写真の例
正解データ、不正解データに分類するのにものすごく時間かかりました・・・・。正解、不正解合わせて800枚ほどのデータが集まったのですが3時間くらいひたすら画像とにらめっこして分類してました。macのトラックパッドにクリックしすぎて指がおかしくなりましたが、ありとあらゆるカワウソの写真を見れて幸せな時間でもありました笑。
正解のデータのテキストファイルです。【画像ファイル名 対象の物体の数 x座標 y座標 width height】 の順に記録されます。
info.dat
static/img/otter-6814063158.jpg 1 50 24 66 50
static/img/otter-4494710692.jpg 1 174 30 52 38
static/img/otter-8885141162.jpg 1 78 42 67 60
static/img/otter-25450661078.jpg 1 102 36 69 49
static/img/otter-14377745688.jpg 1 38 46 152 97
static/img/otter-32616826780.jpg 1 44 22 133 101
static/img/otter-26964747057.jpg 1 57 55 37 24
static/img/otter-27695436712.jpg 1 64 26 84 67
static/img/otter-5105713030.jpg 1 113 35 75 54
static/img/otter-7394889242.jpg 1 35 73 51 43
static/img/otter-27807883499.jpg 1 89 31 77 50
static/img/otter-33148797350.jpg 1 99 42 37 36
static/img/otter-7941157504.jpg 1 126 14 58 41
static/img/otter-32646878413.jpg 1 49 89 35 27
static/img/otter-33520353216.jpg 1 110 49 52 46
.
.
.
モデル構築
そんなこんなで苦労して分類したデータをOpencvを用いて学習させてみました。まずOpenCVに認識してもらうために、正解データをバイナリファイルに変換する処理を行います。
opencv_createsamples -info info.dat -vec kawauso.vec -num 175
numはサンプル数です。学習させるために以下のコマンドを実行します。
opencv_traincascade -data kawauso/ -vec kawauso.vec -bg bg.txt -numPos 157 -numNeg 376 -featureType HAAR -mode ALL
- data モデルの保存先です。kawausoというディレクトリを作成しておきました。
- numPos 正解データの要素数を指定しているのですが、サンプル数×0.9くらいがいいらしいです。
OpenCVのtraincascadeのnumPos引数はvecファイル内のサンプル数より少ない数を設定すること – takminの書きっぱなし備忘録
- featureType 機械学習の特徴量を指定しています。先ほど述べたHaar-Like特徴を用いていました。
検証
構築したモデルを使用してカワウソの顔が検出されるか実験してみました。
テスト画像
検証結果 なかなか良さそうです。他の画像でも検証してみました。
いい感じに検出されていますね。
友人の飼ってる猫。くろまるって言います。
カワウソ検出器では検出されませんでした。
軍団カワウソ。うーん。やはり改善の余地がありそうです。
ラッコです。顔が似ているのか検出されました笑
検証の時に使用したスクリプトです。
import sys
import cv2 as cv
def detect(imagefilename, cascadefilename):
srcimg = cv.imread(imagefilename)
if srcimg is None:
print('cannot load image')
sys.exit(-1)
dstimg = srcimg.copy()
cascade = cv.CascadeClassifier(cascadefilename)
if cascade.empty():
print('cannnot load cascade file')
sys.exit(-1)
objects = cascade.detectMultiScale(srcimg, 1.1, 3)
for (x, y, w, h) in objects:
print(x, y, w, h)
cv.rectangle(dstimg, (x, y), (x + w, y + h), (0, 0, 255), 2)
return dstimg
if __name__ == '__main__':
result = detect('otter1.jpg', './kawauso/cascade.xml')
cv.imwrite('otter1_result.jpg', result)
感想
教師データをもう少し用意できたらよかったです。
機械学習は前処理が8割なんてもんじゃない。99%が前処理といっても過言じゃないと思いました。便利なライブラリやツールがある中で泥臭い作業こそ機械学習では必要なのだと体感しました。 こんなやり方あるよというのがありましたらぜひ教えてください!!