get_camera_params.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. import cv2
  2. import numpy as np
  3. import glob
  4. import scipy.io as sio
  5. import matplotlib.pyplot as plt
  6. from aprilgrid import Detector
  7. from .calibrate import generate_3d_points, show_detect_result
  8. def calibrate_world_aprilgrid(calibration_images_path, world_chessboard_size=(6,6), world_square_size=35.2, world_square_spacing=10.56, debug=False):
  9. # 初始化 AprilTag 检测器
  10. detector = Detector('t36h11')
  11. # 定义标定板的大小和标签大小
  12. # grid_size = (5, 6) # 标定板的行数和列数
  13. # tag_size = 25.3125 # 每个标签的边长(单位:毫米)
  14. object_point_single = generate_3d_points(world_chessboard_size, world_square_size, world_square_spacing)
  15. if 0:
  16. # 可视化3D点
  17. fig = plt.figure()
  18. ax = fig.add_subplot(111, projection='3d')
  19. # 提取并可视化每个标签的四个角点
  20. for tag in object_point_single:
  21. x_vals = tag[:, 0]
  22. y_vals = tag[:, 1]
  23. z_vals = tag[:, 2]
  24. # 绘制每个标签的四个角点连成的方形
  25. ax.plot(x_vals[[0, 1, 2, 3, 0]], y_vals[[0, 1, 2, 3, 0]], z_vals[[0, 1, 2, 3, 0]], marker='o')
  26. # 设置轴标签和标题
  27. ax.set_xlabel('X (mm)')
  28. ax.set_ylabel('Y (mm)')
  29. ax.set_zlabel('Z (mm)')
  30. ax.set_title('3D Visualization of AprilGrid Points')
  31. plt.show()
  32. # 用于保存所有图像的检测到的角点和 ID
  33. all_corners = []
  34. all_object_points = []
  35. image_size = None
  36. img_lst = []
  37. # 遍历每张图像
  38. for img_file in calibration_images_path:
  39. print('Processing:', img_file)
  40. # 从文件读取并解码图像
  41. image_data = np.fromfile(img_file, dtype=np.uint8)
  42. img = cv2.imdecode(image_data, cv2.IMREAD_COLOR)
  43. img_lst.append(img)
  44. if image_size is None:
  45. image_size = (img.shape[1], img.shape[0]) # 保存图像尺寸
  46. # 转换为灰度图像
  47. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  48. # 检测 AprilTags
  49. detections = detector.detect(gray)
  50. corners = detections[0].corners # 检测到的角点 (4, 2)
  51. tag_id = detections[0].tag_id # 标签 ID
  52. print(tag_id, corners)
  53. if debug:
  54. show_detect_result(img, detections)
  55. # 如果检测到标签
  56. if detections:
  57. image_points = []
  58. object_points = []
  59. # 遍历每个检测到的标签
  60. for detection in detections:
  61. corners = detection.corners # 检测到的角点 (4, 2)
  62. tag_id = detection.tag_id # 标签 ID
  63. print(tag_id, corners)
  64. # 将检测到的角点存储为 np.float32 格式
  65. image_points.append(corners.astype(np.float32))
  66. # 根据标签 ID 提取相应的 3D 坐标
  67. if tag_id < len(object_point_single):
  68. object_points.append(object_point_single[tag_id])
  69. else:
  70. print(f"Warning: Tag ID {tag_id} is out of range.")
  71. if object_points and image_points:
  72. # 保存当前图像的 2D 角点
  73. all_corners.append(np.array(image_points, dtype=np.float32).reshape(-1, 2)) # 转为 (N, 2) 形状
  74. # 保存对应的 3D 坐标
  75. all_object_points.append(np.array(object_points, dtype=np.float32).reshape(-1, 3)) # 转为 (N, 3) 形状
  76. else:
  77. print("No valid detections in this image.")
  78. # 调用相机标定
  79. ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
  80. all_object_points, # 3D 物体点
  81. all_corners, # 2D 图像点
  82. image_size, # 图像大小
  83. None, # 相机内参初始值
  84. None # 畸变系数初始值
  85. )
  86. # 计算重投影误差
  87. total_error = 0
  88. for i in range(len(all_object_points)):
  89. imgpoints2, _ = cv2.projectPoints(all_object_points[i], rvecs[i], tvecs[i], mtx, dist)
  90. # 将 imgpoints2 从 (N, 1, 2) 转换为 (N, 2)
  91. imgpoints2 = np.squeeze(imgpoints2)
  92. # 确保 all_corners[i] 和 imgpoints2 类型匹配
  93. error = cv2.norm(all_corners[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
  94. total_error += error
  95. if debug:
  96. for (x, y), (px, py) in zip(all_corners[i], np.squeeze(imgpoints2)):
  97. cv2.circle(img_lst[i], (int(x), int(y)), 5, (0, 255, 0), -1) # Original points in green
  98. cv2.circle(img_lst[i], (int(px), int(py)), 5, (0, 0, 255), -1) # Reprojected points in red
  99. cv2.namedWindow('Error Visualization', 0)
  100. cv2.imshow('Error Visualization', img_lst[i])
  101. cv2.waitKey(0)
  102. mean_error = total_error / len(all_object_points)
  103. #print(f"Mean re-projection error: {mean_error}")
  104. print('rvecs[0] = ', rvecs[0])
  105. #print('rvecs[0] = ', (rvecs[0]))
  106. # 获得第一张图片的旋转矩阵和平移向量
  107. R = cv2.Rodrigues(rvecs[0])[0] # 转换为旋转矩阵
  108. T = tvecs[0]
  109. # 创建一个字典来保存所有的标定数据
  110. calibration_data = {
  111. 'camera_matrix': mtx,
  112. 'distortion_coefficients': dist,
  113. 'rotation_matrix': R,
  114. 'translation_vector': T,
  115. 'error': ret/len(all_object_points)
  116. }
  117. # # 创建一个字典来保存所有的标定数据
  118. # calibration_data = {
  119. # 'camera_matrix': mtx,
  120. # 'distortion_coefficients': dist,
  121. # 'rotation_matrix': rvecs,
  122. # 'translation_vector': tvecs,
  123. # 'mean_reprojection_error': mean_error,
  124. # 'error':ret/len(all_object_points)
  125. # }
  126. print(calibration_data)
  127. return calibration_data
  128. def calibrate_world(world_img_path, chessboard_size, square_size, debug=False):
  129. # 准备棋盘格的世界坐标
  130. objp = np.zeros((chessboard_size[0] * chessboard_size[1], 3), np.float32)
  131. objp[:, :2] = np.mgrid[0:chessboard_size[0], 0:chessboard_size[1]].T.reshape(-1, 2)
  132. objp *= square_size
  133. # 储存棋盘格角点的世界坐标和图像坐标
  134. objpoints = [] # 世界坐标系中的三维点
  135. imgpoints = [] # 图像平面的二维点
  136. for fname in world_img_path:
  137. img = cv2.imread(fname)
  138. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  139. # 寻找棋盘格角点
  140. #ret, corners = cv2.findChessboardCorners(gray, chessboard_size, None)
  141. flag = cv2.CALIB_CB_EXHAUSTIVE | cv2.CALIB_CB_ACCURACY
  142. ret, corners = cv2.findChessboardCornersSB(gray, chessboard_size, None)
  143. # 如果找到足够的角点,添加到列表中
  144. if ret:
  145. objpoints.append(objp)
  146. criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
  147. corners_refined = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
  148. imgpoints.append(corners_refined)
  149. if debug:
  150. img = cv2.drawChessboardCorners(img, chessboard_size, corners, ret)
  151. # 在角点图像上标记坐标原点(第一个角点)
  152. origin = tuple(corners[0].ravel().astype(int))
  153. cv2.putText(img, '(0,0)', origin, cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
  154. # 添加 x 轴箭头 (右方向)
  155. end_x = tuple(corners[1].ravel().astype(int)) # 选择横向的下一个角点
  156. cv2.arrowedLine(img, origin, end_x, (255, 255, 255), 2, tipLength=0.2)
  157. cv2.putText(img, 'X', (end_x[0] + 10, end_x[1]), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
  158. # 添加 y 轴箭头 (下方向)
  159. end_y = tuple(corners[chessboard_size[0]].ravel().astype(int)) # 选择纵向的下一个角点
  160. cv2.arrowedLine(img, origin, end_y, (255, 255, 255), 2, tipLength=0.2)
  161. cv2.putText(img, 'Y', (end_y[0], end_y[1] + 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
  162. cv2.namedWindow('img', 0)
  163. cv2.imshow('img', img)
  164. cv2.waitKey(100)
  165. cv2.destroyAllWindows()
  166. objpoints = np.array(objpoints)
  167. imgpoints = np.array(imgpoints)
  168. if debug:
  169. fig, ax = plt.subplots(1, 2, figsize=(12, 6))
  170. # 第一个子图:objp 点
  171. ax[0].scatter(objpoints[-1, :, 0], objpoints[-1, :, 1], color='blue', label='objp (Object Points)')
  172. for i in range(len(objpoints[0])):
  173. ax[0].text(objpoints[-1, i, 0], objpoints[-1, i, 1], f'{i}', color='blue', fontsize=12)
  174. ax[0].set_title('Object Points (objp)')
  175. ax[0].set_xlabel('X Axis')
  176. ax[0].set_ylabel('Y Axis')
  177. ax[0].grid(True)
  178. ax[0].axis('equal')
  179. # 第二个子图:corners 点
  180. ax[1].scatter(imgpoints[-1, :, 0, 0], imgpoints[-1, :, 0, 1], color='red', label='corners (Image Points)')
  181. for i in range(len(imgpoints[0])):
  182. ax[1].text(imgpoints[-1, i, 0, 0], imgpoints[-1, i, 0, 1], f'{i}', color='red', fontsize=12)
  183. ax[1].set_title('Camera Image Points (corners)')
  184. ax[1].set_xlabel('X')
  185. ax[1].set_ylabel('Y')
  186. ax[1].grid(True)
  187. ax[1].axis('equal')
  188. #plt.show()
  189. # 相机标定
  190. ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
  191. # 获得最后一张图片的旋转矩阵和平移向量
  192. R_vector = rvecs[-1]
  193. R = cv2.Rodrigues(R_vector)[0] # 转换为旋转矩阵
  194. T = tvecs[-1]
  195. # 创建一个字典来保存所有的标定数据
  196. calibration_data = {
  197. 'camera_matrix': mtx,
  198. 'distortion_coefficients': dist,
  199. 'rotation_matrix': R,
  200. 'rotation_vector': R_vector,
  201. 'translation_vector': T,
  202. 'error': ret/len(objpoints)
  203. }
  204. return calibration_data