iMind Developers Blog

iMind開発者ブログ

Python+OpenCVで画像をリサイズして保存する

概要

OpenCVで画像を読み込んでサイズを一定のルールで変更して保存する処理を実行する。

用途としては機械学習用に集めた画像データを保存しておく用として、最大で縦横のどっちか長い方が512pixelになるようにしておくことを想定している。

バージョン情報

  • Python 3.6.8
  • opencv-python==3.4.2.17

導入

OpenCVが入っていること。

$ conda install opencv

リサイズの手法

OpenCVのリサイズのモードは下記などがある。

interpolation description
INTER_LINEAR bilinear(双線型) interpolation
INTER_NEAREST nearest neighbor(近傍) interpolation
INTER_CUBIC bicubic(バイキュービック) interpolation
INTER_AREA resampling using pixel area relation
INTER_LANCZOS4 Lanczos interpolation over 8x8 neighborhood

デフォルトはINTER_LINEAR。

リサイズ手法に関する参考サイト

URL 概要
http://www.chrismadden.co.uk/inkline-press/create... 実際のリサイズ後画像の比較
http://imagingsolution.net/imaging/interpolation/ アルゴリズムの説明
https://ja.wikipedia.org/wiki/%E7%B7%9A%E5%BD%A2%... 線形補間の説明

実践

手元に800×535サイズのとてもかわいいワンコの写真があるので、こちらをぎゅっと小さく50×33まで縮めて差を比較する。

import cv2
from matplotlib import pylab as plt

def plot(img, img_size, figsize):
    # 2 x 3で表示
    f, ax_list = plt.subplots(3, 2, figsize=figsize)

    # INTER_LINEAR
    img_linear = cv2.resize(img, img_size, interpolation=cv2.INTER_LINEAR)
    ax_list[0, 0].imshow(cv2.cvtColor(img_linear, cv2.COLOR_BGR2RGB))
    ax_list[0, 0].set_title('INTER_LINEAR')

    # INTER_NEAREST
    img_nearest = cv2.resize(img, img_size, interpolation=cv2.INTER_NEAREST)
    ax_list[0, 1].imshow(cv2.cvtColor(img_nearest, cv2.COLOR_BGR2RGB))
    ax_list[0, 1].set_title('INTER_NEAREST')

    # INTER_CUBIC
    img_cubic = cv2.resize(img, img_size, interpolation=cv2.INTER_CUBIC)
    ax_list[1, 0].imshow(cv2.cvtColor(img_cubic, cv2.COLOR_BGR2RGB))
    ax_list[1, 0].set_title('INTER_CUBIC')

    # INTER_AREA
    img_area = cv2.resize(img, img_size, interpolation=cv2.INTER_AREA)
    ax_list[1, 1].imshow(cv2.cvtColor(img_area, cv2.COLOR_BGR2RGB))
    ax_list[1, 1].set_title('INTER_AREA')

    # INTER_LANCZOS4
    img_lanc = cv2.resize(img, img_size, interpolation=cv2.INTER_LANCZOS4)
    ax_list[2, 0].imshow(cv2.cvtColor(img_lanc, cv2.COLOR_BGR2RGB))
    ax_list[2, 0].set_title('INTER_LANCZOS4')

    # axis削除
    for row in ax_list:
        for col in row:
            col.axis('off')

    # 余白削除
    plt.subplots_adjust(wspace=0.01, hspace=0.1)
            
# 画像の読み込み
img = cv2.imread('data/opencv_image_resize/dog1.jpeg')

# 各種plot
plot(img, img_size=(50, 33), figsize=(15, 15))

f:id:mwsoft:20190429163617p:plain

人の目で見るとINTER_AREAはボケてるけどエッジが目立たず落ち着いて見える。INTER_NEARESTは周辺から一番情報が落ちているように見える。

もう少し現実的な圧縮サイズとして150×100にしてみるとこうなる。

f:id:mwsoft:20190429163710p:plain

どれを使うべきか

巷の機械学習の処理ではリサイズする際にどのアルゴリズムを用いているのだろうか。

ちょうどSSDの実装を探していたところだったのでその手のソースコードの中を調べてみる。

下記のTensorflowのSSD実装では cv2.INTER_LINEARが使われている。

https://github.com/balancap/SSD-Tensorflow/blob/master/preprocessing/ssd_vgg_preprocessing.py

下記のTensorflowのSSD実装では cv2.INTER_LINEAR, cv2.INTER_AREA, cv2.INTER_NEAREST, cv2.INTER_CUBIC, cv2.INTER_LANCZOS4からランダムに選択してサイズを変更している。

https://github.com/ljanyst/ssd-tensorflow/blob/master/transforms.py

学習するたびに違うアルゴ使った方がパターンが増えて良いということか。

こちらのKerasによるSSD実装でも同じようにランダムでアルゴリズムを選択している。

https://github.com/pierluigiferrari/ssd_keras/blob/master/data_generator/object_detection_2d_geometric_ops.py

指定最大辺になるようリサイズして保存するコード

最大辺を512にリサイズして保存するコードの例。

import cv2, numpy as np

INTERPOLATIONS = [cv2.INTER_LINEAR, cv2.INTER_AREA, cv2.INTER_NEAREST, cv2.INTER_CUBIC, cv2.INTER_LANCZOS4]

def resize(src_path, dest_path, size=512, interpolations=INTERPOLATIONS):
    img = cv2.imread(src_path)
    y, x = img.shape[0:2]
    max_len = max(x, y)
    interpolation = np.random.choice(interpolations)
    if max_len > size:
        scale = max_len / size
        x, y = round(x / scale), round(y / scale)
        img = cv2.resize(img, (x, y), interpolation=interpolation)
    cv2.imwrite(dest_path, img)

resize('data/opencv_image_resize/dog1.jpeg', 'data/opencv_image_resize/dog_resize.jpeg')

ディレクトリ配下の画像を連番のJPEG画像に変換するコードも書いておく。

import os

def resize_dir_files(dir_path, name_prefix, size=512, interpolation=INTERPOLATIONS):
    # 変換後のディレクトリを作っておく
    dest_dir = os.path.join(dir_path, 'resized')
    if not os.path.exists(dest_dir):
        os.mkdir(dest_dir)

    # ディレクトリ配下のファイルのうち画像ファイルを変換して保存する
    img_id = 0
    for fname in os.listdir(dir_path):
        src_path = os.path.join(dir_path, fname)
        if src_path.endswith(('.png', '.jpg', '.jpeg', '.gif')):
            img_id += 1
            dest_path = os.path.join(dest_dir, '{}{:05d}.jpeg'.format(name_prefix, img_id))
            resize(src_path, dest_path, size=size, interpolations=INTERPOLATIONS)

resize_dir_files('data/opencv_image_resize', name_prefix='dog_')

これで指定ディレクトリ配下に resized/dog_00001.jpeg のような形式で画像ファイルが出力される。

改定履歴

Author: Masato Watanabe, Date: 2019-05-06 記事投稿