How to fill only inside areas in B&W image?

Viewed 76

Let's assume:

  • We have a B&W image with closed loops (lines are 1px thick)
  • If we travel on the image, each time we cross a line we change area type (inside or outside)
  • The top left pixel (0,0) is defined as outside and it has no line crossing it
  • Lines are black the rest is white

I want to fill all the inside areas in the most efficient way (I am working with very large images).

For example Before:

Before

and After:

After

1 Answers

I'll assume that it's indeed a "tree" of nested areas, delineated by thin black boundaries. A tree is trivially 2-colorable so if this isn't, the solution would be less obvious.

Approach:

  • connected components labeling
  • walk the tree from the "root" (label of top left pixel)
  • find neighboring components by dilating a mask and intersecting with other components
(nlabels, labelmap, stats, centroids) = cv.connectedComponentsWithStats(im, connectivity=4, ltype=cv.CV_32S)

root_label = labelmap[0,0]
area_coloring = { root_label: False } # label -> color
work = [ root_label ]
while work:
    area_label = work.pop(0)
    assert area_label in area_coloring
    color = area_coloring[area_label]

    # adjacent areas by dilating mask and finding unique labels
    mask = (labelmap == area_label).astype(np.uint8)
    mask_dilated = cv.dilate(mask, kernel=None, iterations=3) # 3 iterations to bridge boundaries (1-2 pixels)
    adjacent_areas = set(np.unique(labelmap[mask_dilated.astype(bool)])) - {0, area_label} # excluding background and self

    areas_to_color = adjacent_areas - set(area_coloring) # - already colored
    for adjacent_area in areas_to_color:
        area_coloring[adjacent_area] = not color
    work += areas_to_color
canvas = cv.cvtColor(im, cv.COLOR_GRAY2BGR)
for (label, color) in area_coloring.items():
    if not color: continue
    mask = (labelmap == label)
    canvas[mask] = 186 # gray

output

Related