Using cv2.findContours, we may find the external contours, and then use cv2.drawContours for filling the contours with the desired color.
The more interesting part is computing the "fill color" automatically (assume it's required).
Note: We may also assume that different contours in the same image may have different colors.
Assume we know the color from advance, use the following stages:
- Convert image to grayscale.
- Find contours (use cv2.RETR_EXTERNAL for ignoring the inner contours).
- Iterate contours and draw a filled up contour over each contour.
Code sample:
import cv2
import numpy as np
img = cv2.imread('contiguous_zones.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Convert to grayscale.
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV) # Apply automatic thresholding (use THRESH_OTSU) and invert polarity since background is white.
# Find contours (use cv2.RETR_EXTERNAL for ignoring the inner contours).
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Iterate contours
for c in contours:
# Draw contour over the original image with the mean color.
cv2.drawContours(img, [c], 0, (177, 35, 159), -1)
Finding the color of each contour:
Based on the following answer, we may find the average color inside a contour.
There is a small change - we have to ignore the white pixels inside the contour.
For improving the accuracy, we may also erode
the mask a little.
Sample code for finding the color of contour c
:
mask = np.zeros((img.shape[0], img.shape[1]), np.uint8)
cv2.drawContours(mask, [c], 0, 255, -1)
mask = mask & thresh # Remove inner part from the mask (black pixels in thresh).
mask = cv2.erode(mask, np.ones((5, 5), np.uint8)) # Erode the mask for improving accuracy (there are few white pixels from the outer contour).
mean_color = cv2.mean(img, mask=mask) # Compute the mean color of the pixels inside the mask.
mean_color = np.round(mean_color[0:3]).astype(int) # Take first 3 elements applies BGR - (the 4'th element is alpha) and convert to int.
Complete code sample:
import cv2
import numpy as np
img = cv2.imread('contiguous_zones.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Convert to grayscale.
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV) # Apply automatic thresholding (use THRESH_OTSU) and invert polarity since background is white.
# Find contours (use cv2.RETR_EXTERNAL for ignoring the inner contours).
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Iterate contours
for c in contours:
# Get the average color inside a contour
# The mean color excludes the inner white pixels
############################################################################
# https://stackoverflow.com/questions/54316588/get-the-average-color-inside-a-contour-with-open-cv
mask = np.zeros((img.shape[0], img.shape[1]), np.uint8)
cv2.drawContours(mask, [c], 0, 255, -1)
mask = mask & thresh # Remove inner part from the mask (black pixels in thresh).
mask = cv2.erode(mask, np.ones((5, 5), np.uint8)) # Erode the mask for improving accuracy (there are few white pixels from the outer contour).
#cv2.imshow('mask', mask) # Show mask for testing
#cv2.waitKey()
mean_color = cv2.mean(img, mask=mask) # Compute the mean color of the pixels inside the mask.
mean_color = np.round(mean_color[0:3]).astype(int) # Take first 3 elements applies BGR - (the 4'th element is alpha) and convert to int.
############################################################################
# Draw contour over the original image with the mean color.
cv2.drawContours(img, [c], 0, mean_color.tolist(), -1)
# Show results for testing:
cv2.imshow('thresh', thresh)
cv2.imshow('img', img)
cv2.waitKey()
cv2.destroyAllWindows()
img
(output):

thresh
:
