Question: I have this python code that supposedly fills the contours of an image, but leaves the holes contained in it unfilled. This is what I want:  But this is what I get:  I've tried specifying the contour hierarchies for filling with cv2, but I can't get the result I want. This is what I've tried:  import numpy as np import cv2 # Load the PNG image img = cv2.imread('slice.png') # Convert the image to grayscale gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Threshold the image to create a binary image ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY) # Find the contours in the binary image contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # Create a blank image with the same dimensions as the original image filled_img = np.zeros(img.shape[:2], dtype=np.uint8) # Iterate over the contours and their hierarchies for i, contour in enumerate(contours): # Check if the contour has a parent if hierarchy[0][i][3] == -1: # If the contour doesn't have a parent, fill it with pixel value 255 cv2.drawContours(filled_img, [contour], -1, 255, cv2.FILLED) # Display the result cv2.imshow('Original Image', img) cv2.imshow('Filled Regions', filled_img) cv2.waitKey(0) cv2.destroyAllWindows()  I've tried modifying the -1, 0, 1 values for the 'if hierarchy[0][i][3] == -1:' part, but it either fills the smaller holes, or fills the entire bigger contour like the first pic I posted. Update I also would like to fill with white the inside of lesser hierarchy contours, like this:  

Answer: The issue is that cv2.drawContours fills the entire inner part of a closed contour, regardless if there is an inner contour. Instead of filling the contours without a parent with white, we may start with white contour, and fill the contours without a child with black.  Assuming we know that the inner part should be black, we may apply the following stages:  Find contours using cv2.RETR_EXTERNAL, and fill the outer contour with white. Find contours using cv2.RETR_TREE. Iterate the contours hierarchy, and fill with black color only contours that doesn't have a child contour (fill with black the most inner contours).   Code sample: import numpy as np import cv2 # Load the PNG image img = cv2.imread('slice.png') # Convert the image to grayscale gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Threshold the image to create a binary image ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY) # Find the outer contours in the binary image (using cv2.RETR_EXTERNAL) contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # Create a blank image with the same dimensions as the original image filled_img = np.zeros(img.shape[:2], dtype=np.uint8) # Fill the outer contour with white color cv2.drawContours(filled_img, contours, -1, 255, cv2.FILLED) # Find contours with hierarchy, this time use cv2.RETR_TREE contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # Iterate over the contours and their hierarchies for i, contour in enumerate(contours): # Check if the contour has no child if hierarchy[0][i][2] < 0: # If contour has no child, fill the contour with black color cv2.drawContours(filled_img, [contour], -1, 0, cv2.FILLED) # Display the result cv2.imshow('Original Image', img) cv2.imshow('Filled Regions', filled_img) cv2.waitKey(0) cv2.destroyAllWindows()   Result filled_img:  Note: In case we don't know the color of the most inner contour, we may draw a white contour on a black background, and use the result as a mask - use the mask for copying the original content of the input image.  Update: Support contours that don't have a child: For supporting both contours that have a child and contours without a child, we may fill with black color, only contours that match both conditions:  Contours has no child contour. Contour has a grandparent contour (look for grandparent instead of a parent because an empty contour has an inner contour and its parent is the outer contour).  Code sample: import numpy as np import cv2 # Load the PNG image img = cv2.imread('slice.png') # Convert the image to grayscale gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Threshold the image to create a binary image ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY) # Find the outer contours in the binary image (using cv2.RETR_EXTERNAL) contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # Create a blank image with the same dimensions as the original image filled_img = np.zeros(img.shape[:2], dtype=np.uint8) # Fill the outer contour with white color cv2.drawContours(filled_img, contours, -1, 255, cv2.FILLED) # Find contours with hierarchy, this time use cv2.RETR_TREE contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # Iterate over the contours and their hierarchies for i, contour in enumerate(contours): has_grandparent = False has_parent = hierarchy[0][i][3] >= 0 if has_parent: # Check if contour has a grandparent parent_idx = hierarchy[0][i][3] has_grandparent = hierarchy[0][parent_idx][3] >= 0 # Check if the contour has no child if hierarchy[0][i][2] < 0 and has_grandparent: # If contour has no child, fill the contour with black color cv2.drawContours(filled_img, [contour], -1, 0, cv2.FILLED) # Display the result cv2.imshow('Original Image', img) cv2.imshow('Filled Regions', filled_img) cv2.waitKey(0) cv2.destroyAllWindows()   Update: Fill with white the inside of lesser hierarchy contours: Before filling the contour with black color, we may check is the nominated contour has black pixels inside. Fill with black only if it has no child, has a grandparent and has black inside. For testing if has black pixels inside we may draw the contour (with white color) over temporary image. Then check if the minimum value is 0 (value where drawn contour is white). tmp = np.zeros_like(thresh) cv2.drawContours(tmp, [contour], -1, 255, cv2.FILLED) has_innder_black_pixels = (thresh[tmp==255].min() == 0)   Code sample: import numpy as np import cv2 # Load the PNG image img = cv2.imread('slice.png') # Convert the image to grayscale gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Threshold the image to create a binary image ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY) # Find the outer contours in the binary image (using cv2.RETR_EXTERNAL) contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # Create a blank image with the same dimensions as the original image filled_img = np.zeros(img.shape[:2], dtype=np.uint8) # Fill the outer contour with white color cv2.drawContours(filled_img, contours, -1, 255, cv2.FILLED) # Find contours with hierarchy, this time use cv2.RETR_TREE contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # Iterate over the contours and their hierarchies for i, contour in enumerate(contours): has_grandparent = False has_parent = hierarchy[0][i][3] >= 0 if has_parent: # Check if contour has a grandparent parent_idx = hierarchy[0][i][3] has_grandparent = hierarchy[0][parent_idx][3] >= 0 # Draw the contour over temporary image first (for testing if it has black pixels inside). tmp = np.zeros_like(thresh) cv2.drawContours(tmp, [contour], -1, 255, cv2.FILLED) has_innder_black_pixels = (thresh[tmp==255].min() == 0) # If the minimum value is 0 (value where draw contour is white) then the contour has black pixels inside if hierarchy[0][i][2] < 0 and has_grandparent and has_innder_black_pixels: # If contour has no child and has a grandparent and it has black inside, fill the contour with black color cv2.drawContours(filled_img, [contour], -1, 0, cv2.FILLED) # Display the result cv2.imshow('Original Image', img) cv2.imshow('Filled Regions', filled_img) cv2.waitKey(0) cv2.destroyAllWindows()