You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

cv2.findContours提取简单二值图轮廓效果不佳,如何正确提取?

二值图像轮廓提取问题

问题背景

给定如下二值图像:

import numpy as np
thresh = np.array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
       [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1]]).astype('uint8')

尝试用以下代码提取轮廓:

import cv2
contours, hierarchy = cv2.findContours(
            thresh,
            cv2.RETR_CCOMP,
            cv2.CHAIN_APPROX_SIMPLE
        )

绘制轮廓的代码:

canvas = np.zeros_like(thresh)
for ct in contours:
    cv2.drawContours(canvas, ct,-1, 1, 1)

直观来看,二值图像中值为1的区域是箭头形状的完整区域,但绘制后得到零散的轮廓点,需解决如何正确提取轮廓的问题。


问题原因

该二值图像存在多个内部孔洞(如第14行的(0,8)位置、第16行的(0,14)位置均为0值孔洞),结合使用的cv2.RETR_CCOMP模式,会同时提取所有外层轮廓和内层孔洞的轮廓,观察到的零散轮廓实际是这些孔洞的边界。


解决方案

1. 提取最外层轮廓

改用cv2.RETR_EXTERNAL模式,该模式仅提取最外层轮廓,忽略所有内层孔洞的轮廓,直接得到完整的箭头外层轮廓:

contours, hierarchy = cv2.findContours(
    thresh,
    cv2.RETR_EXTERNAL,  # 仅保留最外层轮廓
    cv2.CHAIN_APPROX_SIMPLE
)

2. 填充内部孔洞(可选)

若需要将箭头区域变为实心后再提取轮廓,可使用floodFill填充孔洞:

# 复制原图像并初始化填充掩码
filled = thresh.copy()
h, w = filled.shape
mask = np.zeros((h+2, w+2), np.uint8)

# 从左上角0区域填充,反转后得到实心箭头区域
cv2.floodFill(filled, mask, (0,0), 1)
filled = cv2.bitwise_not(filled)

# 提取实心区域的轮廓
contours, hierarchy = cv2.findContours(filled, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

3. 正确绘制轮廓

确保绘制参数正确:thickness设为正数时绘制轮廓线,设为-1时填充轮廓内部。使用RETR_EXTERNAL模式后,contours列表仅包含一个大轮廓,可直接绘制:

canvas = np.zeros_like(thresh)
# 绘制轮廓线(厚度为2)
cv2.drawContours(canvas, contours, -1, 1, thickness=2)
# 或者填充轮廓内部
# cv2.drawContours(canvas, contours, -1, 1, thickness=-1)

内容的提问来源于stack exchange,提问作者Sandeep Thapa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 10:36:23