はじめに
今回の内容は、画像から顔を検出し、検出された顔の性別と年齢を推定するソースコードを作成することです。ただし、Pythonの勉強も兼ねているため、完全自作ではなく、複数のサイトを参考にソースコードを作成しました。
ソースコードは、こちらです。
顔検出
顔検出を行うライブラリとして、OpenCVのHaar-like特徴分類器やDlibなどがありますが、FacenetのMTCNNを採用。「davidsandberg/facenet/src/compare.py」中の関数load_and_align_dataを基に書き換えました。
def Face_detection(Img, image_size):
minsize = 20
threshold = [ 0.6, 0.7, 0.7 ]
factor = 0.709
margin = 44
gpu_memory_fraction = 1.0
print('Creating networks and loading parameters')
with tf.Graph().as_default():
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=gpu_memory_fraction)
sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False))
with sess.as_default():
pnet, rnet, onet = align.detect_face.create_mtcnn(sess, None)
Img_size = np.asarray(Img.shape)[0:2]
bounding_boxes, _ = align.detect_face.detect_face(Img, minsize, pnet, rnet, onet, threshold, factor)
faces = np.zeros((len(bounding_boxes), image_size, image_size, 3), dtype = "uint8")
bb = np.zeros((len(bounding_boxes), 4), dtype=np.int32)
for i in range(len(bounding_boxes)):
det = np.squeeze(bounding_boxes[i,0:4])
bb[i, 0] = np.maximum(det[0]-margin/2, 0)
bb[i, 1] = np.maximum(det[1]-margin/2, 0)
bb[i, 2] = np.minimum(det[2]+margin/2, Img_size[1])
bb[i, 3] = np.minimum(det[3]+margin/2, Img_size[0])
cropped = Img[bb[i, 1]:bb[i, 3],bb[i, 0]:bb[i, 2],:]
aligned = misc.imresize(cropped, (image_size, image_size), interp='bilinear')
faces[i, :, :, :] = cv2.cvtColor(aligned, cv2.COLOR_BGR2RGB)
return faces, bb
- 引数
- Img:入力画像
- image_size:リサイズする顔画像の大きさ
- 戻り値
- faces:顔画像が格納された4次元配列
- bb:入力画像から検出された顔の位置
年齢・性別予測
年齢・性別を予測するために、「Kerasでウェブカメラから顔領域を検出し、年齢・性別を推定する」を参考にさせていただきながら、demo.pyを基に書き換えました。また、学習済みモデルも使わせていただいております。
def age_gender_predict(faces):
if len(faces) > 0:
# モデルの設定
if os.isdir("model") == False:
pre_model = "https://github.com/yu4u/age-gender-estimation/releases/download/v0.5/weights.28-3.73.hdf5"
modhash = 'fbe63257a054c1c5466cfd7bf14646d6'
weight_file = get_file("weights.28-3.73.hdf5", pre_model, cache_subdir="model",
file_hash=modhash, cache_dir=str(Path(__file__).resolve().parent))
else:
weight_file = "model/weights.28-3.73.hdf5"
img_size = np.asarray(faces.shape)[1]
model = WideResNet(img_size, depth=16, k=8)()
model.load_weights(weight_file)
# 予測
results = model.predict(faces)
Genders = results[0]
ages = np.arange(0, 101).reshape(101, 1)
Ages = results[1].dot(ages).flatten()
return Ages, Genders
- 引数
- faces:顔画像が格納された配列
- 戻り値
- Ages:予測された各年齢(0~100歳)の確率
- Genders:予測された各性別(Male、Female)の確率
メインプログラム
import cv2
import numpy as np
from wide_resnet import WideResNet
from pathlib import Path
import align.detect_face
import tensorflow as tf
from scipy import misc
from keras.utils.data_utils import get_file
import os.path as os
img = cv2.imread("input.jpg") #入力画像
img_size = 64
# 性別・年齢を表記する関数
def draw_label(image, point, label, font=cv2.FONT_HERSHEY_SIMPLEX,
font_scale=1.0, thickness=1):
size = cv2.getTextSize(label, font, font_scale, thickness)[0]
x, y = point
cv2.rectangle(image, (x, y - size[1]), (x + size[0], y), (0,255,255), cv2.FILLED)
cv2.putText(image, label, point, font, font_scale, (0, 0, 0), thickness, lineType=cv2.LINE_AA)
faces, bb = Face_detection(img, img_size) # 顔検出
Ages, Genders = age_gender_predict(faces) # 性別・年齢予測
for face in range(len(faces)):
cv2.rectangle(img,(bb[face, 0], bb[face, 1]),(bb[face, 2], bb[face, 3]),(0,255,255),10)
label = "{}, {}".format(int(Ages[face]), "Male" if Genders[face][0] < 0.5 else "Female")
draw_label(img, (bb[face, 0], bb[face, 1]), label)
# 出力画像の保存
cv2.imwrite('output.jpg', img)
結果
検証画像として、「商用無料の写真検索さん」を利用しました。
上が入力画像、下が出力結果となります。
性別:(左から)Female、Female、male 年齢:(左から)27、44、26
出力結果において、性別は合っており、年齢もそれっぽくなりました。
最後に
今回の技術は、下図のように、小売業などにおける来店者の性別・年齢層に関するデータを抽出することができるため、(既に商品・サービスが販売されているが)来店客分析に用いることができます。