語り得ぬことについては、沈黙するほかない

世界は事実の総体であり、ものの総体ではない

PythonとOpenCVで写真から顔だけ切り取ってみたよ

前回の記事の続きです。

乃木坂46の3期生の顔認識Webアプリについて進捗ができました。

やったこ

bing画像検索から、一人につきおよそ300枚なので合計3000枚超の画像を集めてきました。
ここから人間が一つ一つ確認して、顔だけ正方形の形に切り取るのはこの上なく非効率なので、画像処理ライブラリのOpenCVを使っていきたいと思います。
OpenCVは、pipを使っている人なら簡単に使用できます。

$ pip install opencv-python

コードと解説

import os
import cv2

def faceDetection(fname):
  print('input file: {}...'.format)
  prefix = fname.split('/')[-1].split('_')[0]
  num = fname.split('/')[-1].split('.')[0].split('_')[-1]
  extension = fname.split('.')[-1]
  save_dir = './face_images/{}_face'.format(prefix)

  if os.path.exists('{}'.format(save_dir)):
    pass
  else:
    os.mkdir('{}'.format(save_dir))
    print('Succeed to make directory {}.'.format(save_dir))

  face_cascade = cv2.CascadeClassifier('opencv/data/haarcascades/haarcascade_frontalface_alt.xml')

  if os.path.getsize(fname) == 0:
    pass
  else:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
 
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)

    if len(faces) > 0:
      for i, (x, y, w, h) in enumerate(faces):
        face = img[y:y+h, x:x+w]
        save_path = '{}/{}_{}-{}.{}'.format(save_dir, prefix, num, i, extension)
        try:
          cv2.imwrite(save_path, face)
        except cv2.error as e:
          print('{} failed with OpenCV Error.'.format(save_path))         
        except:
          print('{} failed.'.format(save_path)) 
        else:
          print('{} saved.'.format(save_path))


if __name__ == '__main__':
  prefixes = [
    'ririan',
    'rentan',
    'minamin',
    'momochan',
    'kubochan',
    'tamachan',
    'denchan',
    'renochan',
    'hazukichan',
    'miichan',
    'ayaty',
    'yodachan'
  ]

  def find_all_files(dirname):
    for root, dirs, files in os.walk(dirname):
      for file in files:
        yield os.path.join(root, file)

  for file in find_all_files('./raw_images'):
    if os.path.getsize(file) == 0:
      pass
    else:
      faceDetection(file)

という感じで顔の部分だけトリミングした画像を取得することができました。

f:id:hoshimure-47:20170929172220p:plain

OpenCVのCascadeClassifierクラスのメソッド'detectMultiScale'を使用すると、画像から顔を検出した場合に返り値として四角形の左上x座標、左上y座標、幅、高さをリスト型で返します。
それらの値をfor文のイテレータとして受け取って、うまい具合に顔部分だけ抽出して画像として保存しています。
詳しくは、他の方のリンクをご覧ください。

qiita.com

問題点

やっていくうちにいくつか問題点が出てきたのであげておきます。誰か解決してください。

  1. 画像内に違う人がいる、または複数の人がいる
  2. そもそも顔の画像じゃない
  3. 横顔は検出できない、顔が傾いていてもできない
  4. 顔以外の場所が顔として検出されてしまう

とりあえず今から約3000枚の画像を人力で分類していこうと思います。

githubのコードです!git少しだけ慣れてきました!
github.com