python + OpenCVで画像を加工⑤ 動画の顔を検出してモザイクをかける

[前回の記事](https://qiita.com/HIROKOBA/items/ad593af066579228dc38)で画像の中から顔を検出し、モザイクをかけることができるようになりました。

今回は動画の中から人の顔を検出してモザイクをかけてみようと思います。

動画だと難しそう・・・と尻込みしてしまいそうですが「静止画像の連続が動画である」と考えると、それほど怖がる必要もないのかなという印象です。

最終的なコードはこちらになります。

```python
import cv2

#モザイク処理を施す関数
def mosaic(img, mosaic_rate):
    w = img.shape[1]
    h = img.shape[0]

    #画像を縮小→その後元の大きさに戻すことがモザイク処理
    img = cv2.resize(img, (int(w * mosaic_rate), int(h * mosaic_rate)))
    img = cv2.resize(img, (w, h), interpolation = cv2.INTER_NEAREST)

    return img
    
#識別器を読み込む
cascade_file= cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
clas = cv2.CascadeClassifier(cascade_file)
cap = cv2.VideoCapture("movie2.mp4")

#１フレームずつモザイクをかける
while True:
    ret, frame = cap.read()
    
    #retがFalseの場合は終了
    if not ret:
        break

　　　　#グレースケールに変換
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    #１フレーム（＝静止画）の顔をすべて検出して周囲の四角形の座標を取得
    #検出できた顔のすべてについて[四角の左上のx座標、四角の左上のy座標、幅、高さ]の数値を取得
    face_list = clas.detectMultiScale(gray, scaleFactor = 1.1, minNeighbors = 3, minSize=(30,30))

    #モザイク処理
    #[四角の左上のx座標、四角の左上のy座標、幅、高さ]をそれぞれ変数x, y, w, hに代入
    for x, y, w, h in face_list:
       frame[y : y+h, x : x+w] = mosaic(frame[y : y + h, x : x + w], 0.05)

    cv2.imshow("frame", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

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

今回は下記の部分を「モザイク関数」として切り出しました。
```python
def mosaic(img, mosaic_rate):
    w = img.shape[1]
    h = img.shape[0]

    #画像を縮小→その後元の大きさに戻すことがモザイク処理
    img = cv2.resize(img, (int(w * mosaic_rate), int(h * mosaic_rate)))
    img = cv2.resize(img, (w, h), interpolation = cv2.INTER_NEAREST)

    return img
```
顔の部分一度縮小してその後拡大することでモザイク化し、その結果を返す処理をしています。
特に下記の部分。
```python
w = img.shape[1]
h = img.shape[0]
```
OpenCVで画像を読み込むと列の数、行の数などの属性情報がタプルで付与されるので、shape[]で取得しています（[こちらのチュートリアル「画像の属性情報の取得」参照](http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_core/py_basic_ops/py_basic_ops.html)）。モザイク処理の「画像縮小→拡大」あたりの説明は[以前の記事](https://qiita.com/HIROKOBA/items/92c6b8584b615b7ec401)で書いた内容が参考になるかと思います。

##### 識別器の読み込み
```python
cascade_file= cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
```
OpenCVが準備している分類器をダウンロードして使っていたのですが、上記の記述でダウンロードせずに使えるそうなので活用しています。

##### whileループで動画処理
そしてここから始めるwhileループが動画向けの処理になります。
```python
while True:
    ret, frame = cap.read()
    
    #retがFalseの場合は終了
    if not ret:
        break
```
ret, frame = cap.read() で画像の１フレーム分を読み込んで、戻り値をそれぞれretとframeに代入しています。
①retに代入される値は、正しくフレームが読み込まれていればtrue,そうでなければfalse
②読み込んだ画像のデータを数値化したもの
##### あとは写真と同じ処理をして、whileループで繰り返す

```python
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    face_list = clas.detectMultiScale(gray, scaleFactor = 1.1, minNeighbors = 3, minSize=(30,30))

    for x, y, w, h in face_list:
       frame[y : y　+　h, x : x　+　w] = mosaic(frame[y : y + h, x : x + w], 0.05)

    cv2.imshow("frame", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
```
上記は[前回の記事](https://qiita.com/HIROKOBA/items/ad593af066579228dc38)で説明した通り、顔のある部分にモザイクを当てて、動画ではなく静止画を表示しています。あくまで「動画ではなく静止画」です。

ここでモザイクをかけた静止画を１枚表示した後、whileループの先頭に戻り、次の１フレームの顔部分にモザイクをかけて表示→whileループの先頭に戻る →　次の１フレームの顔部分にモザイクをかけて表示・・・という作業を高速で繰り返しています。要はパラパラ漫画の要領で、人間の目には動画に見えるというわけですね。

モザイク処理済みの動画を掲載しようとしたのですが、動画を埋め込むのが上手くいかなかったので今回はありません。。