基于图像形状绘制轮廓掩码——使用Canny边缘检测(更新版)
基于OpenCV获取不含内部区域的轮廓掩码解决方案
问题说明
需要使用OpenCV-Python 4.9.0.80,针对两类不规则形状的测试图像(内部含空洞/空白区域),生成仅保留外部形状、不含内部区域的轮廓掩码(需排除灰色背景)。但使用cv.convexHull()得到的结果会强制包裹所有边缘点,覆盖内部空洞,不符合预期需求。
当前使用代码
import cv2 as cv import numpy as np path = '/opt/boundary-remover/SW_test2-01.png' # path = '/opt/boundary-remover/scaled_2x.png' # Load image src = cv.imread(cv.samples.findFile(path)) cv.imwrite('1_original.png', src) # Convert image to gray and blur it src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) cv.imwrite('2_gray.png', src_gray) # Detect edges using Canny canny_output = cv.Canny(src_gray, 100, 200) cv.imwrite('3_canny.png', canny_output) # Find contours contours, _ = cv.findContours(canny_output, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE) # Combine all contours into a single contour all_contours = np.concatenate(contours) # Find the convex hull object for the combined contour hull = cv.convexHull(all_contours) # Draw the combined contour and its convex hull drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8) color = (0, 255, 0) # Green color # cv.drawContours(drawing, [all_contours], 0, color) # cv.imwrite('4_all_contours.png', drawing) cv.drawContours(drawing, [hull], 0, color) cv.imwrite('5_hull.png', drawing) cv.drawContours(drawing, [hull], -1, color, thickness=cv.FILLED) cv.imwrite('7_filled.png', drawing)
解决方案
核心思路是筛选出最外层的原始轮廓,而非使用凸包(凸包会忽略内部空洞)。具体步骤:
- 更改轮廓检索模式,获取包含层级关系的所有轮廓
- 通过面积筛选出最大的外部轮廓(排除内部小空洞的轮廓)
- 用该外部轮廓绘制填充掩码
修改后的完整代码
import cv2 as cv import numpy as np path = '/opt/boundary-remover/SW_test2-01.png' # path = '/opt/boundary-remover/scaled_2x.png' # 加载图像 src = cv.imread(cv.samples.findFile(path)) cv.imwrite('1_original.png', src) # 转灰度并添加高斯模糊(减少噪声干扰) src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) src_gray = cv.GaussianBlur(src_gray, (5,5), 0) cv.imwrite('2_gray_blur.png', src_gray) # Canny边缘检测 canny_output = cv.Canny(src_gray, 50, 150) cv.imwrite('3_canny.png', canny_output) # 查找轮廓:使用RETR_CCOMP获取层级,区分内外轮廓 contours, hierarchy = cv.findContours(canny_output, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE) # 筛选面积最大的轮廓(即外部形状轮廓) max_area = 0 outer_contour = None for cnt in contours: area = cv.contourArea(cnt) if area > max_area: max_area = area outer_contour = cnt # 创建并填充掩码 mask = np.zeros_like(src_gray) cv.drawContours(mask, [outer_contour], -1, 255, thickness=cv.FILLED) cv.imwrite('final_mask.png', mask) # 彩色可视化版本(可选) color_mask = np.zeros((src.shape[0], src.shape[1], 3), dtype=np.uint8) cv.drawContours(color_mask, [outer_contour], -1, (0,255,0), thickness=cv.FILLED) cv.imwrite('final_color_mask.png', color_mask)
关键说明
cv.RETR_CCOMP:可检索所有轮廓并分为两层(外层/内层),便于区分外部形状和内部空洞cv.contourArea():通过面积筛选,最大轮廓即为目标外部形状,自动排除内部小轮廓- 高斯模糊:预处理减少噪声,避免Canny检测出多余的干扰边缘
内容的提问来源于stack exchange,提问作者NiRmaL
相关产品推荐
相关产品推荐

