Filling gaps along the edges of the form

Is there an algorithm that works well with filling holes, as in the sample? Dilation does not work, because before I can connect the ends of these curves over time, the curves become very thick. I would like to avoid thickening the lines. Thanks for any help.

enter image description here

Yes, the image can be only any letter or shape with such holes.

+5
source share
3 answers

Another, simpler way, which is probably better converted to OpenCV , since it uses convolution rather than serial Perl / C code.

Basically set all black pixels to 10 and all white pixels to 0 , then collapse the image with the following 3x3 core:

 1 1 1 1 10 1 1 1 1 

Now the black pixel in the middle of the kernel will give 100 (10x10), and any other black pixel in the neighborhood will give 10 (10x1). Therefore, if we need points with a central black pixel with one adjacent black pixel, it will have a value of 110 (100 + 10). So let the color of all pixels having a value of 110 V with red. This gives the following command:

 convert EsmKh.png -colorspace gray -fill gray\(10\) -opaque black -fill gray\(0\) -opaque white -morphology convolve '3x3: 1,1,1 1,10,1 1,1,1' -fill red -opaque gray\(110\) out.png 

with the resulting image (you may need to increase the spaces to see red):

enter image description here

If you need a list of red pixels, replace the output file name with txt: and search as follows:

 convert EsmKh.png -colorspace gray -fill rgb\(10,10,10\) -opaque black -fill rgb\(0,0,0\) -opaque white -morphology convolve '3x3: 1,1,1 1,10,1 1,1,1' txt: | grep "110,110,110" 

which gives:

 86,55: (110,110,110) #6E6E6E grey43 459,55: (110,110,110) #6E6E6E grey43 83,56: (110,110,110) #6E6E6E grey43 507,59: (110,110,110) #6E6E6E grey43 451,64: (110,110,110) #6E6E6E grey43 82,65: (110,110,110) #6E6E6E grey43 134,68: (110,110,110) #6E6E6E grey43 519,75: (110,110,110) #6E6E6E grey43 245,81: (110,110,110) #6E6E6E grey43 80,83: (110,110,110) #6E6E6E grey43 246,83: (110,110,110) #6E6E6E grey43 269,84: (110,110,110) #6E6E6E grey43 288,85: (110,110,110) #6E6E6E grey43 315,87: (110,110,110) #6E6E6E grey43 325,87: (110,110,110) #6E6E6E grey43 422,104: (110,110,110) #6E6E6E grey43 131,116: (110,110,110) #6E6E6E grey43 524,116: (110,110,110) #6E6E6E grey43 514,117: (110,110,110) #6E6E6E grey43 122,118: (110,110,110) #6E6E6E grey43 245,122: (110,110,110) #6E6E6E grey43 76,125: (110,110,110) #6E6E6E grey43 456,128: (110,110,110) #6E6E6E grey43 447,129: (110,110,110) #6E6E6E grey43 245,131: (110,110,110) #6E6E6E grey43 355,135: (110,110,110) #6E6E6E grey43 80,146: (110,110,110) #6E6E6E grey43 139,151: (110,110,110) #6E6E6E grey43 80,156: (110,110,110) #6E6E6E grey43 354,157: (110,110,110) #6E6E6E grey43 144,160: (110,110,110) #6E6E6E grey43 245,173: (110,110,110) #6E6E6E grey43 246,183: (110,110,110) #6E6E6E grey43 76,191: (110,110,110) #6E6E6E grey43 82,197: (110,110,110) #6E6E6E grey43 126,200: (110,110,110) #6E6E6E grey43 117,201: (110,110,110) #6E6E6E grey43 245,204: (110,110,110) #6E6E6E grey43 248,206: (110,110,110) #6E6E6E grey43 297,209: (110,110,110) #6E6E6E grey43 309,210: (110,110,110) #6E6E6E grey43 

Now you can process the list of red dots, and for each of them find the nearest other red dot and connect them with a straight line - or make some curve if you feel very sharp. Of course, there may be some kind of processing, and you can set the maximum length of the gap filling line.

+4
source

I did a little work. This may require some tweaking, but this is an idea. My algorithm is as follows:

Find all the black pixels that have exactly one black adjacent pixel, color it and place it at the pixels at ends list.

Go through the list of all red pixels and find the nearest other red pixel and draw a straight line between them.

By the way, I just implemented the first part - I have to leave something for the reader ,-)

 #!/usr/bin/perl use strict; use warnings; use Image::Magick; use Data::Dumper; my $im=Image::Magick->new(); $im->Read('EsmKh.png'); my ($width,$height)=$im->Get('width','height'); my $out=Image::Magick->new(); $out->Read('EsmKh.png'); my @pixels; # Iterate over pixels for my $y (0..($height-1)){ for my $x (0..($width-1)){ my (@pixel) = split(/,/, $im->Get("pixel[$x,$y]")); $pixels[$x][$y]=$pixel[0]; } } # Find black pixels that have precisely 1 black neighbour for my $y (1..($height-2)){ for my $x (1..($width-2)){ next if $pixels[$x][$y]!=0; my $neighbours=0; for(my $i=$x-1;$i<=$x+1;$i++){ for(my $j=$y-1;$j<=$y+1;$j++){ $neighbours++ if $pixels[$i][$j]==0; } } $neighbours--; # Uncount ourself ! if($neighbours==1){ $out->Set("pixel[$x,$y]"=>'red'); } } } $out->Write(filename=>'out.png'); 

Result

You need to zoom in to see the red pixels ...

enter image description here

Enlarged image

enter image description here

+1
source

After you get a thickened image, you can restore your "thin" shape using skeletonization . I found a skeletonization implementation here .

If you want to avoid too much thickening (since it distorts the image and parts of the merging of the figure together), use soft erosion and skeletonization as an alternative until you fill the holes.

+1
source

Source: https://habr.com/ru/post/1205403/


All Articles