如何用经典计算机视觉实现旋转缩放不变的限位开关检测?
限位开关检测:解决旋转/尺度变化下的检测问题
我希望通过单目相机或立体视觉装置检测图像中的限位开关,判断其是否存在并获取其像素位置。曾尝试使用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
相关产品推荐
相关产品推荐

