如何基于已知直线对OpenCV检测得到的轮廓进行拆分?
OpenCV Python实现直线分割轮廓方案
核心思路
利用点与直线的位置关系分类原轮廓点,再补充直线与轮廓的交点,拼接形成两个独立的闭合轮廓。示例效果如下:
具体实现步骤
- 第一步:推导点位置判断规则
把已知直线方程y = mx + c转换为标准式m*x - y + c = 0,对于任意轮廓点(x, y),代入计算得到符号值:
符号值大于0、小于0分别对应点在直线的两侧,等于0代表点在直线上,可随意归入任意一侧。sign = m * x - y + c - 第二步:计算直线与轮廓的有效交点
遍历轮廓中每一段由相邻两个点组成的边,计算该边和分割直线的交点,仅保留落在边的线段范围内的交点。如果最终有效交点数量小于2,说明直线与轮廓无交叉或者仅相切,无需进行分割。 - 第三步:分类原轮廓点
遍历原轮廓的所有点,按照第一步的符号值规则,将点分别存入两个不同的点集contour1、contour2。 - 第四步:补全交点形成闭合轮廓
将两个交点分别添加到两个点集的首尾位置,让两个点集各自形成闭合的点序列,再转换为OpenCV支持的轮廓格式(维度为(n, 1, 2)的numpy数组,dtype为int32)即可。
参考代码示例
import cv2 import numpy as np def split_contour_by_line(contour, m, c): # 存储交点 intersections = [] # 遍历轮廓的每条边 for i in range(len(contour)): p1 = contour[i][0] p2 = contour[(i+1)%len(contour)][0] x1, y1 = p1 x2, y2 = p2 # 计算p1、p2的符号值 s1 = m * x1 - y1 + c s2 = m * x2 - y2 + c # 两点在直线两侧,存在交点 if s1 * s2 < 0: # 计算交点坐标 if x1 == x2: x = x1 y = m * x + c elif y1 == y2: y = y1 x = (y - c)/m else: # 联立两个直线方程求解 k_edge = (y2 - y1)/(x2 - x1) b_edge = y1 - k_edge * x1 x = (b_edge - c)/(m - k_edge) y = m * x + c intersections.append(np.array([[[x, y]]], dtype=np.int32)) # 交点不足2个,不分割 if len(intersections) < 2: return [contour] # 取前两个交点(闭合轮廓和直线相交最多两个有效交点) p_inter1, p_inter2 = intersections[0], intersections[1] # 分类轮廓点 contour1 = [] contour2 = [] for point in contour: x, y = point[0] s = m * x - y + c if s >= 0: contour1.append(point) else: contour2.append(point) # 补充交点形成闭合轮廓 contour1 = np.concatenate([contour1, [p_inter1, p_inter2]], axis=0) contour2 = np.concatenate([contour2, [p_inter1, p_inter2]], axis=0) return [contour1.astype(np.int32), contour2.astype(np.int32)]
注意事项
- 如果分割直线是竖直方向,斜率m为无穷大,可以将直线方程转换为
x = k的格式,修改符号值判断逻辑为sign = x - k即可适配。 - 实际使用时可以对生成的新轮廓调用
cv2.approxPolyDP()做简化,去除冗余点。
内容的提问来源于stack exchange,提问作者Amit Agarwal
相关产品推荐
相关产品推荐

