结构光三维重建之单目相机标定

学习目标:

提示:这里可以添加学习目标

  • MATLAB实现相机标定

学习内容:

提示:这里可以添加要学的内容


学习产出:

1.1.1 相机标定的概念

相机标定的原理:找到合适的相机内外参数和畸变系数来使相机对棋盘格的重投影误差最小
相机标定的最终目的是:建立相机成像几何模型并矫正相机畸变。

1.1.2 四大坐标系

  1. 世界坐标系(world coordinate system):用户自己定义的三维世界的坐标系,为了描述目标物在真实三位空间的位置而被引入,下图中的(Xw,Yw,Zw)。单位为m。
    在这里插入图片描述
  2. 相机坐标系(camera coordinate system):以相机为坐标原点建立坐标系,从相机的角度描述物体的空间位置。下图中的(Xc,Yc,Zc)单位为m。
    在这里插入图片描述
  3. 图像坐标系(image coordinate system):以图像中心为坐标原点,X,Y轴平行于图像两边,可以用(x, y)表示物体的坐标值,单位为m。
  4. 像素坐标系(pixel coordinate system):以图像左上角为原点,X,Y轴分别平行于图像两边的坐标系。用(u, v)表示其坐标值,单位为个(像素数目)。

1.1.3 畸变

1.1.3.1 径向畸变

它们主要分为两大类,桶形畸变和枕形畸变。
桶形畸变是由于图像放大率随着离光轴的距离增加而减小,而枕形畸变却恰好相反。在这两种畸变中,穿过图像中心和光轴有交点的直线还能保持形状不变。
在这里插入图片描述

1.1.3.2 切向畸变

除了透镜的形状会引入径向畸变外,在相机的组装过程中由于不能使得透镜和成像面严格平行也会引入切向畸变。
在这里插入图片描述

https://blog.csdn.net/qq_42118719/article/details/112332147原文地址
在这里插入图片描述
因此,联合上面两种畸变,对于相机坐标系中的一点 P(X, Y, Z),我们能够
通过五个畸变系数(K1,K2,K3,p1,p2)找到这个点在像素平面上的正确位置

在这里插入图片描述
在这里插入图片描述
上文删除线对张正波博士的论文总结的不好,甚至感觉有点误区,下面找一个通俗易懂的推导过程进行叙述。

如何估计单应矩阵?

https://zhuanlan.zhihu.com/p/136827980?utm_source=qq&utm_medium=social&utm_oi=1044327556153163776

首先,我们假设两张图像中的对应点对齐次坐标为(x’,y’,1)和(x,y,1),单应矩阵H定义为:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以我们在进行标定过程中至少需要四个点进行H矩阵的估计。

2.1 张正友标定中的单应性矩阵

在张正友标定中,用于标定的棋盘格是三维场景中的一个平面P,其在成像平面的像是另一个平面p,单应性矩阵就是指两个平面之间的映射关系,准确的来说是世界坐标系和像素坐标系之间的映射关系。
p = HP
在 单应矩阵中,我们根据标定棋盘图纸及其对应的照片已经可以得到单应矩阵H了。如下所示:
在这里插入图片描述

2.1 张正友标定中求解参数

下一步如何求相机内外参数呢?

我们知道H是内参矩阵和外参矩阵的混合体,而我们想要最终分别获得内参和外参。所以需要想个办法,先把内参求出来(先求内参是因为更容易,因为每张图片的内参都是固定的,而外参是变化的),得到内参后,那张标定图片的外参也就随之解出了。
求解思路是利用旋转向量的约束关系,以下是具体推导,建议自己演算一遍,加深理解。
为了利用旋转向量之间的约束关系,我们先将单应性矩阵H化为3个列向量,即H=[h1 h2 h3],则有
在这里插入图片描述
按元素对应关系可得:

在这里插入图片描述
因为旋转向量在构造中是相互正交的,即r1和r2相互正交,由此我们就可以利用“正交”的两个含义,得出每个单应矩阵提供的两个约束条件:

  1. 约束条件1: 旋转向量点积为0(两垂直平面上的旋转向量互相垂直),这个可以参考上面对旋转矩阵的性质介绍 ,旋转矩阵的每一列都是彼此正交,且模为1,即:
    在这里插入图片描述
    约束条件2:旋转向量长度相等(旋转不改变尺度),(我认为还是上面的那个性质–旋转矩阵的每一列都是彼此正交,且模为1)即:
    在这里插入图片描述
    所以一个单应性矩阵H可以提供上述两个约束条件 。那么如何利用上述两个约束条件求解内参或者外参呢?我们一步一步来看,由前面可知内参矩阵M:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    如果我们拍摄了n张不同角度的标定图片,因为每张图片我们都可以得到一组(2个)上述的等式。其中,v12,v11,v22可以通过前面已经计算好的单应矩阵得到,因此是已知的,而b中6个元素是待求的未知数。因此,至少需要保证图片数 n>=3,我们才能解出b。
    根据n张不同角度的标定图片,最终我们得到了一个矩阵集合 Vb=0 ,其中V是一个 (2n x 6) 的矩阵。
    在这里插入图片描述

MATLAB相机标定

https://pan.baidu.com/s/1pKshsPP?at=1658215191911
张正友标定中的单应性矩阵

代码学习解读

参考的是matlab官方代码

function [cameraParams, imagesUsed, estimationErrors] = estimateCameraParameters(varargin)

输入:
Inputs:
% -------
% imagePoints - an M-by-2-by-P array of [x,y] intrinsic image coordinates
% of keypoints on the calibration pattern. M > 3
% is the number of keypoints in the pattern. P > 2 is the
% number of images containing the calibration pattern.
%
% worldPoints - an M-by-2 array of [x,y] world coordinates of
% keypoints on the calibration pattern. The pattern
% must be planar, so all z-coordinates are assumed to be 0.
输出:
% --------
% cameraParams - a cameraParameters object containing the camera parameters.
%
% imagesUsed - a P-by-1 logical array indicating which images were
% used to estimate the camera parameters. P > 2 is the
% number of images containing the calibration pattern.
%
% estimationErrors - a cameraCalibrationErrors object containing the
% standard errors of the estimated camera parameters.
%
% [stereoParams, pairsUsed, estimationErrors] =
% estimateCameraParameters(imagePoints, worldPoints) estimates parameters
% of a stereo camera.
参数:

  1. WorldUnits 描述指定 worldPoints 的单位的字符串。
  2. EstimateSkew 倾斜估计 一个逻辑标量,指定是否应估计图像轴倾斜。当设置为 false 时,图像轴区域假定完全垂直。
  3. NumRadialDistortionCoefficients 径向失真系数
  4. EstimateTangentialDistortion 切向失真系数
  5. InitialIntrinsicMatrix 初始内参矩阵 一个3*3的矩阵来估计相机内参
  6. InitialRadialDistortion 初始径向畸变一个2或者3的矩阵 包含 2 或 3 个元素的向量 径向畸变的初始猜测 系数。 如果值为空, 0 用作所有的初始值系数。

代码解析:

 function [cameraParams, imagesUsed, errors] = calibrateOneCamera(imagePoints, ...
    worldPoints, imageSize, cameraModel, worldUnits, calibrationParams)

intrinisc 内参
extrinsic 外参
distortion 失真

  [cameraParams, imagesUsed] = computeInitialParameterEstimate(...
    worldPoints, imagePoints, imageSize, cameraModel, worldUnits, ...
    calibrationParams.initIntrinsics, calibrationParams.initRadial);

计算多个H矩阵

 [H, validIdx] = computeHomographies(imagePoints, worldPoints);

这代码里有单个计算H的函数

computeHomography(double(points(:, :, i)), worldPoints);

从世界坐标系到图像坐标系,根据求出的图像坐标以及设置的世界坐标系下的坐标进行计算H的大小。

H = fitgeotrans(worldPoints, imagePoints, 'projective');

这一段两个函数找不到源代码,导致不会分析

% assume zero skew and centered principal point for initial guess
cx = (imageSize(2)-1)/2;
cy = (imageSize(1)-1)/2;
[fx, fy] = vision.internal.calibration.computeFocalLength(H, cx, cy);
A = vision.internal.calibration.constructIntrinsicMatrix(fx, fy, cx, cy, 0);

下一步到 computeV
然后我们来看computeV的源代码:

function V = computeV(homographies)
% Vb = 0
numImages = size(homographies, 3);
V = zeros(2 * numImages, 6);
for i = 1:numImages
    H = homographies(:, :, i)';
    V(i*2-1,:) = computeLittleV(H, 1, 2);
    V(i*2, :) = computeLittleV(H, 1, 1) - computeLittleV(H, 2, 2);
end

对应这部分代码
在这里插入图片描述

function B = computeB(V)
% lambda * B = inv(A)' * inv(A), where A is the intrinsic matrix

[~, ~, U] = svd(V);
b = U(:, end);

% b = [B11, B12, B22, B13, B23, B33]
B = [b(1), b(2), b(4); b(2), b(3), b(5); b(4), b(5), b(6)];

在这里插入图片描述

function A = computeIntrinsics(B)
% Compute the intrinsic matrix

cy = (B(1,2)*B(1,3) - B(1,1)*B(2,3)) / (B(1,1)*B(2,2)-B(1,2)^2);
lambda = B(3,3) - (B(1,3)^2 + cy * (B(1,2)*B(1,3) - B(1,1)*B(2,3))) / B(1,1);
fx = sqrt(lambda / B(1,1));
fy = sqrt(lambda * B(1,1) / (B(1,1) * B(2,2) - B(1,2)^2));
skew = -B(1,2) * fx^2 * fy / lambda;
cx = skew * cy / fx - B(1,3) * fx^2 / lambda;
A = vision.internal.calibration.constructIntrinsicMatrix(fx, fy, cx, cy, skew);
if ~isreal(A)
    error(message('vision:calibrate:complexCameraMatrix'));
end

在这里插入图片描述

function [rotationVectors, translationVectors] = ...
    computeExtrinsics(A, homographies)
% Compute translation and rotation vectors for all images

numImages = size(homographies, 3);
rotationVectors = zeros(3, numImages);
translationVectors = zeros(3, numImages); 
Ainv = inv(A);
for i = 1:numImages
    H = homographies(:, :, i);
    h1 = H(:, 1);
    h2 = H(:, 2);
    h3 = H(:, 3);
    lambda = 1 / norm(Ainv * h1); %#ok
    
    % 3D rotation matrix
    r1 = lambda * Ainv * h1; %#ok
    r2 = lambda * Ainv * h2; %#ok
    r3 = cross(r1, r2);
    R = [r1,r2,r3];
    
    rotationVectors(:, i) = vision.internal.calibration.rodriguesMatrixToVector(R);
    
    % translation vector
    t = lambda * Ainv * h3;  %#ok
    translationVectors(:, i) = t;
end

rotationVectors = rotationVectors';
translationVectors = translationVectors';

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值