fill areas with color in opencv-python

Viewed 782

How to fill specific areas with white color in opencv-python?

I'm trying to make a mask with the contours found in cv2.findContours(). But cannot find a way to fill those two specific areas up.

(cv2.drawContours(thickness = -1) didn't work as I expected.)

Eample:

From: enter image description here

To: enter image description here

1 Answers

You can build a tree of contours hierarchies and iterate the tree, until reaching the inner contours.

See: Contours Hierarchy.

In your case the hierarchy is:

  • Outer part of the circle.
  • Inner part of the circle (the inner part is marked as separate contour).
  • Large inner contour, and few small contour.
    We want to fill the small contours and not the large one.

We can use the following stages:

  • Find contours and hierarchy, use RETR_TREE for creating a tree of contours within contours.
  • Find contour with the maximum area (assume the circle is the largest contour) - parent contour.
  • Find the child of the child of the parent contour.
  • Iterate the siblings, and find the sibling with the maximum area (we need to ignore it).
  • Iterate the siblings again and fill all the siblings contours except the largest one.

Here is the code:

import numpy as np
import cv2

# Read input image
img = cv2.imread('input.png', cv2.IMREAD_GRAYSCALE)

# Apperanly there is a white rectangle around the image - crop the inner area.
img = img[4:-4, 4:-4]


# Find contours and hierarchy, use RETR_TREE for creating a tree of contours within contours
cnts, hiers = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2:]  # [-2:] indexing takes return value before last (due to OpenCV compatibility issues).

img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)  # Convert image to BGR for testing - fill contours with green color.

# https://docs.opencv.org/master/d9/d8b/tutorial_py_contours_hierarchy.html
# Hierarchy Representation in OpenCV
# Each contour has its own information regarding what hierarchy it is, who is its child, who is its parent etc. 
# OpenCV represents it as an array of four values : [Next, Previous, First_Child, Parent]
hiers = hiers[0]  # Remove redundant dimension of hiers.

# Find contour with the maximum area (assume the circle is the largest contour).
c = max(cnts, key=cv2.contourArea)

parent_idx = cnts.index(c)

parent_hier = hiers[parent_idx]  # Get the first outer contour - assume there is only one in our case.
parent_idx = parent_hier[2]  # Index of the first child.
parent_hier = hiers[parent_idx] # Ooops - the inner contour is the inner part of the circle - need one more inner hierarchy

child_idx = parent_hier[2]  # Index of the first child

# Iterate all childes and find the contour with the maximum area - we need to ignore it.
max_area = 0
while child_idx != -1:
    child_hier = hiers[child_idx]  # Next child in hierarchy
    c = cnts[child_idx]  # Get the contour with the same index
    area = cv2.contourArea(c)
    max_area = max(area, max_area)
    child_idx = child_hier[0]  # Get index of next contour in the same hierarchy of the child.


child_idx = parent_hier[2]  # Index of the first child

# Iterate all childes (there are supposed to be three "large" childes and few small childes in our case).
while child_idx != -1:
    child_hier = hiers[child_idx]  # Next child in hierarchy
    c = cnts[child_idx]  # Get the contour with the same index

    if cv2.contourArea(c) < max_area:
        # Fill the contour with green color
        cv2.drawContours(img, [c], -1, (0, 255, 0), -1)

    child_idx = child_hier[0]  # Get index of next contour in the same hierarchy of the child.

Result:
enter image description here

Related