Визуализируйте фигуру MATLAB в памяти

Существуют ли альтернативы использованию getframe и saveas для сохранения содержимого рисунка в растровое изображение для дальнейшей обработки?

Подход 1: getframe

h = figure('visible', 'off');
a = axes('parent', h);

% render using `scatter3()` or other plot function.

content = frame2im(getframe(h));

Это имеет серьезный недостаток, заключающийся в том, что на рисунке показано выполнение захвата экрана при вызове getframe(), и это проблематично при выполнении такого рендеринга в цикле (т. Е. Сохранении content на каждой итерации в виде видеокадра).

Подход 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');

Этот подход имеет серьезный недостаток записи на диск, что проблематично в многопоточных приложениях, а также медленнее, чем рендеринг непосредственно в память. Поскольку saveas(), очевидно, будет отображаться в памяти перед вызовом кодировщика PNG, то, что я хочу, возможно, но я не могу найти какую-либо функцию в документации MATLAB, которая выполняет только часть рендеринга.

Вопрос:

Знаете ли вы альтернативный способ рендеринга произвольного содержимого axes в растровое изображение?


person André Caron    schedule 09.11.2010    source источник
comment
У меня похожая проблема, мне также очень любопытно, почему люди MathWorks реализовали getframe() таким образом. Я думаю, что это полное безумие. Кроме того, зачем вам потоки (если вы не используете несколько процессов Matlab)? Можете ли вы даже создавать потоки с помощью Matlab?   -  person devin    schedule 16.09.2011
comment
Я использую инструментарий для параллельной обработки. У меня также были проблемы с изменением глобального состояния, потому что вычисления выполняются в отдельном потоке от пользовательского интерфейса. Например. открытие файлового браузера в MATLAB и переход в другой каталог изменяет текущий каталог процесса.   -  person André Caron    schedule 16.09.2011
comment
Люди из MathWorks исходят из того, что людям нужны простые интерфейсы, а не полный контроль. Иногда это упрощает выполнение операций в простом случае и делает его невозможным в сложном случае.   -  person André Caron    schedule 16.09.2011


Ответы (4)


Если вы создаете файл avi с помощью avifile, а затем добавляете в него кадры с помощью 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
person rescdsk    schedule 09.11.2010
comment
Извините за более ранний ответ, я не понял, откуда взялась дополнительная цифра. Получил это сейчас! - person rescdsk; 10.11.2010
comment
Вау, я не знал, что addframe приняты ручки для фигурок! Однако это не отвечает на мой вопрос, потому что он не преобразует фигуру в растровое изображение: мне нужно выполнить постобработку изображения перед выводом на видео. - person André Caron; 10.11.2010
comment
Ах, очень плохо. Я просматриваю исходный код addframe, и не похоже, чтобы промежуточные данные хранились полезным образом. В подфункции getFrameForFigure функции addframe похоже, что есть недокументированная функция hardcopy, которая может получить содержимое фигуры, но, конечно, она недокументирована + может измениться... - person rescdsk; 10.11.2010
comment
@rescdsk: Хотя это не совсем отвечает на мой первоначальный вопрос, это наиболее жизнеспособная альтернатива. Я переписал свой код, чтобы использовать subplot, и переупорядочил свой контент, чтобы я мог использовать предложенное вами решение. - person André Caron; 10.11.2010
comment
@rescdsk: я взглянул на getFrameForFigure() addframe(), и это точно делает то, что я хочу, поэтому я придерживаюсь такого подхода. Кроме того, на веб-сайте MathWorks есть ссылка на hardcopy: mathworks. com/support/solutions/archived/1-15KWU.html . Этого достаточно для меня. - person André Caron; 10.11.2010

Я понимаю, что это старая тема, но в последнее время я снова столкнулся с этой проблемой, поэтому я хотел обобщить свои выводы. Мой основной источник — эта страница (кэшировано). Согласно ему, есть три альтернативы:

  1. используя ADDFRAME непосредственно с дескриптором рисунка (без использования GETFRAME). Это именно то, что @rescdsk показал в своем ответе.

    hFig = figure('Visible','off');
    
    aviobj = avifile('file.avi');
    for k=1:N
        %#plot(...)
        aviobj = addframe(aviobj, hFig);
    end
    aviobj = close(aviobj);
    
  2. используя PRINT/СОХРАНИТЬКАК/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);
    
  3. используя недокументированную функцию 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, где есть новый флаг печати -RGBImage):

    getframe [M-file] --> capturescreen [builtin]
    

Примечание. Поскольку AVIFILE больше не рекомендуется, вы можете заменить его на новый VIDEOWRITER в (2) и (3), но не в (1), так как он не поддерживает передачу дескриптора фигуры напрямую.

person Amro    schedule 25.10.2012
comment
Спасибо за резюме. Обратите внимание, что подход № 3 уже был предложен в комментариях к ответу @rescdsk, и это то, что я в итоге использовал. - person André Caron; 27.10.2012
comment
Действительно интересное обсуждение, так как я столкнулся с той же проблемой и погрузился в коды print и getframe, чтобы увидеть, как получить реальные растровые данные, не обращаясь к файлу буфера обмена. Большое спасибо! - person CitizenInsane; 20.01.2013
comment
Очень мило, Амро. Я создаю avifile размером более 2 Гб, поэтому VideoWriter был для меня обязательным, а getframe сводил меня с ума, так как окно моей фигуры выходило за пределы экрана моего ноутбука. - person Stretch; 16.03.2014
comment
Вместо прямого вызова hardcopy может быть лучше вызвать getFrameForFigure( figHandle ). getFrameForFigure( figHandle ) — это подфункция внутри addframe, и, похоже, она настраивает передачу данных в hardcopy. getFrameForFigure( figHandle ) также недокументирован. Если вы хотите его использовать, вам придется открыть исходный код в addframe, скопировать его, вставить в новый m-файл и вставить m-файл в путь. - person Stretch; 16.03.2014
comment
@Amro, а если нам нужно вставить legend или title в данный кадр? В контексте videoWriter class - person Tin; 18.03.2014

Запустите MATLAB в автономном режиме: matlab -noFigureWindows

MATLAB работает в автономном режиме. Окна рисунков отображаться не будут.

затем просто начертите и сохраните цифры, как обычно (конечно, вы не увидите никакого графического вывода). Пример:

surf(peaks);
print output.eps     %# SAVEAS works as well
close

Я проверил это на компьютере с Windows под управлением R2010a. У меня сейчас нет доступа к машине Unix, но я ответил на похожий вопрос в прошлом, и в то время он работал нормально (вам нужно будет сбросить переменную $DISPLAY перед запуском MATLAB)


ИЗМЕНИТЬ

Другой вариант, если вы хотите сохранить обычное рабочее пространство, — запустить новый экземпляр MATLAB в фоновом режиме, который будет генерировать и сохранять графики (источник).

Запустите это из командной строки вашего текущего сеанса MATLAB (все в одной строке):

!start /B /MIN matlab -noFigureWindows 
                      -automation 
                      -r "cd('c:\yourpath'); myscript; quit"

Это запустит новую сессию MATLAB в фоновом режиме (с использованием COM-автоматизации) и выполнит скрипт с именем myscript (простой M-файл), который содержит весь ваш код построения:

c:\вашпуть\myscript.m

surf(peaks);
saveas(gcf, 'output.eps');
person Amro    schedule 09.11.2010
comment
Это круто, я не знал о безголовом режиме. Однако это не то, что я ищу, поскольку я обычно проверяю свои данные, используя другие цифры, прежде чем запускать задание вывода. - person André Caron; 10.11.2010
comment
Я думал, что это будет полезно для использования getframe(), но я не уверен, что это сработает. В документации говорится, что окно, которое будет захвачено getframe, существует на текущем активном рабочем столе. Я проведу тест, когда у меня будет еще несколько минут. - person André Caron; 10.11.2010
comment
вы правы насчет GETFRAME, он жалуется, что Figure window not valid during getframe если вы запустите его в вышеуказанных условиях. Тем не менее, ПЕЧАТЬ/СОХРАНИТЬ КАК работает нормально. - person Amro; 10.11.2010
comment
+1. Я никогда не думал запускать Matlab изнутри Matlab. Вероятно, вы могли бы использовать это как функцию параллельной обработки для бедных. - person Jonas; 10.11.2010
comment
@Amro: все ваше решение было основано на попытке заставить getframe() работать, не выталкивая фигуру, поэтому, если он не может этого сделать, то он на самом деле не решает мою проблему. Итак, теперь вместо дорогостоящего рендера, проходящего мимо моего жесткого диска, у меня проходит дорогостоящий рендеринг, проходящий мимо моего жесткого диска, и мне приходится запускать дополнительный процесс. - person André Caron; 10.11.2010
comment
Спасибо, что изучили это. Мне нравится метод мультиобработки бедняги :-) - person André Caron; 10.11.2010
comment
В Unix вам не нужно сбрасывать $DISPLAY. Просто добавьте в командную строку -nodisplay (mathworks.com/help/techdoc/ref/matlabunix .html) - person yuk; 10.11.2010

Поскольку 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);
person Uri Cohen    schedule 06.05.2015