[E検定]主な画像データの拡張手法(Data Augmentation)

機械学習でよく使われる主な画像データの拡張手法について紹介します。E検定でも出題範囲になっています。pytonで実装方法に関しても紹介します。拡張手法は以下があります。

概要

機械学習はよいモデルを作成するためによいデータをいかにあつめるかが重要になります。データを集める方法としてもともとある画像から新しい画像を作成する手法があります。ここでは以下の画像拡張方法についての概要とpythonでの実装方法を説明します。

● horizontal flip 画像を水平方向に回転させる
● vertical flip 画像を垂直方向に回転させる
● random crop 一枚の画像から指定された大きさでランダムに切り出す
● scale augmentation 画像のスケールを変化させてから切り出しを行う
● random rotation 画像をランダムに回転させる
● cutout 画像の一部にマスクをかける
● random erasing 画像内の箇所をランダムに選び画像を消去する
● elastic distortion 画像をいくつかの領域に細分化しそれぞれランダムに移動させる

サンプルの画像は以下を使用します。

画像に alt 属性が指定されていません。ファイル名: image-218.png

拡張手法

horizontal flip

画像を水平方向に回転させます。以下で実装方法を紹介します。

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

if __name__ == '__main__':
    img = np.asarray(Image.open("C:/minecraft.jpg"), dtype=np.uint8)
    img = img[:, ::-1, :]
    plt.imshow(img)
    plt.savefig("minecraft_revised.jpg")
    pass

以下が実行結果です。

vertical flip

画像を垂直方向に回転させます。以下で実装方法を紹介します。

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt


if __name__ == '__main__':
    img = np.asarray(Image.open("C:/minecraft.jpg"), dtype=np.uint8)
    img = img[::-1, :, :]
    plt.imshow(img)
    plt.savefig("minecraft_vertical_revised.jpg")
    pass

以下が実行結果です。

random crop

一枚の画像から指定された大きさでランダムに切り出します。以下で実装方法を紹介します。


from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

def random_crop(image, crop_size=(400, 400)):
    h, w, _ = image.shape

    top = np.random.randint(0, h - crop_size[0])
    left = np.random.randint(0, w - crop_size[1])
    bottom = top + crop_size[0]
    right = left + crop_size[1]

    image = image[top:bottom, left:right, :]
    return image

if __name__ == '__main__':
    img = np.asarray(Image.open("C:/minecraft.jpg"), dtype=np.uint8)
    img = random_crop(img)
    plt.imshow(img)
    plt.savefig("minecraft_random_crop_revised.jpg")
    pass

以下が実行結果です。

scale augmentation

画像のスケールを変化させてから切り出しを行います。以下で実装方法を紹介します。

from PIL import Image

from scipy.ndimage.interpolation import rotate
import numpy as np
import matplotlib.pyplot as plt

def random_crop(image, crop_size=(400, 400)):
    h, w, _ = image.shape
    top = np.random.randint(0, h - crop_size[0])
    left = np.random.randint(0, w - crop_size[1])
    bottom = top + crop_size[0]
    right = left + crop_size[1]

    image = image[top:bottom, left:right, :]
    return image



def scale_augmentation(image, scale_range=(256, 400), crop_size=224):
    scale_size = np.random.randint(*scale_range)
    image = np.array(Image.fromarray(image).resize((scale_range), resample=2))
    image = random_crop(image, (crop_size, crop_size))
    return image


if __name__ == '__main__':
    img = np.asarray(Image.open("C:/minecraft.jpg"), dtype=np.uint8)
    img = scale_augmentation(img)
    plt.imshow(img)
    plt.savefig("minecraft_scale_augmentation.jpg")
    pass

以下が実行結果です。

random rotation

画像をランダムに回転させます。以下で実装方法を紹介しています。

from scipy.ndimage.interpolation import rotate

from PIL import Image
from scipy.ndimage.interpolation import rotate
import numpy as np
import matplotlib.pyplot as plt


def random_rotation(image, angle_range=(0, 180)):
    h, w, _ = image.shape
    angle = np.random.randint(*angle_range)
    image = rotate(image, angle)
    image = np.array(Image.fromarray(image).resize((h, w), resample=2))
    return image

if __name__ == '__main__':
    img = np.asarray(Image.open("C:/minecraft.jpg"), dtype=np.uint8)
    img = random_rotation(img)
    plt.imshow(img)
    plt.savefig("minecraft_random_rotation.jpg")
    pass

以下が実行結果です。

cutout

画像の一部にマスクをかけます。以下で実装方法を紹介しています。

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt


def cutout(image_origin, mask_size):
    image = np.copy(image_origin)
    mask_value = image.mean()

    h, w, _ = image.shape

    top = np.random.randint(0 - mask_size // 2, h - mask_size)
    left = np.random.randint(0 - mask_size // 2, w - mask_size)
    bottom = top + mask_size
    right = left + mask_size

    if top < 0:
        top = 0
    if left < 0:
        left = 0

    image[top:bottom, left:right, :].fill(mask_value)
    return image


if __name__ == '__main__':
    img = np.asarray(Image.open("C:/minecraft.jpg"), dtype=np.uint8)
    img = cutout(img,300)
    plt.imshow(img)
    plt.savefig("minecraft_random_cutout.jpg")
    pass

以下が実行結果です。

random erasing

画像内の箇所をランダムに選び画像を消去する

from PIL import Image

from scipy.ndimage.interpolation import rotate
import numpy as np
import matplotlib.pyplot as plt


def random_erasing(img, p=0.5, sl=0.02, sh=0.4, r1=0.3, r2=3.3):
    target_img = img.copy()

    if p < np.random.rand():
        return target_img

    H, W, _ = target_img.shape
    S = H * W

    while True:
        Se = np.random.uniform(sl, sh) * S
        re = np.random.uniform(r1, r2)

        He = int(np.sqrt(Se * re))
        We = int(np.sqrt(Se / re))

        xe = np.random.randint(0, W)
        ye = np.random.randint(0, H)
        if xe + We <= W and ye + He <= H:
            break

    mask = np.random.randint(0, 255, (He, We, 3))
    target_img[ye:ye + He, xe:xe + We, :] = mask

    return target_img



if __name__ == '__main__':
    img = np.asarray(Image.open("C:/minecraft.jpg"), dtype=np.uint8)
    img = random_erasing(img)
    plt.imshow(img)
    plt.savefig("minecraft_random_erasing.jpg")
    pass

以下が実行結果です。

elastic distortion

画像をいくつかの領域に細分化しそれぞれランダムに移動させます。

まとめ

機械学習でよく使われる主な画像データの拡張手法について紹介しました。