0

I' have an a PNG image that i converted into a Numpy array with only ones and zero. I want to find contiguous zones of 0 and that all the 1 that are on this contiguous zone are turned into 0.

I have got the contiguous zones with :

labels, num_features = label(temp)

But I have no idea on how to determine if a pixel is surrounded by a contiguous zone.

Note that I can't use dilation in my case to just fill the white as it damages the image in some other areas.

Here is an example : 0 is purple and 1 is white :

This is the result I'm looking for :

Dan Mašek
  • 17,852
  • 6
  • 57
  • 85

1 Answers1

3

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):
enter image description here

thresh:
enter image description here

Rotem
  • 30,366
  • 4
  • 32
  • 65