Applying function to all pixels of an image (width, height, 3) with np.vectorize

Viewed 27

I want to apply a function to every pixel of an image of shape (width, height, 3) and receive a new matrix of shape (width, height, 2) (XYZ -> u'v'). How can this be accomplished with np.vectorize()?

Is np.vectorize() the fastest solution here or are there better approaches?

I played around with the following code but received the error RuntimeWarning: invalid value encountered in long_scalars (9 * Y) / (X + 15 * Y + 3 * Z). Also it seems to be running forever...

# input is im_xyz with shape (width, height, 3)

im_xyz_reshape = im_xyz.reshape((im_xyz.shape[0] * im_xyz.shape[1], 3))
def xyz2uv(XYZ):
    [X,Y,Z] = XYZ
    return np.array([
        (4 * X) / (X + (15 * Y) + (3 * Z)),
        (9 * Y) / (X + 15 * Y + 3 * Z)
    ])
xyz2uv_v = np.vectorize(xyz2uv, signature='(xyz)->(uv)')
im_uv = xyz2uv_v(im_xyz_reshape).reshape((im.shape[0], im.shape[1], 2))
1 Answers

As you have realized, np.vectorize is a glorified python-for loop and quite slow. A better way to achieve that is writing your function in a vectorized way.

def xyz2uv(xyz):  # untested as we have no example
    x, y, z = xyz[ ..., 0], xyz[..., 1], xyz[..., 3] 
    denom = x + 15*y + 3*z
    return np.stack([4*x/denom, 9*y/denom], axis=2)

This ensures that we always operate on the whole array, which can be done with few python-function calls, and the rest is done under the hood with C/C++, which is what makes it fast. With your approach we have to do the loops all in python which is what makes it slower.

Related