for loop not working

I have 2 frames of a video containing 700 balls.
I want to create a series of subimages, defined by the coordinates of the balls, so I have 700 individual ball images.
The code below generates 1 ball image, but I cannot get it to loop through the rest of the 699 ball coordinates.
Can anybody help me adapt this loop?
r = 20; %radius of whole ball
for k = 1:numFrames
%subimage the balls in frames 1 and 2 by shrinking them to the coordinates of the balls
%should produce a series of images (700 ish) showing each individual
%ball
subframe1 = frame_1(round(yb_1{k})-r:round(yb_1{k})+r, round(xb_1{k})-r:round(xb_1{k})+r); %Issue - not getting all balls
%check its worked
figure(4)
imshow(subframe1)
end

4 comentarios

Brent Kostich
Brent Kostich el 28 de Mayo de 2020
Is numFrames equal to 700? Or is numFrames a vector?
C.G.
C.G. el 29 de Mayo de 2020
numFrames is the number of frames in the video im working off, in this case it is 10
Rik
Rik el 29 de Mayo de 2020
Did you store the ball coordinates as yb_1, yb_2 etc? If you did, then you should really change that. It forces you to write horrible code, as you are discovering now.
C.G.
C.G. el 29 de Mayo de 2020
Somebody else wrote the first half of the code with the ball coordinates as yb_1 etc, and now im using that to do this next step

Iniciar sesión para comentar.

Respuestas (1)

Rik
Rik el 29 de Mayo de 2020

0 votos

Then you are stuck with first undoing that mistake. You should convert those coordinate variables to a single array. You can use two strategies:
  1. Save the workspace that contains all those numbered variables (with the save function), then use load to load to a struct. Now you can use S.(sprintf('yb_%d',ball_index)) to retrieve the values and store it in the array.
  2. Use eval to evaluate that char. Use it only once to fix this mistake. If you can fix the source that would be even better.
Now you have an array with the ball coordinates, so it is trivial to write an inner loop that loops over all the balls.
Why are numbered variables like this so bad? (I have taken the liberty of adapting some of Stephen Cobeldick's thoughts on this topic) Introspective programing (e.g. eval, assignin and other functions like it) is slow, and the MATLAB documentation warns specifically against it. Using eval is slow, makes debugging difficult, and removes all code helper tools (code checking, syntax hints, code completion, variable highlighting, etc.). A longer discussion can be found here. If you are not an expert and you think you need eval, there are probably better solutions to your problem. Also, if you are not an expert, you might find interesting and helpful hints on this page (and if you are an expert, you can still learn from it).

8 comentarios

C.G.
C.G. el 29 de Mayo de 2020
I appreciate your response but I am very new to Matlab.
The first part of the code, written by somebody else, I have put below. The ball centres are found, then the distance moved computed, then these assigned to coordinates.
Is there a way to adapt this part of the code to do what you suggest?
%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
%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;
%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
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);
%show
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
% show a velocity map for the ball translations
figure(3)
% Set graphics objects properties: size of the figure (x0,y0,width,height)
set(gcf,'position',[246,738,1701,210])
% This loop will now display what we have just made
for k = 1:numFrames-1;
subplot(1,2,1); %creates a figure with 2 plots side by side
scatter(xb_1{k},yb_1{k},20,velb_res{k})
hold on
subplot(1,2,2);
%displays RES as an image using the full range of colors where each element of RES corresponds to a rectangular area in the image
imagesc(RESb(:,:,k))
pause(1)
end
Rik
Rik el 29 de Mayo de 2020
Ah, this code makes more sense than yb_1, yb_2, yb_3, ... , yb_700.
Since the yb_1 variable looks like a vector, you should loop through the elements to crop every ball separately.
If you don't know how to implement this, I would strongly suggest doing a good Matlab tutorial like Onramp. It is worth it to invest some time in understanding the tools you're using, especially a tool so versatile as Matlab.
Brent Kostich
Brent Kostich el 29 de Mayo de 2020
I see that you are trying to take each center-point and grab the pixels around it, 20 in each coordinate direction. However, the xb_1, yb_1 vectors were generated using all ten frames, you are just applying your index filtering to frame1 (which doesn't seem to be defined precisely). So that will pose a problem to start with.
Like Rik said, you are probably trying to loop through the elements of yb_1 and xb_1 to extract all of the balls from a single frame.
For example, if you want 700 seperate images, your loop will have to run that many times. Here is a very generic place to start:
xb_coords = xb_1{1}; % the '1' index takes all x_coords from frame 1
yb_coords = yb_1{1}; % " y_coords "
num_balls = length(xb_coords); % the number of xb_coords is the number of balls detected
for i = 1:num_balls
ball_xcoord = xb_coords(i);
ball_ycoord = yb_coords(i);
% code that will extract image from each ball center x, y coordinates
% ...
end
Since you are very new to MATLAB let me encourage you by saying your goal is very attainable, but it will require you to understand the details of what you are doing. If you know the author of the code you shared above, I would recommend you reach out and have him/her explain the steps of that code if you do not understand it fully.
Start with a simple way to accomplish your task (like the example above), and worry about efficiency later.
C.G.
C.G. el 29 de Mayo de 2020
Thank you both for your responses. I understand what you are saying and I will try and see if I can work through your suggestions
C.G.
C.G. el 3 de Jun. de 2020
I have tried to implement your responses to get the code below, however it is still only giving me 1 image of 1 ball rather than looping through all the balls. It loops through the coordinates fine. Is there something i am missing to prevent it doing this?
thresholdvalue = 13; %use as threshold when binarizing
r = 22; %radius of whole ball; grab 20 pixels in each direction of the coordinates
xbcoords = xb_1{1};
ybcoords = yb_1{1};
numballs = length(xbcoords)
for i = 1:numballs
ball_xcoord(i) = xbcoords(i);
ball_ycoord(i) = ybcoords(i);
subframe1 = frame_1(round(ball_ycoord)-r:round(ball_ycoord)+r, round(ball_xcoord)-r:round(ball_xcoord)+r);
imshow(subframe1)
end
Rik
Rik el 3 de Jun. de 2020
It actually is looping through all balls, you just don't see it, because the imshow will overwrite the previous image.
C.G.
C.G. el 3 de Jun. de 2020
Editada: C.G. el 3 de Jun. de 2020
Ok I understand.
I need to be able to store each image that subframe1 produces in each iteration of the loop to use further down in my code, as from what you are saying it is being overwritten each time.
I know this involves indexing, but when i tried in the code below i get the following error. Do you have any succestions?
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
subframe1(i) = frame_1(round(ball_ycoord1)-r:round(ball_ycoord1)+r, round(ball_xcoord1)-r:round(ball_xcoord1)+r);
subframe2(i) = frame_2(round(ball_ycoord2)-r:round(ball_ycoord2)+r, round(ball_xcoord2)-r:round(ball_xcoord2)+r);
end
Unable to perform assignment because the left and right sides have a different
number of elements.
Rik
Rik el 3 de Jun. de 2020
How would you like them to be ordered? You can use montage, but then you have to make sure all subimages are in the same 4D array:
%before your loop:
montage_data=zeros(2*r+1,2*r+1,1,numballs);%row,col,color_channel,balls
%in your loop:
montage_data(:,:,:,i)=subframe1;

Iniciar sesión para comentar.

Etiquetas

Preguntada:

el 27 de Mayo de 2020

Comentada:

Rik
el 3 de Jun. de 2020

Community Treasure Hunt

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

Start Hunting!

Translated by