Robochameleon  v1.0
myaa.m
1 function [varargout] = myaa(varargin)
2 %MYAA Render figure with anti-aliasing.
3 % MYAA
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.
10 %
11 % Usage:
12 % myaa: Renders an anti-aliased version of the current figure.
13 %
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.
19 %
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.
24 %
25 % myaa('publish'): An experimental parameter, useful for publishing
26 % matlab programs (see example 3). Beware, it kills the current figure.
27 %
28 % Interactivity:
29 % The anti-aliased figure can be updated with the following keyboard
30 % commands:
31 %
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
37 %
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
47 % the image toolbox.
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
54 %
55 % Example 1:
56 % spharm2;
57 % myaa;
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.
61 %
62 % Example 2:
63 % line(randn(2500,2)',randn(2500,2)','color','black','linewidth',0.01)
64 % myaa(8);
65 %
66 % Example 3:
67 % xpklein;
68 % myaa(2,'standard');
69 %
70 % Example 3:
71 % Put the following in test.m
72 % %% My test publish
73 % % Testing to publish some anti-aliased images
74 % %
75 % spharm2; % Produce some nice graphics
76 % myaa('publish'); % Render an anti-aliased version
77 %
78 % Then run:
79 % publish test.m;
80 % showdemo test;
81 %
82 %
83 % BUGS:
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
86 % future version.
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').
91 %
92 % See also PUBLISH, PRINT
93 %
94 % Version 1.1, 2008-08-21
95 % Version 1.0, 2008-08-05
96 %
97 % Author: Anders Brun
98 % anders@cb.uu.se
99 %
100 
101 %% Force drawing of graphics
102 drawnow;
103 
104 %% Find out about the current DPI...
105 screen_DPI = get(0,'ScreenPixelsPerInch');
106 
107 %% Determine the best choice of convolver.
108 % If IPPL is available, imfilter is much faster. Otherwise it does not
109 % matter too much.
110 try
111  if ippl()
112  myconv = @imfilter;
113  else
114  myconv = @conv2;
115  end
116 catch
117  myconv = @conv2;
118 end
119 
120 %% Set default options and interpret arguments
121 if isempty(varargin)
122  self.K = [4 4];
123  try
124  imfilter(zeros(2,2),zeros(2,2));
125  self.aamethod = 'imresize';
126  catch
127  self.aamethod = 'standard';
128  end
129  self.figmode = 'figure';
130 elseif strcmp(varargin{1},'publish')
131  self.K = [4 4];
132  self.aamethod = 'noshrink';
133  self.figmode = 'publish';
134 elseif strcmp(varargin{1},'update')
135  self = get(gcf,'UserData');
136  figure(self.source_fig);
137  drawnow;
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];
146  end
147  if self.K(1) > 16
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.');
149  end
150  try
151  imfilter(zeros(2,2),zeros(2,2));
152  self.aamethod = 'imresize';
153  catch
154  self.aamethod = 'standard';
155  end
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';
168  end
169 else
170  error('Wrong syntax, run: help myaa');
171 end
172 
173 if length(self.K) == 1
174  self.K = [self.K self.K];
175 end
176 
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);
189  delete(tempfile);
190 end
191 %% Start filtering to remove aliasing
192 w = warning;
193 warning off;
194 if strcmp(self.aamethod,'standard') || strcmp(self.aamethod,'noshrink')
195  % Subsample hires figure image with standard anti-aliasing using a
196  % butterworth filter
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)));
205  else
206  raw_lowres = self.raw_hires;
207  end
208  else
209  raw_lowres = double(cat(3,a1,a2,a3));
210  end
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;
214 end
215 warning(w);
216 
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);
230  ax = axes;
231  image(raw_lowres);
232  set(ax,'Units','pixels');
233  set(ax,'Position',[1 1 sz(2) sz(1)]);
234  axis off;
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)]);
245  ax = axes;
246  image(raw_lowres);
247  set(ax,'Units','normalized');
248  set(ax,'Position',[0 0 1 1]);
249  axis off;
250  close(self.source_fig);
251 elseif strcmp(self.figmode,'update');
252  fig = self.myaa_figure;
253  figure(fig);
254  clf;
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);
262  ax = axes;
263  image(raw_lowres);
264  set(ax,'Units','pixels');
265  set(ax,'Position',[1 1 sz(2) sz(1)]);
266  axis off;
267 elseif strcmp(self.figmode,'lazyupdate');
268  clf;
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);
274  ax = axes;
275  image(raw_lowres);
276  set(ax,'Units','pixels');
277  set(ax,'Position',[1 1 sz(2) sz(1)]);
278  axis off;
279 end
280 
281 %% Store current state
282 
283 set(gcf,'userdata',self);
284 set(gcf,'KeyPressFcn',@keypress);
285 set(gcf,'Interruptible','off');
286 
287 %% Avoid unnecessary console output
288 if nargout == 1
289  varargout(1) = {fig};
290 end
291 
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)));
304 kk = kk./sum(kk(:));
305 
306 function keypress(src,evnt)
307 if isempty(evnt.Character)
308  return
309 end
310 recognized = 0;
311 self = get(gcf,'userdata');
312 
313 if evnt.Character == '+'
314  self.K(2) = max(self.K(2).*0.5^(1/2),1);
315  recognized = 1;
316  set(gcf,'userdata',self);
317  myaa('lazyupdate');
318 elseif evnt.Character == '-'
319  self.K(2) = min(self.K(2).*2^(1/2),16);
320  recognized = 1;
321  set(gcf,'userdata',self);
322  myaa('lazyupdate');
323 elseif evnt.Character == ' ' || evnt.Character == 'r' || evnt.Character == 'R'
324  set(gcf,'userdata',self);
325  myaa('update');
326 elseif evnt.Character == 'q'
327  close(gcf);
328 elseif find('123456789' == evnt.Character)
329  self.K = [str2double(evnt.Character) str2double(evnt.Character)];
330  set(gcf,'userdata',self);
331  myaa('update');
332 end
333 
334 
335 
336 
337