Connecting the dots in a binary image

29 views (last 30 days)
Mia Grahn
Mia Grahn on 7 Mar 2022
Commented: DGM on 7 Mar 2022
I have an image (attached) and I want to connect the dots into a circle (to later be filled in and the white coordinates will be collected). For context, the current coordinates shown are those making up the verticies of an annotation I did around an image I hope to eventually classify, and I annotated it at the pixel level. I annotated a complete circle, but I think the software I did it on had a different process of deciding which coordinates to save and resulting in my image not being fully connected. In order to fully fill in my image eventually, I want to make sure I have a fully connected border first, and to air on the side of caution, I want to close the border using pixels closest to the inside (or centroid of the shape) in cases where 2 edge lines could be connected by a point closer to the center or further (I always want the closer one). For example, if you look at the bottom of the image I have attached, there seems to be one black square disconnecting two long white boarder lines. I want to add a white square bridging those two lines, but I dont want it to fill in the line and make it straight, I want the white pixel I am adding to be up a line and inbetween the two line edges, touching each edgeline diagonally at the corners (or be on the y=2 line, not the y=1 line if you'd rather think of it in terms of the bottom left corner being (1,1)).
I have tried using imclose() and unfortunately it is too generous and estimates points outside the boundaries I have annotated to complete the shape. If anyone has any smart ways of going about this they can share, please let me know, I would really appreciate it! Like I said, I am working with this shape at the coordinate level, so if you know of a way to compute the points at that level, that would be totally find as well. I have attached a photo of just the image I am trying to change and rougly how I would ideally like it to be connected if this would help to see.
  3 Comments
Mia Grahn
Mia Grahn on 7 Mar 2022
@Image Analyst also to add on to the above in case I did not clarify, My goal is to make a closed circle out of my original image so there is one perimiter that can I can then easily fill in.

Sign in to comment.

Accepted Answer

DGM
DGM on 7 Mar 2022
Edited: DGM on 7 Mar 2022
It may be overkill for such a small image, but my answer to a previous question works well for largely convex closed paths like this.
Either method works, but for simplicity, i'm just going to use the one based on interp1(). For an image this small, there really isn't an advantage to a spline fit.
% get the data
load xvariable.mat
load yvariable.mat
x = x-min(x)+1;
y = y-min(y)+1;
% construct an image out of it
A = zeros(max(y),max(x));
A(sub2ind(size(A),y,x)) = 1;
A = padarray(A,[1 1],0,'both');
imshow(A)
s = size(A);
cn = round(s(1:2)/2); % [ycenter xcenter]
% find locations of outline pixels
[idxy idxx] = find(A); % in rect coord
[idxth idxr] = cart2pol(idxx-cn(2),idxy-cn(1)); % in polar coord
% sort pixel locations by angular position wrt image center
[idxth sortmap] = sort(idxth,'ascend');
idxr = idxr(sortmap);
% use any 1-D interpolation or fitting method
newth = linspace(-pi,pi,1000).';
newr = interp1(idxth,idxr,newth,'linear');
nanmk = ~isnan(newr);
newr = newr(nanmk);
newth = newth(nanmk);
% construct output image
[newx newy] = pol2cart(newth,newr);
B = false(s);
B(sub2ind(s,round(newy+cn(1)),round(newx+cn(2)))) = true;
clf
imshow(double(cat(3,A,A,B))) % show which pixels are new
Note that this method of interpolation really tries to force the result to be convex, hence the spurious fill pixels placed along the exterior where there are no gaps. Either way, this isn't the end result you wanted anyway. We can use this closed path to find the pixels where the fill contacts the new pixels and try to fill the gaps at a smaller radius:
% get only the new pixels
newpx = B & ~A;
% get only the region inside the closed path
fillpx = imfill(B,'holes') & ~B;
% create a map to determine which fill pixels contact new pixels
contactmap = double(fillpx);
contactmap(newpx) = 2;
% process the map to find the contact pixels
st = [0 1 0; 1 1 1; 0 1 0];
fillcontactsnew = imdilate(contactmap,st)>1 & fillpx;
imshow(double(cat(3,A,A,fillcontactsnew)))
These can of course be combined to form a single image:
outpict = A | fillcontactsnew;
imshow(outpict)
You can use a similar technique to get rid of all the exterior spurs on the original path.
% create a map to determine which original pixels contact fill pixels
contactmap = double(A);
contactmap(fillpx) = 2;
Acontactsfill = imdilate(contactmap,st)>1 & A;
outpict = fillcontactsnew | Acontactsfill;
imshow(outpict)
  2 Comments
DGM
DGM on 7 Mar 2022
Weeeeell...
As I mentioned, the initial part of the example used to get the intermediate closed path works by polar interpolation. It doesn't behave well for really complicated shapes.
That said, if you can at least close the path by other means (e.g. Image Analyst's edge-linking), then you can apply the methods used in the second part of the example to find new closure pixels on the interior.

Sign in to comment.

More Answers (1)

yanqi liu
yanqi liu on 7 Mar 2022
img = imread('https://ww2.mathworks.cn/matlabcentral/answers/uploaded_files/916929/Screen%20Shot%202022-03-06%20at%208.51.29%20PM.png');
img = imcrop(img,[78 74 202 224]);
bw = im2bw(img);
bw2 = ~bw;
bw2 = imerode(bw2, strel('disk', 10));
bw2 = bwareafilt(bw2,1);
bw2 = imadd(bw2,bw);
bw3 = imclose(bw2, strel('disk', 8));
bw4 = imadd(bw, imdilate(bwperim(bw3), strel('disk',3)));
figure; imshow(bw2);
figure; imshow(bw3);
figure; imshow(bw4);
  1 Comment
Mia Grahn
Mia Grahn on 7 Mar 2022
@yanqi liu Thank you for your answer! I think your method may have increases the pixel counts more than I would like it to unfortunatley

Sign in to comment.

Community Treasure Hunt

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

Start Hunting!