Sandro Martis
Sandro Martis

Reputation: 113

Fast way to decode RGB image in python

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!

Upvotes: 1

Views: 1583

Answers (2)

Paul Panzer
Paul Panzer

Reputation: 53029

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.

Upvotes: 4

Daniel F
Daniel F

Reputation: 14399

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

Upvotes: 3

Related Questions