Ubuntu に OpenCV をインストールして Python で画像から顔をクロップするまで

以前に Tensorflow のセットアップをして、付属するサンプルを試すところまでして終わっていましたが、 実際に何か試してみないとということで、よくある顔判別をやるためにデータを用意する仕組みを作ろうとしてます。

Python 3 と OpenCV 3 を使って画像から顔検出してそれを正方形でクロップして同一サイズにするというスクリプトを作ります。

セットアップ

ほぼ下記のサイトの通りやったところうまくいきました。 www.pyimagesearch.com 以前にcudaのライブラリを VirtualBox 上の仮想環境にに入れていたせいでコンパイルエラーになりましたが、 それも下記のオプションを指定して進められました。 codeyarns.com

スクリプト

Python はほとんど書いたことないのですが、色々と調べてなんとかそれらしいものができました。

import cv2
import sys
import os


class FaceCropper(object):
    CASCADE_PATH = "data/haarcascades/haarcascade_frontalface_default.xml"

    def __init__(self):
        self.face_cascade = cv2.CascadeClassifier(self.CASCADE_PATH)

    def generate(self, image_path, show_result):
        img = cv2.imread(image_path)
        if (img is None):
            print("Can't open image file")
            return 0

        #img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = self.face_cascade.detectMultiScale(img, 1.1, 3, minSize=(100, 100))
        if (faces is None):
            print('Failed to detect face')
            return 0

        if (show_result):
            for (x, y, w, h) in faces:
                cv2.rectangle(img, (x,y), (x+w, y+h), (255,0,0), 2)
            cv2.imshow('img', img)
            cv2.waitKey(0)
            cv2.destroyAllWindows()

        facecnt = len(faces)
        print("Detected faces: %d" % facecnt)
        i = 0
        #height, width = img.shape[:2]

        for (x, y, w, h) in faces:
            r = max(w, h) / 2
            centerx = x + w / 2
            centery = y + h / 2
            nx = int(centerx - r)
            ny = int(centery - r)
            nr = int(r * 2)

            faceimg = img[ny:ny+nr, nx:nx+nr]
            lastimg = cv2.resize(faceimg, (32, 32))
            i += 1
            cv2.imwrite("image%d.jpg" % i, lastimg)


if __name__ == '__main__':
    args = sys.argv
    argc = len(args)

    if (argc != 2):
        print('Usage: %s [image file]' % args[0])
        quit()

    detecter = FaceCropper()
    detecter.generate(args[1], True)

CASCADE_PATH は OpenCV に付属している顔検出の XML 定義のファイルパスを指定します。 顔検出は face_cascade.detectMultiScale の部分で最低の検出サイズを 100x100 として、scaleFactor=1.1, minNeighbors=3 を色々試行して良さそうな値としました。 また下記のサイトを参考にさせてもらいました。 物体検出(detectMultiScale)をパラメータを変えて試してみる(minNeighbors編) | Workpiles

スクリプトJPEGファイルパスを指定すると、サイズ 32x32 の imageナンバー.jpg というファイルが検出された数だけ保存されます。 generateメソッドの第2引数を True にすると検出状態をウィンドウで表示します。

実装例によっては一旦グレースケールに変換しているものを見ましたが(上記ではコメントアウトしてます)、手元の画像で試した限り結果に差はなさそうでした。データセットとしてDeep Learningに用いることまで考慮するとモノクロの方が情報削減にはなりそうですが。