ノイズ除去【TensorFlow入門①】

### <font color="BlueViolet">このページについて</font>
TensorFlowというライブラリを使ってノイズ除去をします．

### <font color="BlueViolet">今回紹介するコードの大まかな流れ</font>
今回紹介するプログラムは、ノイズ除去オートエンコーダーを使って、ノイズが追加された画像を元の画像に復元することを目的としています。以下で，このプログラムの流れを簡潔に説明します。

##### ライブラリのインポート:
必要なライブラリ（numpy, matplotlib, tensorflow.keras, mnistデータセットなど）をインポートします。

##### データの準備:
MNISTデータセットをロードし、CNNで扱いやすい形に変換して正規化します。

##### ノイズの追加:
マスキングノイズとガウシアンノイズを追加する関数を定義し、トレーニングデータとテストデータにノイズを追加します。

##### オートエンコーダーモデルの構築:
エンコーダーとデコーダーからなるオートエンコーダーモデルを構築します。

##### モデルのコンパイル:
モデルをコンパイルし、最適化アルゴリズムと損失関数を設定します。

##### モデルの学習（ガウシアンノイズ）:
ガウシアンノイズが追加されたデータでモデルを学習させます。

##### 予測と表示（ガウシアンノイズ）:
テストデータに対する予測を行い、元の画像、ノイズが追加された画像、予測された画像を表示します。

##### モデルのリセット:
初期重みにリセットします。

##### モデルの学習（マスキングノイズ）:
マスキングノイズが追加されたデータでモデルを学習させます。

##### 予測と表示（マスキングノイズ）:
テストデータに対する予測を行い、元の画像、ノイズが追加された画像、予測された画像を表示します。



### <font color="BlueViolet">コードと解説</font>

``` Python
# ライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import Sequential
from tensorflow.keras.preprocessing.image import array_to_img
from tensorflow.keras.layers import Conv2D, MaxPooling2D, UpSampling2D
from tensorflow.keras.datasets import mnist
```

必要なライブラリをインポートします。numpyは数値計算、matplotlibはプロット、tensorflow.kerasは深層学習モデルの構築に使用します。

``` Python
# MNISTデータセットをロード
(x_train, _), (x_test, _) = mnist.load_data()
```
MNISTデータセットをロードします。このデータセットは手書き数字の画像データです。
ちなみに，"_"は、Pythonで「無視する」ことを意味している．通常，mnist.load_data()はトレーニングデータのラベルとテストデータのラベルも返してくるが，今回はそれらを無視しているということです．

``` Python
# データをCNNで扱いやすい形に変換し、0-1の範囲に正規化
x_train = x_train.reshape(-1, 28, 28, 1) / 255.0
x_test = x_test.reshape(-1, 28, 28, 1) / 255.0
```
データをCNN（畳み込みニューラルネットワーク）で扱いやすい形に変換し、0から1の範囲に正規化します。

"x_train.reshape(-1, 28, 28, 1)"の引数の-1は自動的に適切なサイズに調整することを意味している．

``` Python
# 画像を表示する関数
def show_image(image):
    plt.imshow(image.squeeze(), cmap='gray')
    plt.axis('off')
    plt.show()

# 画像を表示
show_image(x_train[0])
```
最初のトレーニング画像を表示します。

``` Python
# マスキングノイズを追加する関数
def make_masking_noise_data(data_x, percent=0.1):
    size = data_x.shape
    masking = np.random.binomial(n=1, p=percent, size=size)
    return data_x * masking
```
マスキングノイズを追加する関数を定義します。指定した割合でピクセルをランダムにマスクします。

percentはノイズの割合を指定する引数で、デフォルト値は0.1（10%）です。

np.random.binomial関数は，バイナリ（0または1）のマスクを生成する関数です．
nは試行回数，pはノイズの割合です．

例えば、percent=0.1 の場合、生成されるマスクの約10%が0（ノイズが追加される部分）、残りの90%が1（ノイズが追加されない部分）になる．

``` Python
# ガウシアンノイズを追加する関数
def make_gaussian_noise_data(data_x, scale=0.8): # data_xは入力データ、scaleはノイズの標準偏差（デフォルトは0.8）
    gaussian_data_x = data_x + np.random.normal(loc=0, scale=scale, size=data_x.shape) # np.random.normal関数を使って、平均0、標準偏差scaleの正規分布に従うノイズを生成して，生成したノイズを入力データに加えている．
    return np.clip(gaussian_data_x, 0, 1) # np.clip関数を使って、データの値を0から1の範囲にクリップ
```
ここでは，ガウシアンノイズを定義する関数を定義している．

``` Python
# ノイズデータを作成
x_train_masked = make_masking_noise_data(x_train)
x_test_masked = make_masking_noise_data(x_test)
x_train_gauss = make_gaussian_noise_data(x_train)
x_test_gauss = make_gaussian_noise_data(x_test)

# ノイズ画像を表示
show_image(x_train_gauss[0])
show_image(x_train_masked[0])
```

マスキングノイズを加えたデータと，ガウシアンノイズを加えたデータを，それぞれ作成します．

その後，display_png関数を使って、それぞれのノイズが追加された画像を表示している．
（配列の中にたくさんの画像がある中で，一番最初の画像を1枚だけ表示している．）


``` Python
# オートエンコーダーモデルの構築
autoencoder = Sequential()

# エンコーダー部分
autoencoder.add(Conv2D(16, (3, 3), activation='relu', padding='same', input_shape=(28, 28, 1)))
autoencoder.add(MaxPooling2D((2, 2), padding='same'))
autoencoder.add(Conv2D(8, (3, 3), activation='relu', padding='same'))
autoencoder.add(MaxPooling2D((2, 2), padding='same'))

# デコーダー部分
autoencoder.add(Conv2D(8, (3, 3), activation='relu', padding='same'))
autoencoder.add(UpSampling2D((2, 2)))
autoencoder.add(Conv2D(16, (3, 3), activation='relu', padding='same'))
autoencoder.add(UpSampling2D((2, 2)))
autoencoder.add(Conv2D(1, (3, 3), activation='sigmoid', padding='same'))

# モデルのコンパイル
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')

# モデルの概要を表示
autoencoder.summary()
```
オートエンコーダーモデルを構築しています。エンコーダー部分で画像を圧縮し、デコーダー部分で元の画像に復元します。モデルをコンパイルし、概要を表示しています。

``` Python
# 初期重みを保存
initial_weights = autoencoder.get_weights()

# ガウシアンノイズデータで学習
autoencoder.fit(x_train_gauss, x_train, epochs=10, batch_size=20, shuffle=True)

# テストデータに対する予測
gauss_preds = autoencoder.predict(x_test_gauss)
```

autoencoder.fit(x_train_gauss, x_train, epochs=10, batch_size=20, shuffle=True)の行では，オートエンコーダーモデルを学習させている．入力データはx_train_gaussであり，これはガウシアンノイズが追加されたトレーニングデータのことである．ターゲットデータはx_trainであり，これは元のクリーンなデータ．

つまり，ノイズが追加されたデータをクリーンなデータに変換するような感じで学習させているっぽい．


``` Python
# 画像を表示する関数
def show_images(images, titles=None): #imagesは表示する画像のリスト、titlesは各画像のタイトルのリスト（オプション）
    plt.figure(figsize=(15, 5)) # 新しい図を作成し、図のサイズを設定します。ここでは、幅15インチ、高さ5インチに設定しています。
    for i, image in enumerate(images): # imagesリストをループし、各画像とそのインデックスを取得
        plt.subplot(1, len(images), i + 1) # サブプロットを作成します。1は行数、len(images)は列数、i + 1は現在のプロットの位置を示します。
        plt.imshow(image.squeeze(), cmap='gray') # 画像を表示します。image.squeeze()は不要な次元を削除し、cmap='gray'はグレースケールで表示することを指定します。
        if titles: # titlesが指定されている場合、以下のコードを実行します。
            plt.title(titles[i]) # 各画像に対応するタイトルを設定します。
        plt.axis('off') # 軸を表示しないように設定します。
    plt.show() # すべての画像を表示します。
```
この関数は、複数の画像を一度に表示するためのものです。


``` Python
# 結果を表示
for i in range(10):
    show_images([x_test[i], x_test_gauss[i], gauss_preds[i]], titles=['Original', 'Noisy', 'Denoised'])
    print('-' * 25)
```
x_test, x_test_gauss, gauss_predsについて，最初の10枚の画像をそれぞれ表示させている．

``` Python
# 初期重みにリセット
autoencoder.set_weights(initial_weights)

# マスキングノイズデータで学習
autoencoder.fit(x_train_masked, x_train, epochs=10, batch_size=20, shuffle=True)

# テストデータに対する予測
masked_preds = autoencoder.predict(x_test_masked)

# 結果を表示
for i in range(10):
    show_images([x_test[i], x_test_masked[i], masked_preds[i]], titles=['Original', 'Noisy', 'Denoised'])
    print('-' * 25)

```
初期重みにリセットし、マスキングノイズデータで再度学習させ、結果を表示しています。

このプログラムは、ノイズのある画像を元の画像に復元するためのオートエンコーダーを構築し、異なる種類のノイズに対してその性能を評価するものです。

### <font color="BlueViolet">実行結果</font>
いっぱい画像が出力されたんで，一部だけをピックアップして紹介します．

**↓マスキングノイズのノイズ除去結果はこんなかんじ．**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3700895/3c2cdc62-a078-de96-34bc-fe86dd9cf58d.png)

**↓ガウシアンノイズのノイズ除去結果はこんなかんじ．**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3700895/e7f8cda1-641c-c148-a1ba-fbe30d46011e.png)




### <font color="BlueViolet">参考にさせてもらった文献</font>

https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B%EF%BC%81TensorFlow%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-Keras%E3%81%AB%E3%82%88%E3%82%8B%E6%B7%B1%E5%B1%A4%E5%AD%A6%E7%BF%92%E3%83%A2%E3%83%87%E3%83%AB%E6%A7%8B%E7%AF%89%E6%89%8B%E6%B3%95-%E5%A4%AA%E7%94%B0-%E6%BA%80%E4%B9%85-ebook/dp/B07BHJZ19L