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%)。
  • 多个零件同时出现:当前实现取最大轮廓,如需多目标分类需遍历所有轮廓并分别判定。