概要
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))
人の目で見るとINTER_AREAはボケてるけどエッジが目立たず落ち着いて見える。INTER_NEARESTは周辺から一番情報が落ちているように見える。
もう少し現実的な圧縮サイズとして150×100にしてみるとこうなる。
どれを使うべきか
巷の機械学習の処理ではリサイズする際にどのアルゴリズムを用いているのだろうか。
ちょうど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実装でも同じようにランダムでアルゴリズムを選択している。
指定最大辺になるようリサイズして保存するコード
最大辺を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 記事投稿