OpenCV Python: cv2.findContours - ValueError: too many values to unpack - python

I'm writing an opencv program and I found a script on another stackoverflow question: Computer Vision: Masking a human hand
When I run the scripted answer, I get the following error:
Traceback (most recent call last):
File "skinimagecontour.py", line 13, in <module>
contours, _ = cv2.findContours(skin_ycrcb, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
ValueError: too many values to unpack
The code:
import sys
import numpy
import cv2
im = cv2.imread('Photos/test.jpg')
im_ycrcb = cv2.cvtColor(im, cv2.COLOR_BGR2YCR_CB)
skin_ycrcb_mint = numpy.array((0, 133, 77))
skin_ycrcb_maxt = numpy.array((255, 173, 127))
skin_ycrcb = cv2.inRange(im_ycrcb, skin_ycrcb_mint, skin_ycrcb_maxt)
cv2.imwrite('Photos/output2.jpg', skin_ycrcb) # Second image
contours, _ = cv2.findContours(skin_ycrcb, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for i, c in enumerate(contours):
area = cv2.contourArea(c)
if area > 1000:
cv2.drawContours(im, contours, i, (255, 0, 0), 3)
cv2.imwrite('Photos/output3.jpg', im)
Any help is appreciated!

I got the answer from the OpenCV Stack Exchange site. Answer
THE ANSWER:
I bet you are using the current OpenCV's master branch: here the return statements have changed, see http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=findcontours.
Thus, change the corresponding line to read:
_, contours, _= cv2.findContours(skin_ycrcb, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
Or: since the current trunk is still not stable and you probably will run in some more problems, you may want to use OpenCV's current stable version 2.4.9.

This works in all cv2 versions:
contours, hierarchy = cv2.findContours(
skin_ycrcb, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2:]
Explanation: By using [-2:], we are basically taking the last two values from the tuple returned by cv2.findContours. Since in some versions, it returns (image, contours, hierarchy) and in other versions, it returns (contours, hierarchy), contours, hierarchy are always the last two values.

You have to change this line;
image, contours, _ = cv2.findContours(skin_ycrcb, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

I'm using python3.x and opencv 4.1.0
i was getting error in the following code :
cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
ERROR : too many values to Unpack
then i came to know that above code is used in python2.x
SO i just replaced above code with below one(IN python3.x) by adding one more '_' in the left most side
have a look
_,cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

The thing you need to do is just add '_' where you are not using the required var , originally given by:
im2, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
to
_ , contours, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
Here the original doc is given:
https://docs.opencv.org/3.1.0/d4/d73/tutorial_py_contours_begin.html

python is right.
you cannot unpack 3 values from the turple and place them in a turple of two
contours, _ = cv2.findContours(skin_ycrcb, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
use
img, contours, _ = cv2.findContours(skin_ycrcb, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

#Safwan has provided a simple hack by choosing the last two tuples returned by cv2.findContours() function. But what if you want all the values returned from every tuple of the function independent of the version of cv2?
The following snippet will work irrespective of the OpenCV version (2, 3 or 4) installed in your system/environment and will also store all the tuples in individual variables.
OpenCV version 2 & 4, return 2 variables: contours and its hierarchy
OpenCV version 3, returns 3 variables: input image, contours and its hierarchy
First, get the version of OpenCV installed (we don't want the entire version just the major number either 2, 3 or 4) :
import cv2
major_version = 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_version == '4' | major_version == '2':
contours, hierarchy = cv2.findContours(img_binary, cv2.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
elif major_version == '3':
im2, contours, hierarchy = cv2.findContours(img_binary, cv2.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
The contours can be used for further processing.

No big deal, just you might be using open-cv 3.something, which returns 3 values at the point of error and you must be catching only 2, just add any random variable before the contours variable -
_,contours,_ = cv2.findContours(skin_ycrcb, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

If the error is:
not enough values to unpack (expected 3, got 2)
then use:
ctrs,hier=cv2.findContours(im_th.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
instead of
_,ctrs,hier=cv2.findContours(im_th.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

Related

Errors with python and OpenCV ( error: (-215:Assertion failed) npoints > 0 in function 'cv::drawContours') ( too many values to unpack (expected 2)) [duplicate]

When I run this code:
import cv2
image = cv2.imread('screenshoot10.jpg')
cv2.imshow('input image', image)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edged = cv2.Canny(gray, 30, 200)
cv2.imshow('canny edges', edged)
_, contours = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cv2.imshow('canny edges after contouring', edged)
print(contours)
print('Numbers of contours found=', len(contours))
cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
cv2.imshow('contours', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
I am getting this error:
OpenCV(4.1.1)
C:\projects\opencv-python\opencv\modules\imgproc\src\drawing.cpp:2509:
error: (-215:Assertion failed) npoints > 0 in function
'cv::drawContours'
What am I doing wrong?
According to the documentation for findContours, the method returns (contours, hierarchy), so I think the code should be:
contours, _ = cv2.findContours(edged,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
instead of
_, contours = cv2.findContours(edged,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)
Depending on the OpenCV version, cv2.findContours() has varying return signatures. In v3.4.X, three items are returned.
image, contours, hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
In v2.X and v4.1.X, two items are returned.
contours, hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
You can easily obtain the contours regardless of the version like this:
cnts = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
...
Since the last two values are always the same, we can further condense it into a single line using [-2:] to extract the contours from the tuple returned by cv2.findContours()
cnts, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2:]
Because this question shows up on top when looking for Error: (-215:Assertion failed) npoints > 0 while working with contours using OpenCV, I want to point out another possible reason as to why one can get this error:
I was using the BoxPoints function after doing minAreaRect to get a rotated rectangle around a contour. I hadn't done np.int0 to its output (to convert the returned array to integer values) before passing it to drawContours. This fixed it:
rect = cv2.minAreaRect(cnt)
box = cv2.cv.BoxPoints(rect)
box = np.int0(box) # convert to integer values
cv2.drawContours(im,[box],0,(0,0,255),2)
All the solutions provided here are either fixed for a particular version or they do not store all the values returned from cv2.findContours(). You can store all the values returned from every tuple of the function independent of the version of cv2.
The following snippet will work irrespective of the OpenCV version installed in your system/environment and will also store all the tuples in individual variables.
First, get the version of OpenCV installed (we don't want the entire version just the major number either 3 or 4) :
import cv2
version = cv2.__version__[0]
Based on the version either of the following two statements will be executed and the corresponding variables will be populated:
if version == '4':
contours, hierarchy = cv2.findContours(binary_image, cv2.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
elif version == '3':
img, contours, hierarchy = cv2.findContours(binary_image, cv2.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
The contours returned from the function in either scenarios will be stored in contours.
Note: the above snippet is written assuming either version 3 or 4 of OpenCV is installed. For older versions, please refer to the documentation or update to the latest.
If you are using OpenCV version 4, and wondering what the first variable returned by cv2.findContours() of version 3 is; its just the same as the input image (in this case binary_image).

How do I fix an OpenCV Error npoints > 0 error?

so I am trying to follow a tutorial using OpenCV in order to compare 2 images with a stationary background but I move 2 markers slightly. The code should be recognizing the changes of the marker, but it isn't doing it for some reason. It continues to return this error. I am trying to learn how to use the absdiff function to accomplish this goal of recognizing the changes, but I commented it out in an attempt to fix the error, which turned out to be unsuccessful.
Tutorial: https://www.authentise.com/post/how-to-track-objects-with-stationary-background
File "/Users/Starpool13/Desktop/OpenCV/compare.py", line 26, in <module>
cv2.drawContours(image_sub, contours, -1, (100, 0, 255),2)
cv2.error: OpenCV(4.5.1) /private/var/folders/nz/vv4_9tw56nv9k3tkvyszvwg80000gn/T/pip-req-build-yaf6rry6/opencv/modules/imgproc/src/drawing.cpp:2501: error: (-215:Assertion failed) npoints > 0 in function 'drawContours'
Below is my full code
import cv2
import numpy
image1 = cv2.imread('Photos/Before.png')
image2 = cv2.imread('Photos/After.png')
'''
destination_image = cv2.absdiff(image1, image2)
'''
def preprocess_image(image):
bilateral_filtered_image = cv2.bilateralFilter(image, 7, 150, 150)
gray_image = cv2.cvtColor(bilateral_filtered_image, cv2.COLOR_BGR2GRAY)
return gray_image
preprocessed_image1 = preprocess_image(image1)
preprocessed_image2 = preprocess_image(image2)
image_sub = cv2.absdiff(preprocessed_image1, preprocessed_image2)
kernel = numpy.ones((5,5),numpy.uint8)
close_operated_image = cv2.morphologyEx(image_sub, cv2.MORPH_CLOSE, kernel)
_, thresholded = cv2.threshold(close_operated_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
median = cv2.medianBlur(thresholded, 5)
_, contours = cv2.findContours(image_sub, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image_sub, contours, -1, (100, 0, 255),2)
_, _, angle = cv2.fitEllipse(contour)
Please let me know any possible solutions to this error, thanks in advance!
I see your opencv version is 4.5.1. You seem to be following a tutorial for an old version of opencv where it states,
_, contours, _ = cv2.findContours(median, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
Based on the documentation for, findContours it should be the first variable that has contour. In your case it is getting in, _ as you are doing,
_, contours = cv2.findContours(image_sub, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
Try this instead,
contours, _ = cv2.findContours(image_sub, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
Documentation for opencv 4.5.1,
https://docs.opencv.org/4.5.1/d4/d73/tutorial_py_contours_begin.html
Another option is use try using older opencv version with the tutorial link you provided. Here you will be able to use, _, contours, _.

too many values to unpack (expected 2) while generating contour [duplicate]

I am a python beginner . I was trying to run this code :
#applying closing function
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
closed = cv2.morphologyEx(th3, cv2.MORPH_CLOSE, kernel)
#finding_contours
(cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
cv2.drawContours(frame, [approx], -1, (0, 255, 0), 2)
when I summon the mask.py I got this ValueError :
Traceback (most recent call last):
File "mask.py", line 22, in <module>
(cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
ValueError: too many values to unpack
what is wrong with this code ?
It appears that you're using OpenCV version 3.x, while writing code intended for the 2.x branch. There were some API changes between those two branches. Since you're using Python, you have a handy help available -- make sure to use it, along with the documentation.
OpenCV 2.x:
>>> import cv2
>>> help(cv2.findContours)
Help on built-in function findContours in module cv2:
findContours(...)
findContours(image, mode, method[, contours[, hierarchy[, offset]]]) -> contours, hierarchy
OpenCV 3.x:
>>> import cv2
>>> help(cv2.findContours)
Help on built-in function findContours:
findContours(...)
findContours(image, mode, method[, contours[, hierarchy[, offset]]]) -> image, contours, hierarchy
This means that in your script the correct way to call findContours when using OpenCV 3.x would be something like
(_, cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
UPDATE (Dec 2018)
In OpenCV 4.x, findContours returns 2 values only.
>>> help(cv2.findContours)
Help on built-in function findContours:
findContours(...)
findContours(image, mode, method[, contours[, hierarchy[, offset]]]) -> contours, hierarchy
. #brief Finds contours in a binary image.
You can use cv2.findContours() irrespective of the version with following code snippet:
import cv2 as cv
version = cv.__version__
version = version[0]
if version == '4' | version == '2':
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
elif version == '3':
im2, contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
OpenCV 2.x and 4.x returns 2 variables, while 3.x return 3 variables

cv2.findContours function is not working in both versions

I am new to computer vision and haven't really went through any tutorials on thresholding or blurring or other filters.
I am using the below two piece of codes which finds out the contours in an image. On one hand the method is working but on the other it is not. I would need help in understanding the reason this is happening so as to convince myself what is happening in the background.
Working code snippet:
img=cv2.imread('path.jpg')
imgBlurred = cv2.GaussianBlur(img, (5, 5), 0)
gray = cv2.cvtColor(imgBlurred, cv2.COLOR_BGR2GRAY)
sobelx = cv2.Sobel(gray, cv2.CV_8U, 1, 0, ksize=3)
cv2.imshow("Sobel",sobelx)
cv2.waitKey(0)
ret2, threshold_img = cv2.threshold(sobelx, 120, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
im2, contours, hierarchy = cv2.findContours(threshold_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
Not working code snippet
# read image
src = cv2.imread(file_path, 1)
# show source image
cv2.imshow("Source", src)
# convert image to gray scale
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
# blur the image
blur = cv2.blur(gray, (3, 3))
# binary thresholding of the image
ret, thresh = cv2.threshold(blur, 200, 255, cv2.THRESH_BINARY)
# find contours
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
I would really appreciate if anyone can find out the reason for the wrong which is happening here.
The error that i am facing is:
Traceback (most recent call last): File "convexhull.py", line 27, in
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) ValueError: not enough values to unpack
(expected 3, got 2)
Let me know if any other information is also required.
This is due to a change in openCV. Since version 4.0 findContours returns only 2 values: the contours and the hierarchy. Before, in version 3.x, it returned 3 values. You can use the documentation to compare the different versions.
The second code snippet should work when you change your code to:
# find contours
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
Why the first snippet picks a different openCV version can't be determined from the information given.
The following snippet will work irrespective of the OpenCV version installed in your system/environment and will also store all the tuples in individual variables that can be used later in the code.
Store the major version of OpenCV installed:
import cv2
major_version = 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_version == '4':
contours, hierarchy = cv2.findContours(image_binary, cv2.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
elif major_version == '3':
image, contours, hierarchy = cv2.findContours(image_binary, cv2.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
The contours returned from the function in either scenarios will be stored in contours.

How to draw contours using opencv in Python?

I have following image preprocessed:
Now I want to use findContours method in order to find cells from image and then use drawContours in order draw contour for every cell from image. I do this but nothing is shown.
contours = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, -1, (0, 255, 0), 3)
I receive the following error.
contours is not a numpy array, neither a scalar
Another answers didn't helped me.
I also use following solution.
contours = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
ctr = numpy.array(contours).reshape((-1,1,2)).astype(numpy.int32)
cv2.drawContours(img, ctr, -1, (0, 255, 0), 3)
But this solution didn't draw anything on the given image.
Update, OpenCV 4.0 change the cv2.findContours. So I modify the code example. And a more general way:
cnts, hiers = cv2.findContours(...)[:-2]
cv2.findContours returns a tuple, not just the contours. You can do like this:
## Find contours
if cv2.__version__.startswith("3."):
_, contours, _ = cv2.findContours(bimg.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
else:
contours, _ = cv2.findContours(bimg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
## Draw contours
cv2.drawContours(img, contours, -1, (0, 255, 0), 3)
Related similar questions:
(1) Python Opencv drawContour error
(2) How to use `cv2.findContours` in different OpenCV versions?
The tuple returned by cv2.findContours has the form (im, contours, hierarchy) where im is the image with contours visualized, contours is a tuple of two numpy arrays in the form (x_values, y_values) and heirarchy depends on the retrieval mode. Since you are using CV_RETR_EXTERNAL, heirarchy doesn't have much significance.

Categories