admin管理员组

文章数量:1432436

I have an array with shape (100, 80, 3) which is an rgb image.
I have a boolean mask with shape (100, 80).

I want each pixel where the mask is True to have value of pix_val = np.array([0.1, 0.2, 0.3]).

cols = 100
rows = 80
img = np.random.rand(rows, cols, 3)
mask = np.random.randint(2, size=(rows, cols), dtype=np.bool_)
px = np.array([0.1, 0.2, 0.3])

for ch in range(3):
  img[:, :, ch][mask] = px[ch]

I thought broadcasting:

img[mask[:, :, None]] = px

would work. But it did not.

I am looking for a vectorized (efficient) way to implement it.

I have an array with shape (100, 80, 3) which is an rgb image.
I have a boolean mask with shape (100, 80).

I want each pixel where the mask is True to have value of pix_val = np.array([0.1, 0.2, 0.3]).

cols = 100
rows = 80
img = np.random.rand(rows, cols, 3)
mask = np.random.randint(2, size=(rows, cols), dtype=np.bool_)
px = np.array([0.1, 0.2, 0.3])

for ch in range(3):
  img[:, :, ch][mask] = px[ch]

I thought broadcasting:

img[mask[:, :, None]] = px

would work. But it did not.

I am looking for a vectorized (efficient) way to implement it.

Share Improve this question edited Nov 20, 2024 at 21:02 Christoph Rackwitz 16k5 gold badges39 silver badges51 bronze badges asked Nov 19, 2024 at 11:45 Avi TAvi T 1656 bronze badges 2
  • 1 Look at what img[mask] selects. I believe it will be a (n,3) array. px is (3,) which broadcasts to (1,3) and (n,3). – hpaulj Commented Nov 19, 2024 at 16:53
  • 2 The main indexing documentation explains that boolean indexing is equivalent to applying np.nonzero(mask). With a 2d mask that produces a tuple of 2 indexing arrays. So that's like doing img[I, J, :]= px. Broadcasting is used here, but doesn't need extra help from you. – hpaulj Commented Nov 19, 2024 at 18:49
Add a comment  | 

2 Answers 2

Reset to default 3

You can use:

img[mask] = px

Small example:

cols = 3
rows = 4
np.random.seed(0)
img = np.random.rand(rows, cols, 3)
mask = np.random.randint(2, size=(rows, cols), dtype=np.bool_)
px = np.array([0.1, 0.2, 0.3])

Inputs:

# img
array([[[0.5488135 , 0.71518937, 0.60276338],
        [0.54488318, 0.4236548 , 0.64589411],
        [0.43758721, 0.891773  , 0.96366276]],

       [[0.38344152, 0.79172504, 0.52889492],
        [0.56804456, 0.92559664, 0.07103606],
        [0.0871293 , 0.0202184 , 0.83261985]],

       [[0.77815675, 0.87001215, 0.97861834],
        [0.79915856, 0.46147936, 0.78052918],
        [0.11827443, 0.63992102, 0.14335329]],

       [[0.94466892, 0.52184832, 0.41466194],
        [0.26455561, 0.77423369, 0.45615033],
        [0.56843395, 0.0187898 , 0.6176355 ]]])

# mask
array([[False,  True,  True],
       [False, False,  True],
       [False, False, False],
       [ True,  True,  True]])

Output:

array([[[0.5488135 , 0.71518937, 0.60276338],
        [0.1       , 0.2       , 0.3       ],
        [0.1       , 0.2       , 0.3       ]],

       [[0.38344152, 0.79172504, 0.52889492],
        [0.56804456, 0.92559664, 0.07103606],
        [0.1       , 0.2       , 0.3       ]],

       [[0.77815675, 0.87001215, 0.97861834],
        [0.79915856, 0.46147936, 0.78052918],
        [0.11827443, 0.63992102, 0.14335329]],

       [[0.1       , 0.2       , 0.3       ],
        [0.1       , 0.2       , 0.3       ],
        [0.1       , 0.2       , 0.3       ]]])

I'll attempt to explain why your indexing attempt didn't work.

Make a smaller 3d array, and 2d mask:

In [1]: import numpy as np

In [2]: img = np.arange(24).reshape(2,3,4)

In [3]: mask = np.array([[1,0,1],[0,1,1]],bool);mask
Out[3]: 
array([[ True, False,  True],
       [False,  True,  True]])

Using @mozway's indexing, produces a (4,4) array. The first first 4 is the number of True values in the mask, the second is the trailing dimension:

In [4]: img[mask]
Out[4]: 
array([[ 0,  1,  2,  3],
       [ 8,  9, 10, 11],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])

With your indexing attempt, we get an error. (You really should have shown the error message):

In [5]: img[mask[:,:,None]]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[5], line 1
----> 1 img[mask[:,:,None]]

IndexError: boolean index did not match indexed array 
along dimension 2; dimension is 4 but corresponding 
boolean dimension is 1

With this None, mask dimension is (2,3,1). That last 1 doesn't match the 4 of img. broadcasting doesn't apply in this context.

Now if we attempt to use mask in a multiplication, the (2,3,4) and (2,3) don't broadcast together:

In [6]: img*mask
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[6], line 1
----> 1 img*mask

ValueError: operands could not be broadcast together 
with shapes (2,3,4) (2,3) 

But (2,3,1) does broadcast with (2,3,4), producing a select number of 0 rows:

In [7]: img*mask[:,:,None]
Out[7]: 
array([[[ 0,  1,  2,  3],
        [ 0,  0,  0,  0],
        [ 8,  9, 10, 11]],

       [[ 0,  0,  0,  0],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])

As I commented, using a boolean mask is equivalent to indexing with nonzero arrays:

In [13]: I,J = np.nonzero(mask); I,J
Out[13]: (array([0, 0, 1, 1], dtype=int64), array([0, 2, 1, 2], dtype=int64))

In [14]: img[I,J,:]
Out[14]: 
array([[ 0,  1,  2,  3],
       [ 8,  9, 10, 11],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])

In the assignment expresion, a size (4,) value can broadcast to the (n,4) indexed img[mask]. Now if we were attempting to mask other dimensions we might need to make a px[:,None,:] or something like that.

本文标签: pythonSetting RGB value for a numpy array using boolean indexingStack Overflow