Extracting "endpoints" from a skeleton image to enable a circle to be defined.
10 views (last 30 days)
Show older comments
Hi, I am trying to find the centre and radius of the circle (whilst ignoring the cross) so I can locate the centre and display a circle on the image:
After reading another matlab central thread, IA has suggested skeletonising and finding the endpoints, so I am trying this approach
So after binarising my raw tiff image and filling in the holes, I have created the skeleton.
But Im not sure how to extract say the 8 most extreme endpoints and use this to define a circle (or fit a circle to)
figure
subplot(1,4,1)
myImshow(IM,2) %IM is my raw image
title(['Raw Image'])
subplot(1,4,2)
imshow(BI,[0 1]) %BI is my binarised image
title(['Binary: Thresh=',num2str(level,'%.1f')])
%Fill holes
subplot(1,4,3)
bw = imfill(BI, 'holes');
imshow(bw,[0 1])
title(['Fill Holes'])
bw3 = bwmorph(bw2,'skel',Inf);
imshow(bw3,[0 1]); hold on
title(['Skeletonise'])
ep=bwmorph(bw2,'endpoints');
What to do with ep?
I have included the fill holes image incase its useful.
Thanks
Jason
0 Comments
Accepted Answer
Image Analyst
on 22 May 2020
I don't remember seeing that image and I don't think I would have recommended that, anything with skeletonization and endpoints unless I totally misunderstood. What I'd for this image is to call bwconvhull() on it and then call regionprops to get the circle. Something like (untested):
mask = bwconvhull(mask, 'union');
props = regionprops(mask, 'Centroid', 'EquivDiameter');
viscircles(props.Centroid, props.EquivDiameter/2);
There are other ways but with only two or three lines of code I think this is the simplest. See if that works. Note that since the edge is fuzzy/blurry and there is apparently no ground truth answer to check accuracy against, you just have to see if whatever center and radius you get from whatever method suits your needs. It could be that consistency is more important than accuracy.
2 Comments
Rik
on 22 May 2020
Feel free to accept either and give a vote to the other if you think both would suit your needs.
fprintf('fminsearch: C=[%.1f %.1f], r=%.1f\n',[x,y,r])
fprintf('bwconvhull: C=[%.1f %.1f], r=%.1f\n',[props.Centroid props.EquivDiameter/2])
%returns:
fminsearch: C=[79.5 88.3], r=74.3
bwconvhull: C=[79.5 90.1], r=78.4
As you can see the centroids are very close and the radius is slightly smaller for my method (which is expected)
More Answers (1)
Rik
on 22 May 2020
This can probably be improved, but this will find an approximate circle. It will slightly underestimate the radius, as it tries to maximize the number of correctly labeled pixels. You may find the perfomance improves if you manage to fill in that crosshair.
Watch out for the differences in convention for the orientation between image space and plotting space: y is flipped. The code below works also if you extend your masked image in one direction, so I'm reasonably confident I didn't make a mistake here.
%convert image back to binary
mask=imread('im154.png');mask=mask(:,:,1)>128;
[x,y,r]=fit_circle(mask);
t=linspace(0,2*pi,200);
x_c=r*cos(t)+x;
y_c=r*sin(t)+y;
figure(1),clf(1)
imshow(mask),hold on
plot(x_c,y_c,'r-*')
plot(x,y,'ro')
function [x,y,r]=fit_circle(mask)
[Y,X]=ndgrid(1:size(mask,1),1:size(mask,2));
count_of_correct_pixels=@(y,x,r) sum(sum( (sqrt((Y-y).^2+(X-x).^2)<=r) == mask));
%initialize as centered
y=size(mask,1)/2;x=size(mask,2)/2;
r=mean([y x]);
p_fitted=fminsearch(@(p) -count_of_correct_pixels(p(1),p(2),p(3)),[y,x,r]);
y=p_fitted(1);
x=p_fitted(2);
r=p_fitted(3);
end
0 Comments
See Also
Products
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!