如何在 MATLAB 中平滑旋转 3D 绘图?

发布于 2024-10-06 02:30:32 字数 1095 浏览 3 评论 0原文

如果我尝试使用 plot3 围绕当前图形旋转相机

while true; camorbit(0.9,-0.1); drawnow; end

,则旋转会定期挂起一段时间 (示例),甚至在 8 核 MacPro 上也是如此。

我可以让它顺利吗?

EDIT1:

虽然我原来的问题还没有解决方案,但我已经成功地使用 getframe 函数制作了一部更好的电影。但它不允许记录徒手旋转,并且在 MATLAB2010b for Mac 中存在很多错误。

%# fix wrong figure position in MATLAB2010b for Mac - depends on your layout
correctedPosition = get(gcf,'Position') + [21 -125 0 0];

fps = 60; sec = 10;

vidObj = VideoWriter('newfile.avi');
vidObj.Quality = 100;
vidObj.FrameRate = fps;

open(vidObj);
for i=1:fps*sec
  camorbit(0.9,-0.1);
  writeVideo(vidObj,getframe(gcf, correctedPosition));
end
close(vidObj);

EDIT2:

我在 MATLAB Central 创建了一个类似的线程。

EDIT3:

您可以自己尝试下载我的一张图

If I try to rotate camera around my current figure with plot3 using

while true; camorbit(0.9,-0.1); drawnow; end

then the rotation periodically hangs for a while (example) even on 8-core MacPro.

Can I make it smooth?

EDIT1:

While there is no solution for my original question yet, I've managed to make a better movie with getframe function. It doesn't allow recording free-hand rotation, though, and is quite buggy in MATLAB2010b for Mac.

%# fix wrong figure position in MATLAB2010b for Mac - depends on your layout
correctedPosition = get(gcf,'Position') + [21 -125 0 0];

fps = 60; sec = 10;

vidObj = VideoWriter('newfile.avi');
vidObj.Quality = 100;
vidObj.FrameRate = fps;

open(vidObj);
for i=1:fps*sec
  camorbit(0.9,-0.1);
  writeVideo(vidObj,getframe(gcf, correctedPosition));
end
close(vidObj);

EDIT2:

I created a similar thread at MATLAB Central.

EDIT3:

You can try it yourself downloading one of my figures.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

万劫不复 2024-10-13 02:30:32

我想说的是您绘制的大量点导致了速度减慢。一种选择是下采样。您也可以使用较低级别的函数来绘制(检查 这篇相关文章用于比较plot3/scatter3/line性能)。

考虑下面针对速度进行优化的动画:

[X Y Z] = sphere(64);
X = X(:); Y = Y(:); Z = Z(:);

%# set-up figure
hFig = figure('Backingstore','off', 'renderer','zbuffer');

%# use lower-level function LINE
line(0.50*[X,X], 0.50*[Y,Y], 0.50*[Z,Z], 'LineStyle','none', 'Marker','.', 'MarkerSize',1, 'Color','r')
line(0.75*[X,X], 0.75*[Y,Y], 0.75*[Z,Z], 'LineStyle','none', 'Marker','.', 'MarkerSize',1, 'Color','g')
line(1.00*[X,X], 1.00*[Y,Y], 1.00*[Z,Z], 'LineStyle','none', 'Marker','.', 'MarkerSize',1, 'Color','b')
view(3)

%# freeze the aspect ratio to override stretch-to-fill behaviour
axis vis3d

%# fix the axes limits manually
%#set(gca, 'xlim',[-1 1], 'ylim',[-1 1], 'zlim',[-1 1])
axis manual

%# maybe even remove the tick labels
%set(gca, 'xticklabel',[], 'yticklabel',[], 'zticklabel',[])

%# animate (until figure is closed)
while ishandle(hFig); camorbit(0.9,-0.1); drawnow; end

alt text

注意我们如何使用 Z 缓冲区 渲染器,并关闭 后备存储 属性。


编辑:

如果我理解正确,您想要做的是录制截屏视频(使用第 3 方应用程序),同时手动旋转图形,但在您的情况下,这些手动旋转是“跳跃的”。另一方面,在 while 循环中使用 CAMORBIT/VIEW 为您的图形设置动画运行平稳...

我提出了一种替代解决方案:首先使用鼠标旋转图形,然后在每一步(方位角、仰角)写入这些视图配置。然后,您可以在录制视频时使用 VIEW 功能重播它们,例如:

v = [...];   %# matrix where each row specify Az/El of view
for i=1:size(v,1)
    view( v(i,:) )
    drawnow
end

缺点是您必须使用鼠标小步按下/旋转/释放(ROTATE3D 对象不会公开鼠标运动事件)

I编写了一个简单的函数来帮助您完成此过程。它加载保存的图形,启用 3d 旋转,并跟踪每一步的中间位置。完成后,按“完成”按钮返回视图列表...

function v = rotationDemo(figFileName)
    views = [];                     %# list of views (Az,El)

    hFig = hgload(figFileName);     %# load the saved figure

    views(1,:) = get(gca,'View');   %# store initial view

    %# add a button, used to terminate the process
    hButton = uicontrol('Style','pushbutton', 'Position',[400 1 80 20], ...
                        'String','Done?', 'Callback',@buttonCallback);
    set(hFig, 'Toolbar','figure')   %# restore toolbar

    %# start 3d rotation, and handle post-callback to record intermediate views
    h = rotate3d(hFig);             %# get rotation object
    set(h, 'ActionPostCallback',@rotateCallback)
    set(h, 'Enable','on')           %# enable rotation

    msgbox('Rotate the view step-by-step', 'rotate3d', 'warn', 'modal')

    uiwait(hFig)                    %# wait for user to click button
    delete(hButton)                 %# delete button on finish
    set(h, 'Enable','off')          %# disable rotation
    v = round(views);               %# return the list of views

    %# callback functions
    function rotateCallback(o,e)
        views(end+1,:) = get(e.Axes,'View');  %# add current view to list
    end
    function buttonCallback(o,e)
        uiresume(gcbf)                        %# uiresume(hFig)
    end
end

alt text

您可以调用上述函数,然后重播动画:

v = rotationDemo('smooth_rotation.fig');
for i=1:size(v,1)
    view(v(i,:))
    drawnow
end

我们可以通过简单的插值来平滑过渡:

v = rotationDemo('smooth_rotation.fig');
n = size(v,1);
nn = linspace(1,n,100)';     %'# use 100 steps
vv = round( [interp1(v(:,1),nn) interp1(v(:,2),nn)] );
for i=1:size(vv,1)
    view(vv(i,:))
    DRAWNOW                  %# or PAUSE(..) to slow it down
end

作为旁注,我应该提到 ROTATE3D 和 CAMORBIT 具有不同的效果。 ROTATE3D 改变当前轴的 View 属性,而 CAMORBIT 控制当前轴的相机属性 CameraTarget/CameraPosition/CameraUpVector当前轴。

I would say it's the large number of points you are drawing that's causing the slowdown. One option is to downsample.. Also you could use lower-level functions to draw (check this related post for a comparison of plot3/scatter3/line performance).

Consider the animation below optimized for speed:

[X Y Z] = sphere(64);
X = X(:); Y = Y(:); Z = Z(:);

%# set-up figure
hFig = figure('Backingstore','off', 'renderer','zbuffer');

%# use lower-level function LINE
line(0.50*[X,X], 0.50*[Y,Y], 0.50*[Z,Z], 'LineStyle','none', 'Marker','.', 'MarkerSize',1, 'Color','r')
line(0.75*[X,X], 0.75*[Y,Y], 0.75*[Z,Z], 'LineStyle','none', 'Marker','.', 'MarkerSize',1, 'Color','g')
line(1.00*[X,X], 1.00*[Y,Y], 1.00*[Z,Z], 'LineStyle','none', 'Marker','.', 'MarkerSize',1, 'Color','b')
view(3)

%# freeze the aspect ratio to override stretch-to-fill behaviour
axis vis3d

%# fix the axes limits manually
%#set(gca, 'xlim',[-1 1], 'ylim',[-1 1], 'zlim',[-1 1])
axis manual

%# maybe even remove the tick labels
%set(gca, 'xticklabel',[], 'yticklabel',[], 'zticklabel',[])

%# animate (until figure is closed)
while ishandle(hFig); camorbit(0.9,-0.1); drawnow; end

alt text

Note how we are using the Z-buffer renderer, and turned off the Backingstore property.


EDIT:

If I understood correctly, what you are trying to do is to record a screencast (using a 3rd-party app), while you manually rotate the figure, but in your case these manual rotations are "jumpy". On the other animating your figure with CAMORBIT/VIEW in a while-loop is running smooth...

I propose an alternative solution: start by rotating the figure using the mouse and write these view configurations at each step (azimuth,elevation). Then you can replay them using the VIEW function while recording the video, something like:

v = [...];   %# matrix where each row specify Az/El of view
for i=1:size(v,1)
    view( v(i,:) )
    drawnow
end

The downside is that you will have to press/rotate/release using the mouse in small steps (the ROTATE3D object does not expose a mouse-motion event)

I wrote a simple function to help you in this process. It loads the saved figure, enable 3d-rotation, and keeps track of the intermediate position at each step. Once finished, press the "Done" button to return the list of views...

function v = rotationDemo(figFileName)
    views = [];                     %# list of views (Az,El)

    hFig = hgload(figFileName);     %# load the saved figure

    views(1,:) = get(gca,'View');   %# store initial view

    %# add a button, used to terminate the process
    hButton = uicontrol('Style','pushbutton', 'Position',[400 1 80 20], ...
                        'String','Done?', 'Callback',@buttonCallback);
    set(hFig, 'Toolbar','figure')   %# restore toolbar

    %# start 3d rotation, and handle post-callback to record intermediate views
    h = rotate3d(hFig);             %# get rotation object
    set(h, 'ActionPostCallback',@rotateCallback)
    set(h, 'Enable','on')           %# enable rotation

    msgbox('Rotate the view step-by-step', 'rotate3d', 'warn', 'modal')

    uiwait(hFig)                    %# wait for user to click button
    delete(hButton)                 %# delete button on finish
    set(h, 'Enable','off')          %# disable rotation
    v = round(views);               %# return the list of views

    %# callback functions
    function rotateCallback(o,e)
        views(end+1,:) = get(e.Axes,'View');  %# add current view to list
    end
    function buttonCallback(o,e)
        uiresume(gcbf)                        %# uiresume(hFig)
    end
end

alt text

You can call the above function, then replay the animation:

v = rotationDemo('smooth_rotation.fig');
for i=1:size(v,1)
    view(v(i,:))
    drawnow
end

We can smooth the transitions by simple interpolation:

v = rotationDemo('smooth_rotation.fig');
n = size(v,1);
nn = linspace(1,n,100)';     %'# use 100 steps
vv = round( [interp1(v(:,1),nn) interp1(v(:,2),nn)] );
for i=1:size(vv,1)
    view(vv(i,:))
    DRAWNOW                  %# or PAUSE(..) to slow it down
end

As a side note, I should mention that ROTATE3D and CAMORBIT have different effects. ROTATE3D changes the View property of the current axis, while CAMORBIT controls the camera properties CameraTarget/CameraPosition/CameraUpVector of the current axis.

过期以后 2024-10-13 02:30:32

我认识到您在常规 MATLAB 图上谈论的相同的抽搐动作。但是当我尝试运行 Amro 的代码并创建电影 (*.AVI) 时,它在我的 Mac 笔记本上看起来也很流畅。

我使用的电影制作代码如下:

%在制作电影时添加了图形“关闭”的“可见”属性(尽管我不确定这是否会使情况变得更好),如下所示:

hFig = 图('Backingstore','off','visible','off','渲染器','zbuffer');

% 然后,我用一个简单的 AVI 制作循环替换了 Amro 的 while 循环,如下所示:

aviobj=avifile('test.avi'); %创建 AVI 文件

对于 I=1:360

camorbit(0.9,-0.1);拉多;

aviobj=addframe(aviobj,hFig); %将帧添加到 AVI 文件

结束

aviobj=关闭(aviobj); %关闭​​ AVI 文件

关闭(hFig); %关闭​​hFig

问题:
在渲染图形之前抽取一些点或创建密度图会有帮助吗?

[参考文献。关于各种渲染选项:http://www.mathworks.com/support/ tech-notes/1200/1201.html]

我希望以上评论对您有所帮助。

I recognize the same jerking movements that you are talking about on a regular MATLAB Figure. But when I tried running the Amro's code, created a movie (*.AVI), it looks smooth on my Mac notebook also.

The movie making code that I used is the following:

% Added the 'Visible' property of the figure 'off' while making a movie (although I am not exactly certain if this will make the situation better) like so:

hFig = figure('Backingstore','off','visible','off','renderer','zbuffer');

% Then, I replaced Amro's while-loop with a simple AVI production loop, as follows:

aviobj=avifile('test.avi'); %creates AVI file

for I=1:360

camorbit(0.9,-0.1); drawnow;

aviobj=addframe(aviobj,hFig); %adds frames to the AVI file

end

aviobj=close(aviobj); %closes AVI file

close(hFig); %close hFig

Question:
Would it help to decimate some points or to create a density map before rendering the figure?

[Ref. on various Rendering Options: http://www.mathworks.com/support/tech-notes/1200/1201.html ]

I hope the comments above would be of any help.

流年里的时光 2024-10-13 02:30:32

我不知道这是否对您的问题有帮助,但出于某种原因,我使用 pause(0.001) 比使用 drawnow 强制更新图形窗口

您还可能会看到rotate3d 是否更快。

核心数量并不像你想象的那么重要,因为 matlab 中的许多函数不支持多线程。


解决方法是像现在一样继续,但将图形窗口写入电影文件。然后您就可以播放电影了。

I don't know if this will help your issue, but for some reason, I've had better success with pause(0.001) than drawnow to force an update of the graphics window

You might also see if rotate3d is faster.

The number of cores doesn't matter as much as you think, as many functions in matlab do not support multi-threading.


A workaround would be to proceed as you are now, but write the figure window to a movie file. Then you can play back the movie.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文