Find and Draw Contours with OpenCV in Python

In this tutorial we will learn how to find and draw contours with OpenCV in Python. I will also show you how to draw a bounding box around these detected objects (using contours).

What contours are?

Contours is the curve joining all the continuous points along the boundary which are having same color or intensity.

Real-World Applications of Contour Detection

Contour can be useful tool for:

  • Shape analysis
  • Object detection
  • Object recognition
  • Object Tracking
  • Object Classification
  • etc.

How Contour Detection Works

Find object using Contour detection in OpenCV can be break into below steps:

  • Read an image
  • Convert image to grayscale
  • Convert image to binary (black and white)
  • Find and Draw Contours using OpenCV function
  • Additionally if you want draw contour bounding box in image

Read Image

For this tutorial I will use a color image of different shapes as my input image for contour detection in python.

import cv2  # import OpenCV library

# Read image for contour detection
input_image = cv2.imread("shapes.png")

# Make a copy to draw bounding box
input_image_cpy = input_image.copy()

# Show input image in OpenCV
cv2.imshow('Input image', input_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Read input image for contour detection opencv python
Input Image

Convert image to grayscale

To find contours in OpenCV you first should convert color image to grey scale. To convert color image to grey scale image in OpenCV we will use cv2.COLOR_BGR2GRAY function in OpenCV.

# Convert input image to grayscale
gray_img = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)

# Show grey image in OpenCV
cv2.imshow('Grey image', gray_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Convert image to grey scale for object detection in image processing
Convert image to grey scale

Convert image to binary

Image binarization is used for separating an object from its background. Image binarization in OpenCV is also called Image Thresholding technique. In Image Thresholding, each pixel value is compared with the provided threshold value. If a pixel value is smaller than the threshold value, the pixel value is set to 0 (black), otherwise, that pixel value is set to a maximum value (255 {white}).

There are so many Image Thresholding techniques in OpenCV. Listing some popular function of Image Thresholding techniques in OpenCV python.

  • cv2.THRESH_BINARY: If pixel value is greater than the threshold value, pixel value is set to 255 (white), else set to 0 (black)
  • cv2.THRESH_BINARY_INV: Opposite of cv2.THRESH_BINARY
  • cv2.THRESH_TRUNC: If pixel value is greater than threshold value, then pixel values are set to be the same as the threshold else pixel values remain the same
  • cv2.THRESH_TOZERO: If the pixel value is less than threshold value, set to 0 (black)
  • cv2.THRESH_TOZERO_INV: If the pixel value is less than threshold value, set to 255 (white)
Also Read:  Social distancing detector using OpenCV and Python

In all above steps we manually need to provide threshold values. In below methods threshold value can be calculated automatically.

  • cv2.ADAPTIVE_THRESH_MEAN_C: Threshold Value = (Mean of the neighborhood area values – constant value)
  • cv2.ADAPTIVE_THRESH_GAUSSIAN_C: Threshold Value = (Gaussian – weighted sum of the neighborhood values – constant value)
  • Otsu Thresholding: rect, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 0 and 255 are pixel values for black and white respectively

In this tutorial I will be using simple cv2.THRESH_BINARY function in OpenCV. Now in cv2.THRESH_BINARY function as you have to provide threshold value manually. For that you need to find best threshold value.

How to find best Threshold value

The best way to find threshold value is as follows:

  • Open your input image in paint application in windows
  • Hover your mouse to any point and you will see (x, y) coordinate value (459, 257) for that particular pixel (where your mouse cursor is). Preferably hover your mouse to a light color object in your image (Yellow rectangle)
  • Now you need to print pixel value of that particular point (459, 257) of that image and use that value as your threshold value

find pixel value for a point opencv python
find pixel value for a point

First line of below code is to find threshold value by finding out pixel value of a particular point in the image shown in above image.

threshold_value = gray_img[257, 459]
print(threshold_value)

# Convert the grayscale image to binary (image binarization opencv python)
ret, binary_img = cv2.threshold(gray_img, threshold_value, 255, cv2.THRESH_BINARY)

# Show binary image in OpenCV
cv2.imshow('Binary image', binary_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Image Thresholding technique to find contour opencv python
Image Thresholding

Invert Binary image

To detect object contours, we need our background as black background and foreground or object color as in white color. Since in our binary image background color is white and color of object are in black color, we need to invert our binary image.

# Invert image
inverted_binary_img = ~ binary_img

# Show binary image in OpenCV
cv2.imshow('Inverted image', inverted_binary_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Invert image findcontours opencv python
Invert image

Detect contours using OpenCV function

Now let’s detect contours and store them in a list. Then try to draw just the first contour from this list.

# Detect contours
# hierarchy variable contains information about the relationship between each contours
contours_list, hierarchy = cv2.findContours(inverted_binary_img,
                                       cv2.RETR_TREE,
                                       cv2.CHAIN_APPROX_SIMPLE) # Find contours

# Draw first contour
first_contour = 0
second_contour = 1

contour1 = cv2.drawContours(input_image, contours_list, first_contour,(255,0,255),3)
cv2.imshow('First detected contour', contour1)
cv2.waitKey(0)
cv2.destroyAllWindows()
opencv draw contours
Draw contours

Draw a bounding box around the contour

You can also draw bounding box around any detected object by finding out(x, y) coordinate of the contour.

# Draw a bounding box around the first contour
x, y, w, h = cv2.boundingRect(contours_list[first_contour])
cv2.rectangle(contour1,(x,y), (x+w,y+h), (0,0,255), 5)
cv2.imshow('First contour with bounding box', contour1)
cv2.waitKey(0)
cv2.destroyAllWindows()
draw contours bounding box
Draw contours bounding box

Draw bounding box around all detected contours

Now at final step let’s draw bounding box around all detected contours

# Draw a bounding box around all detected contours
for c in contours_list:
    x, y, w, h = cv2.boundingRect(c)

    # Make sure contour area is large enough
    if (cv2.contourArea(c)) > 10000:
        cv2.rectangle(input_image_cpy, (x, y), (x + w, y + h), (0, 0, 255), 5)

cv2.imshow('All contours with bounding box', input_image_cpy)
cv2.waitKey(0)
cv2.destroyAllWindows()
opencv draw contours bounding box for all contours python

Full Code

import cv2  # import OpenCV library

# Read image for contour detection
input_image = cv2.imread("shapes.png")

# Make a copy to draw bounding box
input_image_cpy = input_image.copy()

# # Show input image in OpenCV
# cv2.imshow('Input image', input_image)
# cv2.waitKey(0)
# cv2.destroyAllWindows()

# Convert input image to grayscale
gray_img = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)

# # Show grey image in OpenCV
# cv2.imshow('Grey image', gray_img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()

threshold_value = gray_img[216, 402]
print(threshold_value)

# Convert the grayscale image to binary (image binarization opencv python)
ret, binary_img = cv2.threshold(gray_img, threshold_value, 255, cv2.THRESH_BINARY)

# # Show binary image in OpenCV
# cv2.imshow('Binary image', binary_img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()

# Invert image
inverted_binary_img = ~ binary_img

# # Show binary image in OpenCV
# cv2.imshow('Inverted image', inverted_binary_img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()

# Detect contours
# hierarchy variable contains information about the relationship between each contours
contours_list, hierarchy = cv2.findContours(inverted_binary_img,
                                       cv2.RETR_TREE,
                                       cv2.CHAIN_APPROX_SIMPLE) # Find contours

# Draw first contour
first_contour = 0
second_contour = 1

contour1 = cv2.drawContours(input_image, contours_list, first_contour,(255,0,255),3)
# cv2.imshow('First detected contour', contour1)
# cv2.waitKey(0)
# cv2.destroyAllWindows()

# Draw a bounding box around the first contour
x, y, w, h = cv2.boundingRect(contours_list[first_contour])
cv2.rectangle(contour1,(x,y), (x+w,y+h), (0,0,255), 5)
# cv2.imshow('First contour with bounding box', contour1)
# cv2.waitKey(0)
# cv2.destroyAllWindows()

# Draw a bounding box around all detected contours
for c in contours_list:
    x, y, w, h = cv2.boundingRect(c)

    # Make sure contour area is large enough
    if (cv2.contourArea(c)) > 10000:
        cv2.rectangle(input_image_cpy, (x, y), (x + w, y + h), (0, 0, 255), 5)

cv2.imshow('All contours with bounding box', input_image_cpy)
cv2.waitKey(0)
cv2.destroyAllWindows()

Conclusion

In this tutorial you learned:

  • Different types of image thresholding techniques in OpenCV
  • Find object and detect using OpenCV Python
  • Draw contours OpenCV python
  • Draw contour bounding box in OpenCV with Python
Also Read:  Use Opencv with GPU with just 2 lines of code

If you have any question or suggestion regarding this post, let me know in the comment section.

Leave a comment