引言 在工业检测、机器人视觉、智能分拣等应用场景中,我们经常需要实时检测特定颜色和形状的物体。例如:
冰壶比赛自动计分系统:检测冰面上的圆形冰壶 工业零件分拣:检测红色圆形螺丝、蓝色方形螺母 自动驾驶交通标志识别:检测圆形红圈禁令标志 AGV 小车导航:识别地面彩色圆形二维码 本文将从简单到复杂,介绍几种常见的实现方案,对比它们的性能,并提供完整的开源参考代码,帮助你根据实际场景选择最合适的方案。
方案对比总览 我们主要对比四种主流方案:
方案 原理 计算量 准确率 适合场景 MCU 能否运行 颜色分割 + 轮廓检测 阈值分割 + 形状分析 极低 对颜色形状变化敏感 背景简单、光照稳定 ✅ Cortex-M7 可以 颜色空间转换 + Hough 变换 Hough 圆/直线检测 低 圆形检测较好 固定形状检测 ✅ Cortex-M4 可以 Blob 分析 + 特征匹配 连通域分析 + 形状分类 中 中等 多目标批量处理 ✅ Cortex-M7 可以 深度学习目标检测 YOLO/SSD 直接检测 高 鲁棒性强 复杂背景、光照变化 ❌ 需要 MCU+NPU 或 Linux 下面详细介绍每种方案的实现。
方案一:颜色分割 + 轮廓检测 1.1 算法流程 原始图像 RGB/BGR 颜色空间转换 RGB → HSV 颜色阈值分割 二值掩码 形态学处理 腐蚀 + 膨胀 查找轮廓 cv2.findContours 形状特征计算 面积、周长、圆形度 输出:符合颜色和形状要求的目标 颜色分割 + 轮廓检测流程图 1.2 核心原理 颜色空间转换:从 RGB 转到 HSV 颜色空间,更容易按颜色分割 阈值分割:对 H/S/V 三个通道设置范围,得到二值掩码 形态学处理:腐蚀 + 膨胀去除噪声 轮廓查找:找到所有连通区域 形状特征计算:计算面积、周长、圆形度、矩形度等特征 特征匹配:筛选符合指定形状的目标 1.3 完整 Python 实现 import cv2 import numpy as np def detect_color_shape( image, color_lower=np.array([0, 120, 70]), color_upper=np.array([10, 255, 255]), shape_type="circle", min_area=100, max_area=10000, circularity_threshold=0.8, aspect_ratio_range=(0.9, 1.1) ): """ 检测指定颜色和形状的物体 参数: image: 输入 RGB/BGR 图像 color_lower: HSV 颜色下限 color_upper: HSV 颜色上限 shape_type: "circle" / "square" / "rectangle" min_area: 最小面积(像素) max_area: 最大面积(像素) circularity_threshold: 圆形度阈值(0~1,越大越圆) aspect_ratio_range: 宽高比范围(方形接近 1) 返回: detections: 检测结果列表 [(x, y, w, h, contour), ...] mask: 颜色分割掩码(用于调试) """ # 1. 颜色空间转换:BGR → HSV hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 2. 颜色阈值分割 mask = cv2.inRange(hsv, color_lower, color_upper) # 3. 形态学处理去除噪声 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 4. 查找轮廓 contours, hierarchy = cv2.findContours( mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) detections = [] for contour in contours: # 计算轮廓面积,过滤太小/太大的 area = cv2.contourArea(contour) if area < min_area or area > max_area: continue # 计算轮廓周长 perimeter = cv2.arcLength(contour, True) # 多边形逼近 approx = cv2.approxPolyDP(contour, 0.04 * perimeter, True) # 获取外接矩形 x, y, w, h = cv2.boundingRect(contour) # 形状判断 matched = False if shape_type == "circle": # 圆形度 = 4π * 面积 / (周长^2) # 完美圆形 = 1,越不规则值越小 circularity = 4 * np.pi * area / (perimeter * perimeter) if circularity >= circularity_threshold: matched = True elif shape_type == "square": # 宽高比接近 1,且顶点数约为 4 aspect_ratio = float(w) / h if (len(approx) == 4 and aspect_ratio >= aspect_ratio_range[0] and aspect_ratio <= aspect_ratio_range[1]): matched = True elif shape_type == "rectangle": # 顶点数约为 4 即可 if len(approx) == 4: matched = True elif shape_type == "triangle": if len(approx) == 3: matched = True if matched: center_x = x + w // 2 center_y = y + h // 2 detections.append((center_x, center_y, w, h, contour)) return detections, mask 1.4 使用示例 # 检测红色圆形物体 # HSV 红色范围(两种分段,因为红色在 H 通道首尾) lower_red1 = np.array([0, 120, 70]) upper_red1 = np.array([10, 255, 255]) lower_red2 = np.array([170, 120, 70]) upper_red2 = np.array([180, 255, 255]) # 读取图像 image = cv2.imread("test_image.jpg") # 第一次检测 detections1, mask1 = detect_color_shape( image, lower_red1, upper_red1, shape_type="circle", min_area=500 ) # 第二次检测(另一红色区间) detections2, mask2 = detect_color_shape( image, lower_red2, upper_red2, shape_type="circle", min_area=500 ) # 合并结果 all_detections = detections1 + detections2 combined_mask = mask1 | mask2 # 在原图上绘制结果 for (x, y, w, h, contour) in all_detections: cv2.drawContours(image, [contour], -1, (0, 255, 0), 2) cv2.circle(image, (x, y), 3, (0, 0, 255), -1) cv2.putText(image, "Red Circle", (x-20, y-h-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.imwrite("result.jpg", image) print(f"检测到 {len(all_detections)} 个红色圆形物体") 1.5 常见颜色 HSV 范围参考 颜色 H 下限 H 上限 S 下限 S 上限 V 下限 V 上限 红色 0/170 10/180 120 255 70 255 橙色 11 25 120 255 70 255 黄色 26 35 120 255 70 255 绿色 36 70 120 255 70 255 青色 71 99 120 255 70 255 蓝色 100 124 120 255 70 255 紫色 125 155 120 255 70 255 粉色 156 169 120 255 70 255 黑色 0 180 0 255 0 46 灰色 0 180 0 43 47 221 白色 0 180 0 30 222 255 注意:不同相机的白平衡和亮度设置不同,实际使用时需要根据你的图像微调范围。建议用 OpenCV 窗口滑动条调参:
...