空间建模的数据帧长什么样?HMS_SpatialRecon_DataFrame 详解

空间建模的核心就是"喂数据"——你把一帧帧的图像数据推进去,系统才能帮你建出 3D 模型。这些数据帧就是 HMS_SpatialRecon_DataFrame 结构体。这篇文章来仔细看看这个结构体的每个字段是什么意思,怎么填,以及相关的两个函数怎么用。

DataFrame 的完整结构

下面的流程图展示了 DataFrame 数据结构的三大组成部分及其关系:

HMS_SpatialRecon_DataFrame

相机内参

相机外参

图像数据

focalX/focalY: 焦距

principalX/principalY: 主点

distortionCoef: 8个畸变参数

imageWidth/imageHeight: 图像尺寸

position: 3D位置x,y,z

rotation: 四元数x,y,z,w

timestamp: 时间戳ns

imageData: 像素数据指针

format: RGB格式

先看看这个结构体都有哪些字段:

typedef struct HMS_SpatialRecon_DataFrame {
    float focalX;                      // X轴焦距,单位px
    float focalY;                      // Y轴焦距,单位px
    float principalX;                  // 主点X坐标(光心),单位px
    float principalY;                  // 主点Y坐标(光心),单位px
    float distortionCoef[8];           // 失真参数 [k1, k2, p1, p2, k3, k4, k5, k6]
    int32_t imageWidth;                // 图像宽度,单位px
    int32_t imageHeight;               // 图像高度,单位px
    float position[3];                 // 相机3D位置 [x, y, z]
    float rotation[4];                 // 相机旋转四元数 [x, y, z, w]
    int64_t timestamp;                 // 帧时间戳,单位ns
    uint8_t *imageData;                // 图像像素数据指针
    HMS_SpatialReconImageDataFormat format;  // 图像格式(目前只有RGB)
} HMS_SpatialRecon_DataFrame;

字段挺多的,我们分三组来看:相机内参、相机外参、图像数据。

相机内参:描述相机本身的特性

相机内参(Intrinsic Parameters)描述的是相机"长什么样",跟相机在空间中的位置无关。

焦距:focalX 和 focalY

float focalX = 0.0f;  // X轴方向的焦距,单位px
float focalY = 0.0f;  // Y轴方向的焦距,单位px

焦距决定了相机的视野范围。焦距越大,视野越窄(长焦效果);焦距越小,视野越广(广角效果)。通常 focalX 和 focalY 的值很接近,但不完全一样,因为传感器的像素可能不是完美的正方形。

这两个值的单位是像素(px),不是毫米。你可以从相机的标定数据中获取,或者从 AR Engine 的相机参数里直接读。

主点:principalX 和 principalY

float principalX = 0.0f;  // 主点X坐标,单位px
float principalY = 0.0f;  // 主点Y坐标,单位px

主点(Principal Point)是光轴和成像平面的交点。理论上它应该在图像的正中心,但实际上由于制造工艺的原因,会有轻微的偏移。对于大多数手机相机来说,principalX 大约等于 imageWidth/2,principalY 大约等于 imageHeight/2,但不会完全精确。

畸变系数:distortionCoef

float distortionCoef[8];  // [k1, k2, p1, p2, k3, k4, k5, k6]

镜头畸变是真实相机不可避免的问题。这个数组包含了 8 个畸变参数:

  • k1, k2, k3:径向畸变参数,描述图像边缘的桶形或枕形变形
  • p1, p2:切向畸变参数,描述镜头和传感器不平行导致的畸变
  • k4, k5, k6:额外的径向畸变参数,用于更精确的畸变模型

如果你用的是 AR Engine 的数据,这些参数会自动帮你算好。如果是自己获取的相机数据,需要用标定工具来计算这些值。

图像尺寸:imageWidth 和 imageHeight

int32_t imageWidth = 0;   // 图像宽度,单位px
int32_t imageHeight = 0;  // 图像高度,单位px

这个很好理解,就是图像的像素尺寸。注意要和 imageData 的实际数据大小匹配——如果 imageWidth * imageHeight * 3(RGB 三通道)不等于 imageData 的数据长度,就会出问题。

相机外参:描述相机在哪里、朝哪看

相机外参(Extrinsic Parameters)描述的是相机在 3D 空间中的位置和朝向。

位置:position

float position[3];  // [x, y, z]

相机在世界坐标系中的 3D 位置。x、y、z 三个分量分别对应三个轴向。这个值通常来自 SLAM(即时定位与地图构建)系统的追踪结果。

旋转:rotation

float rotation[4];  // [x, y, z, w]

相机的旋转用四元数(Quaternion)表示。四元数是一种避免万向节锁(Gimbal Lock)问题的旋转表示方法,比欧拉角更稳定。四个分量分别是 x、y、z、w。

如果你不熟悉四元数,不用担心。大多数情况下,你会直接从 AR Engine 获取这个值,不需要自己算。

时间戳:timestamp

int64_t timestamp = 0;  // 单位:纳秒(ns)

这一帧被拍摄的时间。时间戳对于空间建模非常重要——系统需要根据时间戳来排列帧的顺序,以及计算帧间的时间间隔。如果时间戳乱了,重建出来的模型可能会变形或者错位。

图像数据:真正的像素信息

像素数据:imageData

uint8_t *imageData = 0;  // 指向原始图像像素数据的指针

这是图像的原始像素数据。注意它是一个指针,你需要确保这块内存在推帧的时候是有效的。推帧完成后,你可以释放这块内存。

图像格式:format

HMS_SpatialReconImageDataFormat format = SPATIAL_RECON_IMAGEDATA_FORMAT_RGB;

目前只支持 RGB 格式,每个像素 3 个字节(R、G、B 各一个字节)。所以 imageData 指向的内存大小应该是 imageWidth * imageHeight * 3 字节。

怎么用 PushFrame 推帧

知道了 DataFrame 的结构,接下来看看怎么用它。

HMS_SpatialReconStatus HMS_SpatialRecon_PushFrame(
    HMS_SpatialRecon_Session *spatialReconSession,
    HMS_SpatialRecon_DataFrame *inputFrame
);

这个函数把一帧数据推入会话。每次调用推一帧,你需要在循环里反复调用它来推入所有帧。

推帧有几个注意事项:

  1. 必须在 StartSession 之前推:一旦 StartSession 被调用,PushFrame 就会返回失败
  2. 帧数据必须有效:如果 imageData 为空、imageWidth/imageHeight 为 0,或者内参没有正确填充,会返回 SPATIAL_RECON_STATUS_INVALID_FRAME_DATA
  3. 有最大帧数限制:推太多帧会返回 SPATIAL_RECON_STATUS_EXCEEDS_MAXIMUM
  4. 帧的顺序和质量很重要:虽然系统不要求严格按时间戳排序,但帧之间需要有足够的重叠区域,否则重建效果会很差

打个比方,推帧就像你给一个拼图游戏提供碎片。碎片越多、越清晰、重叠部分越多,最终拼出来的图就越完整。

用 GetRefinedFrame 获取优化后的帧

空间建模不只是生成 3D 模型,还会优化每一帧的相机参数。你可以通过 GetRefinedFrame 获取优化后的结果。

HMS_SpatialReconStatus HMS_SpatialRecon_GetRefinedFrame(
    HMS_SpatialRecon_Session *spatialReconSession,
    int iFrame,
    HMS_SpatialRecon_DataFrame *outFrame
);

三个参数:

  • spatialReconSession:会话句柄
  • iFrame:帧索引,从 0 开始
  • outFrame:输出参数,函数会把优化后的帧数据写入这个结构体

需要注意的是,这个函数返回的 outFrame 里,imageData 字段是 NULL。它只返回优化后的相机内外参数(焦距、主点、位置、旋转等),不返回图像像素数据。

这个功能有什么用呢?在空间建模的过程中,系统会通过全局优化(比如 Bundle Adjustment)来修正每一帧的相机参数,让所有帧的参数更加一致、更准确。如果你需要把 3D 模型和原始图像做进一步的处理(比如纹理映射),优化后的相机参数会比原始参数效果好很多。

一个实际的推帧流程

下面的流程图展示了手动推帧和使用 AR Engine 推帧两种方式的对比:

AR Engine方式

获取ARFrame

调用PushARFrame

还有更多帧?

推帧完成

手动推帧方式

获取相机内参

获取每帧相机位姿

填充DataFrame所有字段

调用PushFrame推入会话

还有更多帧?

推帧完成

假设你从相机获取图像数据,大致流程是这样的:

  1. 获取相机的内参(焦距、主点、畸变系数)
  2. 每一帧图像到达时,获取当前的相机位姿(位置和旋转)
  3. 填充 DataFrame 结构体
  4. 调用 PushFrame 推入会话
  5. 重复 2-4 直到所有帧推完

如果你用的是 AR Engine 的 ARSession 和 ARFrame,可以跳过这些手动填充的步骤,直接用 PushARFrame 把 AR 帧推进去就行。AR Engine 已经帮你搞定了相机参数和追踪数据。

DataFrame 是空间建模最核心的数据结构,理解它的每个字段对于正确使用空间建模 API 至关重要。如果你是通过 AR Engine 获取数据,可以不用太关心这些细节;但如果你需要自己提供数据,这些字段一个都不能填错。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐