画像やWebカメラを加工してみる パート３ ～モザイク処理編～

# はじめに
Pythonを使って、画像やWebカメラを加工してみた パート３

<a href="https://qiita.com/str32/items/3dda077713095df850c8" rel="nofollow noopener" target="_blank">前回</a>に引き続き、 **OpenCV** を使って画像（映像）加工をやってみた
今回は画像とWebカメラの映像にモザイクを入れる処理！

お品書きは下記の通り

* モジュール，ディレクトリ構造など
* 画像モザイク
* Webカメラモザイク

# モジュール，ディレクトリ構造など

### モジュール
モジュールは前回と同様のものを使用
必要があればインストールしてください！

```
pip install opencv-python # OpenCVの機能を使用するときに必要(python上ではcv2で使う)
pip install numpy         # 数値計算を効率的に行うための拡張モジュール
pip install os            # osに依存している機能を利用できる
```

### ディレクトリ構造
ディレクトリ構造は下記の通り

```
-- rgb.py
   -- camera_mosaic.py # new
   -- color_process.py
   -- photo_process.py
   -- photo_mosaic.py  # new
   -- rgb.py
   -- img
      -- fruit.jpg
```

写真は前回同様、果物たちを使用する
![fruit.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435504/687d55f0-8346-d7d4-2811-9aac8e392426.jpeg)

# 画像モザイク
モザイクをかけたい部分を縮小しもとに戻す

### プログラム

```python:photo_mosaic.py
import cv2
import os

PATH = './img'
FILES = os.listdir(PATH)

OLD_WINDOW_NAME = 'old'
NEW_WINDOW_NAME = 'new'
ratio = 0.1 # モザイクの粗さ

# get file name
def get_file_name():
    print('*** All  Pictures ***')
    print(*FILES, sep='\n')
    print('***      End      ***')
    
    while True:
        file_name = input('Which use file name ?: ')
        if file_name in FILES:
            return file_name
        else:
            print('not exist: ' + file_name)

# モザイク処理
def mosaic(src):
    small = cv2.resize(src, None, fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)
    return cv2.resize(small, src.shape[:2][::-1], interpolation=cv2.INTER_NEAREST)

# モザイク部分を設定
def mosaic_area(src, x, y, width, height):
    dst = src.copy()
    dst[y:y + height, x:x + width] = mosaic(dst[y:y + height, x:x + width])
    return dst

if __name__ == '__main__':
    
    old_file_name = get_file_name()
    file_prefixes = old_file_name.rsplit('.', 1)
    new_file_name = file_prefixes[0] + '_mosaic.' + file_prefixes[1]

    # 元の画像を読み込む
    old_img = cv2.imread(PATH + '/'+ old_file_name)
    
    # モザイク化
    new_img = mosaic_area(old_img, 100, 50, 100, 150)
    
    # ウィンドウを作成
    cv2.namedWindow(OLD_WINDOW_NAME)
    cv2.namedWindow(NEW_WINDOW_NAME)

    # ウィンドウに表示
    cv2.imshow(OLD_WINDOW_NAME, old_img)
    cv2.imshow(NEW_WINDOW_NAME, new_img)

    # ファイルに保存
    cv2.imwrite(r'img/{}'.format(new_file_name), new_img)

    # 終了処理
    cv2.waitKey(0)
    cv2.destroyAllWindows()
```

### 少し解説

> dst = src.copy()

src(元の画像)をdst(加工する画像)にコピーする
最初は以下のようにコーディングしていて頭を抱えた…
`dst = src`
※気になる方は実行してみてどうなるか試してみたください(笑)

> dst[y:y + height, x:x + width] = mosaic(dst[y:y + height, x:x + width])

dstで指定した範囲をmosaic関数から送られてきた画像に変える処理
xとyが逆になっているので注意！

> small = cv2.resize(src, None, fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)

cv2.resizeで画像のサイズを変更
ratio が小さいほど粗く，大きいほど細かくモザイク加工できる

INTER_NEARESTは最近傍補間法というもの
最近傍にある画素をそのまま使用する方法

> return cv2.resize(small, src.shape[:2][::-1], interpolation=cv2.INTER_NEAREST)

元の大きさに戻す
src.shapeで配列の形状を取得
今回は(150, 100, 3)なので、100×3の150次元配列です
これを加工していくと以下のようになる

```
src.shape => (150, 100, 3)
src.shape[:2] => (150, 100)
src.shape[:2][::-1] => (100, 150) 
```

これで元の大きさに戻るらしい

### 出力結果

コマンドライン
![gazou3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435504/a76edea9-621c-9bbf-df3d-63467d21befc.png)

画像モザイク
ratio = 0.1のとき
![fruit_mosaic_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435504/108d1209-2715-3732-7d92-96a40b75e11d.jpeg)

ratio = 0.9のとき
![fruit_mosaic_06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435504/709b2195-ccf2-9280-8698-83b13913a805.jpeg)


# Webカメラモザイク
webカメラを使用して、映像に全体モザイクを入れてみた
全体モザイクするには、mosaic_area関数を通らずにモザイク処理をしてあげる

### プログラム

```python:camera_mosaic.py
import cv2

cap = cv2.VideoCapture(0)
ratio = 0.1

def mosaic(src):
    small = cv2.resize(src, None, fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)
    return cv2.resize(small, src.shape[:2][::-1], interpolation=cv2.INTER_NEAREST)

if __name__ == '__main__':

    while True:
        ret, old_frame = cap.read()
        copy_frame = old_frame .copy()
        # モザイク化
        new_frame = mosaic(copy_frame)

        cv2.imshow('OldRawFrame', old_frame)
        cv2.imshow('NewRawFrame', new_frame)
        
        k = cv2.waitKey(1)
        if k == 27:
            break
            
    cap.release()
    cv2.destroyAllWindows()
```

### 少し解説

> cap = cv2.VideoCapture(0)

動画又は、カメラ映像を読み込む
()内に動画パスを入力すると動画データが読み込まれる

カメラ映像の場合は0が内蔵カメラ，1がUSBカメラとのこと…？
※私のPCには内蔵カメラがなかったため0がUSBカメラになっていました

> ret, old_frame = cap.read()

読み込んだ動画を1フレームずつ読み込んでいく
ret にはTrue/Falseが入力される
正しく読み込まれない場合、ret = False となり、強制終了される

old_flameには1フレーム画像データが入力される
while文でループすることにより、映像になっている

> k = cv2.waitKey(1)
> if k == 27:
>     break

プログラム終了用
いつもは `cv2.waitKey(0)` を使用するのですが、
動画のときは、 `cv2.waitKey(1)` を使用する

k == 27 (Escキーを押す)の時だけwhileから脱出
if文の前にprint(k)を置けばボタン番号がわかるので、お好きな番号でどうぞ！

### 出力結果

```
python camera_mosaic.py
```
上記スクリプトを実行すると全体にモザイクがかかったウェブカメラが動き出すはず

（なぜかGIFが添付できなかったので、HUEちゃんの静止画をご覧ください…）
![hue.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435504/c139eaea-995a-3533-4b4e-8d161cd514a4.png)

思った以上にwebカメラの遅延がなかったので、いろいろこれで作れそう
次回は顔を認識してモザイクかけちゃうプログラムを作成する予定( ˘ω˘ )