单目相机标定工具(学习笔记2024.3.21)

单目相机部分参考于:单目相机标定实现--张正友标定法_张正友标定法 opencv-CSDN博客

单目相机标定

1.为什么单目相机需要进行相机标定

        单目相机需要相机标定是因为相机在拍摄图像时会引入畸变,这会影响到图像的几何形状和尺度,使得图像中的物体位置和大小不能直接反映真实世界中的情况。相机标定的目的是通过对已知几何形状的图像进行分析,来推导相机的内部参数(如焦距、主点位置)和畸变参数,从而纠正图像中的畸变,使得图像能够更准确地反映真实场景的几何信息。

        具体来说,单目相机的畸变主要包括径向畸变和切向畸变。径向畸变是由于相机的透镜不是完全理想的,导致图像中离光心较远的点在图像中表现为弧形弯曲;切向畸变则是由于相机的透镜和成像平面不完全平行引起的,使得图像中的线条不是水平或垂直的。

        可以通过相机标定,得到相机的内参矩阵和畸变系数,然后利用这些参数可以对图像进行畸变校正,从而得到更准确的图像信息,方便后续的图像处理和计算。

2.单目相机标定的流程原理

现实世界中有一个P点和一个相机(光心),描述这个P点的空间坐标首先得有一个坐标系,那么以光心为原点O建一个坐标系,叫相机坐标系。
那么就可以在相机坐标系下,设 P 坐 标 ( X , Y , Z ) P坐标(X,Y,Z) P坐标(X,Y,Z)和P的投影点 P ′ ( x ′ , y ′ , z ′ ) P'(x',y',z') P′(x′,y′,z′)。值得一提的是, P ′ ( x ′ , y ′ , z ′ ) P'(x',y',z') P′(x′,y′,z′)坐落在物理成像平面和像素平面。
物理成像平面,像素平面是二维的,他们的坐标系并不一样:
物理成像平面在 O ′ ( x ′ , y ′ ) O'(x',y') O′(x′,y′)平面上;
像素平面的原点在那个黑灰色图的左上角(图片的左上角),横轴向右称为 u u u轴,纵轴向下称为 v v v轴。
这样就得到了 P ′ P' P′的像素坐标 P ( u , v ) P(u,v) P(u,v),称为 P u v Puv Puv。

归一化的成像平面,就是将三维空间点的坐标都除以Z,在相机坐标系下,P有X, Y, Z 三个量,如果把它们投影到归一化平面 Z = 1 上,就会得到P的归一化坐标P(X/Z, Y/Z, 1)。

其中外参 T T T是平移向量 ( t 1 , t 2 , t 3 ) T (t1,t2,t3)^T (t1,t2,t3)T.
R R R旋转矩阵如下图:

3.个人工具代码

单目相机标定测试阶段

一.

  • 用手机先拍摄10~15张棋盘格图片(正对、倾斜等角度)
  • 棋盘格尺寸为角点数量,比如我的棋盘格横有九个格子,则记8
  • 输出图片方便查看
import cv2
import glob
import os
import numpy as np

# 棋盘格尺寸
pattern_size = (8, 5)

# 获取目录中所有图片的路径
image_paths = glob.glob('/home/lxy/Desktop/camera_calibration/picture/*.jpg')

for image_path in image_paths:
    # 读取图像
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 查找棋盘格角点
    ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)

    # 如果找到角点,绘制角点
    if ret == True:
        # 亚像素级别的角点优化
        criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
        corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)

        cv2.drawChessboardCorners(img, pattern_size, corners, ret)

        # 获取匹配点对
        srcPoints = corners.squeeze()
        dstPoints = np.array([[j * 80, i * 80] for i in range(5) for j in range(8)], np.float32)

        # 计算单应矩阵H
        H, _ = cv2.findHomography(srcPoints, dstPoints)

        # 使用单应矩阵H进行后续操作
        # ...

        # 保存图像至指定路径
        save_path = os.path.join('/home/lxy/Desktop/camera_calibration/output', os.path.basename(image_path))
        cv2.imwrite(save_path, img)
        print(f"Saved image to: {save_path}")

# 关闭窗口
cv2.destroyAllWindows()

input:

output:

 二.

配合第一个代码使用,将打印无畸变前提条件下的内参、外参

import cv2
import numpy as np
import glob

# 定义棋盘格尺寸
pattern_size = (8, 5)

# 创建棋盘格角点的世界坐标
objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)

# 用于保存所有棋盘格角点的世界坐标和图像坐标
objpoints = []  # 世界坐标系中的棋盘格角点
imgpoints = []  # 图像坐标系中的棋盘格角点

# 获取目录中所有图片的路径
image_paths = glob.glob('/home/lxy/Desktop/camera_calibration/output/*.jpg')

for image_path in image_paths:
    # 读取图像
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 查找棋盘格角点
    ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)

    # 如果找到角点,将世界坐标和图像坐标保存起来
    if ret == True:
        objpoints.append(objp)
        imgpoints.append(corners)

# 进行相机标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

# 打印内参和畸变系数
# 无畸变下的内参、外参
print("Camera matrix:")
print(mtx)
print("Distortion coefficients:")
print(dist)

output:

三.

将以上代码统合,输入原始图片,输出打印内参、外参、畸变系数,保存矫正后的图像

import cv2
import numpy as np
import glob
import os

# 定义棋盘格尺寸
pattern_size = (8, 5)

# 创建棋盘格角点的世界坐标
objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)

# 用于保存所有棋盘格角点的世界坐标和图像坐标
objpoints = []  # 世界坐标系中的棋盘格角点
imgpoints = []  # 图像坐标系中的棋盘格角点

# 获取目录中所有图片的路径
image_paths = glob.glob('/home/lxy/Desktop/camera_calibration/picture/*.jpg')

for image_path in image_paths:
    # 读取图像
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 查找棋盘格角点
    ret, corners = cv2.findChessboardCorners(gray, pattern_size, None)

    # 如果找到角点,将世界坐标和图像坐标保存起来
    if ret == True:
        objpoints.append(objp)
        imgpoints.append(corners)

        # 绘制棋盘格角点
        cv2.drawChessboardCorners(img, pattern_size, corners, ret)

        # 保存图像至指定路径
        save_path = os.path.join('/home/lxy/Desktop/camera_calibration/output', os.path.basename(image_path))
        cv2.imwrite(save_path, img)
        print(f"Saved image to: {save_path}")

# 进行相机标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

# 打印内参、畸变系数
print("Camera matrix:")
print(mtx)
print("Distortion coefficients:")
print(dist)

# 打印外参
for i in range(len(objpoints)):
    retval, rvec, tvec = cv2.solvePnP(objpoints[i], imgpoints[i], mtx, dist)
    print(f"Image {i+1}:")
    print("Rotation vector:")
    print(rvec)
    print("Translation vector:")
    print(tvec)
    
# 矫正图像畸变并输出
for image_path in image_paths:
    img = cv2.imread(image_path)
    # 矫正图像畸变
    undistorted_img = cv2.undistort(img, mtx, dist)

    # 保存矫正后的图像
    undistorted_save_path = os.path.join('/home/lxy/Desktop/camera_calibration/undistorted_output', os.path.basename(image_path))
    cv2.imwrite(undistorted_save_path, undistorted_img)
    print(f"Saved undistorted image to: {undistorted_save_path}")

四.利用得到的内参和畸变系数,对非棋盘格图片进行去畸变操作。结束

import cv2
import numpy as np

# 内参矩阵
camera_matrix = np.array([[287.45316998, 0.0, 266.30516443],
                           [0.0, 287.74766998, 313.24311295],
                           [0.0, 0.0, 1.0]])

# 畸变系数
dist_coeffs = np.array([-0.04538883, -0.03414269, -0.00016384, -0.00078098, 0.00900785])

# 读取待校正的图像
image_path = "/home/lxy/Desktop/camera_calibration/rosbag_picture/2/image_1710390491359887524.jpg"
image = cv2.imread(image_path)

# 校正图像
undistorted_image = cv2.undistort(image, camera_matrix, dist_coeffs)

# 将原始图像和校正后的图像拼接在一起
concatenated_image = np.hstack((image, undistorted_image))

# 显示拼接后的图像
cv2.imshow("Concatenated Images", concatenated_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值