自動モザイク生成

どうも、Deep learningを勉強中のかわたくです。
今回は画像に自動でモザイクを生成するプログラムをやってみました。

# 実行環境
MacOS, VScode, python3.6(anaconda)

# 実行プログラム

今回のプログラムを書く上では、
[30行で顔認識(OpenCV)してファイル出力！](https://qiita.com/hiraku00/items/de0c1d0e159d65328a1b)
[Python, OpenCVで画像にモザイク処理（全面、一部、顔など）](https://note.nkmk.me/python-opencv-mosaic/)
[opencv の基本的な画像変形： 全12実例！](https://blog.capilano-fw.com/?p=1990#i-7)
これらの記事を参考にさせていただきました!

```python

# Man〜Womenみたいにstr型で格納されているフォルダはこのpathに付け加えてからじゃないと画像を表示できない
DATADIR = "/Users/ユーザー名/Documents/書類 - MacBook Pro/HelloWorld.py/"
```
まずは、こちらのように画像ファイルが保存されている、フォルダがあると思いますが、その1つ前までのパスを変数に代入します。
今回ですと、`HelloWorld.py`というフォルダの中に、`Man〜Women`のフォルダが入っています。

```python

# haarcascadeのxmlファイルの読み込み(正面の顔を読み込む)
xml_path = "./haarcascade_frontalface_default.xml"
```
Cascadeで部位を検出することが可能です。
`・正面の顔検出：haarcascade_frontalface_default_xml
・瞳検出：haarcascade_eye.xml
・笑顔検出：haarcascade_smile.xml`

それぞれ何を検出したいのかを考えて読み込みをすると良いです。

```python

Categories = ["Man", "Men", "Woman", "Women"]
img_size = 250
mosaic_size = 10
training_data = []

# 顔検出のためのプログラム + データセット生成
def create_training_data():  # Man->Womenの順に引数
    # 顔領域識別器をセット
    classifier = cv2.CascadeClassifier(xml_path)
    cnt = 1   

    for class_num, category in enumerate(Categories):   # enumerateでインデックスと要素の2つを取り出せる
        path = os.path.join(DATADIR, category)  # Categoriesの1つずつの要素とDATADIRを結合　-> Man〜Womenにアクセスが可能
         
        for image_name in os.listdir(path): # ManやWomanの写真の一覧を取得 -> image_nameは全部str型        
            
            # グレースケール画像(識別用) <- 1枚ずつ画像を取り出す
            gray_img = cv2.imread(os.path.join(path, image_name), cv2.IMREAD_GRAYSCALE)    # 一連のpathじゃなきゃimreadは読み込めない -> image_nameだけではエラーが出る 
            # x座標、y座標、横幅、縦幅が戻り値となる
            face_points = classifier.detectMultiScale(gray_img, minSize=(20, 20))
        
            # 識別結果よりカラー画像をトリミングする
            for x, y, width, height in face_points: # 顔領域の座標点取得
                # 顔領域をトリミング
                dst_img = gray_img[y:y+height, x:x+width] # 画像は縦×横になっている
                face_img = cv2.resize(dst_img, (mosaic_size, mosaic_size), interpolation=cv2.INTER_NEAREST)    # 画像のリサイズ -> resize(リサイズする画像, (行, 列))
                mosaic_img = cv2.resize(face_img, (width, height), interpolation=cv2.INTER_NEAREST) # 元のサイズに戻す -> (width, height)で戻っている
                gray_img[y:y+height, x:x+width] = mosaic_img    # モザイクした部分を元の画像に挿入している
                cv2.imwrite('mosaic' + str(cnt) + '.jpg', gray_img)    # 画像を保存
            try:    
                training_data.append([gray_img, class_num]) # 画像データ、ラベル情報を追加 -> class_numでインデックス番号もリストに格納
            except Exception as e:  # Exceptionという例外の型が来たらエラーにせず、スルー
                pass  
            cnt += 1 

create_training_data()
```
#### cv2.imread()：画像ファイルの読み込みが可能
しかし、データの型が`3次元ndarray`の形になりますので注意が必要です。
第2引数で画像のタイプを決定できます。今回ですと、`グレースケール画像`です。

#### detectMultiScale：画像の中で検出できる物体のレベルを変化させられる
`・scaleFactor：大きいほど誤検出するし、小さいほど未検出する。1.01に近づけるほど細かく検出ができる
┗大きいと広い範囲を検出できる
・minNeighbors：大きいほど信頼性が高い⇄顔を見逃す可能性が上がる
┗検出する場所が重複する→重複している物体は信頼性が高い
・minSize：物体が取りうる最小のサイズ、これより小さい物体は無視される`

＊戻り値は`(x座標、y座標、横幅、縦幅)`になる

#### imwrite：画像ファイルを保存できる
・第一引数→ファイルのpath(.jpgとかを最後につける)
・第二引数→保存したい画像の変数(ndarray)

#### モザイク生成方法
①モザイクを行いたい領域をトリミングする。
②その部分の画像サイズを一回小さくする。
③元のサイズに戻す
④モザイク化された部分を元の画像に入れ込む

＊②→③のタイミングで**画像の粗さを粗くしている**ため、元のサイズにしたら画像がモザイク状になる。
＊**inter_nearestをinterpolationに代入→1番滑らかさが雑→モザイクに適している**

```python

random.shuffle(create_training_data)   # データをシャッフル


x_train = []    # 画像データ
t_train = []    # 正解ラベル

# データセット作成 -> featureには奇数番目が、labelには偶数番目が入っていく
for feature, label in create_training_data:
    x_train.append(feature)
    t_train.append(label)

# numpy配列に変換
x_train = np.array(x_train)
t_train = np.array(t_train)

# データセットの確認
for i in range(0, 4):
    print("学習のデータのラベル", t_train[i])
    plt.subplot(2, 2, i+1)  # subplot(何行, 何列, 描きたい領域)
    plt.axis('off') # 座標軸を非表示にする
    # インデックス番号が0か1なら男性を、2か3なら女性とラベルつける
    if t_train[i] == 0 or t_train[i] == 1:
        plt.title(label =  "Man")
        # plt.title("男性", fontname="MS Gothic") # -> 日本語表記が可能
    else:
        plt.title(label = 'Woman')
        # plt.title("女性", fontname="MS Gothic") # -> 日本語表記が可能
    plt.imshow(x_train[i], cmap='gray')

plt.show()

```
こっち側のコードはモザイク生成には関係のないコードですが、一応モザイクがきちんと行われているかの確認のためにも使わせてもらいました。
あとは、データセットは今回作る必要はなかったのですが、データセットができる流れも掴むことができました。

# 今回は使わなかったメモ
#### isinstance：その変数がその型かどうかをTrueかFalseで返す
`・第一引数に調べたい変数
・第二引数に型`

#### setメソッド
{}で囲む、重複のある要素は無視される

#### os.remove(‘ファイルパス’)：不要ファイルを削除


# 感想
当初の想像していたよりも簡単な実装に終わりました。
データセットを作成してから、複雑な処理をしなければならないのかなと思っていましたが、全然そんなことなくてすぐに終わりました。
やはり、顔検出の`Cascade`が便利だったなと思います。
あとは、`モザイクの方法がめちゃくちゃ簡単だった`というのもあげられますね。

最初は難しいと思っていて、もっと準備必要かなと思っていましたが、作り始めてみたら楽しかったです。


めちゃくちゃコメントの多いコードになりましたが、その処理が何をしているのかをメモしておかないと忘れてしまい、パニックになるので、書かせてもらいました。


# まとめ
今回初めて自分の力で最初から最後まで実装することができました。
まだ全然大したことないプログラムですが、ここからたくさんのプログラムを書けていければ良いなと思います。