5
votes

Looks like I seem to have some fundamental gaps in my understanding of how images are represented in numpy arrays.

img = np.ones([100,100,3], dtype=np.uint8)*255
plt.imshow(img)

The above code creates a "white" 3 channel image.

Each pixel has the value [255,255,255]

Understood.

Now I'd like create a "white" gray scale image. I don't really need RGB channels to store a white image, do I?

img_bw = np.ones([100,100], dtype=np.uint8)*255
plt.imshow(img_bw, cmap = "gray")

This creates a "black image" even though value at each pixel location is 255?

Okay, let me just take my earlier 3 channel white image and convert it to grayscale and see what the numpy array looks like.

img_bw1 = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
plt.imshow(img_bw1, cmap = "gray") 

This gives me a "black image" as well?

So what exactly does the numpy matrix for a grayscale "white image" look like?

img = io.imread("https://www.colorcombos.com/images/colors/FFFFFF.png" , as_grey=True)
plt.imshow(img*255, cmap = "gray")

This is a white image. Each pixel in this matrix has a value of like 216 and above

plt.imshow(img, cmap = "gray")

This is also a white image. Each pixel in this matrix has a value of 0.86 and above.

I'm totally lost.

Questions -

  1. How do I create a grayscale 2-D white image in numpy?

  2. Why doesn't converting the 3 channel white image in numpy to grayscale using cv2.Color give me a white image?

2

2 Answers

6
votes

When displaying a 2D array using a colormap, matplotlib will first normalize the data such that it lies between 0 and 1.

Your white array is composed solely of 255's, when attempting to normalize an array of equal values, documentation states that they are all converted to 0 (see above link), resulting in a black rendering. To manually specify a range, use:

plt.imshow(img, cmap='gray', vmin=0, vmax=255)

You can also try setting the first pixel in your img_bw array to any value <255 and using your original method to show it, you should see an all white image with a black square in the corner.


As for why this "worked":

img = io.imread("https://www.colorcombos.com/images/colors/FFFFFF.png" , as_grey=True)
plt.imshow(img*255, cmap = "gray")

There's a thin grey border around the image in that link, so after normalization, the grey border (darkest part of the image) is scaled to 0 (black) and the rest of the white interior stays white.

0
votes

Using PIL to encode the array as an image is one way to go -

img_bw = np.ones([100,100], dtype=np.uint8)*255
img = Image.fromarray(img_bw)
img.show()

Output

enter image description here

For black, if you just change to img_bw = np.ones([100,100], dtype=np.uint8)*0 you get - enter image description here