3

Description

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

  • The pixels where the mask is red (255, 0, 0), sets to (0, 0, 0).
  • The pixels where the mask is green (0, 255, 0), sets to (64, 64, 64)
  • The pixels where the mask is blue(0, 0, 255), sets to (128, 128, 128)
  • The pixels where the mask is yellow(255, 255, 0), sets to (255, 255, 255)
  • Otherwise, keep the pixel unchanged.

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?

2
  • 3
    This isn't your problem, but if you're using import Image rather than from PIL import Image, that implies that you're using PIL rather than its modern fork Pillow. Unless you really need backward compatibility with very old versions of Python, or with code that for some reason won't work with Pillow (there shouldn't be any such thing, but there are always bugs, right?), don't do that. Commented Apr 20, 2015 at 3:50
  • 1
    For future reference: The PIL stuff isn't relevant here at all; you could demonstrate the same problem with, say, a pair of 5x5x3 arrays hardcoded in the source. Which would make a better minimal, complete, verifiable example for this problem. If you can't do that, at least upload the 64x64x3 PNG files somewhere so people can debug your example—but even better to make them unnecessary. (I think this was a good question, it's just that it could be an even better one.) Commented Apr 20, 2015 at 5:07

1 Answer 1

4

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?

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.