OpenCV - visualize polygonal curve(s) extracted with cv2.approxPolyDP()
Asked Answered
B

2

32

I want to visualize polygonal curve(s) extracted with cv2.approxPolyDP(). Here's the image I am using:

map of UK

My code attempts to isolate the main island and define and plot the contour approximation and contour hull. I have plotted the contour found in green, the approximation in red:

import numpy as np
import cv2

# load image and shrink - it's massive
img = cv2.imread('../data/UK.png')
img = cv2.resize(img, None,fx=0.25, fy=0.25, interpolation = cv2.INTER_CUBIC)

# get a blank canvas for drawing contour on and convert img to grayscale
canvas = np.zeros(img.shape, np.uint8)
img2gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# filter out small lines between counties
kernel = np.ones((5,5),np.float32)/25
img2gray = cv2.filter2D(img2gray,-1,kernel)

# threshold the image and extract contours
ret,thresh = cv2.threshold(img2gray,250,255,cv2.THRESH_BINARY_INV)
im2,contours,hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)


# find the main island (biggest area)
cnt = contours[0]
max_area = cv2.contourArea(cnt)

for cont in contours:
    if cv2.contourArea(cont) > max_area:
        cnt = cont
        max_area = cv2.contourArea(cont)

# define main island contour approx. and hull
perimeter = cv2.arcLength(cnt,True)
epsilon = 0.01*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)

hull = cv2.convexHull(cnt)

# cv2.isContourConvex(cnt)

cv2.drawContours(canvas, cnt, -1, (0, 255, 0), 3)
cv2.drawContours(canvas, approx, -1, (0, 0, 255), 3)
## cv2.drawContours(canvas, hull, -1, (0, 0, 255), 3) # only displays a few points as well.

cv2.imshow("Contour", canvas)
k = cv2.waitKey(0)

if k == 27:         # wait for ESC key to exit
    cv2.destroyAllWindows()

Here are the resulting images:

enter image description here

The first image plots the contour in green. The second plots the approximation in red - how do I plot this approximation as a continuous closed curve?

The documentation isn't terribly clear and neither is the tutorial, but my understanding is that cv2.approxPolyDP() should define a continuous, closed curve, which I should be able to plot with cv2.drawContours(). Is that correct? If so, what am I doing wrong?

Binate answered 26/1, 2017 at 17:23 Comment(8)
What is your question? What do you want to achieve? Your code does not correspond to visualization. cv2.approxPolyDP() returns list of points, and yes, you are able to draw them as a curve, but I am not sure how it deals with self-intersections (which exist on your initial curve).Nineteenth
@Nineteenth - I have updated my question - how do I plot the returned list of points as a continuous curve if not by using drawContours?Binate
I am still not sure what you are drawing. You are correct, you should use drawContours for drawing contours, but in your code you are drawing them on the same canvas, and them showing one picture, but your attached pictures are different! Could you please post relevant code? And I am still suppose that it can be due to self-intersections. Try to draw lines istead for figuring it out.Nineteenth
@Nineteenth - to get the first image I commented out the second call to drawCanvas() and visa-versa. Make sense?Binate
I see, sorry for inconvenience. Please try to draw lines instead - you will see points order in your approximated contour.Nineteenth
@Nineteenth - what's the best way for me to draw lines? If I use cv2.polylines(canvas, approx, True, (255, 0, 255)), I get basically the same result.Binate
Let us continue this discussion in chat.Nineteenth
Try using convex hull. Example Application of Convex Hull Refs: http://opencvexamples.blogspot.com/2013/10/convex-hull.htmlBroadleaf
N
45

The problem is in visualization only: drawContours expects array (list in case of python) of contours, not just one numpy array (which is returned from approxPolyDP).

Solution is the following: replacing

cv2.drawContours(canvas, approx, -1, (0, 0, 255), 3)

to

cv2.drawContours(canvas, [approx], -1, (0, 0, 255), 3)
Nineteenth answered 26/1, 2017 at 18:24 Comment(0)
L
-3
cv2.approxPolyDP()

approx = cv2.approxPolyDP(cnt, 0.03 * cv2.arcLength(cnt, True), True)
Lothair answered 9/8, 2020 at 8:22 Comment(1)
Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes.Lithography

© 2022 - 2024 — McMap. All rights reserved.