Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenCV Python: fast solution for 3-channel float32 image reading?

I need 3-channel RBG-ordered color images of type float32 with their values in the interval [0.0, 1.0] for each color channel.

This is my current solution:

def read_images(imagelist):
    buffer = list()
    for f in imagelist:
        # load single image, convert to float32
        img = cv2.imread(f).astype(np.float32)
        # change interval from [0, 255] to [0.0, 1.0]
        img /= 255.0
        # leave out alpha channel, if any
        if img.shape[2] == 4:
           img = img[:, :, 0:3]
        buffer.append(img)
    return np.array(buffer)

Afterwards, in an image handler, I change BGR to RGB ordering (because cv2's imread reads images in BGR order by default).

This procedure is quite time consuming for large image sets: I am loading thousands of images for pre-processing and then feed the images to some neural networks implemented in TensorFlow.

Is there a way of improving the performance of this approach?

like image 826
daniel451 Avatar asked Aug 25 '17 22:08

daniel451


1 Answers

With this approach, there's probably not too much you can do to speed up your image reading. I thought maybe Matplotlib would be faster, since it reads directly as a float and in RGB order, but it is thrice as slow as OpenCV, even after converting the type and channel order. PIL is a bit faster than Matplotlib, but still twice as slow as OpenCV so that doesn't help, and scikit-image is about the same speed as PIL:

import matplotlib.image as mpimg
import cv2
import numpy as np
from skimage import io
from PIL import Image

import timeit
times = range(1000)

# matplotlib
start_time = timeit.default_timer()
for t in times:
    img = mpimg.imread('img1.png')
print("mpimg.imread(): ", timeit.default_timer() - start_time, "s")

# OpenCV
start_time = timeit.default_timer()
for t in times:
    img = cv2.cvtColor(
        cv2.imread('img1.png'), cv2.COLOR_BGR2RGB).astype(np.float32)/255.0
print("cv2.imread(): ", timeit.default_timer() - start_time, "s")

# scikit-image
start_time = timeit.default_timer()
for t in times:
    img = io.imread('img1.png').astype(np.float32)/255.0
print("io.imread(): ", timeit.default_timer() - start_time, "s")

# PIL
start_time = timeit.default_timer()
for t in times:
    img = np.asarray(Image.open('img1.png')).astype(np.float32)/255.0
print("Image.open(): ", timeit.default_timer() - start_time, "s")

mpimg.imread(): 37.68960806101677 s
cv2.imread(): 13.830177563999314 s
io.imread(): 29.395271296001738 s
Image.open(): 26.633562815986807 s

Instead it might be better to preprocess by reading through all the images and saving them to a better format for reading (that is, directly reading from bytes) instead of using image readers. You can serialize (pickle) your images into .p or .pickle files, and then load the data directly into a list. That way you just have to do the slow loading once and once only. As Dan Mašek notes below in the comments, pickling your files means uncompressing them into raw data, so the files sizes will be far larger. You can create your same list as you have now (your buffer) with the correct type and channel ordering, and then pickle the list; when it comes time to train you can load the pickle file; it's way faster, and super simple:

with open(training_file, mode='rb') as f:
    training_data = pickle.load(f)
like image 156
alkasm Avatar answered Nov 14 '22 22:11

alkasm