非常详细的相机标定(六)(2维坐标点转为3维坐标点)

本文介绍了使用OpenCV进行相机标定和2维坐标点转换为3维坐标的Python函数,包括角点检测、亚像素校准和3D空间重建的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

根据提取的相机的参数,2维坐标点转为3维坐标点,代码如下:

import argparse
from argparse import RawTextHelpFormatter
import numpy as np
import cv2

# 寻找焦点
def cam_calib_find_corners(img, rlt_dir, col, row):
    # 灰度化图片,减少参数的运算
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 寻找角点
    ret, corners = cv2.findChessboardCorners(gray, (col, row), None)

    # 为了得到稍微精确一点的角点坐标,进一步对角点进行亚像素寻找
    corners2 = cv2.cornerSubPix(
        gray, corners, (7, 7), (-1, -1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 10, 0.001))
    if ret == True:
        # 保存角点图像
        sav_path = rlt_dir + "/1_corner.jpg"
        # 绘制角点
        cv2.drawChessboardCorners(img, (col, row), corners2, ret)
        cv2.imwrite(sav_path, img)

    return (ret, corners2)

# 相机标定
def cam_calib_calibrate(img_dir, rlt_dir, col, row, img_num):
    w = 0
    h = 0
    all_corners = []
    patterns = []

    x, y = np.meshgrid(range(col), range(row))
    prod = row * col
    pattern_points = np.hstack(
        (x.reshape(prod, 1), y.reshape(prod, 1), np.zeros((prod, 1)))).astype(np.float32)

    img_path = r"C:\Users\17930\Desktop\1.jpg"
    img = cv2.imread(img_path)
    if img is None:
        print(f"Error reading image at {img_path}")
        pass
    else:
        (h, w) = img.shape[:2]
        ret, corners = cam_calib_find_corners(img, rlt_dir, col, row)
        all_corners.append(corners)
        patterns.append(pattern_points)

    rms, cameraMatrix, distCoeffs, rvecs, tvecs = cv2.calibrateCamera(
        patterns, all_corners, (w, h), None, None)
    print('相机内参', cameraMatrix)
    print('相机外参旋转向量', rvecs)
    print('相机外参平移向量', tvecs)


    return cameraMatrix, distCoeffs, rvecs, tvecs


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="读取标定的图片并保存结果", formatter_class=RawTextHelpFormatter)
    parser.add_argument("--img_dir", help="标定图片路径", type=str,
                        metavar='', default=r'D:\螺丝数据集与相机标定代码\camera\calib_img')
    parser.add_argument("--rlt_dir", help="保存路径", type=str,
                        metavar='', default="rlt_dir")
    parser.add_argument("--crct_img_dir", help="待矫正图像路径",
                        type=str, metavar='', default="crct_img")
    parser.add_argument("--row_num", help="每一行有多少个角点,边缘处的不算",
                        type=int, metavar='', default="7")
    parser.add_argument("--col_num", help="每一列有多少个角点,边缘处的不算",
                        type=int, metavar='', default="6")
    parser.add_argument("--img_num", help="多少幅图像",
                        type=int, metavar='', default="4")

    args = parser.parse_args()
    cameraMatrix, distCoeffs, rvecs, tvecs = cam_calib_calibrate(
        args.img_dir, args.rlt_dir, args.row_num, args.col_num, args.img_num)

    image_points = np.array([[55, 66], [77, 88]], dtype=np.float32)
    world_z = 0
    world_points = []

    for i in range(len(rvecs)):
        rotation_matrix, _ = cv2.Rodrigues(rvecs[i])
        projection_matrix = np.dot(cameraMatrix, np.hstack(
            (rotation_matrix, tvecs[i])))

        inv_projection_matrix = np.linalg.pinv(projection_matrix)

        for pt in image_points:
            img_pt = np.array([pt[0], pt[1], 1])
            ray_dir = np.dot(inv_projection_matrix, img_pt)
            scale = (world_z - tvecs[i][2]) / ray_dir[2]
            world_pt = tvecs[i].reshape(3) + scale * ray_dir[:3]
            world_points.append(world_pt)

    world_points = np.array(world_points)
    print(world_points)


 

### 将三维空间坐标转换为二维图像坐标的投影矩阵 将三维空间中的坐标映射到二维图像平面的过程通常称为 **透视投影** 或 **相机投影**。这一过程可以通过一个 $4 \times 4$ 的齐次变换矩阵完成,该矩阵被称为 **投影矩阵 (Projection Matrix)**。 #### 投影矩阵的定义 投影矩阵可以分为两个主要部分:内在参数矩阵 ($K$) 和外在参数矩阵 ($[R|t]$)[^1]。 - 内在参数矩阵描述了摄像机内部属性,例如焦距、光学中心位置以及像素比例因子。 - 外在参数矩阵描述了摄像机相对于世界坐标系的姿态(旋转和平移)。 完整的投影方程如下所示: $$ \begin{bmatrix} u \\ v \\ w \end{bmatrix} = K \begin{bmatrix} R & t \end{bmatrix} \begin{bmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{bmatrix}, $$ 其中: - $(X_w, Y_w, Z_w)$ 是世界坐标系下的三维点, - $(u, v)$ 是对应的图像平面上的二维点, - $w$ 是缩放因子用于归一化齐次坐标[^2]。 #### 内在参数矩阵 ($K$) 内在参数矩阵的形式通常是这样的: ```plaintext K = [ [f_x s c_x], [ 0 f_y c_y], [ 0 0 1] ] ``` - $f_x$, $f_y$: 表示水平和垂直方向上的焦距; - $c_x$, $c_y$: 图像传感器的光心位置; - $s$: 斜向畸变系数,在大多数现代摄像头中可忽略不计。 #### 外在参数矩阵 ($[R|t]$) 外部参数由两部分组成: - $R$: 描述摄像机姿态的旋转矩阵(3x3),它表示从世界坐标系到摄像机坐标系的旋转变换; - $t$: 平移向量(3x1),表示摄像机原点与世界坐标系之间的偏移。 最终的投影关系可以用以下形式表达: $$ \lambda \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = K [R | t] \begin{bmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{bmatrix}. $$ 这里 $\lambda$ 是一个尺度因子,用来处理齐次坐标系统的规范化操作。 --- 以下是 Python 中实现此投影的一个简单例子: ```python import numpy as np def project_3d_to_2d(K, R, t, points_3d): """ Project a set of 3D world points into their corresponding 2D image coordinates. Parameters: K (np.ndarray): Intrinsic camera parameters matrix (3x3). R (np.ndarray): Rotation matrix (3x3). t (np.ndarray): Translation vector (3x1). points_3d (np.ndarray): Array of 3D points in homogeneous form (N x 3). Returns: np.ndarray: Array of projected 2D points. """ # Convert 3D points to homogeneous coordinates ones = np.ones((points_3d.shape[0], 1)) points_homogeneous = np.hstack([points_3d, ones]) # Apply extrinsic transformation [R|t] transformed_points = np.dot(points_homogeneous, np.vstack([np.hstack([R.T, -R.T @ t]), [[0, 0, 0, 1]]]).T) # Normalize by dividing through z-coordinate normalized_points = transformed_points[:, :3] / transformed_points[:, 2].reshape(-1, 1) # Apply intrinsic parameter matrix K projected_points = np.dot(normalized_points, K.T) return projected_points[:, :2] # Example usage if __name__ == "__main__": # Define example matrices fx, fy, cx, cy = 500, 500, 320, 240 K = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]]) theta = np.radians(30) R = np.array([ [np.cos(theta), -np.sin(theta), 0], [np.sin(theta), np.cos(theta), 0], [0, 0, 1] ]) t = np.array([[0], [0], [-2]]) points_3d = np.array([[1, 1, 1], [2, 2, 2]]) # Two sample 3D points result = project_3d_to_2d(K, R, t, points_3d) print(result) ``` 上述代码实现了基于给定内外参矩阵的 3D 到 2D 坐标转换功能。 --- #### 注意事项 - 如果存在镜头畸变,则需要额外校正这些影响。常见的模型包括径向畸变项和切向畸变项。 - 实际应用中可能还需要考虑深度信息丢失的情况,即当某些点位于摄像机视锥之外时无法成功投影。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值