How to apply a matrix to an image

1.6k views Asked by At

i've made some search but didn't find the exact same questions - and the solutions I found were not adaptable.

I have an Image, represented by a numpy array of shape (l1,l2,3) where l1,l2 are integers and three because RGB.

And for reasons, I want to change the basis, it means applying a matrix P to all the RGB vectors. Please note that P has a shape of (3,3).

I've written this:

def change_base(Image,P):
    Image_copie=np.zeros(Image.shape)

    for i in range(Image_copie.shape[0]):
        for j in range(Image_copie.shape[1]):
            Image_copie[i,j]=np.dot(P,Image[i,j])

    return Image_copie

It works, obviously, but it's ugly and extremely slow.

Do you guys have any solution, using numpy maybe ? I don't use opencv ..!

Thanks !

1

There are 1 answers

0
Divakar On

You are reducing the last axis on the two inputs Image and P. So, you can use np.tensordot, like so -

np.tensordot(Image,P,axes=(-1,-1))

This can also be expressed as np.dot with some reshaping before and after it, like so -

Image.reshape(-1,3).dot(P.T).reshape(Image.shape[:2]+(-1,))

One can also use np.einsum for such a reduction operation, like so -

np.einsum('ijk,lk->ijl',Image,P)

For performance, being a solely reduction operation, with no axis-alignment requirement, dot-based solutions would be faster for large arrays, but for small to decent size arrays, einsum might be better.

Runtime test

Case #1 :

In [46]: # Inputs
    ...: Image = np.random.randint(0,255,(256,256,3))
    ...: P = np.random.randint(0,255,(3,3))
    ...: 

In [47]: %timeit change_base(Image,P)
    ...: %timeit np.tensordot(Image,P,axes=(-1,-1))
    ...: %timeit Image.reshape(-1,3).dot(P.T).reshape(Image.shape[:2]+(-1,))
    ...: %timeit np.einsum('ijk,lk->ijl',Image,P)
    ...: 
1 loops, best of 3: 206 ms per loop
100 loops, best of 3: 3.28 ms per loop
100 loops, best of 3: 3.22 ms per loop
100 loops, best of 3: 3.06 ms per loop

Case #2 :

In [48]: # Inputs
    ...: Image = np.random.randint(0,255,(512,512,3))
    ...: P = np.random.randint(0,255,(3,3))
    ...: 

In [49]: %timeit change_base(Image,P)
    ...: %timeit np.tensordot(Image,P,axes=(-1,-1))
    ...: %timeit Image.reshape(-1,3).dot(P.T).reshape(Image.shape[:2]+(-1,))
    ...: %timeit np.einsum('ijk,lk->ijl',Image,P)
    ...: 
1 loops, best of 3: 845 ms per loop
100 loops, best of 3: 12.8 ms per loop
100 loops, best of 3: 12.7 ms per loop
100 loops, best of 3: 13.4 ms per loop