Change the color of a pixel in an imagesc plot without repainting everything in Matlab/Octave - matlab

I have a for-loop where I would like to paint at every step a new pixel in an imagesc plot. I am currently repainting the whole figure but the figure is blinkering and I know it is not the proper way to do so. Can anyone help to find the appropriate function to do this task?

You can address the data in each pixel with the CData property of the image without having to close and redraw the figure, axes, or axes object over and over. Ends up being about 1.5x - 2x faster than trying to generate a new image object or just calling imagesc() over and over.
In these loops don't forget the drawnow call or MATLAB will try to skip the figure drawing until the looping completes.
Example code:
data = rand(200, 200); % Data to display
figure(1) % Make a figure
imgHand = imagesc(data); % Display data in it
% Naive way - call imagesc() each time.
% Slow. Don't do it this way.
for k = 1:numel(data);
data(k) = data(k) + 10*rand(1,1); % Update data
imagesc(data) % Redraw it by calling imagesc()
drawnow; % Display updated figure
end
% Faster way - address CData of image object directly
% 1.5-2x faster than above method
for k = 1:numel(data);
data(k) = data(k) + 10*rand(1,1); % Update data
set(imgHand, 'CData', data); % Change CData property of object
drawnow; % Display updated figure
end

You don't have a choice but to paint everything at each iteration if you want to draw every frame that has an update. However, you can minimize the flickering (or perhaps even remove it) by perhaps placing a pause at the end of your loop before the next iteration. This way, it'll give the frame buffer a chance to draw to the screen completely before you draw the next frame.
Something like:
for idx = 1 : total_frames
%// Do stuff
drawnow; %// Draw frame
pause(0.1); %// Pause
end
total_frames would be the total number of times you are refreshing the plot, and inside the loop you would do the work necessary, you then draw the figure, then pause for 0.1 ms. Adjust the time to whatever works for you.

Related

How to update a scatter3 plot (in a loop) in Matlab

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

Freeze the axes size in matlab

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'.

Matlab GUi How to come back to the command window when axes is refreshing

I am writing a Matlab GUI and I need to show images in one axes. The images come from the camera. So I find when use preview to display the camera with one axes, I can go back to the command window. But when I use get the image with the getsnapshot() function and do some processing then use the other axes to display the processed images in real time, I find I can not go back to the command window. That maybe because the axes is refreshing all the time. So when I click the command window, the axes popup then disturb the command window. So anyone knows the solution. The following is my GUI panel.
The code like this:
while length>0 % length is the video duration that user set
frame = getsnapshot(VidObj); % VidObj is the camera
signal = imageprocess(frame); % image processing
axes(handles.Signal_Monitor); % show in axes Signal_Monitor
imshow(signal); % show result image
end
Thanks a lot!
Image show by function imshow(signal,'InitialMagnification','fit','Parent',handles.Signal_Monitor); 4x4 resolution
Image show by function set(handles.ih, 'cData', signal); It is only a tiny region. Because I process the original image captured by camera to a very small picture. 4x4 resolution
Instead of replotting the signal in every iteration, change the CData of your image using
set(ih, 'cData', signal);
where ih is the handle to the plotted image when you plot it first time using ih = imshow(signal). And remove the axes call, when you do this you switch the focus. For example you can modify your code as follows to achieve this
length = Total_Time;
while length>0 % length is the video duration that user set
frame = getsnapshot(VidObj); % VidObj is the camera
signal = imageprocess(frame); % image processing
if length==Total_Time % plot only during the first iteration
axes(handles.Signal_Monitor); % show in axes Signal_Monitor
handles.ih = imshow(signal); % show result image
else
set(handles.ih, 'cData', signal);
end
length = Total_Time - 1;
end
I am sure there is a nicer way, but I don't have access to all of your code to do this.

MATLAB's drawnow doesn't flush

Is there any reason that MATLAB's drawnow wouldn't flush?
This is my code:
j=1;
for k = 1:length(P)
for i = 1:n
plot(P(k,j),P(k,j+1),'.');
j = j+2;
end
axis equal
axis([-L L -L L]);
j=1;
drawnow
end
(rungekutta4 is my own function I wrote, and it works OK, so the problem isn't there.)
The particles just stay drawn on the plot and don't get overwritten every time the loop executes.
How could I fix this problem?
The proper and efficient way to do this is with handle graphics. You should also vectorize your plot commands.
% Example data to make runnable
L = 1;
n = 10; % Number of points
P = 2*rand(1e2,n+1)-1;
% Initialize plot, first iteration
h = plot(P(1,1:n),P(1,2:n+1),'.'); % Plot first set of points and return handle
axis equal;
axis([-L L -L L]);
hold on; % Ensure axis properties are fixed
drawnow;
% Animate
for k = 2:size(P,1) % size is safer in this case
% Use handle to update the positions of the previously plotted points
set(h,{'XData','YData'},{P(k,1:n),P(k,2:n+1)});
drawnow;
pause(0.1); % Slow down animation a bit to make visible
end
Calling clf and/or plot on each iteration of an animation causes many things already in memory to be unnecessarily deleted and reallocated, resulting much slower code. It may also result in flickering in some cases.
See also this very similar question and answer.
When you want to animate something, you want to, of course, force draw. Thats what the command drawnow is there for. But there are other things to consider!
One of them is that you need to make sure that everything is drawn in each frame. For that, use the hold on function just before you start drawing (plot).
However, you also need to make sure that you clear the image before drawing, else the plots will stack forever. Use the clf "clear figurecommand before the previously mentionedhold on` and that will do the job.
remember that if the animation is too fast you can always add a pause(0.2) line after drawnow to slow it.

Plot is not reset

i need your help. My program reads a M-File which is a recorded Video.
To display each image i use "imagesc", because the image is saved as a 200x200 Matrix with normalized values ranging from 0 to 1.
After reading each image i do some calculus. The Result should be displayed as an overlay to the image(one Point, and one Line). With the code below this works as expected for the first iteration of the loop.
At all further iterations the image is redrawn(which is correct) but the Point and Line is not cleared.
How can i achieve that the plots are cleared when a new image is displayed.
I tried several variations with the "hold" command. But got no success.
Additional question(not that important):
Is it possible to exchange the "plot" commands below with "set"(especially for the Point)?
My program consist of several more Axes Elements which i cut out to keep the example simple. This means my UI is very slow with "plot" commands so i tried to speed it up with "set".
It works quite well, but I'm not sure if a simple Point can be displayed with "set".
Thanks in advance.
function work()
h_figure = figure('Name','MainFig');
hImage.ax = axes('Units', 'Pixels','Position', [50 375 200 200]);
imagesc('Parent',hImage.ax,'CData',zeros(200));
hImage.axc = get(gca, 'Children');
hProfileLeft.ax = axes('Position', [50 200 200 100]);
hProfileLeft.pl = plot(hProfileLeft.ax, 1:200);
for(frame = obj.Startframe:obj.Endframe)
imgIntens= obj.video.A.intens(:,:,frame);
ProfileResult = doSomeCalc(someArgs);
set(hImage.axc, 'CData', imgIntens); % Show Image(200x200 double)
hold(hImage.ax, 'on'); % Using hold so that plot is overlayed
plot(ProfileResult.peaks.x, ProfileResult.peaks.y,'Parent',hImage.ax); % Simple Point
plot(ProfileResult.corridor.left, 1:200, 'Parent',hImage.ax); % Line
set(hProfileLeft.pl,'YData', ProfileResult.trace); % Draw data to different axes
hold(hImage.ax, 'off');
end
end
I encountered the same annoying behavior. For me the conclusion was to manually call cla.
And use line instead of plot, it has more options (especially nice that you can label the lines yourself) and won't delete the plot when called, also returns you the handles. Once you got the handles you can delete them separately.