1

I'm trying to save binary masks, i.e. arrays with False and True values, as .png files.

However, when doing so with matplotlib.pyplot, it saves the image with 4 channels and I don't know why.

For example to save the image:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

m = np.array([[False, True],
             [False, True]])

plt.imsave("mask.png", m, cmap=cm.gray)

Then reading it again and printing the shape:

plt.imread("mask.png").shape

Gives me:

(2, 2, 4)

Any reason for that and how to just save it as plain grayscale with 0/1 values so that the shape essentially will just be (2,2)?

TheDude
  • 1,205
  • 2
  • 13
  • 21
  • Since all of the three RGB layers are the same, you can extract your original mask with `arr[:, :, 0]`, with `arr` being the mask loaded by `imread`. You can also convert it back to a Boolean array `a[:, :, 0].astype(bool)` – Mr. T May 27 '18 at 10:17
  • Okay, this workaround seem to work for me, thanks. But anyway I'm still curious why is it doing it in the first place? – TheDude May 27 '18 at 10:28
  • I don't know the inner workings of imsave, but it is obvious that it has to create a file that adheres to the file structure defined by the extension, e.g., jpg, png, eps etc. There are other libraries like PIL that can deal with greyscale images directly: https://stackoverflow.com/a/26929408/8881141 If you retrieve a Boolean array in the end, you don't even have to scale it `Image.fromarray(m.astype(np.uint8)).save("maskgrey.png")` – Mr. T May 27 '18 at 11:14

1 Answers1

1

I had the same issue. It is related to matplotlib.pyplot. Use library PIL and the png file has only 1 channel.

from PIL import Image
Image.fromarray(img).save('img.png')

If you're dealing with a float image, you also have to convert it before to uint8. For example like this:

img = 255 * img
img = img.astype(np.uint8)
Dragondin
  • 11
  • 2