一个计算reprojection error的小程序

该程序提供了一个计算重投影误差的函数,输入包括RGB图像、对象点、图像点、校准矩阵、旋转矩阵和平移向量。通过矩阵运算和图像处理,它计算并显示了每个点的重投影误差,最终返回平均重投影误差。
double reprojectionErrorCalculation(IplImage *rgbImage, CvMat *objectPoints, CvMat *imagePoints, CvMat *calibrationMatrix, CvMat *rotationMatrix, CvMat *translationVector){
	   IplImage* img=cvCreateImage(cvSize(rgbImage->width,rgbImage->height),rgbImage->depth,rgbImage->nChannels);
	   cvCopy(rgbImage,img);
	   int numPoints=objectPoints->height;
	   CvMat* translationMatrix=cvCreateMat(3,numPoints,CV_32FC1);
	   for(int i=0;i<3;i++){
		   float* ptr=(float*)(translationMatrix->data.ptr+i*translationMatrix->step);
		   float* ptrTranslation=(float*)(translationVector->data.ptr+i*translationVector->step);
		   for(int j=0;j<numPoints;j++){
		       *(ptr+j)=*ptrTranslation;
		   }
	   }

	   CvMat* objectPointsTranspose=cvCreateMat(3,numPoints,CV_32FC1);
	   cvTranspose(objectPoints,objectPointsTranspose);
	   CvMat* imagePointsHomogeneous=cvCreateMat(3,numPoints,CV_32FC1);
	   cvMatMulAdd(rotationMatrix,objectPointsTrans
import numpy as np import cv2 import os from matplotlib import pyplot as plt import matplotlib # 设置中文字体支持 def setup_chinese_font(): """设置中文字体支持,避免显示方块""" try: # 方法1: 使用系统自带的中文字体 plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans'] plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题 # 方法2: 如果上述方法不行,尝试使用matplotlib的字体管理器 from matplotlib.font_manager import FontProperties chinese_font = None # 尝试常见的中文字体路径 font_paths = [ 'C:/Windows/Fonts/simhei.ttf', # 黑体 'C:/Windows/Fonts/msyh.ttc', # 微软雅黑 'C:/Windows/Fonts/simsun.ttc', # 宋体 ] for font_path in font_paths: if os.path.exists(font_path): chinese_font = FontProperties(fname=font_path) matplotlib.rcParams['font.family'] = chinese_font.get_name() break # 如果找不到中文字体,使用默认字体但避免中文 if chinese_font is None: print("警告: 未找到中文字体,将使用英文显示") return False return True except Exception as e: print(f"字体设置错误: {e}") return False # 在程序开始处调用字体设置 chinese_supported = setup_chinese_font() class ImprovedCameraCalibration: def __init__(self, pattern_size=(6, 4), square_size=0.025): # 核心修改:内角点改为6×4 """ 改进的相机标定类,包含多种角点检测方法 """ self.pattern_size = pattern_size # 6列(x方向)×4行(y方向)内角点 self.square_size = square_size self.objpoints = [] # 3D世界坐标点 self.imgpoints = [] # 2D图像坐标点 self.camera_matrix = None # 相机内参矩阵 self.dist_coeffs = None # 畸变系数 self.rvecs = None # 旋转向量 self.tvecs = None # 平移向量 self.reprojection_error = None # 重投影误差 self.errors_per_image = [] # 每张图像的重投影误差 self.detection_methods_used = [] # 记录每张图像使用的检测方法 def prepare_object_points(self): """准备世界坐标系中的棋盘格角点坐标""" # 核心逻辑不变:生成(6*4, 3)的3D坐标,x方向6个点,y方向4个点 objp = np.zeros((self.pattern_size[0] * self.pattern_size[1], 3), np.float32) objp[:, :2] = np.mgrid[0:self.pattern_size[0], 0:self.pattern_size[1]].T.reshape(-1, 2) objp *= self.square_size return objp def improved_find_chessboard_corners(self, image_path, show_corners=False): """ 改进的角点检测方法,尝试多种预处理技术 返回: (是否成功, 角点坐标, 使用的方法名称) """ # 读取图像 img = cv2.imread(image_path) if img is None: return False, None, "Cannot read image" gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) height, width = gray.shape # 定义不同的预处理方法和参数 methods = [ { "name": "Standard detection", "image": gray, "flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE }, { "name": "Gaussian blur (3x3)", "image": cv2.GaussianBlur(gray, (3, 3), 0), "flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE }, { "name": "Gaussian blur (5x5)", "image": cv2.GaussianBlur(gray, (5, 5), 0), "flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE }, { "name": "Histogram equalization", "image": cv2.equalizeHist(gray), "flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE }, { "name": "Median filter", "image": cv2.medianBlur(gray, 3), "flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE }, { "name": "Bilateral filter", "image": cv2.bilateralFilter(gray, 9, 75, 75), "flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE } ] # 尝试不同的检测标志组合 flag_combinations = [ cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE, cv2.CALIB_CB_FAST_CHECK, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FILTER_QUADS ] # 首先尝试标准方法(使用修改后的pattern_size=6×4) for method in methods: for flags in flag_combinations: ret, corners = cv2.findChessboardCorners(method["image"], self.pattern_size, flags=flags) if ret: # 亚像素级优化 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners_refined = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) # 验证角点质量:检查角点是否在合理范围内 corners_flat = corners_refined.reshape(-1, 2) x_coords = corners_flat[:, 0] y_coords = corners_flat[:, 1] # 确保角点不会太靠近图像边缘 margin = 20 if (np.min(x_coords) > margin and np.max(x_coords) < width - margin and np.min(y_coords) > margin and np.max(y_coords) < height - margin): if show_corners: img_draw = cv2.drawChessboardCorners(img.copy(), self.pattern_size, corners_refined, ret) plt.figure(figsize=(10, 8)) plt.imshow(cv2.cvtColor(img_draw, cv2.COLOR_BGR2RGB)) # 根据字体支持情况设置标题 if chinese_supported: plt.title(f'检测方法: {method["name"]} - {os.path.basename(image_path)}') else: plt.title(f'Method: {method["name"]} - {os.path.basename(image_path)}') plt.axis('off') plt.show() return True, corners_refined, method["name"] # 如果标准方法都失败,尝试更宽松的条件(使用修改后的pattern_size=6×4) for method in methods: ret, corners = cv2.findChessboardCorners(method["image"], self.pattern_size, flags=cv2.CALIB_CB_ADAPTIVE_THRESH) if ret: criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) corners_refined = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) if show_corners: img_draw = cv2.drawChessboardCorners(img.copy(), self.pattern_size, corners_refined, ret) plt.figure(figsize=(10, 8)) plt.imshow(cv2.cvtColor(img_draw, cv2.COLOR_BGR2RGB)) # 根据字体支持情况设置标题 if chinese_supported: plt.title(f'检测方法: {method["name"]} (宽松条件) - {os.path.basename(image_path)}') else: plt.title(f'Method: {method["name"]} (Loose condition) - {os.path.basename(image_path)}') plt.axis('off') plt.show() return True, corners_refined, method["name"] + " (Loose condition)" return False, None, "All methods failed" def find_chessboard_corners(self, image_paths, show_corners=False): """检测所有图像中的棋盘格角点(使用改进的方法)""" objp = self.prepare_object_points() if chinese_supported: print("开始检测棋盘格角点(使用改进方法)...") print(f"使用内角点配置: {self.pattern_size[0]} × {self.pattern_size[1]}") # 显示修改后的6×4 else: print("Starting chessboard corner detection (improved method)...") print(f"Using pattern size: {self.pattern_size[0]} × {self.pattern_size[1]}") # 显示修改后的6×4 valid_images = 0 for i, image_path in enumerate(image_paths): print(f"Processing image {i+1}/{len(image_paths)}: {os.path.basename(image_path)}") # 使用改进的角点检测方法(依赖修改后的pattern_size) success, corners, method_used = self.improved_find_chessboard_corners(image_path, show_corners) if success: self.objpoints.append(objp) self.imgpoints.append(corners) self.detection_methods_used.append(method_used) valid_images += 1 print(f" ✓ Successfully found corners (Method: {method_used})") else: print(f" ✗ Failed to find corners") if chinese_supported: print(f"成功检测到 {valid_images}/{len(image_paths)} 张图像的角点") else: print(f"Successfully detected corners in {valid_images}/{len(image_paths)} images") # 打印使用的检测方法统计 if valid_images > 0: if chinese_supported: print("\n检测方法统计:") else: print("\nDetection method statistics:") method_counts = {} for method in self.detection_methods_used: method_counts[method] = method_counts.get(method, 0) + 1 for method, count in method_counts.items(): print(f" {method}: {count} images") return valid_images > 0 def calibrate_camera(self, image_size): """进行相机标定""" if len(self.objpoints) < 3: if chinese_supported: print("错误: 需要至少3张有效图像进行标定") else: print("Error: Need at least 3 valid images for calibration") return False if chinese_supported: print("开始相机标定...") else: print("Starting camera calibration...") # 相机标定(使用修改后的objpoints/imgpoints,对应6×4角点) ret, self.camera_matrix, self.dist_coeffs, self.rvecs, self.tvecs = cv2.calibrateCamera( self.objpoints, self.imgpoints, image_size, None, None) if ret: self.calculate_reprojection_error() return True else: if chinese_supported: print("错误: 相机标定失败") else: print("Error: Camera calibration failed") return False def calculate_reprojection_error(self): """计算重投影误差(逻辑不变,自动适配6×4角点数量)""" total_error = 0 self.errors_per_image = [] for i in range(len(self.objpoints)): # 将3D点投影到2D图像平面 imgpoints2, _ = cv2.projectPoints(self.objpoints[i], self.rvecs[i], self.tvecs[i], self.camera_matrix, self.dist_coeffs) # 计算误差(6×4=24个角点,自动按实际数量计算error = cv2.norm(self.imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2) self.errors_per_image.append(error) total_error += error self.reprojection_error = total_error / len(self.objpoints) return self.reprojection_error def evaluate_calibration_quality(self): """评估标定质量(逻辑不变)""" error = self.reprojection_error if chinese_supported: print(f"\n标定质量评估:") print(f"重投影误差: {error:.6f} 像素") if error < 0.1: print("质量评级: 优秀 - 标定结果非常精确") elif error < 0.3: print("质量评级: 良好 - 标定结果可用于大多数应用") elif error < 0.5: print("质量评级: 可接受 - 标定结果基本可用") else: print("质量评级: 需要改进 - 建议检查图像质量或增加标定图像数量") else: print(f"\nCalibration quality assessment:") print(f"Reprojection error: {error:.6f} pixels") if error < 0.1: print("Quality rating: Excellent - Very accurate calibration") elif error < 0.3: print("Quality rating: Good - Suitable for most applications") elif error < 0.5: print("Quality rating: Acceptable - Basically usable") else: print("Quality rating: Needs improvement - Check image quality or add more images") return error def print_results(self): """打印标定结果(逻辑不变)""" if self.camera_matrix is None: if chinese_supported: print("错误: 请先进行相机标定") else: print("Error: Please calibrate the camera first") return print("\n" + "="*60) if chinese_supported: print("相机标定结果分析") else: print("Camera Calibration Results Analysis") print("="*60) if chinese_supported: print(f"\n1. 相机内参矩阵 (Camera Matrix):") print("[[fx, 0, cx],") print(" [ 0, fy, cy],") print(" [ 0, 0, 1]]") print(f"fx (x轴焦距): {self.camera_matrix[0,0]:.2f} 像素") print(f"fy (y轴焦距): {self.camera_matrix[1,1]:.2f} 像素") print(f"cx (主点x坐标): {self.camera_matrix[0,2]:.2f} 像素") print(f"cy (主点y坐标): {self.camera_matrix[1,2]:.2f} 像素") print(f"\n完整矩阵:") else: print(f"\n1. Camera Intrinsic Matrix:") print("[[fx, 0, cx],") print(" [ 0, fy, cy],") print(" [ 0, 0, 1]]") print(f"fx (focal length x): {self.camera_matrix[0,0]:.2f} pixels") print(f"fy (focal length y): {self.camera_matrix[1,1]:.2f} pixels") print(f"cx (principal point x): {self.camera_matrix[0,2]:.2f} pixels") print(f"cy (principal point y): {self.camera_matrix[1,2]:.2f} pixels") print(f"\nFull matrix:") print(self.camera_matrix) if chinese_supported: print(f"\n2. 畸变系数 (Distortion Coefficients):") print(f"k1 (径向畸变1): {self.dist_coeffs[0,0]:.6f}") print(f"k2 (径向畸变2): {self.dist_coeffs[0,1]:.6f}") print(f"p1 (切向畸变1): {self.dist_coeffs[0,2]:.6f}") print(f"p2 (切向畸变2): {self.dist_coeffs[0,3]:.6f}") else: print(f"\n2. Distortion Coefficients:") print(f"k1 (radial distortion 1): {self.dist_coeffs[0,0]:.6f}") print(f"k2 (radial distortion 2): {self.dist_coeffs[0,1]:.6f}") print(f"p1 (tangential distortion 1): {self.dist_coeffs[0,2]:.6f}") print(f"p2 (tangential distortion 2): {self.dist_coeffs[0,3]:.6f}") if len(self.dist_coeffs[0]) > 4: if chinese_supported: print(f"k3 (径向畸变3): {self.dist_coeffs[0,4]:.6f}") else: print(f"k3 (radial distortion 3): {self.dist_coeffs[0,4]:.6f}") print(f"\n3. 重投影误差分析:" if chinese_supported else "\n3. Reprojection Error Analysis:") self.evaluate_calibration_quality() if chinese_supported: print(f"\n4. 各图像重投影误差 (前10张):") else: print(f"\n4. Reprojection Error per Image (first 10):") # 只显示前10张图像的误差,避免输出过长 for i, error in enumerate(self.errors_per_image[:10]): method = self.detection_methods_used[i] if chinese_supported: print(f" 图像 {i+1}: {error:.6f} 像素 (检测方法: {method})") else: print(f" Image {i+1}: {error:.6f} pixels (Method: {method})") # 显示误差统计信息 if len(self.errors_per_image) > 10: if chinese_supported: print(f" ... (共{len(self.errors_per_image)}张图像)") print(f" 最大误差: {max(self.errors_per_image):.6f} 像素") print(f" 最小误差: {min(self.errors_per_image):.6f} 像素") print(f" 误差标准差: {np.std(self.errors_per_image):.6f} 像素") else: print(f" ... (total {len(self.errors_per_image)} images)") print(f" Max error: {max(self.errors_per_image):.6f} pixels") print(f" Min error: {min(self.errors_per_image):.6f} pixels") print(f" Error std: {np.std(self.errors_per_image):.6f} pixels") if chinese_supported: print(f"\n5. 外参示例 (第一张图像):") else: print(f"\n5. Extrinsic Parameters Example (First Image):") # 将旋转向量转换为旋转矩阵 rmat, _ = cv2.Rodrigues(self.rvecs[0]) if chinese_supported: print(f"旋转矩阵:") else: print(f"Rotation Matrix:") print(rmat) if chinese_supported: print(f"平移向量 (米): {self.tvecs[0].flatten()}") else: print(f"Translation Vector (meters): {self.tvecs[0].flatten()}") def visualize_results(self, image_paths): """可视化标定结果(使用修改后的pattern_size绘制角点)""" if self.camera_matrix is None: if chinese_supported: print("错误: 请先进行相机标定") else: print("Error: Please calibrate the camera first") return # 选择前3张图像进行可视化 num_show = min(3, len(image_paths)) fig, axes = plt.subplots(2, num_show, figsize=(5*num_show, 10)) if num_show == 1: axes = axes.reshape(2, 1) for i in range(num_show): img = cv2.imread(image_paths[i]) if img is None: continue # 去畸变 undistorted_img = cv2.undistort(img, self.camera_matrix, self.dist_coeffs) # 绘制角点重投影(使用修改后的pattern_size) imgpoints2, _ = cv2.projectPoints(self.objpoints[i], self.rvecs[i], self.tvecs[i], self.camera_matrix, self.dist_coeffs) # 原始图像(绘制6×4角点) img_with_corners = cv2.drawChessboardCorners(img.copy(), self.pattern_size, self.imgpoints[i], True) axes[0, i].imshow(cv2.cvtColor(img_with_corners, cv2.COLOR_BGR2RGB)) if chinese_supported: axes[0, i].set_title(f'原始图像 {i+1}\n误差: {self.errors_per_image[i]:.4f}px\n方法: {self.detection_methods_used[i]}') else: axes[0, i].set_title(f'Original Image {i+1}\nError: {self.errors_per_image[i]:.4f}px\nMethod: {self.detection_methods_used[i]}') axes[0, i].axis('off') # 去畸变图像 axes[1, i].imshow(cv2.cvtColor(undistorted_img, cv2.COLOR_BGR2RGB)) if chinese_supported: axes[1, i].set_title(f'去畸变后 {i+1}') else: axes[1, i].set_title(f'Undistorted {i+1}') axes[1, i].axis('off') plt.tight_layout() plt.show() # 绘制误差分布图(逻辑不变) plt.figure(figsize=(12, 6)) x_pos = range(1, len(self.errors_per_image)+1) bars = plt.bar(x_pos, self.errors_per_image) # 为不同检测方法使用不同颜色 method_colors = {} colors = ['blue', 'green', 'red', 'orange', 'purple', 'brown'] for i, method in enumerate(self.detection_methods_used): if method not in method_colors: method_colors[method] = colors[len(method_colors) % len(colors)] bars[i].set_color(method_colors[method]) plt.axhline(y=self.reprojection_error, color='r', linestyle='--', label=f'Average error: {self.reprojection_error:.4f}px') plt.xlabel('Image Number') plt.ylabel('Reprojection Error (pixels)') if chinese_supported: plt.title('各图像重投影误差分布 (颜色表示检测方法)') else: plt.title('Reprojection Error Distribution (Color indicates detection method)') # 添加图例 legend_handles = [] for method, color in method_colors.items(): legend_handles.append(plt.Rectangle((0,0),1,1, color=color, label=method)) legend_handles.append(plt.Line2D([0],[0], color='r', linestyle='--', label=f'Average error')) plt.legend(handles=legend_handles) plt.grid(True, alpha=0.3) plt.show() def main(): # 修改为新的数据集路径:从1.jpg到60.jpg image_dir = r"F:\JIQISHIJUE\13" image_paths = [] # 生成从1到60的图像路径 for i in range(1, 61): filename = f"{i}.jpg" full_path = os.path.join(image_dir, filename) image_paths.append(full_path) # 检查图像文件是否存在 existing_paths = [] for path in image_paths: if os.path.exists(path): existing_paths.append(path) else: print(f"Warning: Image file does not exist: {path}") if not existing_paths: print(f"Error: No image files found") return print(f"Found {len(existing_paths)} images:") # 只显示前10个文件路径,避免输出过长 for path in existing_paths[:10]: print(f" {os.path.basename(path)}") if len(existing_paths) > 10: print(f" ... (and {len(existing_paths)-10} more images)") # 创建改进的标定对象,核心修改:内角点设置为6×4 calibrator = ImprovedCameraCalibration(pattern_size=(6, 4), square_size=0.025) # 检测角点(设置show_corners=False避免显示过多图像) success = calibrator.find_chessboard_corners(existing_paths, show_corners=False) if not success: if chinese_supported: print("错误: 角点检测失败") print("可能的原因:") print("1. 棋盘格内角点数量设置不正确") print("2. 图像质量不佳") print("3. 棋盘格在图像中不完整或被遮挡") print("4. 棋盘格太小或太大") else: print("Error: Corner detection failed") print("Possible reasons:") print("1. Incorrect pattern size setting") print("2. Poor image quality") print("3. Chessboard not fully visible or occluded") print("4. Chessboard too small or too large") return # 获取图像尺寸 sample_img = cv2.imread(existing_paths[0]) image_size = (sample_img.shape[1], sample_img.shape[0]) print(f"Image size: {image_size}") # 进行相机标定 success = calibrator.calibrate_camera(image_size) if success: # 打印详细结果 calibrator.print_results() # 可视化结果 calibrator.visualize_results(existing_paths) # 保存标定结果(文件名添加6x4标识,避免与原结果混淆) np.savez('camera_calibration_6x4_results.npz', camera_matrix=calibrator.camera_matrix, dist_coeffs=calibrator.dist_coeffs, reprojection_error=calibrator.reprojection_error, image_size=image_size, detection_methods=calibrator.detection_methods_used) if chinese_supported: print(f"\n标定结果已保存到 'camera_calibration_6x4_results.npz'") else: print(f"\nCalibration results saved to 'camera_calibration_6x4_results.npz'") # 保存可用于OpenCV的YAML文件(文件名添加6x4标识) fs = cv2.FileStorage("camera_calibration_6x4.yml", cv2.FILE_STORAGE_WRITE) fs.write("camera_matrix", calibrator.camera_matrix) fs.write("distortion_coefficients", calibrator.dist_coeffs) fs.write("reprojection_error", calibrator.reprojection_error) fs.write("image_width", image_size[0]) fs.write("image_height", image_size[1]) fs.release() if chinese_supported: print(f"标定结果已保存到 'camera_calibration_6x4.yml'") else: print(f"Calibration results saved to 'camera_calibration_6x4.yml'") else: if chinese_supported: print("相机标定失败") else: print("Camera calibration failed") if __name__ == "__main__": main()把这里面的文件地址修改为"D:\jupyter notebook\homework3\images\13"
最新发布
09-26
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值