摄像机与物体追踪是高级虚拟制片工作流的关键组成部分。 然而,行业面临一个重大挑战:大量追踪设备采用不同的通信协议,为硬件制造商、软件开发者和制作团队带来复杂性。
全功能追踪协议(C-Tracking)解决了这一难题。该协议由行业专家开发,是一款 免费、统一且面向未来的协议,旨在实现跨平台设备的追踪流程标准化。其精度高、灵活性强、实施简单,且无限制免费使用,堪称业界理想标准。
无论您从事硬件开发、虚拟制片平台搭建,还是管理大规模制作流程,C-Tracking协议均能精准适配需求,且无需许可费用或使用限制。
下载《全功能追踪协议技术规范》
掌握协议实施方案,加入无缝标准化追踪技术革命
无需注册,即刻获取
修订版 43
本协议用于通过网络传输设备追踪数据。C-Tracking专为发送实际物理值设计(详见附录D)。除追踪信息外,还可包含其他测量数据。
本协议的消息通过UDP传输。消息最大长度(不含IP和UDP头)为1400字节。
消息由源("发送方")生成,并发送至预设的目标IP地址-UDP端口对("接收方")。 消息可按固定间隔生成,或在数据可用时立即发送(由发送方决定)。目标IP地址可为单播、广播或组播。无反向通信(从接收方到发送方)。
UDP数据包应包含单个逻辑设备的信息,发送至同一端口的所有UDP数据包应包含同一设备的信息。 若设备为摄像机,则应在一个数据包中包含其所有信息(如镜头参数、位置等)。 设备也可仅表示空间中单个物体的位置和/或方向测量,此时数据包可能仅包含位置/方向信息。
可使用任意目标端口号,但默认值不建议使用1024以下的端口号,因为若接收系统需要特殊权限监听此类端口, 可能会引发问题。首个设备的推荐默认目标端口为2001,其他设备使用后续端口号。
数据包格式灵活,所有传输数据(除短报文头外)均包含在可选的数据元中。 这允许发送方省略未知或不适用于所报告设备的数据(例如,追踪设备的镜头畸变信息)。 但每个数据包必须包含该设备所有可用数据,即使自上次数据包以来未发生变化。
数据包中跨越多字节的所有数字字段均以大端字节序存储。有符号整数字段采用二进制补码表示法。 浮点数值根据IEEE 754-2008以binary32("单精度")格式编码。仅在明确声明处允许使用无限值和NaN值。 所有发送的NaN值必须为静默(非信号型),带有指定有效载荷,若未提及则有效载荷为0。
下表中所用类型的描述:
报文头从数据包地址0开始。
接收方丢弃协议标识符不符的数据包。
接收方丢弃报文头长度小于6的数据包。若报文头长度大于6,接收方将静默忽略超出前6字节的内容。
协议标识符 | 4字节 | "CTrk"(大端字节序值为0x4354726B) |
报文头长度 | uint16 | 6 |
所有数据元均为可选。但建议发送方在数据包中包含帧率元素。 若缺少此元素,接收方需假设数据包可能非定期到达。除非元素描述中另有说明,否则每个数据元在单个数据包中只能出现一次。
所有数据元起始地址必须对齐到4的倍数(首个数据元在报文头后对齐)。 发送方需在报文头后、数据元之间及最后一个数据元后(可选)插入最多3个填充字节(置0)以满足对齐要求。 接收方忽略填充字节内容。数据包在最后一个数据元后最多只能包含3个填充字节。
接收方丢弃包含长度小于4的数据元的数据包。
接收方静默忽略未知类型的数据元。
若接收方遇到长度小于预期的数据元,将静默忽略该数据元。
若接收方遇到长度大于预期的数据元,将按预期长度解析并忽略多余数据。下一数据元的地址仍基于ElementLength字段值计算。
元素类型 | uint16 | 元素类型标识符 |
元素长度 | uint16 | 元素总长度(包含ElementType和ElementLength字段,不包含任何填充字节) |
有效载荷 | 元素类型特定的数据内容 |
本元素包含数据包的时间码信息。
HH:MM:SS:FF时间码的四个部分通过独立字段传输,另含一个可选的子帧索引字段(详见下文)。
时间码基准通常与发送系统的帧率一致。协议支持最高255的整数时间码基准,但仅基准值≤30时符合SMPTE标准。 若需为更高帧率发送SMPTE兼容时间码,应将Base设置为帧率最高除数(且≤30)。此时连续多帧将共享相同HH:MM:SS:FF时间码。 为区分这些帧,需在帧号相同时逐帧递增Subframe值,帧号变化时将其归零。
示例:
表示50帧/秒时,Base可直接设为50。此时Frame在单秒内从0递增至49,Subframe始终为0。
为符合SMPTE标准,Base可设为25。此时Frame仅从0递增至24后归零,每个HH:MM:SS:FF时间码将重复出现在连续两帧中:第一帧Subframe设为0,第二帧设为1。
对于NTSC制式帧率(23.976、29.97、59.94、119.88、239.76):
分别将Base设为24/30/60/120/240,并在Flags中置位NTSC比特(最低有效位)。
SMPTE兼容表示中,无子帧时的最高可表示帧率为29.97。例如表示239.76帧/秒时:设Base为30,置位NTSC比特,并循环使用0-7的Subframe值区分相同HH:MM:SS:FF值。
注:除23.976外,所有基于NTSC的帧时间码均默认为丢帧模式。
元素类型 | uint16 | 0 |
元素长度 | uint16 | 11 |
小时 | uint8 | 时间码的小时部分(0-23) |
分钟 | uint8 | 时间码的分钟部分(0-59) |
秒 | uint8 | 时间码的秒部分(0-59) |
帧 | uint8 | 时间码的帧部分 |
子帧索引 | uint8 | 子帧编号 |
时间码基准 | uint8 | 时间码的基准帧率 |
标志位 | uint8 | 最低有效位:1表示NTSC帧率(如29.97fps),0表示其他帧率 其他位:保留位(发送时必须置0) |
本元素包含摄像机的视场角(FOV)和帧画面宽高比信息。
元素类型 | uint16 | 1 |
元素长度 | uint16 | 12 |
水平方向视场角 | single | 水平方向视场角 |
帧画面宽高比 | single | 帧画面宽高比 |
本元素包含基于K1/K2系数的摄像机镜头畸变参数。
仅用于描述通用镜头的径向畸变,不适用于鱼眼镜头、变形镜头或移轴镜头的畸变描述。
包含本元素的数据包必须同时包含"视场角"元素。若无视场角信息,本元素内容对接收方无效。
本元素字段包含OpenCV畸变模型系数(详见附录B),默认仅使用K1/K2系数(其他系数视为0)。
未测量/校准的字段应置零。
基础参数集与扩展参数集的选用原则参见附录B.2。
元素类型 | uint16 | 2 |
元素长度 | uint16 | 20 |
X坐标 | single | 主点Y坐标(相对图像中心归一化坐标: 上边缘=-0.5,下边缘=0.5) |
Y坐标 | single | 主点Y坐标(相对图像中心归一化坐标: 上边缘=-0.5,下边缘=0.5) |
K1 | single | 第一径向畸变系数 |
K2 | single | 第二径向畸变系数 |
本元素包含摄像机镜头的扩展畸变参数集。
本元素仅用于描述通用镜头的畸变,不适用于鱼眼镜头、变形镜头或移轴镜头的畸变描述。
包含本元素的数据包应同时包含"视场角"元素。若无视场角信息,本元素内容对接收方无效。
本元素字段包含OpenCV畸变模型的系数。畸变模型描述见附录B。
未测量/校准的字段应置零。
基础参数集与扩展参数集的选择参见附录B.2。
元素类型 | uint16 | 3 |
元素长度 | uint16 | 60 |
X坐标 | single | 主点X坐标,相对于图像中心。图像左边缘为-0.5,右边缘为0.5。 |
Y坐标 | single | 主点Y坐标,相对于图像中心。图像上边缘为-0.5,下边缘为0.5。 |
K1 | single | 第一径向畸变系数 |
K2 | single | 第二径向畸变系数 |
K3 | single | 第三径向畸变系数 |
K4 | single | 第四径向畸变系数 |
K5 | single | 第五径向畸变系数 |
K6 | single | 第六径向畸变系数 |
P1 | single | 第一切向畸变系数 |
P2 | single | 第二切向畸变系数 |
S1 | single | 第一薄棱镜畸变系数 |
S2 | single | 第二薄棱镜畸变系数 |
S3 | single | 第三薄棱镜畸变系数 |
S4 | single | 第四薄棱镜畸变系数 |
本元素包含摄像机对焦物体的距离值。
距离值以入瞳位置为基准测量(详见附录B),可能与从相机传感器计算的物理距离存在差异,但在大多数实际应用中差异可忽略不计。
元素类型 | uint16 | 4 |
元素长度 | uint16 | 8 |
对焦距离 | single | 对焦距离(单位:米) |
本元素包含摄像机传感器的相关信息。
元素类型 | uint16 | 5 |
元素长度 | uint16 | 16 |
有效传感器宽度 | single | 用于生成输出图像的传感器部分宽度(毫米),未知时可设为零 |
有效传感器高度 | single | 用于生成输出图像的传感器部分高度(毫米),未知时可设为零 |
有效水平分辨率 | unit16 | 用于生成输出图像的传感器部分水平分辨率(像素),未知时可设为零 |
有效垂直分辨率 | unit16 | 用于生成输出图像的传感器部分垂直分辨率(像素),未知时可设为零 |
本元素包含摄像机光圈宽度的f数值。
元素类型 | uint16 | 6 |
元素长度 | uint16 | 8 |
光圈值 | single | f数值(例如f/5.6应表示为5.6) |
本元素包含图像边缘亮度逐渐衰减的信息。
本元素仅用于描述具有圆形渐晕效果的通用镜头,可能不适用于鱼眼镜头、变形镜头或移轴镜头。
本元素包含任意数量N(≥1)的数据字段。 每个字段包含图像中心特定距离处的亮度衰减比例,取值0.0-1.0(0.0表示无衰减,1.0表示完全变黑)。
数值按从中心到角落的顺序等距排列,第1个值:中心到角落1/N处的衰减,第2个值:中心到角落2/N处的衰减,第N个值:角落处的衰减
图像中心点的亮度衰减值不传输,默认始终为0。
元素类型 | uint16 | 7 |
元素长度 | uint16 | 元素长度 = 8+4*比率 |
亮度衰减计数 | uint16 | 数据包中的比例字段数量(N) |
保留字段 | uint16 | Zero |
比例1 | single | 从图像中心到角落1/N处的亮度衰减比例 |
比例2 | single | 从图像中心到角落2/N处的亮度衰减比例 |
... | ||
比例 | single | 图像角落处的亮度衰减比例 |
本元素包含摄像机或独立追踪设备的位置信息。
如果设备是摄像机,所报告的位置是入瞳的位置,如附录B所述。
位置表示为平移和旋转的组合。按照通常的顺序(先旋转,然后平移)应用这些变换来表示位置。
平移以米为单位,在右手坐标系中指定。坐标系的轴表示追踪系统的右(X)、上(Y)和后(Z)方向。
如果追踪系统能够确保,Y = 0应对应于地面高度,Y > 0表示地面以上的物体。
旋转表示为单位四元数(使用Hamilton定义的基元素乘法),形式为(x, y, z, w),其中x、y和z是对应于相应坐标的组件,w是实部。
平移值的误差在TranslationError字段中描述。该字段包含所报告平移与真实平移向量之间差异的最大幅度,置信度为99.73%(3σ,假设测量误差呈正态分布)。
如果平移未知,平移误差字段应设置为正无穷大。如果平移的误差未知,平移误差字段应包含零。
旋转值的误差在RotationError字段中描述。它包含所报告旋转(由RotationX、RotationY、RotationZ和RotationW字段描述)与真实旋转之间的最大旋转角度,置信度为99.73%。
如果旋转未知,旋转误差字段应设置为正无穷大。如果旋转的误差未知,旋转误差字段应包含零
元素类型 | uint16 | 8 |
元素长度 | uint16 | 40 |
平移X | single | 平移向量的X分量(米) |
平移Y | single | 平移向量的Y分量(米) |
平移Z | single | 平移向量的Z分量(米) |
旋转X | single | 旋转四元数的X分量 |
旋转Y | single | 旋转四元数的Y分量 |
旋转Z | single | 旋转四元数的Z分量 |
平移误差 | single | real component of the rotation quaternion |
TranslationError | single | 平移向量的误差(米)。若平移向量未知则为+∞;若误差未知则为0.0 |
旋转误差 | single | 旋转分量的误差(弧度)。若旋转未知则为+∞;若误差未知则为0.0 |
本元素包含摄像机或独立追踪设备的速度和角速度信息。这些信息可帮助接收方在数据包间隔期间估算设备位置。
速度:与位置相同的坐标系(米/秒)
角速度(每秒):采用与旋转相同的单位四元数表示
若速度或角速度未知,对应分量字段应设为NaN
元素类型 | uint16 | 9 |
元素长度 | uint16 | 32 |
速度X | single | 速度向量的X分量(米/秒);若速度未知则为NaN |
速度Y | single | 速度向量的Y分量(米/秒);若速度未知则为NaN |
速度Z | single | 速度向量的Z分量(米/秒);若速度未知则为NaN |
角速度X | single | 角速度四元数的X分量(每秒);若角速度未知则为NaN |
角速度Y | single | 角速度四元数的Y分量(每秒);若角速度未知则为NaN |
角速度Z | single | 角速度四元数的Z分量(每秒);若角速度未知则为NaN |
角速度W | single | 角速度四元数的实部(每秒);若角速度未知则为NaN |
本元素包含追踪设备的帧率信息。
该元素的存在表示发送方以固定间隔生成数据包,每个数据包的间隔时间为1/帧率秒。若包含此元素,发送方必须尽可能按照该时间间隔发送数据包。
帧率以有理数形式表示(两个整数的商)。这是精确表示基于NTSC的帧率所必需的。
对于非NTSC情况:
对于NTSC情况:
元素类型 | uint16 | 10 |
元素长度 | uint16 | 12 |
帧率分子 | uint32 | 每秒帧数值的分子部分 |
帧率分母 | uint32 | 每秒帧数值的分母部分 |
本元素可用于报告测量值。该测量值可以是常见的编码器值(如变焦、对焦或光圈),也可以是任何自定义测量值(例如温度)。一个数据包中可以包含多个此类元素,但每个实例的MeasurementType字段必须包含不同的值。
重要说明:
元素类型 | uint16 | 11 |
元素长度 | uint16 | 20 |
测量类型 | uint32 | 标识元素中包含的值的类型: 0表示变焦编码器值 1表示对焦编码器值 2表示光圈编码器值 其他值表示自定义测量值 |
测量值 | single | 测量值 |
最小值 | single | 测量值的最小可能值,若未知则为NaN。若无下限则可为负无穷大 |
最大值 | single | 测量值的最大可能值,若未知则为NaN。若无上限则可为正无穷大 |
接收方可根据镜头焦距、对焦距离和光圈宽度计算景深。其中焦距本身是通过视场角和用于生成输出图像的相机传感器部分尺寸计算得出的。
这意味着数据包需要包含以下元素,接收方才能计算景深信息:
传输数据中包含"水平视场角"和"画面宽高比"值。如需计算垂直视场角,可通过以下公式得出:
垂直视场角 = 2 × arctan(tan(水平视场角/2) / 画面宽高比)
\[ \textit{FOV}_y = 2 \cdot \textit{arctan}\left( \textit{tan}\left( \frac{\textit{FOV}_x}{2} \right) \cdot \frac{1}{\textit{AR}} \right) \]
为了表示相机的投影和畸变,我们采用针孔相机模型,并附加一个类似于OpenCV(详见 https://docs.opencv.org/4.x/d9/d0c/group__calib3d.html#details)使用的畸变模型。该模型能很好地描述大多数通用相机镜头,但不适用于描述鱼眼镜头、变形镜头或移轴镜头的畸变。
该模型以相机的入瞳(透视中心,有时也错误地称为节点)为中心。入瞳不是镜头或相机中的物理点,而是必须放置虚拟针孔相机以实现与真实相机视野完全重叠的点。入瞳位于相机光轴上,镜头后方某处。对于变焦镜头,其位置不固定,会随着视场角的变化而前后移动。
相机的坐标系以入瞳为中心,X轴向右,Y轴向上,Z轴向后。
要找到该坐标系中坐标为(xc, yc, zc)的点在相机显示的像素坐标(u, v),按照以下步骤进行:
首先将坐标投影到z=1平面:
$$x_1 = \frac{x_c}{z_c}$$
$$y_1 = \frac{y_c}{z_c}$$
光学轴距离的计算:
$$r = \sqrt{x_1^2 + y_1^2}$$
应用镜头畸变:
$$ x_d = x_1 \cdot \frac{1 + k_1 r^2 + k_2 r^4 + k_3 r^6}{1 + k_4 r^2 + k_5 r^4 + k_6 r^6} + 2p_1 x_1 y_1 + p_2 (r^2 + 2x_1^2) + s_1 r^2 + s_2 r^4 $$
$$ y_d = y_1 \cdot \frac{1 + k_1 r^2 + k_2 r^4 + k_3 r^6}{1 + k_4 r^2 + k_5 r^4 + k_6 r^6} + p_1 (r^2 + 2 y_1^2) + 2 p_2 x_1 y_1 + s_3 r^2 + s_4 r^4 $$
将畸变后的点投影到图像传感器上
$$u = f_x \cdot x_d + c_x$$
$$v = f_y \cdot y_d + c_y$$
系数k1至k6表示径向畸变(桶形畸变、枕形畸变或两者的组合)。p1和p2表示切向(偏心)畸变。s1至s4表示薄棱镜畸变。"镜头畸变"元素包含这些系数。如果其中任何系数未经过测量,应将其值设为零传递。
通用镜头中最常见的畸变类型是径向畸变,通常仅使用k1和k2系数就能相当准确地描述。如果在模型中仅使用这两个系数计算镜头畸变,"镜头畸变"元素中的其余字段应设为零。
在最后一步,fx和fy包含以像素为单位的焦距值,(cx, cy)是以像素为单位的像主点坐标(光学轴与图像传感器相交的点)。为了避免依赖于摄像机传输图像的分辨率,这些值不会直接包含在数据包中。作为替代,传输以下数据:
$$\textit{FOV}_x = 2 \cdot \textit{arctan}\left(\frac{r_x}{2 f_x}\right)$$
\[ \textit{AR} = \frac{r_x}{r_y} \cdot \frac{f_x}{f_y} \]
(视场角以度为单位测量),以及在"镜头畸变"元素中:
\[ C_x = \frac{c_x}{r_x} - 0.5 \]
\[ C_y = \frac{c_y}{r_y} - 0.5 \]
其中rx和ry分别表示图像的水平分辨率和垂直分辨率。
虽然扩展模型是基础模型的超集,但其K1和K2系数可能与基础模型的K1和K2值不同。发送方应传输进行镜头校准时所使用的模型。然而,如果发送方使用扩展模型,强烈建议同时计算并传输基础参数集,或者至少在其用户界面中提供切换至基础参数集的可选功能。
接收方可以根据自身需求,选择仅支持其中一种模型。
本协议仅接受一种格式的镜头畸变数据。如果镜头畸变是使用其他模型计算的,则需要将其转换为这种表示形式。具体的转换方法取决于所使用的畸变模型;下面列出了一些常见的差异。
相机模型通常使用焦距来描述视场角;而本协议则需要使用视场角数值。通常情况下...
$$\textit{FOV}_x = 2 \cdot \textit{arctan}\left(\frac{s_x}{2 f_x}\right)$$
$$\textit{FOV}_y = 2 \cdot \textit{arctan}\left(\frac{s_y}{2 f_y}\right)$$
可以通过公式从焦距(fx, fy)和传感器尺寸(sx, sy)计算出视场角。这两个量必须使用相同的测量单位。 校准工具通常以像素为单位输出fx和fy,在这种情况下,sx和sy分别是图像的水平尺寸和垂直尺寸。 不过某些设备可能以毫米为单位输出这两个值,在这种情况下可以使用相同的公式。
其他模型可能使用不同的单位或方向来描述像主点。如果模型的描述没有明确说明像主点是什么,通常最简单的方法是将其视为径向畸变的中心点。
某些模型以相反的顺序应用畸变和投影;也就是说,它们首先将物体坐标投影到传感器上(使用fx, fy, cx和cy), 然后在此之后应用畸变。要将此类模型的畸变系数转换为本协议中使用的系数,首先需要将图像重新投影到单位距离的平面上,并相应地转换系数。
此外,系数的值取决于坐标单位的选择。坐标通常以像素、毫米(在相机传感器上)或图像宽度、高度或对角线的倍数来指定。 例如,如果坐标以毫米为单位测量,则k1的单位为mm-2,k2的单位为mm-4。在将系数转换为协议中使用的表示形式时,需要考虑测量单位。
在本协议使用的模型中,未畸变的坐标x1和y1是在距离入瞳单位距离的平面上测量的,这可以被视为一种自然的表示形式。这意味着畸变系数既独立于焦距,也独立于用于测量xc、yc和zc的单位。
还应注意,从技术上讲,使用焦距(fx和fy)在投影后应用的畸变无法用本协议中的模型表示(反之亦然):一种模型中的圆形对称径向畸变通常会导致另一种模型中的非圆形畸变。 然而,只要fx和fy几乎相同——就像任何不刻意在一个方向上压缩图像的镜头那样——结果在视觉上应该是无法区分的。
除上述情况外,某些模型以反向方式描述畸变;即它们将未畸变像素的坐标表示为畸变像素坐标的函数。
通常没有精确的方法来计算此类模型的反向形式,但这里有一种适用于畸变不太大的情况的方法。areaWidth指的是投影区域,通常是相机传感器使用部分的宽度。其单位取决于您所转换的模型。
bool InvertRadial(double K1, double K2, float areaWidth, double& invK1, double& invK2)
{
invK1 = invK2 = 0.0;
const int N = 5;
double pa = 0.5f * areaWidth;
double dx = 1.0 / N;
double x = dx;
double D[N][2];
double d[N];
for (int i = 0; i < N; i++, x += dx)
{
double undist = x;
double p = x * pa;
double p2 = p * p;
p *= (1.0f + p2 * (K1 + p2 * K2));
double dist = p / pa;
double r = dist * pa;
double q = undist * pa;
double r2 = r * r;
D[i][0] = r * r2;
D[i][1] = r * r2 * r2;
d[i] = q - r;
}
// D.transp() * D
double A[2][2];
memset(A, 0, sizeof(A));
for (int i = 0; i < N; i++)
{
A[0][0] += D[i][0] * D[i][0];
A[0][1] += D[i][0] * D[i][1];
A[1][1] += D[i][1] * D[i][1];
}
A[1][0] = A[0][1];
// (D.transp() * D).inv()
double B[2][2];
double det = (A[0][0] * A[1][1] - A[0][1] * A[1][0]);
if (fabs(det) < 1e-14) return false;
B[0][0] = A[1][1] / det;
B[1][1] = A[0][0] / det;
B[0][1] = B[1][0] = -A[0][1] / det;
// (D.transp() * D).inv() * D.transp()
double C[2][N];
for (int i = 0; i < N; i++)
{
C[0][i] = B[0][0] * D[i][0] + B[0][1] * D[i][1];
C[1][i] = B[1][0] * D[i][0] + B[1][1] * D[i][1];
}
// (D.transp() * D).inv() * D.transp() * d
for (int i = 0; i < N; i++)
{
invK1 += C[0][i] * d[i];
invK2 += C[1][i] * d[i];
}
return true;
}
C-Tracking协议专为传输真实物理量值而设计。例如,建议直接发送真实的视场角(FOV)和镜头畸变数据,而非变焦传感器的原始编码器数值。 这要求在追踪设备端完成镜头校准工作。虽然这可能需要稍多的技术投入,但回报是使追踪设备成为真正的即插即用设备。 对于某些情况(例如PTZ摄像机),这项工作可以在工厂完成。对于一批具有相同构建参数的摄像机,只需进行一次校准,校准配置文件即可存储在固件中。