Related
Quite a simple question but just couldn't find the answer online... I want to visualise a point cloud gathered from a lidar. I can plot the individual frames but wanted to loop them to create a "animation". I know how to do it for normal plots with drawnow but can't get it working with a scatter3. If I simply call scatter3 again like I have done in the commented code then the frame that I am viewing in the scatter plot jumps around with every update (Very uncomfortable). How do i get the scatter3 plot to update to the new points without changing the UI of the scatter ie. Still be able to pan and zoom around the visualised point cloud while it loops through.
EDIT: The file is a rosbag file, I cannot attach it because it is 170MB. The problem doesn't happen when using scatter3 in a loop with a normal array seems to be something with using scatter3 to call a PointCloud2 type file using frame = readMessages(rawBag, i).
EDIT: The problem does not seem to be with the axis limits but rather with the view of the axis within the figure window. When the scatter is initialised it is viewed with the positive x to the right side, positive y out of the screen and positive z upwards, as shown in view 1. Then after a short while it jumps to the second view, where the axis have changed, positive x is now out of the screen, positive y to the right and positive z upwards (both views shown in figures). This makes it not possible to view in a loop as it is constantly switching. So basically how to update the plot without calling scatter3(pointCloudData)?
rawBag = rosbag('jackwalking.bag');
frame = readMessages(rawBag, 1);
scatter3(frame{1});
hold on
for i = 1:length(readMessages(rawBag))
disp(i)
frame = readMessages(rawBag, i);
% UPDATE the 3D Scatter %
% drawnow does not work?
% Currently using:
scatter3(frame{1})
pause(.01)
end
The trick is to not use functions such as scatter or plot in an animation, but instead modify the data in the plot that is already there. These functions always reset axes properties, which is why you see the view reset. When modifying the existing plot, the axes are not affected.
The function scatter3 (as do all plotting functions) returns a handle to the graphics object that renders the plot. In the case of scatter3, this handle has three properties of interest here: XData, YData, and ZData. You can update these properties to change the location of the points:
N = 100;
data = randn(N,3) * 40;
h = scatter3(data(:,1),data(:,2),data(:,3));
for ii = 1:500
data = data + randn(N,3);
set(h,'XData',data(:,1),'YData',data(:,2),'ZData',data(:,3));
drawnow
pause(1/5)
end
The new data can be totally different too, it doesn't even need to contain the same number of points.
But when modifying these three properties, you will see the XLim, YLim and ZLim properties of the axes change. That is, the axes will rescale to accommodate all the data. If you need to prevent this, set the axes' XLimMode, YLimMode and ZLimMode to 'manual':
set(gca,'XLimMode','manual','YLimMode','manual','ZLimMode','manual')
When manually setting the limits, the limit mode is always set to manual.
As far as I understood what you describe as "plots jumpying around", the reason for this are the automatically adjusted x,y,z limits of the scatter3 plot. You can change the XLimMode, YLimMode, ZLimMode behaviour to manual to force the axis to stay fixed. You have to provide initial axes limits, though.
% Mock data, since you haven't provided a data sample
x = randn(200,50);
y = randn(200,50);
z = randn(200,50);
% Plot first frame before loop
HS = scatter3(x(:,1), y(:,1), z(:,1));
hold on
% Provide initial axes limits (adjust to your data)
xlim([-5,5])
ylim([-5,5])
zlim([-5,5])
% Set 'LimModes' to 'manual' to prevent auto resaling of the plot
set(gca, 'XLimMode', 'manual', 'YLimMode', 'manual', 'ZLimMode', 'manual')
for i=2:len(x,2)
scatter3(x(:,i), y(:,i), z(:,i))
pause(1)
end
This yields an "animation" of plots, where you can pan and zoom into the data while continuous points are added in the loop
I have images captured in many frames. All the frames have different sizes so I set the global maximum and minimum of each axes and used axis command like below:
h = figure;
axis([-8.4188e+03 -7.9061e+03 -102.6261 518.2502 -25.4673 0.2780])
%axis tight manual % this ensures that getframe() returns a consistent size
filename = 'testanim.gif';
But the animation boxes still keep changing the sizes although I have found the maximum and minimum limits of Xs, Ys and Zs. Do I need something more in my code?
This is the code I use for plotting:
% read the data
for k=1:length(FileNames)
FName = plyfiles(k).name;
plys=pcread(['Files\',FName]);
pcshow(plys) % Capture the plot as an image
frame = getframe(h);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256); % Write to the GIF File
if k == 1
imwrite(imind,cm,filename,'gif', 'Loopcount',inf);
else
imwrite(imind,cm,filename,'gif','WriteMode','append');
end
end
pcshow will, just like imshow or plot, reset the axes it writes to. You can prevent this by setting
hold on
after creating the axes. However, now the point clouds will be shown on top of the previous ones, so you will also have to clear the axes before plotting a new one.
The easiest solution is, instead of holding the plot, to set the axes position every time after plotting. That is, do
set(gca, 'PropertyName', property_value)
after the pcshow call. The properties to set are, I believe, 'XLim', 'YLim', and 'ZLim'.
I am creating some figures in MATLAB and automatically save them to files. The problem that by definition the images are small. A good way to solve my problem by hand is to create an image (figure), maximize it, and save to a file.
I am missing this step of automatically maximize a figure.
Any suggestions?
Up till now I only found this:
http://answers.yahoo.com/question/index?qid=20071127135551AAR5JYh
http://www.mathworks.com/matlabcentral/newsreader/view_thread/238699
but none are solving my problem.
This worked for me:
figure('units','normalized','outerposition',[0 0 1 1])
or for current figure:
set(gcf,'units','normalized','outerposition',[0 0 1 1])
I have also used MAXIMIZE function on FileExchange that uses java. This is true maximization.
For an actual Maximize (exactly like clicking the maximize button in the UI of OS X and Windows)
You may try the following which calls a hidden Java handle
figure;
pause(0.00001);
frame_h = get(handle(gcf),'JavaFrame');
set(frame_h,'Maximized',1);
The pause(n) is essential as the above reaches out of the Matlab scape and is situated on a separate Java thread. Set n to any value and check the results. The faster the computer is at the time of execution the smaller n can be.
Full "documentation" can be found here
As of R2018a, figure as well as uifigure objects contain a property called WindowState. This is set to 'normal' by default, but setting it to 'maximized' gives the desired result.
In conclusion:
hFig.WindowState = 'maximized'; % Requires R2018a
Furthermore, as mentioned in Unknown123's comments:
Making figures maximized by default is possible using:
set(groot, 'defaultFigureWindowState', 'maximized');
Maximizing all open figures is possible using:
set(get(groot, 'Children'), 'WindowState', 'maximized');
More information about 'WindowState' as well as other properties controlling figure appearance can be found in this documentation page.
Finally, to address your original problem - if you want to export the contents of figures to images without having to worry about the results being too small - I would highly recommend the export_fig utility.
To maximize the figure, you can mimic the sequence of keys you would actually use:
ALT-SPACE (as indicated here) to access the window menu; and then
X to maximize (this may vary in your system).
To send the keys programmatically, you can use a Java-based procedure similar to this answer, as follows:
h = figure; %// create figure and get handle
plot(1:10); %// do stuff with your figure
figure(h) %// make it the current figure
robot = java.awt.Robot;
robot.keyPress(java.awt.event.KeyEvent.VK_ALT); %// send ALT
robot.keyPress(java.awt.event.KeyEvent.VK_SPACE); %// send SPACE
robot.keyRelease(java.awt.event.KeyEvent.VK_SPACE); %// release SPACE
robot.keyRelease(java.awt.event.KeyEvent.VK_ALT); %// release ALT
robot.keyPress(java.awt.event.KeyEvent.VK_X); %// send X
robot.keyRelease(java.awt.event.KeyEvent.VK_X); %// release X
VoilĂ ! Window maximized!
As it is proposed by an author above, if you want to simulate clicking the "maximize" windows button, you can use the code that follows. The difference with the mentionned answer is that using "drawnow" instead of "pause" seems more correct.
figure;
% do your job here
drawnow;
set(get(handle(gcf),'JavaFrame'),'Maximized',1);
imho maximizing the figure window is not the best way to save a figure as an image in higher resolution.
There are figure properties for printing and saving. Using these properties you can save files in any resolution you want. To save the files you have to use the print function, because you can set an dpi value. So, firstly set the following figure properties:
set(FigureHandle, ...
'PaperPositionMode', 'manual', ...
'PaperUnits', 'inches', ...
'PaperPosition', [0 0 Width Height])
and secondly save the file (for example) as png with 100dpi ('-r100')
print(FigureHandle, Filename, '-dpng', '-r100');
To get a file with 2048x1536px set Width = 2048/100 and Height 1536/100, /100 because you save with 100dpi. If you change the dpi value you also have to change the divisor to the same value.
As you can see there is no need for any extra function from file exchange or java-based procedure. In addition you can choose any desired resolution.
you can try this:
screen_size = get(0, 'ScreenSize');
f1 = figure(1);
set(f1, 'Position', [0 0 screen_size(3) screen_size(4) ] );
%% maximizeFigure
%
% Maximizes the current figure or creates a new figure. maximizeFigure() simply maximizes the
% current or a specific figure
% |h = maximizeFigure();| can be directly used instead of |h = figure();|
%
% *Examples*
%
% * |maximizeFigure(); % maximizes the current figure or creates a new figure|
% * |maximizeFigure('all'); % maximizes all opened figures|
% * |maximizeFigure(hf); % maximizes the figure with the handle hf|
% * |maximizeFigure('new', 'Name', 'My newly created figure', 'Color', [.3 .3 .3]);|
% * |hf = maximizeFigure(...); % returns the (i.e. new) figure handle as an output|
%
% *Acknowledgements*
%
% * Big thanks goes out to Yair Altman from http://www.undocumentedmatlab.com/
%
% *See Also*
%
% * |figure()|
% * |gcf()|
%
% *Authors*
%
% * Daniel Kellner, medPhoton GmbH, Salzburg, Austria, 2015-2017
%%
function varargout = maximizeFigure(varargin)
warning('off', 'MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame')
% Check input variables
if isempty(varargin)
hf = gcf; % use current figure
elseif strcmp(varargin{1}, 'new')
hf = figure(varargin{2:end});
elseif strcmp(varargin{1}, 'all')
hf = findobj('Type', 'figure');
elseif ~isa(varargin{1}, 'char') && ishandle(varargin{1}) &&...
strcmp(get(varargin{1}, 'Type'), 'figure')
hf = varargin{1};
else
error('maximizeFigure:InvalidHandle', 'Failed to find a valid figure handle!')
end
for cHandle = 1:length(hf)
% Skip invalid handles and plotbrowser handles
if ~ishandle(cHandle) || strcmp(get(hf, 'WindowStyle'), 'docked')
continue
end
% Carry the current resize property and set (temporarily) to 'on'
oldResizeStatus = get(hf(cHandle), 'Resize');
set(hf(cHandle), 'Resize', 'on');
% Usage of the undocumented 'JavaFrame' property as described at:
% http://undocumentedmatlab.com/blog/minimize-maximize-figure-window/
jFrame = get(handle(hf(cHandle)), 'JavaFrame');
% Due to an Event Dispatch thread, the pause is neccessary as described at:
% http://undocumentedmatlab.com/blog/matlab-and-the-event-dispatch-thread-edt/
pause(0.05)
% Don't maximize if the window is docked (e.g. for plottools)
if strcmp(get(cHandle, 'WindowStyle'), 'docked')
continue
end
% Don't maximize if the figure is already maximized
if jFrame.isMaximized
continue
end
% Unfortunately, if it is invisible, it can't be maximized with the java framework, because a
% null pointer exception is raised (java.lang.NullPointerException). Instead, we maximize it the
% straight way so that we do not end up in small sized plot exports.
if strcmp(get(hf, 'Visible'), 'off')
set(hf, 'Units', 'normalized', 'OuterPosition', [0 0 1 1])
continue
end
jFrame.setMaximized(true);
% If 'Resize' will be reactivated, MATLAB moves the figure slightly over the screen borders.
if strcmp(oldResizeStatus, 'off')
pause(0.05)
set(hf, 'Resize', oldResizeStatus)
end
end
if nargout
varargout{1} = hf;
end
This is the shortest form
figure('Position',get(0,'ScreenSize'))
I recommend the set command to change MenuBar and Toolbar properties of your figure. The set command is more versatile because it works for older and newer versions of Matlab.
fig = figure(1);
set(fig, 'MenuBar', 'none');
set(fig, 'ToolBar', 'none');
Now you can use set again to make your figure full screen.
set(fig, 'Position', get(0,'Screensize'));
Note that if you make the figure full screen first, and then remove the MenuBar and Toolbar, the figure will not be full screen, so make sure to run these in the correct order.
I have multiple 2D line plots in Matlab (they represent some wave moving through space). Each plot represents the wave at some time t. I want to animate through these plots (i.e. show the first plot for a fraction of a second, then show the next one, and the next, etc. I want to loop back to the beginning once it reaches the end time) to show the time evolution of the system. surf and mesh don't really do what I want since it is too difficult to see the changes with the number of time steps I have. Is there a way to do this in Matlab?
I assume with "2d-line" you mean a 2d-plot. This is done by the plot-function, so there is no need of surf or mesh. Sorry, when I got you wrong.
The following code does what I think you asked for:
% Generate some propagating wave
n = 20;
t = linspace(0,10,100);
x = cell(1,n);
for i = 1:n
x{i} = (1-abs(i/n-0.4))*sin(t+i*0.2);
end
% Create frames
figure;
for i = 1:length(x)
clf;
plot(t,x{i});
ylim([-1,1]);
myFrames(i) = getframe; %#ok<SAGROW>
end
% Show movie
figure;
movie(myFrames,2,2); % frames, repetitions, frames per second
I'm trying to stitch together a bunch of plots I created within a loop into a single video file. I've been at this for several hours, but had no luck. Here is my minimum working example where I attempt to use the VideoWriter function to create a video. I always get an error saying my frame(s) can't be copied into the video objects. Grr.
Here is my minimum working example:
n=(1:50)*2*pi;
for t = 1:1000
Y = sin(n*50/t);
plot(Y); %plot shows a sine wave with decreasing frequency
F(t) = getframe; %I capture the plot here
end
writerObj = VideoWriter('test2.avi'); %Attempt to create an avi
open(writerObj);
for t= 1:time
writeVideo(writerObj,F(t))
end
close(writerObj);
Matheburg answer is correct and identified the part which was causing the error (at some point the scale of your axis was resized, which cause the frame size to change).
His solution works fine and if the usage of fplot works for you then follow his way.
In case you still want to use the traditional plot (2d lineserie object) method, then here's how I usually organize "animated" plots:
The plot function is high level. It means when it runs it plots the data (obviously) but also does a lot of other things. In any case it generate a completely new plot (erasing previous plot if hold on wasn't specified), but also readjust the axes limits and many other settings (color, style etc ...).
If in your animation you only want to update the plot data (the points/line position) but not change any other settings (axes limits, colors etc ...), it is better to define the plots and it's settings one time only, outside of the loop, then in the loop you only update the YData of the plot object (and/or the XData if relevant).
This is done by retrieving the plot object handle when you create it, then use the set method (which unlike plot will only modify the parameters you specify explicitly, and won't modify anything else).
In your case it looks like this:
n=(1:50)*2*pi ;
Y = sin(n*50) ;
hp = plot(Y) ; %// Generate the initial plot (and retrieve the handle of the graphic object)
ylim([-1,1]) ; %// Set the Y axes limits (once and for all)
writerObj = VideoWriter('test2.avi'); %// initialize the VideoWriter object
open(writerObj) ;
for t = 1:1000
Y = sin(n*50/t) ; %// calculate new Y values
set(hp,'YData',Y) ; %// update the plot data (this does not generate a "new" plot), nor resize the axes
F = getframe ; %// Capture the frame
writeVideo(writerObj,F) %// add the frame to the movie
end
close(writerObj);
Also, this method will usually runs faster, and save a significant amount of time if your loop has a great number of iterations.
Side note: As said above, Matheburg solution runs also fine. For such an application, the difference of speed will not be a major issue, but note that the plots (and movie) generated are slightly different (due to it's use of fplot instead of plot). So I encourage you to try both versions and choose which one suits you best.
You are missing a constant height of images. You can guarantee it by, e.g., ylim:
time = 100;
for t = 1:time
fplot(#(x) sin(x*50/t),[0,2*pi]); % plot
ylim([-1,1]); % guarantee consistent height
F(t) = getframe; % capture it
end
writerObj = VideoWriter('test2.avi');
open(writerObj);
writeVideo(writerObj, F)
close(writerObj);
I have further replaced your discrete plot by a "continuous" one (using fplot).