Render MATLAB figure in memory
Solution 1
If you create an avi file with avifile
, and then add frames to it with addframe
, MATLAB doesn't open up extra visible figures like it does with getframe
.
avi = avifile('/path/to/output');
figure_handle = figure('visible', 'off');
% ...
for something = 1:1000
cla
% (draw stuff...)
avi = addframe(avi, figure_handle);
end
Solution 2
I realize this is an old thread, but I ran into this problem again lately, so I wanted to summarize my findings. My main source is this page (cached). According to it, there are three alternatives:
-
using ADDFRAME directly with the figure handle (without using GETFRAME). This is exactly what @rescdsk has shown in his answer.
hFig = figure('Visible','off'); aviobj = avifile('file.avi'); for k=1:N %#plot(...) aviobj = addframe(aviobj, hFig); end aviobj = close(aviobj);
-
using PRINT/SAVEAS/HGEXPORT to export the figure to an image file, then reading the image back from disk. This is approach#2 that you listed yourself in the question above.
hFig = figure('Visible','off'); set(hFig, 'PaperPositionMode','auto', 'InvertHardCopy','off') aviobj = avifile('file.avi'); for k=1:N %#plot(...) print(['-f' num2str(hFig)], '-zbuffer', '-r0', '-dpng', 'file.png') img = imread('file.png'); aviobj = addframe(aviobj, im2frame(img)); end aviobj = close(aviobj);
-
using the undocumented HARDCOPY function to capture the figure in-memory.
hFig = figure('Visible','off'); set(hFig, 'PaperPositionMode','auto') aviobj = avifile('file.avi'); for k=1:N %#plot(...) img = hardcopy(hFig, '-dzbuffer', '-r0'); aviobj = addframe(aviobj, im2frame(img)); end aviobj = close(aviobj);
In fact, this is the underlying function that other functions use either directly or indirectly. By inspecting the source codes where possible, here is an illustration of the dependencies of the related functions where
A --> B
denotesA calls B
:saveas [M-file] --> print [M-file] --> render [private M-file] --> hardcopy [P-file] hgexport [P-file] --> print [M-file] --> ... @avifile/addframe [M-file] --> hardcopy [P-file]
On the other hand, GETFRAME does not call HARDCOPY but an undocumented built-in function named CAPTURESCREEN (although it seems that it will be using PRINT for the upcoming HG2 system where there is a new
-RGBImage
print flag):getframe [M-file] --> capturescreen [builtin]
Note: Since AVIFILE is now deprecated, you can replace it with the newer VIDEOWRITER in (2) and (3), but not in (1) since it does not support passing figure handle directly.
Solution 3
Start MATLAB in headless mode: matlab -noFigureWindows
MATLAB is running in headless mode. Figure windows will not be displayed.
then simply plot and save the figures as usual (you won't see any graphical output of course). Example:
surf(peaks);
print output.eps %# SAVEAS works as well
close
I tested the above on a Windows machine running R2010a. I don't have access to a Unix machine right now, but I answered a similar question in the past, and it worked just fine at the time (you will need to unset the $DISPLAY
variable before starting MATLAB)
EDIT
Another option, in case you want to keep your normal workspace, is to start a new MATLAB instance in the background which will generate and save the plots (source).
Run this from the command prompt of your current MATLAB session (all on the same line):
!start /B /MIN matlab -noFigureWindows
-automation
-r "cd('c:\yourpath'); myscript; quit"
This will start a new MATLAB session in the background (using COM Automation), and execute a script called myscript
(a simple M-file) that contains all your plotting code:
c:\yourpath\myscript.m
surf(peaks);
saveas(gcf, 'output.eps');
Solution 4
With avifile
being deprecated, this is how you do it with VideoWriter:
hFig = figure('Visible','off');
set(hFig, 'PaperPositionMode','auto')
aviobj = VideoWriter('file','Archival');
for k=1:N
%#plot(...)
img = hardcopy(hFig, '-dzbuffer', '-r0');
writeVideo(aviobj, im2frame(img));
end
close(aviobj);
Related videos on Youtube
André Caron
You can find some of my M.Sc. projects and more of my personal projects online. Some of my better posts on StackOverflow: In C++, why should new be used as little as possible? Not using getters and setters Why and when shouldn't I kill a thread? How to properly use namespaces to avoid name collision? Using a Class Variable vs Sending Local Variable to Functions/Methods
Updated on May 08, 2022Comments
-
André Caron about 2 years
Are there any alternatives to using
getframe
andsaveas
for saving the contents of a figure to a raster image for further processing?Approach 1:
getframe
h = figure('visible', 'off'); a = axes('parent', h); % render using `scatter3()` or other plot function. content = frame2im(getframe(h));
This has the serious drawback of showing the figure to perform a screen capture in the call to
getframe()
and it is problematic when performing such a render in a loop (i.e. savingcontent
at each iteration as a video frame).Approach 2:
saveas
h = figure('visible', 'off'); a = axes('parent', h); % render using `scatter3()` or other plot function. saveas(h, '/path/to/file.png'); content = imread(/path/to/file.png');
This approach has the serious drawback of writing to disk, which is problematic in multithreaded applications, as well as being slower than rendering directly to memory. Since
saveas()
will obviously render to memory before invoking the PNG encoder, what I want is possible, but I can't find any function it in the MATLAB documentation that only performs the rendering part.Question:
Does you know of an alternate way of rendering an arbitrary
axes
content to a raster image?-
devin almost 13 yearsI'm having a similar problem, I'm also very curious why the MathWorks people implemented getframe() this way. I think it's completely insane. Also, why do you care about threads (unless you're running multiple matlab processes)? Can you even spawn threads with matlab?
-
André Caron almost 13 yearsI run the parallel processing toolbox. I've also had problems with global state changing because computation runs in a seperate thread from the UI. For example. opening the file browser in MATLAB and navigating to another directory changes the process' current directory.
-
André Caron almost 13 yearsThe MathWorks people operate behind the idea that people want simple interfaces, not full control. Sometimes, this makes operations "easy for the simple case and impossible for the hard case".
-
-
rescdsk over 13 yearsSorry about the earlier answer, I didn't understand where the extra figure was coming from. Got it now!
-
André Caron over 13 yearsThis is cool, I didn't know about the headless mode. However, this is not what I'm looking for as I usually inspect my data using other figures before running the output job.
-
André Caron over 13 yearsWow, I didn't know
addframe
accepted figure handles! However, this is does not answer my question, because it does not render a figure to a raster image: I need to post-process the image before outputting to video. -
André Caron over 13 yearsI thought this would be useful for using
getframe()
but I'm not sure it will work. The documentation says "make sure that the window to be captured by getframe exists on the currently active desktop". I'll run a test when I have a few more minutes. -
Amro over 13 yearsyou're right about GETFRAME, it complains that
Figure window not valid during getframe
if you run it under the above conditions. Still, PRINT/SAVEAS work just fine. -
Jonas over 13 years+1. I never thought of starting Matlab from within Matlab. You could probably use this as a poor man's parallel processing function.
-
André Caron over 13 years@Amro: you whole solution was based on trying to get
getframe()
to work without popping the figure, so if it can't do that, then it doesn't really solve my problem. So now, instead of an expensive render passing by my hard drive, I have an expensive render passing by my hard drive and I have to launch an extra process. -
André Caron over 13 yearsThanks for looking into this, though. I like the poor man's multi-processing method :-)
-
yuk over 13 yearsUnder unix you don't have to unset $DISPLAY. Just add to command line
-nodisplay
(mathworks.com/help/techdoc/ref/matlabunix.html) -
rescdsk over 13 yearsAh, too bad. I'm looking at the source for
addframe
, and it doesn't seem that the intermediate data is stored in a useful way. In thegetFrameForFigure
subfunction ofaddframe
, it looks like there's an undocumented function calledhardcopy
that can retrieve the contents of a figure, but of course it's undocumented + might change... -
André Caron over 13 years@rescdsk: Although this doesn't quite answer my original question, it's the most viable alternative. I've re-written my code to use
subplot
and re-arrange my content so that I can use your proposed solution. -
André Caron over 13 years@rescdsk: I've taken a look at
addframe()
'sgetFrameForFigure()
, and that does exactly what I want, so I'm going with that approach. Also, there is a reference tohardcopy
on the MathWorks' website: mathworks.com/support/solutions/archived/1-15KWU.html . That's good enough for me. -
André Caron over 11 yearsThanks for you summary. Note that approach #3 was already suggested in the comments to @rescdsk's answer and that's what I ended up using.
-
CitizenInsane over 11 yearsTruly interesting discussion as I fell in same issue and was diving in
print
andgetframe
codes to see how to get real raster data without going to file of clipboard. Thanks a lot! -
Stretch over 10 yearsVery nice, Amro. I am creating a larger than 2Gb avifile, so VideoWriter was a must for me, and getframe was driving me nuts since my figure window extended beyond my laptop screen.
-
Stretch over 10 yearsInstead of calling
hardcopy
directly, it may be better to callgetFrameForFigure( figHandle )
.getFrameForFigure( figHandle )
is a sub-function insideaddframe
and it seems to set things up for feeding intohardcopy
.getFrameForFigure( figHandle )
is also undocumented. If you want to use it, you'll have to open the source code toaddframe
, copy it, paste it into a new m-file, and stick the m-file on your path. -
Tin over 10 years@Amro what about if we need to insert a
legend
or atitle
to a given frame? In the context of thevideoWriter class