1. 基于形状特征的零件分类
任务目标
构建一个基于形状特征的零件分类器,能够识别传送带图像中的圆形、方形和三角形零件。输入为包含单个零件的灰度图像,输出为对应的类别标签(circle / square / triangle)。
实现方案
采用轮廓检测与多边形逼近提取形状特征:计算轮廓面积、周长及顶点数,结合圆度(4π×面积/周长²)判定形状类别。该方法对平移、旋转和尺度变化具有鲁棒性,适用于传送带场景中零件位姿不固定的情况。
依赖环境
- Python 3.8+
- OpenCV(
pip install opencv-python) - NumPy(
pip install numpy)
完整代码
import cv2
import numpy as np
def classify_shape(image_path):
"""
对包含单个零件的灰度图像进行分类。
返回: (类别标签, 轮廓顶点数, 圆度值)
"""
# 读取图像并预处理
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
if img is None:
raise FileNotFoundError(f"无法读取图像: {image_path}")
# 二值化(OTSU自动阈值)
_, binary = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 形态学闭运算填补细小空洞
kernel = np.ones((5, 5), np.uint8)
binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
# 提取轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(contours) == 0:
return "unknown", 0, 0.0
# 取最大轮廓(假设图像中只有一个目标零件)
contour = max(contours, key=cv2.contourArea)
# 计算形状特征
area = cv2.contourArea(contour)
perimeter = cv2.arcLength(contour, True)
circularity = 4 * np.pi * area / (perimeter * perimeter) if perimeter > 0 else 0
# 多边形逼近获取顶点数
epsilon = 0.02 * perimeter
approx = cv2.approxPolyDP(contour, epsilon, True)
vertices = len(approx)
# 分类判定
if circularity > 0.85:
label = "circle"
elif vertices == 3:
label = "triangle"
elif vertices == 4:
label = "square"
else:
label = "unknown"
return label, vertices, round(circularity, 3)
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print("用法: python classify_shape.py <图像路径>")
sys.exit(1)
label, vertices, circ = classify_shape(sys.argv[1])
print(f"分类结果: {label}")
print(f"顶点数: {vertices}, 圆度: {circ}")
运行方式
python classify_shape.py test_circle.png
python classify_shape.py test_square.png
python classify_shape.py test_triangle.png
验证方法
准备三张测试图像,每张包含一个黑色背景上的白色零件(或白色背景上的深色零件,代码已做反色处理):
- 圆形:绘制实心圆,预期输出
circle,圆度接近1.0 - 方形:绘制实心矩形,预期输出
square,顶点数为4 - 三角形:绘制实心三角形,预期输出
triangle,顶点数为3
可通过以下Python脚本生成测试图像:
import cv2
import numpy as np
# 生成测试图像
canvas = np.zeros((300, 300), dtype=np.uint8)
cv2.circle(canvas, (150, 150), 80, 255, -1)
cv2.imwrite("test_circle.png", canvas)
canvas = np.zeros((300, 300), dtype=np.uint8)
cv2.rectangle(canvas, (80, 80), (220, 220), 255, -1)
cv2.imwrite("test_square.png", canvas)
canvas = np.zeros((300, 300), dtype=np.uint8)
pts = np.array([[150, 50], [50, 230], [250, 230]], np.int32)
cv2.fillPoly(canvas, [pts], 255)
cv2.imwrite("test_triangle.png", canvas)
通过标准:三张测试图像均输出正确类别标签,且终端打印的顶点数与圆度值符合预期(圆形圆度>0.85,方形顶点数=4,三角形顶点数=3)。
常见问题
- 图像中零件与背景对比度不足:OTSU阈值可能失效,可改为固定阈值或先进行直方图均衡化。
- 轮廓不闭合导致顶点数误判:增大形态学闭运算的核尺寸,或调整多边形逼近的epsilon系数(当前为周长的2%)。
- 多个零件同时出现:当前实现取最大轮廓,如需多目标分类需遍历所有轮廓并分别判定。