夏休みなので小学生とPythonを勉強してみた - 課題9 顔の検出

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

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

* [夏休みなので小学生とPythonを勉強してみた - 課題1 FizzBuzz - Qiita](https://qiita.com/shakiyam/items/b8366fde704639abb63d)
* [夏休みなので小学生とPythonを勉強してみた - 課題2 掛け算九九 - Qiita](https://qiita.com/shakiyam/items/0e4153fe7c610b3e8e49)
* [夏休みなので小学生とPythonを勉強してみた - 課題3 掛け算ゲーム - Qiita](https://qiita.com/shakiyam/items/4b4df04369914df3baa6)
* [夏休みなので小学生とPythonを勉強してみた - 課題4 じゃんけん - Qiita](https://qiita.com/shakiyam/items/05c756b4726c941c1c5c)
* [夏休みなので小学生とPythonを勉強してみた - 課題5 サイコロの目の出る確率 - Qiita](https://qiita.com/shakiyam/items/9e0c5548efd0952ab250)
* [夏休みなので小学生とPythonを勉強してみた - 課題6 写真の整理 - Qiita](https://qiita.com/shakiyam/items/2dd2e2763ccf85b19126)
* [夏休みなので小学生とPythonを勉強してみた - 課題7 掛け算ゲームWeb版 - Qiita](https://qiita.com/shakiyam/items/3945a224a8774c332220)
* [夏休みなので小学生とPythonを勉強してみた - 課題8 掲示板 - Qiita](https://qiita.com/shakiyam/items/c3aa6425ff42b75d3310)
* [夏休みなので小学生とPythonを勉強してみた - 課題9 顔の検出 - Qiita](https://qiita.com/shakiyam/items/110b4819b12124587d80)
* [夏休みなので小学生とPythonを勉強してみた - 課題10 顔の認識 - Qiita](https://qiita.com/shakiyam/items/0125dd5db15976d5520e)

コードは[GitHub](https://github.com/shakiyam/python4kids)にて公開しています。

課題9-1 画像一覧
----------------

### 指示 ###

./srcにある画像の一覧を表示しましょう。

### 回答例 ###

```python
import glob
import os

for image_path in glob.glob('./src/*.jpg'):
    image_name = os.path.basename(image_path)
    print(f'image_path: {image_path} image_name: {image_name}')
```

### 実行例 ###

```console
image_path: ./src/DSCN0295.jpg image_name: DSCN0295.jpg
image_path: ./src/DSCN0320.jpg image_name: DSCN0320.jpg
image_path: ./src/DSCN0347.jpg image_name: DSCN0347.jpg
image_path: ./src/DSCN0351.jpg image_name: DSCN0351.jpg
image_path: ./src/DSC_6152.jpg image_name: DSC_6152.jpg
image_path: ./src/DSC_6322.jpg image_name: DSC_6322.jpg
image_path: ./src/DSC_6509.jpg image_name: DSC_6509.jpg
以下略
```

課題9-2 顔の検出
----------------

### 指示 ###

./srcにある画像から、顔の検出を行うプログラムを作りましょう。

### ヒント ###

OpenCVには事前に学習を済ませた顔検出のための識別器があります。

* [OpenCVのHaar-cascadeを使った顔検出 — OpenCV-Python Tutorials](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html#opencvhaar-cascade)

### 回答例 ###

今回はhaarcascade_frontalface_alt.xmlを使用します。まずcv2.CascadeClassifierで識別器を読み込みます。次にcv2.imreadで画像を読み込み、cv2.cvtColorでグレーに変換し、detectMultiScaleで顔検出を行います。もし顔が検出されれば、検出された顔の位置が出力されます。

```python
import glob
import os

import cv2

cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_alt.xml'
cascade = cv2.CascadeClassifier(cascade_path)

for image_path in glob.glob('./src/*.jpg'):
    image_name = os.path.basename(image_path)
    print(f'image_path: {image_path} image_name: {image_name}')

    image = cv2.imread(image_path)
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    face_list = cascade.detectMultiScale(
        gray_image, 1.1, 5, minSize=(100, 100))

    if len(face_list) > 0:
        print(face_list)
```

### 実行例 ###

```console
image_path: ./src/DSCN0295.jpg image_name: DSCN0295.jpg
[[1818  805  353  353]]
image_path: ./src/DSCN0320.jpg image_name: DSCN0320.jpg
[[1976 1350  479  479]]
image_path: ./src/DSCN0347.jpg image_name: DSCN0347.jpg
image_path: ./src/DSCN0351.jpg image_name: DSCN0351.jpg
[[1958  437  826  826]]
image_path: ./src/DSC_6152.jpg image_name: DSC_6152.jpg
[[1210  746  377  377]
 [ 449  802  336  336]]
image_path: ./src/DSC_6322.jpg image_name: DSC_6322.jpg
[[1023 1776  204  204]
 [ 647 1856  206  206]]
image_path: ./src/DSC_6509.jpg image_name: DSC_6509.jpg
[[2616  307  187  187]
 [ 754  247  228  228]
 [ 911 1555  260  260]]
以下略
```

課題9-3 顔の検出と結果の出力
----------------------------

### 指示 ###

./srcにある画像から、顔の検出を行うプログラムを作りましょう。検出された顔は./faceに、検出された位置は元の画像に白枠を描いたものを./face_rectangleに格納してください。

### 回答例 ###

```python
import glob
import os

import cv2

cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_alt.xml'
cascade = cv2.CascadeClassifier(cascade_path)
WHITE = (255, 255, 255)

for image_path in glob.glob('./src/*.jpg'):
    image_name = os.path.basename(image_path)
    print(f'image_path: {image_path} image_name: {image_name}')

    image = cv2.imread(image_path)
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    face_list = cascade.detectMultiScale(
        gray_image, 1.1, 5, minSize=(100, 100))

    if len(face_list) > 0:
        print(face_list)
        for x, y, w, h in face_list:
            face_image = image[y: y + h, x: x + w]
            face_path = f'./face/{ image_name[:-4] }_{ x }-{ y }.jpg'
            cv2.imwrite(face_path, face_image)
        for x, y, w, h in face_list:
            cv2.rectangle(image, (x, y), (x + w, y + h), WHITE, 2)
    cv2.imwrite('./face_rectangle/' + image_name, image)
```

### 実行例 ###

![検出結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174/22764d37-fa5b-f033-9c75-c659227270ca.jpeg)
![切り出された顔画像1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174/662c25cb-a414-8366-3ca2-d7edc73a20da.jpeg)
![切り出された顔画像2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174/2ae784f5-99f6-dc09-515f-a721d2554165.jpeg)
![切り出された顔画像3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174/e860d083-bbfc-007a-bca3-46ff3a6b1006.jpeg)