Using movie to record subplot frames in for loop

Hi Mathworks,
I have the following code:
N1=rand(121,20e3);
X1=rand(1,121);
Y1=rand(1,121);
Z1=rand(1,121);
N2=rand(121,20e3);
X2=rand(1,121);
Y2=rand(1,121);
Z2=rand(1,121);
dt=100;
for i=1:dt:length(N1)
ax=gca
subplot(1,2,1);
ax=gca;
cmap=N1(:,i)';
hAxes=scatter3(X1,Y1,Z1,50,cmap,'filled');
title(strcat('Time = ',num2str(i*0.1),' ms'));
colorbar;
caxis(ax,[0 1]);
subplot(1,2,2)
ax2=gca
cmap2=N2(:,i)';
hAxes2=scatter3(X2,Y2,Z2,50,cmap2,'filled');
title(strcat('Time = ',num2str(i*0.1),' ms'));
colorbar;
caxis(ax2,[0 1]);
drawnow
end
It generates some subplots that update at each iteration of the FOR loop. Crucially the colorbars do not change their MIN and MAX on each iteration.
How do I record this into a movie?
I have tried creating a structure F and using movie(F) but it doesn't seem to work.
Thanks! Linford
PS also any clues on how better to have a "time = " updating during the movie would be really appreciated!

1 Comment

Please post the code for teh creatrion of the struct F and explain, what "doesn't seem to work" exactly means. It is easier to suggest an improvement than to guess, what you have done and which problem occurred. :-)
Concerning a "better" way to display the time: What do you see as drawback currently?

Sign in to comment.

 Accepted Answer

Jan
Jan on 31 Jan 2017
Edited: Jan on 2 Feb 2017
% [EDITED] Updated version, 31-Jan-2017 16:19:11 UTC]
FigH = figure;
ax1 = subplot(1, 2, 1, 'NextPlot', 'add'); % as: hold on
colorbar;
caxis(ax1,[0 1]);
t1 = title('dummy');
view(ax1, 3);
ax2 = subplot(1, 2, 2, 'NextPlot', 'add'); % as: hold on
colorbar;
caxis(ax2,[0 1]);
t2 = title('dummy');
view(ax2, 3);
dt = 100;
nFrame = numel(1:dt:length(N1)); % [EDITED]
F(nFrame) = struct('cdata',[],'colormap',[]);
ScatterH1 = [];
ScatterH2 = [];
iFrame = 0;
for i = 1:dt:length(N1)
if ~isempty(ScatterH1) % Clean up former objects
delete(ScatterH1); % Alternative: Update the 'CData' only
delete(ScatterH2);
end
cmap=N1(:,i)';
ScatterH1 = scatter3(X1,Y1,Z1,50,cmap,'filled', 'Parent', ax1);
set(t1, 'String', sprintf('Time = %.1f ms', i*0.1)); % [EDITED]
cmap2=N2(:,i)';
ScatterH2 = scatter3(X2,Y2,Z2,50,cmap2,'filled', 'Parent', ax2);
set(t2, 'String', sprintf('Time = %.1f ms', i*0.1)); % [EDITED]
drawnow
iFrame = iFrame + 1;
F(iFrame) = getframe(FigH); % Not i, because this steps in dt
end
figure;
MovieSize = [size(F(1).cdata, 2), size(F(1).cdata, 1)];
AxesH = axes('Visible', 'off', 'Units', 'pixels', ...
'Position', [10, 10, MovieSize]);
movie(AxesH, F);
I runtime matters, create the scatter3 objects once and only update the 'XData', 'YData' and 'ZData', or 'CData' in the loop.
If you speficy your problems, I will update this suggestion in the "..." line.

12 Comments

Thanks for you speedy reply Jan.
Well I think its something like:
N1=rand(121,20e3);
X1=rand(1,121);
Y1=rand(1,121);
Z1=rand(1,121);
N2=rand(121,20e3);
X2=rand(1,121);
Y2=rand(1,121);
Z2=rand(1,121);
dt=100;
FigH = figure;
ax1 = subplot(1, 2, 1); % Create the axes once only
colorbar;
caxis(ax1,[0 1]);
t1 = title('dummy');
ax2 = subplot(1, 2, 2);
colorbar;
caxis(ax2,[0 1]);
t2 = title('dummy');
dt = 100;
F(length(N1))=struct('cdata',[],'colormap',[]);
for i = 1:dt:length(N1)
cmap=N1(:,i)';
scatter3(X1,Y1,Z1,50,cmap,'filled', 'Parent', ax1);
set(t1, sprintf('Time = %.1f ms', i*0.1));
cmap2=N2(:,i)';
scatter3(X2,Y2,Z2,50,cmap2,'filled', 'Parent', ax2);
set(t2, sprintf('Time = %.1f ms', i*0.1));
drawnow
F(i)=getframe(FigH);
end
but I am getting the error:
Error using matlab.graphics.primitive.Text/set
Invalid or deleted object.
Please try this:
FigH = figure;
ax1 = subplot(1, 2, 1, 'NextPlot', 'add'); % as: hold on
colorbar;
caxis(ax1,[0 1]);
t1 = title('dummy');
ax2 = subplot(1, 2, 2, 'NextPlot', 'add'); % as: hold on
colorbar;
caxis(ax2,[0 1]);
t2 = title('dummy');
dt = 100;
nFrame = numel(1:dt:length(N1)); % [EDITED]
F(nFrame) = struct('cdata',[],'colormap',[]);
ScatterH1 = [];
ScatterH2 = [];
iFrame = 0;
for i = 1:dt:length(N1)
if ~isempty(ScatterH1) % Clean up former objects
delete(ScatterH1);
delete(ScatterH2);
end
cmap=N1(:,i)';
ScatterH1 = scatter3(X1,Y1,Z1,50,cmap,'filled', 'Parent', ax1);
set(t1, 'String', sprintf('Time = %.1f ms', i*0.1)); % [EDITED]
cmap2=N2(:,i)';
ScatterH2 = scatter3(X2,Y2,Z2,50,cmap2,'filled', 'Parent', ax2);
set(t2, 'String', sprintf('Time = %.1f ms', i*0.1)); % [EDITED]
drawnow
iFrame = iFrame + 1;
F(iFrame) = getframe(FigH); % Not i, because this steps in dt
end
Thanks Jan, that works, although I had to comment out the set command because it gave the errors:
Error using matlab.graphics.primitive.Text/set
There is no Time = 0.1 ms property on the Text class.
Any idea why?
How do I replay the movie? When I type:
figure
movie(F)
I get this image:
It has a very weird axis inside an axis. How do I prevent this from happening?
Thanks again for your help.
Error using matlab.graphics.primitive.Text/set
This was a typo in my code, because I forgot: 'String' . See [EDITED].
The movie command plays the movie inside an axes object. So you can create this object explicitly to remove the ticks:
figure;
MovieSize = [size(F(1).cdata, 2), size(F(1).cdata, 1)];
AxesH = axes('Visible', 'off', 'Units', 'pixels', ...
'Position', [10, 10, MovieSize]);
movie(AxesH, F);
Not trivial, unfortunately.
I update the code in my answer with the latest version.
Jan why does the scatter3 plot display only 2axis? Do I have to change the axis command so that it shows a 3D axis.
This (untitled.jpg) is how the scatter3 plot looks like when you call a particular time step without the
'Parent', ax1
added to the scatter3 command. But the code produces a 2D image.
@Linford Briant: I've inserted two view(ax, 3) commands in the code now. See "help view".
Thanks Jan.
Now I use the following to write the file to a .avi movie:
movie2avi(F,'test.avi','compression','None')
But it is telling me to use VideoWriter. How do I open a video object and write to this instead?
You never looked at my answer, did you?
No I didn't look at your answer, I looked at his answer!
Jan
Jan on 2 Feb 2017
Edited: Jan on 2 Feb 2017
@Linford: It is always worth to read Image Analyst's professional answers. I did read them. The link he has posted contains an example for the usage of the VideoWriter.
Thanks Jan and Image Analyst, very useful material.
Here is my solution:
v = VideoWriter('example.avi');
open(v);
N1=rand(121,20e3);
X1=rand(1,121);
Y1=rand(1,121);
Z1=rand(1,121);
N2=rand(121,20e3);
X2=rand(1,121);
Y2=rand(1,121);
Z2=rand(1,121);
dt=100;
% [EDITED] Updated version, 31-Jan-2017 16:19:11 UTC]
FigH = figure;
hold on
ax1 = subplot(1, 2, 1, 'NextPlot', 'add'); % as: hold on
colorbar;
caxis(ax1,[0 1]);
t1 = title('dummy');
view(ax1, 3);
ax2 = subplot(1, 2, 2, 'NextPlot', 'add'); % as: hold on
colorbar;
caxis(ax2,[0 1]);
t2 = title('dummy');
view(ax2, 3);
dt = 1000;
nFrame = numel(1:dt:length(N1)); % [EDITED]
F(nFrame) = struct('cdata',[],'colormap',[]);
ScatterH1 = [];
ScatterH2 = [];
iFrame = 0;
for i = 1:dt:length(N1)
if ~isempty(ScatterH1) % Clean up former objects
delete(ScatterH1); % Alternative: Update the 'CData' only
delete(ScatterH2);
end
cmap=N1(:,i)';
ScatterH1 = scatter3(X1,Y1,Z1,50,cmap,'filled', 'Parent', ax1);
set(t1, 'String', sprintf('Time = %.1f ms', i*0.1)); % [EDITED]
cmap2=N2(:,i)';
ScatterH2 = scatter3(X2,Y2,Z2,50,cmap2,'filled', 'Parent', ax2);
set(t2, 'String', sprintf('Time = %.1f ms', i*0.1)); % [EDITED]
drawnow
iFrame = iFrame + 1;
F(iFrame) = getframe(FigH); % Not i, because this steps in dt
writeVideo(v,F(iFrame));
end
close(v);
I had to hold on to the figure because the frame changes size on every iteration... not sure why though because when the video is playing it doesn't seem to change size!

Sign in to comment.

More Answers (1)

3 Comments

+1: These demos have a high level of complexity and the transfer to the original problem is not easy. But finally the instructions for using the VideoWriter are required for the complete solution.
This is something I like in this forum: You can get multiple answers focussing different aspects of the problems and supplement each other. Although only one answer can be accepted, the quality of the forum is based on the variety of answers. The forum acts as a community, not as a cluster of lone fighters. Usually.
@IA: Thanks!
Sorry for the off-topic comment. It has been time for a positive emotional statement.
I actually opened these initially but it was above my station.
So I have to extract movie frames to make a video?
Well I didn't have the time, at that time, to adapt the demo for you - I was hoping you'd see how to do it. No, you don't have to write images or graphs to intermediate/temporary files. You can go right from the graph/axes to a video file and that's what my movie_made_from_surf.m demo shows. You can either make up the entire movie structure in memory, as I did, or if the movie would take up too much memory, you can skip that and go right from read() to writeVideo() without any intermediate complete movie being made. Something like this snippet
% Extract the frame from the movie.
thisFrame = read(videoObject, frame);
% Write this frame out to a new video file.
writeVideo(writerObj, thisFrame);
For what it's worth, I'm attaching another demo that I did for someone who wanted to convert an .avi format movie into a .mp4 format movie.

Sign in to comment.

Categories

Find more on Graphics Performance in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!