How to trace the boundray of object in an image using MATLAB?

Hi,
I want to trace the boundary of an object in an image.(the bended black part)
I am attaching the binarised image herewith.
My questions are:
when I use dim= size(I) inin the following code, it gives different size and when I type in dim = size(BW), it gives different pixel size, so which should I follow?
I = imread('flap2.png');
imshow(I);
dim = size(I)
secondly how can I define the row and coloumn using this size information in order to continue with bwtraceboundary , because when i use this command it gives me a following error:
Error using bwtraceboundary
Expected input number 1, BW, to be two-dimensional.
waiting for a kind response.
Regards
Tayyaba

2 Comments

"I want to trace the boundary of an object in an image.(the bended black part)"
Which image? Can you attach it (Please use clip button)

Sign in to comment.

Answers (4)

se=strel('disk',2);
im=imerode(~binary_image,se);
result=bwareafilt(im,1);
result=imdilate(result,se);
imshow(result);
Please adjust the morpho operation to get more accurate results

2 Comments

To get the indices of boundary, apply hit and miss transformation, the
[r,c]=find(resultant_image==1)

Sign in to comment.

If you want to manually trace some boundary. Use drawfreehand().
If you have a binary image and you want an image of the perimeter only, then use bwperim().
perimImage = bwperim(binaryImage);
imshow(perimImage);
If you have a binary image and you want a list of the (x,y) coordinates, use bwboundaries().
boundaries = bwboundaries(binaryImage);
hold on; % Don't let boundaries blow away the image.
for k = 1 : length(boundaries)
thisBoundary = boundaries{k};
x = thisBoundary(:, 2);
y = thisBoundary(:, 1);
plot(x, y, 'LineWidth', 2);
end
hold off;

2 Comments

Thank you very much for your kind reply,
Yes I actually need the (x,y) coordinates of the boundary in the binary image.
I tried this command already but it states the following error:
Error using bwboundaries
Expected input number 1, BW, to be two-dimensional.
Error in bwboundaries>parseInputs (line 187)
validateattributes(BW_in, {'numeric','logical'}, {'real','2d','nonsparse'}, ...
Error in bwboundaries (line 140)
[BW, conn, findHoles] = parseInputs(args{:});
Error in Untitled3 (line 4)
boundaries = bwboundaries(BW);
Alltough the image is two-dimensional, still it displays this error.
Couldyou please help me in this regards
Thanks much.
Tayyaba
You passed in your color image. You need to use the binary image, after it's been converted to gray scale and segmented.

Sign in to comment.

Try this:
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 = 'image.jpeg';
% 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;
% The image has a huge white frame around it. Let's crop that away.
verticalProfile = all(grayImage == 255, 2);
row1 = find(~verticalProfile, 1, 'first');
row2 = find(~verticalProfile, 1, 'last');
horizontalProfile = all(grayImage == 255, 1);
col1 = find(~horizontalProfile, 1, 'first');
col2 = find(~horizontalProfile, 1, 'last');
% Do the crop
grayImage = grayImage(row1:row2, col1:col2);
% Display the image.
subplot(2, 3, 2);
imshow(grayImage, []);
axis('on', 'image');
title('Cropped Grayscale Image', 'FontSize', fontSize, 'Interpreter', 'None');
impixelinfo;
% Update size.
[rows, columns, numberOfColorChannels] = size(grayImage);
% Display histogram
subplot(2, 3, 3);
imhist(grayImage);
grid on;
title('Histogram of gray image', 'FontSize', fontSize);
%--------------------------------------------------------------------------------------------------------
% SEGMENTATION OF IMAGE
% Get a binary image
mask = grayImage < 25; %imbinarize(grayImage);
% Display the mask.
subplot(2, 3, 4);
imshow(mask, []);
impixelinfo;
title('Initial Binary Image', 'FontSize', fontSize, 'Interpreter', 'None');
impixelinfo;
% Make a circle mask to get rid of corners
% Create a logical image of a circle with specified
% diameter, center, and image size.
% First create the image.
imageSizeX = columns;
imageSizeY = rows;
[columnsInImage, rowsInImage] = meshgrid(1:imageSizeX, 1:imageSizeY);
% Next create the circle in the image.
centerX = columns/2;
centerY = rows/2;
radius = 450;
circlePixels = (rowsInImage - centerY).^2 ...
+ (columnsInImage - centerX).^2 <= radius.^2;
% circlePixels is a 2D "logical" array.
% Now, display it.
% imshow(circlePixels) ;
% title('Binary image of a circle');
% Erase the corners
mask = mask & circlePixels;
% Do an opening to break the stick away from the background things.
se = strel('disk', 2, 0);
mask = imopen(mask, se);
% Take the biggest blob.
mask = bwareafilt(mask, 1);
% Fill holes
mask = imfill(mask, 'holes');
% Blur it a bit to smooth it out.
windowSize = 17;
kernel = ones(windowSize, windowSize) / windowSize ^ 2;
mask = imfilter(mask, kernel) > 0.5;
subplot(2, 3, 5);
imshow(mask, []);
impixelinfo;
title('Final Binary Image', 'FontSize', fontSize, 'Interpreter', 'None');
impixelinfo;
% Get boundaries and plot them, just for fun.
boundaries = bwboundaries(mask);
subplot(2, 3, 6);
imshow(grayImage); % Show cropped image again.
hold on;
for k = 1 : length(boundaries)
thisBoundary = boundaries{k};
x = thisBoundary(:, 2);
y = thisBoundary(:, 1);
plot(x, y, 'r-', 'LineWidth', 2);
end
title('Image With Boundaries', 'FontSize', fontSize, 'Interpreter', 'None');

15 Comments

Thank you very much for such a detailed description :).Actually it took me a while to understand it.
I did not understand the following points:
  1. In cropping: how 255 is slected?
  2. For creating circles, radius = 450; is used? how this number is selected.
  3. If I run the code without above mentioned points, it looks like as an attached image.(Fig 1), the image is inverted and I do not find the pixel information on boundaries too.
  4. The last question is: Actually I have to draw the boundaries just on the one edge, them how it can be done?(Image is attached).
Thank you very much for your help, waiting for a kind response further.
Regards
Tayyaba
  1. You did not post the actual image but it appeared to be some kind of screenshot. It had a huge white frame around it as the comments said. To get rid of that frame and get to just the image inside, I had to crop it. I identified the cropping region by noting that the white frame had a value of exactly 255. You would not have to do this step if you did not have an image with a big white frame around it.
  2. I just guessed at the 450 to get a mask that would exclude the corners. If you don't like it, you can experiment around with different radii.
  3. Post an image without the white frame and I'll try it.
  4. You can scan the image column by column finding the lowest row that is white
lastRow = zeros(1, columns);
for col = 1 : columns
r = find(mask(:, col), 1, 'last');
if ~isempty(r)
% There is at least one white pixel in this column.
lastRow(col) = r;
end
end
Thank you very much for your reply.
I used the following code for the cropped image as attached. And the objective is to get the boundary across the one end only as shown last time.
grayImage = imread('flap2.tif');
[rows, columns, numberOfColorChannels] = size(grayImage);
if numberOfColorChannels > 1
grayImage = rgb2gray(grayImage);
end
% Display the image.
subplot(2, 3, 1);
imshow(grayImage, []);
title('Original Grayscale Image');
impixelinfo;
%--------------------------------------------------------------------------------------------------------
% SEGMENTATION OF IMAGE
% Get a binary image
mask = grayImage < 25; %imbinarize(grayImage);
% Display the mask.
subplot(2, 3, 4);
imshow(mask, []);
impixelinfo;
title('Initial Binary Image');
impixelinfo;
% Get boundaries and plot them.
boundaries = bwboundaries(mask);
subplot(2, 3, 6);
hold on;
for k = 1 : length(boundaries)
thisBoundary = boundaries{k};
x = thisBoundary(:, 2);
y = thisBoundary(:, 1);
plot(x, y, 'r-', 'LineWidth', 2);
end
Why didn't you use the code I gave you in my last comment to find the last row of the fiber?
I used that too but I got the same results, as I have attached previously,
it is giving the edge in the middle I guess and it is also still not clear.
may be i have to change the threshold for mask, instead of 25.
im attaching it again. and code is as follows:
grayImage = imread('flap2.tif');
[rows, columns, numberOfColorChannels] = size(grayImage);
if numberOfColorChannels > 1
grayImage = rgb2gray(grayImage);
end
% Display the image.
subplot(2, 3, 1);
imshow(grayImage, []);
title('Original Grayscale Image');
impixelinfo;
%--------------------------------------------------------------------------------------------------------
% SEGMENTATION OF IMAGE
% Get a binary image
mask = grayImage < 25; %imbinarize(grayImage);
% Display the mask.
subplot(2, 3, 4);
imshow(mask, []);
impixelinfo;
title('Initial Binary Image');
impixelinfo;
% Get boundaries and plot them.
boundaries = bwboundaries(mask);
subplot(2, 3, 6);
hold on;
for k = 1 : length(boundaries)
thisBoundary = boundaries{k};
x = thisBoundary(:, 2);
y = thisBoundary(:, 1);
plot(x, y, 'r-', 'LineWidth', 2);
end
title('Image With Boundaries');
lastRow = zeros(1, columns);
for col = 1 : columns
r = find(mask(:, col), 1, 'last');
if ~isempty(r)
% There is at least one white pixel in this column.
lastRow(col) = r;
end
end
Thanks,
it is the same as attached 'Fig.png' previously.
I am attaching it again.
I was hoping for the original image without the big white frame so we don't need to do the cropping operation. But anyway, try the attached.
Adapt as needed.
Thank you very much for your great help. It is working well :)
Im really sorry but I didnot understand this part of the code at the end to define the boundary:
What is meant by the first row actually?
firstRow = zeros(1, columns);
for col = 1 : columns
thisColumn = find(mask(:, col), 1, 'first');
if ~isempty(thisColumn)
% There is at least one white pixel in this column.
firstRow(col) = thisColumn;
end
end
% Get rid of columns with no white pixels or where it hits the top of the image.
x = 1 : columns;
badColumns = firstRow <= 0;
x(badColumns) = [];
firstRow(badColumns) = [];
% Replace any outliers.
[firstRow, outlierIndexes] = filloutliers(firstRow, 'spline');
x = x(~outlierIndexes);
I attach herewith my images,
I need the other edge, so how can I adjust the parameters in the above part to define the boundary.
Thanks again
Tayyaba
It is alos working well with the boundaries, you suggested me the last time.
Please see the attached image.
Regards
Tayyaba
The first row is the top edge. If you need the bottom row also, you can use this:
firstRow = zeros(1, columns);
lastRow = zeros(1, columns);
for col = 1 : columns
thisColumn = find(mask(:, col), 1, 'first');
if ~isempty(thisColumn)
% There is at least one white pixel in this column.
firstRow(col) = thisColumn;
lastRow(col) = find(mask(:, col), 1, 'last');;
end
end
Thanks a lot for your kind help.
I have a problem again with boundaries:
Attached image-1 did not indicate the full boundary, when I set mask paramerer nearly 1000 in
mask = bewareaopen(mask,1000)
Howver when I enter the small value i.e 500 or 300 it indicates boundaries as shown in 2.
But the resulting X and Y values are not sufficient.
Could you please help me in this.Which parameter should I adjust for it?
I am attaching the original image also (401).
Thanks again
Regards
Tayyaba
401 is not the original image - it has a huge white frame around it. I'd try adjusting the threshold. You could also try morphological operations like imopen() to see if you can cut off some of the bright background that intrudes into the shape and gives it a ragged boundary. Perhaps the image capture conditions could also be improved, like change the exposure, lighting geometry, use polarizers, optical filters, or other such "tricks".
A bit more complicated, you could try deep learning with SegNet:
Thank you very much for your detailed reply.
Actually I have to apply the code for number of images. Initially it worked well but for 401 and some others it doesnot show the compelete boundary.
Any how I am trying further to improve it.
Regards
Tayyaba
Can you increase your exposure time to get a less noisy photo?
Maybe try some denoising routines, of which there are many. Maybe imnlmfit().

Sign in to comment.

Hi,
I have averaged 100 images and I have to get the boundary of that average image.
I run the following code for getting boundaries:
grayImage = imread('100.tif');
[rows, columns, numberOfColorChannels] = size(grayImage);
if numberOfColorChannels > 1
grayImage = rgb2gray(grayImage);
end
% Display the image.
subplot(2, 3, 1);
imshow(grayImage, []);
title('Original Grayscale Image');
impixelinfo;
% Croping image.
grayImage = imcrop(grayImage);
% Display the image.
subplot(2, 3, 2);
imshow(grayImage, []);
axis('on', 'image');
title('Cropped Grayscale Image');
impixelinfo;
% Update size.
[rows, columns, numberOfColorChannels] = size(grayImage);
% Display histogram
subplot(2, 3, 3);
imhist(grayImage);
grid on;
title('Histogram of gray image');
%--------------------------------------------------------------------------------------------------------
% SEGMENTATION OF IMAGE
% Get a binary image
mask = grayImage < 39
; %imbinarize(grayImage);
% Display the mask.
subplot(2, 3, 4);
imshow(mask, []);
impixelinfo;
title('Initial Binary Image');
impixelinfo;
% Make a circle mask to get rid of corners
% Create a logical image of a circle with specified
% diameter, center, and image size.
% First create the image.
imageSizeX = columns;
imageSizeY = rows;
[columnsInImage, rowsInImage] = meshgrid(1:imageSizeX, 1:imageSizeY);
% Next create the circle in the image.
centerX = columns/2;
centerY = rows/2;
% radius = max(centerX, centerY);
radius = 500;
circlePixels = (rowsInImage - centerY).^2 ...
+ (columnsInImage - centerX).^2 <= radius.^2;
% circlePixels is a 2D "logical" array.
% Now, display it.
% imshow(circlePixels) ;
% title('Binary image of a circle');
% Erase the corners
mask = mask & circlePixels;
% Fill interior holes.
mask = imfill(mask, 'holes');
% Check areas so we know what the size of the small specks are so we can filter them out.
props = regionprops(mask, 'Area');
allAreas = sort([props.Area], 'ascend')
% Get rid of blobs smaller than 300 in size
mask = bwareaopen(mask, 300);
subplot(2, 3, 5);
imshow(mask, []);
impixelinfo;
title('Final Binary Image');
impixelinfo;
% Get boundaries .
boundaries = bwboundaries(mask);
subplot(2, 3, 6);
imshow(grayImage);
hold on;
for k = 1 : length(boundaries)
thisBoundary = boundaries{k};
x = thisBoundary(:, 2);
y = thisBoundary(:, 1);
plot(x, y, 'r-', 'LineWidth', 2);
end
title('Image With Boundaries')
The average image is blur and also because of this the boundaries are not clear. I run the following code for taking average:
I0 = imread('img_1.tif')
sumImage = double(I0); % Inialize to first image.
for i=2:100
rgbImage = imread(['img_',num2str(i),'.tif']);
sumImage = sumImage + double(rgbImage);
end;
meanImage = sumImage / 100;
imshow(meanImage(:), []);title('Average');
imshow(uint8(meanImage));
The original averaged image and the result of the boundary code are attached herewith.
Thanks and waiting for your kind response.
Regards
Tayyaba Bano

3 Comments

You posted this as an Answer to your original question. And I don't see any sentence in there ending in a question mark. So I'm not going to do anything with this Answer, if that's what you were expecting. I also gave two Answers above.
Im really sorry for that.
I want to ask:
  1. How to avoid blur edges in the averaged image?
  2. How to extract the boundary from that average image?
Thanks
Tayyaba Bano
Another point I have noticed that, the difference in the scale for averaged images and the scale beforetaking the average.
For example the scale of averaged image is ranging from 0-450 whereas the scale before taking the average is 0-1400.
I attached the cropped image before taking the average.
Why the difference in scale with average?
Kind regards
Tayyaba Bano

Sign in to comment.

Asked:

on 26 Nov 2020

Commented:

on 30 Aug 2021

Community Treasure Hunt

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

Start Hunting!