OpenCVとWatsonで顔認識の精度はどのくらい違うのか?

昨日は、中野ICTCOにて第4回IoT+AIもくもく会を開催しました。月に1回のイベントで第4回ですから、もう4ヶ月経つんですね。その前の企画(IoT+AI先遣隊)を合わせると半年くらい会が続いていることになります。ありがたいことです。

もくもく会なので、それぞれがやりたいことを気ままにやっています。私は、ついこの間、解散したアイドルグループ℃-uteのメンバーの顔認識をWatsonのVisual Recognitionでやってみようということで、℃-uteが解散した日にちょっとだけやったことの延長戦を。

まずは顔だけを切り抜くと認識精度が上がるのではないかというアイディアをFacebookでいただいていたので、そこから始めました。

OpenCVでやってみる

まずは定番というか、OpenCVを使って画像の中から顔の部分を検出してみることにしました。

import cv2
import glob
import os
from os.path import basename

#cascade_path = '/usr/local/opt/opencv3/share/OpenCV/haarcascades/haarcascade_frontalface_default.xml'
cascade_path = '/usr/local/opt/opencv3/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml'
#cascade_path = '/usr/local/opt/opencv3/share/OpenCV/haarcascades/haarcascade_frontalface_alt2.xml'
#cascade_path = '/usr/local/opt/opencv3/share/OpenCV/haarcascades/haarcascade_frontalface_alt_tree.xml'
image_path = './data/'
tmp_path = './tmp/'

members = ['airi', 'chisato', 'mai', 'maimi', 'saki']

for member in members:
  member_image_path = image_path + member
  member_tmp_path = tmp_path + member

for file in glob.glob(member_tmp_path + '/*.*'):
  os.remove(file)

  color = (255, 255, 255)
  cascade = cv2.CascadeClassifier(cascade_path)

for file in glob.glob(member_image_path + '/*.*'):
  print(file)
  tmp_file = tmp_path + member + '/' + basename(file)

  image = cv2.imread(file)
  image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

  facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1))

if len(facerect) > 0:
  for rect in facerect:
    cv2.rectangle(image, tuple(rect[0:2]), tuple(rect[0:2] + rect[2:4]), color, thickness=4)
    cv2.imwrite(tmp_file, image)
else:
  print('face not found! : ' + str(len(facerect)))

ざっと、こんなコード。これは切り抜くというより、画像の顔の部分に矩形を描く実装です。

これで、きちんと顔を検出できたのはだいたい3〜4割といったところでした。顔以外の部分も顔として検出してしまったり、まったく顔を検出できなかったり、いろいろでした。
OpenCVの場合、機械学習とかではなく、単なるパターン認識なので仕方ないのか・・・。

Watsonでやってみる

ということで、WatsonのVisual Recognitionを使い(具体的にはFace Detect)、顔の部分の座標を得て、切り抜くことにしました。

import cv2
import json
import glob
import os
from os.path import basename
from watson_developer_cloud import VisualRecognitionV3

vr = VisualRecognitionV3('2016-05-20', api_key='')

image_path = './data/'
tmp_path = './tmp/'

members = ['airi', 'chisato', 'mai', 'maimi', 'saki']

for member in members:
  member_image_path = image_path + member
  member_tmp_path = tmp_path + member

for file in glob.glob(member_tmp_path + '/*.*'):
  os.remove(file)

color = (255, 255, 255)

for file in glob.glob(member_image_path + '/*.*'):
  print(file)
  tmp_file = tmp_path + member + '/' + basename(file)

  image = cv2.imread(file)

  try:
    with open(file, 'rb') as image_file:
      result = vr.detect_faces(images_file=image_file)
  except:
    continue

  if 'faces' not in result['images'][0]:
    continue

  if len(result['images'][0]['faces']) > 0:
    rect = result['images'][0]['faces'][0]['face_location']
    top = int(rect['top'])
    left = int(rect['left'])
    height = int(rect['height'])
    width = int(rect['width'])
    print("top:%d left:%d width:%d height:%d" % (top, left, width, height))
    #cv2.rectangle(image, (left, top), (left + width, top + height), color, thickness=4)
    #cv2.imwrite(tmp_file, image)
    dst = image[top:top+height, left:left+width]
    cv2.imwrite(tmp_file, dst)
  else:
    print('face not found! : ' + str(len(result.images[0].faces)))

こちらは、きちんと切り抜く実装になっています。

Watsonを使うときちんと顔を認識した確率はなんと100%!
メンバー毎に15枚なので、全部で75毎の画像がありましたが、すべて顔の部分を検出し切り抜くことに成功したのです。

さすがのWatson。機械学習の威力か?

ただ、本題は・・・

で、ここまでの顔の切り抜きは前振りでありまして、本題はVisual Recognitionのカスタム学習器を作り、メンバーの画像から名前を予測したいのです。
こちらの方は、結局まだいまひとつの結果となってしまいました・・・。訓練データとして画像の枚数が少ないのか、ネガティブデータを入れていないのがマズいのか・・・。

もうちょっと調べてみたいと思います。

第5回IoT+AIもくもく会は7月16日開催

こんな感じでやっているもくもく会ですが、次回は7月16日開催です。
こちらのFacebookグループに参加いただくと、開催のお知らせを致します。

https://www.facebook.com/groups/iot.ai.tokyo/

この記事を書いた人

井上 研一

株式会社ビビンコ代表取締役、ITエンジニア/経済産業省推進資格ITコーディネータ。AI・IoTに強いITコーディネータとして活動。画像認識モデルを活用したアプリや、生成AIを業務に組み込むためのサービス「Gen2Go」の開発などを行っている。近著に「使ってわかった AWSのAI」、「ワトソンで体感する人工知能」。日本全国でセミナー・研修講師としての登壇も多数。