LoginSignup
16
10

More than 3 years have passed since last update.

Python 画像の読み込みを高速化する

Last updated at Posted at 2019-11-24

画像処理や画像系ディープラーニングでは,画像を読み込む処理が頻繁に発生します.

数百枚ならまだしも,数万枚規模になると読み込むだけで数分かかってしまいますし,1回だけならまだしも,実験のために何度も読み込むならなおさら高速化したいところです.

ここでは,いくつかのライブラリを比較して,読み込み時間を減らす方法を紹介します.

結論

  • picklenp.saveで画像を保存しておく(データサイズは肥大化する)
  • pickleのプロトコルは新しいのを使う
  • np.uint8で保存する

実行環境

  • macOS Mojave 10.14.6
  • Python 3.6

読み込み速度の比較

使用したライブラリと,画像1枚の読み込み(numpy.arrayとしてデータを取得)にかかった時間は以下の通りです.

ライブラリ 読み込み時間
OpenCV 4.23 ms
matplotlib 4.37 ms
keras.preprocessing 3.49 ms
skimage 2.56 ms
PIL 2.63 ms
numpy 333 µs
pickle(protocol=1) 597 µs
pickle(protocol=2) 599 µs
pickle(protocol=3) 112 µs
pickle(protocol=4) 118 µs
_pickle(protocol=4) 117 µs

読み込んだ画像は512×512のpngファイルです.

numpyとpickleについては,あらかじめ画像を.npy, .pickleとして保存しておいたものを読み込みましたので,公平な比較ではありません.
前もって画像を変換しておけば,これくらいの速度が出るという表であり,numpyやpickleが一概に速いと結論できるものではありません.

pickleはpickle.dumpする際にprotocolを指定することができ,新しいプロトコルほど,読み込みの速度が上がるそうですので,プロトコルごとに画像データを保存しています.

accimageのような高速なライブラリもありますが,macOSに対応していなかったので使用していません.

hdf5という選択肢もありますが,未検討です.

使用したコードは以下のとおりです.(jupyter notebook使用)

import cv2
import matplotlib.pyplot as plt
import pickle
import numpy as np
from keras.preprocessing import image
from PIL import Image
from skimage import io
import _pickle

def imread1(path):
    return cv2.imread(path)

def imread2(path):
    return plt.imread(path)

def imread3(path):
    img = image.load_img(path)
    return image.img_to_array(img)

def imread4(path):
    return io.imread(path)

def imread5(path):
    img = Image.open(path)
    return np.asarray(img)

def numpy_load(path):
    return np.load(path)

def pickle_load(path):
    with open(path, mode='rb') as f:
        return pickle.load(f)

def _pickle_load(path):
    with open(path, mode='rb') as f:
        return _pickle.load(f)

%timeit img = imread1(img_path)
%timeit img = imread2(img_path)
%timeit img = imread3(img_path)
%timeit img = imread4(img_path)
%timeit img = imread5(img_path)
%timeit img = numpy_load(npy_path)
%timeit img = pickle_load(pickle_path_1)
%timeit img = pickle_load(pickle_path_2)
%timeit img = pickle_load(pickle_path_3)
%timeit img = pickle_load(pickle_path_4)
%timeit img = _pickle_load(pickle_path_4)

データサイズの比較

512×512の.png画像を,numpyとpickleで保存した際のサイズは以下の通りです.

ライブラリ データ型 サイズ
元データ - 236 KB
numpy np.uint8 820 KB
pickle(protocol=1) np.uint8 820 KB
pickle(protocol=2) np.uint8 820 KB
pickle(protocol=3) np.uint8 787 KB
pickle(protocol=4) np.uint8 787 KB
numpy np.float32 3.1 MB
pickle(protocol=1) np.float32 4.9 MB
pickle(protocol=2) np.float32 4.8 MB
pickle(protocol=3) np.float32 3.1 MB
pickle(protocol=4) np.float32 3.1 MB

np.uint8でも,元データの3倍以上の容量を占めてしまうことがわかりました.

ストレージに余裕があって,読み込み速度をできるだけ上げたい場合には,いったんnpyやpickleなどで読み込みやすいように変換しておくのがよいようです.

16
10
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
16
10