【OpenPose】〇〇の画像をアップすると近い構図の〇〇の画像が送られてくる。を自動化してみた。


はじめに

この記事は、Twitterのハッシュタグで話題の

#アニメの画像をアップすると近い構図の水谷隼の画像が送られてくる

#羽生くんの画像をアップすると近い構図の羽生さんの画像が送られてくる

をOpenPoseで自動化してみた。という内容です。タイトルで誤解を生みそうですが、 自動で送られてくるbotではなく近い構図の自動抽出までをスコープにしています。

また、近い構図の抽出と言っても何も高度なことはしておらず、「あれ?OpenPose使えば簡単にできるんじゃね?」と見切り発車でやってみた系記事なので、ご了承ください。(期待値調整)

それにしても面白いですね。

参考にした記事

OpenPoseってなに?どうやってインストールするの?という方はこちらの記事がとても参考になります!!

この場をお借りして感謝申し上げます。

近い構図とは?

「近い構図」と一口に言ってもいくつか考え方があると思います。人物が写っている前提で考えると

  • 人物のポーズが近い
  • 画像における人物の配置が近い
  • 画像における人物の大きさが近い

などなど、定義次第でどうとでも考えられてしまいます。
難しさでいうと、下二つは簡単に検出できますが、一番上は結構大変です。
しかも一番上だけでは、「似た構図」というより「似たポーズ」のタスクになるのでやや今回の題材とは離れてしまいそうです。

上記理由により、今回は上記3点を妥協点を取ったような雑な方法を採用しました。(決して似たポーズ判定エンジンが作成できなかったわけではありません。また「全部やれよwww」というコメントも受け付けておりません。)

実装

  • 複数人写っている場合は一番大きい人間のみ、構図判定に使用しました。「一番大きい = 首から各ポイントへの距離の和が最大のもの」としています。非常に雑ですね。
ポイントの定義
Nose = 0
Neck = 1
RShoulder = 2
RElbow = 3
RWrist = 4
LShoulder = 5
LElbow = 6
LWrist = 7
RHip = 8
RKnee = 9
RAnkle = 10
LHip = 11
LKnee = 12
LAnkle = 13
REye = 14
LEye = 15
REar = 16
LEar = 17
一番大きい人間判定(抜粋)

humans = e.inference(image, resize_to_default=(w > 0 and h > 0), upsample_size=args.resize_out_ratio)

best_human_idx = 0
best_human_size = 0

# 複数人検出されている場合
if len(humans) > 1:
    for idx, human in enumerate(humans):
        d = 0
        # 首があるか
        if 0 not in human.body_parts.keys():
            continue
        for i in human.body_parts.keys():
            if i == 0:
                continue
            # 首から各ポイントへの距離を加算
            d += abs(human.body_parts[i].x - human.body_parts[0].x)\
               + abs(human.body_parts[i].y - human.body_parts[0].y)
            if d > best_human_size:
                best_human_idx = idx
                best_human_size = d

best_human = humans[best_human_idx]

best_human_pos = {}

# 各ポイントの座標を辞書に格納
for i in best_human.body_parts.keys():
    best_human_pos[i] = (best_human.body_parts[i].x , best_human.body_parts[i].y)
  • 「近い構図 = OpenPoseが出力する18個のポイントごとに距離を計算し、それらの和が小さいもの」としました。ポイント間の距離や角度など色々と特徴量を作成して類似度を取ろうかとも思いましたが、雑なロジックでも意外と良い精度だったのでやめました。
類似度判定
# target =  比較対象の座標辞書
# best_human_pos = インプット画像の座標辞書
d = 0
# inputと比較両方に存在するポイントの距離を計算
for i in best_human_pos.keys() & target.keys():
    d += (abs(tmp[i][0] - best_human_pos[i][0]) \
        + abs(tmp[i][1] - best_human_pos[i][1]))

# どちらかにしか存在していないポイントの個数 * 1 を加算
# そもそも存在しているポイントが違う = 構図が近くない可能性
d += len(set(best_human_pos.keys()).symmetric_difference(target.keys()))

これだけです。

実験結果(第1弾)

さて、では実際の画像に適用してみます。好きな芸能人をピックアップした結果

#橋本環奈の画像をアップすると近い構図の朝青龍の画像が送られてくる

にしてみました。

比較対象の朝青龍の画像は800枚ほど用意しました。


↑割と良い


↑なかなか良い


↑ 微妙

感想(第1弾)

「橋本環奈 to 朝青龍」をやっていて、ふと気づいてしまいました。
朝青龍こんなポーズしないだろ。。。。

実験結果(第2弾)

ポーズが多いのってなんだろう。。。と安直に考えた結果
「ボディビルダー! プロレスラー!」が思い浮かんだので

#橋本環奈の画像をアップすると近い構図の屈強な男の画像が送られてくる

でリベンジです。

ボディビルダーとプロレスラーの画像は約2000枚収集しました。


↑中指を立てている画像と一致してしまったのは申し訳ありませんが、、、、良いですね。


↑そう言われると、、、そういうポーズに見えてくる。


↑微妙に違うけどまあ。。。


↑良いですね。

感想(第2弾)

意外とよくできてるなという感想です。

おわりに

今回はOpenPoseを使って、近い構図の画像を見つけられるのでは?というアイデアからスタートし、その目的はある程度達成できたと思います。が、非常に雑なロジックなので、死ぬほど改良の余地があると思います.
良いロジック、もっと面白い適用方法など教えていただけると嬉しいです。