LoginSignup
25
35

More than 3 years have passed since last update.

顔検出 + 年齢・性別予測

Last updated at Posted at 2019-09-20

はじめに

今回の内容は、画像から顔を検出し、検出された顔の性別と年齢を推定するソースコードを作成することです。ただし、Pythonの勉強も兼ねているため、完全自作ではなく、複数のサイトを参考にソースコードを作成しました。

ソースコードは、こちらです。

顔検出

顔検出を行うライブラリとして、OpenCVのHaar-like特徴分類器やDlibなどがありますが、FacenetのMTCNNを採用。「davidsandberg/facenet/src/compare.py」中の関数load_and_align_dataを基に書き換えました。

MTCNNを用いた顔検出
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)の確率

メインプログラム

main

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)

結果

検証画像として、「商用無料の写真検索さん」を利用しました。

上が入力画像、下が出力結果となります。

Business Woman by thisisedinburgh output.jpg
性別:Female 年齢:40

Small Business Summit 2011 Pre Event Photo 9 by Grant Wickes output.jpg
性別:(左から)Female、Female、male 年齢:(左から)27、44、26

出力結果において、性別は合っており、年齢もそれっぽくなりました。

最後に

今回の技術は、下図のように、小売業などにおける来店者の性別・年齢層に関するデータを抽出することができるため、(既に商品・サービスが販売されているが)来店客分析に用いることができます。

image.png

25
35
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
35