1
votes

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)

2
Check the answer, If it solves, please make it as answerRCvaram

2 Answers

3
votes

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.

0
votes

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].