Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
134 views
in Technique[技术] by (71.8m points)

python - Contour shows dots rather than a curve when retrieving it from the list, but shows the curve otherwise

I'm finding the contour of a thresholded image and drawing it like so:

self.disc_contour = cv2.findContours(self.thresh.copy(), cv2.RETR_LIST,cv2.CHAIN_APPROX_NONE)[1] 
cv2.drawContours(self.original_image, self.disc_contour, -1, (0,255,0), 2)

and I get the contour as desired:

(ignore the inner circle. The outer part is the contour in context)

enter image description here

But if I change self.disc_contour in the drawContour function to self.disc_contour[0] I get the following result:

enter image description here

What could be the reason?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

NB: Specific to OpenCV 3.x

The second result from cv2.findContours is a list of contours. The second parameter of cv.drawContours should be a list of contours.

A contour is represented as a list (or array) of points. Each point is a list of coordinates.

There are multiple ways how to draw only a single contour:

import cv2

src_img = cv2.imread("blob.png")
gray_img = cv2.cvtColor(src_img, cv2.COLOR_BGR2GRAY)

contours = cv2.findContours(gray_img, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)[1]

print(contours)

# Choose one:    

# Draw only first contour from the list
cv2.drawContours(src_img, contours, 0, (0,255,0), 2)
# Pass only the first contour (v1)
cv2.drawContours(src_img, [contours[0]], -1, (0,255,0), 2)
# Pass only the first contour (v2)
cv2.drawContours(src_img, contours[0:1], -1, (0,255,0), 2)


cv2.imshow("Contour", src_img)
cv2.waitKey()

Sample input image:

When we inspect the result of cv2.findContours, the reason why you were seeing dots becomes apparent -- there are 4 levels of nesting.

[
    array([
        [[ 95,  61]], # Point 0
        [[ 94,  62]], # Point 1
        [[ 93,  62]],
        ... <snip> ...
        [[ 98,  61]],
        [[ 97,  61]],
        [[ 96,  61]]
    ]) # Contour 0
]

According to the definitions at the beginning of this answer, we can see that the points in this case are wrapped in an additional list, e.g. [[ 98, 61]]. OpenCV apparently deals with this correctly - I suppose this was intended as a feature.

If we remove the outer list by using only the first element of contours, we effectively turn each point into a separate contour containing a single point.

array([
    [
        [ 95,  61] # Point 0
    ], # Contour 0
    [
        [ 94,  62] # Point 0
    ], # Contour 1
    ... and so on
])

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...