Title: OpenCV Contour Approximation - PyImageSearch

URL Source: https://pyimagesearch.com/2021/10/06/opencv-contour-approximation/

Published Time: 2021-10-06T14:00:00+00:00

Markdown Content:
In this tutorial, we’ll learn about a step-by-step implementation and utilization of OpenCV’s Contour Approximation.

![Image 1](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20650%20365'%3E%3C/svg%3E)

When I first chanced upon the concept of **Contour Approximation**, the first question that hit me was: **Why**? Throughout my journey in Machine Learning and its related fields, I was always taught that Data is everything. Data is currency. The more you have of it, the more you are likely to succeed. **Figure 1** aptly describes the scenario.

![Image 2](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20650%20405'%3E%3C/svg%3E)

**Figure 1:** Data is the New World Currency.

Hence, I didn’t quite understand the concept of approximating the data points of a curve. Wouldn’t that make it simpler? Wouldn’t that make us lose data? I was in for a very big surprise.

In today’s tutorial, we’ll learn about OpenCV Contour Approximation, more precisely called the Ramer–Douglas–Peucker algorithm. You’ll be amazed to know how important it was for a lot of high-priority real-life applications.

In this blog, you’ll learn:

*   What Contour Approximation is
*   Prerequisites required to understand Contour Approximation
*   How to implement Contour Approximation
*   Some Practical Applications of Contour Approximation

**To learn how to implement contour approximation,** **_just keep reading._**

![Image 3](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20128%20128'%3E%3C/svg%3E)

#### Looking for the source code to this post?

[Jump Right To The Downloads Section](#download-the-code)

**OpenCV Contour Approximation ( cv2.approxPolyDP )**
-----------------------------------------------------

Let’s imagine a scenario: you are a self-driving robot. You move according to the data collected by your radars (LIDAR, SONAR, etc.). You’ll constantly have to deal with tons of data that will be transformed into a format understandable to you. Then, you’ll make the decisions required for you to move. What’s your biggest obstacle in a situation like this? Is it that piece of brick purposefully left in your route? Or a zigzag road standing between you and your destination?

Turns out, the simple answer is…. _All of them._ Imagine the sheer amount of data you would be intaking at a given time to assess a situation. Turns out _Data_ was playing both sides of the fence the whole time. Was _Data_ our enemy all this time?

While it’s true that more data gives you a better perspective for your solution, it also introduces problems like computation complexity and storage. Now you, like a robot, would need to make reasonably quick decisions to traverse the route that lies in front of you.

That means simplifying complex data would be your top priority.

Let’s say you were given a top-down view of the route that you would be taking. Something like **Figure 2.**

![Image 4](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20650%20310'%3E%3C/svg%3E)

**Figure 2:** Top-down view of a route.

What if, given the exact width of the road and other parameters, there was a way you could simplify this map? Considering your size is small enough to ignore redundant turns (parts where you can go straight without following the exact curves of the road), it would be easier for you if you could remove some redundant vertices. Something like **Figure 3**:

![Image 5](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20650%20310'%3E%3C/svg%3E)

**Figure 3**: Contour Approximation in action.

Notice how iteratively, some of the vertices are smoothened out, resulting in a much more linear route. Ingenious, isn’t it?

This is just one of the many applications Contour Approximation has in the real world. Before we proceed any further, let’s formally understand what it is.

### **What Is Contour Approximation?**

Contour approximation, which uses the **Ramer**–**Douglas**–**Peucker (RDP)** algorithm, aims to simplify a polyline by reducing its vertices given a threshold value. In layman terms, we take a curve and reduce its number of vertices while retaining the bulk of its shape. For example, take a look at **Figure 4**.

![Image 6](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20650%20207'%3E%3C/svg%3E)

**Figure 4**: RDP algorithm in action (source: [Wikipedia](https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm)).

This very informative GIF from Wikipedia’s RDP article shows us how the algorithm works. I’ll give a crude idea of the algorithm here.

Given the start and end points of a curve, the algorithm will first find the vertex at maximum distance from the line joining the two reference points. Let’s refer to it as `max_point`. If the `max_point` lies at a distance less than the threshold, we automatically neglect all the vertices between the start and end points and make the curve a straight line.

If the `max_point` lies outside the threshold, we’ll recursively repeat the algorithm, now making the `max_point` one of the references, and repeat the checking process as shown in **Figure 4**.

Notice how certain vertices are getting eliminated systematically. In the end, we retain the bulk of the information but in a less complex state.

With that out of the way, let’s see how we can use OpenCV to harness the power of RDP!

### **Configuring Your Development Environment**

To follow this guide, you need to have the OpenCV library and the `` `imutils` `` package installed on your system.

Luckily, OpenCV is pip-installable:

$ pip install opencv-contrib-python
$ pip install imutils

**If you need help configuring your development environment for OpenCV, I _highly recommend_ that you read my** [**_pip install OpenCV_ guide**](https://pyimagesearch.com/2018/09/19/pip-install-opencv/) — it will have you up and running in a matter of minutes.

### **Having Problems Configuring Your Development Environment?**

[![Image 7](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20500%20334'%3E%3C/svg%3E)](https://pyimagesearch.com/pyimagesearch-university/)

**Figure 5:** Having trouble configuring your dev environment? Want access to pre-configured Jupyter Notebooks running on Google Colab? Be sure to join [PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/) — you’ll be up and running with this tutorial in a matter of minutes.

All that said, are you:

*   Short on time?
*   Learning on your employer’s administratively locked system?
*   Wanting to skip the hassle of fighting with the command line, package managers, and virtual environments?
*   **Ready to run the code** **_right now_** **on your Windows, macOS, or Linux system?**

Then join [PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/) today!

**Gain access to Jupyter Notebooks for this tutorial and other PyImageSearch guides that are** **_pre-configured_** **to run on Google Colab’s ecosystem right in your web browser!** No installation required.

And best of all, these Jupyter Notebooks will run on Windows, macOS, and Linux!

### **Project Structure**

Let’s take a look at the project structure.

$ tree . --dirsfirst
.
├── opencv\_contour\_approx.py
└── shape.png

0 directories, 2 files

The parent directory contains a single script and an image:

*   `opencv_contour_approx.py`: The only script required in the project contains all the coding involved.
*   `shape.png`: The image on which we’ll test out Contour Approximation.

### **Implementing Contour Approximation with OpenCV**

Before jumping to Contour Approximation, we’ll go through a few prerequisites to better understand the whole process. So, without further ado, let’s jump into `opencv_contour_approx.py` and start coding!

\# import the necessary packages
import numpy as np
import argparse
import imutils
import cv2

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add\_argument("-i", "--image", type=str, default="shape.png",
	help="path to input image")
args = vars(ap.parse\_args())

An argument parser instance is created to give the user an easy command line interface experience when they choose the image they wish to tinker with (**Lines 8-11**). The default image is set as `shape.png`, the image which already exists in the directory. However, readers are encouraged to try this experiment out with their own custom images!

\# load the image and display it
image = cv2.imread(args\["image"\])
cv2.imshow("Image", image)

# convert the image to grayscale and threshold it
gray = cv2.cvtColor(image, cv2.COLOR\_BGR2GRAY)
thresh = cv2.threshold(gray, 200, 255,
	cv2.THRESH\_BINARY\_INV)\[1\]
cv2.imshow("Thresh", thresh)

The image provided as the argument is then read using OpenCV’s `imread` and displayed (**Lines 14 and 15**).

The image is going to look like **Figure 6**:

![Image 8](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20591%20500'%3E%3C/svg%3E)

**Figure 6**: The base Image.

Since we’ll be using the boundary of the shape in the image, we convert the image from RGB to grayscale (**Line **18****). Once in grayscale format, the shape can easily be isolated using OpenCV’s `threshold` function (**Lines **19-21****). The result can be seen in **Figure 7**:

![Image 9](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20591%20500'%3E%3C/svg%3E)

**Figure 7**: Isolated shape.

Note that since we have chosen `cv2.THRESH_BINARY_INV` as the parameter on **Line 2**0****, the high-intensity pixels are changed to `0`, while the surrounding low-intensity pixels become `255`.

\# find the largest contour in the threshold image
cnts = cv2.findContours(thresh.copy(), cv2.RETR\_EXTERNAL,
	cv2.CHAIN\_APPROX\_SIMPLE)
cnts = imutils.grab\_contours(cnts)
c = max(cnts, key=cv2.contourArea)

# draw the shape of the contour on the output image, compute the
# bounding box, and display the number of points in the contour
output = image.copy()
cv2.drawContours(output, \[c\], -1, (0, 255, 0), 3)
(x, y, w, h) = cv2.boundingRect(c)
text = "original, num\_pts={}".format(len(c))
cv2.putText(output, text, (x, y - 15), cv2.FONT\_HERSHEY\_SIMPLEX,
	0.9, (0, 255, 0), 2)

# show the original contour image
print("\[INFO\] {}".format(text))
cv2.imshow("Original Contour", output)
cv2.waitKey(0)

Using OpenCV’s `findContours` function, we can single out all possible contours in the given image (depending on the given arguments) (**Lines **24 and 25****). We have used the `RETR_EXTERNAL` argument, which only returns single representations of available contours. You can read more about it [here](https://docs.opencv.org/master/d9/d8b/tutorial_py_contours_hierarchy.html).

The other argument used is `CHAIN_APPROX_SIMPLE`. This removes many vertices in a single chain line connection, vertices that are essentially redundant.

We then grab the largest contour from the array of contours (this contour belongs to the shape) and trace it on the original image (**Lines **26-36****). For this purpose, we use OpenCV’s `drawContours` function. We also use the `putText` function to write on the image. The output is shown in **Figure 8**:

![Image 10](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20591%20500'%3E%3C/svg%3E)

**Figure 8**: The Contour traced over the shape.

Now, let’s demonstrate what contour approximation can do!

\# to demonstrate the impact of contour approximation, let's loop
# over a number of epsilon sizes
for eps in np.linspace(0.001, 0.05, 10):
	# approximate the contour
	peri = cv2.arcLength(c, True)
	approx = cv2.approxPolyDP(c, eps \* peri, True)

	# draw the approximated contour on the image
	output = image.copy()
	cv2.drawContours(output, \[approx\], -1, (0, 255, 0), 3)
	text = "eps={:.4f}, num\_pts={}".format(eps, len(approx))
	cv2.putText(output, text, (x, y - 15), cv2.FONT\_HERSHEY\_SIMPLEX,
		0.9, (0, 255, 0), 2)

	# show the approximated contour image
	print("\[INFO\] {}".format(text))
	cv2.imshow("Approximated Contour", output)
	cv2.waitKey(0)

As mentioned earlier, we’ll need a value `eps`, which will act as the threshold value to measure vertices. Accordingly, we start looping epsilon’s (`eps`) value over a range to feed it to the contour approximation function (**Line **45****).

On **Line **47****, the perimeter of the contour is calculated using `cv2.arcLength`. We then use the `cv2.approxPolyDP` function and initiate the contour approximation process (**Line **48****). The `eps` × `peri` value acts as the approximation accuracy and will change with each epoch due to `eps`’s incremental nature.

We proceed to trace the resultant contour on the image on each epoch to assess the results (**Lines **51-60****).

Let’s see the results!

### **Contour Approximation Results**

Before moving on to the visualizations, let’s see how contour approximation affected the values.

$ python opencv\_contour\_approx.py
\[INFO\] original, num\_pts=248
\[INFO\] eps=0.0010, num\_pts=43
\[INFO\] eps=0.0064, num\_pts=24
\[INFO\] eps=0.0119, num\_pts=17
\[INFO\] eps=0.0173, num\_pts=12
\[INFO\] eps=0.0228, num\_pts=11
\[INFO\] eps=0.0282, num\_pts=10
\[INFO\] eps=0.0337, num\_pts=7
\[INFO\] eps=0.0391, num\_pts=4
\[INFO\] eps=0.0446, num\_pts=4
\[INFO\] eps=0.0500, num\_pts=4

Notice how, with each increasing value of `eps`, the number of points in the contour keeps decreasing? This signifies that the approximation did indeed work. Note that at an `eps` value of **0.0391**, the number of points starts to saturate. Let’s analyze this better with visualizations.

### **Contour Approximation Visualizations**

Through **Figures 9-12**, we chronicle the evolution of the contour through some of the epochs.

![Image 11](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20591%20500'%3E%3C/svg%3E)

**Figure 9**: At num\_pts 43.

![Image 12](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20591%20500'%3E%3C/svg%3E)

**Figure 10**: At num\_pts 17.

![Image 13](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20591%20500'%3E%3C/svg%3E)

**Figure 11**: At num\_pts 7.

![Image 14](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20591%20500'%3E%3C/svg%3E)

**Figure 12**: At num\_pts 4.

Notice how gradually the curves get smoother and smoother. As the threshold increases, the more linear it gets. By the time the value of `eps` has reached **0.0500**, the contour is now a perfect rectangle, having a mere **4** points. That’s the power of the Ramer–Douglas–Peucker algorithm.

### **Credits**

Distorted shape image inspired by [OpenCV documentation](https://docs.opencv.org/master/dd/d49/tutorial_py_contour_features.html).

### What's next? We recommend [PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/?utm_source=blogPost&utm_medium=bottomBanner&utm_campaign=What%27s%20next%3F%20I%20recommend).

**Course information:**  
84 total classes • 114+ hours of on-demand code walkthrough videos • Last updated: February 2024  
★★★★★ 4.84 (128 Ratings) • 16,000+ Students Enrolled

**I strongly believe that if you had the right teacher you could _master_ computer vision and deep learning.**

Do you think learning computer vision and deep learning has to be time-consuming, overwhelming, and complicated? Or has to involve complex mathematics and equations? Or requires a degree in computer science?

That’s _not_ the case.

All you need to master computer vision and deep learning is for someone to explain things to you in _simple, intuitive_ terms. _And that’s exactly what I do_. My mission is to change education and how complex Artificial Intelligence topics are taught.

If you're serious about learning computer vision, your next stop should be PyImageSearch University, the most comprehensive computer vision, deep learning, and OpenCV course online today. Here you’ll learn how to _successfully_ and _confidently_ apply computer vision to your work, research, and projects. Join me in computer vision mastery.

**Inside PyImageSearch University you'll find:**

*   ✓ **84 courses** on essential computer vision, deep learning, and OpenCV topics
*   ✓ **84 Certificates** of Completion
*   ✓ **114+ hours** of on-demand video
*   ✓ **Brand new courses released _regularly_**, ensuring you can keep up with state-of-the-art techniques
*   ✓ **Pre-configured Jupyter Notebooks in Google Colab**
*   ✓ Run all code examples in your web browser — works on Windows, macOS, and Linux (no dev environment configuration required!)
*   ✓ Access to **centralized code repos for _all_ 536+ tutorials** on PyImageSearch
*   ✓ **Easy one-click downloads** for code, datasets, pre-trained models, etc.
*   ✓ **Access** on mobile, laptop, desktop, etc.

[Click here to join PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/?utm_source=blogPost&utm_medium=bottomBanner&utm_campaign=What%27s%20next%3F%20I%20recommend)

**Summary**
-----------

Simplifying data while retaining the bulk of the information is probably one of the most sought-after scenarios in this data-driven world. Today, we learned about how RDP is used to simplify our tasks. Its contribution to the vector graphics and robotics field is immense.

RDP also spreads over to other domains like Range Scanning, where it is used as a denoising tool. I hope this tutorial helped you understand how you can use contour approximation in your work.

### **Citation Information**

**Chakraborty, **D.**** “OpenCV Contour Approximation ( cv2.approxPolyDP ),” _PyImageSearch_, 2021, [https://pyimagesearch.com/2021/10/06/opencv-contour-approximation/](https://pyimagesearch.com/2021/10/06/opencv-contour-approximation/)

`@article{dev2021opencv, author = {Devjyoti Chakraborty}, title = {Open{CV} Contour Approximation ( cv2.approx{PolyDP} )}, journal = {PyImageSearch}, year = {2021}, note = {https://pyimagesearch.com/2021/10/06/opencv-contour-approximation/}, }`

**To download the source code to this post (and be notified when future tutorials are published here on PyImageSearch), _simply enter your email address in the form below!_**

![Image 15](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20410%20567'%3E%3C/svg%3E)

#### Download the Source Code and FREE 17-page Resource Guide

Enter your email address below to get a .zip of the code and a **FREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning.** Inside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL!