Help us understand the problem. What is going on with this article?

『animeface-2009』をPythonで使うための悪あがき & PASCAL VOC形式のXMLファイルへ出力する関数の実装

animeface2009_and_python.png

Pythonでアニメの画像からキャラクターの顔を検出したい場合、nagadomiさんが実装されたlbpcascade_animeface使うケースをしばしば見かけます。このlbpcascade_animefaceは顔のバウンディングボックスを推定するモジュールです。

また、前述のnagadomiさんが実装されたモジュールであるanimeface-2009は、顔だけでなく、顔のパーツ(目、鼻、口、あご)のバウンディングボックスも検出可能1です。ただし、こちらはRubyのみバインディングレベルのAPIが提供されています。

lbpcascade_animefaceのREADME.mdによると、「検出精度はanimeface-2009の方が高い」という旨が書かれています。したがって、ぜひともPythonで使いたいものです。

『animeface-2009』をPythonから呼び出す関数

今回実装したもののGitリポジトリはこちらです。

Ruby用に実装されたC言語のコードをPython用に書き直すのは、私のようなクソザコエンジニアには簡単なことではありません。
そこで、悪あがきの策ではありますが、Pythonのsubprocessモジュールでシェルを介してRubyを起動し、animeface-2009のRubyスクリプトを実行するPythonの関数を実装しました。

もちろん、このコード単体で顔検出できるわけではありません。事前準備が必要です。ディレクトリの構成を上述のGitリポジトリのようにし、また、animeface-2009をビルドする必要があります2

# a poor python caller of animeface-2009
# call rb module via shell by using subprocess...

import subprocess

import sys
import json
from pathlib import Path

this_file_dir = (Path(__file__).resolve()).parent


def detect_animeface(im_path):

    im_path = Path(im_path).resolve()
    assert im_path.exists()

    ruby_script_path = this_file_dir / 'animeface-2009/animeface-ruby/sample.rb'
    ret = subprocess.check_output(["ruby", str(ruby_script_path), str(im_path)]).decode('utf-8')

    ret = ret.replace("=>", ":")
    ret = ret.replace(">", "\"")
    ret = ret.replace("#", "\"")
    list_ = json.loads(ret)

    return list_

RubyからPythonへデータは直接渡していません。検出したバウンディングボックス情報を標準出力した後、Python側でその文字列をJSONフォーマットになるようになんとか整形し、辞書型にしています3
関数のインタフェースですが、入力は画像パス、返り値は検出結果を下記のような辞書のリストを返します。一つの顔検出結果が一つの辞書になっています。

[{'chin': {'x': 228, 'y': 266},
  'eyes': {'left': {'colors': ['<Magick::Pixel:0x00007ff93e969148',
                               '<Magick::Pixel:0x00007ff93e968e28',
                               '<Magick::Pixel:0x00007ff93e968d88',
                               '<Magick::Pixel:0x00007ff93e968bf8'],
                    'height': 31,
                    'width': 39,
                    'x': 222,
                    'y': 181},
           'right': {'colors': ['<Magick::Pixel:0x00007ff93e968040',
                                '<Magick::Pixel:0x00007ff93e968018',
                                '<Magick::Pixel:0x00007ff93e968180',
                                '<Magick::Pixel:0x00007ff93e9681a8'],
                     'height': 28,
                     'width': 31,
                     'x': 165,
                     'y': 202}},
  'face': {'height': 127, 'width': 127, 'x': 158, 'y': 158},
  'hair_color': '<Magick::Pixel:0x00007ff93e969cb0',
  'likelihood': 1.0,
  'mouth': {'height': 12, 'width': 25, 'x': 210, 'y': 243},
  'nose': {'x': 207, 'y': 233},
  'skin_color': '<Magick::Pixel:0x00007ff93e96a020'},
 {'chin': {'x': 379, 'y': 243},
  'eyes': {'left': {'colors': ['<Magick::Pixel:0x00007ff93e96b6a0',
                               '<Magick::Pixel:0x00007ff93e96b8d0',
                               '<Magick::Pixel:0x00007ff93e96b9e8',
                               '<Magick::Pixel:0x00007ff93e96bab0'],
                    'height': 29,
                    'width': 32,
                    'x': 418,
                    'y': 177},
           'right': {'colors': ['<Magick::Pixel:0x00007ff93e963568',
                                '<Magick::Pixel:0x00007ff93e963478',
                                '<Magick::Pixel:0x00007ff93e963298',
                                '<Magick::Pixel:0x00007ff93e9631a8'],
                     'height': 31,
                     'width': 39,
                     'x': 354,
                     'y': 157}},
  'face': {'height': 139, 'width': 139, 'x': 329, 'y': 121},
  'hair_color': '<Magick::Pixel:0x00007ff93e96ab38',
  'likelihood': 1.0,
  'mouth': {'height': 12, 'width': 20, 'x': 383, 'y': 218},
  'nose': {'x': 401, 'y': 205},
  'skin_color': '<Magick::Pixel:0x00007ff93e96a7c8'}]

ちなみに、Pythonのファイルをスクリプト実行すると、検出結果を描画した画像を出力します。

result_image.png

検出結果をPASCAL VOC形式のXMLファイルへ出力するモジュール

上述の関数を使って、アニメ顔(パーツ)検出をするのでもよいですが、すべての顔を検出できるわけではありません。
検出性能のスケーラビリティの観点から、DNNベースの検出器への移行も視野に入れます。

そこで、animeface-2009の検出結果を用いて、DNNを学習するための物体検出データセットを作る関数を実装しました。
データセットのフォーマットは定番のPASCAL VOCにしました。

$ python animeface_result2xml.py [検出対象の画像のディレクトリ] [XMLファイルを出力するディレクトリ] [作成したXMLファイルリストのテキストファイルパス]

予めディレクトリ内に検出したい画像をまとめておき(サブディレクトリ構成可)、XMLファイルを出力するディレクトリへのパスと、作成したファイルの一覧を示すテキストファイルを指定します。

ちなみに、XMLファイルは下記のようになります。

<annotation>
    <folder>folder_name</folder>
    <filename>img_file_path</filename>
    <path>/path/to/dummy</path>
    <source>
    <database>Unknown</database>
    </source>
    <size>
        <width>600</width>
        <height>600</height>
        <depth>3</depth>
    </size>
    <segmented>0</segmented>
    <object>
        <name>face</name>
        <pose>Unspecified</pose>
        <truncated>1</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>158</xmin>
            <ymin>158</ymin>
            <xmax>285</xmax>
            <ymax>285</ymax>
        </bndbox>
    </object><object>
        <name>right_eye</name>
        <pose>Unspecified</pose>
        <truncated>1</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>165</xmin>
            <ymin>202</ymin>
            <xmax>196</xmax>
            <ymax>230</ymax>
        </bndbox>
...

ツールを使ったラベルの確認

XMLファイルを編集する時は、アノテーションツールであるLabelImgを使用するとよいと思います。
推定したバウンディングボックスをグラフィカルに確認でき、誤っている場所に対してはその場で修正もできます。

labelimg.png

後は、作ったXMLを、ディープラーニングフレームワークのデータローダにパースさせ、物体検出DNNモデルを学習するという流れになります。

おわりに

アニメの顔パーツ検出を行うRubyのモジュールであるanimeface-2009を何とかPythonで扱うためのdirtyな関数を実装しました。また、その検出結果をPascal VOC形式のXMLファイルにする関数を実装しました。
これを使って、アニメ機械学習の応用可能性の幅を広げていきたいです。

なお、本記事ではDNNでアニメの物体検出をするにはアノテーションラベルが必要という立場をとりましたが、やや面倒と思われる方もいらっしゃるかもしれません。
最近kanosawaさんが、物体検出モデルの学習にDomain Adaptationテクニックを使った記事を公開されました。これは、アニメ顔のバウンディングボックスのアノテーションなしで物体検出モデル(Faster RCNN)の学習ができるので、ご興味があればご確認いただければと思います。
- 高精度なアニメ顔検出をアノテーションなし(Domain Adaptation)で実現してみた


  1. ただし、鼻とあごのバウンディングボックスは1x1ピクセルですので、ランドマークと呼んだ方がいいかもしれません。 

  2. オリジナルのanimeface-2009は複数の環境でビルドが難しかったので、私が変更を加えたforkリポジトリを使用しています。こちらのリポジトリのREADME.mdに、ビルド時に必要な手順を追記していますので併せてご確認いただけると幸いです。 

  3. 一部、不要な要素(Rubyのオブジェクトを表す文字列)が含まれてしまっていますが、後処理が面倒だったので、そのまま出力するようにしています。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした