マミルトンのゲーム実況動画で ffmpeg RMagick rekognition を使おう～その１～ヽ(ﾟｰﾟ*ヽ)(ﾉ*ﾟｰﾟ)ﾉわぁい

※Ubuntuは12.04系です

はい。今日もゲーム実況動画をBGMにしてコーディングしてます。ゲーム実況をしているマミルトン、もうみなさんご存知ですよね？この↓の右側の女性です。

![マミルトン](http://img.youtube.com/vi/NNpOTng4b40/mqdefault.jpg)

まだ、ご存知ない方は下の動画をどうぞ。これは、ステマじゃないよ！

[マミルトンのゲーム実況動画:【パズドラ】聖の龍騎姫【同キャラ禁止】 地獄級にマミルトンと挑む！](https://www.youtube.com/watch?v=Gy2bA-cxp7s)

ゲーム実況動画をうまいこと分類したりできないか試行錯誤してきます。使用環境は Ubuntu (12.04) とRubyがメインで、shellスクリプトも使います。画像APIを提供するRekognitionですが、月に5,000くおーたしか使えないので、一部OpenCV使う予定です。

# ffmpeg を使ってフレーム画像の抜出し

## YouTube 動画のダウンロード

youtube-dl を使って、動画をダウンロードします。顔が大きめに映っているものを使いたいので、今回は↓の動画を使わせてもらいます。※ステマじゃないって！でもこの動画、おいしそーです。

![マミルトン](http://img.youtube.com/vi/NNpOTng4b40/mqdefault.jpg)

[マミルトン＆ミカリタンのカフェデート 第２弾！](https://www.youtube.com/watch?v=NNpOTng4b40)

### youtube-dl コマンドライン

youtube-dl はそのままだとダウンロードしたファイル名に動画タイトルが含まれます。それはいやなので、videoId だけになるようにオプション渡します。

```
$ youtube-dl -f 22/17  -o "%(id)s.%(ext)s" https://www.youtube.com/watch?v=NNpOTng4b40
```

これで、NNpOTng4b40.mp4 というファイルがダウンロードされます。

## フレーム画像の抜出 ffmpeg 使うよ

ダウンロードした動画からフレームの画像を抜き出します。全部は多いので、とりあえず1秒毎に1枚、最初の60秒ぐらい抜き出しましょう。シェル書きます。

```bash:mkimages.sh
#! /bin/bash

for i in `seq 1 1 60`
do
    echo "$i"
    #ls $1 -al
    ffmpeg -y -i $1 -vframes 1 -ss $i -r 1 -f image2 $i.jpg
done
exit 0
```
シェルは苦手です。もっと良い書き方があったら教えてください。 :laughing: 

これで、 1.jpg から 60.jpg までの画像ができていると思います。

![4.jpg](http://gameranking.scleen.com/images/mamiruton/4.jpg)

4.jpg

![10.jpg](http://gameranking.scleen.com/images/mamiruton/10.jpg)

10.jpg

### ffmpeg のコマンドライン引数の順番でエラー？

以前より厳密になったのでしょうか？ほかサイトでは

```
ffmpeg -vframes 1 -i hoge.mp4 -ss 1 -r 1 -f image2 1.jpg
```

みたいに書いていたのも、-vframesの位置が悪いとおこられました。こんな感じ：

```
Option vframes (set the number of video frames to record) cannot be applied to an input option to an output file or vice versa. Move this option before the fi
Error parsing options for input file NNpOTng4b40.mp4.
```

# せっかくなので RMagick 使って mosaic

```rb:mamiruton.rb
# -*- coding: utf-8 -*-

require 'test/unit'
# RMagick
require 'rubygems'
require 'RMagick'

# http://www.imagemagick.org/ImageMagick-7.0.0/RMagick/doc/mosaic.rb.html
class TC_Foo < Test::Unit::TestCase
    def setup
		puts "setup"
    end
	def test_rmagick_mosaic
		puts "open_images"
		imageUrls = ["1.jpg",
			"2.jpg",
			"3.jpg",
			"4.jpg",
			"5.jpg",
			"6.jpg",
			"7.jpg",
			"8.jpg",
			"9.jpg"
			]
		images = Magick::ImageList.new(*imageUrls)

		b = Magick::ImageList.new
		page = Magick::Rectangle.new(0,0,0,0)
		images.scene = 0
		
		3.times do |i|
			3.times do |j|
				b << images.scale(0.57)
				page.x = j * b.columns
				page.y = i * b.rows
				b.page = page
				(images.scene += 1) rescue images.scene = 0
			end
		end
		image = b.mosaic
		image.write("mamiruton2.jpg")
	end
end
```
test/unit を使っているのは、問題があったときのデバッグのためです。

![マミルトン](http://gameranking.scleen.com/images/mamiruton2.jpg)

# Rekognition のAPIを使ってみる

## とりあえずためしに 4.jpg を使ってみます

大きめの顔になっている 4.jpg を使います。

![4.jpg](http://gameranking.scleen.com/images/mamiruton/4.jpg)

### rekognize の gem を入れる

```
$ sudo gem install rekognize
```

### APIを呼んでみる

```rb:rekognize.rb
class TC_Foo < Test::Unit::TestCase
    def setup
        @client = Rekognize::Client::Base.new(api_key: YOUR_API_KEY, api_secret: YOUR_API_SECRET)
    end
    def test_open_remote_image
        response = @client.face_detect(urls: "http://gameranking.scleen.com/images/mamiruton/4.jpg", jobs: 'face_part_aggresive_emotion_age_beauty')
        puts response
    end
end
```
※test/unit 使っているのはデバッグしやすいからです・・・
**※face_detect はパラメーター base64 をサポートして無いのかな・・・**

### APIの結果

レスポンス：

|項目|数値|
|:-----:|:----:|
| happy | 0.98 |
| sad   | 0.03 |
| disgust   | 0.01 |
| age   | 17.07 |
| smile   | 0.97 |
| beauty   | 0.54331 |

とこんな感じのデータが得られます。happy と smile で数字が取れているのでこの数字を使ってみましょう。

### 顔の位置の確認

顔の位置も正しく認識できました。

![result.jpg](http://gameranking.scleen.com/images/mamiruton/result.jpg)

このRectangleはRMagick使って描きました。

```rb:rekog.rb
class TC_Foo < Test::Unit::TestCase
    def setup
		@client = Rekognize::Client::Base.new(api_key: YOUR_API_KEY, api_secret: YOUR_API_SECRET)
    end
	def test_open_remote_image
		response = @client.face_detect(urls: "http://gameranking.scleen.com/images/mamiruton/4.jpg", jobs: 'face_part_aggresive_emotion_age_beauty')
		puts response
		# draw rectangles
		
		image = Magick::Image.read("http://gameranking.scleen.com/images/mamiruton/4.jpg").first
		response['face_detection'].each do |face|
			x = face['boundingbox']['tl']['x']
			y = face['boundingbox']['tl']['y']
			w = face['boundingbox']['size']['width']
			h = face['boundingbox']['size']['height']
			
			puts "#{x},#{y} #{w},#{h}"
			
			gc = Magick::Draw.new
			gc.stroke = 'red'
			gc.fill = 'transparent'
			gc.rectangle x, y, (w+x), (y+h)
			gc.draw(image)
		end
		image.write "result.jpg"
	end
end
```

ミカルタンが一緒のフレームでもうまく顔が認識できます。Rekognition のAPIは beauty という超主観的な結果を戻してくれますが、とりあえず使わないでおきます。smile happy 程度がまぁいいんじゃないでしょうか。

![result7.jpg](http://gameranking.scleen.com/images/mamiruton/result7.jpg)

### えがおとHAPPY度合でスコアリング

Rekognition のAPIを使って開始1分間のえがおとHAPPY度合 :smile: をスコアリングして上位の画像を探してみた。

![result8.jpg](http://gameranking.scleen.com/images/mamiruton/result7.jpg)
7秒

![result8.jpg](http://gameranking.scleen.com/images/mamiruton/result8.jpg)
8秒

![result8.jpg](http://gameranking.scleen.com/images/mamiruton/result11.jpg)
11秒

![result8.jpg](http://gameranking.scleen.com/images/mamiruton/result22.jpg)
22秒

![result8.jpg](http://gameranking.scleen.com/images/mamiruton/result32.jpg)
32秒

![result8.jpg](http://gameranking.scleen.com/images/mamiruton/result33.jpg)
33秒

**これはなかなかやるのではないか？**マミルトンの笑顔をお楽しみいただけましたでしょーか？ :smile: 

## いろいろ

### Rekognition の制限事項

* 画像サイズは縦横800pxまで、それを超える場合は内部的に縮小してから処理してるらしい。
* 無料5,000回/月は少ないなー

### 検討すること

 * Rekognitionが800pxまでなので、YouTubeからのダウンロードする動画サイズを小さくした方がいろいろ良さそう。処理が速くなるしディスクサイズの節約にもなる。
 * ffmpeg のフレーム画像の切り出しに時間かかるので短縮する方法を考える。全部出してから削除したほうが速い感じだ。