一、核心思路(3 步)
- 转 HSV:用 HSV 空间做颜色过滤(比 RGB 稳)。
- 轮廓检测 + 形状判断 :用
approxPolyDP数角点。 - 颜色 + 形状组合输出:给每个物体打标签(如 "Blue Rectangle")。
二、代码
python
import cv2
import numpy as np
# ---------------------- 1. 颜色识别函数 ----------------------
def get_color(h, s, v):
if s < 100 or v < 100:
return "White/Gray"
if (h >= 0 and h < 10) or (h >= 170 and h <= 180):
return "Red"
elif h >= 40 and h < 80:
return "Green"
elif h >= 100 and h < 130:
return "Blue"
else:
return "Other"
# ---------------------- 2. 形状识别函数 ----------------------
def get_shape(approx):
sides = len(approx)
if sides == 3:
return "Triangle"
elif sides == 4:
return "Rectangle"
elif sides >= 6:
return "Circle"
else:
return "Unknown"
# ---------------------- 3. 主程序 ----------------------
img = cv2.imread("shapes.png") # 换成你的图片路径
h, w = img.shape[:2]
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化 + 轮廓
_, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area = cv2.contourArea(cnt)
if area < 500: # 过滤噪点
continue
# 形状判断
epsilon = 0.04 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
shape = get_shape(approx)
# 找轮廓中心(用于取颜色)
M = cv2.moments(cnt)
if M["m00"] == 0:
continue
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
# 取中心像素的HSV → 判断颜色
h_val, s_val, v_val = hsv[cy, cx]
color = get_color(h_val, s_val, v_val)
# 画轮廓 + 写文字
cv2.drawContours(img, [approx], -1, (0,255,0), 2)
text = f"{color} {shape}"
cv2.putText(img, text, (cx-40, cy),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,0,0), 2)
cv2.imshow("Result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
三、关键代码与原理说明
python
h, w = img.shape[:2]
cv2.imread()得到的img是一个h,w,c的三维数组
python
_, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)
240是阈值,表示阈值为240
255是最大值
cv2.THRESH_BINARY_INV 表示黑白反转
意思是:灰度值>=240的变成黑色,<240 变成白色
反二值化规则:
- 像素值 > 240 → 设为 0(黑)
- 像素值 ≤ 240 → 设为 255(白)

findContours 的 "怪脾气"
它只在:
- 黑色背景(0)
- 白色物体(255)
这种图里,才能正确找到物体轮廓。反过来:白底、黑 / 彩色物体 → 直接找不到或乱找。
python
_, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY)
白底黑物 ,findContours 不认,检测不到轮廓。
python
epsilon = 0.04 * cv2.arcLength(cnt, True)
周长的0.04倍作为误差值,True表示闭合曲线
python
M = cv2.moments(cnt)
if area < 500: # 过滤噪点
continue
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
返回M是一个字典,计算这个轮廓的 "矩",M["m10"] 里的 m = moment(矩) ,后面的 0 和 10 是公式编号
其中:M["m00"] = 物体的面积, 如果面积 = 0 → 这个轮廓是空的,跳过它
M"m10"表示x的所有点的 X 坐标总和
M"m01"= 所有点的 Y 坐标总和
- cx = X 坐标总和 ÷ 总面积(得到中心 X)
- cy = Y 坐标总和 ÷ 总面积(得到中心 Y)
python
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
#hsv是一张图片
- 每个像素 =
[蓝, 绿, 红] - hsv 是 HSV 空间每个像素 =
[色相H, 饱和度S, 亮度V]
都是图片,都是三维数组(高度 × 宽度 × 3)
它和你最开始的 img 结构完全一样 ,只是颜色空间变了:
- img 是 BGR 空间每个像素 =
[蓝, 绿, 红] - hsv 是 HSV 空间每个像素 =
[色相H, 饱和度S, 亮度V
那我们为什么要取 hsv cy, cx?
因为我们要拿物体中心点的像素值:
- 在 HSV 图片里
- 第 cy 行 ,第 cx 列
- 拿出这个像素的 H、S、V 三个值