1 | %EXPORT_FIG Exports figures suitable for publication
|
---|
2 | %
|
---|
3 | % Examples:
|
---|
4 | % im = export_fig
|
---|
5 | % [im alpha] = export_fig
|
---|
6 | % export_fig filename
|
---|
7 | % export_fig filename -format1 -format2
|
---|
8 | % export_fig ... -nocrop
|
---|
9 | % export_fig ... -native
|
---|
10 | % export_fig ... -m<val>
|
---|
11 | % export_fig ... -r<val>
|
---|
12 | % export_fig ... -a<val>
|
---|
13 | % export_fig ... -q<val>
|
---|
14 | % export_fig ... -<renderer>
|
---|
15 | % export_fig ... -<colorspace>
|
---|
16 | % export_fig ... -append
|
---|
17 | % export_fig(..., handle)
|
---|
18 | %
|
---|
19 | % This function saves a figure or single axes to one or more vector and/or
|
---|
20 | % bitmap file formats, and/or outputs a rasterized version to the
|
---|
21 | % workspace, with the following properties:
|
---|
22 | % - Figure/axes reproduced as it appears on screen
|
---|
23 | % - Cropped borders (optional)
|
---|
24 | % - Embedded fonts (vector formats)
|
---|
25 | % - Improved line and grid line styles
|
---|
26 | % - Anti-aliased graphics (bitmap formats)
|
---|
27 | % - Render images at native resolution (optional for bitmap formats)
|
---|
28 | % - Transparent background supported (pdf, eps, png)
|
---|
29 | % - Semi-transparent patch objects supported (png only)
|
---|
30 | % - RGB, CMYK or grayscale output (CMYK only with pdf, eps, tiff)
|
---|
31 | % - Variable image compression, including lossless (pdf, eps, jpg)
|
---|
32 | % - Optionally append to file (pdf, tiff)
|
---|
33 | % - Vector formats: pdf, eps
|
---|
34 | % - Bitmap formats: png, tiff, jpg, bmp, export to workspace
|
---|
35 | %
|
---|
36 | % This function is especially suited to exporting figures for use in
|
---|
37 | % publications and presentations, because of the high quality and
|
---|
38 | % portability of media produced.
|
---|
39 | %
|
---|
40 | % Note that the background color and figure dimensions are reproduced
|
---|
41 | % (the latter approximately, and ignoring cropping & magnification) in the
|
---|
42 | % output file. For transparent background (and semi-transparent patch
|
---|
43 | % objects), set the figure (and axes) 'Color' property to 'none'; pdf, eps
|
---|
44 | % and png are the only file formats to support a transparent background,
|
---|
45 | % whilst the png format alone supports transparency of patch objects.
|
---|
46 | %
|
---|
47 | % The choice of renderer (opengl, zbuffer or painters) has a large impact
|
---|
48 | % on the quality of output. Whilst the default value (opengl for bitmaps,
|
---|
49 | % painters for vector formats) generally gives good results, if you aren't
|
---|
50 | % satisfied then try another renderer. Notes: 1) For vector formats (eps,
|
---|
51 | % pdf), only painters generates vector graphics. 2) For bitmaps, only
|
---|
52 | % opengl can render transparent patch objects correctly. 3) For bitmaps,
|
---|
53 | % only painters will correctly scale line dash and dot lengths when
|
---|
54 | % magnifying or anti-aliasing. 4) Fonts may be substitued with Courier when
|
---|
55 | % using painters.
|
---|
56 | %
|
---|
57 | % When exporting to vector format (pdf & eps) and bitmap format using the
|
---|
58 | % painters renderer, this function requires that ghostscript is installed
|
---|
59 | % on your system. You can download this from:
|
---|
60 | % http://www.ghostscript.com
|
---|
61 | % When exporting to eps it additionally requires pdftops, from the Xpdf
|
---|
62 | % suite of functions. You can download this from:
|
---|
63 | % http://www.foolabs.com/xpdf
|
---|
64 | %
|
---|
65 | %IN:
|
---|
66 | % filename - string containing the name (optionally including full or
|
---|
67 | % relative path) of the file the figure is to be saved as. If
|
---|
68 | % a path is not specified, the figure is saved in the current
|
---|
69 | % directory. If no name and no output arguments are specified,
|
---|
70 | % the default name, 'export_fig_out', is used. If neither a
|
---|
71 | % file extension nor a format are specified, a ".png" is added
|
---|
72 | % and the figure saved in that format.
|
---|
73 | % -format1, -format2, etc. - strings containing the extensions of the
|
---|
74 | % file formats the figure is to be saved as.
|
---|
75 | % Valid options are: '-pdf', '-eps', '-png',
|
---|
76 | % '-tif', '-jpg' and '-bmp'. All combinations
|
---|
77 | % of formats are valid.
|
---|
78 | % -nocrop - option indicating that the borders of the output are not to
|
---|
79 | % be cropped.
|
---|
80 | % -m<val> - option where val indicates the factor to magnify the
|
---|
81 | % on-screen figure dimensions by when generating bitmap
|
---|
82 | % outputs. Default: '-m1'.
|
---|
83 | % -r<val> - option val indicates the resolution (in pixels per inch) to
|
---|
84 | % export bitmap outputs at, keeping the dimensions of the
|
---|
85 | % on-screen figure. Default: sprintf('-r%g', get(0,
|
---|
86 | % 'ScreenPixelsPerInch')). Note that the -m and -r options
|
---|
87 | % change the same property.
|
---|
88 | % -native - option indicating that the output resolution (when outputting
|
---|
89 | % a bitmap format) should be such that the vertical resolution
|
---|
90 | % of the first suitable image found in the figure is at the
|
---|
91 | % native resolution of that image. To specify a particular
|
---|
92 | % image to use, give it the tag 'export_fig_native'. Notes:
|
---|
93 | % This overrides any value set with the -m and -r options. It
|
---|
94 | % also assumes that the image is displayed front-to-parallel
|
---|
95 | % with the screen. The output resolution is approximate and
|
---|
96 | % should not be relied upon. Anti-aliasing can have adverse
|
---|
97 | % effects on image quality (disable with the -a1 option).
|
---|
98 | % -a1, -a2, -a3, -a4 - option indicating the amount of anti-aliasing to
|
---|
99 | % use for bitmap outputs. '-a1' means no anti-
|
---|
100 | % aliasing; '-a4' is the maximum amount (default).
|
---|
101 | % -<renderer> - option to force a particular renderer (painters, opengl
|
---|
102 | % or zbuffer) to be used over the default: opengl for
|
---|
103 | % bitmaps; painters for vector formats.
|
---|
104 | % -<colorspace> - option indicating which colorspace color figures should
|
---|
105 | % be saved in: RGB (default), CMYK or gray. CMYK is only
|
---|
106 | % supported in pdf, eps and tiff output.
|
---|
107 | % -q<val> - option to vary bitmap image quality (in pdf, eps and jpg
|
---|
108 | % files only). Larger val, in the range 0-100, gives higher
|
---|
109 | % quality/lower compression. val > 100 gives lossless
|
---|
110 | % compression. Default: '-q95' for jpg, ghostscript prepress
|
---|
111 | % default for pdf & eps. Note: lossless compression can
|
---|
112 | % sometimes give a smaller file size than the default lossy
|
---|
113 | % compression, depending on the type of images.
|
---|
114 | % -append - option indicating that if the file (pdfs only) already
|
---|
115 | % exists, the figure is to be appended as a new page, instead
|
---|
116 | % of being overwritten (default).
|
---|
117 | % handle - The handle of the figure or axes to be saved. Default: gcf.
|
---|
118 | %
|
---|
119 | %OUT:
|
---|
120 | % im - MxNxC uint8 image array of the figure.
|
---|
121 | % alpha - MxN single array of alphamatte values in range [0,1], for the
|
---|
122 | % case when the background is transparent.
|
---|
123 | %
|
---|
124 | % Some helpful examples and tips can be found at:
|
---|
125 | % http://sites.google.com/site/oliverwoodford/software/export_fig
|
---|
126 | %
|
---|
127 | % See also PRINT, SAVEAS.
|
---|
128 |
|
---|
129 | % Copyright (C) Oliver Woodford 2008-2010
|
---|
130 |
|
---|
131 | % The idea of using ghostscript is inspired by Peder Axensten's SAVEFIG
|
---|
132 | % (fex id: 10889) which is itself inspired by EPS2PDF (fex id: 5782).
|
---|
133 | % The idea for using pdftops came from the MATLAB newsgroup (id: 168171).
|
---|
134 | % The idea of editing the EPS file to change line styles comes from Jiro
|
---|
135 | % Doke's FIXPSLINESTYLE (fex id: 17928).
|
---|
136 | % The idea of changing dash length with line width came from comments on
|
---|
137 | % fex id: 5743, but the implementation is mine :)
|
---|
138 | % The idea of anti-aliasing bitmaps came from Anders Brun's MYAA (fex id:
|
---|
139 | % 20979).
|
---|
140 | % The idea of appending figures in pdfs came from Matt C in comments on the
|
---|
141 | % FEX (id: 23629)
|
---|
142 |
|
---|
143 | % Many thanks to Roland Martin for pointing out the colour MATLAB
|
---|
144 | % bug/feature with colorbar axes and transparent backgrounds.
|
---|
145 | % Thanks also to Andrew Matthews for describing a bug to do with the figure
|
---|
146 | % size changing in -nodisplay mode. I couldn't reproduce it but included a
|
---|
147 | % fix anyway.
|
---|
148 |
|
---|
149 | function [im alpha] = export_fig(varargin)
|
---|
150 | % Parse the input arguments
|
---|
151 | [fig options] = parse_args(nargout, varargin{:});
|
---|
152 | % Isolate the subplot, if it is one
|
---|
153 | cls = strcmp(get(fig, 'Type'), 'axes');
|
---|
154 | if cls
|
---|
155 | % Given a handle of a single set of axes
|
---|
156 | fig = isolate_subplot(fig);
|
---|
157 | else
|
---|
158 | old_mode = get(fig, 'InvertHardcopy');
|
---|
159 | end
|
---|
160 | % Hack the font units where necessary (due to a font rendering bug in
|
---|
161 | % print?). This may not work perfectly in all cases. Also it can change the
|
---|
162 | % figure layout if reverted, so use a copy.
|
---|
163 | magnify = options.magnify * options.aa_factor;
|
---|
164 | if isbitmap(options) && magnify ~= 1
|
---|
165 | fontu = findobj(fig, 'FontUnits', 'normalized');
|
---|
166 | if ~isempty(fontu)
|
---|
167 | % Some normalized font units found
|
---|
168 | if ~cls
|
---|
169 | fig = copyfig(fig);
|
---|
170 | set(fig, 'Visible', 'off');
|
---|
171 | fontu = findobj(fig, 'FontUnits', 'normalized');
|
---|
172 | cls = true;
|
---|
173 | end
|
---|
174 | set(fontu, 'FontUnits', 'points');
|
---|
175 | end
|
---|
176 | end
|
---|
177 | % Set to print exactly what is there
|
---|
178 | set(fig, 'InvertHardcopy', 'off');
|
---|
179 | % Set the renderer
|
---|
180 | switch options.renderer
|
---|
181 | case 1
|
---|
182 | renderer = '-opengl';
|
---|
183 | case 2
|
---|
184 | renderer = '-zbuffer';
|
---|
185 | case 3
|
---|
186 | renderer = '-painters';
|
---|
187 | otherwise
|
---|
188 | renderer = '-opengl'; % Default for bitmaps
|
---|
189 | end
|
---|
190 | % Do the bitmap formats first
|
---|
191 | if isbitmap(options)
|
---|
192 | % Get the background colour
|
---|
193 | tcol = get(fig, 'Color');
|
---|
194 | if isequal(tcol, 'none') && (options.png || options.alpha)
|
---|
195 | % Get out an alpha channel
|
---|
196 | % MATLAB "feature": black colorbar axes can change to white and vice versa!
|
---|
197 | hCB = findobj(fig, 'Type', 'axes', 'Tag', 'Colorbar');
|
---|
198 | if isempty(hCB)
|
---|
199 | yCol = [];
|
---|
200 | xCol = [];
|
---|
201 | else
|
---|
202 | yCol = get(hCB, 'YColor');
|
---|
203 | xCol = get(hCB, 'XColor');
|
---|
204 | if iscell(yCol)
|
---|
205 | yCol = cell2mat(yCol);
|
---|
206 | xCol = cell2mat(xCol);
|
---|
207 | end
|
---|
208 | yCol = sum(yCol, 2);
|
---|
209 | xCol = sum(xCol, 2);
|
---|
210 | end
|
---|
211 | % MATLAB "feature": apparently figure size can change when changing
|
---|
212 | % colour in -nodisplay mode
|
---|
213 | pos = get(fig, 'Position');
|
---|
214 | % Set the background colour to black, and set size in case it was
|
---|
215 | % changed internally
|
---|
216 | set(fig, 'Color', 'k', 'Position', pos);
|
---|
217 | % Correct the colorbar axes colours
|
---|
218 | set(hCB(yCol==0), 'YColor', [0 0 0]);
|
---|
219 | set(hCB(xCol==0), 'XColor', [0 0 0]);
|
---|
220 | % Print large version to array
|
---|
221 | B = print2array(fig, magnify, renderer);
|
---|
222 | % Downscale the image
|
---|
223 | B = downsize(single(B), 0, options.aa_factor);
|
---|
224 | % Set background to white (and set size)
|
---|
225 | set(fig, 'Color', 'w', 'Position', pos);
|
---|
226 | % Correct the colorbar axes colours
|
---|
227 | set(hCB(yCol==3), 'YColor', [1 1 1]);
|
---|
228 | set(hCB(xCol==3), 'XColor', [1 1 1]);
|
---|
229 | % Print large version to array
|
---|
230 | A = print2array(fig, magnify, renderer);
|
---|
231 | % Downscale the image
|
---|
232 | A = downsize(single(A), 255, options.aa_factor);
|
---|
233 | % Set the background colour (and size) back to normal
|
---|
234 | set(fig, 'Color', 'none', 'Position', pos);
|
---|
235 | % Compute the alpha map
|
---|
236 | alpha = sum(B - A, 3) / (255*3) + 1;
|
---|
237 | A = alpha;
|
---|
238 | A(A==0) = 1;
|
---|
239 | A = B ./ A(:,:,[1 1 1]);
|
---|
240 | clear B
|
---|
241 | % Convert to greyscale
|
---|
242 | if options.colourspace == 2
|
---|
243 | A = rgb2grey(A);
|
---|
244 | end
|
---|
245 | A = uint8(A);
|
---|
246 | % Crop the background
|
---|
247 | if options.crop
|
---|
248 | [alpha v] = crop_background(alpha, 0);
|
---|
249 | A = A(v(1):v(2),v(3):v(4),:);
|
---|
250 | end
|
---|
251 | if options.png
|
---|
252 | % Compute the resolution
|
---|
253 | res = options.magnify * get(0, 'ScreenPixelsPerInch') / 25.4e-3;
|
---|
254 | % Save the png
|
---|
255 | imwrite(A, [options.name '.png'], 'Alpha', alpha, 'ResolutionUnit', 'meter', 'XResolution', res, 'YResolution', res);
|
---|
256 | % Clear the png bit
|
---|
257 | options.png = false;
|
---|
258 | end
|
---|
259 | % Return only one channel for greyscale
|
---|
260 | if isbitmap(options)
|
---|
261 | A = check_greyscale(A);
|
---|
262 | end
|
---|
263 | if options.alpha
|
---|
264 | % Store the image
|
---|
265 | im = A;
|
---|
266 | % Clear the alpha bit
|
---|
267 | options.alpha = false;
|
---|
268 | end
|
---|
269 | % Get the non-alpha image
|
---|
270 | if isbitmap(options)
|
---|
271 | alph = alpha(:,:,ones(1, size(A, 3)));
|
---|
272 | A = uint8(single(A) .* alph + 255 * (1 - alph));
|
---|
273 | clear alph
|
---|
274 | end
|
---|
275 | if options.im
|
---|
276 | % Store the new image
|
---|
277 | im = A;
|
---|
278 | end
|
---|
279 | else
|
---|
280 | % Print large version to array
|
---|
281 | if isequal(tcol, 'none')
|
---|
282 | % MATLAB "feature": apparently figure size can change when changing
|
---|
283 | % colour in -nodisplay mode
|
---|
284 | pos = get(fig, 'Position');
|
---|
285 | set(fig, 'Color', 'w', 'Position', pos);
|
---|
286 | A = print2array(fig, magnify, renderer);
|
---|
287 | set(fig, 'Color', 'none', 'Position', pos);
|
---|
288 | tcol = 255;
|
---|
289 | else
|
---|
290 | A = print2array(fig, magnify, renderer);
|
---|
291 | tcol = tcol * 255;
|
---|
292 | if ~isequal(tcol, round(tcol))
|
---|
293 | tcol = squeeze(A(1,1,:));
|
---|
294 | end
|
---|
295 | end
|
---|
296 | % Crop the background
|
---|
297 | if options.crop
|
---|
298 | A = crop_background(A, tcol);
|
---|
299 | end
|
---|
300 | % Downscale the image
|
---|
301 | A = downsize(A, tcol, options.aa_factor);
|
---|
302 | if options.colourspace == 2
|
---|
303 | % Convert to greyscale
|
---|
304 | A = rgb2grey(A);
|
---|
305 | else
|
---|
306 | % Return only one channel for greyscale
|
---|
307 | A = check_greyscale(A);
|
---|
308 | end
|
---|
309 | % Outputs
|
---|
310 | if options.im
|
---|
311 | im = A;
|
---|
312 | end
|
---|
313 | if options.alpha
|
---|
314 | im = A;
|
---|
315 | alpha = zeros(size(A, 1), size(A, 2), 'single');
|
---|
316 | end
|
---|
317 | end
|
---|
318 | % Save the images
|
---|
319 | if options.png
|
---|
320 | res = options.magnify * get(0, 'ScreenPixelsPerInch') / 25.4e-3;
|
---|
321 | imwrite(A, [options.name '.png'], 'ResolutionUnit', 'meter', 'XResolution', res, 'YResolution', res);
|
---|
322 | end
|
---|
323 | if options.bmp
|
---|
324 | imwrite(A, [options.name '.bmp']);
|
---|
325 | end
|
---|
326 | % Save jpeg with given quality
|
---|
327 | if options.jpg
|
---|
328 | quality = options.quality;
|
---|
329 | if isempty(quality)
|
---|
330 | quality = 95;
|
---|
331 | end
|
---|
332 | if quality > 100
|
---|
333 | imwrite(A, [options.name '.jpg'], 'Mode', 'lossless');
|
---|
334 | else
|
---|
335 | imwrite(A, [options.name '.jpg'], 'Quality', quality);
|
---|
336 | end
|
---|
337 | end
|
---|
338 | % Save tif images in cmyk if wanted (and possible)
|
---|
339 | if options.tif
|
---|
340 | if options.colourspace == 1 && size(A, 3) == 3
|
---|
341 | A = double(255 - A);
|
---|
342 | K = min(A, [], 3);
|
---|
343 | K_ = 255 ./ max(255 - K, 1);
|
---|
344 | C = (A(:,:,1) - K) .* K_;
|
---|
345 | M = (A(:,:,2) - K) .* K_;
|
---|
346 | Y = (A(:,:,3) - K) .* K_;
|
---|
347 | A = uint8(cat(3, C, M, Y, K));
|
---|
348 | clear C M Y K K_
|
---|
349 | end
|
---|
350 | append_mode = {'overwrite', 'append'};
|
---|
351 | imwrite(A, [options.name '.tif'], 'Resolution', options.magnify*get(0, 'ScreenPixelsPerInch'), 'WriteMode', append_mode{options.append+1});
|
---|
352 | end
|
---|
353 | end
|
---|
354 | % Now do the vector formats
|
---|
355 | if isvector(options)
|
---|
356 | % Set the default renderer to painters
|
---|
357 | if ~options.renderer
|
---|
358 | renderer = '-painters';
|
---|
359 | end
|
---|
360 | % Generate some filenames
|
---|
361 | tmp_nam = [tempname '.eps'];
|
---|
362 | if options.pdf
|
---|
363 | pdf_nam = [options.name '.pdf'];
|
---|
364 | else
|
---|
365 | pdf_nam = [tempname '.pdf'];
|
---|
366 | end
|
---|
367 | % Generate the options for print
|
---|
368 | p2eArgs = {renderer};
|
---|
369 | if options.colourspace == 1
|
---|
370 | p2eArgs = [p2eArgs {'-cmyk'}];
|
---|
371 | end
|
---|
372 | if ~options.crop
|
---|
373 | p2eArgs = [p2eArgs {'-loose'}];
|
---|
374 | end
|
---|
375 | try
|
---|
376 | % Generate an eps
|
---|
377 | print2eps(tmp_nam, fig, p2eArgs{:});
|
---|
378 | % Generate a pdf
|
---|
379 | eps2pdf(tmp_nam, pdf_nam, 1, options.append, options.colourspace==2, options.quality);
|
---|
380 | catch
|
---|
381 | % Delete the eps
|
---|
382 | delete(tmp_nam);
|
---|
383 | rethrow(lasterror);
|
---|
384 | end
|
---|
385 | % Delete the eps
|
---|
386 | delete(tmp_nam);
|
---|
387 | if options.eps
|
---|
388 | try
|
---|
389 | % Generate an eps from the pdf
|
---|
390 | pdf2eps(pdf_nam, [options.name '.eps']);
|
---|
391 | catch
|
---|
392 | if ~options.pdf
|
---|
393 | % Delete the pdf
|
---|
394 | delete(pdf_nam);
|
---|
395 | end
|
---|
396 | rethrow(lasterror);
|
---|
397 | end
|
---|
398 | if ~options.pdf
|
---|
399 | % Delete the pdf
|
---|
400 | delete(pdf_nam);
|
---|
401 | end
|
---|
402 | end
|
---|
403 | end
|
---|
404 | if cls
|
---|
405 | % Close the created figure
|
---|
406 | close(fig);
|
---|
407 | else
|
---|
408 | % Reset the hardcopy mode
|
---|
409 | set(fig, 'InvertHardcopy', old_mode);
|
---|
410 | end
|
---|
411 | return
|
---|
412 |
|
---|
413 | function [fig options] = parse_args(nout, varargin)
|
---|
414 | % Parse the input arguments
|
---|
415 | % Set the defaults
|
---|
416 | fig = get(0, 'CurrentFigure');
|
---|
417 | options = struct('name', 'export_fig_out', ...
|
---|
418 | 'crop', true, ...
|
---|
419 | 'renderer', 0, ... % 0: default, 1: OpenGL, 2: ZBuffer, 3: Painters
|
---|
420 | 'pdf', false, ...
|
---|
421 | 'eps', false, ...
|
---|
422 | 'png', false, ...
|
---|
423 | 'tif', false, ...
|
---|
424 | 'jpg', false, ...
|
---|
425 | 'bmp', false, ...
|
---|
426 | 'colourspace', 0, ... % ): RGB/gray, 1: CMYK, 2: gray
|
---|
427 | 'append', false, ...
|
---|
428 | 'im', nout == 1, ...
|
---|
429 | 'alpha', nout == 2, ...
|
---|
430 | 'aa_factor', 4, ...
|
---|
431 | 'magnify', 1, ...
|
---|
432 | 'quality', []);
|
---|
433 | native = false; % Set resolution to native of an image
|
---|
434 |
|
---|
435 | % Go through the other arguments
|
---|
436 | for a = 1:nargin-1
|
---|
437 | if ishandle(varargin{a})
|
---|
438 | fig = varargin{a};
|
---|
439 | elseif ischar(varargin{a}) && ~isempty(varargin{a})
|
---|
440 | if varargin{a}(1) == '-'
|
---|
441 | switch lower(varargin{a}(2:end))
|
---|
442 | case 'nocrop'
|
---|
443 | options.crop = false;
|
---|
444 | case 'opengl'
|
---|
445 | options.renderer = 1;
|
---|
446 | case 'zbuffer'
|
---|
447 | options.renderer = 2;
|
---|
448 | case 'painters'
|
---|
449 | options.renderer = 3;
|
---|
450 | case 'pdf'
|
---|
451 | options.pdf = true;
|
---|
452 | case 'eps'
|
---|
453 | options.eps = true;
|
---|
454 | case 'png'
|
---|
455 | options.png = true;
|
---|
456 | case {'tif', 'tiff'}
|
---|
457 | options.tif = true;
|
---|
458 | case {'jpg', 'jpeg'}
|
---|
459 | options.jpg = true;
|
---|
460 | case 'bmp'
|
---|
461 | options.bmp = true;
|
---|
462 | case 'rgb'
|
---|
463 | options.colourspace = 0;
|
---|
464 | case 'cmyk'
|
---|
465 | options.colourspace = 1;
|
---|
466 | case {'gray', 'grey'}
|
---|
467 | options.colourspace = 2;
|
---|
468 | case {'a1', 'a2', 'a3', 'a4'}
|
---|
469 | options.aa_factor = str2double(varargin{a}(3));
|
---|
470 | case 'append'
|
---|
471 | options.append = true;
|
---|
472 | case 'native'
|
---|
473 | native = true;
|
---|
474 | otherwise
|
---|
475 | val = str2double(regexp(varargin{a}, '(?<=-(m|M|r|R|q|Q))(\d*\.)?\d+(e-?\d+)?', 'match'));
|
---|
476 | if ~isscalar(val)
|
---|
477 | error('option %s not recognised', varargin{a});
|
---|
478 | end
|
---|
479 | switch lower(varargin{a}(2))
|
---|
480 | case 'm'
|
---|
481 | options.magnify = val;
|
---|
482 | case 'r'
|
---|
483 | options.magnify = val ./ get(0, 'ScreenPixelsPerInch');
|
---|
484 | case 'q'
|
---|
485 | options.quality = max(val, 0);
|
---|
486 | end
|
---|
487 | end
|
---|
488 | else
|
---|
489 | name = varargin{a};
|
---|
490 | if numel(name) > 3 && name(end-3) == '.' && any(strcmpi(name(end-2:end), {'pdf', 'eps', 'png', 'tif', 'jpg', 'bmp'}))
|
---|
491 | options.(lower(name(end-2:end))) = true;
|
---|
492 | name = name(1:end-4);
|
---|
493 | end
|
---|
494 | options.name = name;
|
---|
495 | end
|
---|
496 | end
|
---|
497 | end
|
---|
498 |
|
---|
499 | % Check we have a figure handle
|
---|
500 | if isempty(fig)
|
---|
501 | error('No figure found');
|
---|
502 | end
|
---|
503 |
|
---|
504 | % Set the default format
|
---|
505 | if ~isvector(options) && ~isbitmap(options)
|
---|
506 | options.png = true;
|
---|
507 | end
|
---|
508 |
|
---|
509 | % If requested, set the resolution to the native vertical resolution of the
|
---|
510 | % first suitable image found
|
---|
511 | if native && isbitmap(options)
|
---|
512 | % Find a suitable image
|
---|
513 | list = findobj(fig, 'Type', 'image', 'Tag', 'export_fig_native');
|
---|
514 | if isempty(list)
|
---|
515 | list = findobj(fig, 'Type', 'image', 'Visible', 'on');
|
---|
516 | end
|
---|
517 | for hIm = list(:)'
|
---|
518 | % Check height is >= 2
|
---|
519 | height = size(get(hIm, 'CData'), 1);
|
---|
520 | if height < 2
|
---|
521 | continue
|
---|
522 | end
|
---|
523 | % Account for the image filling only part of the axes, or vice
|
---|
524 | % versa
|
---|
525 | yl = get(hIm, 'YData');
|
---|
526 | if isscalar(yl)
|
---|
527 | yl = [yl(1)-0.5 yl(1)+height+0.5];
|
---|
528 | else
|
---|
529 | if ~diff(yl)
|
---|
530 | continue
|
---|
531 | end
|
---|
532 | yl = yl + [-0.5 0.5] * (diff(yl) / (height - 1));
|
---|
533 | end
|
---|
534 | hAx = get(hIm, 'Parent');
|
---|
535 | yl2 = get(hAx, 'YLim');
|
---|
536 | % Find the pixel height of the axes
|
---|
537 | oldUnits = get(hAx, 'Units');
|
---|
538 | set(hAx, 'Units', 'pixels');
|
---|
539 | pos = get(hAx, 'Position');
|
---|
540 | set(hAx, 'Units', oldUnits);
|
---|
541 | if ~pos(4)
|
---|
542 | continue
|
---|
543 | end
|
---|
544 | % Found a suitable image
|
---|
545 | % Account for stretch-to-fill being disabled
|
---|
546 | pbar = get(hAx, 'PlotBoxAspectRatio');
|
---|
547 | pos = min(pos(4), pbar(2)*pos(3)/pbar(1));
|
---|
548 | % Set the magnification to give native resolution
|
---|
549 | options.magnify = (height * diff(yl2)) / (pos * diff(yl));
|
---|
550 | break
|
---|
551 | end
|
---|
552 | end
|
---|
553 | return
|
---|
554 |
|
---|
555 | function fh = isolate_subplot(ah, vis)
|
---|
556 | % Isolate the axes in a figure on their own
|
---|
557 | % Tag the axes so we can find them in the copy
|
---|
558 | old_tag = get(ah, 'Tag');
|
---|
559 | set(ah, 'Tag', 'AxesToCopy');
|
---|
560 | % Create a new figure exactly the same as the old one
|
---|
561 | fh = copyfig(ancestor(ah, 'figure')); %copyobj(ancestor(ah, 'figure'), 0);
|
---|
562 | if nargin < 2 || ~vis
|
---|
563 | set(fh, 'Visible', 'off');
|
---|
564 | end
|
---|
565 | % Reset the axes tag
|
---|
566 | set(ah, 'Tag', old_tag);
|
---|
567 | % Get all the axes
|
---|
568 | axs = findobj(fh, 'Type', 'axes');
|
---|
569 | % Find the axes to save
|
---|
570 | ah = findobj(axs, 'Tag', 'AxesToCopy');
|
---|
571 | if numel(ah) ~= 1
|
---|
572 | close(fh);
|
---|
573 | error('Too many axes found');
|
---|
574 | end
|
---|
575 | I = true(size(axs));
|
---|
576 | I(axs==ah) = false;
|
---|
577 | % Set the axes tag to what it should be
|
---|
578 | set(ah, 'Tag', old_tag);
|
---|
579 | % Keep any legends which overlap the subplot
|
---|
580 | ax_pos = get(ah, 'OuterPosition');
|
---|
581 | ax_pos(3:4) = ax_pos(3:4) + ax_pos(1:2);
|
---|
582 | for ah = findobj(axs, 'Tag', 'legend', '-or', 'Tag', 'Colorbar')'
|
---|
583 | leg_pos = get(ah, 'OuterPosition');
|
---|
584 | leg_pos(3:4) = leg_pos(3:4) + leg_pos(1:2);
|
---|
585 | % Overlap test
|
---|
586 | if leg_pos(1) < ax_pos(3) && leg_pos(2) < ax_pos(4) &&...
|
---|
587 | leg_pos(3) > ax_pos(1) && leg_pos(4) > ax_pos(2)
|
---|
588 | I(axs==ah) = false;
|
---|
589 | end
|
---|
590 | end
|
---|
591 | % Delete all axes except for the input axes and associated items
|
---|
592 | delete(axs(I));
|
---|
593 | return
|
---|
594 |
|
---|
595 | function fh = copyfig(fh)
|
---|
596 | % Is there a legend?
|
---|
597 | if isempty(findobj(fh, 'Type', 'axes', 'Tag', 'legend'))
|
---|
598 | % Safe to copy using copyobj
|
---|
599 | fh = copyobj(fh, 0);
|
---|
600 | else
|
---|
601 | % copyobj will change the figure, so save and then load it instead
|
---|
602 | tmp_nam = [tempname '.fig'];
|
---|
603 | hgsave(fh, tmp_nam);
|
---|
604 | fh = hgload(tmp_nam);
|
---|
605 | delete(tmp_nam);
|
---|
606 | end
|
---|
607 | return
|
---|
608 |
|
---|
609 | function A = downsize(A, padval, factor)
|
---|
610 | % Downsample an image
|
---|
611 | if factor == 1
|
---|
612 | % Nothing to do
|
---|
613 | return
|
---|
614 | end
|
---|
615 | try
|
---|
616 | % Faster, but requires image processing toolbox
|
---|
617 | A = imresize(A, 1/factor, 'bilinear');
|
---|
618 | catch
|
---|
619 | % No image processing toolbox - resize manually
|
---|
620 | % Lowpass filter - use Gaussian as is separable, so faster
|
---|
621 | switch factor
|
---|
622 | case 4
|
---|
623 | % sigma: 1.7
|
---|
624 | filt = single([0.0148395 0.0498173 0.118323 0.198829 0.236384 0.198829 0.118323 0.0498173 0.0148395]);
|
---|
625 | case 3
|
---|
626 | % sigma: 1.35
|
---|
627 | filt = single([0.025219 0.099418 0.226417 0.297892 0.226417 0.099418 0.025219]);
|
---|
628 | case 2
|
---|
629 | % sigma: 1.0
|
---|
630 | filt = single([0.054489 0.244201 0.40262 0.244201 0.054489]);
|
---|
631 | end
|
---|
632 | padding = floor(numel(filt) / 2);
|
---|
633 | if numel(padval) == 3 && padval(1) == padval(2) && padval(2) == padval(3)
|
---|
634 | padval = padval(1);
|
---|
635 | end
|
---|
636 | if numel(padval) == 1
|
---|
637 | B = repmat(single(padval), [size(A, 1) size(A, 2)] + (2 * padding));
|
---|
638 | end
|
---|
639 | for a = 1:size(A, 3)
|
---|
640 | if numel(padval) == 3
|
---|
641 | B = repmat(single(padval(a)), [size(A, 1) size(A, 2)] + (2 * padding));
|
---|
642 | end
|
---|
643 | B(padding+1:end-padding,padding+1:end-padding) = A(:,:,a);
|
---|
644 | A(:,:,a) = conv2(filt, filt', B, 'valid');
|
---|
645 | end
|
---|
646 | clear B
|
---|
647 | % Subsample
|
---|
648 | A = A(2:factor:end,2:factor:end,:);
|
---|
649 | end
|
---|
650 | return
|
---|
651 |
|
---|
652 | function A = rgb2grey(A)
|
---|
653 | A = cast(reshape(reshape(single(A), [], 3) * single([0.299; 0.587; 0.114]), size(A, 1), size(A, 2)), class(A));
|
---|
654 | return
|
---|
655 |
|
---|
656 | function A = check_greyscale(A)
|
---|
657 | % Check if the image is greyscale
|
---|
658 | if size(A, 3) == 3 && ...
|
---|
659 | all(reshape(A(:,:,1) == A(:,:,2), [], 1)) && ...
|
---|
660 | all(reshape(A(:,:,2) == A(:,:,3), [], 1))
|
---|
661 | A = A(:,:,1); % Save only one channel for 8-bit output
|
---|
662 | end
|
---|
663 | return
|
---|
664 |
|
---|
665 | function [A v] = crop_background(A, bcol)
|
---|
666 | % Map the foreground pixels
|
---|
667 | [h w c] = size(A);
|
---|
668 | if isscalar(bcol) && c > 1
|
---|
669 | bcol = bcol(ones(1, c));
|
---|
670 | end
|
---|
671 | bail = false;
|
---|
672 | for l = 1:w
|
---|
673 | for a = 1:c
|
---|
674 | if ~all(A(:,l,a) == bcol(a))
|
---|
675 | bail = true;
|
---|
676 | break;
|
---|
677 | end
|
---|
678 | end
|
---|
679 | if bail
|
---|
680 | break;
|
---|
681 | end
|
---|
682 | end
|
---|
683 | bail = false;
|
---|
684 | for r = w:-1:l
|
---|
685 | for a = 1:c
|
---|
686 | if ~all(A(:,r,a) == bcol(a))
|
---|
687 | bail = true;
|
---|
688 | break;
|
---|
689 | end
|
---|
690 | end
|
---|
691 | if bail
|
---|
692 | break;
|
---|
693 | end
|
---|
694 | end
|
---|
695 | bail = false;
|
---|
696 | for t = 1:h
|
---|
697 | for a = 1:c
|
---|
698 | if ~all(A(t,:,a) == bcol(a))
|
---|
699 | bail = true;
|
---|
700 | break;
|
---|
701 | end
|
---|
702 | end
|
---|
703 | if bail
|
---|
704 | break;
|
---|
705 | end
|
---|
706 | end
|
---|
707 | bail = false;
|
---|
708 | for b = h:-1:t
|
---|
709 | for a = 1:c
|
---|
710 | if ~all(A(b,:,a) == bcol(a))
|
---|
711 | bail = true;
|
---|
712 | break;
|
---|
713 | end
|
---|
714 | end
|
---|
715 | if bail
|
---|
716 | break;
|
---|
717 | end
|
---|
718 | end
|
---|
719 | % Crop the background
|
---|
720 | A = A(t:b,l:r,:);
|
---|
721 | v = [t b l r];
|
---|
722 | return
|
---|
723 |
|
---|
724 | function b = isvector(options)
|
---|
725 | b = options.pdf || options.eps;
|
---|
726 | return
|
---|
727 |
|
---|
728 | function b = isbitmap(options)
|
---|
729 | b = options.png || options.tif || options.jpg || options.bmp || options.im || options.alpha;
|
---|
730 | return
|
---|