Clear Filters
Clear Filters

How to measure crack width

28 views (last 30 days)
yasmin ismail
yasmin ismail on 7 Mar 2023
Commented: Image Analyst on 12 Oct 2024 at 19:24
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));
  2 Comments
yasmin ismail
yasmin ismail on 7 Mar 2023
another thing when i added the whole areas and divided by the maximum diamter I got different answer for example if minimum accpetable area is 100 the output is :
3×3 table
MaxDiameter MaxAngle MaxCoordinates
________________ _________________ ______________
136.297468795279 -108.833752912659 {2×2 double}
45.6946386351835 113.198590513648 {2×2 double}
69.3541635375988 -95.7927964950321 {2×2 double}
allAreas =
523 253 230
The average width = 11.45 pixels.
if I take (523+253+230)/136.29= 7.46 not 11.45

Sign in to comment.

Answers (2)

Image Analyst
Image Analyst on 8 Mar 2023
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
  1. Threshold/mask your image
  2. Get the Euclidean Distance Transform (EDT) of your mask
  3. Get the skeleton of your mask
  4. Multiply your EDT image by your skeleton image to get the radius of the blob at each point along it's skeleton;
  5. Double the radii to get the widths.
  6. Display the histogram to see the distribution, or take the mean.
See attached demo.
  50 Comments
yasmin ismail
yasmin ismail on 13 Jul 2023
Edited: yasmin ismail on 13 Jul 2023
@Image Analyst so when I take photo by mobile , I have to save it in labtop as PNG, then start use it in matlab, and also the result output from process by matlb save it as png
is it correct?
The thing that I noticed when I send captured photo from my mobile to labtop automatically formated as jpg ,so its entered matlab as it jpg, so its already jpg. The only thing I can do if my model has several process for example start with filteration, the output could be saved as png then input it to next step for binarization.
Thus , what do you think?
Image Analyst
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.

Sign in to comment.


yasmin ismail
yasmin ismail on 2 Oct 2024
@Image Analyst can you explain to me why the mean width = 1.7mm as shown in the result image No.1
althought there is no blue column on 1.7 mm and the mean width is 0.9 mm althougth also no blue column on 0.9 mm as shown in the histogram .
The code used:
% 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 = '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.
widths = diameterImage(diameterImage > 0);
% Show histogram of widths.
subplot(2, 3, 6);
histogram(widths);
grid on;
xlabel('Width in mm', 'FontSize', fontSize);
ylabel('Count', 'FontSize', fontSize);
% Compute the mean width in pixels
meanWidthPixls = mean(widths)
%%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
% Put a line on the histogram at the mean width
xline(crack_Mean_width, 'LineWidth', 2, 'Color', 'r');
caption = sprintf('Histogram of Widths. Mean Width = %.1f mm', crack_Mean_width);
title(caption, 'FontSize', fontSize);
message = sprintf('Mean Width = %.1f mm', crack_Mean_width);
msgbox(message);
% Compute the max width in pixels
maxWidthPixels = max(widths)
%%Calaculate Max crack width in mm
calibration_width=25.4;
calibration_pixels=96;
Max_Width=maxWidthPixels;
crack_width=(Max_Width *calibration_width)/calibration_pixels
% Put a line on the histogram at the max width
xline(crack_width, 'LineWidth', 2, 'Color', 'r');
caption = sprintf('Histogram of Widths. Max Width = %.1f mm', crack_width);
title(caption, 'FontSize', fontSize);
message = sprintf(' Max crack width = %.1f mm', crack_width);
msgbox(message);
  11 Comments
yasmin ismail
yasmin ismail on 12 Oct 2024 at 12:21
@Image Analyst first of all i would thanks and appreciated your helps
I am civil engineer and I donnt have a good experince in this topic, thus I tried to test the code above as followig but I didnt get the answer as shwon below:
% 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 = '7006-134.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)
[labeledImage, numberOfCracks] = bwlabel(skelImage);
allCrackWidths10 = zeros(numberOfCracks, 10); % 10 widths for every crack.
for k = 1 : numberOfCracks
oneSingleCrack = ismember(labeledImage, k);
end
endPointImage = bwmorph(oneSingleCrack, 'endpoints');
props = regionprops(oneSingleCrack, 'PixelList');
% 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
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
So, could you help me to fix it, please?
Image Analyst
Image Analyst on 12 Oct 2024 at 19:24
I told you the k2 and k3 loops should be INSIDE the k loop, not outside it.

Sign in to comment.

Categories

Find more on Convert Image Type in Help Center and File Exchange

Community Treasure Hunt

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

Start Hunting!