Reputation: 23
I have two numpy ndarrays of the same shape (15081, 56724, 3, 3)
. What I want to do is as follows:
Say we have a cross section of the first array, array1[1, 1, :, :]
, looks like this:
[[120, 110, 220],
[ 85, 99, 72],
[197, 80, 75]]
I want to convert it to a boolean in a way that the max of each row is True
and the rest is False
. In the whole array, this corresponds to axis=3
. So the array looks like this after conversion:
[[False, False, True],
[False, True, False],
[ True, False, False]]
Now I want to filter the other array, array2
, using this boolean array to have something that looks like below. I only want to keeping those values of array2
that correspond to True
in array1
and set the rest to zero.
[[ 0, 0, 65],
[ 0, 179, 0],
[125, 0, 0]]
I can do this using a loop but it takes an age (even more).
I expect something like numpy.where(array1.is_max(axis=3), True, False)
, but there is no function like is_max
in python, besides this way the axis 3 is collapsed and I cannot filter array2
using array1
.
Upvotes: 2
Views: 151
Reputation: 114548
In numpy, is_max
is approximately argmax
:
indices = array1.argmax(axis=-1)
Instead of a mask, this will give you a linear index, so you can do
array2b = np.zeros_like(array2)
index = np.indices(array2.shape[:-1], sparse=True) + (indices,)
array2b[index] = array1[index]
You can do something similar with np.take_along_axis
and np.put_along_axis
:
indices = np.expand_dims(array1.argmax(-1), array1.ndim - 1)
array2b = np.zeros_like(array2)
np.put_along_axis(array2b, indices, np.take_along_axis(array2, indices, -1), -1)
If you want a mask-based approach, you can create the mask like this:
mask = array1 != array1.max(-1, keepdims=True)
Now you can set all elements to zero directly:
array2[mask] = 0
Alternatively you can do something like
mask = array1 == array1.max(-1, keepdims=True)
array2 *= mask
Update
From your description in the comments, you are looking for a different operation entirely. You can start by thresholding array1
(which I assume represents the difference between the blurred and original image):
mask = array1 >= 100 # find a threshold that works
array2 *= mask
OR
mask = array1 < 100
array2[mask] = 0
You may also be looking for local minima in an image. You can get those by finding pixels that are bigger than their surroundings. To do that, run a non-linear filter on the image, like scipy.ndimage.maximum_filter
:
mask = array1 == scipy.ndimage.maximum_filter(array1, 5)
array2 *= mask
Upvotes: 3
Reputation: 4083
Can be done with
array2[array1 != np.tile(np.max(array1,axis=1), (array1.shape[0],1)).T] = 0
Here is how.
For your particular example, i would suggest to use np.tile
, where you can create a repeating pattern out of the elements in your 1-D mask.
Suppose we have your matrix a with values
array1 = [[120, 110, 220]
[85, 99, 72]
[197, 80, 75]]
Then the maximum values in every row of your matrix would be
maxes = np.max(array1,axis=1)
which will have the values
array([220, 99, 197])
now, what we need is to use each of these values in each row per each row element. Simply put, we need to compare 220 with each element in the first row. This means that actually we need to repeat the value 220, to be able to perform the operation in a single vector, which can be done with:
np.tile(maxes, (array1.shape[0],1)).T
which gives us
array([[220, 220, 220],
[ 99, 99, 99],
[197, 197, 197]])
filtering is rather straightforward:
a != np.tile(maxes, (3,1)).T
is sufficient to create the mask.
Long story short, the operation can be done in a single liner as following:
array2[array1 != np.tile(maxes, (a.shape[0],1)).T] = 0
which enables a to have following values
array2 = array([[0, 0, 65]
[0, 179, 0]
[125, 0, 0]])
Upvotes: 0