detect defects (notches and breaks) in rubber O-rings

1 view (last 30 days)
Hi,
I'm trying to detect defects (notches and breaks) in rubber O-rings. Tried many command: "imopen", "adapthisteq", "bwmorph", etc...they work on some images, but not on all of them. You'll realise that some backgrounds are not 100% clear, the shadow and rubber pieces (in some) are considered part of the object sometimes. I tried "graythresh" to sepatrate the object from the background, nothing works....any idea? you assistance would be apprciated.
images attached....
Sincerely,
BR

Accepted Answer

Image Analyst
Image Analyst on 20 Aug 2020
Here is a more complete answer:
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 = 22;
%--------------------------------------------------------------------------------------------------------
% READ IN IMAGE
folder = pwd;
baseFileName = 'oring03.jpg';
% Get the full filename, with path prepended.
fullFileName = fullfile(folder, baseFileName);
% Check if file exists.
if ~exist(fullFileName, 'file')
% The file doesn't exist -- didn't find it there in that folder.
% Check the entire search path (other folders) for the file by stripping off the folder.
fullFileNameOnSearchPath = baseFileName; % No path this time.
if ~exist(fullFileNameOnSearchPath, 'file')
% Still didn't find it. Alert user.
errorMessage = sprintf('Error: %s does not exist in the search path folders.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
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(grayImage);
% 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
% Display the image.
subplot(2, 3, 1);
imshow(grayImage, []);
title('Original Grayscale Image', 'FontSize', fontSize, 'Interpreter', 'None');
impixelinfo;
hFig = gcf;
hFig.WindowState = 'maximized'; % May not work in earlier versions of MATLAB.
drawnow;
% Display histogram
subplot(2, 3, 2);
imhist(grayImage);
grid on;
title('Histogram of original gray image', 'FontSize', fontSize);
%--------------------------------------------------------------------------------------------------------
% SEGMENTATION OF IMAGE
threshold = 100;
outerMask = grayImage < threshold;
outerMask = imfill(outerMask, 'holes');
outerMask = bwareafilt(outerMask, 1);
% Display the image.
subplot(2, 3, 3);
imshow(outerMask, []);
title('Outer Mask', 'FontSize', fontSize, 'Interpreter', 'None');
impixelinfo;
% Measure area and solidity.
props = regionprops(outerMask, 'Area', 'Solidity', 'ConvexHull', 'EquivDiameter');
fprintf('Outer Area = %d pixels. Outer Solidity = %.5f\n', props.Area, props.Solidity);
% Fit the convex hull perimeter to a circle.
[xCenter, yCenter, R, a] = FitCircle(props.ConvexHull(:, 1), props.ConvexHull(:, 2));
% Plot that circle in the overlay
hold on;
theta = linspace(0, 2*pi, 2 * (rows + columns));
x = R * cos(theta) + xCenter;
y = R * sin(theta) + yCenter;
plot(x, y, 'r-', 'LineWidth', 2);
% Make a mask of the circle.
circleMask = poly2mask(x, y, rows, columns);
% Do an xor with the mask to see where it's different.
differing = xor(circleMask, outerMask);
% Display the image.
subplot(2, 3, 4);
imshow(differing, []);
title('Non-Circular Parts', 'FontSize', fontSize, 'Interpreter', 'None');
impixelinfo;
% Now get the inner circle.
innerMask = (grayImage >= threshold) & outerMask;
innerMask = imfill(innerMask, 'holes');
innerMask = bwareafilt(innerMask, 1);
% Display the image.
subplot(2, 3, 5);
imshow(innerMask, []);
title('Inner Mask', 'FontSize', fontSize, 'Interpreter', 'None');
impixelinfo;
% Measure area and solidity.
props = regionprops(innerMask, 'Area', 'Solidity', 'ConvexHull', 'EquivDiameter');
fprintf('Inner Area = %d pixels. Inner Solidity = %.5f\n', props.Area, props.Solidity);
% Fit the convex hull perimeter to a circle.
[xCenter, yCenter, R, a] = FitCircle(props.ConvexHull(:, 1), props.ConvexHull(:, 2));
% Plot that circle in the overlay
hold on;
theta = linspace(0, 2*pi, 2 * (rows + columns));
x = R * cos(theta) + xCenter;
y = R * sin(theta) + yCenter;
plot(x, y, 'r-', 'LineWidth', 2);
% Make a mask of the circle.
circleMask = poly2mask(x, y, rows, columns);
% Do an xor with the mask to see where it's different.
differing = xor(circleMask, innerMask);
% Display the image.
subplot(2, 3, 6);
imshow(differing, []);
title('Non-Circular Parts', 'FontSize', fontSize, 'Interpreter', 'None');
impixelinfo;
function [xCenter, yCenter, R, a] = FitCircle(x,y)
% FitCircle() : Fits a circle in x-y plane.
% Result is center point (xCenter, yCenter) and radius R.
% "a" is an optional output describing the circle's equation:%
% x^2+y^2+a(1)*x+a(2)*y+a(3)=0
% by Bucher izhak 25/oct/1991
n = length(x);
xx = x.*x;
yy = y.*y;
xy = x.*y;
A = [sum(x) sum(y) n;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);
R = sqrt((a(1)^2+a(2)^2)/4-a(3));
end
  3 Comments
Image Analyst
Image Analyst on 20 Aug 2020
If you want to ignore small parts that are basically noise in the parts that are non-circular, you can use bwareafilt() or bwareaopen() to extract blobs larger than a specified size only.
Borhan Raad
Borhan Raad on 20 Aug 2020
Thank you again Image Analyst; I was inspired by your work and able to use the solidity value (by creating a data table) to distinguish between the brakes, the notches and the good rings....

Sign in to comment.

More Answers (1)

Image Analyst
Image Analyst on 20 Aug 2020
Edited: Image Analyst on 20 Aug 2020
Doesn't look too hard. If it's not cut into a C shape, try this:
  1. Background correct, if needed
  2. Threshold to find dark ring: mask = grayImage < someValue.
  3. Fill holes with imfill(mask, 'holes')
  4. Call bwareafilt(mask, 1) to retain only the largest blob.
  5. Call regionprops(mask, 'Solidity') to get "Solidity". Should be 1 if no nicks or cuts in the outer boundary.
  6. Threshold to find light pixels.
  7. Call imclearborder to remove surround (outside ring) and leave just the inner disc.
  8. Repeat steps 3 - 5
It's just a little more complicated to do steps 6-8 if there is a cut so that the rubber is in a C shape, but I'm sure you'll be able to figure it out. Then you can subtract the mask from the convex hull of the mask to get the area of the blobs sticking out of or into the ring.
  1 Comment
Borhan Raad
Borhan Raad on 20 Aug 2020
Thank you Image Analyst for your quick and prompt reply. would you please put this in an example? for some reason, I keep getting error (e.g. Error using bwpropfilt Expected input number 1, BW, to be two-dimensional.)
I'm follwing this approach:
I = imread('ORing01.jpg');
level = graythresh(I);
BW = imbinarize(I,level);
SE = strel('disk',10);
BI1 = imfill(BW,'holes');
BI2 = imopen(BI1,SE);
then I'm taking diff_image = BI1 - BI2; not sure if the conversion to binary is necessary, but I couldn't do it without it.
thank you for your help on this....

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!