如何在 OpenCV 中找到两个轮廓之间的最近点
How to find nearest points between two contours in OpenCV
对于两个轮廓c1和c2,如何找到轮廓之间最近的点。
一个解决方案可能是遍历所有点,获得欧几里得距离并使用最小欧几里得距离,但这将具有巨大的时间复杂度。
我已经用cv2.findContours
找到轮廓了。
contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
我想找到两个轮廓之间蓝色显示的两个点。
由于问题中没有测试数据,因此无法测试,但这应该可以让你继续。使用 KDTree
s.
from scipy.spatial import KDTree
max_distance = 10 #tune to your data
kd_trees = [KDTree(c) for c in contours]
out = {}
for i, j in zip(np.triu_indices((len(contours),)*2, 1)):
coo = kd_trees[i].sparse_distance_matrix(kd_trees[j],
max_distance = max_distance,
output_type = 'coo_matrix')
ix = np.argmin(coo.data)
out[(i, j)] = [contours[i][coo.row[ix]], contours[j][coo.col[ix]]]
使用双 for
循环可能很耗时。根据 Giovanni Tardini 的评论和 an answer from CodeReview.stackexchange,您可以只遍历单个轮廓的点。我没有做过速度对比,但是原作者说向量化可以加速计算。
代码:
# To draw the final result
img = cv2.imread('image_path', 1)
# grayscale to binary conversion -> find contours
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th = cv2.threshold(gray,127,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
contours, hierarchy = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# store the 2 contours
c1, c2 = contours[0], contours[1]
# Function to index and distance of the point closest to an array of points
# borrowed shamelessly from: https://codereview.stackexchange.com/questions/28207/finding-the-closest-point-to-a-list-of-points
def closest_point(point, array):
diff = array - point
distance = np.einsum('ij,ij->i', diff, diff)
return np.argmin(distance), distance
# initialize some variables
min_dist = np.max(th.shape)
chosen_point_c2 = None
chosen_point_c1 = None
# iterate through each point in contour c1
for point in c1:
t = point[0][0], point[0][1]
index, dist = closest_point(t, c2[:,0])
if dist[index] < min_dist :
min_dist = dist[index]
chosen_point_c2 = c2[index]
chosen_point_c1 = t
# draw the two points and save
cv2.circle(img,(chosen_point_c1), 4, (0,255,255), -1)
cv2.circle(img,tuple(chosen_point_c2[0]), 4, (0,255,255), -1)
cv2.imshow('Result', img)
结果:
(我用的是题中分享的图片,仔细看,每条轮廓上都有一个黄点。)
提供了其他解决方案,其中 KDtrees(由 Daniel F 提出)是一个选项。
对于两个轮廓c1和c2,如何找到轮廓之间最近的点。
一个解决方案可能是遍历所有点,获得欧几里得距离并使用最小欧几里得距离,但这将具有巨大的时间复杂度。
我已经用cv2.findContours
找到轮廓了。
contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
我想找到两个轮廓之间蓝色显示的两个点。
由于问题中没有测试数据,因此无法测试,但这应该可以让你继续。使用 KDTree
s.
from scipy.spatial import KDTree
max_distance = 10 #tune to your data
kd_trees = [KDTree(c) for c in contours]
out = {}
for i, j in zip(np.triu_indices((len(contours),)*2, 1)):
coo = kd_trees[i].sparse_distance_matrix(kd_trees[j],
max_distance = max_distance,
output_type = 'coo_matrix')
ix = np.argmin(coo.data)
out[(i, j)] = [contours[i][coo.row[ix]], contours[j][coo.col[ix]]]
使用双 for
循环可能很耗时。根据 Giovanni Tardini 的评论和 an answer from CodeReview.stackexchange,您可以只遍历单个轮廓的点。我没有做过速度对比,但是原作者说向量化可以加速计算。
代码:
# To draw the final result
img = cv2.imread('image_path', 1)
# grayscale to binary conversion -> find contours
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
th = cv2.threshold(gray,127,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
contours, hierarchy = cv2.findContours(th, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# store the 2 contours
c1, c2 = contours[0], contours[1]
# Function to index and distance of the point closest to an array of points
# borrowed shamelessly from: https://codereview.stackexchange.com/questions/28207/finding-the-closest-point-to-a-list-of-points
def closest_point(point, array):
diff = array - point
distance = np.einsum('ij,ij->i', diff, diff)
return np.argmin(distance), distance
# initialize some variables
min_dist = np.max(th.shape)
chosen_point_c2 = None
chosen_point_c1 = None
# iterate through each point in contour c1
for point in c1:
t = point[0][0], point[0][1]
index, dist = closest_point(t, c2[:,0])
if dist[index] < min_dist :
min_dist = dist[index]
chosen_point_c2 = c2[index]
chosen_point_c1 = t
# draw the two points and save
cv2.circle(img,(chosen_point_c1), 4, (0,255,255), -1)
cv2.circle(img,tuple(chosen_point_c2[0]), 4, (0,255,255), -1)
cv2.imshow('Result', img)
结果:
(我用的是题中分享的图片,仔细看,每条轮廓上都有一个黄点。)