Nicolas GZ
Nicolas GZ

Reputation: 63

Numpy : how to use np.where in a multidimensional array with a given test condition?

Edit : I reduce to a minimal problem, since my first question was probably too messy

when I use np.where on a condition on a scalar cell things work fine:

new_array = np.where(old_array==6, rempl_array, old_array)

but if I want my condition to work on a full dimension of the array:

new_array = np.where((old_array == [1, 2, 3]).all(axis=-1), rempl_array, old_array)

I does not any more, for dimension mismatch

But I can't figure out how to transform the 2D boolean (old_array == [1, 2, 3]).all(axis=-1) in a suitable 3D boolean for where


Here was the initial post :

I have a 3D array, that I have created from a picture (so dimensions hold for height, width and RGB value). I want to change colors according to a given condition.

    submap = np.any([(carr == [pr["red"], pr["green"], pr["blue"]]).all(axis=-1) for pr in list_areas], axis=0)

The condition works fine, retruning a 2D array with True for pixels where the condition is met, and False otherwise.

However, when I try to build a new 3D array where I change colors according to that condition:

    new_carr = np.where(submap, new_color, carr)

I get a shape mismatch error :

ValueError: operands could not be broadcast together with shapes (2048,5632) (3,) (2048,5632,3)

The problem seems not to be only the fact that my new_color has shape (3,), since the problem still holds when I replace it with an array of shape (2048,5632,3), but the fact that my condition is 2D while my initial array is 3D. But how could this condition not be 2D by definition, and how could I make this work?

Thanks for your help

Upvotes: 3

Views: 2453

Answers (2)

Mark Setchell
Mark Setchell

Reputation: 207465

Starting with this posterised image of Paddington:

enter image description here

I think you want to use np.where() as follows to make all red areas into magenta and all other areas into yellow:

#!/usr/bin/env python3

from PIL import Image
import numpy as np

# Load PIL Image and ensure RGB rather than palette based, then make into Numpy array
pi = Image.open('paddington.png').convert('RGB')
na = np.array(pi)

# Now make 2 images same size, one magenta, one yellow
magenta = np.zeros_like(na) + [255,0,255]
yellow  = np.zeros_like(na) + [255,255,0]

# Anywhere paddington is red, make him magenta. Anywhere else, make him yellow.
result = np.where((na==[255,0,0]).all(axis=-1)[...,None], magenta, yellow) 

# Save result
Image.fromarray(result.astype(np.uint8)).save('result.png')

enter image description here

Of course, it was not necessary to make a full size image of magenta and yellow, I just did that to match your original code. You could have used a single pixel and saved memory, making him green and blue like this:

result = np.where((na==[255,0,0]).all(axis=-1)[...,None], [0,255,0], [0,0,255]) 

enter image description here

Upvotes: 3

Nicolas GZ
Nicolas GZ

Reputation: 63

Actually, I have solved my problem in a very ugly way

submap = np.array([[[b, b, b] for b in x] for x in submap.tolist()])

But boy that seems inefficient. There should be a way to do that with arrays only.

Upvotes: 0

Related Questions