画像処理や画像系ディープラーニングでは,画像を読み込む処理が頻繁に発生します.
数百枚ならまだしも,数万枚規模になると読み込むだけで数分かかってしまいますし,1回だけならまだしも,実験のために何度も読み込むならなおさら高速化したいところです.
ここでは,いくつかのライブラリを比較して,読み込み時間を減らす方法を紹介します.
結論
-
pickle
かnp.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などで読み込みやすいように変換しておくのがよいようです.