OpenCV环形ROI轮廓提取问题:阈值图像无法捕获轮廓的解决方法
环形ROI轮廓提取问题解决方法
我写了一个环形ROI选择函数,想在这个区域里提取轮廓,但轮廓像素和零值相邻,而且掩码区域外的像素都是零,导致阈值化后的图像没法捕获到轮廓。想问问当前函数如果能用的话,该怎么正确定义环形ROI或者提取轮廓?
def annulusROI(img, center, innerR, outerR): """ img: Image matrix center: ROI center point [px] (x,y tuple) innerR: ROI inner radius [px] outerR: ROI outer radius [px] mode: Mask selection for white (255, 255, 255), for black (0, 0, 0) [BGR tuple] return roi matrix and left-top start point coordinate """ outRoi, rectC = rectangleROI(img, center, outerR*2, outerR*2) mask1 = np.zeros_like(outRoi) mask2 = np.zeros_like(outRoi) mask1 = cv2.circle(mask1, (round(outerR),round(outerR)), innerR, (255, 255, 255), -1) mask2 = cv2.circle(mask2, (round(outerR),round(outerR)), outerR, (255, 255, 255), -1) mask = cv2.subtract(mask2, mask1) roi = cv2.bitwise_and(outRoi, mask) return roi, (center[0]-outerR, center[1]-innerR)
相关图像



问题分析
当前函数返回的ROI中,环形区域外的像素被置为0,和轮廓边缘的像素值直接相邻,阈值化时无法区分轮廓和背景零值,导致轮廓丢失。下面是两种解决思路:
一、优化环形ROI定义(保留原函数逻辑)
- 扩大ROI边界:避免环形掩码边缘紧贴矩形ROI边界,减少零值干扰;
- 简化掩码生成:直接生成单通道环形掩码,再根据图像通道数扩展,提升效率。
修正后的代码:
def annulusROI(img, center, innerR, outerR): """ img: 图像矩阵 center: ROI中心点 [像素] (x,y 元组) innerR: ROI内半径 [像素] outerR: ROI外半径 [像素] 返回: roi矩阵和左上角起始坐标 """ # 扩大ROI尺寸,避免边缘零值和轮廓重叠 roi_size = outerR * 2 + 2 outRoi, rectC = rectangleROI(img, center, roi_size, roi_size) mask_center = (round(roi_size / 2), round(roi_size / 2)) # 直接生成单通道环形掩码 mask = np.zeros(outRoi.shape[:2], dtype=np.uint8) cv2.circle(mask, mask_center, outerR, 255, -1) cv2.circle(mask, mask_center, innerR, 0, -1) # 彩色图像则扩展掩码到3通道 if len(outRoi.shape) == 3: mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR) roi = cv2.bitwise_and(outRoi, mask) # 修正左上角起始坐标 start_x = center[0] - (roi_size // 2) start_y = center[1] - (roi_size // 2) return roi, (start_x, start_y)
二、调整轮廓提取流程
1. 先掩码再阈值化
不在ROI内做阈值化,而是先给整幅图像的非ROI区域设置一个中间值(比如127),让轮廓和背景产生明显差异,再阈值化提取轮廓:
# 获取图像尺寸 height, width = img.shape[:2] # 生成整幅图的环形掩码 mask = np.zeros((height, width), dtype=np.uint8) cv2.circle(mask, center, outerR, 255, -1) cv2.circle(mask, center, innerR, 0, -1) # 非ROI区域设为中间值127 masked_img = img.copy() masked_img[mask == 0] = 127 # 自动阈值化(OTSU) _, thresh = cv2.threshold(masked_img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 提取轮廓并过滤出ROI内的部分 contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) filtered_contours = [cnt for cnt in contours if cv2.pointPolygonTest(mask, tuple(cnt[0][0]), False) > 0]
2. 用边缘检测替代阈值化
先提取ROI,再用Canny边缘检测识别轮廓,避开零值背景的干扰:
# 获取优化后的ROI roi, start_coord = annulusROI(img, center, innerR, outerR) # 转为灰度图(彩色图像需转换) gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) if len(roi.shape) == 3 else roi # Canny边缘检测 edges = cv2.Canny(gray_roi, 50, 150) # 提取轮廓 contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
内容的提问来源于stack exchange,提问作者Ali Ünsal
相关产品推荐
相关产品推荐

