0%

BBK-相机内参标定

介绍BBK软件中采用的简易内参标定, 用于计算相机焦距, 本文主要用于老版本HUD和轮眉软件.
新版本相机内参在公司出厂前就会标定好, 不需要现场标定, 并且标定方式也与本文不同

BBK使用的内参标定方法一

原理

小孔成像图自行脑补

D_{\text{物距}} / f_{\text{焦距}} = L_{\text{物体尺寸}} / p_{\text{图像尺寸}}$$$$(D-f)/f = L/p

其中, DD为感光芯片到物体的距离, ff为相机焦距, LL为物体实际尺寸, pp为物体在图像中的像素尺寸
由于无法准确测量像距所以近似认为,D=D物距+f(近似认为像距=焦距)D= D_{\text{物距}} + f(近似认为像距=焦距)
推导出焦距计算公式为:

(Df)/f=D/f1=L/p(D-f)/f = D/f-1 = L/p

D/f=L/p+1=(L+p)/pD/f = L/p + 1 = (L+p)/p

f/D = p/(L+p)$$$$f = D*p / (L+p)

误差说明

  1. 首先公式中唯一需要测量的D即标靶到相机感光芯片的距离要求测量准确
  2. 其次由于近似认为物体成像在一倍焦距上, 所以DfD-f会根据焦距大小不同存在几毫米到到十几毫米的误差, 被测图像越远误差越小

示例: D=2400D = 2400, p=19830.00167p = 1983 * 0.00167, L=280L = 280
f=240019830.00167/(280+19830.00167)=24003.3116/(280+3.3116)=28.0533518571f = 2400 * 1983 * 0.00167 / (280 + 1983 * 0.00167) = 2400 * 3.3116 / (280 + 3.3116) = 28.0533518571
测量误差和像距误差均体现在D上, 假设误差为10mm, 会导致f=24103.3116/(280+3.3116)=28.17024f = 2410 * 3.3116 / (280 + 3.3116) = 28.17024, 焦距误差为0.116888

焦距误差对测量值的影响如下:
体现在投影距离上:

单目:

测量值: Distance=1082/(24950.00167/28.0533518571)=7284.92471Distance = 1082 / (2495 * 0.00167 / 28.0533518571) = 7284.92471
实际值: Distance=1082/(24950.00167/28.17024)=7315.25928Distance = 1082 / (2495 * 0.00167 / 28.17024) = 7315.25928
焦距误差导致的投影距离误差约为: 30mm

双目:

测量值: Distance=28.0533518571/0.0016765/(15841169)=28.0533518571/0.0016765/150=7279.31Distance = 28.0533518571 / 0.00167 * 65 / (1584 - 1169) = 28.0533518571 / 0.00167 * 65 / 150 = 7279.31
实际值: Distance=28.17024/0.0016765/150=7309.643Distance = 28.17024 / 0.00167 * 65 / 150 = 7309.643
焦距误差导致的投影距离误差约为: 30mm

体现在角度上:

测量值: Lda=atan(1320.00167/28.0533518571)=0.45°Lda = atan(132 * 0.00167 / 28.0533518571) = 0.45°
实际值: Lda=atan(1320.00167/28.17024)=0.448°Lda = atan(132 * 0.00167 / 28.17024) = 0.448°
焦距误差导致的角度误差约为:0.002°

BBK软件实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Camera.IntrinsicsCalibriton(_calibrationAgorithm.DotLeftTop,
_calibrationAgorithm.DotRightTop,
_calibrationAgorithm.DotLeftBottom,
_calibrationAgorithm.DotRightBottom,
_calibrationAgorithm.DotHorLength,
_calibrationAgorithm.DotVerLength,
_calibrationAgorithm.DotDistance);

public void IntrinsicsCalibriton(PointF LeftTop, PointF RightTop, PointF LeftBottom, PointF RightBottom, double LengthHor, double LengthVer, double Distance)
{
// 计算四点组成矩形的四条边长(单位像素)
double L1 = Math.Sqrt((RightTop.X - LeftTop.X) * (RightTop.X - LeftTop.X) + (RightTop.Y - LeftTop.Y) * (RightTop.Y - LeftTop.Y));
double L2 = Math.Sqrt((LeftBottom.X - LeftTop.X) * (LeftBottom.X - LeftTop.X) + (LeftBottom.Y - LeftTop.Y) * (LeftBottom.Y - LeftTop.Y));
double L3 = Math.Sqrt((RightBottom.X - LeftBottom.X) * (RightBottom.X - LeftBottom.X) + (RightBottom.Y - LeftBottom.Y) * (RightBottom.Y - LeftBottom.Y));
double L4 = Math.Sqrt((RightBottom.X - RightTop.X) * (RightBottom.X - RightTop.X) + (RightBottom.Y - RightTop.Y) * (RightBottom.Y - RightTop.Y));

double diffPixelX = (L1 + L3) / 2;// 图像平均横向长度(像素)
double diffPixelY = (L2 + L4) / 2;// 图像平均竖向长度(像素)

if ((diffPixelX == 0) || (diffPixelY == 0))
throw new ArgumentOutOfRangeException("boardParam.RightTop , boardParam.LeftTop , boardParam.RightBottom , boardParam.LeftBottom");

double fx = (Distance * diffPixelX * PixelSize) / (LengthHor + diffPixelX * PixelSize);
double fy = (Distance * diffPixelY * PixelSize) / (LengthVer + diffPixelY * PixelSize);
// 相机焦距为XY方向的平均焦距(单位毫米)
Intrinsics_f = fx / 2 + fy / 2;
// 相机内参(单位像素)
Intrinsics_Fx = (fx / PixelSize).Round(4);
Intrinsics_Fy = (fy / PixelSize).Round(4);
// 相机内参(单位像素)
Intrinsics_Cx = PixelWidth / 2;
Intrinsics_Cy = PixelHeight / 2;
}