0

I'm working with binary images representing contours (taken through cv2.Canny), and i want to get the coordinates of each contour clockwise, starting from the first point as intersection of the image and a horizontal line located in the center of the image. Assuming that the image i want to use is a circular contour, i would like to get something like this (assuming Y decreasing vertically, as matplotlib.pyplot.imshow does):

Desired behaviour

I tried with the following code:

indices = numpy.where(edges == [255]) #edges is the contour image
print(indices)

But this solution sorts the coordinates from the upper side of the image. I tried other solution found on the web too, but none of them seems to be usefull for this task.

mao95
  • 1,046
  • 12
  • 21

1 Answers1

2

I will recycle my idea from that answer incorporating the arctan2 function from numpy.

Given is an input image like this:

Input circle

The output will be a plot like this:

Plot circle

Here's the code, which is hopefully self-explaining:

import cv2
import numpy as np
import matplotlib.pyplot as plt

# Generate artificial image
img = np.zeros((400, 400), np.uint8)
center = (150, 150)
img = cv2.circle(img, center, 100, 255, 1)
cv2.imwrite('images/img.png', img)

# Find contour(s)
cnts, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# Center contour
cnt = np.squeeze(cnts[0]) - center

# Calculate atan2 values, and sort
val = np.sort(np.arctan2(cnt[:, 0], cnt[:, 1]))
idx = np.argsort(np.arctan2(cnt[:, 0], cnt[:, 1]))

# atan2 uses (1, 0) as starting point, so correct by 1/2 * pi
corr = np.where(val <= (-0.5 * np.pi))[0][-1]

# Build final indices
indFinal = np.concatenate((idx[corr - 1:], idx[0:corr]))
x = cnt[indFinal, 0]
y = cnt[indFinal, 1]

# Generate plot
ax = plt.subplot(121)
plt.plot(x)
plt.title('x')
ax = plt.subplot(122)
plt.plot(y)
plt.title('y')
plt.savefig('images/plot.png')

Caveat: Concave contours will likely cause corrupted results.

HansHirse
  • 18,010
  • 10
  • 38
  • 67