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

顔検出 + 年齢・性別予測

はじめに

今回の内容は、画像から顔を検出し、検出された顔の性別と年齢を推定するソースコードを作成することです。ただし、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

Why do not you register as a user and use Qiita more conveniently?
  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
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