Raspberry Pi とPython3とOpen CVで顔追跡カメラ

`RaspberryPi`初心者です。
`RaspberryPi`、カメラ、サーボモーターを用いて、顔追跡を行います。
@PonDadさんの記事を参考にさせていただきました。@PonDadさんありがとうございます。

[Raspberry Pi サーボモーターとOpen CVで顔追跡カメラ（Haar-like）](https://qiita.com/PonDad/items/b2769641350aee6085c6)

しかし、自分の環境では、@PonDadさんの記事どおりではうまく動作しなかったため、`face_tracking.py`を以下のとおり変更しています。
![IMG_1193.JPG](https://qiita-image-store.s3.amazonaws.com/0/227952/d9f121a4-7842-07fd-cdc2-c5908ff05173.jpeg)

#環境
- Raspberry Pi3 (RASPBIAN JESSIE WITH PIXEL 4.4/ Python 3.4.2)
- Open CV 3.1.0
- LOGICOOL ウェブカム HD画質 120万画素 C270
- PCA9685 16Channel 12bit PWM（Adafruit 16-Channel Servo Driver互換品）
- SG90サーボ用 2軸 カメラマウント
- ラズパイ用電源とは別にサーボ用の電源としてDCアダプター（5V 2A）をPCA9685へ接続
- コードを実行するためには @PonDadさんの[記事](https://qiita.com/PonDad/items/b2769641350aee6085c6)で紹介されている`PCA9685.py` が必要です。

#概要
`OpenCV`の``Haar-like特徴分類器``を使って顔認識します。
`haarcascade_frontalface_alt.xml` を利用した顔認識を使うと、``cascade.detectMultiScale(）``メソッドを利用して顔の範囲を表す座標を取得する事が出来ます。
この座標から顔の中心点の座標を計算し、ストリーミング画面の中心点と顔の中心点の差が減る方向へ、サーボモータでカメラを移動します。この動作を繰り返すことによって、カメラが顔を追跡するようにします。
#コードの変更点
@PonDadさんの記事からの変更点

- line15　`cap.set(4, 320)` から ``cap.set(4, 240)`` に変更した。デスクトップ上に表示されるストリーミング画像のウィンドウサイズを320*240にした。
- line18から21を追加した。コードを実行する毎にカメラの向きを初期値に戻すため。サーボ2個ともニュートラルである375( Min150～Max600なので`(150+600)/2=375` ）へ移動。
- line23　この行をwhile文の前に出した。
- line27　`now_degree_x`と``now_degree_y``をサーボのニュートラルである375に変更。
- line33から34　`*0.4`を``*0.06``に変更した。掛算の値をいろいろ変更していちばん動きが良かった値に変更した。この部分は試行錯誤の結果で適当に決めた。値が大きいほど早く動くが大きすぎると行き過ぎる。自分の環境では0.04から0.06くらいが良い感じ。
- line34　160を120に変更した。line15の変更に伴い、ストリーミング画像の中心点（＝240/2）を変更した。
- line38　`time.sleep(0.1)` は無くても支障が無かったためコメントアウト。
- line39から40　`now_degree_x = move_degree_x` と``now_degree_y = move_degree_y`` に変更した。

#コード
[face_tracking2.py](https://gist.github.com/9680lhqv/9f99d4f392ecfc79bfb19b0032082cfe)

```face_tracking2.py
# -*- coding: UTF-8 -*-
import cv2
import os
import time
import Adafruit_PCA9685

pwm = Adafruit_PCA9685.PCA9685()
pwm.set_pwm_freq(60)

cascade_path = "haarcascade_frontalface_alt.xml"
cascade = cv2.CascadeClassifier(cascade_path)

cap = cv2.VideoCapture(0)
cap.set(3, 320)
cap.set(4, 240)
color = (255, 255, 255)

pwm.set_pwm(0, 0, 375)
time.sleep(1)
pwm.set_pwm(1, 0, 375)
time.sleep(1)

now_degree_x, now_degree_y, move_degree_x, move_degree_y = 375, 375, 0, 0

while(True):
    ret, frame = cap.read()
    facerect = cascade.detectMultiScale(frame, scaleFactor=1.2, minNeighbors=2, minSize=(10, 10))

    for rect in facerect:
        img_x = rect[0]+rect[2]/2
        img_y = rect[1]+rect[3]/2
        print(img_x, img_y)
        move_degree_x = now_degree_x - (img_x-160)*0.06
        move_degree_y = now_degree_y + (img_y-120)*0.06
        print('deg: ', move_degree_x , move_degree_y)
        pwm.set_pwm(0, 0, int(move_degree_x))
        pwm.set_pwm(1, 0, int(move_degree_y))
        #time.sleep(0.1)
        now_degree_x = move_degree_x
        now_degree_y = move_degree_y
        cv2.circle(frame, (int(img_x), int(img_y)), 10, (255,255,255), -1)
        cv2.rectangle(frame, tuple(rect[0:2]),tuple(rect[0:2] + rect[2:4]), color, thickness=3)

    cv2.imshow("Show FLAME Image", frame)

    k = cv2.waitKey(1)
    if k == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
```

#感想など
顔追跡のためのサーボ制御がなかなかうまくいかず、試行錯誤しながら上記のコードを変更しました。
プログラミングのことが全くわからない初心者なので、間違えが沢山あるかもしれません。どなたかご指摘いただけるとありがたいです。
`python face_tracking2.py`だと``python2``で動作してしまうため、 ```python3 face_tracking2.py```で実行しています。