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

基于OpenCV的相机标定及像素反向投影至方位/俯仰角技术咨询

我之前做过视觉引导波束形成的项目,刚好熟悉这套流程——从摄像头内参标定到像素点反投影为DOA的全流程,给你梳理清楚,代码也可以直接复用:

用OpenCV实现摄像头内参标定+像素点反投影为DOA(方位角/仰角)

一、第一步:棋盘格法标定摄像头内参

内参的准确性是后续DOA估算的核心基础,必须先做好这一步:

  • 准备棋盘格:打印一张内角点为8x6的棋盘格(不是棋盘格方块数量,是交叉点数量),贴在完全平整的平面上
  • 采集标定图像:用目标webcam从不同角度、距离拍摄至少10张图像,确保棋盘格出现在画面的中心、边缘等不同位置,尽量覆盖整个视野
  • 用OpenCV完成标定,得到内参矩阵mtx和畸变系数dist

标定代码示例

import cv2
import numpy as np
import glob

# 棋盘格内角点尺寸
chessboard_size = (8, 6)
# 定义世界坐标系下的棋盘格点(假设每个格子边长20mm)
objp = np.zeros((chessboard_size[0]*chessboard_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2) * 20

objpoints = []  # 存储世界坐标点
imgpoints = []  # 存储图像坐标点

# 读取所有标定图像(假设存在images文件夹下)
images = glob.glob('images/*.jpg')
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)

for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 查找棋盘格内角点
    ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)
    if ret:
        objpoints.append(objp)
        # 亚像素级优化,提升标定精度
        refined_corners = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)
        imgpoints.append(refined_corners)
        # 可视化标定结果(可选)
        cv2.drawChessboardCorners(img, chessboard_size, refined_corners, ret)
        cv2.imshow('Calibration', img)
        cv2.waitKey(500)

cv2.destroyAllWindows()

# 执行标定,获取内参和畸变系数
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

# 保存标定结果,后续直接加载使用
np.save('camera_intrinsic.npy', mtx)
np.save('camera_distortion.npy', dist)

二、第二步:用户选点反投影为方位角/仰角

核心逻辑是:先校正像素点的畸变,再转换为相机坐标系下的射线方向,最后计算相对摄像头中心的方位角和仰角。

反投影原理

  1. 畸变校正:用cv2.undistortPoints()消除镜头畸变对像素坐标的影响,得到归一化图像坐标
  2. 射线方向转换:相机坐标系中,光心在原点,x轴向右、y轴向下、z轴向前,归一化坐标(u,v)对应的射线方向为(u, v, 1)
  3. 角度计算:
    • 方位角:绕y轴的旋转角,公式为atan2(x, z),范围转换为0~360°
    • 仰角:绕x轴的旋转角,公式为atan2(-y, sqrt(x²+z²)),范围为-90°~90°

选点+反投影代码

import cv2
import numpy as np

# 加载之前保存的内参和畸变系数
mtx = np.load('camera_intrinsic.npy')
dist = np.load('camera_distortion.npy')

# 全局变量存储用户选中的像素点
selected_point = None

# 鼠标回调函数:捕捉用户点击的像素坐标
def mouse_callback(event, x, y, flags, param):
    global selected_point
    if event == cv2.EVENT_LBUTTONDOWN:
        selected_point = (x, y)
        print(f"选中像素点: ({x}, {y})")

# 启动摄像头
cap = cv2.VideoCapture(0)
cv2.namedWindow('Webcam DOA Estimator')
cv2.setMouseCallback('Webcam DOA Estimator', mouse_callback)

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    # 若有选中点,计算并显示DOA
    if selected_point is not None:
        x, y = selected_point
        # 对像素点做畸变校正,得到归一化坐标
        pixel = np.array([[x, y]], dtype=np.float32)
        undistorted_pixel = cv2.undistortPoints(pixel, mtx, dist, None, mtx)
        u, v = undistorted_pixel[0][0]
        
        # 生成相机坐标系下的方向向量并归一化
        dir_vec = np.array([u, v, 1.0])
        dir_vec /= np.linalg.norm(dir_vec)
        
        # 计算方位角(转换为0-360°)
        azimuth_rad = np.arctan2(dir_vec[0], dir_vec[2])
        azimuth_deg = np.degrees(azimuth_rad)
        azimuth_deg = azimuth_deg if azimuth_deg >=0 else azimuth_deg + 360
        
        # 计算仰角(向上为正,向下为负)
        elevation_rad = np.arctan2(-dir_vec[1], np.sqrt(dir_vec[0]**2 + dir_vec[2]**2))
        elevation_deg = np.degrees(elevation_rad)
        
        # 在画面上标注结果和选点
        text = f"Azimuth: {azimuth_deg:.1f}° | Elevation: {elevation_deg:.1f}°"
        cv2.putText(frame, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,255,0), 2)
        cv2.circle(frame, (x,y), 5, (0,0,255), -1)
    
    cv2.imshow('Webcam DOA Estimator', frame)
    # 按键控制:q退出,c清除选中点
    key = cv2.waitKey(1) & 0xFF
    if key == ord('q'):
        break
    elif key == ord('c'):
        selected_point = None

cap.release()
cv2.destroyAllWindows()

三、关键注意事项

  • 标定精度:尽量多拍不同角度的棋盘格图像,避免只拍中心区域;确保棋盘格完全平整,否则会引入系统误差
  • 畸变校正:广角摄像头必须做这一步,否则反投影的角度误差会非常大
  • 坐标系匹配:如果你的波束形成算法使用的坐标系和OpenCV默认(x右/y下/z前)不同,需要额外做坐标转换调整角度计算逻辑
  • 复用性:标定一次后保存内参,后续无需重复标定,直接加载即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:15:31