MSの画像解析サービスProject Oxfordでコスプレ ラブライブ四天王の年齢判定をプログラミングしてみる 

#Microsoft Azure Oxfordとは
![スクリーンショット 2015-09-01 0.13.34.png](https://qiita-image-store.s3.amazonaws.com/0/36931/a4c13eb4-31ab-1731-4080-9989664ada00.png)


https://dev.projectoxford.ai/

MSの機械学習 画像解析サービスです。
一応カテゴリ的にはAzure MLグループに位置しています。
一般の人でもDemoサイトで直接画像から年齢判定、性別判定が可能。

またAzureに登録しているユーザーは開発者キーでREST APIでプログラミングが可能です。


教師データをユーザーが使って機械学習させるのではなく、MSが予め持っている判定ロジックをクラウドAPIとして利用可能な素敵なサービスです。

今回は汎用画像解析の**「Computer Vision API」**について触ってみたいと思います。

## 扱える画像フォーマット、サイズ

https://dev.projectoxford.ai/docs/services/54ef139a49c3f70a50e79b7d/operations/550a323849c3f70b34ba2f8d

- Input requirements:
- Supported image formats: JPEG, PNG, GIF, BMP.
- Image file size should be less than 4MB.
- Image dimension should be great than 50 x 50.

つまり画像は4MBというリミットがあるのでREST APIでファイルをアップロードする場合は、プログラム側でリサイズが必要です。


## 試して分かった Azure Project Oxford でできること、できないこと。


### できること

#### 年齢、性別判定


![5464654016.png](https://qiita-image-store.s3.amazonaws.com/0/36931/4264e7de-dac5-9ec4-8d09-142d16c5b017.png)

年齢判定は多少誤差がありますが、だいたいあってます。
性別判定はほぼ100%あっています。
写真は内田真礼


#### 複数人の写真判定も可能


![CBIMfsdsfG001.png](https://qiita-image-store.s3.amazonaws.com/0/36931/96b66bcd-3aeb-528f-5317-30c6f93d015e.png)


普通の写真判定サービスでは、写真の中心に人の顔がないと判定できないとか糞なものが多いですが、これは驚異的な顔判定が可能です。
速度も脅威的で背筋が凍ります。
監視カメラなどに応用できそうです。

### できないこと

#### 写真に顔が写ってないとほぼ無意味な判定しか帰ってこない


![5464654005.png](https://qiita-image-store.s3.amazonaws.com/0/36931/ab35c0e2-ffd4-27d7-abdc-458a45981fc8.png)


風景画像を投げても面白いデータは返ってきません。**「風景」**としかわからない



![2316465003.jpg](https://qiita-image-store.s3.amazonaws.com/0/36931/c44b83e8-b31e-74f6-28ff-58c501cd1953.jpeg)


**street**か**road**の判定ぐらいはできるようです

#### 食べ物も詳しい判定は不可能


![2316465001.jpg](https://qiita-image-store.s3.amazonaws.com/0/36931/c245520c-5971-c64d-4a91-17e0bc5ea31c.jpeg)

food ということしかわからない


#### 肝心の？アニメ画像（二次元画像）はもちろん判定してくれない


![CBIMGfsdasdad001.png](https://qiita-image-store.s3.amazonaws.com/0/36931/1c81b34f-efb2-108e-e771-9a78c57b70c6.png)



さすがにこれは**日本の会社**か**テメーで作れ**ということですね解ります。

### Computer Vision の判定カテゴライズ


![clip_image001.jpg](https://qiita-image-store.s3.amazonaws.com/0/36931/000ecd0f-73c4-c6cb-fedd-eb9a0966d485.jpeg)



かなりざっくりとしたカテゴライズなので、犬の種別とか食い物の詳細な判定まではできないようです。
人の解析に特化したサービスに使えそうです。


## Azureからの利用方法

Azureに登録が済んでいる前提で話をすすめます、

### マーケットプレイスから「Computer Vision API」を追加

![5464654018.png](https://qiita-image-store.s3.amazonaws.com/0/36931/c2f6012e-c344-8bac-d6f5-c8c58c1db2de.png)


![5464654021.png](https://qiita-image-store.s3.amazonaws.com/0/36931/644c5c86-168c-f02b-9acd-221df6064d28.png)


![5464654023.png](https://qiita-image-store.s3.amazonaws.com/0/36931/3338dec1-bd3b-d176-6cb9-e726af93d2c3.png)


### APIキーの発行


管理ボタンを押下

![36565546002.jpg](https://qiita-image-store.s3.amazonaws.com/0/36931/b752981b-1ae0-0a77-51c4-23d4c97bff64.jpeg)

![36565546001.jpg](https://qiita-image-store.s3.amazonaws.com/0/36931/d36c8e1c-d1ad-246e-6615-cebef0475734.jpeg)

Primary keyがAPIキー



これだけです。


### Azure Computer Vision API プログラミング

公式にも各種言語のサンプルが載っているのでそれをコマンドラインで実行できるよう引数パラメーター周りを改良してみました。

#### Computer Vision REST APIを叩く URLイメージ読み込み版

```json:application.json
{
  "Azure" : {
    "subscription_key": ""
  }
}
```

```rb
require 'net/http'
require 'json'

File.open './config/application.json' do |file|
  @conf = JSON.load(file.read)
  @subscription_key = @conf['Azure']['subscription_key']
end

uri = URI('https://api.projectoxford.ai/vision/v1/analyses')
uri.query = URI.encode_www_form({
                                    # Request parameters
                                    'visualFeatures' => 'All'
                                })

request = Net::HTTP::Post.new(uri.request_uri)
# Request headers
request['Content-Type'] = 'application/json'
# Request headers
request['Ocp-Apim-Subscription-Key'] = @subscription_key
# Request body
request.body = sprintf('{ "Url": "%s"}', ARGV[0])

response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
  http.request(request)
end

puts response.body
```

実行
``ruby from_url.rb http://news.mynavi.jp/articles/2014/05/25/uchidamaaya/images/001.jpg``

```json
{
  "categories": [
    {
      "name": "people_",
      "score": 0.98046875
    }
  ],
  "adult": {
    "isAdultContent": false,
    "isRacyContent": false,
    "adultScore": 0.009153234772384167,
    "racyScore": 0.014479533769190311
  },
  "requestId": "1757a848-d47a-4cb6-860d-96e72a65ac7a",
  "metadata": {
    "width": 225,
    "height": 338,
    "format": "Jpeg"
  },
  "faces": [
    {
      "age": 20,
      "gender": "Female",
      "faceRectangle": {
        "left": 84,
        "top": 47,
        "width": 40,
        "height": 40
      }
    }
  ],
  "color": {
    "dominantColorForeground": "White",
    "dominantColorBackground": "White",
    "dominantColors": [
      "White",
      "Black"
    ],
    "accentColor": "356375",
    "isBWImg": false
  },
  "imageType": {
    "clipArtType": 0,
    "lineDrawingType": 0
  }
}
```


#### Computer Vision REST APIを叩く ローカルファイルのイメージ読み込み版

```ruby
require 'net/http'
require 'json'

file_path = ARGV[0]

File.open './config/application.json' do |file|
  @conf = JSON.load(file.read)
  @subscription_key = @conf['Azure']['subscription_key']
end

uri = URI('https://api.projectoxford.ai/vision/v1/analyses')
uri.query = URI.encode_www_form({
                                    # Request parameters
                                    'visualFeatures' => 'All'
                                })

request = Net::HTTP::Post.new(uri.request_uri)
# Request headers
request['Content-Type'] = 'application/octet-stream'
# Request headers
request['Ocp-Apim-Subscription-Key'] = @subscription_key
# Request body


File.open(file_path) do |filename_stream_data|
  request.body = filename_stream_data.read
  response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
    http.request(request)
  end
  puts response.body
end

```

コードのフルセット
https://github.com/AKB428/oxford_vision_sample1


### Azure Computer Vision API プログラミング (応用編)

レスポンスのjsonで年齢や性別が分かるので、その値をImageMagickで書きこめばProject Oxfordのサービスと近いものが自分のアプリ、サービスでも使用可能です。

ざっくりJSONのパース部分、年齢を書き込むべき位置の算出を書いてみます


```ruby
require 'json'
require './lib/image_string_write'

class AzureOxfordVision

  def initialize(target_file_or_url, conf)
    @file_path = target_file_or_url
    @conf = conf

    # Local Fileの場合コピー
    @write_filename = File.join(conf['oxford_app']['output_directory'], File::basename(target_file_or_url))
    FileUtils.cp(target_file_or_url, @write_filename)

    #TODO urlだったらファイルをダウンロード
  end

  def iamge_info_write(face_data)
    p @write_filename
    #imageを初期化
    #loopして情報を書き込む
    #出力ファイルを返す
    face_data.each do |data|
      puts data['age']
      puts data['gender']
      puts data['faceRectangle']

      left = data['faceRectangle']['left']
      top = data['faceRectangle']['top']
      width = data['faceRectangle']['width']
      height = data['faceRectangle']['height']

      write_string = sprintf('age=%d gender=%s',data['age'],data['gender'])

      position_x = left
      position_y = top + height + 1

      puts position_y
      #(filename, write_string = nil, input_desc = nil, del_exif = false, font_size = 16, position_x = 5, position_y = 5)
      #"faceRectangle": {
      #    "left": 256,
      #    "top": 126,
      #    "width": 108,
      #    "height": 108
      #}

      ImageStringWrite.new(@write_filename, write_string, "", false, 16, position_x, position_y).write
    end


  end


  def image_info_parse(response_body)
    image_info = JSON.load(response_body)
    face_data = image_info['faces']

    return if face_data.nil?

    #[{"age"=>19, "gender"=>"Female", "faceRectangle"=>{"left"=>77, "top"=>39, "width"=>51, "height"=>51}}]
    p face_data

    iamge_info_write(face_data)
  end

end
```

フル版のソース
https://github.com/AKB428/oxford_vision_sample2


本当は二次元がよかったですが、実写判定しかできないということでラブライブ四天王と言われているコスプレイヤーの画像を解析してみます。


####一人目

```
oxford_vision_sample2 (master)$ bundle exe ruby from_local.rb sample/single/1.jpg
{"categories":[{"name":"people_","score":0.9765625}],"adult":{"isAdultContent":false,"isRacyContent":false,"adultScore":0.025486733764410019,"racyScore":0.27363106608390808},"requestId":"8cc0d06e-98e2-433d-b13d-0e92a8e8c72d","metadata":{"width":600,"height":900,"format":"Jpeg"},"faces":[{"age":8,"gender":"Female","faceRectangle":{"left":256,"top":126,"width":108,"height":108}}],"color":{"dominantColorForeground":"White","dominantColorBackground":"White","dominantColors":["White"],"accentColor":"6D3A3E","isBWImg":false},"imageType":{"clipArtType":0,"lineDrawingType":0}}
[{"age"=>8, "gender"=>"Female", "faceRectangle"=>{"left"=>256, "top"=>126, "width"=>108, "height"=>108}}]
"./private/1.jpg"
8
Female
{"left"=>256, "top"=>126, "width"=>108, "height"=>108}
235
```

![1.jpg](https://qiita-image-store.s3.amazonaws.com/0/36931/379cd26b-4c37-c0f1-a328-18a3d07ce7aa.jpeg)


age 8 ・・・

まぁこういうこともあります。だってAIだもん。。


####二人目


```
oxford_vision_sample2 (master)$ bundle exe ruby from_local.rb sample/single/2.jpg
{"categories":[{"name":"people_young","score":0.6015625}],"adult":{"isAdultContent":false,"isRacyContent":false,"adultScore":0.045799832791090012,"racyScore":0.085988312959671021},"requestId":"44c64b3d-c4b8-4685-a7ce-0fd1cbd69825","metadata":{"width":599,"height":900,"format":"Jpeg"},"faces":[{"age":17,"gender":"Female","faceRectangle":{"left":254,"top":236,"width":142,"height":142}}],"color":{"dominantColorForeground":"White","dominantColorBackground":"White","dominantColors":["White"],"accentColor":"2673A5","isBWImg":false},"imageType":{"clipArtType":0,"lineDrawingType":0}}
[{"age"=>17, "gender"=>"Female", "faceRectangle"=>{"left"=>254, "top"=>236, "width"=>142, "height"=>142}}]
"./private/2.jpg"
17
Female
{"left"=>254, "top"=>236, "width"=>142, "height"=>142}
379
```

![2.jpg](https://qiita-image-store.s3.amazonaws.com/0/36931/ffeb8b1c-e78a-31c2-75b5-5ef51fbeb42e.jpeg)

Age17・・だいたいあってるような気がします




####三人目

```
oxford_vision_sample2 (master)$ bundle exe ruby from_local.rb sample/single/3.jpg
{"categories":[{"name":"others_","score":0.00390625},{"name":"people_","score":0.65625}],"adult":{"isAdultContent":false,"isRacyContent":false,"adultScore":0.045954208821058273,"racyScore":0.1188076063990593},"requestId":"221d32bb-d3be-4a48-abce-8346b0850e28","metadata":{"width":590,"height":885,"format":"Jpeg"},"faces":[{"age":36,"gender":"Female","faceRectangle":{"left":186,"top":68,"width":107,"height":107}}],"color":{"dominantColorForeground":"White","dominantColorBackground":"White","dominantColors":["White"],"accentColor":"326E99","isBWImg":false},"imageType":{"clipArtType":0,"lineDrawingType":0}}
[{"age"=>36, "gender"=>"Female", "faceRectangle"=>{"left"=>186, "top"=>68, "width"=>107, "height"=>107}}]
"./private/3.jpg"
36
Female
{"left"=>186, "top"=>68, "width"=>107, "height"=>107}
176
```

![3.jpg](https://qiita-image-store.s3.amazonaws.com/0/36931/8c96e341-0430-8614-d816-2e39da86936c.jpeg)

絶対に36歳じゃないと思いますが・・・Adobe加工の問題なのか・・
Azureのコスプレイヤー画像判定能力対策が望まれます



####４人目


```
oxford_vision_sample2 (master)$bundle exe ruby from_local.rb sample/single/4.jpg
{"categories":[{"name":"people_crowd","score":0.4453125}],"adult":{"isAdultContent":false,"isRacyContent":true,"adultScore":0.046315658837556839,"racyScore":0.40509960055351257},"requestId":"386ae066-a4f8-4d46-9d8e-595024813e51","metadata":{"width":599,"height":900,"format":"Jpeg"},"faces":[{"age":20,"gender":"Female","faceRectangle":{"left":270,"top":121,"width":114,"height":114}},{"age":22,"gender":"Female","faceRectangle":{"left":40,"top":566,"width":84,"height":84}}],"color":{"dominantColorForeground":"White","dominantColorBackground":"White","dominantColors":["White","Pink"],"accentColor":"8A417C","isBWImg":false},"imageType":{"clipArtType":0,"lineDrawingType":0}}
[{"age"=>20, "gender"=>"Female", "faceRectangle"=>{"left"=>270, "top"=>121, "width"=>114, "height"=>114}}, {"age"=>22, "gender"=>"Female", "faceRectangle"=>{"left"=>40, "top"=>566, "width"=>84, "height"=>84}}]
"./private/4.jpg"
20
Female
{"left"=>270, "top"=>121, "width"=>114, "height"=>114}
236
22
Female
{"left"=>40, "top"=>566, "width"=>84, "height"=>84}
651
```

![4.jpg](https://qiita-image-store.s3.amazonaws.com/0/36931/966cb4ca-44bb-2b69-eecc-192c475cd19e.jpeg)


２０歳。。。だいたいあってる気がします。
写真のボケ効果をしているのに背景の人物の年齢判定をしているのはかなり怖いです。
Azure Project Oxford 恐るべし。。


## まとめ
Project Oxford・・・
犯罪対策に使ったら美しい世界(ディストピア)が構築されそうな気がします。
APIが開放されてるのはプログラマーにとってはすごく良くて、いろんなアイディアで応用が効くので
これからもマイクロソフトの機械学習APIに期待したいです。



![2316465002.jpg](https://qiita-image-store.s3.amazonaws.com/0/36931/71a3dcde-6a6e-5b46-4785-9a3c006ef940.jpeg)


ちなみにビル・ゲイツは生誕	1955年10月28日で　 2015年9月1日現在（59歳） だそうです。