ごはんと飲み物は紙一重

あんまり更新できてないです

【俺妹】大好きな黒猫をディープラーニングで認識しないわけがない【データ収集編】

タイトルにセンスが無いとかいうのはなしで

やっと必要最低限の情報を整えてブログ公開することができそうでしたので,ひとまず記事としてまとめることにしました.

大学2年になった夏休み・・・Pythonを独学し始めて1年,深層学習を独学し始めて半年,そろそろなにかしら自分の力でなにか結果を残したい!

「俺妹×深層学習・・・そうだ!黒猫をCNNで学習させて認識できるようなプログラムを作ろう!」

というありきたりで,n番煎じなネタではありますがやってみました!

実行環境

  • MacBookPro Retina 13 inch
  • Mac OS X El Capitan 10.11.6
  • pyenv × anaconda2-4.0.0

実行環境を整えるのに半年くらいかかった気はする,直にインストールすれば問題ないのですが,pyenvにtensorflowとかopencv2をインストールするのがだるかった思い出.

今回はbrewのインストールから記述するとか言うところは省いて環境は整っている状態から記事を書き始めることにしますね.

準備編

やっとこさ本編です.まずは学習には必要不可欠である,顔データの収集からしなければいけません. (顔データがその辺に落ちていればこの記事は必要ないことになりますからねw)

初めにGoogle先生

Python 顔認識」

で検索をかけるとすっごくOpenCVを推してくるみたいです.実際キャラクター認識やアイドル認識の記事を見てみるとOpenCVで顔認識をしているところはかなり見かけたので私も使おうってことで,

qiita.com

この記事を見ました.これ見る限りでは人物の画像を認識する記事だったので,アニメキャラクターを認識するようのカスケードファイルはないのかと探したところ,

ultraist.hatenablog.com

あるんですね.これが.先人様々ですねw

# -*- coding: utf-8 -*-

##
## AnimefaceDetection.py
##
## 学習したデータを用いて画像から分類をする.
##

import sys
sys.path.append("/usr/local/lib/python2.7/site-packages")
import cv2
import os.path

def detect(filename, cascade_file="lbpcascade_animeface.xml"):
    if not os.path.isfile(cascade_file):
        raise RuntimeError("%s: not found" % cascade_file)

    # カスケード分類器の特徴量を取得
    cascade = cv2.CascadeClassifier(cascade_file)

    # ファイル読み込み
    img = cv2.imread(filename)

    # グレースケール変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # ヒストグラム表示用のイメージ作成
    gray = cv2.equalizeHist(gray)

    # キャラクター認識の実行
    # scaleFactorは画像スケールにおける縮小量
    # minNeighborsは値が大きくなれば検出の信頼性が上がるが見逃してしまう場合も増える
    # 認識する最小サイズ
    faces = cascade.detectMultiScale(gray,
            # 認識用オプション
            scaleFactor = 1.1,
            minNeighbors = 1,
            minSize = (24,24))

    # 保存先のディレクトリ作成
    if len(faces) > 0 :
        path = os.path.splitext(filename)
        dir_path = path[0] + '_face'
        if os.path.isdir(dir_path) == False:
            os.mkdir(dir_path)


    # 結果を表示
    cv2.imshow("AnimeFaceDetect", image)

    print ('何かキーを押すと続行します')
    cv2.waitKey(0)

    # 実行結果を保存
    cv2.imwrite("detect.png", image)

# 引数判定
if len(sys.argv) != 2:
    sys.stderr.write("usage: detect.py <filename>\n")
    sys.exit(-1)

filename = sys.argv[1]
detect(filename)

上記の用にプログラムを作成すると, f:id:ST_ha1cyon:20161025183500p:plain

こんな感じでざっくりとアニメ顔を認識してデータを集めることが可能・・・

ですが!!

この調子で画像1枚1枚を集めて検出器にかけているようでは,俺の夏休みが終わってしまう!! 実際サンプルとしては主要キャラに1000枚くらいを想定していたので,画像を探しているだけで気が遠くなりそう・・・

そんなとき・・・

動画からフレームごとにキャプチャすれば画像集めの時間を短縮できるのでは・・・

幸運にも動画データはあったため,さっそくプログラムを改良.

# -*- coding: utf-8 -*-

##
## VideoAFDetection.py
##
## VideoAFDetection は先人の知恵の結晶をそのまま使いキャラクターを矩形抽出する.
## face_detectionでは人間の目と顔を認識することで実装していたためアニメ用にする
## 動画からキャラクターを抽出できるようにする.
##

import sys
sys.path.append("/usr/local/lib/python2.7/site-packages")
import cv2
import os.path

cascade_file = "lbpcascade_animeface.xml"

def detect(image):
    if not os.path.isfile(cascade_file):
        raise RuntimeError("%s: not found" % cascade_file)

    # カスケード分類器の特徴量を取得
    cascade = cv2.CascadeClassifier(cascade_file)

    # グレースケール変換
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # ヒストグラム表示用のイメージ作成
    gray = cv2.equalizeHist(gray)

    # キャラクター認識の実行
    # scaleFactorは画像スケールにおける縮小量
    # minNeighborsは値が大きくなれば検出の信頼性が上がるが見逃してしまう場合も増える
    # 認識する最小サイズ
    faces = cascade.detectMultiScale(gray,
            # 認識用オプション
            scaleFactor = 1.1,
            minNeighbors = 1,
            minSize = (24,24))

filename = sys.argv[1]
cap = cv2.VideoCapture(filename)

# 顔だけを切り出して保存する
framenum = 0
faceframenum = 0
color = (0,0,255)

# 保存先のディレクトリ作成
path = os.path.splitext(filename)
dir_path = path[0] + '_face'
if os.path.isdir(dir_path) == False:
    os.mkdir(dir_path)

while(cap.isOpened()):
    framenum += 1

    ret, image = cap.read()
    if not ret:
        break

    if framenum%50 == 0:
        faces = detect(image)
        if len(faces) == 0:
            continue # 認識結果がnullなら次のフレームへ

        for rect in faces:
            x = rect[0]
            y = rect[1]
            width = rect[2]
            height = rect[3]
            dst = image[y:y + height, x:x + width]
            resized_dst = cv2.resize(dst,(64,64)) # サイズを64*64に調整
            if str(faceframenum) < 10:
                cv2.imwrite(dir_path  + '/' + "00" + str(faceframenum) + ".jpg", resized_dst)
            elif 10 < str(faceframenum) < 100:
                cv2.imwrite(dir_path + '/' + "0" + str(faceframenum) + ".jpg", resized_dst)
            else:
                cv2.imwrite(dir_path + '/' + str(faceframenum) + ".jpg", resized_dst)
            faceframenum += 1

cap.release()

このプログラムを動画ファイルに対して実行すると,1話分のキャラクター画像を取り込んでくれるので大体500枚から800枚くらいの顔データを取得することができました.動画からだとモブキャラも同じく顔として認識するので,学習に使用しない顔データを除くと1話あたり大体400枚位のデータが集まりました.

記事が長くなってもいけないのでこのパートはここまでということで.次回では【データ準備編】と題しまして,集めた顔データを整理して学習に使用するための形に持っていくところまでを記事にしたいと考えています.