【物体検出】YOLOv5 で動画のモザイク(ぼかし)処理を自動化してみる【動画編集】

#はじめに

YouTube等に動画を投稿する際に、人の顔や車のナンバーにはモザイク等の処理が必要になるかと思いますが、手作業でモザイク処理を施すにはかなりの労力を要します。
そこで、動画編集の省力化のため、人や車など特定の物体に対して自動でモザイク処理（有料動画編集ソフトなどは使用せず無料でできる方法）を考えます。

ここでは、動画から人や車などの特定の対象物に対してモザイク処理を自動化する一方法についてまとめます。画像中から物体検出により対象を特定したのち、その座標に対してモザイク処理を施します。物体検出はyolov5、モザイク処理はopencvを用います。なお、yolov5であれば、人や車以外にも例えば動物などでも同様に処理が可能です。また必要によりモデルを自作することで、任意の対象物への応用が可能となります。

#本記事の目標(成果物)
以下のように人や車にモザイク処理を自動化します。
物体検出に人を判定してモザイク処理を施します。
（物体検出の精度によってはあまり使えない場合もあるかもしれません。。）
フレームごとにモザイク処理をして、最後は動画に戻します。

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1559455/088f1bce-61d5-f84d-42e5-8b164348733c.png)


https://youtu.be/mtW8LgIl8Lc

https://youtu.be/bi47GhQ3kbA


#opencvによるモザイク処理

モザイク処理はopencvにより行うことができます。
モザイク処理といっても複雑なアルゴリズムは必要なく、画像を一旦縮小してから拡大して元のサイズに戻すだけで実装することが可能です。

```ruby:python

def mosaic(img, alpha):
    w = img.shape[1]
    h = img.shape[0]
    img = cv2.resize(img, (int(w*alpha), int(h*alpha)))
    img = cv2.resize(img, (w, h), interpolation=cv2.INTER_NEAREST)
    return img

```


結果は以下の通りとなります。縮小率alphaによってモザイクの粗さを調整できます。用途に合わせて調整してください。

alpha=0.100の場合
![0100.mp4_0000000052.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1559455/d3caf00b-8fcb-715b-7c59-322d35448f04.jpeg)

alpha=0.075の場合
![0075.mp4_0000000052.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1559455/972ddf2b-e286-1c69-89e6-e53ca7f5a3b9.jpeg)

alpha=0.050の場合
![0050.mp4_0000000052.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1559455/6b8d1d67-59f4-5966-8975-b63499a9724c.jpeg)


#yolov5による物体検出

物体検出の学習済みモデルを使用することで、簡単に実装することができます。
詳細は以前の記事を参照してください。
以前の記事はこちら

https://qiita.com/hkwsdgea_ttt2/items/879c91a4bb5f1a0bb513

```ruby:python
import torch
model = torch.hub.load('ultralytics/yolov5', 'yolov5x', pretrained=True)
```
yolov5では様々な物体を検出することができます。ただし、検出したすべての物体にモザイク処理が必要となるわけでありません。そこで、対象となる物体を指定する必要があります。以下では、人のみ検出する例を示します。


```ruby:python
results.xyxy[0]
results.pandas().xyxy[0]

for j in range(len(results.pandas().xyxy[0])):
   
    if results.pandas().xyxy[0]["class"] [j] == 0:
        ymin = int(results.pandas().xyxy[0]["ymin"][j])
        ymax = int(results.pandas().xyxy[0]["ymax"][j])
        xmin = int(results.pandas().xyxy[0]["xmin"][j])
        xmax = int(results.pandas().xyxy[0]["xmax"][j])                         
        frame[ymin:ymax,xmin:xmax] = mosaic(frame[ymin:ymax,xmin:xmax],  alpha) 
```
`results.pandas().xyxy[0]["class"] [j] =0`とすることで、人間のみを対象とすることができます。
yolov5の中身は以下のように確認できます。
例えば、車の場合は`results.pandas().xyxy[0]["class"] [j] =2`とします。

```ruby:python
print(model.names)
```

`['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush']`

#実装
上記の工程をまとめます。
モザイク処理の座標に物体検出で抽出した座標を適用していきます。各フレームに処理をした後、動画に戻します。

```ruby:python
import glob
import torch
import cv2
import numpy as np

model =torch.hub.load('ultralytics/yolov5', 'yolov5x', pretrained=True)
input_dir = './input/'
output_dir = './output/'

def mosaic(img, alpha):
    w = img.shape[1]
    h = img.shape[0] 
    img = cv2.resize(img, (int(w*alpha), int(h*alpha)))
    img = cv2.resize(img, (w, h), interpolation=cv2.INTER_NEAREST)
    return img

def movie_mosaic(input_video, out_video):    
    video = cv2.VideoCapture(f)
    w = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
    h = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
    size = (w,h)
    frame_count = video.get(cv2.CAP_PROP_FRAME_COUNT)
    frame_rate = video.get(cv2.CAP_PROP_FPS)
    fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
    writer = cv2.VideoWriter(out_video, fmt, float(frame_rate),(w,h))
    num = 0
    while(video.isOpened()):
        ret,frame = video.read()
        if ret == True:            
            results = model(frame)
            results.xyxy[0]
            results.pandas().xyxy[0]
            for j in range(len(results.pandas().xyxy[0])):
                
               if results.pandas().xyxy[0]["class"][j] == 0:
                    ymin = int(results.pandas().xyxy[0]["ymin"][j])
                    ymax = int(results.pandas().xyxy[0]["ymax"][j])
                    xmin = int(results.pandas().xyxy[0]["xmin"][j])
                    xmax = int(results.pandas().xyxy[0]["xmax"][j])                         
                    frame[ymin:ymax,xmin:xmax] = mosaic(frame[ymin:ymax,xmin:xmax],  alpha)                        
                else:
                    pass            
            writer.write(frame)
            print(f,num,frame_count)
            num += 1
        else:
            break
    writer.release()
    video.release()

if __name__ == '__main__':
    alpha = 0.05
    files = glob.glob(input_dir + "*.mp4")
    for f in files:
        input_video = f
        out_video = output_dir+os.path.basename(f)
        movie_mosaic(input_video, out_video)
```
#成果物
動画に対する人や車のモザイク処理を自動化できました。
これを動画に適用することで、動画から特定対象のモザイク処理自動化が実現します。

実際に処理を施した動画です。興味がありましたらご覧ください。

https://youtu.be/mtW8LgIl8Lc

https://youtu.be/bi47GhQ3kbA

#終わりに
最後までご覧いただきありがとうございました。
訂正要望・ご意見等がございましたら、ご連絡頂けますと幸いです。