Area calculation in MATLAB - matlab

I am working on a paper and I need to find out the area of the region having color black from the image that I have attached.
Original image
I have done some processing by using threshold and complimenting the image.Processed image
Now I'm having a problem finding the area of the black colored region. Can someone please help? I'm new to MATLAB.
Here is my code :
img1=imread('C:/Users/Allan/Desktop/unnamed1.jpg');
imshow(img1)
img1=rgb2gray(img1);
imshow(img1)
img2=im2bw(img1,graythresh(img1));
imshow(img2)
img2=~img2;
imshow(img2)
B = bwboundaries(img2);
imshow(img2)
hold on
for k = 1:length(B)
boundary = B{k};
plot(boundary(:,2), boundary(:,1), 'g', 'LineWidth', 0.2)
end

use regionprops or bwarea:
% take the NOT image
bw = ~img2;
% find individual regions
cc = bwconncomp(bw);
% find area for each black region
props = regionprops(cc,{'Area','Centroid'});
regionArea = [props(:).Area];
% find area of total black region
totalArea = bwarea(bw);
% plotting
for ii = find(regionArea > 100)
c = props(ii).Centroid;
text(c(1),c(2),num2str(regionArea(ii)),'Color','b',...
'HorizontalAlignment','center','VerticalAlignment','middle',...
'FontWeight','bold');
end

Related

How can I detect the defect in this image of a semiconductor?

So I have this image of a semiconductor wafer and it has a defect in it which I need to detect using Matlab. I should detect only it and not its background. I need to also measure its perimeter and area.
So far I have this code where I convert the original image to a binary image and then use dilation on it and then try to get its contour. When getting the perimeter and the area, I receive the outcome not only for the defect but for the rest of the image which is not what I want. How can I extract the defect only so I can get only its area and parameter.
The image:
fig3 = imread('figure3.png');
imshow(fig3);
title('Original image', 'FontSize', 18);
%Gray image
fig3Gray = rgb2gray(fig3);
%Binary image
BW3 = imbinarize(fig3Gray,0.5);
imshow(BW3);
title('Binary image', 'FontSize', 18);
se3 = strel('square',5);
%Dilation image
dilated3 = imdilate(BW3,sr);
imshow(dilated3);
title('Dilated image', 'FontSize', 18);
minus3 = ~(BW3-dilated3);
imshow(minus3);
title('Contour image', 'FontSize', 18);
imshowpair(minus3,BW3,'montage');
%Perimeter and Area calculation
Coon3 = bwconncomp(~BW3)
ANS3 = length(Coon3.PixelIdxList{1});
OUTPUT3 = regionprops(Coon3,'Perimeter','Area');
P3 = OUTPUT3.Perimeter
Area3 = OUTPUT3.Area
Let's start by reading the image and converting it to binary. Note that I have lowered the threshold to eliminate unwanted details.
clear; close all; clc
fig3 = imread('XEQ59.png');
imshow(fig3);
title('Original image', 'FontSize', 18);
%Gray image
fig3Gray = rgb2gray(fig3);
%Binary image
BW3 = imbinarize(fig3Gray, 0.2); % lowered threshold
figure; imshow(BW3)
title('binary image')
Now we move on to find the coordinate of the defect. To do this, we define a structuring element that defines our desired shape se.
We are looking for the parts in the image that match with se. For a given coordinate to match, the surrounding area must exactly be se.
Note that gray values here are ignored, they can be either white or black.
se is manually defined where 1 represents white, -1 represents black, and 0 represents ignored pixels.
% hit-miss
se = [1, 1, -1*ones(1,5), ones(1, 3); ...
ones(6,1), -1*ones(6), zeros(6,2), ones(6,1); ...
ones(3,2), zeros(3,1), -1*ones(3,6), ones(3,1)];
figure; imshow(uint8(255/2*(se+1)), 'InitialMagnification', 3000)
title('structuring element')
Applying hit-miss operation to find the position of the defect:
pos = bwhitmiss(BW3, se);
figure; imshow(pos)
title('position of defect')
input('Press enter to continue...')
Now that we have the position, we grow that particular pixel position until it grows no more, in order to obtain defect.
% get the defect
close all; clc
def = pos;
last_def = zeros(size(def));
while ~isequal(def, last_def)
last_def = def;
def = ~BW3 & imdilate(def, ones(3));
imshow(def)
title('defect')
pause(0.1)
end
Calculating the area and the perimeter:
% area
area = sum(def(:))
% perimeter
vert = imdilate(def, [1; 1; 1]) - def;
horz = imdilate(def, [1 1 1]) - def;
perimeter = sum(vert(:)) + sum(horz(:))
area =
102
perimeter =
54
This question is much more difficult then your previous question.
Following solution uses an iterative approach (two iterations).
Includes heuristic about the difference from the cluster to it's neighbors.
Includes a heuristic that the cluster can't be too tall or too long.
Please read the comments:
clear
fig3 = imread('figure3.png');
fig3Gray = rgb2gray(fig3);
fig3Gray = im2double(fig3Gray); %Convert from uint8 to double (MATLAB math works in double).
%figure;imshow(fig3Gray);
%Apply median filter with large radius.
Med = medfilt2(fig3Gray, [51, 51], 'symmetric');
%figure;imshow(Med);
D = abs(fig3Gray - Med);
%figure;imshow(D);impixelinfo
BW = imbinarize(D, 0.3);
%figure;imshow(BW);impixelinfo
Coon = bwconncomp(BW);
fig3GrayMasked = fig3Gray;
%Cover the tall clusters and the long clusters.
for i = 1:length(Coon)
C = Coon.PixelIdxList{i}; %Cluster coordinates.
[Y, X] = ind2sub(size(fig3Gray), C); %Convert to x,y indices.
is_tall = (max(Y) - min(Y)) > 50; %true if cluster is tall.
is_wide = (max(X) - min(X)) > 50; %true if cluster is wide.
%Replace tall and long clusters by pixels from median image.
if ((is_tall) || (is_wide))
fig3GrayMasked(C) = Med(C);
end
end
%figure;imshow(fig3GrayMasked);impixelinfo
%Second iteration: search largest cluster on fig3GrayMasked image.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Med = medfilt2(fig3GrayMasked, [51, 51], 'symmetric');
D = abs(fig3GrayMasked - Med);
%figure;imshow(D);impixelinfo
BW = imbinarize(D, 0.3);
%figure;imshow(BW);impixelinfo
Coon = bwconncomp(BW);
%Find index of largest cluster in list of clusters Coon.PixelIdxList
[~, i] = max(cellfun(#numel, Coon.PixelIdxList));
%Get the indices of the largest cluster
C = Coon.PixelIdxList{i};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Paint cluster in yellow color (just for fun).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
BW = zeros(size(BW), 'logical');
BW(C) = 1;
Y = im2uint8(cat(3, ones(size(BW)), ones(size(BW)), zeros(size(BW))));
fig3(cat(3, BW, BW, BW)) = Y(cat(3, BW, BW, BW));
figure;imshow(fig3)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Result:

How to classify objects with different number of holes?

My object is classifying cells in the given image. Classification is based on the shape and number of the nucleus(holes). Some of the cells have a circular type while others not. Some of has one hole (nucleus), while others have more.
After some clearing and preprocessing, I have classified circular and overlapped cells so far. But there is a cell with two holes(nucleus) which should belong to another class. But I couldn't do it. If I can count the number of holes, I think I can classify also that cell.
im =imread('blood.jpg'); % Reading target image
figure(1),imshow(im), title('Original Image');
imGray = rgb2gray(im); % Converting image RGB to gray-level (2-Dimensional).
gray_thresh = graythresh(imGray);
bw = im2bw(imGray, gray_thresh); figure(2), imshow(bw), title('Binary Image'); % Converting image to binary.
bw = imcomplement(bw); % Taking image's complement.
figure(3), imshow(bw), title('Complement of Image');
%bw=imfill(bw,'holes');
bw = imclearborder(bw);
figure(4), imshow(bw), title('Objects touching image borders removed');
bw = bwareaopen(bw,500); % Remove objects smaller than 500px.
figure(5),imshow(bw); title('Small objects removed');
label = bwlabel(bw);
[B,L] = bwboundaries(bw,'noholes');
stats = regionprops(L,'Area','Centroid');
threshold = 0.70;
figure(6), imshow(bw)
hold on
for k = 1:length(B)
% obtain (X,Y) boundary coordinates corresponding to label 'k'
boundary = B{k};
% compute a simple estimate of the object's perimeter
delta_sq = diff(boundary).^2;
perimeter = sum(sqrt(sum(delta_sq,2)));
% obtain the area calculation corresponding to label 'k'
area = stats(k).Area;
% compute the roundness metric
metric = 4*pi*area/perimeter^2;
if metric > threshold
plot(boundary(:,2),boundary(:,1),'r','LineWidth',2)
%Circular
else
plot(boundary(:,2),boundary(:,1),'g','LineWidth',2)
%Overlapped
end
end
Cell with two nucleus(holes)

Create boundary around the object

I have the input image. I want to create the boundary around the object and marked the boundary pixels as gray i.e. 128 intensity value. I need this type of output image. I traced the boundary of the object:
level = graythresh(im);
BW = im2bw(im,level);
figure; imshow(BW);
[B,L] = bwboundaries(BW,'noholes');
imshow(im);
hold on
for k = 1:length(B)
boundary = B{k};
plot(boundary(:,2), boundary(:,1), 'Color', [0.5 0.5 0.5], 'LineWidth', 5)
end
But it will only plot the boundary over the image. I want to create this boundary around the object of input image. How can I do this? I hope my question is clear.
While I don't know why you wan this, a word of advice: you may not want to do it. You are losing information of the image by doing so. It is possible that for you application knowing the indices of the boundaries is enough, and you do already know them.
However, this is what you would need to do in case of really wanting to do it:
clear;
clc;
im=imread('https://i.stack.imgur.com/r7uQz.jpg');
im=im(31:677,90:1118);
level = graythresh(im);
BW = im2bw(im,level);
[B,L] = bwboundaries(BW,'noholes');
for k = 1:length(B)
boundary = B{k};
%%%%%%%%% Fill the boundary locations with he desired value.
for ii=1:size(boundary,1)
im(boundary(ii,1), boundary(ii,2))=128;
end
end
If you want "fat" lines as in your image, add this after:
im128=im==128;
bigboundary=imdilate(im128,ones(5));
im(bigboundary)=128;
You can binarize the image and detect the contours using OpenCV.
Here is the Python implementation:
img = cv2.imread(r'D:\Image\temp1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,bw = cv2.threshold(gray,100,255,cv2.THRESH_BINARY)
_,contours,hierarchy = cv2.findContours(bw, cv2.RETR_CCOMP, 1)
for cnt in contours:
c = np.random.randint(0,256,3)
c = (int(c[0]), int(c[1]), int(c[2]))
cv2.drawContours(img,[cnt],0,c,2)
Output image:

Convhull() is not giving desired result.

I want to get the convex hull of hand palm by using convhull() function. I am working on an image which is having only a hand palm in it. First I converted it to a binary image and then I applied convhull function. But it is not giving me the desired result. Kindly find the bug in my code. Here is my code:
thresh1 = 0;
thresh2 = 20;
image = imread('C:\Users\...\1_depth.png');
subplot(3,3,1)
imshow(image)
image_bin1 = (image < thresh2);
image_bin2 = (thresh1 < image);
image_bin = abs(image_bin2- image_bin1);
image_bin_filt = medfilt2(image_bin, [18,18]);
subplot(3,3,2)
imshow(imcomplement(image_bin_filt));
BW = im2bw(image_bin_filt, 0.5);
BW = imcomplement(BW);
subplot(3,3,3)
imshow(BW)
title('Binary Image of Hand');
BW2 = bwareaopen(BW, 1000);
subplot(3,3,4)
imshow(BW2)
[y,x] = find(BW2);
k = convhull(x,y);
subplot(3,3,5)
imshow(BW2,'InitialMagnification', 'fit')
hold on;
plot(x,y, 'b.')
plot(x(k), y(k), 'r', 'LineWidth', 2)
title('Objects Convex Hull');
% Find centroid.
labeledImage = bwlabel(BW2);
measurements = regionprops(labeledImage, 'Centroid', 'BoundingBox');
%xCentroid = measurements.Centroid(1);
% yCentroid = measurements.Centroid(2);
centroids = cat(1, measurements.Centroid);
subplot(3, 3, 6);
imshow(BW2);
title('Binary Image with Centroid Marked', 'FontSize', 12);
hold on;
plot(centroids(:,1),centroids(:,2), 'b*')
% Crop the image and display it in a new figure.
boundingBox = measurements.BoundingBox;
croppedImage = imcrop(BW2, boundingBox);
% Get rid of tool bar and pulldown menus that are along top of figure.
%set(gcf, 'Toolbar', 'none', 'Menu', 'none');
subplot(3,3,7)
imshow(croppedImage, []);
title('Cropped Image', 'FontSize', 12, 'Interpreter', 'None');
% Again trying to plot the convex hull
CH_objects = bwconvhull(BW);
subplot(3,3,8)
imshow(CH_objects);
title('Objects Convex Hull');
[r,c]=find(CH_objects);
CH=convhull(r,c);
subplot(3,3,9)
imshow(CH_objects)
hold on;
plot(r(CH),c(CH),'*-');
Here is the result of the code I am getting: https://ibb.co/gLZ555
But it is not the desired result. The convex hull is not proper, it should include only palm and not free space. Also, I am getting two centroids instead of one. Why it is so?
Input image I used is: https://ibb.co/hk28Q5
I want to calculate convex hull of palm containing only palm in it and then want to calculate centroid of the palm. It will be helpful in detecting palm shape.
Kindly reply with the solution for the desired output.
Do you mean the image should contain only palm which is with white pixles, not the black pixles. If it is so then you need to crop the image by considering largest blob area as extracted by 'regionprops'. Then apply convexhull on that only.
I just added this piece of code before applying convhull() and it solved my problem.
roi = regionprops(BW2, 'Area');
BW2 = bwareafilt(BW2,1);
subplot(3,3,5)
imshow(BW2)

Drawing the major and minor axis of an elliptical object in MATLAB

This program currently inputs an image of a coin, thresholds it, binarizes it, and finds the major and minor axis lengths of the segmented elliptical using the regionprops function. How do I output a subplot where I draw the axes used to calculate the 'MajorAxisLength' and 'MinorAxisLength' over the original image?
I have appended my code for your perusal.
% Read in the image.
folder = 'C:\Documents and Settings\user\My Documents\MATLAB\Work';
baseFileName = 'coin2.jpg';
fullFileName = fullfile(folder, baseFileName);
fullFileName = fullfile(folder, baseFileName);
if ~exist(fullFileName, 'file')
fullFileName = baseFileName; % No path this time.
if ~exist(fullFileName, 'file')
%Alert user.
errorMessage = sprintf('Error: %s does not exist.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
rgbImage = imread(fullFileName);
% Get the dimensions of the image. numberOfColorBands should be = 3.
[rows columns numberOfColorBands] = size(rgbImage);
% Display the original color image.
subplot(2, 3, 1);
imshow(rgbImage, []);
title('Original color Image', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'Position', get(0,'Screensize'));
% Extract the individual red color channel.
redChannel = rgbImage(:, :, 1);
% Display the red channel image.
subplot(2, 3, 2);
imshow(redChannel, []);
title('Red Channel Image', 'FontSize', fontSize);
% Binarize it
binaryImage = redChannel < 100;
% Display the image.
subplot(2, 3, 3);
imshow(binaryImage, []);
title('Thresholded Image', 'FontSize', fontSize);
binaryImage = imfill(binaryImage, 'holes');
labeledImage = bwlabel(binaryImage);
area_measurements = regionprops(labeledImage,'Area');
allAreas = [area_measurements.Area];
biggestBlobIndex = find(allAreas == max(allAreas));
keeperBlobsImage = ismember(labeledImage, biggestBlobIndex);
measurements = regionprops(keeperBlobsImage,'MajorAxisLength','MinorAxisLength')
% Display the original color image with outline.
subplot(2, 3, 4);
imshow(rgbImage);
hold on;
title('Original Color Image with Outline', 'FontSize',fontSize);
boundaries = bwboundaries(keeperBlobsImage);
blobBoundary = boundaries{1};
plot(blobBoundary(:,2), blobBoundary(:,1), 'g-', 'LineWidth', 1);
hold off;
I had the same task as you for some project I did 2 years ago. I've modified the code I used then for you below. It involved calculating the covariance matrix for the datapoints and finding their eigenvalues/eigenvectors. Note here that because of circular symmetry, the minor and major axis will be somewhat "random". Also note that I have made the image binary in a very naïve way to keep the code simple.
% Load data and make bw
clear all;close all; clc;
set(0,'Defaultfigurewindowstyle','docked')
I = imread('american_eagle_gold_coin.jpg');
Ibw = im2bw(I,0.95);
Ibw = not(Ibw);
figure(1);clf
imagesc(Ibw);colormap(gray)
%% Calculate axis and draw
[M N] = size(Ibw);
[X Y] = meshgrid(1:N,1:M);
%Mass and mass center
m = sum(sum(Ibw));
x0 = sum(sum(Ibw.*X))/m;
y0 = sum(sum(Ibw.*Y))/m;
%Covariance matrix elements
Mxx = sum(sum((X-x0).^2.*Ibw))/m;
Myy = sum(sum((Y-y0).^2.*Ibw))/m;
Mxy = sum(sum((Y-y0).*(X-x0).*Ibw))/m;
MM = [Mxx Mxy; Mxy Myy];
[U S V] = svd(MM);
W = V(:,1)/sign(V(1,1)); %Extremal directions (normalized to have first coordinate positive)
H = V(:,2);
W = 2*sqrt(S(1,1))*W; %Scaling of extremal directions to give ellipsis half axis
H = 2*sqrt(S(2,2))*H;
figure(1)
hold on
plot(x0,y0,'r*');
quiver(x0,y0,W(1),H(1),'r')
quiver(x0,y0,W(2),H(2),'r')
hold off
Look at the documentation for the Orientation attribute that regionprops() can return to you.
This gives the angle between the positive x-axis and the major axis of the ellipse. You should be able to derive the equation for the major axis line in terms of that angle, and then just make a grid of x-axis points, and compute the major axis line's value for all the points in your grid, then just plot it like you would plot any other curve in MATLAB.
To do the same for the minor axis, just note that it will be 90 degrees further counter-clockwise from the major axis, then repeat the step above.
Usually one does it with computing eigenvectors, as explained in the Wikipedia article Image moment under 'examples'. That would be the correct way.
But I wonder, if you know the centroid and the boundingbox from MATLAB, then the endpoint of the major axis must be in the upper left or upper right corner. So checking (apart from noise) these two corners, if there are pixel, would give you the major axis. The minor axis then is just orthogonal to it with respect to the centroid.
Sorry for not having MATLAB code ready.
The reasoning is not that wrong, but not so good either, using the orientation as written above is better ;)