Python+OpenCVで顔検出(回転不変)
画像を回転させながら顔検出
一番いい角度の調整等は行っていない
必要なら以下を参考に
Face Detection using Haar Cascades
入力画像ではなく、フィルタのほうを回転させて自動で検出してほしい……
はまりポイント
画像処理に慣れている人なら当たり前なのかもしれないが、ほとんどやったことがないので結構はまった
画像の高さや幅を取得・指定するときに順番が違うことがある
- shape…高さ(rows)、幅(columns)
- インデックスでスライス…[高さ下限(y) : 高さ上限(y+h), 幅下限(x) : 幅上限(x+w)]
- OpenCVの座標指定(中心点とか)…(横(x), 縦(y))
ディスプレイの座標系はy軸が下方向なのに角度は反時計回りなので、普通のy軸が上方向の座標系に変換すると時計回りに回っている
コード
face_rotate_detect.py
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import cv2, os, sys, imghdr, shutil, math
import numpy as np
CWD = os.getcwd()
DIR_ORIGIN = CWD + '/images/'
DIR_DESTINATION = CWD + '/faces/'
classifier = cv2.CascadeClassifier('{python_dir}/{classifier_dir}/haarcascade_frontalface_alt2.xml'.format(
python_dir = os.path.split(sys.executable)[0],
classifier_dir = '../share/OpenCV/haarcascades',
))
def getFaces(path_full):
results = []
image = cv2.imread(path_full)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
rows, cols, colors = image.shape
center = tuple(np.array([cols, rows]))
# get rotations
for angle in range(-90, 91, 5):
radian = np.deg2rad(angle)
output_height = int(np.absolute(cols * np.sin(radian)) + np.absolute(rows * np.cos(radian)))
output_width = int(np.absolute(cols * np.cos(radian)) + np.absolute(rows * np.sin(radian)))
output_size = tuple(np.array([output_width, output_height]))
# rotate
Matrix = cv2.getRotationMatrix2D(center, degree, 1.0)
# translate
Matrix[0, 2] += (output_width - width) * 0.5
Matrix[1, 2] += (output_height - height) * 0.5
# warp affine
rotated = cv2.warpAffine(gray, Matrix, output_size, flags = cv2.INTER_LINEAR)
# detect faces
faces = classifier.detectMultiScale(rotated)
if len(faces):
rotated_color = cv2.warpAffine(image, Matrix, output_size, flags = cv2.INTER_LINEAR)
for x, y, w, h in faces:
results.append(rotated_color[y : y + h, x : x + w])
return results
def saveFaces(faces):
global count
for face in faces:
cv2.imwrite(
'{dir_destination}{count}.jpg'.format(dir_destination = DIR_DESTINATION, count = count),
face,
[cv2.IMWRITE_JPEG_QUALITY, 100]
)
count += 1
count = 1
for path, subdirs, files in os.walk(DIR_ORIGIN):
for name in files:
path_full = os.path.join(path, name)
if imghdr.what(path_full) in ['jpeg']:
saveFaces(getFaces(path_full))
print(path_full)