赞
踩
本文采用张氏标定法标定了一个USB相机(网购,一百多块钱),记录了辛酸历程。
废话不多说,直接开始标定!
棋盘格是从网上找的图片打印的,整体还是个歪的......
这一步就有坑,后面解释
直接上代码
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
-
- import cv2
- import numpy as np
- import glob
-
- criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
-
-
-
- # 棋盘格对应的世界坐标下的三维坐标
- # xx = [371., 396., 421., 446., 471., 496.]
- # yy = [402., 427., 452., 477., 502., 527., 552., 577.]
- # 这两种坐标得到的内参一样
- xx = [0., 1., 2., 3., 4., 5.]
- yy = [0., 1., 2., 3., 4., 5., 6., 7.]
-
- xyz = []
- for i in range(len(xx)):
- for j in range(len(yy)):
- xyz.append(np.array([xx[i]*25, yy[j]*25, 0.], dtype=np.float32))
- xyz = np.array(xyz, dtype=np.float32)
-
- print(objp)
- objppoints = [] # 真实世界下的三维坐标
- imgpoints = []
- images = glob.glob(r'./chess/*.JPG')
-
-
- for fname in images:
- img = cv2.imread(fname)
- print(fname)
- gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
- # print(gray.shape)
- ret, corners = cv2.findChessboardCorners(gray, (8, 6), None) # 寻找棋盘内角点
- # print('corners', corners)
- if ret == True:
- objppoints.append(objp)
- corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) # 提取亚像素信息,提高标定精度
- print(corners2)
- imgpoints.append(corners2)
-
- img = cv2.drawChessboardCorners(img, (8, 6), corners2, ret)
- cv2.imshow('gray', img)
- while cv2.waitKey(100) != 27: # esc
- if cv2.getWindowProperty('gray', cv2.WND_PROP_AUTOSIZE) < 0: # 获取窗口状态,如果窗口关闭 执行break
- break
- cv2.destroyAllWindows()
-
- # mrx 内参数矩阵,dist 畸变系数,rvecs 旋转向量,tvecs 平移向量
- ret, mrx, dist, rvecs, tveces = cv2.calibrateCamera(objppoints, imgpoints, (1920, 1080), None, None) # 相机标定
- print(mrx)
- print(dist)
- newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mrx, dist, (1920, 1080), 1) # 矫正畸变,优化内参
- print(roi)
- total_error = 0
- for i in range(len(objppoints)):
- imgpoints2, _ = cv2.projectPoints(objppoints[i], rvecs[i], tveces[i], mrx, dist) # 映射
- error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
- total_error += error
- print("total error: ", total_error / len(objppoints))
-
- # 保存内参、畸变参数、新内参
- np.savez('c2_parameter_1080', k=mrx, dist=dist, newK=newcameramtx)
-
代码是没有问题的,运行的结果如下
贴两张图意思一下,其他图也是这样的。
然后程序会输出内参、畸变参数,我的外参是单独标定的。
先上代码
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
-
-
- import cv2
- import numpy as np
- from tqdm import tqdm
- import os
-
- npzfile = np.load('./calibrateCamera/c2_parameter_1080.npz')
- K = npzfile['k']
- dist = npzfile['dist']
- newcameramtx=npzfile['newK']
- print(npzfile['k'])
- print(npzfile['dist'])
-
-
- img_root=r'./photos_camera3'
- save_root=r'./undistort_photos'
- img_list=os.listdir(img_root)
- for name in tqdm(img_list):
- img_path=os.path.join(img_root,name)
- img = cv2.imread(img_path)
- dst = cv2.undistort(img, K, dist, None, newcameramtx)
- cv2.imwrite(os.path.join(save_root,name), dst)
-
-
填坑来了
第一步、第二步从拍摄到跑代码,看着都很流畅,大家也都是这么做的,本来我也没太注意,直到用标定好的内参给图像去畸变。结果保存出来的是个这鬼样子
这根本就不能用好吧!然后心里就开始默默的问候代码的祖宗,
开始解决问题
首先我是查看了保存的内参和优化后的内参,发现优化后的内参的cx和cy有些大的离谱。
然后又发现在优化内参时 返回的roi是(0,0,0,0)!!!
代码都知道图像里没一个像素是能用的。
现在就基本锁定了是内参的问题,然后重新拍数据。经过多批数据的拍摄总结出来的教训。
我们都知道相机的照片越靠近边缘,畸变程度越大,所以拍摄的时候要尽量把棋盘格放到边上去。
然后得到的结果就和预期的一样了。
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
-
-
- import cv2
- import numpy as np
- import math
-
- def rotation_vector_to_euler_angles(rvec):
- # 将旋转向量转换成旋转矩阵
- R, _ = cv2.Rodrigues(rvec)
-
- # 计算欧拉角(yaw、pitch、roll)
- yaw = np.arctan2(R[1, 0], R[0, 0])
- pitch = np.arctan2(-R[2, 0], np.sqrt(R[2, 1] ** 2 + R[2, 2] ** 2))
- roll = np.arctan2(R[2, 1], R[2, 2])
-
- # 将弧度转换为角度
- yaw = np.rad2deg(yaw)
- pitch = np.rad2deg(pitch)
- roll = np.rad2deg(roll)
-
- return yaw, pitch, roll # z, y, x
-
-
- # 旋转矩阵到欧拉角(角度制)
- def rotateMatrixToEulerAngles2(rvec):
- RM, _ = cv2.Rodrigues(rvec)
- theta_z = np.arctan2(RM[1, 0], RM[0, 0]) / np.pi * 180
- 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
- theta_x = np.arctan2(RM[2, 1], RM[2, 2]) / np.pi * 180
- print(f"Euler angles:\ntheta_x: {theta_x}\ntheta_y: {theta_y}\ntheta_z: {theta_z}")
- return theta_x, theta_y, theta_z
-
-
- npzfile = np.load('c3_parameter_1080.npz')
- cameraMatrix = npzfile['k']
- distCoeffs = npzfile['dist']
- print(npzfile['k'])
- print(npzfile['dist'])
-
- criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
- objppoints = [] # 真实世界下的三维坐标
- imgpoints = []
-
-
- # 拿尺子量的真实坐标
- xx = [406., 431., 456., 481., 506., 531.]
- yy = [305., 330., 355., 380., 405., 430., 455., 480.]
- xyz = []
- for i in range(len(xx)):
- for j in range(len(yy)):
- xyz.append(np.array([xx[i], yy[j], 0.], dtype=np.float32))
-
- xyz = np.array(xyz, dtype=np.float32)
- objp = xyz
- objp = xyz[::-1]
-
-
- print(objp)
-
- img = cv2.imread(r'D:\gzz\data\monkey_hand\20230523three\camera3\Rt/1.jpg')
- gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
- ret, corners = cv2.findChessboardCorners(gray, (8, 6), None) # 寻找棋盘内角点
- print(ret)
- if ret:
- objppoints.append(objp)
- corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) # 提取亚像素信息,提高标定精度
- print(corners2)
- # print(len(corners2))
- # corners2 = mypoints
- imgpoints.append(corners2)
-
- img = cv2.drawChessboardCorners(img, (8, 6), corners2, ret)
- cv2.imshow('gray', img)
- while cv2.waitKey(100) != 27: # esc
- if cv2.getWindowProperty('gray', cv2.WND_PROP_AUTOSIZE) < 0: # 获取窗口状态,如果窗口关闭 执行break
- break
- cv2.destroyAllWindows()
-
- retval, rvec, tvec = cv2.solvePnP(objp, corners2, cameraMatrix, distCoeffs)
- print(rvec)
- print(tvec)
-
- # 测试原点
- imgp, _ = cv2.projectPoints(np.array([[0, 0, 0], [100, 0, 0], [0, 100, 0], [0, 0, 100]], dtype=np.float64), rvec,
- tvec, cameraMatrix, distCoeffs)
- print('imgp:', imgp)
-
- total_error = 0
- for i in range(len(objppoints)):
- imgpoints2, _ = cv2.projectPoints(objp, rvec, tvec, cameraMatrix, distCoeffs) # 映射
- error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
- total_error += error
- print("total error: ", total_error / len(objppoints))
-
- rvec_matrix = cv2.Rodrigues(rvec)[0]
- proj_matrix = np.hstack((rvec_matrix, tvec))
- eulerAngles = cv2.decomposeProjectionMatrix(proj_matrix)[6] # 欧拉角
- pitch, yaw, roll = [math.radians(_) for _ in eulerAngles]
- pitch = math.degrees(math.asin(math.sin(pitch)))
- roll = -math.degrees(math.asin(math.sin(roll)))
- yaw = math.degrees(math.asin(math.sin(yaw)))
- print(pitch, yaw, roll)
-
- roll, yaw, pitch = rotation_vector_to_euler_angles(rvec)
- print(pitch, yaw, roll)
- a = rotateMatrixToEulerAngles2(rvec)
-
- # 保存内参、畸变参数、新内参
- np.savez('c3_parameter_1080', k=cameraMatrix, newK= npzfile['newK'], dist=distCoeffs, rvec=rvec, tvec=tvec)
又来坑了,总会有这样那样的原因出错,继续问候代码的祖宗
这能咋办,我手动调的角点的坐标,
避坑1,这里要说一下,检测的棋盘格角点是有顺序的,所以给定的真实坐标也要一一对应;
避坑2,注意你定义的是左手坐标系还是右手坐标系,会影响结果。
完结,撒花❀
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。