加速动画

3
我正在尝试提高MATLAB图形中动画的绘制和更新速率。我希望通过使用gpuarrayparfor循环来更新图形上的许多变量,但显然这不是情况,当这些函数解析时,图形的句柄会失去连接。是否可能有另一种方法来改进和加速动画?我将附上我的代码并简要说明。我还将附上两个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

我尝试将“hgtransform”从中删除,但看起来这不是主要因素——我只能稍微加快动画速度。看起来绘制补丁很慢。我唯一看到的解决方法是预先记录动画(然后渲染时间无关紧要),或者用标记("线")而不是补丁来进行动画处理。我已经用成千上万个点动画了单个“线”对象,速度非常快。 - Cris Luengo
我曾考虑先构建渲染,再显示动画,但问题是,这段代码的扩展版本中有物体在场地上随机移动。这将导致长时间的构建时间,特别是对于某些超过5分钟的动画。 - Hojo.Timberwolf
1个回答

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的局限性,您可能需要考虑一些专门的图形库,但我不是这方面的专家,所以不能提供具体建议。
请注意我如何更新坐标而不是使用您的for循环:
[tmp.XData] = NextStepX{:};

谢谢,您提供的解释和代码极大地加快了动画速度。需要注意的是,我使用补丁的主要原因是最终将允许不同形状的对象出现在屏幕上。正方形、圆形和三角形将同时出现在场地上。 - Hojo.Timberwolf
1
太好了,我很高兴它能帮到你! - Dev-iL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接