import matplotlib.pyplot as plt
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.transforms as mtransforms
from matplotlib.colors import LightSource
from matplotlib.artist import Artist
def smooth1d(x, window_len):
# copied from http://www.scipy.org/Cookbook/SignalSmooth
s = 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')
return y[window_len-1:-window_len+1]
def smooth2d(A, sigma=3):
window_len = max(int(sigma), 3)*2 + 1
A1 = np.array([smooth1d(x, window_len) for x in np.asarray(A)])
A2 = np.transpose(A1)
A3 = np.array([smooth1d(x, window_len) for x in A2])
A4 = np.transpose(A3)
return A4
class BaseFilter(object):
def prepare_image(self, src_image, dpi, pad):
ny, nx, depth = src_image.shape
# tgt_image = np.zeros([pad*2+ny, pad*2+nx, depth], dtype="d")
padded_src = np.zeros([pad*2 + ny, pad*2 + nx, depth], dtype="d")
padded_src[pad:-pad, pad:-pad, :] = src_image[:, :, :]
return padded_src # , tgt_image
def get_pad(self, dpi):
return 0
def __call__(self, im, dpi):
pad = self.get_pad(dpi)
padded_src = self.prepare_image(im, dpi, pad)
tgt_image = self.process_image(padded_src, dpi)
return tgt_image, -pad, -pad
class OffsetFilter(BaseFilter):
def __init__(self, offsets=None):
if offsets is None:
self.offsets = (0, 0)
else:
self.offsets = offsets
def get_pad(self, dpi):
return int(max(*self.offsets)/72.*dpi)
def process_image(self, padded_src, dpi):
ox, oy = self.offsets
a1 = np.roll(padded_src, int(ox/72.*dpi), axis=1)
a2 = np.roll(a1, -int(oy/72.*dpi), axis=0)
return a2
class GaussianFilter(BaseFilter):
"simple gauss filter"
def __init__(self, sigma, alpha=0.5, color=None):
self.sigma = sigma
self.alpha = alpha
if color is None:
self.color = (0, 0, 0)
else:
self.color = color
def get_pad(self, dpi):
return int(self.sigma*3/72.*dpi)
def process_image(self, padded_src, dpi):
# offsetx, offsety = int(self.offsets[0]), int(self.offsets[1])
tgt_image = np.zeros_like(padded_src)
aa = smooth2d(padded_src[:, :, -1]*self.alpha,
self.sigma/72.*dpi)
tgt_image[:, :, -1] = aa
tgt_image[:, :, :-1] = self.color
return tgt_image
class DropShadowFilter(BaseFilter):
def __init__(self, sigma, alpha=0.3, color=None, offsets=None):
self.gauss_filter = GaussianFilter(sigma, alpha, color)
self.offset_filter = OffsetFilter(offsets)
def get_pad(self, dpi):
return max(self.gauss_filter.get_pad(dpi),
self.offset_filter.get_pad(dpi))
def process_image(self, padded_src, dpi):
t1 = self.gauss_filter.process_image(padded_src, dpi)
t2 = self.offset_filter.process_image(t1, dpi)
return t2
class LightFilter(BaseFilter):
"simple gauss filter"
def __init__(self, sigma, fraction=0.5):
self.gauss_filter = GaussianFilter(sigma, alpha=1)
self.light_source = LightSource()
self.fraction = fraction
def get_pad(self, dpi):
return self.gauss_filter.get_pad(dpi)
def process_image(self, padded_src, dpi):
t1 = self.gauss_filter.process_image(padded_src, dpi)
elevation = t1[:, :, 3]
rgb = padded_src[:, :, :3]
rgb2 = self.light_source.shade_rgb(rgb, elevation,
fraction=self.fraction)
tgt = np.empty_like(padded_src)
tgt[:, :, :3] = rgb2
tgt[:, :, 3] = padded_src[:, :, 3]
return tgt
class GrowFilter(BaseFilter):
"enlarge the area"
def __init__(self, pixels, color=None):
self.pixels = pixels
if color is None:
self.color = (1, 1, 1)
else:
self.color = color
def __call__(self, im, dpi):
pad = self.pixels
ny, nx, depth = im.shape
new_im = np.empty([pad*2 + ny, pad*2 + nx, depth], dtype="d")
alpha = new_im[:, :, 3]
alpha.fill(0)
alpha[pad:-pad, pad:-pad] = im[:, :, -1]
alpha2 = np.clip(smooth2d(alpha, self.pixels/72.*dpi) * 5, 0, 1)
new_im[:, :, -1] = alpha2
new_im[:, :, :-1] = self.color
offsetx, offsety = -pad, -pad
return new_im, offsetx, offsety
class FilteredArtistList(Artist):
"""
A simple container to draw filtered artist.
"""
def __init__(self, artist_list, filter):
self._artist_list = artist_list
self._filter = filter
Artist.__init__(self)
def draw(self, renderer):
renderer.start_rasterizing()
renderer.start_filter()
for a in self._artist_list:
a.draw(renderer)
renderer.stop_filter(self._filter)
renderer.stop_rasterizing()
def filtered_text(ax):
# mostly copied from contour_demo.py
# prepare image
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.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)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)
# draw
im = ax.imshow(Z, interpolation='bilinear', origin='lower',
cmap=cm.gray, extent=(-3, 3, -2, 2))
levels = np.arange(-1.2, 1.6, 0.2)
CS = ax.contour(Z, levels,
origin='lower',
linewidths=2,
extent=(-3, 3, -2, 2))
ax.set_aspect("auto")
# contour label
cl = ax.clabel(CS, levels[1::2], # label every second level
inline=1,
fmt='%1.1f',
fontsize=11)
# change clable color to black
from matplotlib.patheffects import Normal
for t in cl:
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)
def drop_shadow_line(ax):
# copied from examples/misc/svg_filter_line.py
# draw lines
l1, = ax.plot([0.1, 0.5, 0.9], [0.1, 0.9, 0.5], "bo-",
mec="b", mfc="w", lw=5, mew=3, ms=10, label="Line 1")
l2, = ax.plot([0.1, 0.5, 0.9], [0.5, 0.2, 0.7], "ro-",
mec="r", mfc="w", lw=5, mew=3, ms=10, label="Line 1")
gauss = DropShadowFilter(4)
for l in [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 transform
ot = mtransforms.offset_copy(l.get_transform(), ax.figure,
x=4.0, y=-6.0, units='points')
shadow.set_transform(ot)
# adjust zorder of the shadow lines so that it is drawn below the
# original lines
shadow.set_zorder(l.get_zorder() - 0.5)
shadow.set_agg_filter(gauss)
shadow.set_rasterized(True) # to support mixed-mode renderers
ax.set_xlim(0., 1.)
ax.set_ylim(0., 1.)
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
def drop_shadow_patches(ax):
# Copied from barchart_demo.py
N = 5
menMeans = (20, 35, 30, 35, 27)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars
rects1 = ax.bar(ind, menMeans, width, color='r', ec="w", lw=2)
womenMeans = (25, 32, 34, 20, 25)
rects2 = ax.bar(ind + width + 0.1, womenMeans, width,
color='y', ec="w", lw=2)
# gauss = GaussianFilter(1.5, offsets=(1,1), )
gauss = DropShadowFilter(5, offsets=(1, 1), )
shadow = FilteredArtistList(rects1 + rects2, gauss)
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)
def light_filter_pie(ax):
fracs = [15, 30, 45, 10]
explode = (0, 0.05, 0, 0)
pies = ax.pie(fracs, explode=explode)
ax.patch.set_visible(True)
light_filter = LightFilter(9)
for p in pies[0]:
p.set_agg_filter(light_filter)
p.set_rasterized(True) # to support mixed-mode renderers
p.set(ec="none",
lw=2)
gauss = DropShadowFilter(9, offsets=(3, 4), alpha=0.7)
shadow = FilteredArtistList(pies[0], gauss)
ax.add_artist(shadow)
shadow.set_zorder(pies[0][0].get_zorder() - 0.1)
if 1:
plt.figure(1, figsize=(6, 6))
plt.subplots_adjust(left=0.05, right=0.95)
ax = plt.subplot(221)
filtered_text(ax)
ax = plt.subplot(222)
drop_shadow_line(ax)
ax = plt.subplot(223)
drop_shadow_patches(ax)
ax = plt.subplot(224)
ax.set_aspect(1)
light_filter_pie(ax)
ax.set_frame_on(True)
plt.show()