Help us understand the problem. What is going on with this article?

OpenCVの物体検出を回転不変にさせる

More than 3 years have passed since last update.

haarcascadeの特徴量は回転不変じゃない!

OpenCVのhaarcascade_frontalfaceを使って、顔認識をしているのですが、どうもSIFTやSURFと違って、回転に不変な特徴ではないようで、顔をちょっと傾けただけで顔が認識されなくなります。

カメラ画像でリアルタイムに検出する際は、カメラをちゃんとまっすぐに置いておけば、普通の人は首をまっすぐに向けているのでそこそこ認識されるのですが
こんな感じで顔を傾けている写真を単体で検出すると、ちゃんと認識されない。

Show FACES Image_screenshot_18.09.2015.jpg
この写真では、まっすぐ正面を向いてる高城れにさんしか顔の検出がされていない

解決方法

一定の角度しか認識しないのなら、画像自体を傾けてしまえばいいのではないか、との着想で、小刻みに傾きを変えた画像全てを判定させれば、全ての傾きの顔が検出されるだろう。

ステップは以下の通り
1. 画像を読み込む
2. 高さ幅がそれぞれ2倍の空画像を用意する
3. 読みこんだ画像を5度だけ傾ける
4. 空の画像の中心に、傾けた画像を貼り付ける
5. 顔を判定する
6. 2~5を繰り返す

図で表すとこんな感じ
回転不変.001.jpg

ソースコード

顔写真を切り抜いて保存するソースコードです。

import cv2
import numpy as np
import os
from math import ceil

temp_face_img_path = 'work/temp/face/'

# 顔判定で使うxmlファイルを指定する。
cascade_path =  os.path.dirname(os.path.abspath(__file__)) + "/haarcascade_frontalface_alt2.xml"

class classifyPhoto:
    def __init__(self):
        print("init")

    # 画像から顔を切り取り、保存、パスを返す
    def crop_face(self, img_path):
        # ファイル名解析
        base_name = os.path.basename(img_path)
        name,ext = os.path.splitext(base_name)
        if (ext != '.jpg') and (ext != '.jpeg') :
            print('not a jpg image')
            return

        img_src = cv2.imread(img_path, 1)
        # グレースケールに変換
        img_gray = cv2.cvtColor(img_src, cv2.COLOR_BGR2GRAY)
        cascade = cv2.CascadeClassifier(cascade_path)

        org_width = img_src.shape[1]
        org_height = img_src.shape[0]
        i = 0

        for j in range(0,71):
            # 拡大画像の作成
            big_img = np.zeros((org_height * 2, org_width * 2 ,3), np.uint8)
            big_img[ceil(org_height/2.0):ceil(org_height/2.0*3.0), ceil(org_width/2.0):ceil(org_width/2.0*3.0)] = img_src

            # 画像の中心位置
            center = tuple(np.array([big_img.shape[1] * 0.5, big_img.shape[0] * 0.5]))

            # 画像サイズの取得(横, 縦)
            size = tuple(np.array([big_img.shape[1], big_img.shape[0]]))

            # 回転させたい角度
            angle = 5.0 * float(j)
            # 拡大比率
            scale = 1.0

            # 回転変換行列の算出
            rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)

            # アフィン変換
            img_rot = cv2.warpAffine(big_img, rotation_matrix, size, flags=cv2.INTER_CUBIC)
            rot_gray = cv2.cvtColor(img_rot, cv2.COLOR_BGR2GRAY)

            #顔判定
            faces  =  cascade.detectMultiScale(img_rot, scaleFactor=1.2, minNeighbors=2, minSize=(50, 50))
            # 顔があった場合
            if len(faces) > 0:
                for (x,y,w,h) in faces:
                    face = img_rot[y:y+h, x:x+w]
                    file_name =  name + "_face_" + str(i) + ext
                    cv2.imwrite(temp_face_img_path + file_name, face )
                    i += 1

            else :
                print('does not have any faces')

        return 

if __name__ == '__main__':
    classifier = classifyPhoto()
    classifier.crop_faceo('image.jpg')

結果

回転不変.002.jpg

  • 全部で40枚ほど検出
  • れにちゃんが8枚、あーりんが9枚、ももかとしおりんが1枚、かなこは0枚、その他顔パーツや服などの誤検出

考察

  • 判定枚数が増えた分、誤検出が増えた
  • メンバーによって偏りがある。単に傾きだけでなく、顔認識の精度が悪いだけの可能性も。
  • 複数枚検出されたメンバーは、座標変換+クラスタリングすればまとめられそう
  • 5度刻みにそれぞれ計算してるので、計算量が72倍になる

というわけで、チューニングすればもっとマシになるかもしれませんが、多分使えません。

bohemian916
研究機関で働く研究技術員になりました。 技術だけのものはQiitaに ネタを含むものはこっちに書いてます http://bohemia.hatenablog.com/
http://bohemia.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした