当前位置:   article > 正文

避坑—相机标定_相机标定csdn

相机标定csdn

本文采用张氏标定法标定了一个USB相机(网购,一百多块钱),记录了辛酸历程。

废话不多说,直接开始标定!

第一步:拍十几张棋盘格图片

棋盘格是从网上找的图片打印的,整体还是个歪的......

 这一步就有坑,后面解释

 第二步:标定相机内参

直接上代码

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import cv2
  4. import numpy as np
  5. import glob
  6. criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
  7. # 棋盘格对应的世界坐标下的三维坐标
  8. # xx = [371., 396., 421., 446., 471., 496.]
  9. # yy = [402., 427., 452., 477., 502., 527., 552., 577.]
  10. # 这两种坐标得到的内参一样
  11. xx = [0., 1., 2., 3., 4., 5.]
  12. yy = [0., 1., 2., 3., 4., 5., 6., 7.]
  13. xyz = []
  14. for i in range(len(xx)):
  15. for j in range(len(yy)):
  16. xyz.append(np.array([xx[i]*25, yy[j]*25, 0.], dtype=np.float32))
  17. xyz = np.array(xyz, dtype=np.float32)
  18. print(objp)
  19. objppoints = [] # 真实世界下的三维坐标
  20. imgpoints = []
  21. images = glob.glob(r'./chess/*.JPG')
  22. for fname in images:
  23. img = cv2.imread(fname)
  24. print(fname)
  25. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  26. # print(gray.shape)
  27. ret, corners = cv2.findChessboardCorners(gray, (8, 6), None) # 寻找棋盘内角点
  28. # print('corners', corners)
  29. if ret == True:
  30. objppoints.append(objp)
  31. corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) # 提取亚像素信息,提高标定精度
  32. print(corners2)
  33. imgpoints.append(corners2)
  34. img = cv2.drawChessboardCorners(img, (8, 6), corners2, ret)
  35. cv2.imshow('gray', img)
  36. while cv2.waitKey(100) != 27: # esc
  37. if cv2.getWindowProperty('gray', cv2.WND_PROP_AUTOSIZE) < 0: # 获取窗口状态,如果窗口关闭 执行break
  38. break
  39. cv2.destroyAllWindows()
  40. # mrx 内参数矩阵,dist 畸变系数,rvecs 旋转向量,tvecs 平移向量
  41. ret, mrx, dist, rvecs, tveces = cv2.calibrateCamera(objppoints, imgpoints, (1920, 1080), None, None) # 相机标定
  42. print(mrx)
  43. print(dist)
  44. newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mrx, dist, (1920, 1080), 1) # 矫正畸变,优化内参
  45. print(roi)
  46. total_error = 0
  47. for i in range(len(objppoints)):
  48. imgpoints2, _ = cv2.projectPoints(objppoints[i], rvecs[i], tveces[i], mrx, dist) # 映射
  49. error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
  50. total_error += error
  51. print("total error: ", total_error / len(objppoints))
  52. # 保存内参、畸变参数、新内参
  53. np.savez('c2_parameter_1080', k=mrx, dist=dist, newK=newcameramtx)

 代码是没有问题的,运行的结果如下

贴两张图意思一下,其他图也是这样的。

然后程序会输出内参、畸变参数,我的外参是单独标定的。

第三步:图像去畸变

先上代码

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import cv2
  4. import numpy as np
  5. from tqdm import tqdm
  6. import os
  7. npzfile = np.load('./calibrateCamera/c2_parameter_1080.npz')
  8. K = npzfile['k']
  9. dist = npzfile['dist']
  10. newcameramtx=npzfile['newK']
  11. print(npzfile['k'])
  12. print(npzfile['dist'])
  13. img_root=r'./photos_camera3'
  14. save_root=r'./undistort_photos'
  15. img_list=os.listdir(img_root)
  16. for name in tqdm(img_list):
  17. img_path=os.path.join(img_root,name)
  18. img = cv2.imread(img_path)
  19. dst = cv2.undistort(img, K, dist, None, newcameramtx)
  20. cv2.imwrite(os.path.join(save_root,name), dst)

填坑来了

第一步、第二步从拍摄到跑代码,看着都很流畅,大家也都是这么做的,本来我也没太注意,直到用标定好的内参给图像去畸变。结果保存出来的是个这鬼样子

 

 

这根本就不能用好吧!然后心里就开始默默的问候代码的祖宗,

开始解决问题

首先我是查看了保存的内参和优化后的内参,发现优化后的内参的cx和cy有些大的离谱。

然后又发现在优化内参时 返回的roi是(0,0,0,0)!!!

代码都知道图像里没一个像素是能用的。

现在就基本锁定了是内参的问题,然后重新拍数据。经过多批数据的拍摄总结出来的教训。

我们都知道相机的照片越靠近边缘,畸变程度越大,所以拍摄的时候要尽量把棋盘格放到边上去。

然后得到的结果就和预期的一样了。

 第四步:定外参

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import cv2
  4. import numpy as np
  5. import math
  6. def rotation_vector_to_euler_angles(rvec):
  7. # 将旋转向量转换成旋转矩阵
  8. R, _ = cv2.Rodrigues(rvec)
  9. # 计算欧拉角(yaw、pitch、roll)
  10. yaw = np.arctan2(R[1, 0], R[0, 0])
  11. pitch = np.arctan2(-R[2, 0], np.sqrt(R[2, 1] ** 2 + R[2, 2] ** 2))
  12. roll = np.arctan2(R[2, 1], R[2, 2])
  13. # 将弧度转换为角度
  14. yaw = np.rad2deg(yaw)
  15. pitch = np.rad2deg(pitch)
  16. roll = np.rad2deg(roll)
  17. return yaw, pitch, roll # z, y, x
  18. # 旋转矩阵到欧拉角(角度制)
  19. def rotateMatrixToEulerAngles2(rvec):
  20. RM, _ = cv2.Rodrigues(rvec)
  21. theta_z = np.arctan2(RM[1, 0], RM[0, 0]) / np.pi * 180
  22. theta_y = np.arctan2(-1 * RM[2, 0], np.sqrt(RM[2, 1] * RM[2, 1] + RM[2, 2] * RM[2, 2])) / np.pi * 180
  23. theta_x = np.arctan2(RM[2, 1], RM[2, 2]) / np.pi * 180
  24. print(f"Euler angles:\ntheta_x: {theta_x}\ntheta_y: {theta_y}\ntheta_z: {theta_z}")
  25. return theta_x, theta_y, theta_z
  26. npzfile = np.load('c3_parameter_1080.npz')
  27. cameraMatrix = npzfile['k']
  28. distCoeffs = npzfile['dist']
  29. print(npzfile['k'])
  30. print(npzfile['dist'])
  31. criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
  32. objppoints = [] # 真实世界下的三维坐标
  33. imgpoints = []
  34. # 拿尺子量的真实坐标
  35. xx = [406., 431., 456., 481., 506., 531.]
  36. yy = [305., 330., 355., 380., 405., 430., 455., 480.]
  37. xyz = []
  38. for i in range(len(xx)):
  39. for j in range(len(yy)):
  40. xyz.append(np.array([xx[i], yy[j], 0.], dtype=np.float32))
  41. xyz = np.array(xyz, dtype=np.float32)
  42. objp = xyz
  43. objp = xyz[::-1]
  44. print(objp)
  45. img = cv2.imread(r'D:\gzz\data\monkey_hand\20230523three\camera3\Rt/1.jpg')
  46. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  47. ret, corners = cv2.findChessboardCorners(gray, (8, 6), None) # 寻找棋盘内角点
  48. print(ret)
  49. if ret:
  50. objppoints.append(objp)
  51. corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) # 提取亚像素信息,提高标定精度
  52. print(corners2)
  53. # print(len(corners2))
  54. # corners2 = mypoints
  55. imgpoints.append(corners2)
  56. img = cv2.drawChessboardCorners(img, (8, 6), corners2, ret)
  57. cv2.imshow('gray', img)
  58. while cv2.waitKey(100) != 27: # esc
  59. if cv2.getWindowProperty('gray', cv2.WND_PROP_AUTOSIZE) < 0: # 获取窗口状态,如果窗口关闭 执行break
  60. break
  61. cv2.destroyAllWindows()
  62. retval, rvec, tvec = cv2.solvePnP(objp, corners2, cameraMatrix, distCoeffs)
  63. print(rvec)
  64. print(tvec)
  65. # 测试原点
  66. imgp, _ = cv2.projectPoints(np.array([[0, 0, 0], [100, 0, 0], [0, 100, 0], [0, 0, 100]], dtype=np.float64), rvec,
  67. tvec, cameraMatrix, distCoeffs)
  68. print('imgp:', imgp)
  69. total_error = 0
  70. for i in range(len(objppoints)):
  71. imgpoints2, _ = cv2.projectPoints(objp, rvec, tvec, cameraMatrix, distCoeffs) # 映射
  72. error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
  73. total_error += error
  74. print("total error: ", total_error / len(objppoints))
  75. rvec_matrix = cv2.Rodrigues(rvec)[0]
  76. proj_matrix = np.hstack((rvec_matrix, tvec))
  77. eulerAngles = cv2.decomposeProjectionMatrix(proj_matrix)[6] # 欧拉角
  78. pitch, yaw, roll = [math.radians(_) for _ in eulerAngles]
  79. pitch = math.degrees(math.asin(math.sin(pitch)))
  80. roll = -math.degrees(math.asin(math.sin(roll)))
  81. yaw = math.degrees(math.asin(math.sin(yaw)))
  82. print(pitch, yaw, roll)
  83. roll, yaw, pitch = rotation_vector_to_euler_angles(rvec)
  84. print(pitch, yaw, roll)
  85. a = rotateMatrixToEulerAngles2(rvec)
  86. # 保存内参、畸变参数、新内参
  87. np.savez('c3_parameter_1080', k=cameraMatrix, newK= npzfile['newK'], dist=distCoeffs, rvec=rvec, tvec=tvec)

又来坑了,总会有这样那样的原因出错,继续问候代码的祖宗

 

这能咋办,我手动调的角点的坐标

 

避坑1,这里要说一下,检测的棋盘格角点是有顺序的,所以给定的真实坐标也要一一对应;

避坑2,注意你定义的是左手坐标系还是右手坐标系,会影响结果。

完结,撒花❀

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/599242
推荐阅读
相关标签
  

闽ICP备14008679号