如何计算OpenCV轮廓内不同区域点之间的最大距离
问题描述
处理图片时仅检测到1个轮廓,需计算该轮廓分隔出的两个区域内部点之间的最大距离,对应示意图如下:
现有轮廓检测代码如下:
im1 = cv.imread('g1.jpg') img_white = np.zeros([512,512,1],dtype=np.uint8) img_white.fill(255) src_gray = cv.cvtColor(im1, cv.COLOR_BGR2GRAY) src_gray = cv.blur(src_gray, (3,3)) threshold = 100 canny_output = cv.Canny(src_gray, threshold, threshold * 2) contours, _ = cv.findContours(canny_output, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) _ = cv.drawContours(img_white, contours,10,(0,255,0), 3)
需求为基于OpenCV实现上述最大距离的计算。
实现方案
单条闭合轮廓会将画面分割为轮廓内侧、外侧两个独立连通域,按以下步骤计算即可:
- 提取目标轮廓:从检测到的轮廓中筛选出需要的单条目标轮廓,可通过面积阈值过滤噪点轮廓。
- 生成区域掩码:通过轮廓填充操作,分别生成内侧区域、外侧区域的二值掩码。
- 提取区域点集:从两个掩码中取出对应区域的所有像素坐标,整理为点集数组。
- 计算跨区域最大距离:计算两个点集之间所有点对的欧氏距离,取最大值即可。大分辨率场景可先提取点集凸包减少计算量,不会影响最终结果精度。
完整可运行代码
import cv2 as cv import numpy as np # 计算两点欧氏距离 def calc_euc_dist(p1, p2): return np.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) im1 = cv.imread('g1.jpg') h, w = im1.shape[:2] # 原有预处理+轮廓检测逻辑 src_gray = cv.cvtColor(im1, cv.COLOR_BGR2GRAY) src_gray = cv.blur(src_gray, (3,3)) threshold = 100 canny_output = cv.Canny(src_gray, threshold, threshold * 2) contours, _ = cv.findContours(canny_output, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) # 筛选目标轮廓:取面积最大的轮廓,避免边缘噪点干扰 target_contour = max(contours, key=cv.contourArea) # 生成内外区域掩码 mask_outer = np.full((h, w), 255, dtype=np.uint8) cv.drawContours(mask_outer, [target_contour], -1, 0, thickness=-1) # 挖空轮廓内部,剩余为外侧区域 mask_inner = np.zeros((h, w), dtype=np.uint8) cv.drawContours(mask_inner, [target_contour], -1, 255, thickness=-1) # 填充轮廓内部,得到内侧区域 # 提取两个区域的点坐标,转为(x,y)格式 pts_outer = np.column_stack(np.where(mask_outer == 255))[:, ::-1] pts_inner = np.column_stack(np.where(mask_inner == 255))[:, ::-1] # 优化:仅保留凸包上的点计算最大距离,大幅降低计算量 hull_outer = cv.convexHull(pts_outer).squeeze() hull_inner = cv.convexHull(pts_inner).squeeze() # 遍历凸包点对找最大距离(无第三方依赖写法,大尺寸图推荐用scipy.cdist加速) max_dist = 0 dist_p1 = None dist_p2 = None for p1 in hull_outer: for p2 in hull_inner: d = calc_euc_dist(p1, p2) if d > max_dist: max_dist = d dist_p1 = tuple(p1.astype(int)) dist_p2 = tuple(p2.astype(int)) # 结果可视化 res_img = im1.copy() cv.line(res_img, dist_p1, dist_p2, (0,0,255), 2) cv.circle(res_img, dist_p1, 4, (0,255,0), -1) cv.circle(res_img, dist_p2, 4, (0,255,0), -1) print(f"两区域内部点最大距离:{max_dist:.2f}像素") cv.imshow("result", res_img) cv.waitKey(0) cv.destroyAllWindows()
注:如果处理4K以上大分辨率图,可先对掩码做缩放降采样,或引入scipy的
cdist函数批量计算距离矩阵,运行速度会比纯Python遍历提升10~100倍。
内容的提问来源于stack exchange,提问作者Felipe Trajano
相关产品推荐
相关产品推荐

