what is the best way to determine the boundary of a porous object in an 2-D image?
Show older comments
edge detection returns many internal edges which is not desired sample image:
Accepted Answer
More Answers (3)
Image Analyst
on 23 Dec 2012
Edited: Image Analyst
on 23 Dec 2012
0 votes
Use a better edge detector. You'd get a better answer if you uploaded your image so I could then recommend more appropriate functions than edge(). See this http://www.mathworks.com/matlabcentral/answers/7924-where-can-i-upload-images-and-files-for-use-on-matlab-answers for places where you can upload images. Pick one where I can see it right away and not have to download or signup for anything.
If you have the outer edge intact, and just need to get rid of spurious internal edges, then you can use imfill(), but that will not work if the outer edge has a gap in it. Gaps can be closed with imclose() or edge linking methods.
3 Comments
Image Analyst
on 23 Dec 2012
Edited: Image Analyst
on 23 Dec 2012
I see you posted your image now. It looks like something that at one time had nice solid boundaries that were completely closed, but now was imaged in some way such that the boundaries are now broken. What I would do it do first to convert to gray level, then invert and threshold. Then call imclose to close the gaps. Then you can call bwboundaries() or bwperim() though I doubt that is the end of your task. Chances are that once you have the boundaries, you are going to want to do something else with them, like measure their area, count them, find the area fraction, or something. What is your final goal with this? I'm sure it's not just finding boundaries. Unfortunately it looks like your image somehow got some bad jpeg artifacts upon uploading. Nonetheless I did the best I could with a few minutes of programming and this is the result:
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
imtool close all; % Close all imtool figures.
clear; % Erase all existing variables.
workspace; % Make sure the workspace panel is showing.
format longg;
format compact;
fontSize = 20;
% http://imageshack.us/photo/my-images/836/36543017.png/
% Read in a standard MATLAB gray scale demo image.
folder = 'C:\Users\A\Documents\Temporary';
baseFileName = '36543017.png';
% Get the full filename, with path prepended.
fullFileName = fullfile(folder, baseFileName);
% Check if file exists.
if ~exist(fullFileName, 'file')
% File doesn't exist -- 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 in the search path folders.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
grayImage = imread(fullFileName);
% Get the dimensions of the image.
% numberOfColorBands should be = 1.
[rows columns numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
grayImage = grayImage(:,:,1);
end
% Display the original gray scale image.
subplot(2, 2, 1);
imshow(grayImage, []);
title('Original Grayscale Image', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'units','normalized','outerposition',[0 0 1 1]);
% Give a name to the title bar.
set(gcf,'name','Demo by ImageAnalyst','numbertitle','off')
% Let's compute and display the histogram.
% [pixelCount grayLevels] = imhist(grayImage);
% subplot(2, 2, 2);
% bar(pixelCount);
% grid on;
% title('Histogram of original image', 'FontSize', fontSize);
% xlim([0 grayLevels(end)]); % Scale x axis manually.
% Threshold image.
binaryImage = grayImage < 65000;
% binaryImage = imfill(binaryImage, 'holes');
% Display the image.
subplot(2, 2, 2);
imshow(binaryImage, []);
title('Binary Image', 'FontSize', fontSize);
% Close gaps
binaryImage = imclose(binaryImage, true(3));
% Display the image.
subplot(2, 2, 3);
imshow(binaryImage, []);
title('After morphological closing', 'FontSize', fontSize);
% Get the interiors
interiors = imclearborder(~binaryImage);
% Get rid of blobs smaller than 60 pixels.
interiors = bwareaopen(interiors, 60);
% Display the image.
subplot(2, 2, 4);
imshow(interiors, []);
title('Interiors', 'FontSize', fontSize);
% Label the image
labeledImage = bwlabel(interiors);
measurements = regionprops(labeledImage, 'Area', 'Solidity');
allAreas = [measurements.Area]
allSolidities = [measurements.Solidity];
% Get rid of weird-shaped objects
% Get a list of the blobs that meet our criteria and we need to keep.
allowableSolidityIndexes = (allSolidities > 0.8);
keeperIndexes = find(allowableSolidityIndexes);
% Extract only those blobs that meet our criteria, and
% eliminate those blobs that don't meet our criteria.
% Note how we use ismember() to do this.
keeperBlobsImage = ismember(labeledImage, keeperIndexes);
% Re-label with only the keeper blobs kept.
[labeledImage, numberOfBlobs] = bwlabel(keeperBlobsImage, 8); % Label each blob so we can make measurements of it
binaryImage = labeledImage > 0;
figure;
subplot(2, 2, 1);
imshow(binaryImage, []);
title('Filtered by Solidity', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'units','normalized','outerposition',[0 0 1 1]);
% Give a name to the title bar.
set(gcf,'name','Demo by ImageAnalyst','numbertitle','off')
% Find boundaries by bwperim
% NOTE: THIS WILL LOOK BROKEN ON THE SCREEN DUE TO SUBSAMPLING/SHRINKING THE IMAGE
boundaries1 = bwperim(binaryImage);
subplot(2, 2, 2);
imshow(boundaries1, []);
title('Boundaries via bwperim()', 'FontSize', fontSize);
% Find the boundaries via bwboundaries.
% First display the original image.
subplot(2, 2, 3);
imshow(grayImage, []);
title('Original Image (Again)', 'FontSize', fontSize);
subplot(2, 2, 4);
imshow(grayImage, []);
title('Boundaries via bwboundaries()', 'FontSize', fontSize);
% bwboundaries() returns a cell array, where each cell contains the row/column coordinates for an object in the image.
% Plot the borders of all the coins on the original grayscale image using the coordinates returned by bwboundaries.
hold on;
boundaries = bwboundaries(binaryImage);
numberOfBoundaries = size(boundaries, 1);
for k = 1 : numberOfBoundaries
thisBoundary = boundaries{k};
plot(thisBoundary(:,2), thisBoundary(:,1), 'r-', 'LineWidth', 3);
end
hold off;
Image Analyst
on 25 Dec 2012
Edited: Image Analyst
on 25 Dec 2012
What does "doesn't work" mean? I gave you some sample code that found the insides of the shapes. It does work at doing that, and, given no clear guidance, that's what I decided to do. It was not meant to be a complete turnkey solution for all your images. I don't know what the void space is and what the solid space is. All you've given me is a binary image - no gray scale image for context. What is the boundary? Is it each bean-shaped thing? So you want the porosity inside each little bean shape? Or is the boundary the boundary of the entire group of shapes, and the insides of the individual shapes are porous (void space) and everything else (individual boundaries plus the interstitial space) is solid? Please clarify.
If a boundary is each small individual boundary, and you want the porosity inside each boundary, then I found the insides for you. But inside each is just uniform white. How can I find porosity in that case? Again, I don't know what's void space and what's solid.
If you want the envelope of the outside you can use the union convex hull, performed by bwconvexhull() of the Image Processing Toolbox, or you can use a restricted convex hull (I have code for that) or alpha shapes, both of which will allow the outer shape to bend into bays to allows the boundary to more closely follow the shapes at the outer part of the group. Or you could run imclose with a big kernel, call imfill() and then call bwboundaries. It just depends on what you consider the "outside boundary" of your region to be. The definition is not so clear cut if your region is the group of shapes.
You can look up imclose(). You'll see the second argument is the shape of the structuring element. You can tailor it to certain shapes, and I tried that, taking vertical and horizontal slivers, but a 3 by 3 box seemed to work best.
Walter Roberson
on 23 Dec 2012
0 votes
regionprops() and extract the boundary pixels list?
1 Comment
Image Analyst
on 23 Dec 2012
Or bwboundaries() or bwperim() assuming you have the "right" image to start with.
Walter Roberson
on 25 Dec 2012
0 votes
Perhaps what you want to do is use something like John D'Errico's Alpha Shapes routines to wrap a boundary around the collection of points you have. After you have that boundary, regionprops can tell you the porosity.
11 Comments
A
on 25 Dec 2012
Walter Roberson
on 25 Dec 2012
Image Analyst
on 25 Dec 2012
Edited: Image Analyst
on 25 Dec 2012
A
on 26 Dec 2012
Image Analyst
on 26 Dec 2012
But aren't you going to define porosity or share the gray scale image from where your binary image came from?
A
on 26 Dec 2012
Image Analyst
on 27 Dec 2012
Which is the porous part? The thick boundaries, or the interiors of them?
Image Analyst
on 28 Dec 2012
So, the thick boundary is solid, and both inside of that and outside of that is porous. Correct? But what do you want to know? The area fraction of porous pixels? That would be simply
porosity = sum(binaryImage(:))/numel(binaryImage).
A
on 28 Dec 2012
Image Analyst
on 28 Dec 2012
See code in my new answer.
Categories
Find more on Material Sciences 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!