indexing in for loop

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
Rik on 4 Jun 2020
You should probably mention somewhere that this is a continuation of a previous question.
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.
C.G. on 4 Jun 2020
I know I need to use k and i within the loop to run for all the balls, in all the frames, but this is what I am struggling with. I have done the onramp tutorial and it makes sense, but applying this to my code is what I am struggling with.
I have tried your montage suggestion and the output is just 1 image (linked below) so i know the loop isnt working as i want it to as 662 balls should be imaged and montaged.
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.
This is now my code, however I get the error:
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);
end
end
subplot(2,1,1)
imshow(montage_data1(:,:,:,i))
subplot(2,1,2)
imshow(montage_data2(:,:,:,i))
Brace indexing is not supported for variables of this type.
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)
C.G.
C.G. on 4 Jun 2020
Edited: C.G. on 4 Jun 2020
which variable should be in the for i:1:numballs loop?
In the previous question you suggested a code such as this:
But when i now run that, I get the error of: Brace indexing is not supported for variables of this type. But if i change the variable to xb_1 {k}(i), then i get the error of: Index in position 1 exceeds array bounds (must not exceed 720), when the full code is run.
what do you mean where am I storing the subframes? Do I need to index in the lines of code that create the subframes?
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
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)
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
No problem, I understand what you've done now.
Ive put the code above into my original and when I run the lines for subframe1 and subframe 2, i get the error:
Index in position 1 exceeds array bounds (must not exceed 720).
The only way I have managed to avoid this error so far is when you suggested using xbcoords instead of xb_1.
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:
  1. 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?
  2. 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.
C.G. on 4 Jun 2020
I wrote the code you suggested 3 hours ago out line by line into my own script rather than copying and pasting, so your comment 2. never occured in my own script.
I used the debugger in the code as soon as the error occured, so I know where the error is, and this is caused by the variables ball_xcoord1 etc. only showing 1 value rather than looping through all the balls even though it is in a for loop.
Rik
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.

Sign in to comment.

Answers (0)

Tags

Asked:

on 4 Jun 2020

Commented:

Rik
on 4 Jun 2020

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!