在内存中渲染 MATLAB 图

Posted

技术标签:

【中文标题】在内存中渲染 MATLAB 图【英文标题】:Render MATLAB figure in memory 【发布时间】:2011-05-07 11:05:11 【问题描述】:

除了使用getframesaveas 将图形的内容保存到光栅图像以供进一步处理之外,还有其他方法吗?

方法一: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 的答案,这就是我最终使用的方法。 真正有趣的讨论,因为我遇到了同样的问题,并且正在研究printgetframe 代码,以了解如何在不转到剪贴板文件的情况下获取真实的栅格数据。非常感谢! 非常好,阿姆罗。我正在创建一个大于 2Gb 的 avifile,所以 VideoWriter 对我来说是必须的,而 getframe 让我发疯了,因为我的图形窗口超出了我的笔记本电脑屏幕。 与其直接调用hardcopy,不如直接调用getFrameForFigure( figHandle )getFrameForFigure( figHandle )addframe 中的一个子函数,它似乎为输入hardcopy 进行了设置。 getFrameForFigure( figHandle ) 也没有记录。如果你想使用它,你必须打开源代码到addframe,复制它,将它粘贴到一个新的m文件中,并将m文件粘贴到你的路径上。 @Amro 如果我们需要在给定的帧中插入legendtitle 怎么办?在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 的源代码,并且似乎没有以有用的方式存储中间数据。在addframegetFrameForFigure 子函数中,看起来有一个名为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和C中的内存管理器问题[重复]

如何查询/删除matlab 内存中的变量?

matlab中的内存监控

如何在 MATLAB 中将 .mat 文件内容保存在内存中?

使用 opengl 硬件渲染器导出带有颜色条的 MATLAB 冲浪图会导致损坏的 png 文件

使用 QtWebKit 在内存中渲染多个网页会在第二页中断,为啥?