LoginSignup
1
2

More than 3 years have passed since last update.

夏休みなので小学生とPythonを勉強してみた - 課題10 顔の認識

Posted at

小学6年生の長男が夏休みの自由研究にPythonを勉強したいというので付き合ってみました。
どこから手を付けていけばわからないので、いくつか課題を出し、それを実現するコードを一緒に書くということを繰り返しました。

この記事では、「課題10 顔の認識」について扱います。その他の課題については下記の記事をご覧ください。

コードはGitHubにて公開しています。

課題10-1 顔画像の分割

指示

顔画像の集合をトレーニング用とテスト用に分割するプログラムを作りましょう。
./srcにある顔画像を、./trainおよび./testにランダムに格納します。

回答例

import glob
import random
import shutil

for image_path in glob.glob('./src/*/*.jpg'):
    if random.random() <= 0.8:
        shutil.copy(image_path, image_path.replace('./src/', './train/', 1))
    else:
        shutil.copy(image_path, image_path.replace('./src/', './test/', 1))

課題10-2 顔の認識

指示

顔の認識を行うプログラムを作りましょう。
./trainにある顔画像を学習し、./testにある顔画像を認識させます。./trainおよび./test以下の顔画像が格納されているディレクトリの名前は「ラベル_ラベル名」という形式とします。ラベルは数値です。

ヒント

OpenCVには顔認識のモジュールが提供されています。

今回はLocal Binary Patterns Histogramsというアルゴリズムを使います。トレーニング用顔画像をグレーに変換し、サイズをそろえてラベルと与えて学習させます。テスト用顔画像も同様にグレーに変換し、サイズをそろえて結果を取得します。結果には推測されたラベルと、その確からしさ(0に近いほ良い)が含まれます。

回答例

import glob
import os

import cv2

import numpy


def get_gray_resized_image(image_path):
    image = cv2.imread(image_path)
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    gray_resized_image = cv2.resize(gray_image, (500, 500))
    return gray_resized_image


def get_label(path):
    return int(path.split(os.sep)[2].split('_')[0])


train_images = []
train_labels = []
for image_path in glob.glob('./train/*/*.jpg'):
    train_images.append(get_gray_resized_image(image_path))
    train_labels.append(get_label(image_path))
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.train(train_images, numpy.array(train_labels))

for image_path in glob.glob('./test/*/*.jpg'):
    image_name = os.path.basename(image_path)
    gray_resized_image = get_gray_resized_image(image_path)
    predicted_label, confidence = recognizer.predict(gray_resized_image)
    image_label = get_label(image_path)
    print(f'Image: {image_name}', end='\t')
    print(f'Predicted: {predicted_label}', end='\t')
    print(f'Confidence: {confidence:3.1f}', end='\t')
    print(f'Anser: {image_label}')

実行例

Image: DSC_6322_1023-1776.jpg   Predicted: 2    Confidence: 28.3        Anser: 1
Image: DSC_6658_1160-566.jpg    Predicted: 2    Confidence: 41.0        Anser: 1
Image: IMG_3036_895-622.jpg     Predicted: 1    Confidence: 22.1        Anser: 1
Image: IMG_3663_2257-1166.jpg   Predicted: 2    Confidence: 33.7        Anser: 1
Image: IMG_3770_1708-619.jpg    Predicted: 2    Confidence: 24.9        Anser: 1
Image: IMG_5088_780-1565.jpg    Predicted: 1    Confidence: 36.5        Anser: 1
Image: IMG_3663_1220-1431.jpg   Predicted: 2    Confidence: 30.8        Anser: 2
Image: IMG_3671_158-917.jpg     Predicted: 1    Confidence: 19.8        Anser: 2
Image: IMG_3772_774-981.jpg     Predicted: 1    Confidence: 54.6        Anser: 2
Image: IMG_4337_1521-1070.jpg   Predicted: 2    Confidence: 36.5        Anser: 2
Image: IMG_5085_1185-1571.jpg   Predicted: 2    Confidence: 36.7        Anser: 2
Image: IMG_6555_1260-2329.jpg   Predicted: 2    Confidence: 16.0        Anser: 2
Image: IMG_6707_1411-653.jpg    Predicted: 2    Confidence: 24.1        Anser: 2

課題10-3 顔の認識結果をHTML出力

指示

顔の認識を行い、結果をHTMLで出力するプログラムを作りましょう。

ヒント

Flaskで使っていたJinja2は単体でも使えます。

リストの内包表記は辞書にも使えます。

回答例

import glob
import os

import cv2

from jinja2 import Environment, FileSystemLoader, select_autoescape

import numpy


def get_gray_resized_image(image_path):
    image = cv2.imread(image_path)
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    gray_resized_image = cv2.resize(gray_image, (500, 500))
    return gray_resized_image


def get_label(path):
    return int(path.split(os.sep)[2].split('_')[0])


def get_label_name(path):
    return path.split(os.sep)[2].split('_')[1]


label_and_names = {
    get_label(path): get_label_name(path) for path in glob.glob('./src/*')
}

train_images = []
train_labels = []
for image_path in glob.glob('./train/*/*.jpg'):
    train_images.append(get_gray_resized_image(image_path))
    train_labels.append(get_label(image_path))
recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.train(train_images, numpy.array(train_labels))

test_results = []
for image_path in glob.glob('./test/*/*.jpg'):
    image_name = os.path.basename(image_path)
    gray_resized_image = get_gray_resized_image(image_path)
    predicted_label, confidence = recognizer.predict(gray_resized_image)
    test_results.append(
        (image_path,
         label_and_names[predicted_label],
         confidence,
         get_label_name(image_path))
    )

env = Environment(
    loader=FileSystemLoader('./templates'),
    autoescape=select_autoescape()
)
template = env.get_template('02.html')
template.stream(test_results=test_results).dump('output.html')
02.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Face Recognition</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  </head>
  <body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="container">
        <h1 class="navbar-brand">Face Recognition</h1>
      </div>
    </nav>
    <div class="container">
      <div class="list-group">
        {% for image_path, predicted_label, confidence, image_label in test_results %}
          <div class="list-group-item">
            <div class="d-flex flex-row">
              <div class="p-1 bd-highlight">
                  <img src="{{ image_path }}" width="100px" height="100px">
              </div>
              <div class="p-1 bd-highlight">
                <div class="d-flex flex-column">
                  {% if predicted_label == image_label %}
                    <div>Predicted: <span style="color:green">{{ predicted_label }}</span></div>
                  {% else  %}
                    <div>Predicted: <span style="color:red">{{ predicted_label }}</span></div>
                  {% endif %}
                  {% if confidence < 30 %}
                    <div>Confidence: <span style="color:green">{{ '%3.1f'|format(confidence) }}</span></div>
                  {% else  %}
                    <div>Confidence: <span style="color:red">{{ '%3.1f'|format(confidence) }}</span></div>
                  {% endif %}
                  <div>Answer: {{ image_label }}</div>
                </div>
              </div>
            </div>
          </div>
        {% endfor %}
      </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>

実行例

出力されたHTML

1
2
0

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
1
2