commit 39d606552158de83d20328754ebf3f4f13ce7790 Author: fly6516 Date: Fri May 23 11:43:17 2025 +0800 feat(camera): 添加相机标定与运动估计功能 - 新增 camera_calibration.py 文件,实现相机标定功能 - 新增 motion_estimation.py 文件,实现运动估计功能- 编写实验报告,总结相机标定与运动估计的原理和实验结果 diff --git a/camera_calibration.py b/camera_calibration.py new file mode 100644 index 0000000..2ad03ba --- /dev/null +++ b/camera_calibration.py @@ -0,0 +1,56 @@ +import cv2 +import numpy as np +import glob + +# 棋盘格尺寸:内角点数量(宽度,高度) +chessboard_size = (9, 6) + +# 创建棋盘格世界坐标系下的三维点阵 +# 生成形如(0,0,0), (1,0,0)...(8,5,0)的三维坐标 +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) + +# 存储对象点和图像点的列表 +objpoints = [] # 世界坐标系中的3D点 +imgpoints = [] # 图像坐标系中的2D点 + +# 加载所有棋盘格图像路径 +images = glob.glob('images/*.jpg') + +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) + imgpoints.append(corners) + + # 绘制并显示检测到的角点 + cv2.drawChessboardCorners(img, chessboard_size, corners, ret) + cv2.imshow('Corners', img) + cv2.waitKey(0) # 修改此处:改为等待用户按键关闭窗口 + +# 销毁所有OpenCV创建的窗口 +cv2.destroyAllWindows() + +if len(objpoints) > 0: + # 执行相机标定,获取相机矩阵、畸变系数等参数 + ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None) + + # 输出相机矩阵和畸变系数 + print("相机矩阵:\n", mtx) + print("畸变系数:\n", dist) + + # 计算重投影误差 + mean_error = 0 + for i in range(len(objpoints)): + imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist) + error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2) + mean_error += error + + print("平均重投影误差: ", mean_error / len(objpoints)) +else: + print("未找到有效的棋盘格图像。") \ No newline at end of file diff --git a/images/calib_1.jpg b/images/calib_1.jpg new file mode 100644 index 0000000..1f9112c Binary files /dev/null and b/images/calib_1.jpg differ diff --git a/images/left.jpg b/images/left.jpg new file mode 100644 index 0000000..9e0db2d Binary files /dev/null and b/images/left.jpg differ diff --git a/images/right.jpg b/images/right.jpg new file mode 100644 index 0000000..7d5fdae Binary files /dev/null and b/images/right.jpg differ diff --git a/motion_estimation.py b/motion_estimation.py new file mode 100644 index 0000000..9da0314 --- /dev/null +++ b/motion_estimation.py @@ -0,0 +1,55 @@ +import cv2 +import numpy as np + +# 读取左右视角图像(灰度模式) +img1 = cv2.imread('images/left.jpg', 0) +img2 = cv2.imread('images/right.jpg', 0) + +# 初始化SIFT特征检测器 +sift = cv2.SIFT_create() # 创建SIFT对象 + +# 检测关键点并计算描述符 +kp1, des1 = sift.detectAndCompute(img1, None) +kp2, des2 = sift.detectAndCompute(img2, None) + +# FLANN匹配器参数配置 +FLANN_INDEX_KDTREE = 1 +index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) # KD树算法参数 +search_params = dict(checks=50) # 搜索参数:检查次数 + +# 创建FLANN匹配器实例 +flann = cv2.FlannBasedMatcher(index_params, search_params) +matches = flann.knnMatch(des1, des2, k=2) # k近邻匹配 + +pts1 = [] +pts2 = [] +good_matches = [] # 初始化优质匹配列表 + +# 使用Lowe's比率测试筛选优质匹配 +for i, (m, n) in enumerate(matches): + if m.distance < 0.7 * n.distance: # 修改此处:调整距离比阈值 + good_matches.append(m) + pts2.append(kp2[m.trainIdx].pt) # 记录右图匹配点坐标 + pts1.append(kp1[m.queryIdx].pt) # 记录左图匹配点坐标 + +if len(good_matches) > 8: + pts1 = np.int32(pts1) + pts2 = np.int32(pts2) + + # 计算基础矩阵(使用RANSAC算法) + F, mask = cv2.findFundamentalMat(pts1, pts2, cv2.FM_RANSAC) + print("基础矩阵:\n", F) + + # 通过基础矩阵恢复旋转和平移变换 + _, R, t, _ = cv2.recoverPose(F, pts1, pts2) + print("旋转矩阵:\n", R) + print("平移向量:\n", t) + + # 可视化优质匹配结果 + img_match = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, + flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS) + cv2.imshow('特征匹配', img_match) + cv2.waitKey(0) + cv2.destroyAllWindows() +else: + print("匹配点不足,无法计算基础矩阵。") \ No newline at end of file diff --git a/实验报告_相机标定与运动估计.md b/实验报告_相机标定与运动估计.md new file mode 100644 index 0000000..5fac756 --- /dev/null +++ b/实验报告_相机标定与运动估计.md @@ -0,0 +1,43 @@ +# 计算机视觉实验报告:相机标定与运动估计 + +## 一、 实验目的 +1. 了解相机标定和运动估计的基本原理; +2. 了解相机标定和运动估计的应用; +3. 掌握相机标定和运动估计实现方法。 + +## 二、 基本思想 +### 相机标定 +相机标定旨在计算相机的内部参数(焦距、主点坐标等)和外部参数(位置和方向),以及校正镜头畸变。使用棋盘格图案作为标定物,通过已知的3D-2D对应关系来求解相机矩阵和畸变系数。 + +### 运动估计 +运动估计是通过分析连续图像帧之间的特征匹配,确定两个视图之间的相对旋转和平移变换。这通常涉及基础矩阵或本质矩阵的计算,从而恢复场景的三维结构和相机运动。 + +## 三、 源码 +### 相机标定代码 +```python +# 见camera_calibration.py文件 +``` + +### 运动估计代码 +```python +# 见motion_estimation.py文件 +``` + +## 四、 算法分析 +### 时间复杂度 +- **相机标定**:主要耗时在角点检测O(N)和线性优化迭代求解参数,整体近似为O(N·T),其中N为像素数,T为迭代次数。 +- **运动估计**:SIFT特征提取为O(M), 特征匹配为O(M²),RANSAC筛选为O(K)。总体时间复杂度约为O(M²+K)。 + +### 空间复杂度 +- **相机标定**:存储对象点和图像点的数组为O(P),P为角点数目。 +- **运动估计**:保存关键点和描述符的空间为O(Q), Q为特征数量。 + +## 五、 实验运行结果图 +### 相机标定角点检测示例 +![标定角点](results/calibration_corners.png) + +### 运动估计特征匹配图 +![特征匹配](results/feature_matching.jpg) + +## 六、 实验总结 +这里可以添加你的实验心得和遇到的问题及解决方案。 \ No newline at end of file