I have a GUI with two axes. The first axes has a low-resolution image.
What I would like to do is select an area on the first axes using IMRECT and then display that area as a high-resolution image on the second axes, while continuously updating as I move the IMRECT rectangle around.
The only way I have been able to do this is with a "for loop" with a 0.1 pause in it that just runs for a minute or two while I select and change the ROI with IMRECT, very cumbersome.
My thought was to use a function that ran whenever the mouse moved within the first axes, with the ploting and getPosition commands in that function. However, I'm not sure how to write such a function (triggering on mouse movement within an axes).
Any help would be greatly appreciated!
In general, you should assign a callback to your imrect. For example:
x = imrect();
x.addNewPositionCallback( #(x)(disp('The rect has changed')))
The callback should get additional parameters, such as the image and the second axes by utilizing anonymous functions.
I wrote a small code snippet that does what you want. You should add boundary checks, since I did not bother. It updates CData instead of running imshow when you move the rectangle, so it is quite smooth.
function Zoomer
figure();
highResImage = imread('peppers.png');
lowResImage = imresize(highResImage,0.5);
a1 = subplot(2,1,1);
a2 = subplot(2,1,2);
imshow(lowResImage,'Parent',a1);
initialPosition = [10 10 100 100];
lowResRect = imrect(a1,initialPosition);
lowResRect.addNewPositionCallback( #(pos)Callback(pos,a2,highResImage));
Callback( initialPosition , a2, highResImage);
end
function Callback(position,axesHandle, highResImage)
position = position * 2;
x1 = position(1);
y1 = position(2);
x2 = position(1) + position(3);
y2 = position(2) + position(4);
highResThumbnail = highResImage( round(y1:y2),round(x1:x2),:);
if isempty( get(axesHandle,'Children'))
imshow(highResThumbnail,'Parent',axesHandle);
else
imHandle = get(axesHandle,'Children');
oldSize = size(get(imHandle,'CData'));
if ~isequal(oldSize, size(highResThumbnail))
imshow(highResThumbnail,'Parent',axesHandle);
else
set( imHandle,'CData', highResThumbnail);
end
end
end
Simillar functionality as #Andrey's reply, with three differences:
Setting axis limits instead of 'CData' (which may be faster?)
The magnification factor is variable and depends on the rectangle size, due to the 'fit' 'IniitalMagnification'.
Added ConstraintFcn
would be:
function imZ = Zoom(im, s)
f = figure;
a1 = subplot(1,2,1);
imshow(im,'InitialMagnification', 'fit');
a2 = subplot(1,2,2);
imshow(im,'InitialMagnification', 'fit');
Ipos = [0 0 s];
rect = imrect(a1,Ipos);
rect.setPositionConstraintFcn(#(p) Const(p,size(im)));
rect.addNewPositionCallback(#(p) CB(p,a2));
CB(Ipos,a2);
if nargout > 0
uiwait(f);
imZ = im(pm(2):pm(2)+pm(4),pm(1):pm(1)+pm(3),:);
end
function p = Const(p,imS)
p(1:2) = max(1,p(1:2));
p(1:2) = min(imS([2 1])-p(3:4),p(1:2));
end
function CB(p,a)
pm = round(p);
axes(a);
axis([pm(1),pm(1)+pm(3),pm(2),pm(2)+pm(4)]);
end
end
which could be called as:
Zoom(imread('peppers.png'),[100 100]);
Related
I am trying to save an animation as animated GIF.
My plot is similar to the given code below.
I created it with animated line too.
The problem is that:
When I defined my figure as f=figure or figure(1) it creates the .gif file properly.
However, instead of plotting my figure in separate screen using "figure" command, I have to plot in an axes on MATLAB GUI axes as the given figure.
I tried it with: f=(handles.axes_threeDOF);, But when I use this function, the gif file creates different part of the screen.
Could you help me to solve my problem?
numpoints = 500;
x = linspace(0,4*pi,numpoints);
y = square(x);
y2 = 3 +square(x+1);
f = figure
h = animatedline('Color','b','LineWidth',2);
h2 = animatedline('Color','r','LineWidth',2);
grid on;
axis([0,12,-3,+6])
for k = 1:numpoints
addpoints(h,x(k),y(k))
addpoints(h2,x(k),y2(k))
drawnow
% Capture the plot as an image
frame = getframe(f);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
% Write to the GIF File
if k == 1
imwrite(imind,cm,'test.gif','gif', 'Loopcount',inf);
else
imwrite(imind,cm,'test.gif','gif','WriteMode','append');
end
end
I want to create a gif of this animation:
But it creates as given below with this function " f=(handles.axes_threeDOF)"
I think I found the problem:
f = handles.axes_threeDOF gets the handle of the axes instead of getting the handle of the figure.
Since I don't know the name of your figure, I can't give a perfect solution.
You may try the following options:
1.Find the name of the figure, and use something like: f = handles.figure_threeDOF;
2. Use f = gcf();, assuming there is only one figure.
3. Use f = handles.axes_threeDOF.Parent; assuming the figure is the "Parent" of the axes.
Update:
After im = frame2im(frame);, you need to crop the relevant part of the image:
Something like: im = im(36:884, 928:1800, :);
There are more robust solution than using fixed indices, but it requires some knowledge about the internal structure of the figure.
Here is a code that reproduce the problem (instead of figure handle, f gets axes handle):
numpoints = 500;
x = linspace(0,4*pi,numpoints);
y = square(x);
y2 = 3 +square(x+1);
f = figure;
h = animatedline('Color','b','LineWidth',2);
h2 = animatedline('Color','r','LineWidth',2);
grid on;
axis([0,12,-3,+6])
for k = 1:numpoints
addpoints(h,x(k),y(k))
addpoints(h2,x(k),y2(k))
drawnow
%%% Test %%%
%The following code demonstrates the problem.
%f = gca(), returns a handle to the axes, instead of the figure.
%You should remove the following line for the code to work properly...
f = gca();
%%% Test %%%
% Capture the plot as an image
frame = getframe(f);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
% Write to the GIF File
if k == 1
imwrite(imind,cm,'test.gif','gif', 'Loopcount',inf);
else
imwrite(imind,cm,'test.gif','gif','WriteMode','append');
end
end
Result of correct code (without f = gca();):
Result of wrong code (with f = gca();) - getting axes handle instead of figure handle:
I would like to animate a self updating plot on matlab. For example a string vibrating between two ends. All the basic animate functions I have found in the documentation accomplish the same thing, mainly to animate an evolving plot. I.e the function remains the same but the number of points plotted increases (or decreases) in time. For example this script:
h = animatedline;
axis([0,4*pi,-1,1])
x = linspace(0,4*pi,1000);
y = sin(x);
for k = 1:length(x)
addpoints(h,x(k),y(k));
drawnow
end
traces a sine function as if an invisible hand was drawing it. What I would like to do is animate the entire function but with a varying parameter, like the phase or amplitude. I tried to modify using the following:
x = linspace(0,4*pi,1000);
;
axis([0,4*pi,-1,1])
for k = 1:10
h = animatedline(x,sin(k*x))
drawnow
end
This is sort of close to what I need but the functions are progressively appended, not replaced. This results in a total of 10 functions being plotted instead of an animation.
Does anyone understand what I need to do? If so how can this be accomplished?
What about this:
h = animatedline;
axis([0,4*pi,-1,1]);
x = linspace(0,4*pi,1000);
for k = 1:0.01:10
y = sin(k*x);
clearpoints(h);
addpoints(h,x,y);
drawnow
end
I am trying to animate a line over a dynamic background at the same time, the problem is I cannot update both in the same plot. If I animate the background the lines don't appear.
So the question is why? I was trying in different positions without success.
If you remove the part of the imagesc, there is no problem and the animation of the lines flow
for k = 1:numel(t)
decay = rand;
res = decay * background;
imagesc(x,y,flip(res));
hold on
clearpoints(h);
clearpoints(p);
addpoints(p,[l,(cosO(k)],[0,cosO(k)]);
addpoints(h,[r,(senO(k)],[0,senO(k)]);
drawnow
hold off
end
Fixed! Create a handler and modify CData inside of the loop:
imh = imagesc(x,y,flip(res));
for ...
imh.CData = flip(res);
end
The calls to imagesc overrun your plot. you can overcome this by only change the image matrix represented by imagesc (as 'CData' property):
for k = 1:numel(t)
decay = rand;
res = decay * background;
if k == 1
imh = imagesc(x,y,flip(res));
else
imh.CData = flip(res);
% or: set(imh, 'CData', flip(res) ); % in older MATLAB versions
...
end
I was wondering if anyone knew how to do an animation plot of
x = (dataset of 1000 points)
y = (dataset of 1000 points)
plot(x,y)
big problem is these are datasets that i am trying to plot , or x,y coordinates as opposed to a function which I would know how to plot via an animation.
I tried to do frames in a for loop but it gave me dots and didn't join them in a line graph so I couldn't really watch the path being traced out.
code I used was
for i = 1:length(DATASET1)
pause(0.1)
plot(DATASET1(i),DATASET2(i))
draw on
end
If what you want is for the plot to "grow" point by point: the easiest way is to create an empty plot and then update its XData and YData properties at each iteration:
h = plot(NaN,NaN); %// initiallize plot. Get a handle to graphic object
axis([min(DATASET1) max(DATASET1) min(DATASET2) max(DATASET2)]); %// freeze axes
%// to their final size, to prevent Matlab from rescaling them dynamically
for ii = 1:length(DATASET1)
pause(0.01)
set(h, 'XData', DATASET1(1:ii), 'YData', DATASET2(1:ii));
drawnow %// you can probably remove this line, as pause already calls drawnow
end
Here's an example1 obtained with DATASET1 = 1:100; DATASET2 = sin((1:100)/6);
1 In case someone's interested, the figure is an animated gif which can be created by adding the following code (taken from here) within the loop, after the drawnow line:
frame = getframe(1);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
if ii == 1;
imwrite(imind,cm,filename,'gif','Loopcount',inf);
else
imwrite(imind,cm,filename,'gif','WriteMode','append');
end
Looks like you were close. Not sure draw on is any command though.
See if the code here inspires you to solve your case -
%// Sample x and y values assumed for demo.
x = 1:1000;
y = x.^2;
%// Plot starts here
figure,hold on
%// Set x and y limits of the plot
xlim([min(x(:)) max(x(:))])
ylim([min(y(:)) max(y(:))])
%// Plot point by point
for k = 1:numel(x)
plot(x(k),y(k),'-') %// Choose your own marker here
%// MATLAB pauses for 0.001 sec before moving on to execue the next
%%// instruction and thus creating animation effect
pause(0.001);
end
Since R2014b, you can work with annimatedline object (doc and how-to) that is meant to handle animated graphs pretty well. Basically, the annimatedline object has a addpoints function that adds new points to the line without having to redefine the existing points, along with a clearpoints function that clears lines for more complex animations.
Here is an example:
h = animatedline;
axis([0,4*pi,-1,1])
x = linspace(0,4*pi,1000);
y = sin(x);
for k = 1:length(x)
addpoints(h,x(k),y(k));
drawnow
end
I've written this code which makes an animation of 2 ellipsoids. Parameter k1 of these ellipsoids must depend on time (so they'd move asynchronously), but I need to animate them in one figure. Can I use loop for it or is it better to use timer & some kind of callback functions? The second problem - I need to move inner ellipsoid so they would have one common side. How can I do this?
You should use a loop. The majority of your time will be spent plotting and with the "getFrame" command. You can use profile to verfiy this. The for loop won't add significant overhead and is easiest to code and understand
As for your second question, I'm not sure exactly what you're asking, but if you want to keep a point in common, you should parametrize your surface in terms of the radii, skew angle, etc. and the common point, then just move the point around. You might want to consider writing a "drawEllipsoid" function, which would simplify and clarify your code.
The loop seems fine for this job. A timer would work as well.
I'm not sure quite what you mean when you say "have one common side". It looks like you're close with adding 5 to X2, but you would need a scaling term since they're not the same shape. Could you elaborate?
One suggestion though. I think that you'll be a lot happier if you move the object creation out of the loop like this:
a=5;
b=a;
c=10;
u = (0:0.05*pi:2*pi)'; %'
v = [0:0.05*pi:2*pi];
X = a*sin(u)*cos(v);
Y = a*sin(u)*sin(v);
Z = c*cos(u)*ones(size(v));
Z(Z>0)=0; % cut upper
V1=4/3*pi*a*b*c;
d=1/2;
e=2^d;
a2=a/e;
b2=a/e;
c2=c;
V2=4/3*pi*a2*b2*c2;
X2 = a2*sin(u)*cos(v);%-2.5;
Y2 = b2*sin(u)*sin(v);
Z2 = c2*cos(u)*ones(size(v));%+0.25;
Z2(Z2>0)=0; % cut
h=1/3;
hS1=surf(X,Y,Z);
alpha(.11)
hold on
hS2=surf(X2,Y2,Z2);
hold off
axis([-20 20 -20 20 -20 20]);
for j = 1:20
k1=(sin(pi*j/20)+0.5)^h;
a=a*k1;
c=c*k1;
X = a*sin(u)*cos(v);
Y = a*sin(u)*sin(v);
Z = c*cos(u)*ones(size(v));
Z(Z>0)=0;
a2=a2*k1;
b2=a2*k1;
c2=c2*k1;
X2 = a2*sin(u)*cos(v)+5;%-2.5;
Y2 = b2*sin(u)*sin(v);
Z2 = c2*cos(u)*ones(size(v));%+0.25;
Z2(Z2>0)=0;
set(hS1,'XData',X,'YData',Y,'ZData',Z);
set(hS2,'XData',X2,'YData',Y2,'ZData',Z2);
drawnow;
F(j) = getframe;
end
movie(F,4)
The drawnow is not strictly necessary here because the getframe contains one, but it is good to insert one so that you can see what's going on if you remove the getframe.
OK, here's the shortcut to doing a trimmed surface I mentioned in my earlier comment. What you're going to do is put NaNs into the coordinates everywhere the surface is outside the domain. Then the surface object will drop any quads which touch those coordinates. That doesn't give you a nice clean edge, but it is really easy.
a=5;
b=a;
c=10;
u = (0:0.05*pi:pi)'; %'
v = [0:0.05*pi:2*pi];
X = a*sin(u)*cos(v);
Y = a*sin(u)*sin(v);
Z = c*cos(u)*ones(size(v));
Z(Z>0)=0; % cut upper
V1=4/3*pi*a*b*c;
d=1/2;
e=2^d;
a2=a/e;
b2=a/e;
c2=c;
V2=4/3*pi*a2*b2*c2;
X2 = a2*sin(u)*cos(v);%-2.5;
Y2 = b2*sin(u)*sin(v);
Z2 = c2*cos(u)*ones(size(v));%+0.25;
Z2(Z2>0)=0; % cut
h=1/3;
hS1=surf(X,Y,Z);
alpha(.11)
hold on
hS2=surf(X2,Y2,Z2);
hold off
axis([-20 20 -20 20 -20 20]);
for j = 1:20
k1=(sin(pi*j/20)+0.5)^h;
a=a*k1;
c=c*k1;
X = a*sin(u)*cos(v);
Y = a*sin(u)*sin(v);
Z = c*cos(u)*ones(size(v));
Z(Z>0)=0;
a2=a2*k1;
b2=a2*k1;
c2=c2*k1;
X2 = a2*sin(u)*cos(v)+5;%-2.5;
Y2 = b2*sin(u)*sin(v);
Z2 = c2*cos(u)*ones(size(v));%+0.25;
Z2(Z2>0)=0;
set(hS1,'XData',X,'YData',Y,'ZData',Z);
% substitute into implicit form of 1st ellipsoid
d = (X2.^2) / a^2 + (Y2.^2) / a^2 + (Z2.^2) / c^2;
% and zap any that are outside [0 1]
X2(d>1) = nan;
Y2(d>1) = nan;
Z2(d>1) = nan;
set(hS2,'XData',X2,'YData',Y2,'ZData',Z2);
drawnow;
F(j) = getframe;
end
movie(F,4)
Note that I also changed the range of u. That's because you were actually drawing 2 copies of the surface. That was causing some problems with how the transparency was rendered.