1 function [varargout] = myaa(varargin)
2 %MYAA Render figure with anti-aliasing.
4 % Anti-aliased rendering of the current figure. This makes graphics look
5 % a lot better than in a standard matlab figure, which is useful
for 6 % publishing results on the web or to better see the fine details in a
7 % complex and cluttered plot. Some simple keyboard commands allow
8 % the user to
set the rendering quality interactively, zoom in/out and
9 % re-render when needed.
12 % myaa: Renders an anti-aliased version of the current figure.
14 % myaa(K): Sets the supersampling factor to K. Using a
15 % higher K yields better rendering but takes longer time. If K is
16 % omitted, it defaults to 4. It may be useful to run e.g. myaa(2) to
17 % make a low-quality rendering as a first try, because it is a lot
18 % faster than the default.
20 % myaa([K D]): Sets supersampling factor to K but downsamples the
21 % image by a factor of D. If D is larger than K, the rendered image
22 % will be smaller than the original. If D is smaller than K, the
23 % rendering will be bigger.
25 % myaa(
'publish'): An experimental parameter, useful for publishing
26 % matlab programs (see example 3). Beware, it kills the current figure.
29 % The anti-aliased figure can be updated with the following keyboard
32 % <space> Re-render image (to reflect changes in the figure)
33 % + Zoom in (decrease downsampling factor)
34 % - Zoom out (increase downsampling factor)
35 % 1 ... 9 Change supersampling and downsampling factor to ...
36 % q Quit, i.e. close the anti-aliased figure
38 % Myaa can also be called with up to 3 parameters.
39 % FIG = MYAA(K,AAMETHOD,FIGMODE)
40 % Parameters and output:
41 % K Subsampling factor. If a vector is specified, [K D], then
42 % the second element will describe the downsampling factor.
43 % Default is K = 4 and D = 4.
44 % AAMETHOD Downsampling method. Normally this is chosen automatically.
45 %
'standard': convolution based filtering and downsampling
46 %
'imresize': downsampling using the imresize command from
48 %
'noshrink': used internally
49 % FIGMODE Display mode
50 %
'figure': the normal mode, a new figure is created
51 %
'update': internally used for interactive sessions
52 %
'publish': used internally
53 % FIG A handle to the new anti-aliased figure
58 % % Press
'1',
'2' or
'4' and
try '+' and
'-' 59 % % Press
'r' or <space> to update the anti-aliased rendering, e.g.
60 % % after rotating the 3-D
object in the original figure.
63 % line(randn(2500,2)
',randn(2500,2)',
'color',
'black',
'linewidth',0.01)
71 % Put the following in test.m
73 % % Testing to publish some anti-aliased images
75 % spharm2; % Produce some nice graphics
76 % myaa(
'publish'); % Render an anti-aliased version
84 % Dotted and dashed lines in plots are not rendered correctly. This is
85 % probably due to a bug in Matlab and it will hopefully be fixed in a
87 % The OpenGL renderer does not always manage to render an image large
88 % enough. Try the zbuffer renderer
if you have problems or decrease the
89 % K factor. You can
set the current renderer to zbuffer by running e.g.
90 %
set(gcf,
'renderer',
'zbuffer').
92 % See also PUBLISH, PRINT
94 % Version 1.1, 2008-08-21
95 % Version 1.0, 2008-08-05
101 %% Force drawing of graphics
104 %% Find out about the current DPI...
105 screen_DPI =
get(0,
'ScreenPixelsPerInch');
107 %% Determine the best choice of convolver.
108 % If IPPL is available, imfilter is much faster. Otherwise it does not
120 %% Set default options and interpret arguments
124 imfilter(zeros(2,2),zeros(2,2));
125 self.aamethod = 'imresize';
127 self.aamethod = 'standard';
129 self.figmode = 'figure';
130 elseif strcmp(varargin{1},
'publish')
132 self.aamethod =
'noshrink';
133 self.figmode =
'publish';
134 elseif strcmp(varargin{1},
'update')
135 self =
get(gcf,
'UserData');
136 figure(
self.source_fig);
138 self.figmode =
'update';
139 elseif strcmp(varargin{1},
'lazyupdate')
140 self =
get(gcf,
'UserData');
141 self.figmode =
'lazyupdate';
142 elseif length(varargin) == 1
143 self.K = varargin{1};
144 if length(
self.K) == 1
145 self.K = [
self.K
self.K];
148 error(
'To avoid excessive use of memory, K has been limited to max 16. Change the code to fix this on your own risk.');
151 imfilter(zeros(2,2),zeros(2,2));
152 self.aamethod =
'imresize';
154 self.aamethod =
'standard';
156 self.figmode =
'figure';
157 elseif length(varargin) == 2
158 self.K = varargin{1};
159 self.aamethod = varargin{2};
160 self.figmode =
'figure';
161 elseif length(varargin) == 3
162 self.K = varargin{1};
163 self.aamethod = varargin{2};
164 self.figmode = varargin{3};
165 if strcmp(
self.figmode,
'publish') && ~strcmp(varargin{2},
'noshrink')
166 printf(
'\nThe AAMETHOD was not set to ''noshrink'': Fixed.\n\n');
167 self.aamethod =
'noshrink';
170 error(
'Wrong syntax, run: help myaa');
173 if length(
self.K) == 1
174 self.K = [
self.K
self.K];
177 %% Capture current figure in high resolution
178 if ~strcmp(
self.figmode,
'lazyupdate');
179 tempfile =
'myaa_temp_screendump.png';
180 self.source_fig = gcf;
181 current_paperpositionmode =
get(
self.source_fig,
'PaperPositionMode');
182 current_inverthardcopy =
get(
self.source_fig,
'InvertHardcopy');
183 set(
self.source_fig,
'PaperPositionMode',
'auto');
184 set(
self.source_fig,
'InvertHardcopy',
'off');
185 print(
self.source_fig,[
'-r',num2str(screen_DPI*
self.K(1))],
'-dpng', tempfile);
186 set(
self.source_fig,
'InvertHardcopy',current_inverthardcopy);
187 set(
self.source_fig,
'PaperPositionMode',current_paperpositionmode);
188 self.raw_hires = imread(tempfile);
191 %% Start filtering to
remove aliasing
194 if strcmp(
self.aamethod,
'standard') || strcmp(
self.aamethod,
'noshrink')
195 % Subsample hires figure image with standard anti-aliasing
using a
197 kk = lpfilter(
self.K(2)*3,
self.K(2)*0.9,2);
198 mm = myconv(ones(size(
self.raw_hires(:,:,1))),kk,
'same');
199 a1 = max(min(myconv(single(
self.raw_hires(:,:,1))/(256),kk,
'same'),1),0)./mm;
200 a2 = max(min(myconv(single(
self.raw_hires(:,:,2))/(256),kk,
'same'),1),0)./mm;
201 a3 = max(min(myconv(single(
self.raw_hires(:,:,3))/(256),kk,
'same'),1),0)./mm;
202 if strcmp(
self.aamethod,
'standard')
203 if abs(1-self.K(2)) > 0.001
204 raw_lowres =
double(cat(3,a1(2:self.K(2):end,2:self.K(2):end),a2(2:self.K(2):end,2:self.K(2):end),a3(2:self.K(2):end,2:self.K(2):end)));
206 raw_lowres = self.raw_hires;
209 raw_lowres =
double(cat(3,a1,a2,a3));
211 elseif strcmp(self.aamethod,'imresize')
212 % This is probably the fastest method available at this moment...
213 raw_lowres = single(imresize(self.raw_hires,1/self.K(2),'bilinear'))/256;
217 %% Place the anti-aliased image in some image on the screen ...
218 if strcmp(self.figmode,'figure');
219 % Create a new figure at the same place as the previous
220 % The content of this new image is just a bitmap...
221 oldpos = get(gcf,'Position');
222 self.myaa_figure = figure;
223 fig = self.myaa_figure;
224 set(fig,'Menubar','none');
225 set(fig,'Resize','off');
226 sz = size(raw_lowres);
227 set(fig,'Units','pixels');
228 pos = [oldpos(1:2) sz(2:-1:1)];
229 set(fig,'Position',pos);
232 set(ax,'Units','pixels');
233 set(ax,'Position',[1 1 sz(2) sz(1)]);
235 elseif strcmp(self.figmode,'publish');
236 % Create a new figure at the same place as the previous
237 % The content of this new image is just a bitmap...
238 self.myaa_figure = figure;
239 fig = self.myaa_figure;
240 current_units = get(self.source_fig,'Units');
241 set(self.source_fig,'Units','pixels');
242 pos = get(self.source_fig,'Position');
243 set(self.source_fig,'Units',current_units);
244 set(fig,'Position',[pos(1) pos(2) pos(3) pos(4)]);
247 set(ax,'Units','normalized');
248 set(ax,'Position',[0 0 1 1]);
250 close(self.source_fig);
251 elseif strcmp(self.figmode,'update');
252 fig = self.myaa_figure;
255 set(fig,'Menubar','none');
256 set(fig,'Resize','off');
257 sz = size(raw_lowres);
258 set(fig,'Units','pixels');
259 pos = get(fig,'Position');
260 pos(3:4) = sz(2:-1:1);
261 set(fig,'Position',pos);
264 set(ax,'Units','pixels');
265 set(ax,'Position',[1 1 sz(2) sz(1)]);
267 elseif strcmp(self.figmode,'lazyupdate');
269 fig = self.myaa_figure;
270 sz = size(raw_lowres);
271 pos = get(fig,'Position');
272 pos(3:4) = sz(2:-1:1);
273 set(fig,'Position',pos);
276 set(ax,'Units','pixels');
277 set(ax,'Position',[1 1 sz(2) sz(1)]);
281 %% Store current state
283 set(gcf,'userdata',self);
284 set(gcf,'KeyPressFcn',@keypress);
285 set(gcf,'Interruptible','off');
287 %% Avoid unnecessary console output
289 varargout(1) = {fig};
292 %% A simple lowpass filter kernel (Butterworth).
293 % sz is the size of the filter
294 % subsmp is the downsampling factor to be used later
295 % n is the degree of the butterworth filter
296 function kk = lpfilter(sz, subsmp, n)
297 sz = 2*floor(sz/2)+1; % make sure the size of the filter is odd
298 cut_frequency = 0.5 / subsmp;
299 range = (-(sz-1)/2:(sz-1)/2)/(sz-1);
300 [ii,jj] = ndgrid(range,range);
301 rr = sqrt(ii.^2+jj.^2);
302 kk = ifftshift(1./(1+(rr./cut_frequency).^(2*n)));
303 kk = fftshift(real(ifft2(kk)));
306 function keypress(src,evnt)
307 if isempty(evnt.Character)
311 self = get(gcf,'userdata');
313 if evnt.Character == '+'
314 self.K(2) = max(self.K(2).*0.5^(1/2),1);
316 set(gcf,'userdata',self);
318 elseif evnt.Character == '-'
319 self.K(2) = min(self.K(2).*2^(1/2),16);
321 set(gcf,'userdata',self);
323 elseif evnt.Character == ' ' || evnt.Character == 'r' || evnt.Character == 'R'
324 set(gcf,'userdata',self);
326 elseif evnt.Character == 'q'
328 elseif find('123456789' == evnt.Character)
329 self.K = [str2double(evnt.Character) str2double(evnt.Character)];
330 set(gcf,'userdata',self);