You are now following this question
- You will see updates in your followed content feed.
- You may receive emails, depending on your communication preferences.
how to make Geometric shapes detection?
18 views (last 30 days)
Show older comments
i have popup menu to detect shapes. for example, i need when i click circle, just circles will show in rgb image on axes2. i am new to this, so couldn't arrange anything. Can anyone help me? i upload my codes. i think, you can understand exactly what i mean, when you check color menu on my codes. thank you.
1 Comment
Image Analyst
on 27 Dec 2015
You forgot to attach the image.
Accepted Answer
Image Analyst
on 29 Dec 2015
mete, because you did not attach your image, to answer your question I had to write a program to draw random shapes. Please see attached demo. It creates an image with random shapes and then computes the circularity of the shapes and assigns the shape to the shape with the theoretical circularity that is closest to the shape's actual circularity. Someday I hope to add another method where I find and count peaks in the centroid to perimeter distance. For now, it will create an image like this:
9 Comments
Image Analyst
on 31 Dec 2015
Please use this improved and updated demo instead. I deleted the old one.
mete polat
on 31 Dec 2015
i didn't attach any any photo because it will be uploaded any time different one. i think if you try color menu in my codes, you will understand what i mean. Thank you for respond.
Image Analyst
on 31 Dec 2015
I don't understand the grammar of this sentence: "i didn't attach any any photo because it will be uploaded any time different one". Does that mean that you will, or will not, upload an image at any time in the future so that I can help you?
You asked "i need when i click circle, just circles will show in rgb image on axes2", which I have shown you. If you need to use that binary image of circles as a mask, then you can do this:
% Mask the image using bsxfun() function
maskedRgbImage = bsxfun(@times, rgbImage, cast(mask, 'like', rgbImage));
Otherwise explain in more detail what you're asking now. Like are you really doing color segmentation instead of shape segmentation?
mete polat
on 31 Dec 2015
the images which will use will be uploaded from any pc. the program shouldn't work on just one image. but if you need,
you can use this one.
Image Analyst
on 31 Dec 2015
First turn your image into a binary image so that all you have to worry about are the shapes. Then use my shapes code on that binary image.
%===============================================================================
% Read in a color shapes demo image.
folder = pwd;
baseFileName = 'test.bmp';
% Get the full filename, with path prepended.
fullFileName = fullfile(folder, baseFileName);
if ~exist(fullFileName, 'file')
% Didn't find it there. Check the search path for it.
fullFileName = baseFileName; % No path this time.
if ~exist(fullFileName, 'file')
% Still didn't find it. Alert user.
errorMessage = sprintf('Error: %s does not exist.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
rgbImage = imread(fullFileName);
% Get the dimensions of the image. numberOfColorBands should be = 3.
[rows, columns, numberOfColorChannels] = size(rgbImage);
% Display the original color image.
subplot(2, 2, 1);
imshow(rgbImage);
title('Original Color Image', 'FontSize', fontSize, 'Interpreter', 'None');
% Enlarge figure to full screen.
set(gcf, 'Units', 'Normalized', 'Outerposition', [0, 0, 1, 1]);
% Extract the individual red, green, and blue color channels.
redChannel = rgbImage(:, :, 1);
greenChannel = rgbImage(:, :, 2);
blueChannel = rgbImage(:, :, 3);
% Get the color of the background from the upper left pixel
backgroundRGB = impixel(rgbImage, 1,1)
% Get a binary image so that we just need to worry about shapes, not colors.
binaryImage = ~(redChannel == backgroundRGB(1) & greenChannel == backgroundRGB(2) & blueChannel == backgroundRGB(3));
% Display the binary image.
subplot(2, 2, 2);
imshow(binaryImage);
title('Binary Image', 'FontSize', fontSize, 'Interpreter', 'None');
mete polat
on 1 Jan 2016
Thank you it is working :D
Beenish Ishtiaq
on 5 Aug 2021
Need code for above 15 shapes detetction image
Beenish Ishtiaq
on 7 Aug 2021
Error in ''catch''
Image Analyst
on 7 Aug 2021
@Beenish Ishtiaq I did give him code and he said it was working. Not sure how to help you unless you start a new question with your own images and own code, or my code that you modified.
More Answers (1)
Anindya Banerjee
on 17 May 2019
@image analyst your code is not working! in the line pwd its showing error..can you please fix it?
12 Comments
Image Analyst
on 17 May 2019
Unfortunately you forgot to include your error message (ALL the red text), thus delaying a solution for you.
It works for everyone else. Just try
>> folder = pwd
in your command window and tell us what it says. pwd is a built in function and it should work for you, and everyone.
Anindya Banerjee
on 18 May 2019
after changing the basefile name to target image, the error i got are,
Undefined function or variable 'fontSize'.
Error in u3 (line 23)
title('Original Color Image', 'FontSize', fontSize, 'Interpreter', 'None');
Image Analyst
on 19 May 2019
Edited: Image Analyst
on 19 May 2019
My full demo is now "hidden" in the comments above. Perhaps what you saw was just a snippet in one of the exposed comments. Unhide the comments and look where I attached the m-file. Anyway, I'll attach it here also.
You're using a snippet of code where fontSize is not defined. In my full m-file that I'm attaching (here, and before) you'll see that fontSize is defined, but in your code it is not.
Anyway, when you see such an error you should realize that that MATLAB function, title(), is using the very common "Name, Value" pairing that almost all functions use. And fontSize is the value. Since the error message is telling you it's not defined, you shoudl define it. You can set it to 12 or 15 or 20 or whatever size you want. For example:
fontSize = 20; % Define this before you call title().
Anindya Banerjee
on 19 May 2019
Edited: Anindya Banerjee
on 19 May 2019
your code works absolutely fine.thanks.But if we need to detect a image from our choice is this code is efficient enough for that?
Image Analyst
on 19 May 2019
Not sure what your choice is, and how you want to "detect a image from our choice". How many images do you have and what would make you detect one image over another?
My demo works with the synthesized images it creates. If your images or "choices" are different than that then you may need to adapt the code to work with your situation.
Anindya Banerjee
on 20 May 2019
Edited: Image Analyst
on 20 May 2019
I meant to say that suppose I want to use this code to detect shapes for any arbitary image from local directory. In that case,what part of the code should be changed?
Image Analyst
on 20 May 2019
See where it says:
% Now create a demo image.
[binaryImage, numSidesCircularity] = CreateDemoImage();
Replace that code with whatever code you want to create a binary image, for example to read in your image with imread() and call imbinarize() or however you want to do it.
Anindya Banerjee
on 25 Jun 2019
I replaced the code from from
% Now create a demo image.
[binaryImage, numSidesCircularity] = CreateDemoImage();
to-
binaryImage= imread('C:\Users\Hunter\Desktop\C7.bmp');
But still my shapes in the image does not get detected
the error i got are,
Error in function parse_inputs() at line 206.
Error Message:
Data set must contain at least 3 samples.
Error in function u3() at line 54.
Error Message:
Index exceeds matrix dimensions.
>>
also got 2 warning dialog,please help me out!
Image Analyst
on 26 Jun 2019
You forrgot to attach c7.bmp. Chances are it's a gray scale or RGB color image, not a binary image. You'll need to threshold it or something
%=======================================================================================
% Read in demo image.
grayImage = imread(fullFileName);
% Get the dimensions of the image.
[rows, columns, numberOfColorChannels] = size(binaryImage)
if numberOfColorChannels > 1
grayImage = rgb2gray(grayImage);
end
% Convert to binary by thresholding
binaryImage = grayImage < someThreshold; % You need to replace someThreshold with some actual value.
% Display image.
subplot(2, 3, 2);
imshow(binaryImage, []);
impixelinfo;
axis('on', 'image');
caption = sprintf('Original image: %d rows by %d columns', rows, columns);
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
hp = impixelinfo(); % Set up status line to see values when you mouse over the image.
Anindya Banerjee
on 26 Jun 2019
nothing seems to be improved! here is my entire code along with the image file c7.bmp
% Demo to find certain shapes in an image based on their shape (circularities) and number of vertices.
function shape_recognition_demo1
try
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
workspace; % Make sure the workspace panel is showing.
fontSize = 15;
% For reference, compute the theoretical circularity of a bunch of regular polygons
% with different number of sides starting with 3 (triangle).
dividingValues = PlotTheoreticalCircularity;
% Make the last dividing value infinity because any circularity from .99999 up to inifinity should be a circle.
% and sometimes you have a circularity more than 1 due to quantization errors.
dividingValues(end) = inf;
% Now create a demo image.
binaryImage= imread('C:\Users\Hunter\Desktop\C7.bmp');
% Count the number of shapes
[~, numShapes] = bwlabel(binaryImage);
%=======================================================================================
% Read in demo image.
grayImage = imread('C:\Users\Hunter\Desktop\C7.bmp');
% Get the dimensions of the image.
[rows, columns, numberOfColorChannels] = size(binaryImage)
if numberOfColorChannels > 1
grayImage = rgb2gray(grayImage);
end
% Convert to binary by thresholding
binaryImage = grayImage < 120; % You need to replace someThreshold with some actual value.
% Display image.
subplot(2, 3, 2);
imshow(binaryImage, []);
impixelinfo;
axis('on', 'image');
caption = sprintf('Original image: %d rows by %d columns', rows, columns);
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
hp = impixelinfo(); % Set up status line to see values when you mouse over the image.
% Display the polygon demo image.
subplot(1, 2, 1);
imshow(binaryImage);
caption = sprintf('Image with %d Shapes', numShapes);
title(caption, 'FontSize', fontSize);
hold on; % So that text labels won't blow away the image.
% Set up figure properties:
% Enlarge figure to full screen.
set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0 1 1]);
% Get rid of tool bar and pulldown menus that are along top of figure.
% set(gcf, 'Toolbar', 'none', 'Menu', 'none');
% Give a name to the title bar.
set(gcf, 'Name', 'Demo by ImageAnalyst', 'NumberTitle', 'Off')
drawnow; % Make it display immediately.
[labeledImage, numberOfObjects] = bwlabel(binaryImage);
blobMeasurements = regionprops(labeledImage, 'Perimeter', 'Area', 'Centroid', 'Image');
% Now compute the number of vertices by looking at the number of peaks in a plot of distance from centroid.
numSidesDistance = FindNumberOfVertices(blobMeasurements, labeledImage);
% Get all the measurements into single arrays for convenience.
allAreas = [blobMeasurements.Area];
allPerimeters = [blobMeasurements.Perimeter];
circularities = (4 * pi * allAreas) ./ allPerimeters.^2
% Sort in order of increasing circularity
[sortedCircularities, sortOrder] = sort(circularities, 'Ascend');
% Sort all the measurements in the same way.
blobMeasurements = blobMeasurements(sortOrder);
allAreas = allAreas(sortOrder);
allPerimeters = allPerimeters(sortOrder);
numSidesDistance = numSidesDistance(sortOrder);
% Plot a bar chart of the circularities.
subplot(1, 2, 2);
bar(sortedCircularities);
ylim([0.55, 1.1]);
grid on;
title('Actual Measured Circularities', 'FontSize', fontSize);
% Let's compute areas a different way. The "Area" returned by regionprops is a count of the number of pixels.
% This sometimes overestimates the area. Let's use bwarea, which computes the area on a
% pixel-center to pixel center basis.
for k = 1 : numberOfObjects
thisBlob = blobMeasurements(k).Image;
allBwAreas(k) = bwarea(thisBlob);
end
bwCircularities = (4 * pi * allBwAreas) ./ allPerimeters.^2
sortedCircularities = bwCircularities
% Put up red horizontal lines at the dividing values
hold on;
xl = xlim();
for k = 1 : length(numSidesCircularity)-1
thisSideLength = numSidesCircularity(k);
thisDividingValue = dividingValues(thisSideLength);
line(xl, [thisDividingValue, thisDividingValue], 'Color', 'r');
% For the first 6, print the dividing value at the left just above the line.
% After 6 it would get too crowded
if k <= 6
theLabel = sprintf('Dividing value = %.4f', thisDividingValue);
text(xl(1)+0.1, thisDividingValue + 0.005, theLabel, 'Color', 'r');
end
end
% Explain why the labels may not be accurate.
message = sprintf('Before we start classifying the shapes,\nnote that the circularity may deviate from the theoretical circularity\ndepending on the size, rotation, and the algorithm\nused to compute area and perimeter.');
fprintf('%s\n', message);
uiwait(helpdlg(message));
% Say what they are, one by one.
subplot(1, 2, 1);
for blobNumber = 1 : numberOfObjects
%==============================================================
% Determine the number of sizes according to the circularity
% Get the circularity of this specific blob.
thisCircularity = sortedCircularities(blobNumber);
% See which theoretical dividing value it's less than.
% This will determine the number of sides it has.
numSidesCircularity = find(thisCircularity < dividingValues, 1, 'first');
% Assign a string naming the shape according to the distance algorithm.
if numSidesCircularity == 3
% Blob has 3 sides.
theShapeCirc = 'triangle';
elseif numSidesCircularity == 4
% Blob has 4 sides.
theShapeCirc = 'square';
elseif numSidesCircularity == 5
% Blob has 5 sides.
theShapeCirc = 'pentagon';
elseif numSidesCircularity == 6
% Blob has 6 sides.
theShapeCirc = 'hexagon';
else
% Blob has 7 or more sides.
theShapeCirc = 'nearly circular';
end
%==============================================================
% Determine the number of sides according to the centroid-to-perimeter algorithm
% Classify the shape by the centroid-to-perimeter algorithm which seems to be more accurate than the circularity algorithm.
numSidesDist = numSidesDistance(blobNumber);
% Assign a string naming the shape according to the distance algorithm.
if numSidesDist == 3
% Blob has 3 sides.
theShapeDistance = 'triangle';
elseif numSidesDist == 4
% Blob has 4 sides.
theShapeDistance = 'square';
elseif numSidesDist == 5
% Blob has 5 sides.
theShapeDistance = 'pentagon';
elseif numSidesDist == 6
% Blob has 6 sides.
theShapeDistance = 'hexagon';
else
% Blob has 7 or more sides.
theShapeDistance = 'nearly circular';
end
% Place a label on the shape
xCentroid = blobMeasurements(blobNumber).Centroid(1);
yCentroid = blobMeasurements(blobNumber).Centroid(2);
blobLabel = sprintf('#%d = %s', blobNumber, theShapeDistance);
plot(xCentroid, yCentroid, 'r+', 'LineWidth', 2, 'MarkerSize', 15);
text(xCentroid+20, yCentroid, blobLabel, 'FontSize', fontSize, 'Color', 'r', 'FontWeight', 'Bold');
% Inform the user what the circularity and shape are.
distanceMessage = sprintf('The centroid-to-perimeter algorithm predicts shape #%d has %d sides, so it predicts the shape is a %s', blobNumber, numSidesDistance(blobNumber), theShapeDistance);
circMessage = sprintf('The circularity of object #%d is %.3f, so the circularity algorithm predicts the object is a %s shape.\nIt is estimated to have %d sides.\n(Range for %s is [%.4f - %.4f].)',...
blobNumber, thisCircularity, theShapeCirc, numSidesCircularity, ...
theShapeDistance, dividingValues(numSidesCircularity - 1), dividingValues(numSidesCircularity));
% See if the number of sides determined each way agrees with each other.
if numSidesDistance(blobNumber) == numSidesCircularity
agreementMessage = sprintf('For blob #%d, the two algorithms agree on %d sides.', blobNumber, numSidesCircularity);
else
agreementMessage = sprintf('For blob #%d there is disagreement.', blobNumber);
end
% Combine all messages into one.
promptMessage = sprintf('%s\n\n%s\n\n%s', distanceMessage, circMessage, agreementMessage);
fprintf('%s\n', promptMessage);
% Give user an opportunity to bail out if they want to.
titleBarCaption = 'Continue?';
button = questdlg(promptMessage, titleBarCaption, 'Continue', 'Quit', 'Continue');
if strcmpi(button, 'Quit')
return;
end
end
uiwait(helpdlg('Done with demo!'));
catch ME
errorMessage = sprintf('Error in function %s() at line %d.\n\nError Message:\n%s', ...
ME.stack(1).name, ME.stack(1).line, ME.message);
fprintf(1, '%s\n', errorMessage);
uiwait(warndlg(errorMessage));
end
%----------------------------------------------------------------------------------------------------------------------------------
% Creates an image with a specified number of circles, triangles, rectangles, and pentagons
function [binaryImage, numSides] = CreateDemoImage()
try
rows = 800;
columns = round(rows * 3/4); % 4/3 aspect ratio.
figure;
% Create an image and add in some triangles at various angles and various sizes.
binaryImage = false(rows, columns);
numShapesToPlace = 3;
for numSides = 3 : 6
shapesPlacedSoFar = 0;
centroidToVertexDistance = [30, 75];
% Define fail-safe parameters.
maxNumberOfAttempts = 50;
numberOfAttempts = 0;
while shapesPlacedSoFar < numShapesToPlace && numberOfAttempts < maxNumberOfAttempts
thisBinaryImage = CreatePolygon(numSides, centroidToVertexDistance, rows, columns);
% Sometimes two polygons will be next to each other but not overlapping.
% However bwlabel() and bwconncomp() would consider those two regions as being the same region.
% To check for and prevent that kind of situation (which happened to me once),
% we need to dilate the binary image by one layer before checking for overlap.
dilatedImage = imdilate(thisBinaryImage, true(9));
% See if any pixels in this binary image overlap any existing pixels.
overlapImage = binaryImage & dilatedImage;
if ~any(overlapImage(:))
% No pixels overlap, so OR in this image.
binaryImage = binaryImage | thisBinaryImage;
shapesPlacedSoFar = shapesPlacedSoFar + 1;
else
fprintf('Skipping attempt %d because of overlap.\n', numberOfAttempts);
end
numberOfAttempts = numberOfAttempts + 1;
end
end
% Create an image and add in some circles at various angles and various sizes.
numShapesToPlace = 3;
shapesPlacedSoFar = 0;
numSides = 30; % Pretty round
centroidToVertexDistance = [30, 75];
while shapesPlacedSoFar < numShapesToPlace && numberOfAttempts < maxNumberOfAttempts
thisBinaryImage = CreatePolygon(numSides, centroidToVertexDistance, rows, columns);
% See if any pixels in this binary image overlap any existing pixels.
overlapImage = binaryImage & thisBinaryImage;
if ~any(overlapImage(:))
% No pixels overlap, so OR in this image.
binaryImage = binaryImage | thisBinaryImage;
shapesPlacedSoFar = shapesPlacedSoFar + 1;
end
numberOfAttempts = numberOfAttempts + 1;
end
% Pass back the number of sides we decided to use.
numSides = [3:6, 30];
catch ME
errorMessage = sprintf('Error in function %s() at line %d.\n\nError Message:\n%s', ...
ME.stack(1).name, ME.stack(1).line, ME.message);
fprintf(1, '%s\n', errorMessage);
uiwait(warndlg(errorMessage));
end
%----------------------------------------------------------------------------------------------------------------------------------
% Create a single polygon with the specified number of sides in a binary image of the specified number of rows and columns.
% centroidToVertexDistance is the distance from the centroid to each vertex.
% If centroidToVertexDistance is a length 2 vector, then this indicated the minimum and maximum size range and
% it will create a random size polygon between the min and max distance.
function binaryImage = CreatePolygon(numSides, centroidToVertexDistance, rows, columns)
try
% Get the range for the size from the center to the vertices.
if length(centroidToVertexDistance) > 1
% Random size between a min and max distance.
minDistance = centroidToVertexDistance(1);
maxDistance = centroidToVertexDistance(2);
else
% All the same size.
minDistance = centroidToVertexDistance;
maxDistance = centroidToVertexDistance;
end
thisDistance = (maxDistance - minDistance) * rand(1) + minDistance;
% Create a polygon around the origin
for v = 1 : numSides
angle = v * 360 / numSides;
x(v) = thisDistance * cosd(angle);
y(v) = thisDistance * sind(angle);
end
% Make last point the same as the first
x(end+1) = x(1);
y(end+1) = y(1);
% plot(x, y, 'b*-', 'LineWidth', 2);
% grid on;
% axis image;
% Rotate the coordinates by a random angle between 0 and 360
angleToRotate = 360 * rand(1);
rotationMatrix = [cosd(angleToRotate), sind(angleToRotate); -sind(angleToRotate), cosd(angleToRotate)];
% Do the actual rotation
xy = [x', y']; % Make a numSides*2 matrix;
xyRotated = xy * rotationMatrix; % A numSides*2 matrix times a 2*2 = a numSides*2 matrix.
x = xyRotated(:, 1); % Extract out the x as a numSides*2 matrix.
y = xyRotated(:, 2); % Extract out the y as a numSides*2 matrix.
% Get a random center location between centroidToVertexDistance and (columns - centroidToVertexDistance).
% This will ensure it's always in the image.
xCenter = thisDistance + (columns - 2 * thisDistance) * rand(1);
% Get a random center location between centroidToVertexDistance and (rows - centroidToVertexDistance).
% This will ensure it's always in the image.
yCenter = thisDistance + (rows - 2 * thisDistance) * rand(1);
% Translate the image so that the center is at (xCenter, yCenter) rather than at (0,0).
x = x + xCenter;
y = y + yCenter;
binaryImage = poly2mask(x, y, rows, columns);
catch ME
errorMessage = sprintf('Error in function %s() at line %d.\n\nError Message:\n%s', ...
ME.stack(1).name, ME.stack(1).line, ME.message);
fprintf(1, '%s\n', errorMessage);
uiwait(warndlg(errorMessage));
end
%----------------------------------------------------------------------------------------------------------------------------------
% https://en.wikipedia.org/wiki/Regular_polygon
% Which says A = (1/4) * n * s^2 * cot(pi/n)
function circularity = ComputeTheoreticalCircularity(numSides)
try
sideLength = 1;
perimeter = numSides * sideLength;
area = (1/4) * numSides * sideLength^2 / tan(pi / numSides);
circularity = (4 * pi * area) / perimeter ^2;
catch ME
errorMessage = sprintf('Error in function %s() at line %d.\n\nError Message:\n%s', ...
ME.stack(1).name, ME.stack(1).line, ME.message);
fprintf(1, '%s\n', errorMessage);
uiwait(warndlg(errorMessage));
end
%----------------------------------------------------------------------------------------------------------------------------------
% Makes a figure with the theoretical circularity for a bunch of different number of sides plotted.
function dividingValues = PlotTheoreticalCircularity()
try
dividingValues = []; % Initialize
fontSize = 24;
% For reference, compute the theoretical circularity of a bunch of regular polygons with different number of sides.
fprintf('Number of Sides Theoretical Circularity\n');
% Define an array with the number of sides we want to compute the circularity for.
numSides = 3 : 16;
for k = 1 : length(numSides)
thisSideLength = numSides(k);
% Compute the theoretically perfect circularity, if the polygons were perfect instead of digitized.
circularity(k) = ComputeTheoreticalCircularity(thisSideLength);
end
% Plot the theoretical circularities on the curve with a cross.
plot(numSides, circularity, 'b+-', 'LineWidth', 2, 'MarkerSize', 20);
grid on;
hold on;
xl = xlim(); % Get left and right x coordinates of the graph.
% Plot theoretical lines in dark red.
darkRed = [0.85, 0, 0];
for k = 1 : length(numSides)
% Make theoretical line on the plot in a magenta color.
line(xl, [circularity(k), circularity(k)], 'Color', darkRed, 'LineWidth', 2);
fprintf(' %d %f\n', thisSideLength, circularity(k));
if k < 7 % Only print text if it's not too crowded and close together.
% Make text with the true value
message = sprintf('Theoretical value for %d sides = %.4f', thisSideLength, circularity(k));
text(xl(1)+0.1, circularity(k) + 0.005, message, 'Color', darkRed);
end
end
% Set up figure properties:
% Enlarge figure to full screen.
set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0 1 1]);
% Get rid of tool bar and pulldown menus that are along top of figure.
% set(gcf, 'Toolbar', 'none', 'Menu', 'none');
% Give a name to the title bar.
set(gcf, 'Name', 'Demo by ImageAnalyst', 'NumberTitle', 'Off')
drawnow; % Make it display immediately.
title('Theoretical Circularities', 'FontSize', fontSize, 'Interpreter', 'None');
xlabel('Number of Sides', 'FontSize', fontSize);
ylabel('True Circularity', 'FontSize', fontSize);
% Get the midpoint between one circularity and the one for the next higher number of sides.
dividingValues = conv(circularity, [1, 1]/2, 'valid');
% Prepend two zeros so that we can use this array as a lookup table where we pass in
% the number of sides as an index and it tells us the dividing value between
% that number of sides and one more than that.
% For example, right now dividingValues(1) gives us the dividing value between 3 and 4
% and dividingValues(3) gives us the dividing value between 5 and 6 (instead of between 3 and 4).
dividingValues = [0, 0, dividingValues];
% Now dividingValues(3) will give us the dividing value between 3 and 4.
% Put up red horizontal lines at the dividing values
hold on;
xl = xlim();
darkGreen = [0, 0.5, 0];
for k = 1 : length(numSides)-1
thisSideLength = numSides(k);
thisDividingValue = dividingValues(thisSideLength);
h = line(xl, [thisDividingValue, thisDividingValue], 'Color', darkGreen, 'LineWidth', 2, 'LineStyle', '--');
% h.LineStyle = '--';
% For the first 6, print the dividing value at the left just above the line.
% After 6 it would get too crowded
if k <= 6
theLabel = sprintf('Dividing value = %.4f', thisDividingValue);
text(xl(1)+0.1, thisDividingValue + 0.005, theLabel, 'Color', darkGreen);
end
end
catch ME
errorMessage = sprintf('Error in function %s() at line %d.\n\nError Message:\n%s', ...
ME.stack(1).name, ME.stack(1).line, ME.message);
fprintf(1, '%s\n', errorMessage);
uiwait(warndlg(errorMessage));
end
% Now compute the number of vertices by looking at the number of peaks in a plot of distance from centroid.
function numVertices = FindNumberOfVertices(blobMeasurements, labeledImage)
try
numVertices = 0; % Initialize.
% Get the number of blobs in the image.
numRegions = length(blobMeasurements);
hFig = figure;
promptUser = true; % Let user see the curves.
% For each blob, get its boundaries and find the distance from the centroid to each boundary point.
for k = 1 : numRegions
% Extract just this blob alone.
thisBlob = ismember(labeledImage, k) > 0;
if promptUser % Let user see the image.
cla;
imshow(thisBlob);
end
% Find the boundaries
thisBoundary = bwboundaries(thisBlob);
thisBoundary = cell2mat(thisBoundary); % Convert from cell to double.
% Get x and y
x = thisBoundary(:, 2);
y = thisBoundary(:, 1);
% Get the centroid
xCenter = blobMeasurements(k).Centroid(1);
yCenter = blobMeasurements(k).Centroid(2);
% Compute distances
distances = sqrt((x - xCenter).^2 + (y - yCenter).^2);
if promptUser % Let user see the curves.
% Plot the distances.
plot(distances, 'b-', 'LineWidth', 3);
grid on;
message = sprintf('Centroid to perimeter distances for shape #%d', k);
title(message, 'FontSize', 15);
% Scale y axis
yl = ylim();
ylim([0, yl(2)]); % Set lower limit to 0.
end
% Find the range of the peaks
peakRange = max(distances) - min(distances);
minPeakHeight = 0.5 * peakRange;
% Find the peaks
[peakValues, peakIndexes] = findpeaks(distances, 'MinPeakProminence', minPeakHeight);
% Find the valueys.
[valleyValues, valleyIndexes] = findpeaks(-distances, 'MinPeakProminence', minPeakHeight);
numVertices(k) = max([length(peakValues), length(valleyValues)]);
% Circles seem to have a ton of peaks due to the very small range and quanitization of the image.
% If the number of peaks is more than 10, make it zero to indicate a circle.
if numVertices(k) > 10
numVertices(k) = 0;
end
if promptUser % Let user see the curves.
% Plot the peaks.
hold on;
plot(peakIndexes, distances(peakIndexes), 'r^', 'MarkerSize', 10, 'LineWidth', 2);
% Plot the valleys.
hold on;
plot(valleyIndexes, distances(valleyIndexes), 'rv', 'MarkerSize', 10, 'LineWidth', 2);
message = sprintf('Centroid to perimeter distances for shape #%d. Found %d peaks.', k, numVertices(k));
title(message, 'FontSize', 20);
% The figure un-maximizes each time when we call cla, so let's maximize it again.
% Set up figure properties:
% Enlarge figure to full screen.
set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0 1 1]);
% Get rid of tool bar and pulldown menus that are along top of figure.
% set(gcf, 'Toolbar', 'none', 'Menu', 'none');
% Give a name to the title bar.
set(gcf, 'Name', 'Demo by ImageAnalyst', 'NumberTitle', 'Off')
% Let user see this shape's distances plotted before continuing.
promptMessage = sprintf('Do you want to Continue processing,\nor Cancel processing?');
titleBarCaption = 'Continue?';
button = questdlg(promptMessage, titleBarCaption, 'Continue', 'Cancel', 'Continue');
if strcmpi(button, 'Cancel')
promptUser = false;
end
end
end
close(hFig);
catch ME
errorMessage = sprintf('Error in function %s() at line %d.\n\nError Message:\n%s', ...
ME.stack(1).name, ME.stack(1).line, ME.message);
fprintf(1, '%s\n', errorMessage);
uiwait(warndlg(errorMessage));
end
Image Analyst
on 26 Jun 2019
My program was meant for binary images and with this image you can't just threshold it to find shapes.
You will probably have to hand-trace the shapes to create a mask. Or you might try deep learning and see if that can find the shapes you want.
Anindya Banerjee
on 5 Jul 2019
ANY OTHER SUGGETIONS?
See Also
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!An Error Occurred
Unable to complete the action because of changes made to the page. Reload the page to see its updated state.
Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom(English)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)