避坑—相机标定

本文采用张氏标定法标定了一个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,注意你定义的是左手坐标系还是右手坐标系,会影响结果。

完结,撒花❀

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值