在内存中渲染 MATLAB 图
Posted
技术标签:
【中文标题】在内存中渲染 MATLAB 图【英文标题】:Render MATLAB figure in memory 【发布时间】:2011-05-07 11:05:11 【问题描述】:除了使用getframe
和saveas
将图形的内容保存到光栅图像以供进一步处理之外,还有其他方法吗?
方法一:getframe
h = figure('visible', 'off');
a = axes('parent', h);
% render using `scatter3()` or other plot function.
content = frame2im(getframe(h));
这有一个严重的缺点,即在对getframe()
的调用中显示图形以执行屏幕捕获,并且在循环中执行此类渲染时会出现问题(即在每次迭代时将content
保存为视频帧) .
方法二: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');
这种方法具有写入磁盘的严重缺点,这在多线程应用程序中存在问题,并且比直接渲染到内存要慢。由于saveas()
显然会在调用 PNG 编码器之前渲染到内存中,所以我想要的是可能的,但我在 MATLAB 文档中找不到任何只执行渲染部分的函数。
问题:
您知道将任意axes
内容渲染为光栅图像的另一种方法吗?
【问题讨论】:
我也遇到了类似的问题,我也很好奇 MathWorks 的人为什么会这样实现 getframe()。我认为这完全是疯了。另外,您为什么要关心线程(除非您正在运行多个 matlab 进程)?你甚至可以用 matlab 生成线程吗? 我运行parallel processing toolbox。我也遇到了全局状态更改的问题,因为计算在 UI 的单独线程中运行。例如。在 MATLAB 中打开文件浏览器并导航到另一个目录会更改进程的当前目录。 MathWorks 人员的运作理念是人们想要简单的界面,而不是完全控制。有时,这会使操作“对于简单的情况很容易,而对于困难的情况则不可能”。 【参考方案1】:我意识到这是一个旧线程,但我最近又遇到了这个问题,所以我想总结一下我的发现。我的主要来源是this page (cached)。根据它,有三种选择:
直接使用ADDFRAME 和图形句柄(不使用GETFRAME)。这正是@rescdsk 在他的回答中所显示的。
hFig = figure('Visible','off');
aviobj = avifile('file.avi');
for k=1:N
%#plot(...)
aviobj = addframe(aviobj, hFig);
end
aviobj = close(aviobj);
使用PRINT/SAVEAS/HGEXPORT将图形导出到图像文件,然后从磁盘读取图像。这是您在上述问题中列出的方法#2。
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);
使用未记录的 HARDCOPY 函数捕获内存中的图形。
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);
事实上,这是其他函数直接或间接使用的底层函数。通过在可能的情况下检查源代码,这里是相关函数的依赖关系的说明,其中A --> B
表示A 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]
另一方面,GETFRAME 不调用 HARDCOPY,而是调用一个未记录的名为 CAPTURESCREEN 的内置函数(尽管它似乎将 PRINT 用于即将到来的HG2 system,其中有一个新的-RGBImage
打印标志):
getframe [M-file] --> capturescreen [builtin]
注意:由于 AVIFILE 现在已弃用,您可以在 (2) 和 (3) 中将其替换为较新的 VIDEOWRITER,但不能在 (1) 中替换,因为它不支持直接传递图形句柄。
【讨论】:
感谢您的总结。请注意,方法#3 已经在 cmets 中建议@rescdsk 的答案,这就是我最终使用的方法。 真正有趣的讨论,因为我遇到了同样的问题,并且正在研究print
和getframe
代码,以了解如何在不转到剪贴板文件的情况下获取真实的栅格数据。非常感谢!
非常好,阿姆罗。我正在创建一个大于 2Gb 的 avifile,所以 VideoWriter 对我来说是必须的,而 getframe 让我发疯了,因为我的图形窗口超出了我的笔记本电脑屏幕。
与其直接调用hardcopy
,不如直接调用getFrameForFigure( figHandle )
。 getFrameForFigure( figHandle )
是addframe
中的一个子函数,它似乎为输入hardcopy
进行了设置。 getFrameForFigure( figHandle )
也没有记录。如果你想使用它,你必须打开源代码到addframe
,复制它,将它粘贴到一个新的m文件中,并将m文件粘贴到你的路径上。
@Amro 如果我们需要在给定的帧中插入legend
或title
怎么办?在videoWriter class
的上下文中【参考方案2】:
如果您使用 avifile
创建一个 avi 文件,然后使用 addframe
向其中添加帧,MATLAB 不会像使用 getframe
那样打开额外的可见图形。
avi = avifile('/path/to/output');
figure_handle = figure('visible', 'off');
% ...
for something = 1:1000
cla
% (draw stuff...)
avi = addframe(avi, figure_handle);
end
【讨论】:
抱歉之前的回答,我不明白多余的数字是从哪里来的。现在明白了! 哇,我不知道addframe
接受了图形处理!但是,这并不能回答我的问题,因为它不会将图形渲染为光栅图像:我需要在输出到视频之前对图像进行后处理。
啊,太糟糕了。我正在查看addframe
的源代码,并且似乎没有以有用的方式存储中间数据。在addframe
的getFrameForFigure
子函数中,看起来有一个名为hardcopy
的未记录函数可以检索图形的内容,但当然它是未记录的+可能会更改...
@rescdsk:虽然这不能完全回答我最初的问题,但它是最可行的选择。我重写了我的代码以使用 subplot
并重新安排我的内容,以便我可以使用您提出的解决方案。
@rescdsk:我查看了addframe()
的getFrameForFigure()
,这正是我想要的,所以我将采用这种方法.此外,在 MathWorks 的网站上有对 hardcopy
的引用:mathworks.com/support/solutions/archived/1-15KWU.html。这对我来说已经足够了。【参考方案3】:
以无头模式启动 MATLAB:matlab -noFigureWindows
MATLAB 正在无头模式下运行。 图形窗口将不会显示。
然后像往常一样简单地绘制并保存数字(当然,您不会看到任何图形输出)。示例:
surf(peaks);
print output.eps %# SAVEAS works as well
close
我在运行 R2010a 的 Windows 机器上测试了上述内容。我现在无法访问 Unix 机器,但我过去回答了 similar question,当时它工作得很好(您需要在启动 MATLAB 之前取消设置 $DISPLAY
变量)
编辑
如果您想保留正常的工作区,另一种选择是在后台启动一个新的 MATLAB 实例,该实例将生成并保存绘图 (source)。
在当前 MATLAB 会话的命令提示符下运行此命令(都在同一行):
!start /B /MIN matlab -noFigureWindows
-automation
-r "cd('c:\yourpath'); myscript; quit"
这将在后台启动一个新的 MATLAB 会话(使用 COM 自动化),并执行一个名为 myscript
的脚本(一个简单的 M 文件),其中包含您的所有绘图代码:
c:\yourpath\myscript.m
surf(peaks);
saveas(gcf, 'output.eps');
【讨论】:
这很酷,我不知道无头模式。但是,这不是我想要的,因为我通常在运行输出作业之前使用其他数字检查我的数据。 我认为这对于使用getframe()
会很有用,但我不确定它是否会起作用。文档说“确保 getframe 要捕获的窗口存在于当前活动的桌面上”。等我有几分钟的时间,我会进行测试。
你是对的 GETFRAME,如果你在上述条件下运行它,它会抱怨Figure window not valid during getframe
。不过,PRINT/SAVEAS 工作得很好。
+1。我从没想过从 Matlab 中启动 Matlab。您可能可以将其用作穷人的并行处理功能。
@Amro:你的整个解决方案是基于试图让getframe()
在不弹出数字的情况下工作,所以如果它不能做到这一点,那么它并不能真正解决我的问题。所以现在,不是通过我的硬盘驱动器进行昂贵的渲染,而是通过我的硬盘驱动器进行昂贵的渲染,我必须启动一个额外的进程。【参考方案4】:
avifile
已被弃用,这就是您使用 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);
【讨论】:
以上是关于在内存中渲染 MATLAB 图的主要内容,如果未能解决你的问题,请参考以下文章
如何在 MATLAB 中将 .mat 文件内容保存在内存中?