'How to properly use cv2.findContours() on opencv version 4.4.0.?

Im trying to use cv2.findContours() on opencv version 4.4.0. (Im using Python version 3.8.5) but it throws an error, I cant figure out. Im not sure whats wrong with the code. Here's some background:

  • According to OpenCV the syntax for cv2.findContours() is as follows: Python: contours, hierarchy = cv.findContours( image, mode, method[, contours[, hierarchy[, offset]]] )

  • I looked for some examples to make sure how to properly implement it, heres what I found: example 1 _, contours, _ = cv2.findContours(binary_image,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

example 2 (_, cnts, _) = cv2.findContours(thresholded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

Those are from working projects I found online, there are plenty examples like those. So, Im trying to implement some code I got from a video to gain some understanding on the topic but it does not seem to work for me and I cant find why. Heres the code:

import cv2 
import numpy as np 

imagen =cv2.imread('lettuce.jpg')
gray = cv2.cvtColor(imagen,cv2.COLOR_BGR2GRAY)
_,binary = cv2.threshold(gray,100,255,cv2.THRESH_BINARY)

image,contours,hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

cv2.drawContours(image,contours, -1, (0,255,0),3)

Error: Traceback (most recent call last): line 8, in image,contours,hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE) ValueError: not enough values to unpack (expected 3, got 2)



Solution 1:[1]

In Python/OpenCV 4.4.0, findContours returns only 2 values, you list 3.

You show:

image,contours,hierarchy = cv2.findContours(binary,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

OpenCV 4.4.0, lists:

contours, hierarchy = cv.findContours(image, mode, method[, contours[, hierarchy[, offset]]])

Please always check the documentation. See

https://docs.opencv.org/4.4.0/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0

One way to handle this in a version independent way, if all you want are the contours, is (credit to @nathancy):

contours = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]

If you do not want all nested contours, then use RETR_EXTERNAL and not RETR_LIST.

Solution 2:[2]

The problem was the cv2.findContour function will return two things not three.

  1. contours
  2. hierarchy

This cv2.findContours function takes three input arguments. The first argument is an image that should be a grayscale image. The second is a retrieval mode and the third one is the approximation mode. When we apply the findContours method the original image will be affected. The best practice is to take a copy of the image before processing the findContours method.

OpenCV stores the contours in a list of the list. Each list represents a different contour. within the list, all the coordinates of that contour are added to that list. We can store these coordinates differently. How can we store that? The approximation mode comes to play.

Using cv2.CHAIN_APPROX_NONE stores all the boundary points. But we don't necessarily need all the boundary points. If the points form a straight line we only need the start and ending points of that line. Using cv2.CHAIN_APPROX_SIMPLE instead only provides these start and endpoints of bounding contours, thus resulting in much more efficient storage of contour information.

what is retrieval mode? Retrieval mode essentially defines the hierarchy of the contour as so hierarchy being like do you want sub contours or external contours or all contours.

There are four types in retrieval mode in OpenCV.

  • cv2.RETR_LIST ? Retrieve all contours
  • cv2.RETR_EXTERNAL ? Retrieves external or outer contours only
  • cv2.RETR_COMP ? Retrieves all in a 2-level hierarchy
  • cv2.RETR_TREE ? Retrieves all in the full hierarchy

Hierarchy is stored in the following format [next, previous, First child, parent].

Solution 3:[3]

Here is another way to store all the tuples returned from cv2.findContours() irrespective of the OpenCV version installed in your system/environment:

First, get the version of OpenCV installed (we don't want the entire version just the main number either 3 or 4) :

import cv2
major_number = cv2.__version__[0]

Based on the version either of the following two statements will be executed and the corresponding variables will be populated:

if major_number == '4':
    contours, hierarchy = cv2.findContours(img_binary, cv2.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

elif major_number == '3':
    img, contours, hierarchy = cv2.findContours(img_binary, cv2.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

The contours returned from the function in either scenarios will be stored in contours.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 RCvaram
Solution 3 Jeru Luke