Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b5f348d

Browse files
committed
add support for image filtering in agg backend
svn path=/trunk/matplotlib/; revision=7488
1 parent 1e6f21e commit b5f348d

File tree

8 files changed

+452
-12
lines changed

8 files changed

+452
-12
lines changed

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2009-08-14 Add support for image filtering for agg back end. See the example
2+
demo_agg_filter.py. -JJL
3+
14
2009-08-09 AnnotationBbox added. Similar to Annotation, but works with
25
OffsetBox instead of Text. See the example
36
demo_annotation_box.py. -JJL
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
import matplotlib.pyplot as plt
2+
3+
import numpy as np
4+
import scipy.ndimage as NI
5+
import matplotlib.cm as cm
6+
import matplotlib.mlab as mlab
7+
8+
9+
class BaseFilter(object):
10+
def prepare_image(self, src_image, dpi, pad):
11+
ny, nx, depth = src_image.shape
12+
#tgt_image = np.zeros([pad*2+ny, pad*2+nx, depth], dtype="d")
13+
padded_src = np.zeros([pad*2+ny, pad*2+nx, depth], dtype="d")
14+
padded_src[pad:-pad, pad:-pad,:] = src_image[:,:,:]
15+
16+
return padded_src#, tgt_image
17+
18+
def get_pad(self, dpi):
19+
return 0
20+
21+
def __call__(self, im, dpi):
22+
pad = self.get_pad(dpi)
23+
padded_src = self.prepare_image(im, dpi, pad)
24+
tgt_image = self.process_image(padded_src, dpi)
25+
return tgt_image, -pad, -pad
26+
27+
28+
class OffsetFilter(BaseFilter):
29+
def __init__(self, offsets=None):
30+
if offsets is None:
31+
self.offsets = (0, 0)
32+
else:
33+
self.offsets = offsets
34+
35+
def get_pad(self, dpi):
36+
return max(*self.offsets)
37+
38+
def process_image(self, padded_src, dpi):
39+
ox, oy = self.offsets
40+
a1 = np.roll(padded_src, ox, axis=1)
41+
a2 = np.roll(a1, -oy, axis=0)
42+
return a2
43+
44+
class GaussianFilter(BaseFilter):
45+
"simple gauss filter"
46+
def __init__(self, sigma, alpha=0.5, color=None):
47+
self.sigma = sigma
48+
self.alpha = alpha
49+
if color is None:
50+
self.color=(0, 0, 0)
51+
else:
52+
self.color=color
53+
54+
def get_pad(self, dpi):
55+
return int(self.sigma*3)
56+
57+
58+
def process_image(self, padded_src, dpi):
59+
#offsetx, offsety = int(self.offsets[0]), int(self.offsets[1])
60+
tgt_image = np.zeros_like(padded_src)
61+
tgt_image[:,:,-1] = NI.gaussian_filter(padded_src[:,:,-1]*self.alpha,
62+
self.sigma)
63+
tgt_image[:,:,:-1] = self.color
64+
return tgt_image
65+
66+
class DropShadowFilter(BaseFilter):
67+
def __init__(self, sigma, alpha=0.3, color=None, offsets=None):
68+
self.gauss_filter = GaussianFilter(sigma, alpha, color)
69+
self.offset_filter = OffsetFilter(offsets)
70+
71+
def get_pad(self, dpi):
72+
return max(self.gauss_filter.get_pad(dpi),
73+
self.offset_filter.get_pad(dpi))
74+
75+
def process_image(self, padded_src, dpi):
76+
t1 = self.gauss_filter.process_image(padded_src, dpi)
77+
t2 = self.offset_filter.process_image(t1, dpi)
78+
return t2
79+
80+
81+
from matplotlib.colors import LightSource
82+
83+
class LightFilter(BaseFilter):
84+
"simple gauss filter"
85+
def __init__(self, sigma, fraction=0.5):
86+
self.gauss_filter = GaussianFilter(sigma, alpha=1)
87+
self.light_source = LightSource()
88+
self.fraction = fraction
89+
#hsv_min_val=0.5,hsv_max_val=0.9,
90+
# hsv_min_sat=0.1,hsv_max_sat=0.1)
91+
def get_pad(self, dpi):
92+
return self.gauss_filter.get_pad(dpi)
93+
94+
def process_image(self, padded_src, dpi):
95+
t1 = self.gauss_filter.process_image(padded_src, dpi)
96+
elevation = t1[:,:,3]
97+
rgb = padded_src[:,:,:3]
98+
99+
rgb2 = self.light_source.shade_rgb(rgb, elevation,
100+
fraction=self.fraction)
101+
102+
tgt = np.empty_like(padded_src)
103+
tgt[:,:,:3] = rgb2
104+
tgt[:,:,3] = padded_src[:,:,3]
105+
106+
return tgt
107+
108+
109+
110+
class GrowFilter(BaseFilter):
111+
"enlarge the area"
112+
def __init__(self, pixels, color=None):
113+
self.pixels = pixels
114+
if color is None:
115+
self.color=(1, 1, 1)
116+
else:
117+
self.color=color
118+
119+
def __call__(self, im, dpi):
120+
pad = self.pixels
121+
ny, nx, depth = im.shape
122+
new_im = np.empty([pad*2+ny, pad*2+nx, depth], dtype="d")
123+
alpha = new_im[:,:,3]
124+
alpha.fill(0)
125+
alpha[pad:-pad, pad:-pad] = im[:,:,-1]
126+
alpha2 = NI.grey_dilation(alpha, size=(self.pixels, self.pixels))
127+
new_im[:,:,-1] = alpha2
128+
new_im[:,:,:-1] = self.color
129+
offsetx, offsety = -pad, -pad
130+
131+
return new_im, offsetx, offsety
132+
133+
134+
from matplotlib.artist import Artist
135+
136+
class FilteredArtistList(Artist):
137+
"""
138+
A simple container to draw filtered artist.
139+
"""
140+
def __init__(self, artist_list, filter):
141+
self._artist_list = artist_list
142+
self._filter = filter
143+
Artist.__init__(self)
144+
145+
def draw(self, renderer):
146+
renderer.start_rasterizing()
147+
renderer.start_filter()
148+
for a in self._artist_list:
149+
a.draw(renderer)
150+
renderer.stop_filter(self._filter)
151+
renderer.stop_rasterizing()
152+
153+
154+
155+
import matplotlib.transforms as mtransforms
156+
157+
def filtered_text(ax):
158+
# mostly copied from contour_demo.py
159+
160+
# prepare image
161+
delta = 0.025
162+
x = np.arange(-3.0, 3.0, delta)
163+
y = np.arange(-2.0, 2.0, delta)
164+
X, Y = np.meshgrid(x, y)
165+
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
166+
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
167+
# difference of Gaussians
168+
Z = 10.0 * (Z2 - Z1)
169+
170+
171+
# draw
172+
im = ax.imshow(Z, interpolation='bilinear', origin='lower',
173+
cmap=cm.gray, extent=(-3,3,-2,2))
174+
levels = np.arange(-1.2, 1.6, 0.2)
175+
CS = ax.contour(Z, levels,
176+
origin='lower',
177+
linewidths=2,
178+
extent=(-3,3,-2,2))
179+
180+
ax.set_aspect("auto")
181+
182+
# contour label
183+
cl = ax.clabel(CS, levels[1::2], # label every second level
184+
inline=1,
185+
fmt='%1.1f',
186+
fontsize=11)
187+
188+
# change clable color to black
189+
for t in cl:
190+
t.set_color("k")
191+
192+
# Add white glows to improve visibility of labels.
193+
white_glows = FilteredArtistList(cl, GrowFilter(3))
194+
ax.add_artist(white_glows)
195+
white_glows.set_zorder(cl[0].get_zorder()-0.1)
196+
197+
ax.xaxis.set_visible(False)
198+
ax.yaxis.set_visible(False)
199+
200+
201+
def drop_shadow_line(ax):
202+
# copyed from examples/misc/svg_filter_line.py
203+
204+
# draw lines
205+
l1, = ax.plot([0.1, 0.5, 0.9], [0.1, 0.9, 0.5], "bo-",
206+
mec="b", mfc="w", lw=5, mew=3, ms=10, label="Line 1")
207+
l2, = ax.plot([0.1, 0.5, 0.9], [0.5, 0.2, 0.7], "ro-",
208+
mec="r", mfc="w", lw=5, mew=3, ms=10, label="Line 1")
209+
210+
211+
gauss = DropShadowFilter(2)
212+
213+
for l in [l1, l2]:
214+
215+
# draw shadows with same lines with slight offset.
216+
217+
xx = l.get_xdata()
218+
yy = l.get_ydata()
219+
shadow, = ax.plot(xx, yy)
220+
shadow.update_from(l)
221+
222+
# offset transform
223+
ot = mtransforms.offset_copy(l.get_transform(), ax.figure,
224+
x=4.0, y=-6.0, units='points')
225+
226+
shadow.set_transform(ot)
227+
228+
229+
# adjust zorder of the shadow lines so that it is drawn below the
230+
# original lines
231+
shadow.set_zorder(l.get_zorder()-0.5)
232+
shadow.set_agg_filter(gauss)
233+
shadow.set_rasterized(True) # to support mixed-mode renderers
234+
235+
236+
237+
ax.set_xlim(0., 1.)
238+
ax.set_ylim(0., 1.)
239+
240+
ax.xaxis.set_visible(False)
241+
ax.yaxis.set_visible(False)
242+
243+
244+
245+
246+
def drop_shadow_patches(ax):
247+
# copyed from barchart_demo.py
248+
N = 5
249+
menMeans = (20, 35, 30, 35, 27)
250+
251+
ind = np.arange(N) # the x locations for the groups
252+
width = 0.35 # the width of the bars
253+
254+
rects1 = ax.bar(ind, menMeans, width, color='r', ec="w", lw=2)
255+
256+
womenMeans = (25, 32, 34, 20, 25)
257+
rects2 = ax.bar(ind+width+0.1, womenMeans, width, color='y', ec="w", lw=2)
258+
259+
#gauss = GaussianFilter(1.5, offsets=(1,1), )
260+
gauss = DropShadowFilter(1.5, offsets=(1,1), )
261+
shadow = FilteredArtistList(rects1+rects2, gauss)
262+
ax.add_artist(shadow)
263+
shadow.set_zorder(rects1[0].get_zorder()-0.1)
264+
265+
ax.set_xlim(ind[0]-0.5, ind[-1]+1.5)
266+
ax.set_ylim(0, 40)
267+
268+
ax.xaxis.set_visible(False)
269+
ax.yaxis.set_visible(False)
270+
271+
272+
def light_filter_pie(ax):
273+
fracs = [15,30,45, 10]
274+
explode=(0, 0.05, 0, 0)
275+
pies = ax.pie(fracs, explode=explode)
276+
ax.patch.set_visible(True)
277+
278+
light_filter = LightFilter(8)
279+
for p in pies[0]:
280+
p.set_agg_filter(light_filter)
281+
p.set_rasterized(True) # to support mixed-mode renderers
282+
p.set(ec="none",
283+
lw=2)
284+
285+
gauss = DropShadowFilter(3, offsets=(3,4), alpha=0.7)
286+
shadow = FilteredArtistList(pies[0], gauss)
287+
ax.add_artist(shadow)
288+
shadow.set_zorder(pies[0][0].get_zorder()-0.1)
289+
290+
291+
if 1:
292+
293+
plt.figure(1, figsize=(6, 6))
294+
plt.subplots_adjust(left=0.05, right=0.95)
295+
296+
ax = plt.subplot(221)
297+
filtered_text(ax)
298+
299+
ax = plt.subplot(222)
300+
drop_shadow_line(ax)
301+
302+
ax = plt.subplot(223)
303+
drop_shadow_patches(ax)
304+
305+
ax = plt.subplot(224)
306+
ax.set_aspect(1)
307+
light_filter_pie(ax)
308+
ax.set_frame_on(True)
309+
310+
plt.show()
311+
312+

lib/matplotlib/artist.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,15 @@ def before(artist, renderer):
3636
if artist.get_rasterized():
3737
renderer.start_rasterizing()
3838

39+
if artist.get_agg_filter() is not None:
40+
renderer.start_filter()
41+
42+
3943
def after(artist, renderer):
44+
45+
if artist.get_agg_filter() is not None:
46+
renderer.stop_filter(artist.get_agg_filter())
47+
4048
if artist.get_rasterized():
4149
renderer.stop_rasterizing()
4250

@@ -78,7 +86,8 @@ def __init__(self):
7886
self._picker = None
7987
self._contains = None
8088
self._rasterized = None
81-
89+
self._agg_filter = None
90+
8291
self.eventson = False # fire events only if eventson
8392
self._oid = 0 # an observer id
8493
self._propobservers = {} # a dict from oids to funcs
@@ -548,6 +557,7 @@ def _set_gc_clip(self, gc):
548557
gc.set_clip_path(None)
549558

550559
def get_rasterized(self):
560+
"return True if the artist is to be rasterized"
551561
return self._rasterized
552562

553563
def set_rasterized(self, rasterized):
@@ -563,6 +573,17 @@ def set_rasterized(self, rasterized):
563573

564574
self._rasterized = rasterized
565575

576+
def get_agg_filter(self):
577+
"return filter function to be used for agg filter"
578+
return self._agg_filter
579+
580+
def set_agg_filter(self, filter_func):
581+
"""
582+
set agg_filter fuction.
583+
584+
"""
585+
self._agg_filter = filter_func
586+
566587
def draw(self, renderer, *args, **kwargs):
567588
'Derived classes drawing method'
568589
if not self.get_visible(): return

0 commit comments

Comments
 (0)