How to measure crack width
Show older comments
I noticed that when I reduced the minimum acceptable area the crack width decreased although the appearance of blobs in this situation, while when i increased minimum acceptable area and the blobs removed and the width increased
for example:
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 = 10;
% Read in image and convert to gray scale.
rgbImage = imread('7004-46.jpg');
I = rgb2gray(rgbImage);
subplot(2,2,1);
imshow(I);
title('7004-46');
% Take histogram.
subplot(2,2,2);
histogram(I);
grid on; %to make grid line
title('histogram Gray image');
% Binarize image.
threshold = 145;
xline(threshold, 'Color', 'r', 'LineWidth',2)
mask=I < threshold;
mask = imfill(mask, 'holes');
% Filter image.
se = strel('line',1, 0);
filteredImage = imclose(mask, se);
subplot(2, 2, 3);
imshow(filteredImage);
title('closed Image');
%%% Measure the areas to determin minAcceptableArea show the histogram of
%%% area
% props = regionprops(filteredImage, 'Area');
% allAreas = sort([props.Area], 'descend');
% histogram(allAreas)
%%Look at the histogram. What area do you think is the minimum size to be a valid crack?
minAcceptableArea =20;
mask = bwareafilt(filteredImage, [minAcceptableArea, inf]);
subplot(2, 2, 4);
imshow(mask);
%
% % Measure size of crack.
props = regionprops(mask, 'Area');
allAreas = [props.Area];
out=bwferet(mask)
% Measure the areas to know the area not to be considered
% props = regionprops(mask, 'Area');
% allAreas = sort([props.Area], 'descend')
% Get width = area / maximum length
averageWidths = sort(allAreas ./ out.MaxDiameter, 'descend');
message = sprintf('The average width = %.2f pixels.', averageWidths(1));
fprintf('%s\n', message);
caption = sprintf('Binary Image of Cracks larger than %d\nAverage width of largest = %.2f pixels', minAcceptableArea, averageWidths(1));
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
uiwait(helpdlg(message));
Answers (2)
Image Analyst
on 8 Mar 2023
0 votes
That doesn't look like the right algorithm if you want to find crack widths. Did you see my post where I found the mean width of a blob (image of lightning)? Basically you want to
- Threshold/mask your image
- Get the Euclidean Distance Transform (EDT) of your mask
- Get the skeleton of your mask
- Multiply your EDT image by your skeleton image to get the radius of the blob at each point along it's skeleton;
- Double the radii to get the widths.
- Display the histogram to see the distribution, or take the mean.
See attached demo.

50 Comments
yasmin ismail
on 8 Mar 2023
Edited: yasmin ismail
on 8 Mar 2023
Image Analyst
on 9 Mar 2023
Try this, but first download my interactive thresholding app:
% Program to compute the mean width of a blob in an image.
clearvars;
close all;
clc;
fontSize = 15;
% Read in original image, with white lightning on black background.
baseFileName = '7004-46.jpg';
fullFileName = fullfile(pwd, baseFileName);
grayImage = imread(fullFileName);
% 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.
% Use weighted sum of ALL channels to create a gray scale image.
% grayImage = rgb2gray(rgbImage);
% ALTERNATE METHOD: Convert it to gray scale by taking only the green channel,
% which in a typical snapshot will be the least noisy channel.
grayImage = grayImage(:, :, 2); % Take green channel.
else
grayImage = grayImage; % It's already gray scale.
end
% Now it's gray scale with range of 0 to 255.
subplot(2, 3, 1);
imshow(grayImage, [])
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
title('Original Image', 'FontSize', fontSize);
% Binarize the image.
% mask = imbinarize(grayImage);
lowThreshold = 0;
highThreshold = 160;
% Interactively and visually set a threshold on a gray scale image.
% https://www.mathworks.com/matlabcentral/fileexchange/29372-thresholding-an-image?s_tid=srchtitle
[lowThreshold, highThreshold] = threshold(lowThreshold, highThreshold, grayImage);
mask = grayImage >= lowThreshold & grayImage <= highThreshold;
% Fill holes.
mask = imfill(mask, 'holes');
% Take largest blob only.
mask = bwareafilt(mask, 1);
subplot(2, 3, 2);
imshow(mask)
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
title('Mask', 'FontSize', fontSize)
% Compute the skeleton
skelImage = bwskel(mask);
subplot(2, 3, 3);
imshow(skelImage)
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
title('Thinned', 'FontSize', fontSize)
% Enlarge figure to full screen.
g = gcf;
g.WindowState = 'maximized';
% Compute the Euclidean distance image.
edtImage = bwdist(~mask);
subplot(2, 3, 4);
imshow(edtImage, [])
title('Distance Transform Image', 'FontSize', fontSize);
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
% Multiply them to get an image where the only pixels in the image
% are along the skeleton and their value is the radius.
% Multiply radius image by 2 to get diameter image.
diameterImage = 2 * edtImage .* single(skelImage);
subplot(2, 3, 5);
imshow(diameterImage, [])
title('Diameter Image', 'FontSize', fontSize);
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
% Get the widths. These will be where the image is not zero.
widths = diameterImage(diameterImage > 0);
% Show histogram of widths.
subplot(2, 3, 6);
histogram(widths);
grid on;
xlabel('Width in Pixels', 'FontSize', fontSize);
ylabel('Count', 'FontSize', fontSize);
% Compute the mean width
meanWidth = mean(widths)
% Put a line on the histogram at the mean width
xline(meanWidth, 'LineWidth', 2, 'Color', 'r');
caption = sprintf('Histogram of Widths. Mean Width = %.1f Pixels', meanWidth);
title(caption, 'FontSize', fontSize);
message = sprintf('Mean Width = %.1f Pixels', meanWidth);
msgbox(message);

yasmin ismail
on 14 Mar 2023
Image Analyst
on 14 Mar 2023
Yes, I used my utility to determine the threshold. You might be able to use an automatic way like graythresh or otsuthresh, or my attached triangle threshold.
yasmin ismail
on 14 Mar 2023
Image Analyst
on 14 Mar 2023
You can also use the triangle threshold in the interactive thresholding app I attached. It's a button in the lower right of the window.
I'm pretty sure triangle threshold works for double inputs (the counts array from the histogram are always double I believe). You'd need to attach your script that calls the triangle threshold function so I can see what's wrong.
yasmin ismail
on 15 Mar 2023
Moved: DGM
on 17 Mar 2023
yasmin ismail
on 17 Mar 2023
Moved: DGM
on 17 Mar 2023
Image Analyst
on 17 Mar 2023
Moved: DGM
on 17 Mar 2023
maxWidths = max(widths)
yasmin ismail
on 17 Mar 2023
yasmin ismail
on 31 Mar 2023
Image Analyst
on 31 Mar 2023
You need to take out the call to bwareafilt() where it extracts only the largest skeleton and use bwareaopen
% Program to compute the mean width of a blob in an image.
clearvars;
close all;
clc;
fontSize = 15;
% Read in original image, with white lightning on black background.
baseFileName = '7049-198.jpg';
fullFileName = fullfile(pwd, baseFileName);
grayImage = imread(fullFileName);
% 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.
% Use weighted sum of ALL channels to create a gray scale image.
% grayImage = rgb2gray(rgbImage);
% ALTERNATE METHOD: Convert it to gray scale by taking only the green channel,
% which in a typical snapshot will be the least noisy channel.
grayImage = grayImage(:, :, 2); % Take green channel.
else
grayImage = grayImage; % It's already gray scale.
end
% Now it's gray scale with range of 0 to 255.
subplot(2, 3, 1);
imshow(grayImage, [])
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
title('Original Image', 'FontSize', fontSize);
% Binarize the image.
% mask = imbinarize(grayImage);
lowThreshold = 0;
highThreshold = 160;
% Interactively and visually set a threshold on a gray scale image.
% https://www.mathworks.com/matlabcentral/fileexchange/29372-thresholding-an-image?s_tid=srchtitle
[lowThreshold, highThreshold] = threshold(lowThreshold, highThreshold, grayImage);
mask = grayImage >= lowThreshold & grayImage <= highThreshold;
% Fill holes.
mask = imfill(mask, 'holes');
subplot(2, 3, 2);
imshow(mask)
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
title('Mask', 'FontSize', fontSize)
% Take largest blob only.
% mask = bwareafilt(mask, 1);
% Compute the skeleton
skelImage = bwskel(mask);
% Find the areas of all the skeletons.
props = regionprops(skelImage, 'Area');
allAreas = sort([props.Area])
% Extract only skeletons longer than 60 pixels.
skelImage = bwareaopen(skelImage, 60);
subplot(2, 3, 3);
imshow(skelImage)
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
title('Thinned', 'FontSize', fontSize)
% Enlarge figure to full screen.
g = gcf;
g.WindowState = 'maximized';
% Compute the Euclidean distance image.
edtImage = bwdist(~mask);
subplot(2, 3, 4);
imshow(edtImage, [])
title('Distance Transform Image', 'FontSize', fontSize);
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
% Multiply them to get an image where the only pixels in the image
% are along the skeleton and their value is the radius.
% Multiply radius image by 2 to get diameter image.
diameterImage = 2 * edtImage .* single(skelImage);
subplot(2, 3, 5);
imshow(diameterImage, [])
title('Diameter Image', 'FontSize', fontSize);
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
% Get the widths. These will be where the image is not zero.
widths = diameterImage(diameterImage > 0);
% Show histogram of widths.
subplot(2, 3, 6);
histogram(widths);
grid on;
xlabel('Width in Pixels', 'FontSize', fontSize);
ylabel('Count', 'FontSize', fontSize);
% Compute the mean width
meanWidth = mean(widths)
% Put a line on the histogram at the mean width
xline(meanWidth, 'LineWidth', 2, 'Color', 'r');
caption = sprintf('Histogram of Widths. Mean Width = %.1f Pixels', meanWidth);
title(caption, 'FontSize', fontSize);
message = sprintf('Mean Width = %.1f Pixels', meanWidth);
msgbox(message);

yasmin ismail
on 3 Apr 2023
Image Analyst
on 3 Apr 2023
I just picked it. There is lots of noise in the image. There are cracks of all different lengths from just a few pixels to thousands of pixels. What is YOUR definition of how long a legitimate crack should be? Do you want cracks just 4 pixels long? A hundred long? It's very complicated. There is no one single overall, grand "right" answer for the minimum size of valid cracks, so you just have to define one. Do you care about analyzing a crack if it's only 2 pixels long? I think not. So what is your minimum length that you care about?
yasmin ismail
on 3 Apr 2023
yasmin ismail
on 5 Apr 2023
Image Analyst
on 5 Apr 2023
I call it "manual" because the thresholds are what you set by moving the sliders (scrollbars). The threshold is not selected by an automatic method such as Otsu or triangle or graythresh, it's selected by you manually.
yasmin ismail
on 11 Apr 2023
Image Analyst
on 11 Apr 2023
The (x,y) location and RGB color values of the pixel underneath your cursor.
yasmin ismail
on 11 Apr 2023
Image Analyst
on 11 Apr 2023
I never know where or when I might need it so I usually just call impixelinfo as a matter of habit after I call imshow(). Better to have it if you need it than to not have it. It doesn't hurt to have it, so why not? If you're sure you'll never need it, you can delete that line of code.
yasmin ismail
on 11 Apr 2023
yasmin ismail
on 23 Apr 2023
Image Analyst
on 23 Apr 2023
yasmin ismail
on 24 Apr 2023
Moved: Voss
on 12 Jul 2023
yasmin ismail
on 30 Apr 2023
Moved: Voss
on 12 Jul 2023
Image Analyst
on 30 Apr 2023
Moved: Voss
on 12 Jul 2023
Try adapthisteq to flatten the image so that you can use a global threshold. Or use imbinarize with the locally adaptive option. Once you have the binary image, you can take the max of the Euclidean Distance Transform to find the radius of the max crack or blob.
yasmin ismail
on 30 Apr 2023
Moved: Voss
on 12 Jul 2023
Image Analyst
on 30 Apr 2023
Moved: Voss
on 12 Jul 2023
Yeah, basically, though you may need to change some of the parameters, like the threshold level or, in the case of adapthisteq, the tile size.
yasmin ismail
on 23 May 2023
Moved: Voss
on 12 Jul 2023
Image Analyst
on 23 May 2023
Moved: Voss
on 12 Jul 2023
That is a pretty variable image. Could you try a Hessian, Frangi, or COSFIRE-B filter on it? There are ones in the File Exchange.
yasmin ismail
on 27 May 2023
Moved: Voss
on 12 Jul 2023
Image Analyst
on 27 May 2023
Moved: Voss
on 12 Jul 2023
Yes, try various spatial filters so that you can get as much contrast between the crack and the background, and have the background be fairly flat (same gray level range). Then you can apply a fixed global threshold to discriminate crack from background.
yasmin ismail
on 2 Jun 2023
Moved: Voss
on 12 Jul 2023
yasmin ismail
on 6 Jul 2023
Moved: Voss
on 12 Jul 2023
Image Analyst
on 7 Jul 2023
Moved: Voss
on 12 Jul 2023
Let's say that we want the mean of all images to be 150 and the cracks are small enough that they don't affect the mean of the entire image much. So, before you apply the threshold, just scale each image by dividing by it's mean and multiplying by 150.
image1 = 150 * double(image1) / mean2(image1);
image2 = 150 * double(image2) / mean2(image2);
yasmin ismail
on 7 Jul 2023
Moved: Voss
on 12 Jul 2023
Image Analyst
on 7 Jul 2023
Moved: Voss
on 12 Jul 2023
I don't think you actually thought about what you were doing. First of all meanIntensity is a scalar, so when you do mean2(meanIntensity) you get the same scalar. Let's say meanIntensity was 186.1056. Well, mean(186.1056) is simply just 186.1056, right? Secondly, you then call clearvars after that so it clears everything you just computed!
You need to do something like I told you:
% Program to compute the mean width of a blob in an image.
clearvars;
close all;
clc;
fontSize = 15;
%--------------------------------------------------------------------------------------------
% Compute mean intensity that we will set all images to.
refImage = imread('CC1-3.jpg');
meanRefIntensity = mean(refImage, 'all')
%--------------------------------------------------------------------------------------------
% Read in test image, with white lightning on black background.
baseFileName = '7009-30.jpg';
fullFileName = fullfile(pwd, baseFileName);
grayImage = imread(fullFileName);
% 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.
% Use weighted sum of ALL channels to create a gray scale image.
% grayImage = rgb2gray(rgbImage);
% ALTERNATE METHOD: Convert it to gray scale by taking only the green channel,
% which in a typical snapshot will be the least noisy channel.
grayImage = grayImage(:, :, 2); % Take green channel.
end
% Now it's gray scale with range of 0 to 255.
%--------------------------------------------------------------------------------------------
% Scale this intensity so that its mean intensity is the same as the
% reference image
grayImage = meanRefIntensity * double(grayimage) / mean2(grayImage);
subplot(2, 3, 1);
imshow(grayImage, [])
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
title('Original Image', 'FontSize', fontSize);
%--------------------------------------------------------------------------------------------
% Binarize the image by thresholding.
lowThreshold = 0;
highThreshold = 160;
% Interactively and visually set a threshold on a gray scale image.
% https://www.mathworks.com/matlabcentral/fileexchange/29372-thresholding-an-image?s_tid=srchtitle
[lowThreshold, highThreshold] = threshold(lowThreshold, highThreshold, grayImage);
mask = grayImage >= lowThreshold & grayImage <= highThreshold;
yasmin ismail
on 7 Jul 2023
Moved: Voss
on 12 Jul 2023
Image Analyst
on 7 Jul 2023
Moved: Voss
on 12 Jul 2023
OK, it looks like you just refuse to standardize the gray levels to use a fixed threshold. So in that case you're going to be stuck with trying to use an automatic threshold. From the looks of your histogram you might try a triangle threshold on the dark/left side. There is a button for that in my utility, or you can call it directly with the attached function. See if that selects a good threshold. But there may be no global threshold that is perfect. If your crack is not solid then changing the threshold to make it more solid may introduce other noise blobs, which you may be able to get rid of with morphological filtering methods based on area or shape. Or if you have no background noise then your crack may be a series of separated smaller cracks. You have to decide if you REALLY need just one giant crack or if you're still able to make some sort of decision on the quality of the material based on analyzing the smaller cracks. Realize that just because you think you want one single big long crack, this may not actually be necessary to analyze the quality or suitability of the material for use in some building/construction application. For example, maybe the area fraction of all the cracks, regardless of how many or what size they are, is a good indicator of material quality, and maybe that's even better than if you had to go through special image processing gymnastics to get just one big long crack.
yasmin ismail
on 11 Jul 2023
Moved: Voss
on 12 Jul 2023
Image Analyst
on 12 Jul 2023
Moved: Voss
on 12 Jul 2023
I have no idea what filters you want to use, but just go ahead and use them.
"how can I make it" <== it totally depends on what kind of filter you want. If it's a filter that is built-in to MATLAB, then just read the documentation for it.
yasmin ismail
on 12 Jul 2023
Moved: Voss
on 12 Jul 2023
help imgaussfilt
Open the documentation in your own version of MATLAB (with the doc imgaussfilt command) for more complete documentation.
Of course, this function will blur the image so I hope that is what you want.
yasmin ismail
on 12 Jul 2023
Moved: Voss
on 12 Jul 2023
Image Analyst
on 12 Jul 2023
Moved: Voss
on 12 Jul 2023
@yasmin ismail If you want the variable to be used in your other function, just pass the (poorly named) variable into the other function, for example
fi = imgaussfilt(...............)
outputImage = SomeOtherFunction(fi); % Pass fi to the other function.
If you want to have your other function read it in from a disk file, then you need to call imwrite to write fi to a disk file, and then tell the other function what the name of the file is. By the way, NEVER use jpg format for images you're doing image analysis on because it's lossy compression and you won't get the same image back that you saved after reading in a JPG image.
yasmin ismail
on 12 Jul 2023
Moved: Voss
on 12 Jul 2023
Image Analyst
on 12 Jul 2023
For saving to disk, use PNG format. Of course once it's read back into an array in MATLAB, the format it had on disk doesn't matter, but you want to make sure you use a lossless format such as PNG or TIF to make sure you get back exactly the same data you saved out..
yasmin ismail
on 13 Jul 2023
Edited: yasmin ismail
on 13 Jul 2023
Image Analyst
on 2 Oct 2024
@yasmin ismail youi don't have to use PNG, though it's best. If your image capture system takes JPG and you can't improve your image capture system then you're stuck with JPG. Hopefully the degradation is not too bad. But it's not worth converting the files into a PNG file format - that won't improve the quality one tiny bit. Just leave them as JPG format images.
And I don't see any need to save intermediate images to disk unless you want to for some reason. Just keep processing, using the images in memory, until you have the final result.
Also for spatial calibration you need to make sure your camera is the same distance from the scene. If not you'd have to have some calibration object in the scene and you'd have to detect that and calibrate to that embedded object.
yasmin ismail
on 2 Oct 2024
14 Comments
Image Analyst
on 3 Oct 2024
@yasmin ismail you plotted the histogram of widths in pixels. But you then calibrated to millimeters with this:
%%Calaculate Mean crack width in mm
calibration_width=25.4;
calibration_pixels=96;
Mean_Width=meanWidthPixls;
crack_Mean_width=(Mean_Width *calibration_width)/calibration_pixels
then you used xline to put a line up at the mm width but the x axis was in pixel widths, so the red vertical lines won't be in the right place because the vertical red lines are in calibrated units while the histogram was in pixel units.
yasmin ismail
on 3 Oct 2024
yasmin ismail
on 4 Oct 2024
Image Analyst
on 5 Oct 2024
Try this:
% Program to compute the mean width of a crack in an image.
% Initialization steps.
clc; % Clear the command window.
fprintf('Beginning to run %s.m ...\n', mfilename);
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;
% Read in original image, with white lightning on black background.
baseFileName = '7002-9.jpg';
fullFileName = fullfile(pwd, baseFileName);
grayImage = imread(fullFileName);
% 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.
% Use weighted sum of ALL channels to create a gray scale image.
% grayImage = rgb2gray(rgbImage);
% ALTERNATE METHOD: Convert it to gray scale by taking only the green channel,
% which in a typical snapshot will be the least noisy channel.
grayImage = grayImage(:, :, 3); % Take Blue channel.
else
grayImage = grayImage; % It's already gray scale.
end
% Now it's gray scale with range of 0 to 255.
subplot(2, 3, 1);
imshow(grayImage, [])
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
title('Original Image', 'FontSize', fontSize);
% Binarize the image.
% mask = imbinarize(grayImage);
lowThreshold = 0;
highThreshold = 150;
% Interactively and visually set a threshold on a gray scale image.
% https://www.mathworks.com/matlabcentral/fileexchange/29372-thresholding-an-image?s_tid=srchtitle
[lowThreshold, highThreshold] = threshold(lowThreshold, highThreshold, grayImage);
mask = grayImage >= lowThreshold & grayImage <= highThreshold;
% Bridges unconnected pixels (morphological operation).
mask = bwmorph(mask, 'bridge');
% Fill holes.
mask = imfill(mask, 'holes');
subplot(2, 3, 2);
imshow(mask)
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
title('Mask', 'FontSize', fontSize)
% Take largest blob only.
% mask = bwareafilt(mask, 1);
% Compute the skeleton
skelImage = bwskel(mask);
% Find the areas of all the skeletons.
props = regionprops(skelImage, 'Area');
allAreas = sort([props.Area])
% Extract only skeletons longer than 60 pixels.
skelImage = bwareaopen(skelImage, 10);
subplot(2, 3, 3);
imshow(skelImage)
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
title('Thinned', 'FontSize', fontSize)
% Enlarge figure to full screen.
g = gcf;
g.WindowState = 'maximized';
% Compute the Euclidean distance image.
edtImage = bwdist(~mask);
subplot(2, 3, 4);
imshow(edtImage, [])
title('Distance Transform Image', 'FontSize', fontSize);
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
% Multiply them to get an image where the only pixels in the image
% are along the skeleton and their value is the radius.
% Multiply radius image by 2 to get diameter image.
diameterImage = 2 * edtImage .* single(skelImage);
subplot(2, 3, 5);
imshow(diameterImage, [])
title('Diameter Image', 'FontSize', fontSize);
impixelinfo; % Let user mouse around and see values in the status line at the lower right.
% Get the widths. These will be where the image is not zero.
widthsInPixels = diameterImage(diameterImage > 0);
% Calculate crack widths in mm
calibration_width=25.4;
calibration_pixels=96;
widthsInMms =(widthsInPixels * calibration_width) / calibration_pixels;
% Show histogram of widths.
subplot(2, 3, 6);
histogram(widthsInMms);
grid on;
xlabel('Width in mm', 'FontSize', fontSize);
ylabel('Count', 'FontSize', fontSize);
% Compute the mean width in pixels
meanWidthInPixels = mean(widthsInPixels)
% Compute the mean width in mm
meanWidthInMms = mean(widthsInMms)
% Put a line on the histogram at the mean width
xline(meanWidthInMms, 'LineWidth', 2, 'Color', 'r');
caption = sprintf('Histogram of Widths. Mean Width = %.1f mm', meanWidthInMms);
title(caption, 'FontSize', fontSize);
% Compute the max width in pixels
maxWidthPixels = max(widthsInPixels)
% Compute the max width in mm
maxWidthMm = max(widthsInMms)
% Put a line on the histogram at the max width
xline(maxWidthMm, 'LineWidth', 2, 'Color', 'r');
caption = sprintf('Histogram of Widths.\nMean Width = %.2f mm, Max Width = %.2f mm', meanWidthInMms, maxWidthMm);
title(caption, 'FontSize', fontSize);
message = sprintf('Mean Crack Width = %.2f mm\nMax Crack Width = %.2f mm', meanWidthInMms, maxWidthMm);
uiwait(msgbox(message));

yasmin ismail
on 5 Oct 2024
yasmin ismail
on 5 Oct 2024
The widths of each crack are given in the variable widthsInMms.
Why do you think there should be a crack with exactly the mean crack width? For a simple example, let's say your data set is two numbers 10 and 20. The mean value is 15 but the mean value is not one of the values in the dataset.
data = [10, 20];
meanOfData = mean(data)
histogram(data);
xline(meanOfData, 'LineWidth', 3, 'Color', 'r');
grid on;
yasmin ismail
on 6 Oct 2024
Image Analyst
on 9 Oct 2024
Sorry for the delay - you've probably solved it by now. I've just been busy with other stuff and couldn't spend time on Answers recently. So with your skeleton image, you need to label it with bwlabel, then use ismember to loop over all blobs and extract one at a time.
[labeledImage, numberOfCracks] = bwlabel(skelImage);
allCrackWidths10 = zeros(numberOfCracks, 10); % 10 widths for every crack.
for k = 1 : numberOfCracks
oneSingleCrack = ismember(labeledImage, k);
end
If you want the histogram of that, that is easy. If you insist on traversing down the skeleton, that is not so easy since there is no way to give you all those coordinates in order going along the curve. So you can use bwmorph to identify endpoints.
endPointImage = bwmorph(oneSingleCrack, 'endpoints');
props = regionprops(oneSingleCrack, 'PixelList');
Then go though all the pixel coordinates to see which one is closest to the last one. This is the hardest part. Unfortunately I don't have time to do it for you so I'm leaving that part to you.
Then use those coordinates and the edtImage to get the width at every coordinate in order. This loop should be inside the k loop over all cracks.
% x and y are the column and row coordinates of the single crack.
widthsOfSingleCrack = zeros(1, numel(x));
for k2 = 1 : numel(x);
row = y(k2);
col = x(k2);
widthsOfSingleCrack(k2) = edtImage(row, col);
end
Then, after the loop, in another loop, use linspace and interp1 to get 10 distances between the starting point and the ending point. This loop is inside the k loop and after the k2 loop.
for k3 = 1 : numberOfCracks
% Get 10 indexes from first element to last element.
xCrack = linspace(1, numel(widthsOfSingleCrack), 10);
% Get the 10 widths at those xCrack indexes.
allCrackWidths10(k3, :) = interp1(1:numel(widthsOfSingleCrack), widthsOfSingleCrack, xCrack);
end
Give it a try. The code is untested so do the part that I didn't do and debug it. I'll check back in a day or two.
yasmin ismail
on 12 Oct 2024
Image Analyst
on 12 Oct 2024
I told you the k2 and k3 loops should be INSIDE the k loop, not outside it.
yasmin ismail
on 14 Nov 2024
yasmin ismail
on 2 Dec 2024
Image Analyst
on 8 Dec 2024
How about just a list of widths for each crack? I don't have the time to sort the widths in 10 different locations along the midline. If you need it you'll have to figure out that part for yourself. But I don't see why you'd need that anyway since you have the width at every center pixel - just click on it or nover the mouse to see what the value is. Or just write all the widths out to a text file or something.
Categories
Find more on Morphological Operations in Help Center and File Exchange
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!