indexing in for loop
Show older comments
I am working off a video of 662 continuously moving footballs and I want to track these through 10 frames in the video.
I have written the code which identifies all the balls using imfindcircles, and uses the coordinates of the centre of each ball to calculate distance moved and hence velocity between each frame.
The next step is for each of the 10 frames, crop all 662 balls into their own individual image using the coordinates if the ball centre. I have written the code for this below, however I cannot work out how to edit my for loop to:
1) Save the individual images it produces when cropping and not overprint the result. I know this involves indexing in the loop but I am not sure where to put the index.
2) Do the steps for all 10 frames in the video.
Can anybody provide any insight?
%% Input video
%Input video using videoreader command and name it 'obj'
obj = VideoReader('2D_football_video.mp4')
%% Define the start and end frames for all the tracking runs
%Define the frames between which particles are going to be tracked
start_frame = 1; %which frame of the video do you want it to start from
numFrames = 10; %which frame of the video do you want it to end on
%% Identify the whole ball in frames 1 and 2 track these through each video frame
% Identify the centres of each football in frames 1 and 2, and use these to
% track velcoity (translation)
%Define the radii of the circles to get MATLAB to search for
min_radiusball = 15;
max_radiusball = 25;
quality = .9; %quality is a number between 0-1 to see how strong a circle must be in order to be found. Values of 1 discard no circles
t = 0.1; % t is the frame rate, used as time in the velocity calculation
% Grid coarseness
n = 5; %spacing in the grid
% Here I am going to do all the same steps but in one move, this will be much more memory efficient.
tmpframe = read(obj,1); %read only the first frame from the video
% Meshgrid creates 2D grid coordinates with x and y coordinates defined by the length of the two inputted vectors
% Grids going from 1 to the length of tmpframe, in spacings of 5 (n)
[X,Y]=meshgrid(1:n:size(tmpframe,2),1:n:size(tmpframe,1));
%Track the whole balls and plot velocity for frames 1 and 2
for k = 1:numFrames; %loop through all the frames in the video
%binarize frames 1 and 2 from the video (using rgb2gray)
frame_1 = rgb2gray(read(obj,k+start_frame-1));
frame_2 = rgb2gray(read(obj,k+start_frame));
%identify the circles in frames 1 and 2 with radii between the defined min and max
%imfindcircles is a function in matlab that finds circles between a radius range
%radius is set to identify the whole ball
ballcentres_1 =imfindcircles(frame_1,[min_radiusball,max_radiusball],'Sensitivity',quality,'Method','TwoStage');
ballcentres_2 =imfindcircles(frame_2,[min_radiusball,max_radiusball],'Sensitivity',quality,'Method','TwoStage');
%identify where each circle has moved between frames 1 and 2
%returns the distance from each point in centres_2 to the corresponding point in centres_1
%indexb = indicies returned as a column vector containing the indicies
%of the data points closest to the query points
%distb = distance, returned as a column vector containing the euclidean
%distance between each query point and the closest input point
[indexb,distb] = dsearchn(ballcentres_2,ballcentres_1);
% here we have the distances not in order
% assign the circles from frames 1 and 2 to x and y coordinate variables
xb_1{k} = ballcentres_1(:,1);
xb_2{k} = ballcentres_2(indexb,1);
yb_1{k} = ballcentres_1(:,2);
yb_2{k} = ballcentres_2(indexb,2);
%check its assigned the centres correctly in each frame
subplot(2,1,1)
imshow(frame_1)
hold on
scatter(xb_1{k},yb_1{k},'r*')
subplot(2,1,2)
imshow(frame_2)
hold on
scatter(xb_2{k},yb_2{k},'b*')
% now we compute the translational velocity of each ball as v = d/t
velb_x{k} = (xb_2{k}-xb_1{k})/t; % x velocity using frame 2 - frame 1
velb_y{k} = (yb_2{k}-yb_1{k})/t; % y velocity using frame 2 - frame 1
velb_res{k} = sqrt(velb_x{k}.^2 + velb_y{k}.^2); % the final velocity vector as a function as its x and y components
% now we can make a overall velocity map, by reshaping the array
% for all the columns in 'loop', reshape the array 'griddata' to define, size U, V and RES
Ub(:,:,k)=reshape(griddata(xb_1{k},yb_1{k},velb_x{k},X(:),Y(:)),size(X,1),size(X,2));
Vb(:,:,k)=reshape(griddata(xb_1{k},yb_1{k},velb_y{k},X(:),Y(:)),size(X,1),size(X,2));
RESb(:,:,k)=reshape(griddata(xb_1{k},yb_1{k},velb_res{k},X(:),Y(:)),size(X,1),size(X,2));
end
% Code works properly up to here
%% Crop the images to only 1 ball per image and run the code again
% We identified the movement of the whole ball (translation), now we want to identify the
% movement of the pentagons (rotation).
% Want to crop each of the balls in frame 1 and 2 to its their own individual image
% Should end up with 662 individual images for each sub frame
% Once they have been cropped, the code can be reapplied to identify the
% centres of the pentagons.
% ISSUE - the code should subimage 662 balls in each of the 10 video frames, currently it only produces 1 image
thresholdvalue = 13; %use as threshold when binarizing
r = 23; %radius of whole ball; grab 20 pixels in each direction of the coordinates
xbcoords1 = xb_1{1}; % the '1' index tabkes all the x_coords from frame 1
ybcoords1 = yb_1{1};
xbcoords2 = xb_2{2}; % the '2' index tabkes all the x_coords from frame 2
ybcoords2 = yb_2{2};
numballs = length(xbcoords1); %define the number of balls in the frame by the number of centroids found
for k = 1:numFrames
for i = 1:numballs %for all the balls identified
ball_xcoord1(i) = xbcoords1(i);
ball_ycoord1(i) = ybcoords1(i);
ball_xcoord2(i) = xbcoords2(i);
ball_ycoord2(i) = ybcoords2(i);
% crop the frame down to a series of coordinates, and do this for each
% ball's coordinates to end up with 662 individual images, for each frame in the video
subframe1 = frame_1(round(ball_ycoord1)-r:round(ball_ycoord1)+r, round(ball_xcoord1)-r:round(ball_xcoord1)+r);
subframe2 = frame_2(round(ball_ycoord2)-r:round(ball_ycoord2)+r, round(ball_xcoord2)-r:round(ball_xcoord2)+r);
end
end
11 Comments
Rik
on 4 Jun 2020
Your final loop doesn't use the value of k anywhere (so it doesn't load the next frame), nor does it attempt to make a montage like I suggested in your other question.
C.G.
on 4 Jun 2020
Rik
on 4 Jun 2020
You probably need to store the actual frames. The variable names frame_1 and frame_2 are misleading, as those are actually the previous frame and the current frame.
With the code below you could also choose to switch the two loops.
for k = 1:numFrames
frame_1 = rgb2gray(read(obj,k+start_frame-1));
frame_2 = rgb2gray(read(obj,k+start_frame));
for i = 1:numballs %for all the balls identified
ball_xcoord1 = xb_1{k}(i);
ball_ycoord1 = yb_1{k}(i);
ball_xcoord2 = xb_2{k}(i);
ball_ycoord2 = yb_2{k}(i);
%now you can create the subframes properly
end
end
As you haven't shown the code you used to create that montage, I can't comment on it. I did notice that the resolution of that images doesn't look like 61x61 pixels for 662 balls.
C.G.
on 4 Jun 2020
Rik
on 4 Jun 2020
If you edit my code, make sure to edit it correctly. xbcoords1 is a different variable from xb_1.
And where are you storing the subframes into the montage?
montage_data1=zeros(2*r+1,2*r+1,1,numballs);
montage_data2=zeros(2*r+1,2*r+1,1,numballs);
for k = 1:numFrames
frame_1 = rgb2gray(read(obj,k+start_frame-1));
frame_2 = rgb2gray(read(obj,k+start_frame));
for i = 1:numballs %for all the balls identified in each frame
ball_xcoord1 = xbcoords1{k}(i);
ball_ycoord1 = ybcoords1{k}(i);
ball_xcoord2 = xbcoords2{k}(i);
ball_ycoord2 = ybcoords2{k}(i);
% crop the frame down to a series of coordinates, and do this for each
% ball's coordinates to end up with 662 individual images
subframe1 = frame_1(round(ball_ycoord1)-r:round(ball_ycoord1)+r, round(ball_xcoord1)-r:round(ball_xcoord1)+r);
subframe2 = frame_2(round(ball_ycoord2)-r:round(ball_ycoord2)+r, round(ball_xcoord2)-r:round(ball_xcoord2)+r);
montage_data1(:,:,:,i)=subframe1;
montage_data2(:,:,:,i)=subframe2;
end
end
subplot(2,1,1)
montage(montage_data1)
subplot(2,1,2)
montage(montage_data2)
Rik
on 4 Jun 2020
Ah, sorry, I'm didn't fully correct the code. I also removed numballs as a variable, because apparently the number of balls in each frame is not constant.
What I meant with storing the subframes is that you were calculating the subframe variables, but you weren't doing anything with them. The montaga_data variables will be overwritten for every frame in the code you posted, so I adjusted that as well.
for k = 1:numFrames
montage_data1=zeros(2*r+1,2*r+1,1,numballs);
montage_data2=zeros(2*r+1,2*r+1,1,numballs);
frame_1 = rgb2gray(read(obj,k+start_frame-1));
frame_2 = rgb2gray(read(obj,k+start_frame));
for i = 1:numel(xb_1{k}) %for all the balls identified in each frame
ball_xcoord1 = xb_1{k}(i);
ball_ycoord1 = xb_1{k}(i);
ball_xcoord2 = xb_1{k}(i);
ball_ycoord2 = xb_1{k}(i);
% crop the frame down to a series of coordinates, and do this for each
% ball's coordinates to end up with 662 individual images
subframe1 = frame_1(round(ball_ycoord1)-r:round(ball_ycoord1)+r, round(ball_xcoord1)-r:round(ball_xcoord1)+r);
subframe2 = frame_2(round(ball_ycoord2)-r:round(ball_ycoord2)+r, round(ball_xcoord2)-r:round(ball_xcoord2)+r);
montage_data1(:,:,:,i)=subframe1;
montage_data2(:,:,:,i)=subframe2;
end
figure(k),clf(k)
subplot(2,1,1)
montage(montage_data1)
title(sprintf('frame %d montage',k-1))
subplot(2,1,2)
montage(montage_data2)
title(sprintf('frame %d montage',k ))
end
C.G.
on 4 Jun 2020
Rik
on 4 Jun 2020
There is another way to avoid the error: replace all the code you have with disp('Yay! :)'). What I mean with this: that your edit avoids the error doesn't mean it does what it should be doing. Since there is a separate cell created in xb_1 for each frame in your initial loop, you can't just use the first two.
Also:
- You really need to try to understand where your error is comming from. Look at the code. Why does it error? Put a breakpoint just before it. What are the values of your variables at that point? Does that match your intuition?
- I must be tired, because I made a mistake, which you would have caught if you put the advice from 1. into practice.
ball_xcoord1 = xb_1{k}(i);
ball_ycoord1 = xb_1{k}(i);%why is this the same as the line above?
ball_xcoord2 = xb_1{k}(i);%surely that can't be correct?
ball_ycoord2 = xb_1{k}(i);%why would y be coming from a variable with the name x?
%so:
ball_xcoord1 = xb_1{k}(i);
ball_ycoord1 = yb_1{k}(i);
ball_xcoord2 = xb_2{k}(i);
ball_ycoord2 = yb_2{k}(i);
C.G.
on 4 Jun 2020
Rik
on 4 Jun 2020
The whole point is that ball_xcoord1 etc are scalars. That makes them easy to work with. Every ball has only 1 x and 1 y in each frame. That is what you are extracting.
Answers (0)
Categories
Find more on Image Arithmetic 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!