5

I'm new to numpy's masked array data-structure, and I want to use it to work with segmented color images.

When I use matplotlib's plt.imshow( masked_gray_image, "gray") to display a masked gray image, the invalid regions will be displayed transparent, which is what I want. However, when I do the same for color images it doesn't seem to work. Interestingly the data-point cursor won't show the rgb values [r,g,b] but empty [], but still the color values are displayed instead of transparent.

Am I doing something wrong or is this not yet provided in matplotlib imshow?

import numpy as np
import matplotlib.pyplot as plt
from scipy.misc import face

img_col = face() #example image from scipy 
img_gray = np.dot(img_col[...,:3], [0.299, 0.587, 0.114]) #convert to gray
threshold = 25 
mask2D = img_gray < threshold # some exemplary mask 
mask3D = np.atleast_3d(mask2D)*np.ones_like(img_col) # expand to 3D with broadcasting...
# using numpy's masked array to specify where data is valid
m_img_gray = np.ma.masked_where( mask2D, img_gray)
m_img_col  = np.ma.masked_where( mask3D, img_col)

fig,axes=plt.subplots(1,4,num=2,clear=True)
axes[0].imshow(mask2D.astype(np.float32)) # plot mask
axes[0].set_title("simple mask")
axes[1].imshow(m_img_gray,"gray") #plot gray verison => works 
axes[1].set_title("(works)\n masked gray")
axes[2].imshow(m_img_col)  #plot color version, => does not work
axes[2].set_title("(doesn't work)\n masked color")

# manually adding mask as alpha channel to show what I want
axes[3].imshow( np.append( m_img_col.data, 255*(1-(0 < np.sum(m_img_col.mask ,axis=2,keepdims=True) ).astype(np.uint8) ),axis=2) )
axes[3].set_title("(desired) \n alpha channel set manually")

Here is an example image: Here is an example image:

[update]: some minor changes to code and images for better clarity...

taras
  • 6,566
  • 10
  • 39
  • 50
Max
  • 485
  • 4
  • 13
  • 1
    Can't solve your problem, but 1) your mask isn't broadcasting properly. If you do `np.unique(m_img_col.mask.sum(-1))` you'll see you have pixels with 1 or 2 channels masked, which shouldn't be correct. try `m_img_col = np.ma.masked_where( np.broadcast_to(mask2D[..., None], img.shape) * img, img)`. 2) It's likely you can't do the same thing with RGB because masking a pixel means masking 3 values instead of 1. At that point using RGBA is more inutitve and safer (what to do with <3 masked channels?) – Daniel F Jan 11 '18 at 12:54
  • 1
    Thanks for the info... The problem in this simple example was the mask generation in the np.ma.masked_where. I shouldn't have multiplied with the image, since it can be 0 as well. So this is a correct version: m_img_col = np.ma.masked_where( np.atleast_3d(mask2D)*np.ones_like(img), img) Anyway, in my real application, I already have the masks provided, so this is not the issue, but Thank you for spotting it :) – Max Jan 11 '18 at 13:12

1 Answers1

-2

I do not know if this is a feature not provided by matplotlib yet, but you can just set all values to 255 where your mask is True:

m_img_col.data[m_img_col.mask]=255

In this way the invalid regions will be displayed as transparent

Nicki Skafte
  • 479
  • 3
  • 9
  • This method is not for transparent, but just fill masked legion with white. White portions in the posted figure is just a color of white background. – dkato Jan 11 '18 at 13:15