提示:这里可以添加学习目标
提示:这里可以添加要学的内容
相机标定的原理:找到合适的相机内外参数和畸变系数来使相机对棋盘格的重投影误差最小
相机标定的最终目的是:建立相机成像几何模型并矫正相机畸变。
它们主要分为两大类,桶形畸变和枕形畸变。
桶形畸变是由于图像放大率随着离光轴的距离增加而减小,而枕形畸变却恰好相反。在这两种畸变中,穿过图像中心和光轴有交点的直线还能保持形状不变。
除了透镜的形状会引入径向畸变外,在相机的组装过程中由于不能使得透镜和成像面严格平行也会引入切向畸变。
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矩阵的估计。
在张正友标定中,用于标定的棋盘格是三维场景中的一个平面P,其在成像平面的像是另一个平面p,单应性矩阵就是指两个平面之间的映射关系,准确的来说是世界坐标系和像素坐标系之间的映射关系。
p = HP
在 单应矩阵中,我们根据标定棋盘图纸及其对应的照片已经可以得到单应矩阵H了。如下所示:
下一步如何求相机内外参数呢?
我们知道H是内参矩阵和外参矩阵的混合体,而我们想要最终分别获得内参和外参。所以需要想个办法,先把内参求出来(先求内参是因为更容易,因为每张图片的内参都是固定的,而外参是变化的),得到内参后,那张标定图片的外参也就随之解出了。
求解思路是利用旋转向量的约束关系,以下是具体推导,建议自己演算一遍,加深理解。
为了利用旋转向量之间的约束关系,我们先将单应性矩阵H化为3个列向量,即H=[h1 h2 h3],则有
按元素对应关系可得:
因为旋转向量在构造中是相互正交的,即r1和r2相互正交,由此我们就可以利用“正交”的两个含义,得出每个单应矩阵提供的两个约束条件:
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.
参数:
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';