相机标定与3D重建(opencv-python实现)

1. 相机标定的四个坐标系以及转换关系

1.1 四个坐标系介绍

在这里插入图片描述

1.1.1 世界坐标系(Xw, Yw, Zw)
  1. 为了描述相机位置而被引入的
  2. 标定时确定标定物的位置
  3. 作为双目视觉的系统参考系,给出两个摄像机对世界坐标系的关系,从而求出相机之间的相对关系
  4. 作为重建得到三维坐标的容器,重放重建后的物体的三维坐标
1.1.2 摄像机坐标系(Xc, Yc, Zc)

摄像机坐标系是摄像机站在自己角度上衡量的物体的坐标系。摄像机坐标系的原点在摄像机的光心上,z轴与摄像机光轴平行。

1.1.3 图像坐标系(x,y)

主要用于表征从摄像机坐标系向图像坐标系的透视投影关系,特点为连续,原点位于摄像机光轴与成像平面的焦点上

1.1.4 像素坐标系(u,v)

我们能从摄像机得到的真实信息,特点为离散,原点位于图像的左上角,其实是存储器的首地址

1.2 简单知识介绍

在这里插入图片描述
上图是针孔摄像机的基本模型。平面π称为摄像机的像平面,点Oc称为摄像机中心(或光心),f成为摄像机的焦距,Oc为端点且垂直于像平面的射线成为光轴或主轴,主轴与像平面的交点p是摄像机的主点

1.3 图像坐标系与像素坐标系的关系

在这里插入图片描述
如上图所示,像素坐标系u-v的原点为O0,图像坐标系x-y的原点O1在像素坐标系u-v的坐标为(u0,v0),dx和dy分别表示每个像素在横轴x和纵轴y的物理尺寸则图像坐标系和像素坐标系的坐标关系如下所示:
u = x d x + u 0 u = \frac{x}{dx}+u_{0} u=dxx+u0 v = x d x + v 0 v = \frac{x}{dx}+v_{0} v=dxx+v0
写成矩阵形式:
[ u v 1 ] = [ 1 d x 0 u 0 0 1 d y v 0 0 0 1 ] [ x y 1 ] \left [ \begin{matrix} u\\ v\\ 1 \end{matrix}\right ] = \begin{bmatrix} \frac{1}{dx} & 0 & u_{0}\\ 0& \frac{1}{dy} & v_{0}\\ 0& 0 & 1 \end{bmatrix} \left [ \begin{matrix} x\\ y\\ 1 \end{matrix}\right ] uv1=dx1000dy10u0v01xy1

1.4 世界坐标系与摄像机坐标系的关系

世界坐标系的坐标可以通过刚体变换(旋转以及平移转换)为摄像机坐标系的坐标,如下图所示
在这里插入图片描述
可表现为如下:
[ X c Y c Z c ] = [ r 00 r 01 r 02 r 10 r 11 r 12 r 20 r 21 r 22 ] [ X Y Z ] + [ T x T y T z ] \left [ \begin{matrix} X_c\\ Y_c\\ Z_c \end{matrix}\right ] = \begin{bmatrix} r_{00} & r_{01} & r_{02}\\ r_{10}& r_{11} & r_{12}\\ r_{20}& r_{21} & r_{22} \end{bmatrix} \left [ \begin{matrix} X\\ Y\\ Z \end{matrix}\right ] + \left [ \begin{matrix} T_x\\ T_y\\ T_z \end{matrix}\right ] XcYcZc=r00r10r20r01r11r21r02r12r22XYZ+TxTyTz
我们将旋转矩阵R以及平移矩阵T称之为摄像机的外参数
R可以表示为分别绕X,Y,Z轴旋转的效果之和,R=r1r2r3如下图所示:
在这里插入图片描述

1.5 摄像机坐标系、图像坐标系和像素坐标系三者之间的关系

摄像机坐标系中的坐标通过透视投影(用中心投影法将形体投射到投影面上)变为图像坐标系中的坐标
在这里插入图片描述
由简单的相似三角形可以得到
x = f Z c X c x = \frac{f}{Z_c}X_{c} x=ZcfXc y = f Z c Y c y = \frac{f}{Z_c}Y_{c} y=ZcfYc
写成矩阵的形式为:
[ x y 1 ] = 1 Z c [ f 0 0 0 f 0 0 0 1 ] [ X c Y c Z c ] \left [ \begin{matrix} x\\ y\\ 1 \end{matrix}\right ] = \frac{1}{Z_c} \begin{bmatrix} f & 0 & 0\\ 0& f & 0\\ 0& 0 & 1 \end{bmatrix} \left [ \begin{matrix} X_c\\ Y_c\\ Z_c \end{matrix}\right ] xy1=Zc1f000f0001XcYcZc
再由像素坐标系和图像坐标系的关系可得,中间矩阵也称为摄像机的内参矩阵
[ u v 1 ] = 1 Z c [ f d x 0 u 0 0 f d y v 0 0 0 1 ] [ X c Y c Z c ] \left [ \begin{matrix} u\\ v\\ 1 \end{matrix}\right ] = \frac{1}{Z_c} \begin{bmatrix} \frac{f}{dx} & 0 & u_0\\ 0& \frac{f}{dy} & v_0\\ 0& 0 & 1 \end{bmatrix} \left [ \begin{matrix} X_c\\ Y_c\\ Z_c \end{matrix}\right ] uv1=Zc1dxf000dyf0u0v01XcYcZc

1.6 总结

世界坐标通过刚体变换(旋转和平移)得到摄像机坐标,摄像机坐标系通过透视投影转换为图像坐标,图像坐标离散化可得像素坐标
将以上步骤整理得:
[ u v 1 ] = 1 Z c [ f x 0 u 0 0 f y v 0 0 0 1 ] [ R t 1 0 ] [ X w Y w Z w ] \left [ \begin{matrix} u\\ v\\ 1 \end{matrix}\right ] = \frac{1}{Z_c} \begin{bmatrix} f_x & 0 & u_0\\ 0& f_y & v_0\\ 0& 0 & 1 \end{bmatrix} \begin{bmatrix} R & t\\ 1 & 0 \end{bmatrix} \left [ \begin{matrix} X_w\\ Y_w\\ Z_w \end{matrix}\right ] uv1=Zc1fx000fy0u0v01[R1t0]XwYwZw
其中 f x = f d x , f y = f d y f_x=\frac{f}{dx},f_y=\frac{f}{dy} fx=dxf,fy=dyf
1.第一个矩阵中的四个参数称之为相机的内部参数,因为他们只与相机有关
2.第二个矩阵的两个参数称之为相机的外部参数,只要世界坐标系的相对位置关系发生改变,它们就会改变,每一张图片的R、T都是唯一的
3.单目摄像机标定就是在已知像素坐标系下的坐标和世界坐标系下的坐标求解内部参数的过程
可以简化为:
s ∗ m = A [ R t ] M s*m = A\begin{bmatrix} R&t \end{bmatrix}M sm=A[Rt]M
其中s就是Zc,称之为尺度因子,m为图像坐标系的坐标,A为内参矩阵,M为世界坐标系中的坐标

为了能够更好的理解相机内参,举个实例:
现以NiKon D700相机为例进行求解其内参数矩阵:
就算大家身边没有这款相机也无所谓,可以在网上百度一下,很方便的就知道其一些参数——
焦距 f = 35mm 最高分辨率:4256×2832 传感器尺寸:36.0×23.9 mm
根据以上定义可以有:
u0= 4256/2 = 2128 v0= 2832/2 = 1416 dx = 36.0/4256 dy = 23.9/2832
fx = f/dx = 4137.8 fy = f/dy = 4147.3
分辨率可以分为显示分辨率与图像分辨率两个方向:
显示分辨率(屏幕分辨率)是屏幕图像的精密度,是指显示器所能显示的像素有多少
图像分辨率则是单位英寸中所包含的像素点数,其定义更趋近于分辨率本身的定义
在这里插入图片描述

2 畸变模型

2.1 产生原因

采用理想针孔模型,由于通过针孔的光线少,摄像机曝光太慢,在实际使用中均采用透镜,可以使图像生成迅速,但代价是引入了畸变。两种主要的失真是径向失真和切向失真,而在张氏标定中,只关注径向畸变。

2.2 径向畸变

产生原因是光线在远离透镜中心的地方比靠近中心的地方更加弯曲,主要包括桶形畸变和枕形畸变:
在这里插入图片描述
其在真实照片中是这样的:
在这里插入图片描述
它可以被如下方法校正:
x c o r r e c t e d = x ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) x_{corrected} = x(1 + k_1r^2+k_2r^4+k_3r^6) xcorrected=x(1+k1r2+k2r4+k3r6) y c o r r e c t e d = y ( 1 + k 1 r 2 + k 2 r 4 + k 3 r 6 ) y_{corrected} = y(1 + k_1r^2+k_2r^4+k_3r^6) ycorrected=y(1+k1r2+k2r4+k3r6)
其中k1,k2表示径向畸变的系数,r表示图像坐标到原点的距离

2.3 切向畸变

产生的原因透镜不完全平行于图像平面,这种现象发生于成像仪被粘贴在摄像机的时候:
在这里插入图片描述
x c o r r e c t e d = x + [ 2 p 1 x y + p 2 ( r 2 + 2 y 2 ) ] x_{corrected} = x+[2p_1xy+p_2(r^2+2y^2)] xcorrected=x+[2p1xy+p2(r2+2y2)] y c o r r e c t e d = y + [ 2 p 2 x y + p 1 ( r 2 + 2 y 2 ) ] y_{corrected} = y+[2p_2xy+p_1(r^2+2y^2)] ycorrected=y+[2p2xy+p1(r2+2y2)]

3 张正友标定算法详解

3.1 相机标定的目的

  1. 进行摄像机标定的目的:求出相机的内、外参数,以及畸变参数
  2. 标定相机后通常是想做两件事:一是由于每个镜头的畸变程度各不相同,通过相机标定可以矫正这种镜头畸变;另一个就是根据获得的图像重构三维场景

3.2 计算单应性矩阵

设三维世界坐标的点为X=[X,Y,Z,1]T,二维相机平面像素坐标为m=[u,v,1]T,所以标定用的棋盘格平面到图像平面的单应性关系为:
s ∗ m = A [ R t ] M s*m = A\begin{bmatrix} R&t \end{bmatrix}M sm=A[Rt]M
其中s为尺度因子,A为摄像机内参数,R为旋转矩阵,T为平移向量。其中
A = [ f x c u 0 0 f y v 0 0 0 1 ] A = \begin{bmatrix} f_x & c & u_0\\ 0& f_y & v_0\\ 0& 0 & 1 \end{bmatrix} A=fx00cfy0u0v01
c是描述两个特征坐标轴倾斜角倾斜角的参数(两个坐标轴相互垂直时,c = 0,则默认情况下这个c都是为0)。

张氏标定法中,假设模型平面在世界坐标系中Z坐标为0,也就是我们把世界坐标系的原点定在物体上。则可得
s [ u v 1 ] = K [ r 1 r 2 r 3 t ] [ X Y 0 1 ] = K [ r 1 r 2 t ] [ X Y 1 ] s\begin{bmatrix} u\\ v\\ 1 \end{bmatrix} = K \begin{bmatrix} r_1 & r_2 & r_3 & t \end{bmatrix}\begin{bmatrix} X\\ Y\\ 0\\ 1 \end{bmatrix} = K\begin{bmatrix} r_1 & r_2 & t \end{bmatrix}\begin{bmatrix} X\\ Y\\ 1 \end{bmatrix} suv1=K[r1r2r3t]XY01=K[r1r2t]XY1
我们把K[r1 r2 t]叫做单应性矩阵H,被定义为一个平面到另一个平面的投影映射
s [ u v 1 ] = H [ X Y 1 ] s\begin{bmatrix} u\\ v\\ 1 \end{bmatrix} = H\begin{bmatrix} X\\ Y\\ 1 \end{bmatrix} suv1=HXY1 H = [ h 1 h 2 h 3 ] = λ K [ r 1 r 2 t ] H = \begin{bmatrix} h_1& h_2 &h_3 \end{bmatrix}= λK\begin{bmatrix} r_1& r_2 & t \end{bmatrix} H=[h1h2h3]=λK[r1r2t]
H是一个齐次矩阵,所以有8个未知数,至少需要8个方程,每对对应点能提供两个方程,所以至少需要四个对应点,就可以算出世界平面到图像平面的单应性矩阵H。

3.3 计算内参数矩阵

由上式可得
λ = 1 s λ = \frac{1}{s} λ=s1 r 1 = 1 λ K − 1 h 1 r_{1} = \frac{1}{λ}K^{-1}h_{1} r1=λ1K1h1 r 2 = 1 λ K − 1 h 2 r_{2} = \frac{1}{λ}K^{-1}h_{2} r2=λ1K1h2
由于旋转矩阵是个酉矩阵,r1和r2正交,可得
r 1 T r 2 = 0 r_{1}^Tr_{2} = 0 r1Tr2=0 ∥ r 1 ∥ = ∥ r 2 ∥ = 0 \left \| r_{1} \right \| = \left \| r_{2} \right \| = 0 r1=r2=0
代入可得:
h 1 T K − T K − 1 h 2 = 0 h_{1}^{T}K^{-T}K^{-1}h_{2} = 0 h1TKTK1h2=0 h 1 T K − T K − 1 h 1 = h 2 T K − T K − 1 h 2 h_{1}^{T}K^{-T}K^{-1}h_{1} = h_{2}^{T}K^{-T}K^{-1}h_{2} h1TKTK1h1=h2TKTK1h2
即每个单应性矩阵能提供两个方程,而内参数矩阵包含5个参数,要求解,至少需要3个单应性矩阵。为了得到三个不同的单应性矩阵,我们使用至少三幅棋盘格平面的图片进行标定。通过改变相机与标定板之间的相对位置来得到三个不同的图片。为了方便计算,定义如下:
在这里插入图片描述
可以看到,B是一个对称阵,所以B的有效元素为六个,让这六个元素写成向量b,即
b=[B11B12B22B13B23B33]T
可以推导得到
在这里插入图片描述
利用约束条件可以得到:
在这里插入图片描述
通过上式,我们至少需要三幅包含棋盘格的图像,可以计算得到B,然后通过cholesky分解,得到相机的内参数矩阵K。

3.3 计算外参数矩阵

λ = 1 s = 1 ∥ A − 1 h 1 ∥ = 1 ∥ A − 1 h 2 ∥ λ = \frac{1}{s} = \frac{1}{\left \| A^{-1}h_{1} \right \|} = \frac{1}{\left \| A^{-1}h_{2} \right \|} λ=s1=A1h11=A1h21 r 1 = 1 λ K − 1 h 1 r_{1} = \frac{1}{λ}K^{-1}h_{1} r1=λ1K1h1 r 2 = 1 λ K − 1 h 2 r_{2} = \frac{1}{λ}K^{-1}h_{2} r2=λ1K1h2 r 3 = r 1 × r 2 r_3 = r_1×r_2 r3=r1×r2 t = λ K − 1 h 3 t = λK^{-1}h_3 t=λK1h3

3.4 最大似然估计

张正友在论文中提到,前面的这些数学原理和推导并没有太多的物理意义,仅仅是为后面的极大似然优化提供了一个初值。
极大似然估计是一种估计总体未知参数的方法,它主要用于点估计问题。说穿了就是一句话:就是在参数空间中选取使得样本取得观测值的概率最大的参数。
假设我们得到了模型平面的n幅图片,模型平面上有m个点,假设图像上像素点的噪声服从独立的同一分布,下面给出极大似然优化问题: ∑ ∑ ∥ m i j − m ^ ( A , R i , t i , M j ) ∥ \sum \sum\left \| m_{ij}-\hat{m}(A,R_{i},t_{i},M_{j}) \right \| mijm^(A,Ri,ti,Mj)
其中 m ^ ( A , R i , t i , M j ) \hat{m}(A,R_{i},t_{i},M_{j}) m^(A,Ri,ti,Mj)表示Mj在第i副图像上的投影

4 张正友相机标定python实现

在张正友相机标定中,一般分为以下的步骤:

  1. 准备一系列来相机标定的图片
  2. 对每张图片提取角点信息
  3. 由于角点信息不够精确,进一步提取亚像素角点信息
  4. 在图片中画出提取出的角点
  5. 相机标定
  6. 对标定结果评价,计算误差
  7. 使用标定结果对原图片进行校正

4.1 准备好一系列相机标定

在本次实验中我们选取的是opencv官方在github上提供的图片`

opencv/samples/data/left01-left14.jpg

在这里插入图片描述

4.2 对每张图片提取角点信息

# 导入图像
a = cv2.imread('E:/3Dreconstruction/camera calibration/chess/left14.jpg')
# 进行颜色空间的转换
b = cv2.cvtColor(a, cv2.COLOR_BGR2GRAY)
# 提取角点信息,表示每行角点的个数为9,每列角点的个数为6
ret, corners = cv2.findChessboardCorners(b, (9, 6))
# ret为标志位,用来检测是否检测到所有棋盘内角点
print(ret)

4.3 提取亚像素角点信息

# 迭代的终止标准
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 提取亚像素角点信息
corners2 = cv2.cornerSubPix(b, corners, (11, 11), (-1, -1), criteria)

4.4 画出角点

# 画出角点
cv2.drawChessboardCorners(a, (9, 6), corners2 ,False)
cv2.namedWindow('winname', cv2.WINDOW_NORMAL)
cv2.imshow('winname', a)
cv2.waitKey(0)

得如下图像:

在这里插入图片描述

4.5 相机标定

# 相机标定的核心
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(op, imgpoints, b.shape[::-1], None, None)
# ret为极大似然函数的最小值
# mtx是内参矩阵
# dist为相机的畸变参数矩阵
# rvecs为旋转向量
# tvecs为位移向量

4.6 标定结果评价,计算误差

tot_error = 0
for i in range(len(op)):
    imgpoints2, _ = cv2.projectPoints(op[i], rvecs[i], tvecs[i], mtx, dist) #对空间中的三维坐标点进行反向投影
    error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)   # 平均平方误差(重投影误差)
    tot_error += error
 print(ret, (tot_error / len(op))**0.5)

4.7 对原图像进行校正

h, w = a.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (h, w), 1) # 校正内参矩阵
dst = cv2.undistort(a, mtx, dist, None)
# undistort
mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w, h), 5)  # 用于计算畸变映射
dst = cv2.remap(a, mapx, mapy, cv2.INTER_LINEAR)    # 把求得的映射应用到图像上
np.savez("outfile", mtx, dist)
a = cv2.cvtColor(a, cv2.COLOR_BGR2RGB)
dst = cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)
plt.subplot(121), plt.imshow(a), plt.title('source')
plt.subplot(122), plt.imshow(dst), plt.title('undistorted')
plt.show()

得到校正结果
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190622145959192.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTE1MzgwMw==,size_16,color_FFFFFF,t_70

4.8 最终代码

import numpy as np
import cv2
from matplotlib import pyplot as plt
import time
import os

 # 程序流程
 # 1.准备好一系列来相机标定的图片
 # 2.对每张图片提取角点信息
 # 3.由于角点信息不够精确,进一步提取亚像素角点信息
 # 4.在图片中画出提取出的角点
 # 5.相机标定
 # 6.对标定结果评价,计算误差
 # 7.使用标定结果对原图片进行校正

path = 'E:/3Dreconstruction/camera calibration/chess2'   # 文件路径
objp = np.zeros((9 * 6, 3), np.float32)
objp[:, :2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2) * 10
# mgrid是meshgrid的缩写,生成的是坐标网格,输出的参数是坐标范围,得到的网格的点坐标
op = [] # 存储世界坐标系的坐标X,Y,Z,在张正友相机标定中Z=0
imgpoints = []  # 像素坐标系中角点的坐标
for i in os.listdir(path):
    #读取每一张图片
    file = '/'.join((path, i))
    a = cv2.imread(file)
    b = cv2.cvtColor(a, cv2.COLOR_BGR2GRAY)
    # 确定输入图像中是否有棋盘格图案,并检测棋盘格的内角点
    ret, corners = cv2.findChessboardCorners(a, (9, 6), None)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
    if ret == True: # 如果所有的内角点都找到了
        corners2 = cv2.cornerSubPix(b, corners, (11, 11), (-1, -1), criteria)   # 提取亚像素角点信息
        imgpoints.append(corners2)
        op.append(objp)

# 相机标定的核心
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(op, imgpoints, b.shape[::-1], None, None)
# ret为极大似然函数的最小值
# mtx是内参矩阵
# dist为相机的畸变参数矩阵
# rvecs为旋转向量
# tvecs为位移向量
tot_error = 0
for i in range(len(op)):
    imgpoints2, _ = cv2.projectPoints(op[i], rvecs[i], tvecs[i], mtx, dist) #对空间中的三维坐标点进行反向投影
    error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)   # 平均平方误差(重投影误差)
    tot_error += error
# print(ret, (tot_error / len(op))**0.5)
# cv2.namedWindow('winname', cv2.WINDOW_NORMAL)
# cv2.imshow('winname', a)
# cv2.waitKey(0)
"""下面是校正部分"""
h, w = a.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (h, w), 1) # 校正内参矩阵
dst = cv2.undistort(a, mtx, dist, None)
# undistort
mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w, h), 5)  # 用于计算畸变映射
dst = cv2.remap(a, mapx, mapy, cv2.INTER_LINEAR)    # 把求得的映射应用到图像上
# crop the image
# x, y, w, h = roi
# dst = dst[y:y + h, x:x + w]
# print(mapx.shape)
# print(mapy)
np.savez("outfile", mtx, dist)
a = cv2.cvtColor(a, cv2.COLOR_BGR2RGB)
dst = cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)
# print(roi)
plt.subplot(121), plt.imshow(a), plt.title('source')
plt.subplot(122), plt.imshow(dst), plt.title('undistorted')
plt.show()
  • 9
    点赞
  • 106
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: # 导入相应的库 import cv2 import numpy as np # 获取双目相机标定所需要的参数 # 获取棋盘格角点的位置 # 如果是灰度图像,则为1,否则为3 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) objp = np.zeros((9*6, 3), np.float32) objp[:, :2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2) # 初始化左右摄像头 left_camera = cv2.VideoCapture(0) right_camera = cv2.VideoCapture(1) # 创建用于存放标定结果的数组 objpoints = [] imgpoints_l = [] imgpoints_r = [] # 创建计数器 count = 0 # 标定 while True: # 读取图像 _, frame_l = left_camera.read() _, frame_r = right_camera.read() gray_l = cv2.cvtColor(frame_l, cv2.COLOR_BGR2GRAY) gray_r = cv2.cvtColor(frame_r, cv2.COLOR_BGR2GRAY) # 找到标定板上的角点 ret_l, corners_l = cv2.findChessboardCorners(gray_l, (9, 6), None) ret_r, corners_r = cv2.findChessboardCorners(gray_r, (9, 6), None) if ret_l is True and ret_r is True: objpoints.append(objp) # 在左右摄像头标定板图像上寻找角点 corners2_l = cv2.cornerSubPix(gray_l, corners_l, (11, 11), (-1, -1), criteria) corners2_r = cv2.cornerSubPix(gray_r, corners_r, (11, 11), (-1, -1), criteria) imgpoints_l.append(corners2_l) imgpoints_r.append(corners2_r) # 绘制角点 cv2.drawChessboardCorners(frame_l, (9, 6), corners2_l, ret_l) cv2.drawChessboardCorners(frame_r, (9, 6), corners2_r, ret_r) cv2.imshow('left_camera', frame_l) cv2.imshow('right_camera', frame_r) cv2.waitKey(500) count += 1 # 标定成功后跳出循环 if count > 20: break# 获取摄像头的标定矩阵和畸变系数 ret_l, mtx_l, dist_l, rvecs_l, tvecs_l = cv2.calibrateCamera(objpoints, imgpoints_l, gray_l.shape[::-1], None, None) ret_r, mtx_r, dist_r, rvecs_r, tvecs_r = cv2.calibrateCamera(objpoints, imgpoints_r, gray_r.shape[::-1], None, None) # 获取双目摄像头的标定参数 ret, M1, d1, M2, d2, R, T, E, F = cv2.stereoCalibrate(objpoints, imgpoints_l, imgpoints_r, mtx_l, dist_l, mtx_r, dist_r, gray_l.shape[::-1]) # 获取极线参数 stereoRrectify_l, stereoRrectify_r, Rl, Rr, Pl, Pr, Q, roi1, roi2 = cv2.stereoRectify(M1, d1, M2, d2, gray_l.shape[::-1], R, T) # 将极线参数应用到图像上 map1_l, map2_l = cv2.initUndistortRectifyMap(M1, d1, Rl, Pl, gray_l.shape[::-1], cv2.CV_16SC2) map1_r, map2_r = cv2.initUndistortRectifyMap(M2, d2, Rr, Pr, gray_r.shape[::-1], cv2.CV_16SC2) ### 回答2: 双目相机标定是一个关键的前置步骤,为双目视觉系统的深度估计、三维重建和立体测量提供准确的图像数据。下面是一个简单的Python代码实现双目相机标定的示例: ```python import cv2 import numpy as np def calibrate_stereo_camera(image_folder, chessboard_size, square_size): obj_points = [] # 3D object points img_points_l = [] # 2D image points for left camera img_points_r = [] # 2D image points for right camera pattern_size = (chessboard_size[0]-1, chessboard_size[1]-1) # Prepare object points objp = np.zeros((np.prod(pattern_size), 3), dtype=np.float32) objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) * square_size # Iterate through images for i in range(1, 13): # Assuming there are 12 calibration images # Load left and right images img_left = cv2.imread(f"{image_folder}/left_{i}.jpg", 0) img_right = cv2.imread(f"{image_folder}/right_{i}.jpg", 0) # Find chessboard corners ret_l, corners_l = cv2.findChessboardCorners(img_left, pattern_size) ret_r, corners_r = cv2.findChessboardCorners(img_right, pattern_size) if ret_l and ret_r: obj_points.append(objp) img_points_l.append(corners_l) img_points_r.append(corners_r) # Perform stereo calibration ret, K_l, D_l, K_r, D_r, R, T, E, F = cv2.stereoCalibrate(obj_points, img_points_l, img_points_r, None, None, None, None, img_left.shape[::-1]) return ret, K_l, D_l, K_r, D_r, R, T, E, F ``` 以上代码实现了双目相机标定的基本流程。首先,根据传入的图像文件夹路径、棋盘格尺寸和方格尺寸等参数,定义了一些变量和参数。然后,通过循环迭代读取标定图像,并使用`cv2.findChessboardCorners()`函数找到左右相机图像上的棋盘格角点。如果检测到了足够的角点,则将角点坐标存入`img_points_l`和`img_points_r`中。同时,将已知的定标板角点的3D坐标值存入`obj_points`中。 随后,使用`cv2.stereoCalibrate()`函数进行双目标定,得到了相机内参、畸变系数、旋矩阵和平移向量等标定结果。 最后,返回了标定结果,包括左右相机的内参、畸变系数、旋矩阵和平移向量等信息。 请注意,以上只是一个简单示例代码,实际的双目相机标定过程可能需要更多的参数和步骤,如相机畸变校正和立体校正等。具体实现方法可以根据实际需求进行调整和扩展。 ### 回答3: 双目相机标定计算机视觉中的重要任务之一,可以通过该标定过程来获得两个相机之间的换关系,从而实现三维重建、立体视觉等应用。以下是一个简单的Python代码实现双目相机标定的示例: ```python import cv2 import numpy as np # 准备用于标定的棋盘格模板大小 pattern_size = (9, 6) # 内部角点数量 # 创建存储标定图像和物体点的数组 obj_points = [] # 存储三维物体点 img_points_L = [] # 存储左相机图像点 img_points_R = [] # 存储右相机图像点 # 准备棋盘格物体点 obj_p = np.zeros((np.prod(pattern_size), 3), dtype=np.float32) obj_p[:, :2] = np.indices(pattern_size).T.reshape(-1, 2) # 打开相机设备 cap_L = cv2.VideoCapture(0) # 左相机的设备索引,默认为0 cap_R = cv2.VideoCapture(1) # 右相机的设备索引,默认为1 while True: # 读取图像 ret_L, frame_L = cap_L.read() ret_R, frame_R = cap_R.read() # 图像灰度处理 gray_L = cv2.cvtColor(frame_L, cv2.COLOR_BGR2GRAY) gray_R = cv2.cvtColor(frame_R, cv2.COLOR_BGR2GRAY) # 查找棋盘格角点 ret_L, corners_L = cv2.findChessboardCorners(gray_L, pattern_size) ret_R, corners_R = cv2.findChessboardCorners(gray_R, pattern_size) # 若成功找到棋盘格角点,则进行标定 if ret_L and ret_R: obj_points.append(obj_p) img_points_L.append(corners_L) img_points_R.append(corners_R) cv2.drawChessboardCorners(frame_L, pattern_size, corners_L, ret_L) cv2.drawChessboardCorners(frame_R, pattern_size, corners_R, ret_R) cv2.imshow("Left Camera", frame_L) cv2.imshow("Right Camera", frame_R) # 若按下ESC键,则退出标定 if cv2.waitKey(1) == 27: break # 相机标定 ret_L, K_L, dist_L, rvecs_L, tvecs_L = cv2.calibrateCamera(obj_points, img_points_L, gray_L.shape[::-1], None, None) ret_R, K_R, dist_R, rvecs_R, tvecs_R = cv2.calibrateCamera(obj_points, img_points_R, gray_R.shape[::-1], None, None) # 双目标定 ret, K_L, dist_L, K_R, dist_R, R, T, E, F = cv2.stereoCalibrate(obj_points, img_points_L, img_points_R, K_L, dist_L, K_R, dist_R, gray_L.shape[::-1]) # 打印标定结果 print("相机内参数 (左):") print(K_L) print("畸变参数 (左):") print(dist_L) print() print("相机内参数 (右):") print(K_R) print("畸变参数 (右):") print(dist_R) print() print("旋矩阵:") print(R) print("平移向量:") print(T) print() print("本质矩阵:") print(E) print("基础矩阵:") print(F) # 释放相机设备 cap_L.release() cap_R.release() # 关闭窗口 cv2.destroyAllWindows() ``` 以上代码使用OpenCV库来实现相机标定的相关功能。它通过在棋盘格图案中检测角点的方式来获取图像点,然后利用这些点进行相机标定和双目标定。最终输出的结果包括相机的内参数、畸变参数、旋矩阵、平移向量、本质矩阵和基础矩阵。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值