关于相机坐标系下归一化坐标的一个等式为何成立

本文解释了计算机视觉中归一化坐标的概念及其与像素坐标的关系。通过数学推导,阐述了两个特征点归一化坐标间的关系,并说明了归一化坐标在视觉SLAM中的作用。

2022-10-12 更新

“归一化” 这个操作要么在得到相机坐标系坐标后就执行
抑或是经过内参矩阵后执行
总之,在得到像素坐标前,要执行一次“归一化”操作,使得 x,y,z坐标都除以 z 坐标,这样会使得 z 坐标丢失。

原文

按照对极几何中的定义,设 x1,x2\mathbf{x}_1,\mathbf{x}_2x1,x2 为两个特征点的归一化坐标,那么它们满足
s2x2=s1Rx1+ts_2\mathbf{x}_2=s_1\mathbf{R}\mathbf{x}_1+ts2x2=s1Rx1+t
上述内容来自《视觉SLAM十四讲从理论到实践》第二版第 177 页。
当时看到这里,我就非常地不解,为什么这个等式成立呢?
为什么会引入两个变量而不是一个变量呢?
现在,我能回答这个问题了,先大概讲一下。然后再对比一下“归一化坐标”和“像素坐标”的区别,因为它们很像!之后应该对这个问题就很清晰了

式子为什么成立

假设某个三维点在某个相机坐标系下的坐标是
[x,y,z][x,y,z][x,

<think>我们使用OpenCV进行相机标定后,会得到相机的内参矩阵(camera matrix)、畸变系数(distortion coefficients)、旋转向量(rotation vector)和平移向量(translation vector)等外参。 将像素坐标转换为世界坐标需要以下步骤: 1. 首先,我们需要将像素坐标去畸变(undistort)。 2. 然后,通过内参矩阵的逆矩阵将像素坐标转换到相机坐标系下的归一化平面坐标(即归一化坐标,Z=1)。 3. 接着,利用外参(旋转矩阵和平移向量)将归一化坐标转换到世界坐标系。注意:这里需要知道该点在世界坐标系中的深度(Z值)或者通过其他方式获得深度信息。如果已知世界坐标系中该点所在的平面(例如,地面平面),则可以通过解方程得到。 但是,通常我们无法仅凭单目相机一个像素点获得其深度。因此,这里假设我们已知世界坐标系中该点位于某个平面(例如Z=0的平面),然后通过单应性矩阵或解线性方程来求解。 另一种情况是,如果我们有相机的位姿(即旋转矩阵R和平移向量t),那么对于世界坐标系中的一个点P_w,有: P_c = R * P_w + t 其中,P_c是点在相机坐标系下的坐标。而像素坐标u, v与P_c的关系为: s * [u, v, 1]^T = K * P_c 其中K是内参矩阵,s是尺度因子。 反过来,如果我们有一个像素点(u, v),并假设该点对应的世界坐标点位于某个平面(例如,地面平面,其法向量为n,距离为d),那么我们可以通过求解线性方程组得到该点的世界坐标。 以下是一个简化的例子:假设世界坐标系中Z=0(即地面平面),则我们可以将世界坐标表示为P_w = [X, Y, 0]^T。那么: P_c = [x, y, z]^T = R * [X, Y, 0]^T + t = [r1, r2, r3] * [X; Y; 0] + t = [r1, r2] * [X; Y] + t 其中r1, r2, r3是R的列向量。 而像素坐标关系为: s * [u, v, 1]^T = K * P_c = K * ( [r1, r2] * [X; Y] + t ) 令H = K * [r1, r2, t](这是一个3x3矩阵),则上式可以写成: s * [u, v, 1]^T = H * [X, Y, 1]^T 因此,我们可以通过单应性矩阵H将世界平面上的点[X,Y]映射到像素点[u,v]。反过来,如果已知H的逆,则可以将像素点映射回世界平面上的点。 但是,在实际情况中,我们可能没有直接构造这个单应性矩阵,而是通过解方程的方式。 下面给出一个C++代码示例,假设我们已知: - 相机内参矩阵K - 畸变系数distCoeffs(这里为了简化,我们假设已经去畸变,或者畸变很小可以忽略) - 相机的外参:旋转向量rvec(可以用Rodrigues转换为旋转矩阵R)和平移向量tvec - 并且我们假设世界坐标系中的点位于Z=0的平面上。 步骤: 1. 将像素坐标去畸变(如果考虑畸变)。 2. 将像素点(u, v)转换为归一化相机坐标系中的点(x, y, 1)。 3. 构建旋转矩阵R,并提取前两列r1, r2。 4. 构建方程组,求解世界坐标X, Y。 具体公式推导: 归一化相机坐标点P_c_normalized = [x, y, 1]^T = K_inv * [u, v, 1]^T (注意:这里需要归一化,即除以深度,但我们假设在归一化平面上,所以深度为1) 然后,根据相机坐标系与世界坐标系的关系(世界坐标系中Z=0): P_c = R * [X, Y, 0]^T + t = r1*X + r2*Y + t 同时,P_c 和 P_c_normalized 的关系是:P_c = s * P_c_normalized (s为尺度因子,即深度) 所以: s * x = r1[0]*X + r2[0]*Y + t[0] s * y = r1[1]*X + r2[1]*Y + t[1] s = r1[2]*X + r2[2]*Y + t[2] 将第三个等式代入前两个,消去s: x * (r1[2]*X + r2[2]*Y + t[2]) = r1[0]*X + r2[0]*Y + t[0] y * (r1[2]*X + r2[2]*Y + t[2]) = r1[1]*X + r2[1]*Y + t[1] 整理成线性方程组: (x * r1[2] - r1[0]) * X + (x * r2[2] - r2[0]) * Y = t[0] - x * t[2] (y * r1[2] - r1[1]) * X + (y * r2[2] - r2[1]) * Y = t[1] - y * t[2] 写成矩阵形式:A * [X, Y]^T = B 其中: A = [ (x * r1[2] - r1[0]) , (x * r2[2] - r2[0]) ] [ (y * r1[2] - r1[1]) , (y * r2[2] - r2[1]) ] B = [ t[0] - x * t[2] ] [ t[1] - y * t[2] ] 然后使用solve求解线性方程组。 注意:如果点不在Z=0平面,而是其他平面,需要根据平面方程调整。 代码示例(不考虑畸变):</think>在OpenCV中,根据相机内参和外参将像素坐标转换为世界坐标需要以下步骤,核心是通过透视投影方程反解空间点位置。以下是C++代码示例: ```cpp #include <opencv2/opencv.hpp> cv::Point3f pixelToWorld(const cv::Point2f& pixelPoint, const cv::Mat& cameraMatrix, const cv::Mat& rvec, const cv::Mat& tvec) { // 1. 创建齐次像素坐标 cv::Mat pixelHomogeneous = (cv::Mat_<double>(3,1) << pixelPoint.x, pixelPoint.y, 1.0); // 2. 计算旋转矩阵 cv::Mat R; cv::Rodrigues(rvec, R); // 旋转向量转旋转矩阵 // 3. 计算投影矩阵的逆 cv::Mat invCamera; cv::invert(cameraMatrix, invCamera, cv::DECOMP_LU); // 4. 计算归一化相机坐标 cv::Mat normCameraCoord = invCamera * pixelHomogeneous; // 5. 计算射线方向向量(相机坐标系) cv::Mat rayDirection = R.t() * normCameraCoord; // 6. 计算射线起点(相机位置,世界坐标系) cv::Mat cameraPosition = -R.t() * tvec; // 7. 假设目标点位于Z=0平面(根据实际平面调整) double s = -cameraPosition.at<double>(2) / rayDirection.at<double>(2); // 8. 计算世界坐标 cv::Mat worldCoord = cameraPosition + s * rayDirection; return cv::Point3f( worldCoord.at<double>(0), worldCoord.at<double>(1), worldCoord.at<double>(2) ); } // 使用示例 int main() { // 相机内参矩阵 [fx, 0, cx; 0, fy, cy; 0, 0, 1] cv::Mat cameraMatrix = (cv::Mat_<double>(3,3) << 1200, 0, 640, 0, 1200, 360, 0, 0, 1); // 旋转向量 (Rodrigues表示) cv::Mat rvec = (cv::Mat_<double>(3,1) << 0.1, -0.2, 0.3); // 平移向量 (单位:米) cv::Mat tvec = (cv::Mat_<double>(3,1) << 0.5, -1.0, 2.0); // 像素坐标点 (图像坐标系) cv::Point2f pixel(320.0, 240.0); // 转换为世界坐标 cv::Point3f worldPoint = pixelToWorld(pixel, cameraMatrix, rvec, tvec); std::cout << "World Coordinates: (" << worldPoint.x << ", " << worldPoint.y << ", " << worldPoint.z << ")" << std::endl; return 0; } ``` ### 关键步骤说明: 1. **齐次坐标转换**:将2D像素点转换为齐次坐标 `[u, v, 1]^T` 2. **内参逆变换**:使用内参矩阵的逆将像素坐标转换到归一化相机坐标系 3. **外参处理**: - 用Rodrigues公式将旋转向量转换为旋转矩阵 - 计算相机在世界坐标系中的位置:`C = -R^T * t` 4. **深度求解**:通过假设目标平面(示例中Z=0)计算深度比例因子 5. **坐标计算**:沿射线方向计算世界坐标:`P = C + s * (R^T * K^{-1} * p)` ### 注意事项: - **深度假设**:示例假设目标点位于Z=0平面,实际需根据场景调整平面约束 - **畸变校正**:实际应用前需对像素点进行去畸变处理 - **多解问题**:单目相机存在深度歧义,通常需要平面约束或深度传感器 - **坐标系**:OpenCV使用右手坐标系(X右,Y下,Z前)
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

培之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值