Note
Go to the endto download the full example code.
AGG filter#
Most pixel-based backends in Matplotlib useAnti-Grain Geometry (AGG) forrendering. You can modify the rendering of Artists by applying a filter viaArtist.set_agg_filter.

importmatplotlib.pyplotaspltimportnumpyasnpfrommatplotlib.artistimportArtistfrommatplotlib.colorsimportLightSourceimportmatplotlib.transformsasmtransformsdefsmooth1d(x,window_len):# copied from https://scipy-cookbook.readthedocs.io/items/SignalSmooth.htmls=np.r_[2*x[0]-x[window_len:1:-1],x,2*x[-1]-x[-1:-window_len:-1]]w=np.hanning(window_len)y=np.convolve(w/w.sum(),s,mode='same')returny[window_len-1:-window_len+1]defsmooth2d(A,sigma=3):window_len=max(int(sigma),3)*2+1A=np.apply_along_axis(smooth1d,0,A,window_len)A=np.apply_along_axis(smooth1d,1,A,window_len)returnAclassBaseFilter:defget_pad(self,dpi):return0defprocess_image(self,padded_src,dpi):raiseNotImplementedError("Should be overridden by subclasses")def__call__(self,im,dpi):pad=self.get_pad(dpi)padded_src=np.pad(im,[(pad,pad),(pad,pad),(0,0)],"constant")tgt_image=self.process_image(padded_src,dpi)returntgt_image,-pad,-padclassOffsetFilter(BaseFilter):def__init__(self,offsets=(0,0)):self.offsets=offsetsdefget_pad(self,dpi):returnint(max(self.offsets)/72*dpi)defprocess_image(self,padded_src,dpi):ox,oy=self.offsetsa1=np.roll(padded_src,int(ox/72*dpi),axis=1)a2=np.roll(a1,-int(oy/72*dpi),axis=0)returna2classGaussianFilter(BaseFilter):"""Simple Gaussian filter."""def__init__(self,sigma,alpha=0.5,color=(0,0,0)):self.sigma=sigmaself.alpha=alphaself.color=colordefget_pad(self,dpi):returnint(self.sigma*3/72*dpi)defprocess_image(self,padded_src,dpi):tgt_image=np.empty_like(padded_src)tgt_image[:,:,:3]=self.colortgt_image[:,:,3]=smooth2d(padded_src[:,:,3]*self.alpha,self.sigma/72*dpi)returntgt_imageclassDropShadowFilter(BaseFilter):def__init__(self,sigma,alpha=0.3,color=(0,0,0),offsets=(0,0)):self.gauss_filter=GaussianFilter(sigma,alpha,color)self.offset_filter=OffsetFilter(offsets)defget_pad(self,dpi):returnmax(self.gauss_filter.get_pad(dpi),self.offset_filter.get_pad(dpi))defprocess_image(self,padded_src,dpi):t1=self.gauss_filter.process_image(padded_src,dpi)t2=self.offset_filter.process_image(t1,dpi)returnt2classLightFilter(BaseFilter):"""Apply LightSource filter"""def__init__(self,sigma,fraction=1):""" Parameters ---------- sigma : float sigma for gaussian filter fraction: number, default: 1 Increases or decreases the contrast of the hillshade. See `matplotlib.colors.LightSource` """self.gauss_filter=GaussianFilter(sigma,alpha=1)self.light_source=LightSource()self.fraction=fractiondefget_pad(self,dpi):returnself.gauss_filter.get_pad(dpi)defprocess_image(self,padded_src,dpi):t1=self.gauss_filter.process_image(padded_src,dpi)elevation=t1[:,:,3]rgb=padded_src[:,:,:3]alpha=padded_src[:,:,3:]rgb2=self.light_source.shade_rgb(rgb,elevation,fraction=self.fraction,blend_mode="overlay")returnnp.concatenate([rgb2,alpha],-1)classGrowFilter(BaseFilter):"""Enlarge the area."""def__init__(self,pixels,color=(1,1,1)):self.pixels=pixelsself.color=colordef__call__(self,im,dpi):alpha=np.pad(im[...,3],self.pixels,"constant")alpha2=np.clip(smooth2d(alpha,self.pixels/72*dpi)*5,0,1)new_im=np.empty((*alpha2.shape,4))new_im[:,:,:3]=self.colornew_im[:,:,3]=alpha2offsetx,offsety=-self.pixels,-self.pixelsreturnnew_im,offsetx,offsetyclassFilteredArtistList(Artist):"""A simple container to filter multiple artists at once."""def__init__(self,artist_list,filter):super().__init__()self._artist_list=artist_listself._filter=filterdefdraw(self,renderer):renderer.start_rasterizing()renderer.start_filter()forainself._artist_list:a.draw(renderer)renderer.stop_filter(self._filter)renderer.stop_rasterizing()deffiltered_text(ax):# mostly copied from contour_demo.py# prepare imagedelta=0.025x=np.arange(-3.0,3.0,delta)y=np.arange(-2.0,2.0,delta)X,Y=np.meshgrid(x,y)Z1=np.exp(-X**2-Y**2)Z2=np.exp(-(X-1)**2-(Y-1)**2)Z=(Z1-Z2)*2# drawax.imshow(Z,interpolation='bilinear',origin='lower',cmap="gray",extent=(-3,3,-2,2),aspect='auto')levels=np.arange(-1.2,1.6,0.2)CS=ax.contour(Z,levels,origin='lower',linewidths=2,extent=(-3,3,-2,2))# contour labelcl=ax.clabel(CS,levels[1::2],# label every second levelfmt='%1.1f',fontsize=11)# change clabel color to blackfrommatplotlib.patheffectsimportNormalfortincl:t.set_color("k")# to force TextPath (i.e., same font in all backends)t.set_path_effects([Normal()])# Add white glows to improve visibility of labels.white_glows=FilteredArtistList(cl,GrowFilter(3))ax.add_artist(white_glows)white_glows.set_zorder(cl[0].get_zorder()-0.1)ax.xaxis.set_visible(False)ax.yaxis.set_visible(False)defdrop_shadow_line(ax):# copied from examples/misc/svg_filter_line.py# draw linesl1,=ax.plot([0.1,0.5,0.9],[0.1,0.9,0.5],"bo-")l2,=ax.plot([0.1,0.5,0.9],[0.5,0.2,0.7],"ro-")gauss=DropShadowFilter(4)forlin[l1,l2]:# draw shadows with same lines with slight offset.xx=l.get_xdata()yy=l.get_ydata()shadow,=ax.plot(xx,yy)shadow.update_from(l)# offset transformtransform=mtransforms.offset_copy(l.get_transform(),ax.figure,x=4.0,y=-6.0,units='points')shadow.set_transform(transform)# adjust zorder of the shadow lines so that it is drawn below the# original linesshadow.set_zorder(l.get_zorder()-0.5)shadow.set_agg_filter(gauss)shadow.set_rasterized(True)# to support mixed-mode renderersax.set_xlim(0.,1.)ax.set_ylim(0.,1.)ax.xaxis.set_visible(False)ax.yaxis.set_visible(False)defdrop_shadow_patches(ax):# Copied from barchart_demo.pyN=5group1_means=[20,35,30,35,27]ind=np.arange(N)# the x locations for the groupswidth=0.35# the width of the barsrects1=ax.bar(ind,group1_means,width,color='r',ec="w",lw=2)group2_means=[25,32,34,20,25]rects2=ax.bar(ind+width+0.1,group2_means,width,color='y',ec="w",lw=2)drop=DropShadowFilter(5,offsets=(1,1))shadow=FilteredArtistList(rects1+rects2,drop)ax.add_artist(shadow)shadow.set_zorder(rects1[0].get_zorder()-0.1)ax.set_ylim(0,40)ax.xaxis.set_visible(False)ax.yaxis.set_visible(False)deflight_filter_pie(ax):fracs=[15,30,45,10]explode=(0.1,0.2,0.1,0.1)pie=ax.pie(fracs,explode=explode)light_filter=LightFilter(9)forpinpie.wedges:p.set_agg_filter(light_filter)p.set_rasterized(True)# to support mixed-mode renderersp.set(ec="none",lw=2)gauss=DropShadowFilter(9,offsets=(3,-4),alpha=0.7)shadow=FilteredArtistList(pie.wedges,gauss)ax.add_artist(shadow)shadow.set_zorder(pie.wedges[0].get_zorder()-0.1)if__name__=="__main__":fix,axs=plt.subplots(2,2)filtered_text(axs[0,0])drop_shadow_line(axs[0,1])drop_shadow_patches(axs[1,0])light_filter_pie(axs[1,1])axs[1,1].set_frame_on(True)plt.show()
Total running time of the script: (0 minutes 1.435 seconds)