openCVとKerasで画像から人物検出

# はじめに
画像から、人物を検出してみます。
様々な方法があるようですが、**理論はちょっと置いておいて**、以下３つの方法を試してみました。

 - openCV（HOG特徴量＋SVM）
 - openCV（Haar-like特徴量分類器）
 - keras （SSD）

# 環境
- windows7 home（64bit）
- openCV 3.3.1
- tensorflow 1.2.1
- keras 2.1.3

# テスト用画像
フリー画像のサイトはいくつもありますが、スポーツ系の画像はあまりありませんでした。
その中でAdobeの画像がオシャレだったので、いくつかダウンロード（1ヶ月の無料期間あり。ユーザ登録要）

https://stock.adobe.com/jp/

# openCV（HOG特徴量＋SVM）
OpenCVの「cv2.HOGDescriptor」を使って人物を検出します。

## ソースコード
複数画像を対象に人物を検出するソースはこちら→[GitHub](https://github.com/nanako-ut/screen_person/blob/master/openCV-hog-SVM.py)

```python:openCV-hog-SVM.py
import numpy as np
import cv2

#入出力ファイル
input_file = './image/test.jpg'
output_file = './image_out/test.jpg'

# ファイル読込
image = cv2.imread(input_file ,cv2.IMREAD_COLOR)

hog = cv2.HOGDescriptor()
hog = cv2.HOGDescriptor((48,96), (16,16), (8,8), (8,8), 9)

# SVMによる人検出
hog.setSVMDetector(cv2.HOGDescriptor_getDaimlerPeopleDetector())

# リサイズした方が精度がよかった
finalHeight = 800.0
scale = finalHeight / image.shape[0]
image = cv2.resize(image, None, fx=scale, fy=scale)

# 人を検出した座標
human, r = hog.detectMultiScale(image, hitThreshold = 0.6, winStride = (8,8), padding = (32, 32), scale = 1.05, finalThreshold=2)

# 全員のバウンディングボックスを作成
for (x, y, w, h) in human:
    cv2.rectangle(image, (x, y),(x+w, y+h),(0,255,0), 2)

# ファイルを保存
cv2.imwrite(output_file , image)


```
## 実行結果
うーん、、あまり上手くいっていない様子。。。
<img width="796" alt="openCV-hog.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/170139/34265473-b5e3-48d1-63e5-eb09a7a9a87c.png">


# openCV（Haar-like特徴量分類器）
## 事前準備
人物用の特徴量分類器（haarcascade_fullbody.xml）を以下からダウンロードしておく

https://github.com/opencv/opencv/tree/master/data/haarcascades

## ソースコード
複数画像を対象に人物を検出するソースはこちら→[GitHub](https://github.com/nanako-ut/screen_person/blob/master/openCV-Haar-like.py)

```python:openCV-Haar-like.py
import numpy as np
import cv2

# 入出力フォルダ
input_file = './image/test.jpg'
output_file = './image_out/test.jpg'

# 分類器の特徴量を取得する
faceCascade = cv2.CascadeClassifier('./haarcascade_fullbody.xml')

# ファイル読み込み
image = cv2.imread(input_file,cv2.IMREAD_COLOR)

# リサイズ
finalHeight = 800.0
scale = finalHeight / image.shape[0]
image = cv2.resize(image, None, fx=scale, fy=scale)

# 物体認識（人）の実行
facerect = faceCascade.detectMultiScale(image, scaleFactor=1.01, minNeighbors=1, minSize=(30, 30))

#検出した人を囲む矩形の作成
for x, y, w, h in facerect:    
    cv2.rectangle(image, (x, y),(x+w, y+h),(0,255,0), 2)

# ファイルを保存
cv2.imwrite(output_file, image)

```

## 実行結果
うーん、、、こちらもあまり上手くいっていない様子。。
<img width="796" alt="openCV-like.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/170139/fae143ab-2e04-3434-f27f-450a8a3a6de8.png">

# keras （SSD：Single Shot MultiBox Detector）
## 事前準備
[SSD: Single Shot MultiBox Detector 高速リアルタイム物体検出デモをKerasで試す](https://qiita.com/PonDad/items/6f9e6d9397951cadc6be)を参考に、ssd_kerasのモデルと、学習済モデルをダウンロードします。

**補足**

リポジトリには、
> This code was tested with Keras v1.2.2, Tensorflow v1.0.0, OpenCV v3.1.0-dev

とありますが、tensorflow 1.2.1で動きました。
tensorflow 1.4.1はダメでした。


## ソースコード
上記サイトの方も記載されているとおり、
私もジュピターノートブックを開いて、SSD.ipynbをコピペしながら試してみました。
一部、指定したフォルダにある画像を一括して処理してくれるよう、少しだけ書き換えました。

例のごとく、複数画像を対象に人物を検出するソースはこちらに→[GitHub](https://github.com/nanako-ut/screen_person/blob/master/my-SSD.ipynb)


```python:修正前
inputs = []
images = []
img_path = './pics/fish-bike.jpg'
img = image.load_img(img_path, target_size=(300, 300))
img = image.img_to_array(img)
images.append(imread(img_path))
inputs.append(img.copy())

#～複数ファイルある場合は繰り返し記述～

inputs = preprocess_input(np.array(inputs))
```

```python:修正後
import os
input_data_path = './pics/'
inputs = []
images = []

file_list = os.listdir(input_data_path)
for file_name in file_list:
    img_path = input_data_path + file_name
    img = image.load_img(img_path, target_size=(300, 300))
    img = image.img_to_array(img)
    images.append(imread(img_path))
    inputs.append(img.copy())

inputs = preprocess_input(np.array(inputs))
```


## 実行結果
オォォォォ！！！なんてこったい。
水中の人をDogかも、と間違っている以外はほぼ完ぺきです。すごーい。
<img width="792" alt="my-SSD.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/170139/19d020b0-83a7-27ab-c450-fbcc04b6e901.png">

# リアルタイム人物検出
まだやってません。次にリアルタイム検出や、転移学習もやってみたいです。

# まとめ
openCVでは画像のサイズが小さい、背景が単色で人物がはっきり映っているものだとそれなりの精度で検出できていました。
ただ、複雑な画像にになると誤認識が多くなってしまうようでした。
比べて、SSDはすごかった。やってみた感想は、ほぼ完璧と言ってしまいたくなるくらいでした。

# 参考サイト
- [OpenCVを使った顔認識（Haar-like特徴分類器）](https://qiita.com/hitomatagi/items/04b1b26c1bc2e8081427)
- [SSD: Single Shot MultiBox Detector 高速リアルタイム物体検出デモをKerasで試す](https://qiita.com/PonDad/items/6f9e6d9397951cadc6be)
- [CNNを用いた物体検出アルゴリズムの性能比較](https://qiita.com/tokkuman/items/3fabd04a1a524843bea5)
- [Deepに理解する深層学習による物体検出 by Keras](https://qiita.com/GushiSnow/items/8c946208de0d6a4e31e7)