MATLAB is taking endless snapshots instead of a video, when trying to track colored object in webcam video.

So I am trying to track 2 different colored objects in a webcam video, one yellow tag and one red tag. I assigned the webcam to a variable, then in a loop I have taken a snapshot, thresholded for both yellow and red tags, and then done further calculations that are required for my task (found border, calculated centroids, distances between tags, plotting centroids and distances).
The issue I am facing is when I run the code, instead of it playing a video where I can move the tags and it tracks them, it is creating a separate figure for each snapshot, so if I let it run for 30 seconds, there will be over 50 different snapshots produced in separate figures. I need this to be in a video format.
Furthermore, it is not plotting the centroid/distance figures onto the output, but I am assuming this problem is related to the video issue.
P.S. please see my earlier 2 questions if need more context, as they are both related to this same task.
Thanks for any help in advance!
The code is as follows:
clear all;
close all;
clc;
w = webcam;
webcam('Microsoft® LifeCam HD-3000','RESOLUTION','1280x720')
r = 0;
while(1)
RGB = w.snapshot;
%Yellow Tag
yellowChannel1Min = 0.000;
yellowChannel1Max = 255.000;
yellowChannel2Min = 71.000;
yellowChannel2Max = 255.000;
yellowChannel3Min = 0.000;
yellowChannel3Max = 40.000;
yellowSliderBW = (RGB(:,:,1) >= yellowChannel1Min ) & (RGB(:,:,1) <= yellowChannel1Max) & ...
(RGB(:,:,2) >= yellowChannel2Min ) & (RGB(:,:,2) <= yellowChannel2Max) & ...
(RGB(:,:,3) >= yellowChannel3Min ) & (RGB(:,:,3) <= yellowChannel3Max);
yellowBW = yellowSliderBW;
yellowMorph = imclose(yellowBW, ones(25));
%Red Tag
redChannel1Min = 71.000;
redChannel1Max = 255.000;
redChannel2Min = 0.000;
redChannel2Max = 41.000;
redChannel3Min = 0.000;
redChannel3Max = 54.000;
redSliderBW = (RGB(:,:,1) >= redChannel1Min ) & (RGB(:,:,1) <= redChannel1Max) & ...
(RGB(:,:,2) >= redChannel2Min ) & (RGB(:,:,2) <= redChannel2Max) & ...
(RGB(:,:,3) >= redChannel3Min ) & (RGB(:,:,3) <= redChannel3Max);
redBW = redSliderBW;
redSE = strel("rectangle",[5 4]);
redOpen = imopen(redBW, redSE);
redMorph = imclose(redOpen, ones(25));
%Find Border of images
yellowBorder = rangefilt(yellowMorph);
redBorder = rangefilt(redMorph);
yellowOverlay = imoverlay(RGB,yellowBorder,'r');
redYellowOverlay = imoverlay(yellowOverlay,redBorder,'g');
%Calculate Red& Yellow Tag Centroid Coordinates
[ri,rj] = find(redMorph);
riMean = mean2(ri);
rjMean = mean2(rj);
[yi,yj] = find(yellowMorph);
yiMean = mean2(yi);
yjMean = mean2(yj);
%Calculate Distances
dCityBlock = (abs(riMean - yiMean)) + (abs(rjMean - yjMean));
dChessboard = max(abs(riMean - yiMean), abs(rjMean - yjMean));
dEuclidean = sqrt((riMean - yiMean)^2 + (rjMean - yjMean)^2);
figure;
imshow(redYellowOverlay);
hold on;
plot(rjMean,riMean, 'g-x', 'LineWidth',5)
plot(yjMean,yiMean, 'g-x', 'LineWidth',5)
plot([rjMean,yjMean],[riMean,yiMean],'b','LineWidth',2);
dim = [0.15 0.3 0.3 0];
str = {['Red Tag Centroid i coordinates = ',num2str(riMean)],['Red Tag Centroid j coordinates = ',num2str(rjMean)],['Yellow Tag Centroid i coordinates = ',num2str(yiMean)],['Yellow Tag Centroid j coordinates = ',num2str(yjMean)],['City Block Distance = ',num2str(dCityBlock)],['Chessboard Distance = ',num2str(dChessboard)],['Euclidean Distance = ',num2str(dEuclidean)]};
annotation('textbox',dim,'String',str,'FitBoxToText','on','EdgeColor','green','Color','green');
subplot(3,1,1);
imshow(RGB);
subplot(3,1,2);
imshow(RGB);
end

 Accepted Answer

Move call to figure() out of while loop. It opens a new window on each call in every iteration of the loop which is why your plots/images come on different windows. Also, once you create an image in a figure window you should try and call to the same parent axes. See calling imshow with parent property specified. imshow is slow. It would be smoother if you were to update underlying image data rather than calling imshow. E.g.:
Do the below once outside the loop
figure
h = imshow(IMG1);
when you need to update the image displayed to next frame, just do
h.CData = IMG2;
Regards

8 Comments

Hi, thank you for the help but do have couple of questions.
While moving the figure section to the bottom of the code, outside the while loop, this got rid of the border/centroid labels, is there something I have missed here?
Here are some examples, this is one of the snapshots created from the image from the original code (as you can tell this is figure 27) as original problem stated, while not in video format, it did achieve obtaining borders/centroids:
Here is a screenshot of the video, while it works getting the video running, there is no sign of border/centroid code being implemented onto the image, see here:
So just wondering if I have missed something key that will able me to keep the video format but not lose my centroid/distance/border implementation onto the image?
if you are still calling imshow in loop, you should call plot() on to the same axes after calling imshow since the latter will erase the existing axes in the figure and all children objects of the deleted axes are gone.
So I tried the plot() after remaining imshow() in the loop and it gave me the following error:
Error using plot
Not enough input arguments.
Error in TestScript (line 70)
plot()
If you think it is the better idea to move to your suggested:
h = imshow(IMG1);
h.CData = IMG2;
to fix this issue would you mind clarifying a bit? I'm just unsure what this means as I have no h, IMG1, IMG2 in my code.
Again this is my current code with last updates :
clear all;
close all;
clc;
w = webcam;
webcam('Microsoft® LifeCam HD-3000','RESOLUTION','1280x720')
while(1)
RGB = w.snapshot;
%Yellow Tag
yellowChannel1Min = 0.000;
yellowChannel1Max = 255.000;
yellowChannel2Min = 71.000;
yellowChannel2Max = 255.000;
yellowChannel3Min = 0.000;
yellowChannel3Max = 40.000;
yellowSliderBW = (RGB(:,:,1) >= yellowChannel1Min ) & (RGB(:,:,1) <= yellowChannel1Max) & ...
(RGB(:,:,2) >= yellowChannel2Min ) & (RGB(:,:,2) <= yellowChannel2Max) & ...
(RGB(:,:,3) >= yellowChannel3Min ) & (RGB(:,:,3) <= yellowChannel3Max);
yellowBW = yellowSliderBW;
yellowMorph = imclose(yellowBW, ones(25));
%Red Tag
redChannel1Min = 71.000;
redChannel1Max = 255.000;
redChannel2Min = 0.000;
redChannel2Max = 41.000;
redChannel3Min = 0.000;
redChannel3Max = 54.000;
redSliderBW = (RGB(:,:,1) >= redChannel1Min ) & (RGB(:,:,1) <= redChannel1Max) & ...
(RGB(:,:,2) >= redChannel2Min ) & (RGB(:,:,2) <= redChannel2Max) & ...
(RGB(:,:,3) >= redChannel3Min ) & (RGB(:,:,3) <= redChannel3Max);
redBW = redSliderBW;
redSE = strel("rectangle",[5 4]);
redOpen = imopen(redBW, redSE);
redMorph = imclose(redOpen, ones(25));
%Find Border of images
yellowBorder = rangefilt(yellowMorph);
redBorder = rangefilt(redMorph);
yellowOverlay = imoverlay(RGB,yellowBorder,'r');
redYellowOverlay = imoverlay(yellowOverlay,redBorder,'g');
%Calculate Red& Yellow Tag Centroid Coordinates
[ri,rj] = find(redMorph);
riMean = mean2(ri);
rjMean = mean2(rj);
[yi,yj] = find(yellowMorph);
yiMean = mean2(yi);
yjMean = mean2(yj);
%Calculate Distances
dCityBlock = (abs(riMean - yiMean)) + (abs(rjMean - yjMean));
dChessboard = max(abs(riMean - yiMean), abs(rjMean - yjMean));
dEuclidean = sqrt((riMean - yiMean)^2 + (rjMean - yjMean)^2);
plot()
imshow(RGB)
end
figure;
imshow(redYellowOverlay);
hold on;
plot(rjMean,riMean, 'g-x', 'LineWidth',5)
plot(yjMean,yiMean, 'g-x', 'LineWidth',5)
plot([rjMean,yjMean],[riMean,yiMean],'b','LineWidth',2);
dim = [0.15 0.3 0.3 0];
str = {['Red Tag Centroid i coordinates = ',num2str(riMean)],['Red Tag Centroid j coordinates = ',num2str(rjMean)],['Yellow Tag Centroid i coordinates = ',num2str(yiMean)],['Yellow Tag Centroid j coordinates = ',num2str(yjMean)],['City Block Distance = ',num2str(dCityBlock)],['Chessboard Distance = ',num2str(dChessboard)],['Euclidean Distance = ',num2str(dEuclidean)]};
annotation('textbox',dim,'String',str,'FitBoxToText','on','EdgeColor','green','Color','green');
r = 0;
Please consider IMG1, etc as examples or placeholders. Just suggesting the direction to proceed, not a replacement code. Sorry if this wasn't obvious 🙂
Keep your plot commands as they are with the variables rjmean etc. Just move them after imshow() calls.
Okay thanks, no problem!
And just to clarify on the 2nd point, I'm assuming you are refering to these lines following the figure call outside the loop?:
plot(rjMean,riMean, 'g-x', 'LineWidth',5)
plot(yjMean,yiMean, 'g-x', 'LineWidth',5)
plot([rjMean,yjMean],[riMean,yiMean],'b','LineWidth',2);
But these lines are already after the outside of the loop imshow(RGB); call am unsure what to change.
Regards.
You just have to move them after imshow calls not outside the loop, since they change with every frame and are being recomputed. Moving them outside will update them only once. The structure of the code must be like below if you want to see video with overlaid plotting updating with each frame.
while
:
:
imshow(..)
plot(..)
end
Follwing is a better way to do it:
imghandle = imshow(..);
overlayhandle = plot(..);
while
:
imghandle.CData = ... ;%variable with your new image frame
overlayhandle.XData = ... % variable with x-axis data that was in plot() call
overlayhandle.YData = ... % variable with y-axis data that was in plot() call
:
end
Just showing the structure above, it is not literally the code.
Great thank you, got it working perfectly now thanks to this.
Much appreciated!

Sign in to comment.

More Answers (0)

Products

Release

R2022b

Asked:

Ted
on 1 Dec 2022

Commented:

cr
on 5 Dec 2022

Community Treasure Hunt

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

Start Hunting!