LoginSignup
3
2

More than 3 years have passed since last update.

学習画像の水増し

Posted at

はじめに

  • 学習画像が少ない場合のために、水増しをする手法があります。
  • コントラスト、ガンマ、ブラー、ノイズ等色々あります。
  • 今回は、左右反転、ランダムクロップを実施しました。
  • 実は、色々実験した所、学習精度が1番良かった組み合わせでした。あくまで、今回の元画像の場合です。
  • ソース一式は ここ です。

ライブラリ

  • Numpy Pillow を使いました。
$ pip install numpy==1.16.5 pillow

設定

  • CLASSES に従い、逐次処理が繰り返されます。
  • FACE_PATH には、顔画像が保存されています。
  • TEST_NUM に従い、FACE_PATH から TEST_PATH へ画像が複製されます。
  • TRAIN_PATH には、TEST_PATH へ複製されなかった画像が複製されます。
  • AUGMENT_NUM に従い、TRAIN_PATH から AUGMENT_PATH へ水増し画像が作成されます。
config.py
CLASSES = [
    '安倍乙',
    '石原さとみ',
    '大原優乃',
    '小芝風花',
    '川口春奈',
    '森七菜',
    '浜辺美波',
    '清原果耶',
    '福原遥',
    '黒島結菜'
]

BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DATA_PATH = os.path.join(BASE_PATH, 'data')
FACE_PATH = os.path.join(DATA_PATH, 'face')
TRAIN_PATH = os.path.join(DATA_PATH, 'train')
TEST_PATH = os.path.join(DATA_PATH, 'test')
AUGMENT_PATH = os.path.join(DATA_PATH, 'augment')

TRAIN_NUM = 0
TEST_NUM = 100
AUGMENT_NUM = 6000

顔画像を学習画像とテスト画像に複製

  • 顔画像、学習画像、テスト画像のパスを確認します。
  • 顔画像の一覧を作成します。
  • query には、CLASSES が順次与えられます。
save_train_test_from_face.py
def split(query):
    """顔画像の一覧の取得、学習とテストに分割しコピー."""

    face_path = os.path.join(FACE_PATH, query)
    train_path = os.path.join(TRAIN_PATH, query)
    test_path = os.path.join(TEST_PATH, query)

    face_file_list = glob.glob(os.path.join(face_path, '*.jpeg'))
    face_file_list.sort()
  • 顔画像の一覧をシャッフルします。
  • TEST_NUM に従い、顔画像のリストを学習画像とテスト画像に分割します。
save_train_test_from_face.py
    random.shuffle(face_file_list)

    train_file_list = face_file_list[:-TEST_NUM]
    test_file_list = face_file_list[len(train_file_list):]
  • 学習画像とテスト画像の複製を作成します。
  • 元の顔画像は、残しておく方が、やり直しの手間が省けますね。
save_train_test_from_face.py
    for face_file in train_file_list:
        train_file = os.path.join(train_path, os.path.basename(face_file))
        shutil.copy(face_file, train_file)

    for face_file in test_file_list:
        test_file = os.path.join(test_path, os.path.basename(face_file))
        shutil.copy(face_file, test_file)
  • 以下の様に、顔画像が学習画像とテスト画像に分割されました。
  • 学習画像は、最大 392 最小 269 枚ですね。少ないかもな。
$ python save_train_test_from_face.py
query: 安倍乙, face: 415, train: 315, test: 100
query: 石原さとみ, face: 492, train: 392, test: 100
query: 大原優乃, face: 372, train: 272, test: 100
query: 小芝風花, face: 400, train: 300, test: 100
query: 川口春奈, face: 369, train: 269, test: 100
query: 森七菜, face: 389, train: 289, test: 100
query: 浜辺美波, face: 481, train: 381, test: 100
query: 清原果耶, face: 428, train: 328, test: 100
query: 福原遥, face: 420, train: 320, test: 100
query: 黒島結菜, face: 448, train: 348, test: 100

学習画像の水増し

水平方向に反転の関数

  • 最初に、Pillow から Numpy に変換します。
  • また、rate で反転の確率が与えられます。0.5 を設定し半々の確率にしています。
  • Numpy に変換した上で、fliplr で水平方向に反転します。
  • 最後に、Numpy から Pillow に戻します。
def horizontal_flip(image, rate=0.5):
    """水平方向に反転."""

    image = np.array(image, dtype=np.float32)

    if np.random.rand() < rate:
        image = np.fliplr(image)

    return Image.fromarray(np.uint8(image))

ランダムクロップの関数

  • image.shape で、画像の高さと幅を取得します。
  • size を元にクロップサイズを決めます。0.8 は、80% のサイズでクロップする事を意味します。
  • 左上右下 の位置を決めます。
  • top は、0 から height - crop_size の範囲のランダムな値になります。
  • 同様に、left も決めます。
  • bottom は、topcrop_size を足す事で位置を決めます。
  • 同様に、right も決めます。
  • 最後に、image からクロップします。
def random_crop(image, size=0.8):
    """ランダムなサイズでクロップ."""

    image = np.array(image, dtype=np.float32)

    height, width, _ = image.shape
    crop_size = int(min(height, width) * size)

    top = np.random.randint(0, height - crop_size)
    left = np.random.randint(0, width - crop_size)
    bottom = top + crop_size
    right = left + crop_size
    image = image[top:bottom, left:right, :]

    return Image.fromarray(np.uint8(image))

水増し処理

  • 学習画像と水増し画像のパスを設定します。
  • query には、CLASSES が順次与えられます。
def augment(query):
    """学習画像の読み込み、水増し、保存."""

    train_path = os.path.join(TRAIN_PATH, query)
    augment_path = os.path.join(AUGMENT_PATH, query)
  • 顔画像の一覧のリストを作成します。
    train_list = glob.glob(os.path.join(train_path, '*.jpeg'))
    train_list.sort()
  • 水増し画像の枚数から、顔画像を何枚作成するべきかを確認し、ループ処理の回数を決定します。
    loop_num = math.ceil(AUGMENT_NUM / len(train_list))
  • ループ処理回数と顔画像リストのループの中で以下を実施します。
  • 顔画像の読み込み。
  • 50% の割合で、水平方向に反転。
  • 80% の画像サイズで、ランダムクロップ。
  • 顔画像のファイル名に -0001.jpeg の付加し、水増し画像を保存。
    augment_num = 0
    for num in range(1, loop_num + 1):
        for train_file in train_list:
            if augment_num == AUGMENT_NUM:
                break

            image = Image.open(train_file)

            image = horizontal_flip(image)
            image = random_crop(image)

            augment_file = os.path.join(AUGMENT_PATH, query, os.path.basename(train_file).split('.')[0] + '-{:04d}.jpeg'.format(num))
            image.save(augment_file, optimize=True, quality=95)
            print('query: {}, train_file: {}, augment_file: {}'.format(
                query, os.path.basename(train_file), os.path.basename(augment_file)))

            augment_num += 1

おわりに

  • 学習画像の水増しを、PilloNumpy で行いました。
  • 作業の過程で、ランダムクロップ以外の、スケールクロップ、カットアプト、ランダムイレース、ランダムローテートも確認しました。今回の顔画像に場合は、精度向上に向いていなかったので、利用していません。
  • 次回は、学習画像、テスト画像を扱いやすくするための、データセットを作成する予定です。
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2