Doon
Doon

Reputation: 3749

Using array mask to change pixels values

Description

I have an image and its mask. I'm using PIL and Numpy to apply the following rules:

What I have tried

Using the idea of array mask, I tried the following:

import numpy as np
import Image

# (R G B)
red = [255, 0, 0]
green = [0, 255, 0]
blue = [0, 0, 255]
yellow = [255, 255, 0]


def execute():
    im = Image.open('input.png')
    data = np.array(im)
    print "Original = ", data.shape

    mask = Image.open('mask2.png')
    data_mask = np.array(mask)
    print "Mask = ", data_mask.shape

    red_mask = data_mask == red
    green_mask = data_mask == green
    blue_mask = data_mask == blue
    yellow_mask = data_mask == yellow

    data[red_mask] = [0, 0, 0]
    data[green_mask] = [64, 64, 64]
    data[blue_mask] = [128, 128, 128]
    data[yellow_mask] = [255, 255, 255]

    im = Image.fromarray(data)
    im.save('output.png')


if __name__ == "__main__":
    execute()

The problem

The code above outputs:

Original =  (64, 64, 3)
Mask =  (64, 64, 3)
ValueError: NumPy boolean array indexing assignment cannot assign 3 input values to the 5012 output values where the mask is true

Am I missing something? How can I use the idea of array masks to change pixels values?

Upvotes: 3

Views: 3834

Answers (1)

abarnert
abarnert

Reputation: 366083

Look at data[data_mask == red]: it's going to be a flat array, not a 3D array of (X,Y,3). So, the last axis is 5012, not 3. So you can't broadcast the assignment.

The docs explain this:

The result is a 1-D array containing all the elements in the indexed array corresponding to all the true elements in the boolean array.

But…

The result will be multidimensional if y has more dimensions than b. For example:

(Here, y is the equivalent of your data, and b of your red_mask.)

If you think about it, this makes sense. Your red_mask is a 64x64x3 array; it can't possibly pick out 3-vectors (pixels), it can only pick out individual values.


Let's take a smaller, simpler, concrete example (a 1D array of 4 pixels, instead of a 2D array of 64x64 pixels), instead of your example which (a) you didn't give us the data for and (b) is too big to look at all at once:

>>> data = np.array([[1,2,3], [4,5,6], [7,8,9], [10,11,12]])
>>> mask = np.array([[1,2,3], [4,5,6], [1,2,3], [4,5,6]])
>>> red = np.array([1,2,3])
>>> red_mask = mask == red
>>> red_mask
array([[ True,  True,  True],
       [False, False, False],
       [ True,  True,  True],
       [False, False, False]], dtype=bool)
>>> data[red_mask]
array([1, 2, 3, 7, 8, 9])
>>> data[red_mask] = [0,0,0]
ValueError: NumPy boolean array indexing assignment cannot assign 3 input values to the 6 output values where the mask is true
>>> red_mask[:,0]
array([ True, False,  True, False], dtype=bool)
>>> data[red_mask[:,0]]
array([[1, 2, 3],
       [7, 8, 9]])
>>> data[red_mask[:,0]] = [0,0,0]
>>> data
array([[ 0,  0,  0],
       [ 4,  5,  6],
       [ 0,  0,  0],
       [10, 11, 12]])

See how red_mask is the indices of each individual scalar component, while red_mask[:,0] is the indices of each whole 3-vector pixel?

Upvotes: 4

Related Questions