Create a shape transparent with a colored background

I have a little situation. I need a plot with a black background with several white circles drawn on top of this black background.

I managed to do this using the following code:

import numpy import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(1, 1, 1, aspect = "equal", axisbg = "black") ax.add_artist(plt.Circle((0., 0., .5), color = "white")) plt.xlim(-5, 5) plt.ylim(-5, 5) fig.savefig("test.png", dpi = 300) plt.show() 

This leads to the following result:

enter image description here

Now what I would like to do is make this image transparent. So this means that only the white circle should become transparent. Perhaps you can already see the problem, because if I set transparent = True . The black background automatically becomes transparent, and I lose the black color from my figure.

Another thing I tried is not to set transparent = True to savefig , but actually set the alpha = 0. option to plt.Circle . This makes the white circle truly transparent, which is the ultimate goal. However, since it is transparent, I remain on a full black background. Any ideas to solve this problem?

To summarize my goal:

I want to keep a transparent version of the figure in which the white circle is transparent and the black ones are not.

I know that I can use different programs like inkscape and gimp to create what I want. However, I really need to do this in python, and also due to other operations that I need to perform.

Thanks!

+6
source share
3 answers

Edit 3:

It was found that the main question:

How to put a "black and transparent" mask before the matplotlib image created by imshow ? The mask should be derived from a previously completed black and white matplotlib drawing.

The following code demonstrates this feature by accessing and blending rgba bitmaps:

 import numpy as np import matplotlib.pyplot as plt import matplotlib.cm as cm import matplotlib.mlab as mlab def get_rgba_bitmap(fig): fig.canvas.draw() tab = fig.canvas.copy_from_bbox(fig.bbox).to_string_argb() ncols, nrows = fig.canvas.get_width_height() return np.fromstring(tab, dtype=np.uint8).reshape(nrows, ncols, 4) def black_white_to_black_transpa(rgba): rgba[:, :, 3] = 255 - rgba[:, :, 0] rgba[:, :, 0:3] = 0 def over(rgba1, rgba2): if rgba1.shape != rgba2.shape: raise ValueError("rgba1 and rgba2 shall have same size") alpha = np.expand_dims(rgba1[:, :, 3] / 255., 3) rgba = np.array(rgba1 * alpha + rgba2 * (1.-alpha), dtype = np.uint8) return rgba[:, :, 0:3] # fig 1) fig1 = plt.figure(facecolor = "white") fig1.set_dpi(300) ax1 = fig1.add_subplot(1, 1, 1, aspect = "equal", axisbg = "black") ax1.add_artist(plt.Circle((0., 0., .5), color = "white")) ax1.set_xlim(-5, 5) ax1.set_ylim(-5, 5) bitmap_rgba1 = get_rgba_bitmap(fig1) black_white_to_black_transpa(bitmap_rgba1) # fig 2 fig2 = plt.figure(facecolor = "white") fig2.set_dpi(300) delta = 0.025 ax2 = fig2.add_subplot(1, 1, 1, aspect = "equal", axisbg = "black") ax2.set_xlim(-5, 5) ax2.set_ylim(-5, 5) x = y = np.arange(-3.0, 3.0, delta) X, Y = np.meshgrid(x, y) Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) Z = Z2-Z1 # difference of Gaussians im = ax2.imshow(Z, interpolation='bilinear', cmap=cm.jet, origin='lower', extent=[-5, 5, -5, 5], vmax=abs(Z).max(), vmin=-abs(Z).max()) bitmap_rgba2 = get_rgba_bitmap(fig2) # now saving the composed figure fig = plt.figure() fig.patch.set_alpha(0.0) ax = fig.add_axes([0., 0., 1., 1.]) ax.patch.set_alpha(0.0) ax.imshow(over(bitmap_rgba1, bitmap_rgba2)) plt.axis('off') fig.savefig("test_transpa.png", dpi=300) plt.show() 

Donation: enter image description here

I tested your original photon test and the image quality looked fine

enter image description here

Now, if you want the background image to be transparent too:

  • Set fig1 background to white, i.e. fig1 = plt.figure(facecolor='white') , since white will become transparent when transferred to black_white_to_black_transpa
  • Set fig2 background to transparent fig2.patch.set_alpha(0.0) as it will be saved unchanged in bitmap_rgba2
  • Finally, take care of the alpha channel when mixing bitmap_rgba1 and bitmap_rgba2 inside over (see below for a possible modification)
 def over(rgba1, rgba2): if rgba1.shape != rgba2.shape: raise ValueError("rgba1 and rgba2 shall have same size") alpha1 = np.expand_dims(rgba1[:, :, 3] / 255., axis=3) alpha2 = np.expand_dims(rgba2[:, :, 3] / 255., axis=3) alpha = 1. - (1.-alpha1) * (1.-alpha2) C1 = rgba1[:, :, 0:3] C2 = rgba2[:, :, 0:3] C = (alpha1 * C1 + (1-alpha1) * alpha2 * C2) / alpha rgba = np.empty_like(rgba1, dtype = np.uint8) rgba[:, :, 0:3] = C rgba[:, :, 3] = 255 * alpha[:, :, 0] return rgba 

last (?) edit: There seems to be an inconsistency between the array returned by to_string_argb and expected with imshow (rgb channel order). A possible solution is to change ax.imshow(over(bitmap_rgba1, bitmap_rgba2)) to:

 over_tab = over(bitmap_rgba1, bitmap_rgba2) over_tab[:, :, 0:3] = over_tab[:, :, ::-1][:, :, 1:4] ax.imshow(over_tab) 
+5
source

Colored panel masked circles

Colormaps may have an alpha channel, so if your data is in a grid with high or low values ​​showing a circle against a circle, one set of these values ​​may be transparent.

This only works for me when saving the shape programmatically, with the transparent keyword; not from the Python image window.

Starting with one example of the matplotlib gallery (in a gimp-like, I can cut and paste segments, and the transparency is correct):

 # plot transparent circles with a black background import numpy as np import matplotlib.pyplot as plt from matplotlib.colors import LinearSegmentedColormap from matplotlib.cm import Greys dark_low = ((0., 1., 1.), (.3, 1., 0.), (1., 0., 0.)) cdict = {'red': dark_low, 'green': dark_low, 'blue': dark_low} cdict3 = {'red': dark_low, 'green': dark_low, 'blue': dark_low, 'alpha': ((0.0, 0.0, 0.0), (0.3, 0.0, 1.0), (1.0, 1.0, 1.0)) } greys = LinearSegmentedColormap('Greys', cdict) plt.register_cmap(cmap=greys) dropout_high = LinearSegmentedColormap('Dropout', cdict3) plt.register_cmap(cmap = dropout_high) # Make some illustrative fake data: x = np.arange(0, np.pi, 0.1) y = np.arange(0, 2*np.pi, 0.1) X, Y = np.meshgrid(x,y) Z = np.cos(X) * np.sin(Y) * 10 # Make the figure: plt.figure() plt.subplot(1,3,1) plt.imshow(Z, cmap=Greys) plt.title('Smooth\ncolorbar') plt.colorbar() plt.subplot(1,3,2) plt.imshow(Z, cmap=greys) plt.title('Linear\ncolorbar') plt.colorbar() plt.subplot(1,3,3) plt.imshow(Z, cmap = dropout_high) plt.title('Alpha crops\n colorbar') plt.colorbar() plt.savefig('dropout_cmap', transparent=True) 

Adapting colorbar to a alpha-channel visual mask

And like a layer above another image. Interestingly, the color panel with alpha channel does not have transparency. This seems like a mistake.

Plots with and without alpha-channel over another image

+1
source

This may not be the answer you are looking for, but it gives the picture you wanted! I think you want to fill out areas outside the circle! (S) with black and leave the background transparent, not the other way around. It is trivial to calculate the boundaries of one circle and use fill_between . Doing this for multiple circles may be more difficult!

 import numpy as np import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(1, 1, 1, aspect = "equal", ) # A Circle xy=(1,1); r=3 # more points is smoother xdata=np.linspace(-5,5,1001) ydata=np.linspace(-5,5,1001) # circle edges (top and bottom) c1=np.sqrt((xy[0]**2-xdata**2)+r**2)+xy[1] c2=-np.sqrt((xy[0]**2-xdata**2)+r**2)+xy[1] c1=np.where(np.isnan(c1),xy[0],c1) c2=np.where(np.isnan(c2),xy[0],c2) ax.fill_between(xdata,5,c1,color='black') ax.fill_between(xdata,-5,c2,color='black') plt.xlim(-5, 5) plt.ylim(-5, 5) fig.savefig("test.png", dpi = 300, transparent=True) 

Transparent circle with center 1.1 and radius 3

0
source

Source: https://habr.com/ru/post/969345/


All Articles