对于一个项目,我使用 OpenCV 的 findContours 以及 RETR_EXTERNAL 和 CHAIN_APPROX_NONE 从剪影图像(MPEG-7 核心实验 CE-Shape-1 测试集)中提取闭合轮廓。我需要一种有效的方法来采样这些轮廓上的 n 等距点(欧几里得距离)以进行进一步处理,包括曲率计算。
最初,我手动计算轮廓点之间的距离,但对于复杂形状来说速度缓慢且不准确。然后我探索了诸如均匀弧长采样之类的算法,希望得到 OpenCV 实现或集成指导,但发现资源有限。
我正在寻求有关有效采样方法的指导,无论是通过推荐的 OpenCV 函数还是已建立的算法。具体的代码示例或实施见解以及 OpenCV 社区的任何专业知识或最佳实践都是非常宝贵的。我对替代方法持开放态度,最终目标是为我的计算提供准确有效的采样。
您可以使用
np.linspace
生成在一系列值之间均匀采样的点。然后使用这些索引从通过传递 CHAIN_APPROX_NONE
获得的原始轮廓中获取均匀的采样点。
再次强调,如果没有示例图像和 stackoverflow 的其他要求,就很难回答你的问题。所以请阅读如何提问。
这是我在红色圆圈示例中使用的代码,我用不同数量的点来近似:
im = cv2.imread("RedContour.png") # read image
imRGB = cv2.cvtColor(im, cv2.COLOR_BGR2RGB) # convert to RGB for easy plotting with matplotlib
imGray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) # convert to gray for threhshold
_, thresh = cv2.threshold(imGray, 127, 255, cv2.THRESH_BINARY_INV) # inverse thresh to get red blob
contour, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # get contour with not approximation
X,Y = np.array(contour[0]).T # unpack contour to X and Y coordinates
X = X[0] # ==//==
Y = Y[0] # ==//==
fig = plt.figure() # for plotting
for numPoints in range(3, 22): # loop different approximations #
fig.clf() # for plotting
resampleIndices = np.linspace(0, len(Y) - 1, numPoints, dtype=int) # generate uniformally distant indices
resampledX = X[resampleIndices] # ==//==
resampledY = Y[resampleIndices] # ==//==
modifiedContour = np.dstack((resampledX, resampledY)) # re-do contour
imModifiedContour = cv2.drawContours(imRGB.copy(), contour, -1, (0,0,255), 5) # draw raw contour in blue
imModifiedContour = cv2.drawContours(imModifiedContour, modifiedContour, -1, (0,0,0), 5) # draw modified contour
plt.imshow(imModifiedContour) # for plotting
plt.axis("off") # ==//==
plt.title("Approximated with "+str(numPoints-1)+" points") # ==//==
fig.canvas.draw() # ==//==
结果我做了一个动画:
一些注意事项,如果您使用
numPoints = 2
,您将获得轮廓的第一个和最后一个点,它们彼此非常接近,因此从技术上讲,numPoints = 3
是生成线条的点。
感谢您的回答和评论。
实际上,我需要基于欧几里得距离等距的点,而不仅仅是它们在轮廓中的顺序。
虽然我很欣赏您之前的建议,但我在以下代码中的尝试可能会让您更好地理解我正在寻找的内容:
import numpy as np
import cv2 as cv
# Load the image in color.
color_image = cv.imread('ray.png', cv.IMREAD_COLOR)
# Convert the image to grayscale.
grayscale_image = cv.cvtColor(color_image, cv.COLOR_BGR2GRAY)
# Apply thresholding to create a binary image.
_, binary_image = cv.threshold(grayscale_image, 127, 255, 0)
# Find contours in the binary image.
contours, _ = cv.findContours(binary_image, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)
# Extract the first contour.
contour = contours[0]
# Number of equidistant points to mark.
n = 30
# Initialize a list to store marked points.
marked_points = []
# Calculate the step distance between marked points.
step_distance = cv.arcLength(contour, True) / n
# Iterate through the contour points and mark equidistant points.
i = 0
while True:
if i == len(contour):
break
# Append the current point to the list of marked points.
marked_points.append(contour[i])
# Find the next point that is at least step_distance away.
j = i + 1
while True:
if j == len(contour) or np.linalg.norm(contour[i] - contour[j]) > step_distance:
break
j += 1
i = j
# Print information about the contour and marked points.
print('Number of points in the contour:', len(contour))
print('Number of needed equidistant points:', n)
print('Number of marked points:', len(marked_points))
# Draw the contour and marked points on the original image.
cv.drawContours(color_image, [contour], 0, (0, 255, 0), 2)
for point in marked_points:
cv.circle(color_image, tuple(point[0]), 3, (0, 0, 255), -1)
# Display the result.
cv.imwrite('ray_result.png', color_image)
cv.imshow('Result', color_image)
cv.waitKey(0)
cv.destroyAllWindows()
如您所见,这并不完全是预期的结果:
Number of points in the contour: 478
Number of needed equidistant points: 30
Number of marked points: 26