You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用经典计算机视觉实现旋转缩放不变的限位开关检测?

限位开关检测:解决旋转/尺度变化下的检测问题

我希望通过单目相机或立体视觉装置检测图像中的限位开关,判断其是否存在并获取其像素位置。曾尝试使用OpenCV中的模板匹配算法,但开关发生轻微旋转时检测效果极差。作为计算机视觉新手,想了解可靠且易于实现的检测方法,以及具备尺度与旋转不变性的技术,实现开关在远近、旋转状态下的有效检测。

尝试过的模板匹配代码

import cv2
import numpy as np

img = cv2.imread("/home/Documents/cpp/CV/test/POC_test1.bmp")
template = cv2.imread("/home/Documents/cpp/CV/test/template.jpg")

print(img.shape)
print(template.shape)

(h, w, _) = template.shape

methods = [cv2.TM_CCOEFF, cv2.TM_CCOEFF_NORMED, cv2.TM_CCORR,
            cv2.TM_CCORR_NORMED, cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]

for method in methods:
    img2 = img.copy()

    result = cv2.matchTemplate(img2, template, method)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
        location = min_loc
    else:
        location = max_loc

    bottom_right = (location[0] + w, location[1] + h)    
    cv2.rectangle(img2, location, bottom_right, 255, 5)
    img2 = cv2.resize(img2, (int(3840/3), int(2748/3)))
    cv2.imshow('Match', img2)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

相关测试图像

  • 原始场景图:
    原始场景图
  • 模板匹配(使用原图裁剪模板):
    模板匹配效果
  • 旋转后检测效果:
    旋转后检测效果

适合新手的尺度/旋转不变检测方法

1. ORB特征匹配(OpenCV内置,免费易实现)

ORB是具备尺度、旋转不变性的开源特征检测器,无需额外付费,适配快速部署场景。核心逻辑是提取模板与待测图像的ORB特征点,通过匹配特征点计算目标位置。

实现步骤:

  • 初始化ORB检测器,提取模板和待测图像的关键点与描述子
  • 用暴力匹配器筛选优质匹配对
  • 通过匹配特征点计算单应性矩阵,推导目标边界框

代码示例:

import cv2
import numpy as np

# 读取图像并转灰度
img = cv2.imread("/home/Documents/cpp/CV/test/POC_test1.bmp")
template = cv2.imread("/home/Documents/cpp/CV/test/template.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)

# 初始化ORB检测器
orb = cv2.ORB_create(nfeatures=500)
kp1, des1 = orb.detectAndCompute(template_gray, None)
kp2, des2 = orb.detectAndCompute(img_gray, None)

# 暴力匹配并筛选优质结果
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = bf.match(des1, des2)
matches = sorted(matches, key=lambda x: x.distance)[:30]

# 提取匹配点坐标并计算单应性矩阵
src_pts = np.float32([kp1[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)

# 绘制目标边界框
h, w = template_gray.shape
pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2)
dst = cv2.perspectiveTransform(pts, M)
img_result = cv2.polylines(img, [np.int32(dst)], True, (0, 255, 0), 3, cv2.LINE_AA)

# 显示结果
img_result = cv2.resize(img_result, (int(3840/3), int(2748/3)))
cv2.imshow("ORB Detection", img_result)
cv2.waitKey(0)
cv2.destroyAllWindows()

2. 基于轮廓的形状检测(适合形状特征稳定目标)

若限位开关的形状(如矩形+凸起结构)特征稳定,可通过轮廓匹配实现旋转/尺度不变检测,无需训练模型。

实现步骤:

  • 对图像做灰度化、自适应二值化处理
  • 提取轮廓并计算Hu矩(具备旋转、平移、尺度不变性)
  • 对比模板与待测轮廓的Hu矩相似度,筛选匹配目标

代码示例:

import cv2
import numpy as np

# 处理模板:提取轮廓并计算Hu矩
template = cv2.imread("/home/Documents/cpp/CV/test/template.jpg")
template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
_, template_bin = cv2.threshold(template_gray, 127, 255, cv2.THRESH_BINARY_INV)
template_contours, _ = cv2.findContours(template_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
template_cnt = max(template_contours, key=cv2.contourArea)
template_hu = cv2.HuMoments(cv2.moments(template_cnt)).flatten()

# 处理待测图像
img = cv2.imread("/home/Documents/cpp/CV/test/POC_test1.bmp")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_bin = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
img_contours, _ = cv2.findContours(img_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 遍历轮廓匹配
for cnt in img_contours:
    if cv2.contourArea(cnt) < 1000:
        continue
    hu = cv2.HuMoments(cv2.moments(cnt)).flatten()
    # 对数变换后计算差值,越小越相似
    diff = np.sum(np.abs(np.log10(np.abs(template_hu)) - np.log10(np.abs(hu))))
    if diff < 0.5:
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 3)

# 显示结果
img_result = cv2.resize(img, (int(3840/3), int(2748/3)))
cv2.imshow("Contour Detection", img_result)
cv2.waitKey(0)
cv2.destroyAllWindows()

3. 轻量深度学习模型(YOLOv8n,鲁棒性强)

若目标特征复杂,深度学习方法鲁棒性最优。YOLOv8n是轻量级模型,仅需20-50张标注图像即可完成训练,支持旋转、尺度变化的检测。

实现步骤:

  • 用LabelImg工具标注不同角度、距离的限位开关图像
  • 参考YOLO官方教程训练自定义数据集
  • 加载训练好的模型完成检测

部署代码示例(OpenCV加载ONNX模型):

import cv2
import numpy as np

# 加载训练好的ONNX模型
net = cv2.dnn.readNetFromONNX("yolov8n_switch.onnx")
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)

# 读取图像并预处理
img = cv2.imread("/home/Documents/cpp/CV/test/POC_test1.bmp")
h, w = img.shape[:2]
blob = cv2.dnn.blobFromImage(img, 1/255.0, (640, 640), swapRB=True, crop=False)
net.setInput(blob)
outputs = net.forward()

# 解析检测结果
for output in outputs[0]:
    scores = output[5:]
    class_id = np.argmax(scores)
    confidence = scores[class_id]
    if confidence > 0.5:
        # 转换坐标到原图尺寸
        x_center = output[0] * w
        y_center = output[1] * h
        box_w = output[2] * w
        box_h = output[3] * h
        x1 = int(x_center - box_w/2)
        y1 = int(y_center - box_h/2)
        x2 = int(x_center + box_w/2)
        y2 = int(y_center + box_h/2)
        # 绘制边界框与置信度
        cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 3)
        cv2.putText(img, f"Switch: {confidence:.2f}", (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2)

# 显示结果
img_result = cv2.resize(img, (int(3840/3), int(2748/3)))
cv2.imshow("YOLO Detection", img_result)
cv2.waitKey(0)
cv2.destroyAllWindows()

内容的提问来源于stack exchange,提问作者CV_enth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 00:05:02