Intrigued, I posted a reply. The basic algorithm for removing contours from an image goes something like this:
- Step 1: Detect and find contours in your image.
- Step 2: Loop over contours individually.
- Step 3: Determine if the contour is “bad” and should be removed according to some criterion.
- Step 4: Accumulate a mask of “bad” contours to be removed.
- Step 5: Apply the accumulated mask of bad contours to the original image using a bitwise ‘and’.
And that’s it!
The algorithm itself is very straightforward, the main step that you need to pay attention to and consider is Step 3, determining if a contour should be removed.
This step could be very simple — or it also could be quite hard, it really depends on your application. And while it’s impossible for me to guess the criterion as to why you want to remove a contoured region from an image, the remainder of this blog post will demonstrate a toy example that you can use to remove contours from an image. From here, you’ll be able to take this code and modify the contour removal criterion according to your own needs.
Looking for the source code to this post?
Jump right to the downloads section.
OpenCV and Python versions:
This example will run on Python 2.7/Python 3.4+ and OpenCV 2.4.X.
Removing contours from an image using Python and OpenCV
In this toy example our goal is to remove the circles/ellipses from the image above while retaining the rectangles. We’ll accomplish this by applying a test to every contour to determine if it should be removed or not.
Anyway, let’s go ahead and get this example started. Open up a new file, name it remove_contours.py , and let’s get coding:
# import the necessary packages
import numpy as np
# approximate the contour
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
# the contour is 'bad' if it is not a rectangle
return not len(approx) == 4
The first thing we’ll do is import our necessary packages. We’ll use NumPy for numerical processing and cv2 for our OpenCV bindings.
We then define our is_contour_bad function on Line 6. This function handles the implementation of Step 3 above and defines the criterion under which a contour should be marked as “bad” and removed from an image.
The is_contour_bad function requires a single argument, c , which is the contour we are going to test to determine if it should be removed or not.
Remember, in our toy example image above, our goal is to remove the circles/ellipses, while keeping the rectangles intact.
So let’s take a second to consider if we can exploit the geometry of this problem.
A rectangle has 4 sides. And a circle has no sides.
So if we approximate the contour and then examine the number of points within the approximated contour, we’ll be able to determine if the contour is a square or not!
And that’s exactly what Lines 7-11 do. We first approximate the contour on Lines 8 and 9, while Line 12 returns a boolean, indicating whether the contour should be removed or not. In this case, the contour will be kept if the approximation has 4 points (vertices), indicating that the contour is a rectangle.
Let’s finish implementing the other steps to solve this problem:
# load the shapes image, convert it to grayscale, and edge edges in
# the image
image = cv2.imread("shapes.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edged = cv2.Canny(gray, 50, 100)
# find contours in the image and initialize the mask that will be
# used to remove the bad contours
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
mask = np.ones(image.shape[:2], dtype="uint8") * 255
# loop over the contours
for c in cnts:
# if the contour is bad, draw it on the mask
cv2.drawContours(mask, [c], -1, 0, -1)
# remove the contours from the image and show the resulting images
image = cv2.bitwise_and(image, image, mask=mask)
In order to find and detect the contours in our image (Step 1), we preprocess our image on Lines 16-19 by loading it from disk, converting it to grayscale and detecting edges.
Finding the actual contours happens on Line 23 by making a call to cv2.findContours . Subsequently we handle grabbing contours with different versions of OpenCV (Line 24).
We then initialize a mask on Line 25 to store our accumulated “bad” contours. We’ll be using this mask along with bitwise operations later on in the code to perform the actual removal of the contour.
Now we can move on to Step 2, looping over the individual contours which happens on Line 28.
For each of the contours we make a call to is_contour_bad on Line 30, and if the contour is indeed “bad”, then we accumulate our contours to be removed on Line 31 by drawing the contour on our mask.
Finally, all we have to do is apply a bitwise ‘and’ to the image using the accumulated mask to remove the contours on Line 34. Lines 35-37 then display our results.
To execute our script, just issue the following command:
$ python remove_contours.py
First, you’ll see our mask of accumulated contours that will be removed:
Notice how the contours appear as black shapes on a white background. This is because the black shapes will be removed from the original image while the white regions will be retained once we apply the cv2.bitwise_and function.
And here is the output after applying the accumulated mask:
Clearly we have removed the circles/ellipses from the image while retaining the rectangles!
In this blog post I showed you how to remove contoured regions from an image using Python and OpenCV. Removing contours from an image is extremely straightforward and can be accomplished using the following 5 steps:
- Detecting and finding the contours in an image.
- Looping over each of the contours individually.
- Applying a “test” of some sort to determine if the contour should be removed.
- Accumulating a mask of contours to be removed.
- Applying the mask to the original image.
To apply this algorithm to your own images you’ll need to take a second and consider Step 3 and determine the criterion you are using to remove contours. From there, you can apply the rest of the algorithm as-is.