Fast way to decode RGB image in python

February 14, 2017, at 8:08 PM

So, I have a PNG image with the dimensions 1024x1024 that contains RGB encoded elevation data. I read in the image using asarray from numpy:

rgb_data = np.asarray(image)

Which results in an 3D array with dimensions (1024, 1024, 4). (I'm using RGBA)

I need to run some filters (gaussian, median) over that data, but I need to run it on the decoded elevation data.

The elevation data can be decoded with the following function:

def decodeRGB(pixel):
    return (pixel[0] * 256 + pixel[1] + pixel[2] / 256) - 32768

After decoding I will have a 2D array with dimensions (1024, 1024) that contains the decoded elevation data. (The actual height above sea level)

What I have so far is this:

rgb_data = np.asarray(image)
decoded_data = np.zeros(tile.size)
for rownum in range(width):
    for colnum in range(height):
        decoded_data[rownum][colnum] = decodeRGB(rgb_data[rownum][colnum])

Unfortunately this solution is quite slow. It takes approx. 10 seconds for a 1024 by 1024 image.

Is there a more performant way to implement this?

My main issue is that the dimensions of the array changes. I'm going from a (1024, 1024, 4) array to a (1024, 1024) array.

I'm basically looking for a fast/efficient way to apply a function to every RGB pixel in an image which allows reduction of the array size.

Thanks a lot for your help!

Answer 1

Take advantage of vectorisation:

r = rgb_data[..., 0]
g = rgb_data[..., 1]
b = rgb_data[..., 2]
decoded_data = (256*r + g + b/256) - 32768

For example rgb_data[..., 0] selects all 1024x1024 red values at once, 256*r multiplies all of them with 256 in one go, and so forth. Since the interpreted for loops in your code come with a significant overhead avoiding them should drastically speed up the conversion.

Or you could use a one-liner:

decoded_data = (rgb_data[..., :3] * (256, 1, 1/256)).sum(axis=-1) - 32768

There are yet more ways similar in spirit. Look at the comments on this post for a nice collection of one-liners.

Answer 2

You can vectorize the process using the very powerful np.einsum. Just make a transform vector out of your coefficients.

def decodeRGBArray(rgb_data):
    transf = np.array([256., 1., 1./256., 0.])
    return np.einsum('ijk,k->ij', rgb_data, transf) - 32768
Rent Charter Buses Company
Python - Pyqt5. How can I cancel the actual slot execution?

Python - Pyqt5. How can I cancel the actual slot execution?

I am using the object moved into separate thread as following:

Python - parameters sometimes yes and sometimes no

Python - parameters sometimes yes and sometimes no

this code must move a triangle when you press enterMy answer is correct but I don't really understand this

Comparing two sqlite3 tables using python

Comparing two sqlite3 tables using python

Now the question is a little tricky