news 2026/9/7 4:55:15

OpenCV Python calib3d 实战:相机标定、姿态估计、极线几何与立体视差完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenCV Python calib3d 实战:相机标定、姿态估计、极线几何与立体视差完整指南

OpenCV Python calib3d 实战:相机标定、姿态估计、极线几何与立体视差完整指南

【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencv

本文基于 OpenCV 官方 Python 教程中「Camera Calibration and 3D Reconstruction」这一章的目录文档整理成篇,围绕 calib3d 模块的四大核心主题展开:相机畸变模型与标定、基于 PnP 的三维姿态估计、双目标定中的极线几何,以及由立体图像求取深度图。读完本篇,你将掌握cv.calibrateCameracv.undistortcv.solvePnPcv.findFundamentalMatcv.StereoBM等关键函数的完整调用流程与参数含义,并能独立完成从棋盘格标定到三维渲染、立体匹配的工程实践。

1. calib3d 模块的文档体系与代码组织

本章的入口是 calib3d 教程目录,它通过 Doxygen 的@subpage指令把整章拆分为四个子页,分别对应四个独立的技术主题:

子页主题官方文档核心能力
tutorial_py_calibration相机标定py_calibration.markdown找出相机畸变、求解内参/外参、对图像去畸变
tutorial_py_pose姿态估计py_pose.markdown利用标定结果在图像中叠加 3D 坐标轴、渲染立方体
tutorial_py_epipolar_geometry极线几何py_epipolar_geometry.markdown理解极线约束,求基本矩阵与极线
tutorial_py_depthmap立体深度图py_depthmap.markdown从两幅图像提取视差并推导深度

这四个教程的底层实现都位于仓库的 modules/calib3d 模块中,其中findChessboardCornerscalibrateCamerasolvePnPfindFundamentalMatStereoBM等函数的 C++ 实现、性能测试与 Python 绑定均在该目录下。各子教程引用的演示图片也分别存放在各子页的images/目录中,例如 径向畸变示例图、标定结果图。

2. 相机标定:畸变模型、角点检测与去畸变

本节内容继承自 py_calibration.markdown。官方教程给出的目标是三点:了解相机造成的畸变类型、求出相机的内参与外参、基于这些参数对图像去畸变。

2.1 畸变模型:径向畸变与切向畸变

针孔相机会给图像引入两类主要畸变:

  • 径向畸变(Radial distortion):使直线看起来弯曲,离图像中心越远的点畸变越大。官方教程给出的示例图中,棋盘格边缘本应是直线,却整体向外鼓起。径向畸变可表示为:

    x_distorted = x * (1 + k1*r^2 + k2*r^4 + k3*r^6) y_distorted = y * (1 + k1*r^2 + k2*r^4 + k3*r^6)
  • 切向畸变(Tangential distortion):由于成像透镜没有与成像面完全平行而产生,导致图像中某些区域看起来比预期更近。其表达式为:

    x_distorted = x + [2*p1*x*y + p2*(r^2 + 2*x^2)] y_distorted = y + [p1*(r^2 + 2*y^2) + 2*p2*x*y]

综合两类畸变,我们需要求出五个畸变系数(k1, k2, p1, p2, k3)

除畸变系数外,标定还需要两组参数:

  • 内参(Intrinsic parameters):与相机本身绑定的参数,包括焦距(fx, fy)与光心(cx, cy),可组成 3×3 相机矩阵:

    camera matrix = [ fx 0 cx 0 fy cy 0 0 1 ]

    相机矩阵对同一台相机是唯一的,求出后可在所有该相机拍摄的图片上复用。

  • 外参(Extrinsic parameters):旋转与平移向量,用于把 3D 点坐标转换到相机坐标系。

对立体应用来说,这些畸变必须先校正。求解思路是:提供若干张含有已知图案(如棋盘格)的样本图像,找出其中相对位置已知的特征点(棋盘格的方格交点)。由于这些点在真实世界空间与图像中的坐标都已知,即可解出畸变系数。官方教程强调:为获得较好结果,至少需要 10 张有效测试图案

2.2 收集角点:object points 与 image points

官方示例使用了仓库 samples/data 目录中自带的棋盘格图片left01.jpgleft14.jpg

2D image points可以直接从图像中检测。棋盘格中两个黑色方格互相接触的交点即为目标点。

3D object points的处理技巧在于:那些照片是用固定相机拍摄、棋盘格放在不同位置和姿态下得到的。为了简化,可以反向假设棋盘格静止在 XY 平面上(Z 恒为 0)、而相机在移动,这样只需给出 X、Y 值。若以棋盘格方格边长为尺度单位,可直接传入(0,0), (1,0), (2,0), ...;如果知道方格真实尺寸(如 30 mm),则传入(0,0), (30,0), (60,0), ...,结果单位即为毫米。官方示例的作者不掌握原始方格尺寸,因此以方格边长为单位传入。

3D 点称为object points,2D 点称为image points。角点检测分三步:

  1. cv.findChessboardCorners()寻找棋盘格角点,需传入图案尺寸(本例为 6×7 内部角点网格;普通棋盘格是 8×8 方格、7×7 内部角点)。返回 retval(是否找到)与按从左到右、从上到下排序的角点;
  2. cv.cornerSubPix()对角点做亚像素级精化;
  3. cv.drawChessboardCorners()绘制图案以便检查。

官方教程特别提示:单帧图像未必都能找到图案,稳健的做法是启动相机逐帧检测,找到图案后存点、间隔一段时间再读下一帧,以便把棋盘格调整到不同方向,直到凑足足够数量的有效图案。本例对 14 张图逐一读取、只保留检测成功的。此外,官方教程注明:也可以改用圆形网格图案,此时必须用cv.findCirclesGrid()检测,且所需图像数量更少。

完整的角点收集代码如下(完整继承官方教程代码块):

import numpy as np import cv2 as cv import glob # termination criteria criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001) # prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(5,6,0) cols = 6 rows = 7 objp = np.zeros((cols*rows,3), np.float32) objp[:,:2] = np.mgrid[0:cols,0:rows].T.reshape(-1,2) # Arrays to store object points and image points from all the images. objpoints = [] # 3d point in real world space imgpoints = [] # 2d points in image plane. images = glob.glob('*.jpg') for fname in images: img = cv.imread(fname) gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # Find the chess board corners ret, corners = cv.findChessboardCorners(gray, (cols, rows), None) # If found, add object points, image points (after refining them) if ret == True: objpoints.append(objp) corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria) imgpoints.append(corners2) # Draw and display the corners cv.drawChessboardCorners(img, (cols, rows), corners2, ret) cv.imshow('img', img) cv.waitKey(500) cv.destroyAllWindows()

其中criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)表示角点精化在迭代 30 次或精度优于 0.001 像素时终止;(11,11)是搜索角点邻域大小的奇数窗口,(-1,-1)表示整个图像都参与搜索。检测成功后的角点绘制效果可参见官方教程中的 calib_pattern.jpg。

2.3 执行标定:calibrateCamera

拿到 object points 和 image points 后即可执行标定。cv.calibrateCamera()返回相机矩阵、畸变系数、各帧的旋转与平移向量等:

ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

ret是重投影误差,gray.shape[::-1]得到(宽, 高)形式的图像尺寸;最后两个None表示没有初始相机矩阵与畸变系数可供迭代,由算法从零估计。

2.4 去畸变:两种等价方法

去畸变前,可先用cv.getOptimalNewCameraMatrix()基于一个自由缩放参数 alpha 优化新的相机矩阵:

  • alpha=0:返回不含多余无效像素的去畸变图像,但可能裁掉图像四角的一些像素;
  • alpha=1:保留全部像素,代价是画面边缘出现部分黑色填充。

该函数同时返回一个 ROI,可用于裁剪结果。官方示例取alpha=1,对新一张图left12.jpg处理:

img = cv.imread('left12.jpg') h, w = img.shape[:2] newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))

方法一:直接使用cv.undistort(),最简单的做法,调用函数后用上面的 ROI 裁剪:

# undistort dst = cv.undistort(img, mtx, dist, None, newcameramtx) # crop the image x, y, w, h = roi dst = dst[y:y+h, x:x+w] cv.imwrite('calibresult.png', dst)

方法二:使用重映射(remapping),先求出从畸变图像到去畸变图像的映射,再用cv.remap应用:

# undistort mapx, mapy = cv.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w,h), 5) dst = cv.remap(img, mapx, mapy, cv.INTER_LINEAR) # crop the image x, y, w, h = roi dst = dst[y:y+h, x:x+w] cv.imwrite('calibresult.png', dst)

两种方法结果相同(initUndistortRectifyMap的第三个参数传None表示只做去畸变不做重投影校正;插值方式为线性插值)。官方教程展示了去畸变结果(calib_result.jpg),可以看到画面中所有边缘都变直了。最后,官方教程建议用 NumPy 的写盘函数(np.saveznp.savetxt等)保存相机矩阵与畸变系数,供后续(如姿态估计一节)复用。

2.5 重投影误差:评估标定精度

重投影误差能很好地估计所求参数的精确程度:误差越接近零,说明参数越准确。具体做法是:给定内参、畸变、旋转与平移,先用cv.projectPoints()把 object points 投影到图像平面,再计算其与角点检测结果的误差范数;对所有点和所有图像求平方误差平均后开根号,得到 RMSE:

mean_error = 0 for i in range(len(objpoints)): imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist) error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2SQR) / len(imgpoints2) mean_error += error print( "total error: {}".format(np.sqrt(mean_error/len(objpoints))) )

官方教程在结尾还布置了一个练习:尝试用圆形网格(circular grid)完成相机标定——对应 2.2 节提到的cv.findCirclesGrid()路径。

3. 姿态估计:在图像中渲染三维坐标轴与立方体

本节继承自 py_pose.markdown。核心思想是:标定一节已经求出了相机矩阵与畸变系数;给定一张图案图像,就能利用这些信息计算它的姿态(pose),即物体在空间中的姿态——如何旋转、如何平移。对平面物体可假设 Z=0,问题就转化为"相机处于什么位置才能看到这张图案图"。知道了物体在空间中的姿态,就可以在其平面上绘制 2D 图形来模拟 3D 效果。

官方教程的具体目标是把 3D 坐标轴(X 轴蓝色、Y 轴绿色、Z 轴红色)画在棋盘格第一个角点上,使 Z 轴看起来垂直于棋盘格平面。

3.1 加载标定结果并定义绘制函数

首先从上一节保存的标定数据文件(官方示例保存为B.npz)中读回相机矩阵与畸变系数:

import numpy as np import cv2 as cv import glob # Load previously saved data with np.load('B.npz') as X: mtx, dist, _, _ = [X[i] for i in ('mtx','dist','rvecs','tvecs')]

然后定义draw函数:输入棋盘格角点(来自cv.findChessboardCorners())与轴端点投影坐标imgpts,把三条彩色轴线从第一个角点画出:

def draw(img, corners, imgpts): corner = tuple(corners[0].ravel().astype("int32")) imgpts = imgpts.astype("int32") img = cv.line(img, corner, tuple(imgpts[0].ravel()), (255,0,0), 5) img = cv.line(img, corner, tuple(imgpts[1].ravel()), (0,255,0), 5) img = cv.line(img, corner, tuple(imgpts[2].ravel()), (0,0,255), 5) return img

3.2 定义轴端点并用 solvePnP 求姿态

与标定一节相同地创建终止条件与 object points,另外定义axis points——用于画轴线的三维空间点。由于标定以棋盘格方格边长为尺度单位,这里轴长取 3 个方格:X 轴从 (0,0,0) 画到 (3,0,0),Y 轴同理;Z 轴从 (0,0,0) 画到 (0,0,-3),取负值表示向相机方向伸出。

criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001) cols = 6 rows = 7 objp = np.zeros((cols*rows,3), np.float32) objp[:,:2] = np.mgrid[0:cols,0:rows].T.reshape(-1,2) axis = np.float32([[3,0,0], [0,3,0], [0,0,-3]]).reshape(-1,3)

主循环中,对每幅图像检测 6×7 网格并做亚像素精化,然后调用cv.solvePnPRansac()(官方教程文字说明用 RANSAC 版本;代码示例中使用cv.solvePnP)求解旋转和平移向量,再用cv.projectPoints()把 axis points 投影到图像平面,最后从第一个角点向投影点连线:

for fname in glob.glob('left*.jpg'): img = cv.imread(fname) gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY) ret, corners = cv.findChessboardCorners(gray, (cols, rows), None) if ret == True: corners2 = cv.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria) # Find the rotation and translation vectors. ret,rvecs, tvecs = cv.solvePnP(objp, corners2, mtx, dist) # project 3D points to image plane imgpts, jac = cv.projectPoints(axis, rvecs, tvecs, mtx, dist) img = draw(img,corners2,imgpts) cv.imshow('img',img) k = cv.waitKey(0) & 0xFF if k == ord('s'): cv.imwrite(fname[:6]+'.png', img) cv.destroyAllWindows()

运行后每条轴线长度均为 3 个方格,效果即 pose_1.jpg 所示。

3.3 进阶:渲染一个立方体

若想在棋盘格上方"悬空"渲染一个立方体,只需修改draw函数与轴端点。修改后的绘制函数把投影点分成地面层(绿色轮廓)、四根立柱(蓝色线段)与顶面(红色轮廓):

def draw(img, corners, imgpts): imgpts = np.int32(imgpts).reshape(-1,2) # draw ground floor in green img = cv.drawContours(img, [imgpts[:4]],-1,(0,255,0),-3) # draw pillars in blue color for i,j in zip(range(4),range(4,8)): img = cv.line(img, tuple(imgpts[i]), tuple(imgpts[j]),(255),3) # draw top layer in red color img = cv.drawContours(img, [imgpts[4:]],-1,(0,0,255),3) return img

轴端点改为 3D 空间立方体的 8 个角点(边长 3 个方格):

axis = np.float32([[0,0,0], [0,3,0], [3,3,0], [3,0,0], [0,0,-3],[0,3,-3],[3,3,-3],[3,0,-3] ])

渲染结果可参见官方教程中的 pose_2.jpg。官方教程最后指出:如果对图形学、增强现实(AR)感兴趣,可以进一步使用 OpenGL 渲染更复杂的图形。

4. 极线几何:基本矩阵、极线与极点

本节继承自 py_epipolar_geometry.markdown,学习多视图几何基础:极点(epipole)、极线(epilines)与极线约束(epipolar constraint)。

4.1 核心概念

用针孔相机成像时,3D 到 2D 的转换丢失了深度信息。恢复深度的办法是使用多个相机——人眼正是以"双相机"方式工作,即立体视觉(stereo vision)。

只有一台相机时,空间中射线OX上的所有点都投影到图像上的同一个点x,无法确定 3D 位置;引入右相机后,OX上不同的点会投影到右图像平面上不同的点x',于是可以三角化出正确的 3D 点,这就是双目标定的核心思想。

由此引出三个术语:

  • OX上各点在右图像平面上投影形成的直线l'称为点x对应的极线(epiline)。匹配点的搜索不必遍历整幅图像,只需沿极线搜索,性能与精度都更好——这就是极线约束(Epipolar Constraint)
  • 包含XOO'的平面XOO'称为极平面(Epipolar Plane)
  • OO'是两个相机中心。右相机中心O'在左图像上的投影点e称为极点(epipole),即两相机中心连线与图像平面的交点;同理e'是左相机对应的极点。某些情况下极点会落在图像之外(意味着一台相机看不到另一台)。由于所有极线都过极点,可以通过求多条极线的交点来定位极点。

要计算极线,还需要两个"配料":

  • 本质矩阵(Essential Matrix, E):包含描述第二个相机相对第一个相机在全局坐标系中位置的平移与旋转信息(原理图见 essential_matrix.jpg);
  • 基本矩阵(Fundamental Matrix, F):我们希望测量以像素坐标进行,F 包含与 E 相同的信息,外加两台相机的内参信息,从而能在像素坐标下关联两个相机(若图像已校正并按焦距归一化点坐标,则F=E)。简言之,F 把一幅图像中的一个点映射为另一幅图像中的一条极线。F 由两幅图像的匹配点计算,使用八点算法时最少需要 8 对匹配点;点数越多越好,并应使用 RANSAC 获得更稳健的结果。

4.2 特征匹配:SIFT + FLANN + 比值检验

官方示例使用 SIFT 描述子、基于 FLANN 的匹配器和 Lowe 比值检验,从左右两幅图中找出尽可能多的匹配对:

import numpy as np import cv2 as cv from matplotlib import pyplot as plt img1 = cv.imread('myleft.jpg', cv.IMREAD_GRAYSCALE) #queryimage # left image img2 = cv.imread('myright.jpg', cv.IMREAD_GRAYSCALE) #trainimage # right image sift = cv.SIFT_create() # find the keypoints and descriptors with SIFT kp1, des1 = sift.detectAndCompute(img1,None) kp2, des2 = sift.detectAndCompute(img2,None) # FLANN parameters FLANN_INDEX_KDTREE = 1 index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5) search_params = dict(checks=50) flann = cv.FlannBasedMatcher(index_params,search_params) matches = flann.knnMatch(des1,des2,k=2) pts1 = [] pts2 = [] # ratio test as per Lowe's paper for i,(m,n) in enumerate(matches): if m.distance < 0.8*n.distance: pts2.append(kp2[m.trainIdx].pt) pts1.append(kp1[m.queryIdx].pt)

其中 FLANN 参数含义:algorithm = FLANN_INDEX_KDTREE(1)使用 KD 树索引、trees=5建 5 棵树;checks=50控制搜索时的近似程度。knnMatch(..., k=2)返回每个特征点的前 2 近邻,比值检验m.distance < 0.8*n.distance(Lowe 论文的经典 0.8 阈值)过滤掉区分度不足的匹配。

4.3 求基本矩阵并绘制极线

用 LMEDS 鲁棒方法求基本矩阵,并只保留内点:

pts1 = np.int32(pts1) pts2 = np.int32(pts2) F, mask = cv.findFundamentalMat(pts1,pts2,cv.FM_LMEDS) # We select only inlier points pts1 = pts1[mask.ravel()==1] pts2 = pts2[mask.ravel()==1]

注意computeCorrespondEpilines的输入形状与"线画在哪幅图"的对应关系:第一幅图的点产生的极线要画在第二幅图上。官方示例专门写了一个drawlines函数——极线以(a,b,c)形式给出,取x=0x=列宽两点即可画出整条直线:

def drawlines(img1,img2,lines,pts1,pts2): ''' img1 - image on which we draw the epilines for the points in img2 lines - corresponding epilines ''' r,c = img1.shape img1 = cv.cvtColor(img1,cv.COLOR_GRAY2BGR) img2 = cv.cvtColor(img2,cv.COLOR_GRAY2BGR) for r,pt1,pt2 in zip(lines,pts1,pts2): color = tuple(np.random.randint(0,255,3).tolist()) x0,y0 = map(int, [0, -r[2]/r[1] ]) x1,y1 = map(int, [c, -(r[2]+r[0]*c)/r[1] ]) img1 = cv.line(img1, (x0,y0), (x1,y1), color,1) img1 = cv.circle(img1,tuple(pt1),5,color,-1) img2 = cv.circle(img2,tuple(pt2),5,color,-1) return img1,img2

然后在两幅图上分别求、分别画极线:

# Find epilines corresponding to points in right image (second image) and # drawing its lines on left image lines1 = cv.computeCorrespondEpilines(pts2.reshape(-1,1,2), 2,F) lines1 = lines1.reshape(-1,3) img5,img6 = drawlines(img1,img2,lines1,pts1,pts2) # Find epilines corresponding to points in left image (first image) and # drawing its lines on right image lines2 = cv.computeCorrespondEpilines(pts1.reshape(-1,1,2), 1,F) lines2 = lines2.reshape(-1,3) img3,img4 = drawlines(img2,img1,lines2,pts2,pts1) plt.subplot(121),plt.imshow(img5) plt.subplot(122),plt.imshow(img3) plt.show()

第二个参数(1 或 2)指明输入点属于哪幅图像。结果如官方教程中的 epiresult.jpg 所示:左图像中所有极线汇聚于图像右侧外部的一个点,该汇聚点即极点。

官方教程对实践效果的提示是:应使用分辨率较高、且包含大量非平面点的图像。章节练习部分还指出两条经验:其一是相机沿光轴前进运动时,两幅图中的极点会出现在相同位置、极线从一个固定点辐射而出;其二是基本矩阵估计对匹配质量、离群点很敏感,当所有匹配点都位于同一平面时效果会显著变差。

5. 从立体图像求深度图:StereoBM 与参数调优

本节继承自 py_depthmap.markdown,目标是从一对立体图像创建深度图(disparity map)。

5.1 视差与深度的几何关系

上一节的极线约束已经说明,两幅同一场景的图像可以直观地得到深度信息。官方教程给出的示意与推导见 stereo_depth.jpg:图中存在相似三角形,写出等价方程可得视差公式:

disparity = x - x' = B*f / Z

其中xx'是场景点在图像平面上对应点与其相机中心之间的距离差,B是两相机间距(基线,已知),f是焦距(已知)。也就是说,场景中某点的深度与对应图像点距离差成反比。据此可推导出图像中所有像素的深度。实现上先求两图间的对应匹配(极线约束使该操作更快更准),再得到视差。

5.2 用 StereoBM 生成视差图

官方示例使用 Tsukuba 测试序列的tsukuba_l.png/tsukuba_r.png,用块匹配立体算法cv.StereoBM求视差:

import numpy as np import cv2 as cv from matplotlib import pyplot as plt imgL = cv.imread('tsukuba_l.png', cv.IMREAD_GRAYSCALE) imgR = cv.imread('tsukuba_r.png', cv.IMREAD_GRAYSCALE) stereo = cv.StereoBM.create(numDisparities=16, blockSize=15) disparity = stereo.compute(imgL,imgR) plt.imshow(disparity,'gray') plt.show()

结果如官方教程所示:左侧为原图、右侧为视差图(见 disparity_map.jpg),可以看到结果中混入了相当程度的噪声。官方教程指出,通过调整numDisparitiesblockSize可以改善结果。

5.3 关键参数详解

官方教程对StereoBM的一组可调参数给出了工程化解释(这些参数在算法初始化之后通过setTextureThresholdsetSpeckleRangesetUniquenessRatio等专用 setter/getter 设置,详见cv::StereoBM文档):

  • texture_threshold:过滤掉纹理不足、无法可靠匹配的区域;
  • Speckle range 与 speckle_size:基于块的匹配器常在物体边界附近产生"麻点"(matching window 一侧是前景、另一侧是背景);speckle_size规定视差斑块的像素数低于该值即视为麻点丢弃,speckle_range控制斑块内视差值的接近程度,二者共同构成视差后处理的麻点滤波;
  • numDisparities(视差数):窗口在水平方向滑动的像素数,越大可见深度范围越大,但计算量也越大;
  • min_disparity:从左像素 x 坐标开始搜索的偏移量;
  • uniqueness_ratio:另一种后过滤——若最佳视差不比搜索范围内其他视差明显更优,则过滤掉该像素;当texture_threshold和麻点滤波仍放入了伪匹配时可尝试调整;
  • prefilter_size 与 prefilter_cap:预处理阶段,在块匹配前对图像亮度做归一化、对纹理做增强,通常不需要调整。

章节练习部分还提示:OpenCV samples 中有一个生成视差图并做三维重建的示例,即 OpenCV-Python 示例(见 samples/python 目录)中的stereo_match.py,可配合本章内容一起阅读验证。

6. 小结:四个主题的衔接关系

回到 calib3d 教程目录 的脉络,四个子页实际上构成一条完整的"单目标定 → 空间理解 → 多视图重建"技术链:

  1. 相机标定(py_calibration.markdown):用findChessboardCorners+cornerSubPix收集点,calibrateCamera求解相机矩阵、5 个畸变系数与各帧外参,再用undistortinitUndistortRectifyMap+remap去畸变,并用projectPoints计算 RMSE 检验精度;
  2. 姿态估计(py_pose.markdown):复用标定结果,solvePnP/solvePnPRansac求平面图案的旋转平移,projectPoints把任意 3D 轴点投到图像上,实现坐标轴/立方体的 3D 叠加渲染;
  3. 极线几何(py_epipolar_geometry.markdown):SIFT+FLANN+比值检验获取匹配,findFundamentalMat(LMEDS/八点法)求基本矩阵,computeCorrespondEpilines求极线,直观验证极线约束;
  4. 立体深度图(py_depthmap.markdown):由disparity = Bf/Z的几何关系,用StereoBM求出视差并调优texture_thresholdspeckle_size/rangeuniqueness_ratio等参数得到干净的深度信息。

以上所有函数的 C++ 实现与测试均可在 modules/calib3d 目录中进一步查阅,演示图片与教程源文件位于 doc/py_tutorials/py_calib3d 下对应子目录,可作为复现实验时的对照资料。

【免费下载链接】opencvOpen Source Computer Vision Library项目地址: https://gitcode.com/GitHub_Trending/opencv31/opencv

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 4:52:48

告别低速爬行与振荡:台达ASD驱动DD马达调试要点全解析

简介&#xff1a;面向运动控制工程师与自动化调试人员的ASD驱动器简易调试手册&#xff0c;围绕ASD与DD马达联调场景&#xff0c;覆盖面板语言设置、软件中英文切换、USB通讯连接、电机参数配置与方向确认等完整流程。文档基于Akribis Motion Gallery界面&#xff0c;偏重实操路…

作者头像 李华
网站建设 2026/9/7 4:52:46

Vibe Coding实战:做好这四件事,AI编程才不会翻车

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 4:50:57

Karma离线安装包实战:内网前端测试环境部署全攻略

简介&#xff1a;这份离线安装包面向前端开发者与测试工程师&#xff0c;用于在无网络或网络受限的环境中快速搭建Karma测试框架&#xff0c;解决因依赖缺失导致测试环境难以初始化的问题。包内含Karma本体及qs、tinycolor、send、adm-zip、async、sigmund、bytes、deep-equal、…

作者头像 李华
网站建设 2026/9/7 4:50:17

STM32+4G模块接入阿里云物联网平台全流程详解

简介&#xff1a;这是一份基于STM32与有人LET-7S1 4G模块接入阿里云平台的完整工程资源&#xff0c;适合物联网开发者和嵌入式学习者参考。内容围绕透传模式下串口通信、阿里云物联网产品创建、设备凭证配置及SDK对接展开&#xff0c;覆盖从硬件接线、程序初始化到云端双向通信…

作者头像 李华