I want to measure the diameter of this image how can i do it ?

5 visualizzazioni (ultimi 30 giorni)

Risposta accettata

Image Analyst
Image Analyst il 22 Dic 2021
@BERG the problem is the radius is not the same everywhere because the droplet tapers towards the top. So if you want the diameter as defined by the bottom, where it's pretty circular, and not affected by the top, where it's not circular, then you need to ignore the top. In the image down below, look at the green outline. You don't want reginoprops() to get the equivalent diameter of that I wouldn't think. And you can't just chop off the top and use regionprops because the flat-topped droplet is not circular.
So the approach I'm taking is to find the perimeter of the droplet. Then take only the perimeter points below line 150, which is about where the droplet becomes circular. Then fit those coordinates to a circle using the function in the FAQ. If you do that you see the diameter is 325.4 pixels. Here is the full code:
% Demo by Image Analyst
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
clear; % Erase all existing variables. Or clearvars if you want.
workspace; % Make sure the workspace panel is showing.
format long g;
format compact;
fontSize = 15;
markerSize = 40;
%--------------------------------------------------------------------------------------------------------
% READ IN IMAGE
fileName = 'droplet.jpeg';
grayImage = imread(fileName);
% Get the dimensions of the image.
% numberOfColorChannels should be = 1 for a gray scale image, and 3 for an RGB color image.
[rows, columns, numberOfColorChannels] = size(grayImage)
if numberOfColorChannels > 1
% It's not really gray scale like we expected - it's color.
% Extract the blue channel (so the magenta lines will be white).
grayImage = grayImage(:, :, 3);
end
%--------------------------------------------------------------------------------------------------------
% Display the image.
subplot(2, 2, 1);
imshow(grayImage, []);
impixelinfo;
axis('on', 'image');
caption = sprintf('Original image is %d rows by %d columns', rows, columns);
title(caption, 'FontSize', fontSize);
hold on
drawnow;
% Maximize window.
g = gcf;
g.WindowState = 'maximized'
drawnow;
% Display the histogram.
subplot(2, 2, 2);
imhist(grayImage);
grid on;
impixelinfo;
title('Histogram', 'FontSize', fontSize);
drawnow; % Force screen to refresh immediately.
% Get a binary image
threshold = 80;
xline(threshold, 'Color', 'r', 'LineWidth', 2)
mask = grayImage > 80;
% For noise reduction, Fill any potential holes and take the largest blob.
mask = bwareafilt(imfill(mask, 'holes'), 1);
% Display the image.
subplot(2, 2, 3);
imshow(mask, []);
impixelinfo;
axis('on', 'image');
title('Mask', 'FontSize', fontSize);
hold on
drawnow;
% Plot the borders of all the blobs in the overlay above the original grayscale image
% using the coordinates returned by bwboundaries().
% bwboundaries() returns a cell array, where each cell contains the row/column coordinates for an object in the image.
subplot(2, 2, 4);
imshow(grayImage); % Optional : show the original image again. Or you can leave the binary image showing if you want.
hold on;
% Here is where we actually get the boundaries for each blob.
boundaries = bwboundaries(mask);
% boundaries is a cell array - one cell for each blob.
% In each cell is an N-by-2 list of coordinates in a (row, column) format. Note: NOT (x,y).
% Column 1 is rows, or y. Column 2 is columns, or x.
numberOfBoundaries = size(boundaries, 1); % Count the boundaries so we can use it in our for loop
% Now get only those boundary points below 100 since the top of the droplet is distorted
% and will not give an accurate radius because of that.
b = boundaries{1};
xb = b(:, 2);
yb = b(:, 1);
topLine = 150;
plot(xb, yb, 'g-', 'LineWidth', 3)
yline(topLine, 'Color', 'r', 'LineWidth', 3)
indexes = yb > topLine;
% Extract only those boundary points below the top line
xb = xb(indexes);
yb = yb(indexes);
% Fit a circle to it.
[xCenter, yCenter, radius, a] = circlefit(xb, yb)
% Display circle.
viscircles([xCenter, yCenter], radius, 'Color', 'r', 'LineWidth', 2)
% Plot cross hairs at the center.
plot(xCenter, yCenter, 'r+', 'MarkerSize', 100, 'LineWidth', 2)
caption = sprintf('Original image with overlays\nDiameter = %.1f, Radius = %.1f', 2*radius, radius);
title(caption, 'FontSize', fontSize);
%========================================================================================================
% From FAQ https://matlab.fandom.com/wiki/FAQ#How_can_I_fit_a_circle_to_a_set_of_XY_data.3F
function [xCenter, yCenter, radius, a] = circlefit(x, y)
% circlefit(): Fits a circle through a set of points in the x - y plane.
% USAGE :
% [xCenter, yCenter, radius, a] = circlefit(X, Y)
% The output is the center point (xCenter, yCenter) and the radius of the fitted circle.
% "a" is an optional output vector describing the coefficients in the circle's equation:
% x ^ 2 + y ^ 2 + a(1) * x + a(2) * y + a(3) = 0
% by Bucher Izhak 25 - Oct - 1991
numPoints = numel(x);
xx = x .* x;
yy = y .* y;
xy = x .* y;
A = [sum(x), sum(y), numPoints;
sum(xy), sum(yy), sum(y);
sum(xx), sum(xy), sum(x)];
B = [-sum(xx + yy) ;
-sum(xx .* y + yy .* y);
-sum(xx .* x + xy .* y)];
a = A \ B;
xCenter = -.5 * a(1);
yCenter = -.5 * a(2);
radius = sqrt((a(1) ^ 2 + a(2) ^ 2) / 4 - a(3));
end
Look how well the circle fits now that you ignore the top part of the droplet and just fit the bottom part.
  4 Commenti
Image Analyst
Image Analyst il 22 Dic 2021
Berg, your image is not in the same folder as your m-file. So put in this code to browse for your image. Replace this
fileName = 'droplet.jpeg';
grayImage = imread(fileName);
with this:
% Have user browse for a file, from a specified "starting folder."
% For convenience in browsing, set a starting folder from which to browse.
startingFolder = pwd; % or 'C:\wherever';
if ~isfolder(startingFolder)
% If that folder doesn't exist, just start in the current folder.
startingFolder = pwd;
end
% Get the name of the file that the user wants to use.
defaultFileName = fullfile(startingFolder, '*.*');
[baseFileName, folder] = uigetfile(defaultFileName, 'Select a file');
if baseFileName == 0
% User clicked the Cancel button.
return;
end
fullFileName = fullfile(folder, baseFileName);
grayImage = imread(fullFileName);

Accedi per commentare.

Più risposte (1)

Matt J
Matt J il 22 Dic 2021
Modificato: Matt J il 22 Dic 2021
Perhaps as follows,
load Image
stats=bwferet(Image>75,'all')
stats = 1×6 table
MaxDiameter MaxAngle MaxCoordinates MinDiameter MinAngle MinCoordinates ___________ ________ ______________ ___________ ________ ______________ 379.35 103.41 {2×2 double} 326 0 {2×2 double}
  3 Commenti
BERG
BERG il 22 Dic 2021
thanks that approch is very easy really thanks
Image Analyst
Image Analyst il 23 Dic 2021
Berg if you just binarize the whole thing then you'd be getting the equivalent circular diameter of something like an ellipse. In that case you'd be better off asking for the 'MajorAxisLength' and 'MinorAxisLength', which are the axes you'd get from fitting the blob to an ellipse rather than a circle.
Or else you can fit the ellipse yourself like it says in the FAQ:
or use the attached function.

Accedi per commentare.

Categorie

Scopri di più su Images in Help Center e File Exchange

Community Treasure Hunt

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

Start Hunting!

Translated by