Convhull() is not giving desired result. - matlab

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)

Related

MATLAB: Smooth curves on tilted edges - Edge Detection (Polyfit or fitPolynomialRANSAC?)

i have images with bands. The bands are not always 100% straight and not always 100% horizontal. They can be tilted and bent. Each band has a constant width.
I need the width of each band on the image and show the edges of each band as smooth as possible:
Example_edge_detection.png Example_straight.png Example_tilted.png
I have already a solution for 100% straight-horizontal bands. But i dont know how to do it with tilted bands. Maybe Polyfit or houghlines are needed? Please help me. Thank You!
Here the working code for the straight-horizontal bands!
clc;
close all;
clear;
workspace;
format long g;
format compact;
fontSize = 20;
folder = pwd;
baseFileName = 'Example_straight.png';
grayImage = imread(baseFileName);
[rows, columns, numberOfColorChannels] = size(grayImage);
if numberOfColorChannels > 1
grayImage = min(grayImage, [], 3);
end
hFig = gcf;
hFig.WindowState = 'maximized';
grayImage = adapthisteq(grayImage);
verticalProfile = mean(grayImage, 2);
threshold = 118;
binaryImage = imfill(grayImage < threshold, 'holes');
% Take the 3 largest blobs
binaryImage = bwareafilt(binaryImage, 3);
% Snip off small tendrils using imopen()
binaryImage = imopen(binaryImage, true(1, 3));
% Take the 3 largest blobs
binaryImage = bwareafilt(binaryImage, 3);
subplot(1, 1, 1);
imshow(baseFileName);
hFig = gcf;
hFig.WindowState = 'maximized'; %
axis('on', 'image');
title('Edge Detection', 'FontSize', fontSize, 'Interpreter', 'None');
binaryProfile = (verticalProfile > threshold)';
bandStarts = strfind(binaryProfile, [0, 1]);
bandStops = strfind(binaryProfile, [1, 0]);
for k = 1 : length(bandStarts)
yline(bandStarts(k), 'Color', 'r', 'LineWidth', 1);
yline(bandStops(k), 'Color', 'r', 'LineWidth', 1);
end
It looks like your algorithm currently just does a simple threshold to find horizontal lines, where the threshold is applied to the difference in the mean horizontal pixel value between two adjacent rows. If that's good enough to get the job done, that's good enough for me.
As for non-horizontal lines, maybe you can try a routine like this to simply straighten out the image first.

crop circles detected from imfindcircles in matlab

I'm detecting circles from an image using imfindcircles function.
Below is the code.
image here
img= imread('image.png');
imshow(img);
rmin=10
rmax=50
[centersDarkl, radiiDarkl]=imfindcircles(img,
[rmin,rmax],'ObjectPolarity','dark','Sensitivity',0.80);
viscircles(centersDarkl, radiiDarkl,'LineStyle','--')
Now, I want to crop the detected circles and save them as different figures.
Here is a solution that works for this image. I used floor and ceil to avoid the edges and rmax had to be larger than 75.
[img,map] = imread('MwBQo.png','png');
img = ind2rgb(img, map);
figure;
imshow(img);
rmin = 10;
rmax = 80;
[centersDarkl, radiiDarkl] = imfindcircles(img,...
[rmin,rmax],'ObjectPolarity','dark','Sensitivity',0.80);
viscircles(centersDarkl, radiiDarkl,'LineStyle','--')
for iCirc = 1:size(centersDarkl,1)
cropped{iCirc,1} = img(...
ceil(centersDarkl(iCirc,2)-radiiDarkl(iCirc)):...
floor(centersDarkl(iCirc,2)+radiiDarkl(iCirc)),...
ceil(centersDarkl(iCirc,1)-radiiDarkl(iCirc)):...
floor(centersDarkl(iCirc,1)+radiiDarkl(iCirc)),:);
end
figure;
subplot(1,2,1)
imshow(cropped{1})
subplot(1,2,2)
imshow(cropped{2})

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:

Area calculation in 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

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 ;)