OpenCV で人物の顔の大きさを揃えたサムネイルを出力する

webサイトや印刷物のデザインを制作する際に、様々な方のサムネイルを並べて掲載することがしばしば発生するかと思います。
少量であれば手作業で作成しても良いですが、大量に作成する場合にトリミングや顔の大きさを揃えて書き出しを行っていくのも少々手間になります。
今回はOpenCVを用いてそのあたりの作業を自動化してみようかと思います。

下準備

OpenCVを利用するためにPhythonとOpenCVをインストールします。
自分の環境はMacでPhythonはプリインストールされているため、OpenCVのみをインストールしました。

OpenCVのインストールに関してはhomebrewを利用しています。
以下のURLを参考に行いました。
参考サイト

取りあえず動くソースコード

以下はとりあえず動くソースコードになります。
書き出し画像サイズや顔の面積の割合等は任意の値に書き換え可能です。
また、このソースコードを利用するためには顔認識に必要なOpenCV用のカスケード識別器の学習済みファイルが必要になります。以下からダウンロードを行ってください。
学習済みファイルはこちら

ソースコード

import cv2
import glob
import numpy

#カスケード型分類器を読み込み
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt.xml')

# ファイルを開く
files = glob.glob("origin_images/*")

# 書き出し画像サイズ
dist_image_width = 500
dist_image_height = 600

# 画像サイズに対する顔の面積
face_area_ratio = 0.5

for n in range(len(files)):
  # 画像をロード
  image = cv2.imread(files[n])

  # 元画像のサイズを取得
  height, width = image.shape[:2]

  # 顔情報を検出
  faces = face_cascade.detectMultiScale(image, 1.1, 3)

  # 取得した顔情報をもとに処理
  for (x, y, w, h) in faces:
    # 顔の縮小率を取得
    image_scale_ratio = dist_image_width / w

    # 色々計算して画像の中心に顔が来るように調整
    mv_mat = numpy.float32([[1, 0, -x + (w * face_area_ratio)], [0, 1, -y + (h * face_area_ratio)]])
    afin_image = cv2.warpAffine(image, mv_mat, (width, height))
    afin_image = afin_image[0: int((h / face_area_ratio) * (dist_image_height / dist_image_width)), 0: int(w / face_area_ratio)]
    afin_image = cv2.resize(afin_image, dsize = (dist_image_width, dist_image_height))

    # 保存
    cv2.imwrite('dist_images/' + str(n + 1) + '.png', afin_image)

ディレクトリ構成

  • main.pyは上記のソースコードになります
  • haarcascade_frontalface_alt.xmlは学習済みファイルです
  • origin_imagesは処理を行いたい画像を格納するディレクトリです
  • dist_imagesは処理後の画像を保存するためのディレクトリです

処理の実行

以下のコマンドで処理を実行してください。

python main.py

主力結果

実際に今回のソースコードで出力した結果になります。

加工前の画像

加工後の画像

所感

しっかりと指定した画像のサイズにトリミングされ、顔自体もその枠内に収まっているものの、顔の大きさがしっかりと揃っていないところが気になります。
加工前の画像の顔の角度等がもう少し揃っているとそこらへんの精度はもう少し高くなるかもしれません。