I'm building a smile detection system, and I need to plot the probability of smile (from video input) like the graphs on the right in this video.
How can I do this in MATLAB?
Notes
I'm currently displaying the video frames with OpenCV & IntraFace default code, which looks something like this :
cf = 0; % Current Frame.
% create a figure to draw upon
S.fh = figure('units','pixels',...
'position',[100 150 frame_w frame_h],...
'menubar','none',...
'name','Smile Detector',...
'numbertitle','off',...
'resize','off',...
'renderer','painters');
% create axes
S.ax = axes('units','pixels',...
'position',[1 1 frame_w frame_h],...
'drawmode','fast');
set(S.fh,'KeyPressFcn',#pb_kpf);
S.im_h = imshow(zeros(frame_h,frame_w,3,'uint8'));
hold on;
S.frame_h = text(50,frame_h-50,['Frame ' int2str(cf)] , 'fontsize', 15, 'Color' , 'c');
while true && ~stop_pressed
tic;
im = cap.read;
cf = cf + 1;
if isempty(im),
warning('EOF');
break ;
end
set(S.im_h,'cdata',im); % update frame
set(S.frame_h , 'string' ,['Frame ' int2str(cf)]);
do_something_with_frame(im);
if isempty(output.pred) % if lost/no face, delete all drawings
if drawed, delete_handlers(); end
else % face found
update_GUI();
end
drawnow;
end
close;
end
And I want to add a live / moving graph like in the video. The graph will display a single value (a probability) between 0 and 1. And it should be updated with every new frame, therefore the plot should "flow" as the video flows.
What Have I Tried
I tried creating a new figure just like S in the code. But I cannot plot into it. I am also ok with adding the live graph in the same figure (S.fh), preferrably under the scene.
Using linkdata and refreshdata will refresh a graph plot as you have new data.
%some pretend data
pX1 = rand;
pX2 = 1-pX1;
p = [pX1,pX2];
bar(p)
%link the data to the plot
linkdata on
for i=1:100
pX1 = rand;
pX2 = 1-pX1;
p = [pX1,pX2];
%refresh the linked data and draw
refreshdata
drawnow
end
http://www.mathworks.co.uk/help/matlab/ref/linkdata.html
Hope it helps...
Related
I read a lot of answers here, but for some reason my animation still doesn't work as expected.
The axis range should vary from frame to frame. The 'Hurricane Center' caption should remain in the center all the time, but the captions from the previous frames must be erased. Also, I'm afraid that some of the data from previous parts remain.
I used hold on and draw now but it still happens.
The animation can be seen here:
Code:
v = VideoWriter('test_video.avi');
v.FrameRate = 4;
v.open()
hold on
for i=1:length(relevant(1,1,:))
if isempty(relevant) == 0
title('Lightning around Hurricane Jerry')
grid on
ylim([Interp_Jerry(i,2)-Radius Interp_Jerry(i,2)+Radius])
xlim([Interp_Jerry(i,3)-Radius Interp_Jerry(i,3)+Radius])
ylabel('latitude')
xlabel('longitude')
text(Interp_Jerry(i,3),Interp_Jerry(i,2),txt1);
scatter(relevant(:,3,i),relevant(:,2,i),'.');
drawnow
pause(0.1);
v.writeVideo(getframe(fig));
end
end
v.close()
The best of the two worlds:
v = VideoWriter('test_video.avi');
v.FrameRate = 4;
v.open()
hold on;
for i=1:length(relevant(1,1,:))
if ~isempty(relevant) % Corrected
if i == 1
% Prepare first plot and save handles of graphical objects
ht = text(Interp_Jerry(i,3),Interp_Jerry(i,2),txt1);
hold on;
hs = scatter(relevant(:,3,i),relevant(:,2,i),'.');
ylabel('latitude')
xlabel('longitude')
title('Lightning around Hurricane Jerry')
grid on
else
% Update graphical objects
set(ht, 'position', [Interp_Jerry(i,3), Interp_Jerry(i,2)]);
set(hs, 'XData', relevant(:,3,i) , 'YData' , relevant(:,2,i));
end
ylim([Interp_Jerry(i,2)-Radius Interp_Jerry(i,2)+Radius])
xlim([Interp_Jerry(i,3)-Radius Interp_Jerry(i,3)+Radius])
drawnow
pause(0.1);
v.writeVideo(getframe(fig));
end
end
v.close()
Instead of writing the text every time, just modify its position in the loop. Create a text object out side of the loop
t = text(position1, position2, txt);
in the loop change the position and if necessary the text
set(t, 'position', [new_position1, new_position2]);
If you don't want the previous data to remain, then you shouldn't use hold on... I think you should revise your code as follows:
v = VideoWriter('test_video.avi');
v.FrameRate = 4;
v.open();
fg = figure();
% Do not hold on, so that data is not retained frame-to-frame
for i=1:length(relevant(1,1,:))
% You don't need to test if 'relevant' is empty, since you're looping to its length!
% New plot
scatter(relevant(:,3,i),relevant(:,2,i),'.');
% Customise plot (labels / axes / text / ...)
title('Lightning around Hurricane Jerry')
ylabel('latitude')
xlabel('longitude')
ylim([Interp_Jerry(i,2)-Radius Interp_Jerry(i,2)+Radius]);
xlim([Interp_Jerry(i,3)-Radius Interp_Jerry(i,3)+Radius]);
text(Interp_Jerry(i,3),Interp_Jerry(i,2),txt1);
grid on;
drawnow;
% You don't need to pause whilst plotting, you already set the video framerate.
% pause(0.1);
v.writeVideo(getframe(fg));
end
v.close()
I have a matrix representing height of a 10x10 square grid over time. The height is updated in a for loop over the rows and columns. My attempt was to simply put the surf(height) within the this loop, with a 0.1 second pause between plots, because this is how I done it with a 2-d plot. How can I make this work?
I think the best way is to update the data directly from your surface plot.
To do so, assign it a handles (eg. named hSurf) during its creation and then update the ZData property using, for example,
set(hSurf,'ZData',SomeValues)
Sample code:
clear
clc
close all
figure(1)
%// Generate data
Z = peaks(25);
%// Create handles to access/modify data.
hSurf = surf(Z);
k = 0;
%// Set up name to create animated gif.
filename = 'AnimateSurf.gif';
%// Just a loop
while k < 10
%// IMPORTANT part. Update the Z data
set(hSurf,'ZData',k*Z);
%// Set limits so the graph looks nice.
zlim([-80 80])
drawnow
%// Capture frame to write to gif.
frame = getframe(1);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
if k == 0;
imwrite(imind,cm,filename,'gif', 'Loopcount',inf);
else
imwrite(imind,cm,filename,'gif','WriteMode','append');
end
pause(.15)
k = k+1;
end
And output:
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'm trying to design and program a GUI in Matlab, with which I'm not familiar.
Basically, I have two components, which are "axes" and "list box". There is an RGB image in the axes. I'm planning to add the selected point to the list box.
The following code works just fine, but I would like to make it work when the data cursor is on.
How can I make it work when the data cursor is on?
% 100x100x3 RGB image
RgbImage = randi(100, 100, 100, 3);
% Draw the image
axesHandle = axes();
imageHande = imagesc(RgbImage);
axis image;
% ButtonDownFc
set(imageHandle, 'ButtonDownFcn', #imageButtonDownFcn);
function imageButtonDownFcn(hObject, eventdata)
p = get(gca, 'CurrentPoint');
x = floor( p(1) );
y = floor( p(2) );
% Some code to add the [x y] to the list box
end
Edit 1:
The problem is that the function imageButtonDownFcn is not triggered when data cursor is on.
I would start by making a own update funktion for the data cursors
% in your main .m file
hdt = datacursormode;
set(hdt,'UpdateFcn',{#labeldtips,hdt});
Then you can get the position in that function like this:
function output_txt = labeldtips(obj,event_obj,hdt)
% Display an observation's Y-data and label for a data tip
% obj Currently not used (empty)
% event_obj Handle to event object
dcs=hdt.DataCursors;
pos = get(dcs(1),'Position'); %Position of 1st cursor
output_txt{1} = ['X: ', num2str(pos(1))];
output_txt{2} = ['Y: ', num2str(pos(2))]; %this is the text next to the cursor
end
then you have the position in pos and you can add %Some code to add the [x y] to the list box again
Try this for the part that is remaining in your code. Remember to edit "listbox1" to the tag used for listbox in your case -
contents = cellstr(get(handles.listbox1,'String'));
str1 = [ '[' num2str(x) ' ' num2str(y) ']' ];
contents(size(contents,1)+1,1) = {str1};
set(handles.listbox1,'String',contents);
Let us know if it works!
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]);