python + OpenCVで画像を加工② 画像の中から顔を検出する

[前回の記事](https://qiita.com/HIROKOBA/items/d1b076bef22932be5eea)からの続きになります。写真の中から顔を検出して、検出したことが分かるように四角で囲うまでをやってみようと思います。

最初に、今回のコードの全体はこちらになります。

```python
import cv2

#分類器を読み込む
cascade_file= "haarcascade_frontalface_default.xml"
clas = cv2.CascadeClassifier(cascade_file)

#画像を読み込んで分類器にかける
img = cv2.imread("family_photo.jpg")
face_list = clas.detectMultiScale(img, minSize=(30,30))

#顔の座標を読み込んで四角の縁取りをする
for x, y, w, h in face_list:
    red=(0,0,255)
    cv2.rectangle(img, pt1=(x,y), pt2=(x+w, y+h), color=red, thickness=5)

#加工した写真を保存
cv2.imwrite("family_photo_processed.jpg",img)
```

####カスケード分類器
OpenCVでカスケード分類器の学習済みファイルが準備されています。顔の識別ファイル、目の識別ファイルなどたくさんあります。

今回は顔を識別するので "haarcascade_frontalface_default.xml" を使います。
https://github.com/opencv/opencv/tree/master/data/haarcascades
この分類器をダウンロードしてローカルPCに保存。

コードの最初に分類器を"Classifier"に代入しておきます。

```python
cascade_file= "haarcascade_frontalface_default.xml"
Classifier = cv2.CascadeClassifier(cascade_file)
```

####画像を取り込んで分類器にかける
まずは画像を取り込みます。

```python
img = cv2.imread("Hiro.jpg")
face_list = Classifier.detectMultiScale(img, minSize=(30,30))
```
imread()については[前回の記事](https://qiita.com/HIROKOBA/items/d1b076bef22932be5eea)で触れています。

####Classifier.detectMultiScale()
```python
face_list = Classifier.detectMultiScale(img, minSize=(30,30)) 
```
そしてこの一行で顔を検出して、その座標を配列でface_listに代入しています。[detectMultiScale()について詳しい解説はこちら。](https://docs.opencv.org/master/d1/de5/classcv_1_1CascadeClassifier.html#aaf8181cb63968136476ec4204ffca498)
ここでは検出器にかける画像と、検出する顔の最小サイズのみ指定していますが、その他引数を細かく指定できます。。。が、試してみた感想としては、難易度の高い顔検出でない限りはデフォルトの値で問題ないと思いました。私が将来的にこのあたりの設定まで使いこなせるようになったら追記したいと思います。

試しにprint(face_list)で Classifier.detectMultiScale() の戻り値を出力してみると。

```python
import cv2

cascade_file= "cascade_classification.xml"
Classifier = cv2.CascadeClassifier(cascade_file)

img = cv2.imread("family_photo.jpg")
face_list = Classifier.detectMultiScale(img, minSize=(30,30))
print(face_list)

#出力結果
#[[230 133  82  82]
# [187 271  81  81]
# [ 84 220  86  86]
# [274 229  82  82]
# [373 197  95  95]
# [197 342  61  61]]
```
このような配列がかえってきます。顔を検出し、それぞれの[x座標、y座標、幅、高さ]
を配列で返しています(x座標とy座標は長方形の左上の角の座標)。

####検出した座標を変数に代入→ループ処理

```python
for x, y, w, h in face_list:
    red=(0,0,255)
    cv2.rectangle(img, pt1=(x,y), pt2=(x+w, y+h), color=red, thickness=5)
```
検出された顔をすべて処理するために、[x座標、y座標、幅、高さ]の情報をそれぞれ x, y, w, h に代入してループ処理。

####rectangle()
rectangle()は「長方形」のこと。[公式ドキュメント](http://opencv.jp/opencv-2svn/cpp/drawing_functions.html)を見ると、これも引数がたくさんあります(公式ドキュメントでは長方形と言わず「矩形」という記述になっています)。

> img – 画像．
pt1 – 矩形の1つの頂点．
pt2 – pt1 の反対側にある矩形の頂点．
color – 矩形の色，あるいは輝度値（グレースケール画像）．
thickness – 矩形の枠線の太さ． CV_FILLED などの負の値の場合，塗りつぶされた矩形が描かれます．
lineType – 枠線の種類． line() を参照してください．
shift – 点の座標において，小数点以下の桁を表すビット数．
関数 rectangle は，線だけの矩形，あるいは塗りつぶされた矩形を描きます． pt1 と pt2 は，その矩形の対角線上にある頂点を表します．

ここでも引数について詳しい説明がされていますが、特にlineTypeとshiftについては使い分けても違いが分からないと思いましたので割愛いたします(記述しなくてもエラーにはなりません)。細かくは公式ドキュメントの記述を参照なさってください。

マストで記述しなければならないのはimg, pt1, pt2, colorになります。

さて一番の問題はpt1、pt2ですが、一目瞭然、という言葉を信じてちょっと図を書いてみました。これで分かると思うのですが、いかがでしょうか。
![rectangle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634245/3d5a921b-df77-5a56-6916-5fe7ad05022c.png)
上の図を踏まえて記述したのが下記のコードになります。imgで対象の映像を指定した後、pt1(左上), pt2(右下)の順番で座標を指定しています。要するに左上と右下の座標を指定することで四角の範囲を決めているということですね。

```python
cv2.rectangle(img, pt1=(x,y), pt2=(x+w, y+h), color=red, thickness=5)
```
そしてcolorを指定。colorはRGBで指定しなければなりません(色の指定がないとエラーになります)。ここでは

```python
red=(0,0,255)
```
でredに数値を代入して使っています。

thickness は指定しなくても問題ありませんが、デフォルトだと細い線しか表示されないので5ピクセルを指定しました。

ちなみに、"pt1="などの記述を省略して数値だけを書いてもコードは動きますが、視認性が悪くなるので書いた方が分かりやすいかもしれないと思いました。

実際にコードを実行すると、人物の顔が赤い四角で囲われていると思います。

全体のコードを下に記述しておきます。

```python
import cv2

#分類器を読み込む
cascade_file= "haarcascade_frontalface_default.xml"
clas = cv2.CascadeClassifier(cascade_file)

#画像を読み込んで分類器にかける
img = cv2.imread("family_photo.jpg")
face_list = clas.detectMultiScale(img, minSize=(30,30))

#顔の座標を読み込んで四角の縁取りをする
for x, y, w, h in face_list:
    red=(0,0,255)
    cv2.rectangle(img, pt1=(x,y), pt2=(x+w, y+h), color=red, thickness=5)

#加工した写真を保存
cv2.imwrite("family_photo_processed.jpg",img)
```

満点とはいきませんでしたが、一応顔の検出ができるようになったので、次回はモザイク処理をやってみたいと思っています。