2. 形状匹配的圆形零件定位

任务目标

本案例实现基于形状匹配的圆形零件定位:输入一张包含圆形零件的灰度图像,算法输出每个检测到圆形的中心坐标(x, y)与半径r,为机器人抓取提供目标位置。采用OpenCV的HoughCircles作为形状匹配核心,配合参数自适应策略应对不同光照与零件尺寸。

环境依赖

Python 3.8+,OpenCV 4.5+(含opencv-python与opencv-python-headless二选一),NumPy。无需额外数据集,使用本地单张图像即可验证。

完整实现代码

import cv2
import numpy as np
import sys

def detect_circles(image_path, min_radius=20, max_radius=200, param2=30):
    """
    基于Hough变换的圆形零件定位
    返回: [(cx, cy, r), ...],按置信度降序
    """
    # 读取图像并转为灰度
    img = cv2.imread(image_path)
    if img is None:
        raise FileNotFoundError(f"无法读取图像: {image_path}")
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 预处理:高斯模糊降噪,保留边缘清晰度
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)

    # 自适应Canny阈值(基于灰度中值)
    median_val = np.median(blurred)
    low_thresh = int(max(0, 0.66 * median_val))
    high_thresh = int(min(255, 1.33 * median_val))
    edges = cv2.Canny(blurred, low_thresh, high_thresh)

    # Hough圆检测(形状匹配核心)
    circles = cv2.HoughCircles(
        edges,
        cv2.HOUGH_GRADIENT,
        dp=1.2,                # 累加器分辨率与图像分辨率的反比
        minDist=min_radius*2,  # 圆心最小间距,防止重复检测
        param1=high_thresh,    # Canny高阈值
        param2=param2,         # 圆心累加器阈值,越小检测越多(含误检)
        minRadius=min_radius,
        maxRadius=max_radius
    )

    results = []
    if circles is not None:
        # 将结果转为整数坐标并排序(按累加器强度,即第3维)
        circles = np.round(circles[0]).astype(int)
        # 按置信度(第3列)降序排列
        circles = circles[np.argsort(-circles[:, 2])]
        for x, y, r in circles:
            results.append((int(x), int(y), int(r)))

    return results

def visualize_and_save(image_path, circles, output_path="annotated.jpg"):
    """在原图上绘制检测结果并保存"""
    img = cv2.imread(image_path)
    for (x, y, r) in circles:
        cv2.circle(img, (x, y), r, (0, 255, 0), 2)      # 外圆
        cv2.circle(img, (x, y), 2, (0, 0, 255), 3)      # 圆心
        cv2.putText(img, f"({x},{y}) r={r}", (x-30, y-r-10),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
    cv2.imwrite(output_path, img)
    print(f"标注图像已保存: {output_path}")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("用法: python circle_detect.py <图像路径> [min_radius] [max_radius] [param2]")
        sys.exit(1)

    img_path = sys.argv[1]
    # 可选参数:最小半径、最大半径、累加器阈值
    min_r = int(sys.argv[2]) if len(sys.argv) > 2 else 20
    max_r = int(sys.argv[3]) if len(sys.argv) > 3 else 200
    p2 = int(sys.argv[4]) if len(sys.argv) > 4 else 30

    detections = detect_circles(img_path, min_r, max_r, p2)

    if not detections:
        print("未检测到圆形零件")
    else:
        print(f"检测到 {len(detections)} 个圆形零件:")
        for i, (cx, cy, r) in enumerate(detections):
            print(f"  零件{i+1}: 中心=({cx}, {cy}), 半径={r}px")
        visualize_and_save(img_path, detections)

运行与验证

运行方式:

python circle_detect.py parts.jpg 20 200 30

其中parts.jpg为输入图像,后三个参数依次为最小半径、最大半径、Hough累加器阈值(默认值已给出,可按需调整)。

验证方法:

  1. 程序输出每个检测圆的中心坐标与半径,并在原图上绘制绿色外圆与红色圆心,保存为annotated.jpg。
  2. 打开标注图像,目视检查绿色圆是否准确贴合零件边缘,圆心是否位于零件几何中心。
  3. 判定通过标准:图像中每个可见圆形零件均被检出,且无明显的非圆形误检(如方形边缘被误判为圆);输出坐标与图像中零件实际位置偏差不超过3像素。

参数调优建议:若漏检,降低param2(如从30降至20);若误检增多,提高param2。若零件半径超出默认范围,需通过命令行传入正确的min_radius与max_radius。

输入图像 检测输出 (450,140) r=60px HoughCircles 输出圆心坐标与半径,供机器人抓取定位 方形干扰物不会被误检为圆(param2=30时)

常见问题

  • 检测不到圆:优先检查零件半径是否在min_radius与max_radius范围内;其次降低param2至15~20重试。
  • 误检严重:提高param2至40~50,或增大minDist(当前为min_radius*2)以抑制邻近重复检测。
  • 边缘断裂导致漏检:可适当降低Canny低阈值(代码中low_thresh),或改用cv2.medianBlur替代高斯模糊以保留更锐利边缘。