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

OpenCV红色轮廓检测:轮廓计数不符(预期3个实际14个)问题排查

问题:检测红色轮廓时轮廓数量不符的排查与解决

问题背景

我尝试检测带有红色轮廓的特定轮廓,当前代码输出的轮廓数量为14,但正确结果应为3个,请问哪里处理有误?

我的处理代码

import numpy as np
import cv2
image = cv2.imread('C:/Users/htc/Desktop/image.png')
original = image.copy()
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([0,50,50], dtype="uint8")
upper = np.array([10, 255,255], dtype="uint8")
mask = cv2.inRange(image, lower, upper)
# Find contours
cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Extract contours depending on OpenCV version
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
print(len(cnts))
# Iterate through contours and filter by the number of vertices
for c in cnts:
    perimeter = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.04 * perimeter, True)
    if len(approx) > 5:
        cv2.drawContours(original, [c], -1, (36, 255, 12), -1)
cv2.imshow('mask', mask)
cv2.imshow('original', original)
cv2.waitKey()

问题排查与解决思路

咱们一步一步拆解问题,看看哪里出了问题:

1. HSV红色范围不完整

红色在HSV色彩空间里是跨区间的:一部分H值在0-10左右(偏亮红),另一部分在170-180左右(偏深红/暗红)。你现在只取了低区间的红色,会导致目标红色区域没被完全覆盖,反而把一些杂色小区域误识别成轮廓,直接让数量暴增。

解决方法:把两个红色区间的mask合并起来:

# 低区间亮红色
lower_red1 = np.array([0,50,50], dtype="uint8")
upper_red1 = np.array([10, 255,255], dtype="uint8")
mask1 = cv2.inRange(image, lower_red1, upper_red1)
# 高区间暗红色
lower_red2 = np.array([170,50,50], dtype="uint8")
upper_red2 = np.array([180, 255,255], dtype="uint8")
mask2 = cv2.inRange(image, lower_red2, upper_red2)
# 合并两个mask,覆盖所有红色区域
mask = cv2.bitwise_or(mask1, mask2)

2. Mask未做降噪处理

如果原始图像里有红色噪点、小斑点,findContours会把这些都当成独立轮廓,这是轮廓数量超标的常见原因。

解决方法:在找轮廓前对mask做形态学闭运算(先膨胀再腐蚀),把小噪点消除:

# 创建3x3的矩形结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
# 闭运算消除小噪点,迭代2次增强效果
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=2)

3. 轮廓过滤条件太宽松

你现在只用顶点数量len(approx) >5过滤,但很多小杂轮廓也可能满足这个条件。建议加上面积过滤,直接跳过面积过小的轮廓:

for c in cnts:
    area = cv2.contourArea(c)
    # 跳过面积小于100的小轮廓(阈值可根据你的图像调整)
    if area < 100:
        continue
    perimeter = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.04 * perimeter, True)
    if len(approx) > 5:
        cv2.drawContours(original, [c], -1, (36, 255, 12), -1)

验证小技巧

调整后可以先单独查看处理后的mask:如果mask里还有很多小白色斑点,说明噪点没清干净;如果目标红色区域有缺失,就再微调HSV的区间阈值。

内容的提问来源于stack exchange,提问作者Shahnawaz Irfan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 08:02:33