目标理解相机内外参的数学意义知道它们在 3DGS 训练和渲染中的位置能完成基础标定、投影验证、COLMAP 位姿读取和常见问题排查。目录1. 为什么要学相机内外参2. 相机成像模型3. 相机内参 Intrinsics4. 相机外参 Extrinsics5. 坐标系世界、相机、图像、像素6. 从 3D 点投影到 2D 像素7. 畸变参数8. COLMAP 中的相机参数和位姿9. 相机内外参在 3DGS 中的定位10. 3DGS 数据链路图片到可渲染模型11. 实操一OpenCV 标定相机内参12. 实操二用内外参投影 3D 点13. 实操三读取 COLMAP 位姿并转成相机中心14. 实操四检查位姿是否正确15. 常见坐标约定转换16. 3DGS 中常见问题排查17. 面试常问问题18. 练习任务19. 参考资料1. 为什么要学相机内外参3DGS、NeRF、SLAM、三维重建、多视角几何、视觉定位都绕不开相机参数。一句话理解内参描述“相机自己怎么成像”外参描述“相机在世界中的位置和朝向”。在 3DGS 中相机内外参决定了每张训练图片对应的相机位置。3D Gaussian 如何投影到每张图像上。渲染图像和真实图像如何对齐。loss 是否能正确反向优化到高斯点。新视角渲染时相机应该从哪里看场景。如果相机参数错了3DGS 会出现模型散掉。场景重建变形。渲染视角错乱。图像对不齐。浮动伪影多。训练 loss 难以下降。2. 相机成像模型最经典的是针孔相机模型。成像链路世界坐标系 3D 点 - 通过外参变换到相机坐标系 - 透视除法得到归一化平面坐标 - 通过内参变换到像素坐标公式s * [u, v, 1]^T K [R | t] [X, Y, Z, 1]^T其中[X, Y, Z]^T世界坐标系中的 3D 点。[u, v]^T像素坐标。K相机内参矩阵。R旋转矩阵。t平移向量。[R | t]外参矩阵通常表示 world-to-camera。s尺度因子本质上和深度有关。3. 相机内参 Intrinsics内参描述相机内部成像属性。常见内参矩阵K [ fx 0 cx 0 fy cy 0 0 1 ]含义fxx 方向焦距单位是像素。fyy 方向焦距单位是像素。cx主点 x 坐标通常接近图像宽度一半。cy主点 y 坐标通常接近图像高度一半。3.1 焦距和像素焦距物理焦距单位通常是毫米例如 35mm。计算机视觉里的fx、fy单位是像素。如果知道物理焦距和传感器尺寸可以粗略换算fx f_mm / sensor_width_mm * image_width_px fy f_mm / sensor_height_mm * image_height_px实际工程中通常通过标定或 SfM 求得像素焦距。3.2 主点主点是光轴和成像平面的交点。理想情况下cx ≈ image_width / 2 cy ≈ image_height / 2但真实相机可能有偏移。3.3 skew完整内参也可能有 skewK [ fx skew cx 0 fy cy 0 0 1 ]现代相机通常认为 skew 为 0。3.4 内参什么时候会变内参和相机硬件、图像处理方式有关。会改变内参的情况改变图像分辨率。图像裁剪 crop。图像缩放 resize。变焦镜头焦距变化。使用不同相机。去畸变后生成新的相机矩阵。缩放图像时内参也要缩放new_fx fx * scale_x new_fy fy * scale_y new_cx cx * scale_x new_cy cy * scale_y裁剪图像时主点要平移new_cx cx - crop_x new_cy cy - crop_y4. 相机外参 Extrinsics外参描述相机相对于世界坐标系的位置和姿态。常见外参[R | t]其中R3x3 旋转矩阵。t3x1 平移向量。在计算机视觉里很多库中的[R | t]表示X_cam R * X_world t也就是 world-to-camera简称 W2C。4.1 相机中心注意外参里的t通常不是相机在世界坐标系中的位置。如果X_cam R * X_world t那么相机中心C在世界坐标系中满足0 R * C t C -R^T * t这是面试和实操里特别容易踩的坑。4.2 camera-to-world如果要从相机坐标转回世界坐标X_world R^T * (X_cam - t)对应矩阵是 C2WT_c2w inverse(T_w2c)如果T_w2c [ R t 0 1 ]那么T_c2w [ R^T -R^T t 0 1 ]5. 坐标系世界、相机、图像、像素5.1 世界坐标系世界坐标系是场景的全局坐标系。在 3DGS 中高斯点的位置、相机位姿、稀疏点云都在同一个世界坐标系里。5.2 相机坐标系相机坐标系以相机光心为原点。OpenCV/COLMAP 常见约定x 向右 y 向下 z 向前这和很多图形学/OpenGL 系统的相机坐标约定不同。5.3 图像坐标系图像坐标系通常以图像左上角为原点u 向右 v 向下5.4 归一化相机平面相机坐标点X_cam [x, y, z]透视除法x_norm x / z y_norm y / z再通过内参u fx * x_norm cx v fy * y_norm cy6. 从 3D 点投影到 2D 像素完整流程X_world - X_cam R * X_world t - x X_cam[0] / X_cam[2] - y X_cam[1] / X_cam[2] - u fx * x cx - v fy * y cy如果考虑畸变则在归一化坐标到像素坐标之间加入畸变模型。6.1 齐次坐标写法P K [R | t] s * p P * X其中P3x4 投影矩阵。X4x1 齐次世界坐标。p3x1 齐次像素坐标。7. 畸变参数真实镜头不是完美针孔模型常见畸变包括径向畸变 radial distortion。切向畸变 tangential distortion。OpenCV 常见畸变参数k1, k2, p1, p2, k37.1 径向畸变径向畸变常见表现桶形畸变边缘向外鼓。枕形畸变边缘向内收。7.2 切向畸变切向畸变通常来自镜头和成像平面不完全平行。7.3 3DGS 中如何处理畸变原始 3DGS 工作流通常依赖 COLMAP 结果并使用 undistorted images。常见做法原始图片 - COLMAP 特征提取和匹配 - 稀疏重建 - image_undistorter 去畸变 - 3DGS 使用去畸变图片和对应相机参数训练如果图片有明显畸变但没有正确处理3DGS 会很难把所有视角对齐。8. COLMAP 中的相机参数和位姿3DGS 常见输入来自 COLMAP。COLMAP 稀疏重建目录通常包括sparse/0/ cameras.bin images.bin points3D.bin或文本格式cameras.txt images.txt points3D.txt8.1 cameras.txt示例# CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[] 1 PINHOLE 1920 1080 1500.0 1500.0 960.0 540.0含义CAMERA_ID相机 ID。MODEL相机模型。WIDTH HEIGHT图像尺寸。PARAMS内参和畸变参数具体顺序取决于模型。常见模型SIMPLE_PINHOLE:f, cx, cyPINHOLE:fx, fy, cx, cySIMPLE_RADIAL:f, cx, cy, kOPENCV:fx, fy, cx, cy, k1, k2, p1, p28.2 images.txt示例# IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, IMAGE_NAME 1 0.99 0.01 0.02 0.03 1.0 2.0 3.0 1 image_001.jpgCOLMAP 中qvec tvec 表示 world-to-camera X_cam R * X_world t相机中心C -R^T * t8.3 points3D.txt包含稀疏点云POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[]3DGS 通常用这些稀疏点作为初始点云。9. 相机内外参在 3DGS 中的定位3DGS 的核心是优化一组 3D Gaussiansposition scale rotation opacity spherical harmonics color相机参数本身通常不是要学习的主体而是训练监督的几何约束。9.1 在训练中训练时每一步通常会选一张训练图像 - 取该图像对应的相机内参和外参 - 把 3D Gaussians 投影到该相机视角 - rasterize 渲染出预测图 - 和真实图像计算 loss - 反向优化高斯参数也就是说相机内外参决定“从哪里看”和“怎么投影”。9.2 在渲染中新视角渲染时需要提供一个新的相机位姿和内参给定 camera pose intrinsics - 3DGS 渲染该视角下的图像如果只想沿训练轨迹渲染则使用训练或测试相机位姿。如果想自由飞行需要自己生成相机轨迹。9.3 在初始化中原始 3DGS 通常用 COLMAP 稀疏点云初始化高斯位置。相机位姿来自 COLMAP SfM多视角图片 - SfM 求相机位姿和稀疏点 - 3DGS 基于这些相机和点云训练如果 COLMAP 位姿不准3DGS 后续优化会非常吃力。9.4 是否优化相机位姿原始 3DGS 常见训练默认固定 COLMAP 相机位姿主要优化 Gaussians。一些扩展工作会联合优化相机位姿例如相机位姿有噪声。手机视频位姿不稳定。少视角重建。动态场景或 SLAM 场景。但联合优化更复杂也更容易陷入局部最优。10. 3DGS 数据链路图片到可渲染模型典型流程采集图片或视频抽帧 - COLMAP 特征提取 - 特征匹配 - 稀疏重建 SfM - 去畸变 - 得到 images、cameras、sparse - 3DGS 读取相机和点云 - 训练高斯场景 - 新视角渲染10.1 输入图片采集建议采集建议围绕目标多角度拍摄。相邻视角有足够重叠。避免大量运动模糊。避免纯白墙、玻璃、镜面等弱纹理区域。曝光尽量稳定。不要频繁变焦。尽量固定焦距和分辨率。10.2 COLMAP 结果质量对 3DGS 的影响COLMAP 好3DGS 通常比较顺。COLMAP 差常见后果稀疏点很少。相机轨迹断裂。部分图片无法注册。场景尺度异常。训练后漂浮物很多。渲染视角错乱。11. 实操一OpenCV 标定相机内参准备一张棋盘格标定板。多张不同角度的标定图片。每张图都能看到完整棋盘格。安装pipinstallopencv-python numpy保存为calibrate_camera.pyfrompathlibimportPathimportcv2ascvimportnumpyasnpdefmain():image_dirPath(calib_images)pattern_size(9,6)square_size0.025objpnp.zeros((pattern_size[0]*pattern_size[1],3),np.float32)objp[:,:2]np.mgrid[0:pattern_size[0],0:pattern_size[1]].T.reshape(-1,2)objp*square_size object_points[]image_points[]image_sizeNoneforimage_pathinsorted(image_dir.glob(*.jpg)):imagecv.imread(str(image_path))ifimageisNone:continuegraycv.cvtColor(image,cv.COLOR_BGR2GRAY)image_sizegray.shape[::-1]ok,cornerscv.findChessboardCorners(gray,pattern_size)ifnotok:print(not found:,image_path)continuecornerscv.cornerSubPix(gray,corners,winSize(11,11),zeroZone(-1,-1),criteria(cv.TERM_CRITERIA_EPScv.TERM_CRITERIA_MAX_ITER,30,0.001),)object_points.append(objp)image_points.append(corners)print(found:,image_path)ifnotobject_points:raiseRuntimeError(No valid chessboard images found.)rms,K,dist,rvecs,tvecscv.calibrateCamera(object_points,image_points,image_size,None,None,)print(RMS reprojection error:,rms)print(K:)print(K)print(distortion:)print(dist.ravel())if__name____main__:main()输出中的K就是内参矩阵dist是畸变参数。11.1 标定结果怎么看重点看重投影误差 RMS 是否合理。fx、fy是否接近。cx、cy是否接近图像中心。畸变参数是否异常大。标定图片角度是否覆盖充分。12. 实操二用内外参投影 3D 点保存为project_points_demo.pyimportnumpyasnpdefproject_points(points_world,K,R,t):points_cam(R points_world.Tt.reshape(3,1)).T zpoints_cam[:,2:3]validz[:,0]1e-6points_normpoints_cam[:,:2]/z fx,fyK[0,0],K[1,1]cx,cyK[0,2],K[1,2]ufx*points_norm[:,0]cx vfy*points_norm[:,1]cy pixelsnp.stack([u,v],axis1)returnpixels,valid,points_camdefmain():Knp.array([[800.0,0.0,640.0],[0.0,800.0,360.0],[0.0,0.0,1.0],])Rnp.eye(3)tnp.array([0.0,0.0,0.0])points_worldnp.array([[0.0,0.0,2.0],[0.5,0.0,2.0],[0.0,0.5,2.0],[0.0,0.0,-1.0],])pixels,valid,points_camproject_points(points_world,K,R,t)forp3d,pc,pix,okinzip(points_world,points_cam,pixels,valid):print(fworld{p3d}, cam{pc}, pixel{pix}, valid{ok})if__name____main__:main()注意z 0的点在相机后方不能正常投影。投影点超出图像范围说明点不在当前视野内。13. 实操三读取 COLMAP 位姿并转成相机中心这里用文本格式images.txt演示。可以先用 COLMAP 把 bin 转成 txt或者使用现成脚本读取 bin。保存为read_colmap_images_txt.pyfrompathlibimportPathimportnumpyasnpdefqvec_to_rotmat(qvec):qw,qx,qy,qzqvecreturnnp.array([[1-2*qy*qy-2*qz*qz,2*qx*qy-2*qz*qw,2*qx*qz2*qy*qw],[2*qx*qy2*qz*qw,1-2*qx*qx-2*qz*qz,2*qy*qz-2*qx*qw],[2*qx*qz-2*qy*qw,2*qy*qz2*qx*qw,1-2*qx*qx-2*qy*qy],])defparse_images_txt(path):linesPath(path).read_text(encodingutf-8).splitlines()images[]i0whileilen(lines):linelines[i].strip()ifnotlineorline.startswith(#):i1continuepartsline.split()image_idint(parts[0])qvecnp.array([float(x)forxinparts[1:5]])tvecnp.array([float(x)forxinparts[5:8]])camera_idint(parts[8])image_nameparts[9]Rqvec_to_rotmat(qvec)C-R.T tvec T_w2cnp.eye(4)T_w2c[:3,:3]R T_w2c[:3,3]tvec T_c2wnp.linalg.inv(T_w2c)images.append({image_id:image_id,camera_id:camera_id,image_name:image_name,R_w2c:R,t_w2c:tvec,camera_center_world:C,T_w2c:T_w2c,T_c2w:T_c2w,})i2returnimagesdefmain():imagesparse_images_txt(sparse/0/images.txt)foriteminimages[:5]:print(item[image_name])print(camera center:,item[camera_center_world])print(T_c2w:)print(item[T_c2w])if__name____main__:main()重点COLMAP 的 qvec/tvec 是 world-to-camera。 相机中心不是 tvec而是 -R^T t。14. 实操四检查位姿是否正确14.1 用稀疏点投影回图片最可靠的检查之一读取 COLMAP points3D - 读取某张图像的 K、R、t - 把 3D 点投影到该图像 - 叠加到原图上 - 看是否落在对应物体位置如果投影整体偏移可能是内参不匹配。resize 后没有同步缩放内参。R/t 方向用反了。坐标系转换错了。图片去畸变和内参不对应。14.2 看相机中心分布对一个环绕拍摄物体的数据集相机中心应该大致围绕目标。如果相机中心散得很离谱有些图片匹配错了。位姿读取错了。qvec 顺序写错了。W2C/C2W 反了。14.3 看相机朝向相机应该大致朝向场景中心。如果所有相机都背对场景通常是坐标轴方向或矩阵求逆错了。15. 常见坐标约定转换15.1 OpenCV/COLMAP 和 OpenGLOpenCV/COLMAP 常见相机坐标x right y down z forwardOpenGL 常见相机坐标x right y up z backward因此经常需要翻转 y、z 轴。一个常见转换矩阵importnumpyasnp opencv_to_openglnp.diag([1,-1,-1,1])具体放左边还是右边取决于你在转换的是坐标点、相机矩阵还是坐标系基向量。不要死背建议用一个简单点验证。15.2 NeRF transforms.json很多 NeRF 数据使用transforms.json里面常见的是 camera-to-world 矩阵。而 COLMAP 输出常见是 world-to-camera。转换T_c2w inverse(T_w2c)15.3 判断矩阵方向的小技巧如果一个矩阵T是 C2WT[:3, 3]通常就是相机中心在世界坐标中的位置。如果一个矩阵T是 W2CT[:3, 3]通常不是相机中心。相机中心要算-R^T t。16. 3DGS 中常见问题排查16.1 训练结果一团糊可能原因COLMAP 位姿错误。图片太少或重叠不足。图像模糊。场景动态物体太多。去畸变没处理好。内参和图片分辨率不匹配。16.2 渲染视角方向不对可能原因W2C 和 C2W 搞反。OpenCV/OpenGL 坐标系没转换。相机 forward 方向取错。矩阵按行主序/列主序理解错。16.3 场景尺度很奇怪SfM 重建的尺度通常是不确定的。单目多视角重建只能恢复相对尺度除非有已知标定板尺寸。深度传感器。IMU/GPS。已知物体尺寸。多传感器约束。16.4 新视角渲染漂浮物多可能原因训练视角覆盖不足。相机位姿不准。背景动态变化。高斯过度生长。纹理弱导致几何约束不足。16.5 COLMAP 注册图片少处理提高图片重叠率。去掉模糊图片。使用更丰富纹理区域。调整特征提取和匹配参数。视频抽帧不要间隔太大。避免重复纹理和大面积反光。17. 面试常问问题17.1 什么是相机内参内参描述相机自身成像属性通常包括fx、fy、cx、cy也可能包括 skew 和畸变参数。17.2 什么是相机外参外参描述相机和世界坐标系之间的刚体变换通常由旋转R和平移t组成。17.3 内参和外参的区别是什么内参描述相机内部成像模型外参描述相机在世界中的位置和朝向。17.4 外参中的t是相机位置吗不一定。若外参表示X_cam R X_world t则t不是相机中心。相机中心是C -R^T t。17.5 world-to-camera 和 camera-to-world 有什么区别world-to-camera 把世界点变到相机坐标系camera-to-world 把相机坐标点变到世界坐标系。两者互为逆矩阵。17.6 COLMAP 输出的是 W2C 还是 C2WCOLMAPimages.txt/images.bin中的qvec和tvec通常表示 world-to-camera即X_cam R X_world t。17.7 如何从 COLMAP 位姿得到相机中心先由qvec得到旋转矩阵R再计算C -R^T t17.8 相机内参在 3DGS 中有什么作用内参决定 3D Gaussian 如何投影到像素平面包括焦距、主点和视场角。内参错误会导致渲染和真实图像对不齐。17.9 相机外参在 3DGS 中有什么作用外参决定每张训练图像的拍摄位置和朝向。3DGS 训练时根据外参从对应视角渲染再和真实图片计算 loss。17.10 3DGS 会优化相机位姿吗原始常见流程通常固定 COLMAP 相机位姿主要优化 Gaussian 参数。一些扩展方法会联合优化相机位姿。17.11 为什么 3DGS 需要 COLMAPCOLMAP 提供相机内外参和稀疏点云。相机参数用于训练视角约束稀疏点云用于初始化高斯位置。17.12 如果图片 resize 了内参怎么办内参要同步缩放fx, cx 按宽度缩放比例变化 fy, cy 按高度缩放比例变化17.13 如果图片 crop 了内参怎么办焦距通常不变主点需要减去裁剪左上角偏移cx cx - crop_x cy cy - crop_y17.14 什么是畸变为什么要去畸变畸变是镜头导致的非理想成像常见有径向和切向畸变。多视角重建和 3DGS 需要图像和投影模型一致因此通常要正确建模或去畸变。17.15 OpenCV 和 OpenGL 相机坐标有什么区别OpenCV 常用x右、y下、z前OpenGL 常用x右、y上、z后。转换时常需要翻转 y 和 z 轴。17.16 为什么有时相机轨迹看起来反了常见原因是把 W2C 当成 C2W或者坐标系转换方向错了。17.17 3DGS 中渲染结果错位优先排查什么优先排查图片和相机参数是否一一对应。内参是否和当前图像分辨率匹配。W2C/C2W 是否用反。去畸变图片和相机模型是否对应。COLMAP 位姿质量是否可靠。17.18 单目 SfM 的尺度是真实的吗通常不是真实尺度只能恢复相对尺度。除非引入已知尺寸、深度、GPS/IMU 或其他尺度约束。17.19 如何验证内外参正确把已知 3D 点或 COLMAP 稀疏点投影回图片看投影点是否落在真实目标位置同时可视化相机中心和朝向。17.20 面试中如何概括相机参数在 3DGS 中的作用可以这样回答3DGS 训练需要每张图片对应的相机内参和外参。 内参决定投影模型比如焦距和主点外参决定相机在世界坐标系中的位置和朝向。 训练时 3D Gaussians 会根据当前相机参数投影并 rasterize 成图像 再和真实图片计算损失来优化高斯的位置、尺度、旋转、透明度和颜色。 如果相机参数不准渲染和真实图像无法对齐重建质量会明显下降。18. 练习任务18.1 手写投影给定K [[800, 0, 640], [0, 800, 360], [0, 0, 1]] R I t [0, 0, 0] X [0.5, 0, 2]计算像素坐标。18.2 标定相机用手机拍摄 15 到 30 张棋盘格图片用 OpenCV 标定出K和畸变参数。18.3 COLMAP 位姿检查把 COLMAP 的images.txt中的 qvec/tvec 读出来计算每张图片的相机中心并画出相机轨迹。18.4 投影稀疏点读取 COLMAP 的points3D.txt选择一张图片把部分 3D 点投影回图像检查是否对齐。18.5 3DGS 数据排查找一个 3DGS 数据集检查图片数量。cameras 文件。images 文件。sparse 点云数量。图像是否去畸变。图片名和位姿是否对应。19. 参考资料Multiple View Geometry in Computer VisionHartley Zisserman。Learning OpenCVGary Bradski Adrian Kaehler。COLMAP 官方文档https://colmap.github.io/COLMAP Output Formathttps://colmap.github.io/format.htmlOpenCV Camera Calibrationhttps://docs.opencv.org/4.x/dc/dbb/tutorial_py_calibration.htmlOpenCV calib3d 模块https://docs.opencv.org/4.x/d9/d0c/group__calib3d.html3D Gaussian Splatting 官方项目https://github.com/graphdeco-inria/gaussian-splatting3D Gaussian Splatting 论文https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/
相机内外参 学习
发布时间:2026/7/1 23:19:06
目标理解相机内外参的数学意义知道它们在 3DGS 训练和渲染中的位置能完成基础标定、投影验证、COLMAP 位姿读取和常见问题排查。目录1. 为什么要学相机内外参2. 相机成像模型3. 相机内参 Intrinsics4. 相机外参 Extrinsics5. 坐标系世界、相机、图像、像素6. 从 3D 点投影到 2D 像素7. 畸变参数8. COLMAP 中的相机参数和位姿9. 相机内外参在 3DGS 中的定位10. 3DGS 数据链路图片到可渲染模型11. 实操一OpenCV 标定相机内参12. 实操二用内外参投影 3D 点13. 实操三读取 COLMAP 位姿并转成相机中心14. 实操四检查位姿是否正确15. 常见坐标约定转换16. 3DGS 中常见问题排查17. 面试常问问题18. 练习任务19. 参考资料1. 为什么要学相机内外参3DGS、NeRF、SLAM、三维重建、多视角几何、视觉定位都绕不开相机参数。一句话理解内参描述“相机自己怎么成像”外参描述“相机在世界中的位置和朝向”。在 3DGS 中相机内外参决定了每张训练图片对应的相机位置。3D Gaussian 如何投影到每张图像上。渲染图像和真实图像如何对齐。loss 是否能正确反向优化到高斯点。新视角渲染时相机应该从哪里看场景。如果相机参数错了3DGS 会出现模型散掉。场景重建变形。渲染视角错乱。图像对不齐。浮动伪影多。训练 loss 难以下降。2. 相机成像模型最经典的是针孔相机模型。成像链路世界坐标系 3D 点 - 通过外参变换到相机坐标系 - 透视除法得到归一化平面坐标 - 通过内参变换到像素坐标公式s * [u, v, 1]^T K [R | t] [X, Y, Z, 1]^T其中[X, Y, Z]^T世界坐标系中的 3D 点。[u, v]^T像素坐标。K相机内参矩阵。R旋转矩阵。t平移向量。[R | t]外参矩阵通常表示 world-to-camera。s尺度因子本质上和深度有关。3. 相机内参 Intrinsics内参描述相机内部成像属性。常见内参矩阵K [ fx 0 cx 0 fy cy 0 0 1 ]含义fxx 方向焦距单位是像素。fyy 方向焦距单位是像素。cx主点 x 坐标通常接近图像宽度一半。cy主点 y 坐标通常接近图像高度一半。3.1 焦距和像素焦距物理焦距单位通常是毫米例如 35mm。计算机视觉里的fx、fy单位是像素。如果知道物理焦距和传感器尺寸可以粗略换算fx f_mm / sensor_width_mm * image_width_px fy f_mm / sensor_height_mm * image_height_px实际工程中通常通过标定或 SfM 求得像素焦距。3.2 主点主点是光轴和成像平面的交点。理想情况下cx ≈ image_width / 2 cy ≈ image_height / 2但真实相机可能有偏移。3.3 skew完整内参也可能有 skewK [ fx skew cx 0 fy cy 0 0 1 ]现代相机通常认为 skew 为 0。3.4 内参什么时候会变内参和相机硬件、图像处理方式有关。会改变内参的情况改变图像分辨率。图像裁剪 crop。图像缩放 resize。变焦镜头焦距变化。使用不同相机。去畸变后生成新的相机矩阵。缩放图像时内参也要缩放new_fx fx * scale_x new_fy fy * scale_y new_cx cx * scale_x new_cy cy * scale_y裁剪图像时主点要平移new_cx cx - crop_x new_cy cy - crop_y4. 相机外参 Extrinsics外参描述相机相对于世界坐标系的位置和姿态。常见外参[R | t]其中R3x3 旋转矩阵。t3x1 平移向量。在计算机视觉里很多库中的[R | t]表示X_cam R * X_world t也就是 world-to-camera简称 W2C。4.1 相机中心注意外参里的t通常不是相机在世界坐标系中的位置。如果X_cam R * X_world t那么相机中心C在世界坐标系中满足0 R * C t C -R^T * t这是面试和实操里特别容易踩的坑。4.2 camera-to-world如果要从相机坐标转回世界坐标X_world R^T * (X_cam - t)对应矩阵是 C2WT_c2w inverse(T_w2c)如果T_w2c [ R t 0 1 ]那么T_c2w [ R^T -R^T t 0 1 ]5. 坐标系世界、相机、图像、像素5.1 世界坐标系世界坐标系是场景的全局坐标系。在 3DGS 中高斯点的位置、相机位姿、稀疏点云都在同一个世界坐标系里。5.2 相机坐标系相机坐标系以相机光心为原点。OpenCV/COLMAP 常见约定x 向右 y 向下 z 向前这和很多图形学/OpenGL 系统的相机坐标约定不同。5.3 图像坐标系图像坐标系通常以图像左上角为原点u 向右 v 向下5.4 归一化相机平面相机坐标点X_cam [x, y, z]透视除法x_norm x / z y_norm y / z再通过内参u fx * x_norm cx v fy * y_norm cy6. 从 3D 点投影到 2D 像素完整流程X_world - X_cam R * X_world t - x X_cam[0] / X_cam[2] - y X_cam[1] / X_cam[2] - u fx * x cx - v fy * y cy如果考虑畸变则在归一化坐标到像素坐标之间加入畸变模型。6.1 齐次坐标写法P K [R | t] s * p P * X其中P3x4 投影矩阵。X4x1 齐次世界坐标。p3x1 齐次像素坐标。7. 畸变参数真实镜头不是完美针孔模型常见畸变包括径向畸变 radial distortion。切向畸变 tangential distortion。OpenCV 常见畸变参数k1, k2, p1, p2, k37.1 径向畸变径向畸变常见表现桶形畸变边缘向外鼓。枕形畸变边缘向内收。7.2 切向畸变切向畸变通常来自镜头和成像平面不完全平行。7.3 3DGS 中如何处理畸变原始 3DGS 工作流通常依赖 COLMAP 结果并使用 undistorted images。常见做法原始图片 - COLMAP 特征提取和匹配 - 稀疏重建 - image_undistorter 去畸变 - 3DGS 使用去畸变图片和对应相机参数训练如果图片有明显畸变但没有正确处理3DGS 会很难把所有视角对齐。8. COLMAP 中的相机参数和位姿3DGS 常见输入来自 COLMAP。COLMAP 稀疏重建目录通常包括sparse/0/ cameras.bin images.bin points3D.bin或文本格式cameras.txt images.txt points3D.txt8.1 cameras.txt示例# CAMERA_ID, MODEL, WIDTH, HEIGHT, PARAMS[] 1 PINHOLE 1920 1080 1500.0 1500.0 960.0 540.0含义CAMERA_ID相机 ID。MODEL相机模型。WIDTH HEIGHT图像尺寸。PARAMS内参和畸变参数具体顺序取决于模型。常见模型SIMPLE_PINHOLE:f, cx, cyPINHOLE:fx, fy, cx, cySIMPLE_RADIAL:f, cx, cy, kOPENCV:fx, fy, cx, cy, k1, k2, p1, p28.2 images.txt示例# IMAGE_ID, QW, QX, QY, QZ, TX, TY, TZ, CAMERA_ID, IMAGE_NAME 1 0.99 0.01 0.02 0.03 1.0 2.0 3.0 1 image_001.jpgCOLMAP 中qvec tvec 表示 world-to-camera X_cam R * X_world t相机中心C -R^T * t8.3 points3D.txt包含稀疏点云POINT3D_ID, X, Y, Z, R, G, B, ERROR, TRACK[]3DGS 通常用这些稀疏点作为初始点云。9. 相机内外参在 3DGS 中的定位3DGS 的核心是优化一组 3D Gaussiansposition scale rotation opacity spherical harmonics color相机参数本身通常不是要学习的主体而是训练监督的几何约束。9.1 在训练中训练时每一步通常会选一张训练图像 - 取该图像对应的相机内参和外参 - 把 3D Gaussians 投影到该相机视角 - rasterize 渲染出预测图 - 和真实图像计算 loss - 反向优化高斯参数也就是说相机内外参决定“从哪里看”和“怎么投影”。9.2 在渲染中新视角渲染时需要提供一个新的相机位姿和内参给定 camera pose intrinsics - 3DGS 渲染该视角下的图像如果只想沿训练轨迹渲染则使用训练或测试相机位姿。如果想自由飞行需要自己生成相机轨迹。9.3 在初始化中原始 3DGS 通常用 COLMAP 稀疏点云初始化高斯位置。相机位姿来自 COLMAP SfM多视角图片 - SfM 求相机位姿和稀疏点 - 3DGS 基于这些相机和点云训练如果 COLMAP 位姿不准3DGS 后续优化会非常吃力。9.4 是否优化相机位姿原始 3DGS 常见训练默认固定 COLMAP 相机位姿主要优化 Gaussians。一些扩展工作会联合优化相机位姿例如相机位姿有噪声。手机视频位姿不稳定。少视角重建。动态场景或 SLAM 场景。但联合优化更复杂也更容易陷入局部最优。10. 3DGS 数据链路图片到可渲染模型典型流程采集图片或视频抽帧 - COLMAP 特征提取 - 特征匹配 - 稀疏重建 SfM - 去畸变 - 得到 images、cameras、sparse - 3DGS 读取相机和点云 - 训练高斯场景 - 新视角渲染10.1 输入图片采集建议采集建议围绕目标多角度拍摄。相邻视角有足够重叠。避免大量运动模糊。避免纯白墙、玻璃、镜面等弱纹理区域。曝光尽量稳定。不要频繁变焦。尽量固定焦距和分辨率。10.2 COLMAP 结果质量对 3DGS 的影响COLMAP 好3DGS 通常比较顺。COLMAP 差常见后果稀疏点很少。相机轨迹断裂。部分图片无法注册。场景尺度异常。训练后漂浮物很多。渲染视角错乱。11. 实操一OpenCV 标定相机内参准备一张棋盘格标定板。多张不同角度的标定图片。每张图都能看到完整棋盘格。安装pipinstallopencv-python numpy保存为calibrate_camera.pyfrompathlibimportPathimportcv2ascvimportnumpyasnpdefmain():image_dirPath(calib_images)pattern_size(9,6)square_size0.025objpnp.zeros((pattern_size[0]*pattern_size[1],3),np.float32)objp[:,:2]np.mgrid[0:pattern_size[0],0:pattern_size[1]].T.reshape(-1,2)objp*square_size object_points[]image_points[]image_sizeNoneforimage_pathinsorted(image_dir.glob(*.jpg)):imagecv.imread(str(image_path))ifimageisNone:continuegraycv.cvtColor(image,cv.COLOR_BGR2GRAY)image_sizegray.shape[::-1]ok,cornerscv.findChessboardCorners(gray,pattern_size)ifnotok:print(not found:,image_path)continuecornerscv.cornerSubPix(gray,corners,winSize(11,11),zeroZone(-1,-1),criteria(cv.TERM_CRITERIA_EPScv.TERM_CRITERIA_MAX_ITER,30,0.001),)object_points.append(objp)image_points.append(corners)print(found:,image_path)ifnotobject_points:raiseRuntimeError(No valid chessboard images found.)rms,K,dist,rvecs,tvecscv.calibrateCamera(object_points,image_points,image_size,None,None,)print(RMS reprojection error:,rms)print(K:)print(K)print(distortion:)print(dist.ravel())if__name____main__:main()输出中的K就是内参矩阵dist是畸变参数。11.1 标定结果怎么看重点看重投影误差 RMS 是否合理。fx、fy是否接近。cx、cy是否接近图像中心。畸变参数是否异常大。标定图片角度是否覆盖充分。12. 实操二用内外参投影 3D 点保存为project_points_demo.pyimportnumpyasnpdefproject_points(points_world,K,R,t):points_cam(R points_world.Tt.reshape(3,1)).T zpoints_cam[:,2:3]validz[:,0]1e-6points_normpoints_cam[:,:2]/z fx,fyK[0,0],K[1,1]cx,cyK[0,2],K[1,2]ufx*points_norm[:,0]cx vfy*points_norm[:,1]cy pixelsnp.stack([u,v],axis1)returnpixels,valid,points_camdefmain():Knp.array([[800.0,0.0,640.0],[0.0,800.0,360.0],[0.0,0.0,1.0],])Rnp.eye(3)tnp.array([0.0,0.0,0.0])points_worldnp.array([[0.0,0.0,2.0],[0.5,0.0,2.0],[0.0,0.5,2.0],[0.0,0.0,-1.0],])pixels,valid,points_camproject_points(points_world,K,R,t)forp3d,pc,pix,okinzip(points_world,points_cam,pixels,valid):print(fworld{p3d}, cam{pc}, pixel{pix}, valid{ok})if__name____main__:main()注意z 0的点在相机后方不能正常投影。投影点超出图像范围说明点不在当前视野内。13. 实操三读取 COLMAP 位姿并转成相机中心这里用文本格式images.txt演示。可以先用 COLMAP 把 bin 转成 txt或者使用现成脚本读取 bin。保存为read_colmap_images_txt.pyfrompathlibimportPathimportnumpyasnpdefqvec_to_rotmat(qvec):qw,qx,qy,qzqvecreturnnp.array([[1-2*qy*qy-2*qz*qz,2*qx*qy-2*qz*qw,2*qx*qz2*qy*qw],[2*qx*qy2*qz*qw,1-2*qx*qx-2*qz*qz,2*qy*qz-2*qx*qw],[2*qx*qz-2*qy*qw,2*qy*qz2*qx*qw,1-2*qx*qx-2*qy*qy],])defparse_images_txt(path):linesPath(path).read_text(encodingutf-8).splitlines()images[]i0whileilen(lines):linelines[i].strip()ifnotlineorline.startswith(#):i1continuepartsline.split()image_idint(parts[0])qvecnp.array([float(x)forxinparts[1:5]])tvecnp.array([float(x)forxinparts[5:8]])camera_idint(parts[8])image_nameparts[9]Rqvec_to_rotmat(qvec)C-R.T tvec T_w2cnp.eye(4)T_w2c[:3,:3]R T_w2c[:3,3]tvec T_c2wnp.linalg.inv(T_w2c)images.append({image_id:image_id,camera_id:camera_id,image_name:image_name,R_w2c:R,t_w2c:tvec,camera_center_world:C,T_w2c:T_w2c,T_c2w:T_c2w,})i2returnimagesdefmain():imagesparse_images_txt(sparse/0/images.txt)foriteminimages[:5]:print(item[image_name])print(camera center:,item[camera_center_world])print(T_c2w:)print(item[T_c2w])if__name____main__:main()重点COLMAP 的 qvec/tvec 是 world-to-camera。 相机中心不是 tvec而是 -R^T t。14. 实操四检查位姿是否正确14.1 用稀疏点投影回图片最可靠的检查之一读取 COLMAP points3D - 读取某张图像的 K、R、t - 把 3D 点投影到该图像 - 叠加到原图上 - 看是否落在对应物体位置如果投影整体偏移可能是内参不匹配。resize 后没有同步缩放内参。R/t 方向用反了。坐标系转换错了。图片去畸变和内参不对应。14.2 看相机中心分布对一个环绕拍摄物体的数据集相机中心应该大致围绕目标。如果相机中心散得很离谱有些图片匹配错了。位姿读取错了。qvec 顺序写错了。W2C/C2W 反了。14.3 看相机朝向相机应该大致朝向场景中心。如果所有相机都背对场景通常是坐标轴方向或矩阵求逆错了。15. 常见坐标约定转换15.1 OpenCV/COLMAP 和 OpenGLOpenCV/COLMAP 常见相机坐标x right y down z forwardOpenGL 常见相机坐标x right y up z backward因此经常需要翻转 y、z 轴。一个常见转换矩阵importnumpyasnp opencv_to_openglnp.diag([1,-1,-1,1])具体放左边还是右边取决于你在转换的是坐标点、相机矩阵还是坐标系基向量。不要死背建议用一个简单点验证。15.2 NeRF transforms.json很多 NeRF 数据使用transforms.json里面常见的是 camera-to-world 矩阵。而 COLMAP 输出常见是 world-to-camera。转换T_c2w inverse(T_w2c)15.3 判断矩阵方向的小技巧如果一个矩阵T是 C2WT[:3, 3]通常就是相机中心在世界坐标中的位置。如果一个矩阵T是 W2CT[:3, 3]通常不是相机中心。相机中心要算-R^T t。16. 3DGS 中常见问题排查16.1 训练结果一团糊可能原因COLMAP 位姿错误。图片太少或重叠不足。图像模糊。场景动态物体太多。去畸变没处理好。内参和图片分辨率不匹配。16.2 渲染视角方向不对可能原因W2C 和 C2W 搞反。OpenCV/OpenGL 坐标系没转换。相机 forward 方向取错。矩阵按行主序/列主序理解错。16.3 场景尺度很奇怪SfM 重建的尺度通常是不确定的。单目多视角重建只能恢复相对尺度除非有已知标定板尺寸。深度传感器。IMU/GPS。已知物体尺寸。多传感器约束。16.4 新视角渲染漂浮物多可能原因训练视角覆盖不足。相机位姿不准。背景动态变化。高斯过度生长。纹理弱导致几何约束不足。16.5 COLMAP 注册图片少处理提高图片重叠率。去掉模糊图片。使用更丰富纹理区域。调整特征提取和匹配参数。视频抽帧不要间隔太大。避免重复纹理和大面积反光。17. 面试常问问题17.1 什么是相机内参内参描述相机自身成像属性通常包括fx、fy、cx、cy也可能包括 skew 和畸变参数。17.2 什么是相机外参外参描述相机和世界坐标系之间的刚体变换通常由旋转R和平移t组成。17.3 内参和外参的区别是什么内参描述相机内部成像模型外参描述相机在世界中的位置和朝向。17.4 外参中的t是相机位置吗不一定。若外参表示X_cam R X_world t则t不是相机中心。相机中心是C -R^T t。17.5 world-to-camera 和 camera-to-world 有什么区别world-to-camera 把世界点变到相机坐标系camera-to-world 把相机坐标点变到世界坐标系。两者互为逆矩阵。17.6 COLMAP 输出的是 W2C 还是 C2WCOLMAPimages.txt/images.bin中的qvec和tvec通常表示 world-to-camera即X_cam R X_world t。17.7 如何从 COLMAP 位姿得到相机中心先由qvec得到旋转矩阵R再计算C -R^T t17.8 相机内参在 3DGS 中有什么作用内参决定 3D Gaussian 如何投影到像素平面包括焦距、主点和视场角。内参错误会导致渲染和真实图像对不齐。17.9 相机外参在 3DGS 中有什么作用外参决定每张训练图像的拍摄位置和朝向。3DGS 训练时根据外参从对应视角渲染再和真实图片计算 loss。17.10 3DGS 会优化相机位姿吗原始常见流程通常固定 COLMAP 相机位姿主要优化 Gaussian 参数。一些扩展方法会联合优化相机位姿。17.11 为什么 3DGS 需要 COLMAPCOLMAP 提供相机内外参和稀疏点云。相机参数用于训练视角约束稀疏点云用于初始化高斯位置。17.12 如果图片 resize 了内参怎么办内参要同步缩放fx, cx 按宽度缩放比例变化 fy, cy 按高度缩放比例变化17.13 如果图片 crop 了内参怎么办焦距通常不变主点需要减去裁剪左上角偏移cx cx - crop_x cy cy - crop_y17.14 什么是畸变为什么要去畸变畸变是镜头导致的非理想成像常见有径向和切向畸变。多视角重建和 3DGS 需要图像和投影模型一致因此通常要正确建模或去畸变。17.15 OpenCV 和 OpenGL 相机坐标有什么区别OpenCV 常用x右、y下、z前OpenGL 常用x右、y上、z后。转换时常需要翻转 y 和 z 轴。17.16 为什么有时相机轨迹看起来反了常见原因是把 W2C 当成 C2W或者坐标系转换方向错了。17.17 3DGS 中渲染结果错位优先排查什么优先排查图片和相机参数是否一一对应。内参是否和当前图像分辨率匹配。W2C/C2W 是否用反。去畸变图片和相机模型是否对应。COLMAP 位姿质量是否可靠。17.18 单目 SfM 的尺度是真实的吗通常不是真实尺度只能恢复相对尺度。除非引入已知尺寸、深度、GPS/IMU 或其他尺度约束。17.19 如何验证内外参正确把已知 3D 点或 COLMAP 稀疏点投影回图片看投影点是否落在真实目标位置同时可视化相机中心和朝向。17.20 面试中如何概括相机参数在 3DGS 中的作用可以这样回答3DGS 训练需要每张图片对应的相机内参和外参。 内参决定投影模型比如焦距和主点外参决定相机在世界坐标系中的位置和朝向。 训练时 3D Gaussians 会根据当前相机参数投影并 rasterize 成图像 再和真实图片计算损失来优化高斯的位置、尺度、旋转、透明度和颜色。 如果相机参数不准渲染和真实图像无法对齐重建质量会明显下降。18. 练习任务18.1 手写投影给定K [[800, 0, 640], [0, 800, 360], [0, 0, 1]] R I t [0, 0, 0] X [0.5, 0, 2]计算像素坐标。18.2 标定相机用手机拍摄 15 到 30 张棋盘格图片用 OpenCV 标定出K和畸变参数。18.3 COLMAP 位姿检查把 COLMAP 的images.txt中的 qvec/tvec 读出来计算每张图片的相机中心并画出相机轨迹。18.4 投影稀疏点读取 COLMAP 的points3D.txt选择一张图片把部分 3D 点投影回图像检查是否对齐。18.5 3DGS 数据排查找一个 3DGS 数据集检查图片数量。cameras 文件。images 文件。sparse 点云数量。图像是否去畸变。图片名和位姿是否对应。19. 参考资料Multiple View Geometry in Computer VisionHartley Zisserman。Learning OpenCVGary Bradski Adrian Kaehler。COLMAP 官方文档https://colmap.github.io/COLMAP Output Formathttps://colmap.github.io/format.htmlOpenCV Camera Calibrationhttps://docs.opencv.org/4.x/dc/dbb/tutorial_py_calibration.htmlOpenCV calib3d 模块https://docs.opencv.org/4.x/d9/d0c/group__calib3d.html3D Gaussian Splatting 官方项目https://github.com/graphdeco-inria/gaussian-splatting3D Gaussian Splatting 论文https://repo-sam.inria.fr/fungraph/3d-gaussian-splatting/