加快动画制作速度

时间:2018-08-21 04:24:33

标签: matlab performance animation graphics matlab-figure

我正在尝试提高在MATLAB图形中绘制和更新动画的速率。我希望通过使用gpuarrayparfor循环,可以更新图形上的许多变量,但是显然不是这样,图形的句柄在被这些函数解析后会失​​去连接。是否存在另一种改善和加速动画的方法。我将附加我的代码并简要说明。我还将附上2个GIF,以显示我要做什么。

这是代码。我将动画作为对象编写,因为我将插入各种程序。我定义了类,使用第一个函数构建对象,然后使用第二个函数运行刺激。为了降低速度,我将所有转换和XY数据复制到单独的容器中,以便可以对位置数据执行cellfun或其他计算强度大的计算,然后覆盖对象的位置数据。

classdef StimBud<handle
properties
    Window                                                              % holds figure
    ObjectList                                                          % holds object handles
    ObjectTransformation                                                % holds object transformation
    ObjectLocX
    ObjectLocY
    ObjectRotation
end
methods
    function gh = StimBud()  
    end
    function buildobjects(gh,numofObj)
        gh.Window = figure('MenuBar','none', ...                        % Build Window for stimulus
            'Color',[0,0,0]);

        for aa = 1:numofObj
        gh.ObjectTransformation{aa} = hgtransform;                   % Add object to end of transformation list
        gh.ObjectList{aa} = patch(...                                   % Add object to end of Object list, bind to transformation list
                'Parent', gh.ObjectTransformation{aa}, ...   
                'XData',[0,0,1,1], 'YData',[0,1,1,0],...
                'Facecolor', [1,1,0], 'EdgeColor', [1,1,0], ...
                'visible','on');
        end

        % define transforamtion and position variables
        parse = 360/numofObj;
        rotationcalculation = 0;
        XData = repmat(10,1,numofObj);
        YData = zeros(1,numofObj); 

        TmpXdata = cell(numofObj,1);                                    % container to hold all X location data
        TmpRot = zeros(numofObj,1);                                     % container to hold the Rotation data
        TmpYdata = cell(numofObj,1);                                    % container to hold all Y location data

        % Adjust position & rotation of all available objects

        for aa = 1:numofObj
            % Rotate objects to change direction of movement
            gh.ObjectTransformation{aa}.Matrix = makehgtform('zrotate',(deg2rad(aa*parse)+rotationcalculation));

            % move object to proper position
            gh.ObjectList{aa}.XData = gh.ObjectList{aa}.XData + XData(aa);
            gh.ObjectList{aa}.YData = gh.ObjectList{aa}.YData + YData(aa);

            TmpXdata{aa} = gh.ObjectList{aa}.XData;
            TmpYdata{aa} = gh.ObjectList{aa}.YData;
            TmpRot(aa) = (deg2rad(aa*parse)+rotationcalculation);
        end

        % store variable out of objects for threading
        gh.ObjectLocX = TmpXdata;
        gh.ObjectLocY = TmpYdata;
        gh.ObjectRotation = TmpRot;
    end
    function RunStim(gh)
        figure(gh.Window);
        TrialLength = 10;                                               % Length of trial to be run
        Framerate = 60; 
        ObjSpeed = 10;

        % pull variables into function
        ObjList = gh.ObjectList;
        ObjLocX = gh.ObjectLocX;
        ObjLocY = gh.ObjectLocY;
        ObjRotation = gh.ObjectRotation;

        NumofObj = length(ObjList);                                     % Number of Objects in stim system

        timer = tic();                                                  % Timer for the stimulus

        moveforward = .03*.1*ObjSpeed;                                  % Distance to move in figure

        while toc(timer) < TrialLength                                  % Run stimulus through length of project
            NextStepX = cellfun(@(x) x+moveforward,ObjLocX);
            NextStepY = cellfun(@(x) x+moveforward,ObjLocY);
            NextRot = ObjRotation + moveforward;

            for aa = 1:NumofObj  %% parfor does not work here %%
                    ObjList{aa}.XData = NextStepX{aa};
            end

            ObjLocX = NextStepX;                                        % Update X location matrix for next step
            ObjLocY = NextStepY;                                        % Update Y location matrix for next step
            ObjRotation = NextRot;                                      % Update Rotation matrix for next step

            pause(1/Framerate)                                          % Pause window briefly to allow for drawing
        end
    end
end
end

要创建动画:

s = StimBud;
s.buildobjects(5)
s.RunStim

此GIF显示了我使用上面的代码构建的动画,该代码有5个对象。即使我使用for循环,它也可以快速显示和设置动画。

enter image description here

这是当我增加对象数量时发生的事情(由于for循环而显而易见)。动画的速度大大降低,不像平滑的运动那样快,并且移动的距离与具有较少对象的动画的移动距离不同。

enter image description here

我希望使用多线程来纠正此问题,但这似乎不是一个可行的选择(至少从我所学到的)。如何在MATLAB图形中改善动画效果?我是在错误地考虑这个问题,根本不应该使用figure吗?

1 个答案:

答案 0 :(得分:3)

您的方法的主要问题是您的代码忽略了更新工程图需要时间的事实。因此,您设置的帧速率不是实际的帧速率,我将使用profiler进行解释:

Profiler output for n = 100

我们在这里看到标记的线,它既负责绘图又负责等待,花了8.57秒而不是350*1/60 = 5.83秒,增加了近50% !好的,可能暗示我们想要的帧率可能太高。我们可以尝试对较小的帧率进行硬编码-但这只会推迟问题,并不能真正解决问题。

这就是为什么我认为最好的方法是自适应帧速率,它会根据当前的工作量增加或减少。这是一个粗略的实现,以显示我的意思:

% same as yours until line 72 including

    tmp = [ObjList{:}];
    skipNext = 0;
    while true                                          % Run stimulus through length of project
        t1 = toc(timer);
        if t1 >= TrialLength, break; end
        NextStepX = cellfun(@(x) x+moveforward, ObjLocX, 'UniformOutput', false);
        NextStepY = cellfun(@(x) x+moveforward, ObjLocY, 'UniformOutput', false);
        NextRot = ObjRotation + moveforward;

        if ~skipNext
          [tmp.XData] = NextStepX{:};
        end

        ObjLocX = NextStepX;                            % Update X location matrix for next step
        ObjLocY = NextStepY;                            % Update Y location matrix for next step
        ObjRotation = NextRot;                          % Update Rotation matrix for next step
        t2 = toc(timer);
        if t2-t1 < max(1,skipNext)/Framerate
          % if we have some time left, update the plot
          drawnow;
          skipNext = 0;
          % if we still have time left, increase the framerate and wait
          t = 1/Framerate - (toc(timer) - t1);
          if t > 0
            Framerate = Framerate * 1.1;
            java.lang.Thread.sleep( 1E3 * t );
          end
        else
          skipNext = skipNext + 1;
          Framerate = Framerate * 0.75;
          disp("Frame skipped!");
        end
        disp("Framerate is now: " + Framerate);
    end

这还包括一些日志记录,以显示帧率发生了什么。在我的系统上,对于n=10,帧速率最终为227.85,对于n=50,帧速率最终为72.6,对于n=1000,帧速率最终为18.98,依此类推。在最后一种情况下动画不流畅,至少看起来没有卡住。

展望未来,我认为您应该尝试用其他实体替换补丁,也许是使用一些大的正方形标记(可能会模仿正方形补丁,但速度更快)的linescatter图)。如果这些都不适合您的使用,我只想说这是MATLAB的局限性,尽管我不是这些方面的专家,但您可能想研究一些专用的图形库,因此很遗憾,我不能提出一个建议。 / p>

请注意,我如何进行坐标更新,而不是您的for循环:

[tmp.XData] = NextStepX{:};
相关问题