1

I would like to approximate smooth lines with chain of line segments.

cv2.approxPolyDP in OpenCV 3.4 made a good result in the case of closed curve.

Origin close curve: Origin close curve Approximated close curve: Approximated close curve

But in the case of open curve, cv2.approxPolyDP did not achieve the desired effect.

Origin open curve: Origin open curve Approximated open curve: Approximated open curve

The result I want should be one chain of line segments but not a closed polygon, like this(this picture is created by Photoshop but not Python program): enter image description here

Is there a way to use cv2.approxPolyDP to approximate open curve?

My Python program is as follow:

import cv2

img = cv2.imread('1.jpg')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cv2.imshow("gray", gray)
cv2.waitKey(0)

_, binary = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY)

# cv2.imshow("binary", binary)
# cv2.waitKey(0)

_, contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

for contour in contours:
    epsilon = 0.009 * cv2.arcLength(contour, True)
    approx = cv2.approxPolyDP(contour, epsilon, closed=True)
    cv2.drawContours(img, [approx], -1, (0, 255, 255), 1)

cv2.imshow("approx", img)
cv2.waitKey(0)

cv2.destroyAllWindows()

The origin photos used in my program are as follow.

Close curve photo Open curve photo

vividmoon
  • 76
  • 1
  • 6
  • Aha! I think I've spotted the problem from looking at the images in your blog, and now I see what you mean by "doubled up". Contours are *outlines* of shapes, which means even for a single straight line, the contour of that would be a rectangle around that line. So you really need to just approximate the curve itself, which you can get the pixels of with `cv2.findNonZero()` directly on the grayscale image. Not sure how to get those in the correct order, though. Also, I gave you an up-vote so you should be able to add your images directly to your post now (if you'd like). – alkasm Oct 13 '18 at 07:24

3 Answers3

1

According to the docs for approxPolyDP(), you can just simply use closed=False:

closed – If true, the approximated curve is closed (its first and last vertices are connected). Otherwise, it is not closed.

So you should just be able to do:

approx = cv2.approxPolyDP(contour, epsilon, closed=False)
alkasm
  • 22,094
  • 5
  • 78
  • 94
  • I have tried to modify parameter `closed=False`. But in my case, although the curve are only 1 pixel in my original picture, `cv2.approxPolyD` returns chain of line segments that surround my origin curves. I just want to get a single chain of line segments. – vividmoon Oct 13 '18 at 03:58
  • @vividmoon then can you edit and add a [minimal, complete, and verifiable example](https://stackoverflow.com/help/mcve) to the OP of something failing your needs so that a solution to your actual problem can be solved? – alkasm Oct 13 '18 at 04:17
  • 1
    Thank you for reminding me, my reputation is less than 10 so I can not add image in description, but I added and external link to my own blog to explain my case. – vividmoon Oct 13 '18 at 06:55
1

Here is how to do that in Python/OpenCV using cv2.approxPolyDP

Input (cropped off screen snap title bar)

enter image description here

import numpy as np
import cv2

# read input
img = cv2.imread('curve.png')
hh, ww = img.shape[:2]

# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold
thresh = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY)[1]

# get points
points = np.column_stack(np.where(thresh.transpose() != 0))

# list points
for pt in points:
    ptx = pt[0]
    pty = pt[1]
    print(ptx,pty)

# approximate polygon
poly = cv2.approxPolyDP(points, 0.02 * ww, False)

# list polygon points
for p in poly:
    px = p[0]
    py = p[0]
    print(px,py)

# draw polygon on copy of input
result = img.copy()
cv2.polylines(result, [poly], False, (0,0,255), 1)

# save results
cv2.imwrite('curve_polygon.png', result)

cv2.imshow("thresh", thresh)
cv2.imshow("result", result)
cv2.waitKey(0)

enter image description here

fmw42
  • 46,825
  • 10
  • 62
  • 80
0

Finally, I still couldn't find a way that can be used directly in OpenCV. But I found an algorithm(named Ramer–Douglas–Peucker algorithm) to approximate curve with just a little code.

See https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm

See https://www.sciencedirect.com/science/article/abs/pii/0167865594900027

vividmoon
  • 76
  • 1
  • 6