把一件小摆件放进手机里的展厅,最容易想到的流程是拍摄、重建、展示。真正影响体验的往往是两次交接:拍摄数据什么时候足够,以及重建结果什么时候可以交给浏览器。若把拍完、算完和保存成功合成同一个“完成”,用户可能拿到半个模型,退出页面后还留下无法继续的任务。
这个方案以单件静态摆件为输入,设计一次可取消、可恢复检查、可验证产物的端侧任务。展示端复用 GS 模型加载能力;重建端单独拥有采集数据和任务状态,避免让显示页面承担长任务管理。
验证范围与设备限制
测试设备为 Pura 70 Pro+(HBN-AL80,麒麟 9010),运行 API 26 系统。官方重建指南仅保证麒麟 9020、9030S、9030、9030 Pro 及以后旗舰芯片上的重建体验;这一推荐范围不能单独代替设备能力检测。
在HBN-AL80、API 26真机上调用HMS_SpatialRecon_IsSupport(SPATIAL_RECON_MODEL_TYPE_GS),本次返回801,对应SDK的SPATIAL_RECON_STATUS_DEVICE_NOT_SUPPORT。页面保持“创建会话”禁用,未执行重建和导出。API 26系统版本不能代替运行时能力检测;这个结果只对应本次设备与环境,不代表所有设备或空间渲染能力均不支持。
检测过程和验证范围见真机记录。
安装3D壁纸所需渲染包后,重新检测及重启实验应用,结果仍为801。该设备的端侧重建验证到此结束,工程保留方案设计、接入代码、宿主回归与能力检测结果;实机送帧、重建耗时、重建产物和导出成功均无可用记录。已有模型的加载与渲染属于另一条验证路径,不受该能力检测结果影响。
接入前先核对这些具体限制
以下约束按2026年9月20日的官方指南核对。系统版本、芯片推荐范围与运行时支持检测是三个条件,不能互相替代。
| 检查项 | 官方约束及接入动作 |
|---|---|
| 区域与设备 | Kit仅面向中国大陆,不含港澳台;列出的设备类型包括手机、平板、PC/2in1和智慧屏,不代表每个型号均支持。模拟器不支持此能力。 |
| 系统与芯片 | 重建能力从HarmonyOS 6.1.0(API 23)提供;官方仅保证麒麟9020、9030S、9030、9030 Pro及以后旗舰芯片的重建体验。其他芯片即使能力检测成功,也不保证耗时和质量;最终仍须调用HMS_SpatialRecon_IsSupport。 |
| 输入尺寸 | 当前仅支持宽1080、高1440的图像。其他尺寸的结果未定义;不能只看总像素数,也不能把1440×1080当作相同输入。 |
| 输入内容 | 可送入AR Engine帧,或自行组装DataFrame;都需要对应的真实相机内参和位姿。自定义图像数据仅支持RGB,JPEG/PNG压缩字节不能直接替代RGB。缩放、裁剪后必须同步校正内参。 |
| AR采集 | 推送AR帧前先更新AR Engine帧,重建侧自动选择关键帧;采集帧数不能直接当作重建进度。 |
| 启动与温控 | 每次HMS_SpatialRecon_StartSession后设置HMS_SpatialRecon_SetRunningMode,对应前台或后台状态;指南建议监听COMMON_EVENT_THERMAL_LEVEL_CHANGED,过热时暂停任务。设置运行模式不等于获得无限后台运行资格。 |
| 并发 | 同一时间只允许一个会话重建,同时重建结果未定义;同一时间也只允许一个会话保存MP4。应用队列应覆盖这些操作的完整结束过程。 |
| 工作目录 | 创建会话前准备应用沙箱内已存在、可写的工作目录,不能把不存在的目录直接传给接口。 |
| 导出与销毁 | 输出支持PLY或MP4;启动时传入非空writeInfo可自动保存,否则在重建完成后调用保存接口。所有会话任务结束后才能销毁;在重建或保存期间销毁、并发销毁或继续使用已销毁指针均不安全。 |
区域、设备类型和模拟器范围见Kit简介;尺寸、芯片体验范围、模式、温控与导出见重建流程;支持检测、目录与释放时机见会话管理。上述资料未给出通用最低内存、存储容量或必须搭载LiDAR的门槛,因此不额外设定硬件要求。
Demo检查通过,仍需核对平台要求
本项目预检器和Native桥接目前允许宽高1…4096,只验证结构、字节长度及预算,尚未强制1080×1440;此外Native启动路径尚未接入SetRunningMode和温控监听。这些是示例继续接入时需要补齐的代码项,不是已经验证的平台行为。宿主测试通过也不能覆盖它们。
清单至多1 MiB、1…1000帧、累计保留帧至多256 MiB是本Demo自己的限制,不是Kit的上限,各预算需要同时满足。Native桥接在释放会话前保留输入内存,因此不能据“支持1000帧”推断当前内存预算能容纳1000张正式尺寸的图像。
实际接入应依次检查区域与系统→运行时支持→尺寸和真实标定→工作目录与预算→单任务启动和运行模式→任务及保存回调→产物加载→安全释放。当前麒麟9010设备在支持检测处返回801,流程就在这里停止;安装壁纸渲染包不能据此认定重建能力已可用。其他设备仍应从支持检测开始核对接入条件。
采集质量决定后面的计算是否值得
透明玻璃、强镜面、高速运动和大面积重复纹理都会增加重建难度。第一组样本应选择纹理可辨、材质相对稳定的物体,保持光照一致,缓慢环绕拍摄,让相邻视角有足够重叠。把物体移动与相机移动混在一起,会破坏“同一静态场景”的基本假设。
采集层应保留每帧对应的时间、位姿和实际要求的图像信息。具体输入格式、AR 会话衔接和有效帧条件必须以 Spatial Recon Kit 指南为准,不能将任意图片数组直接包装成系统要求的数据帧。本地 API 26 SDK 包含空间重建 Native 接口,其基础声明存在 API 23 起始标记,不能把所有会话操作都描述成 API 26 首次新增。
建议采集界面分别显示有效帧数量、运动提示和阶段状态。进度值若只是“已拍张数”,就称为采集数量,不映射成重建完成百分比。重建进度必须来自任务接口的实际反馈。
一份任务记录贯穿三段流程
以下是应用自己的记录合同,不是系统 API 数据结构:
typeReconPhase='capturing'|'ready'|'running'|'cancelling'|'cancelled'|'validating'|'saved'|'failed';interfaceReconRecord{taskId:string;phase:ReconPhase;inputDirectory:string;temporaryOutput:string;finalOutput:string;validFrames:number;errorCode:string;}taskId标识本次尝试,输入目录与最终模型分离。执行失败后,新尝试使用新的任务号;系统提供恢复接口时,再保存对应恢复令牌。没有恢复接口时,应明确重新计算,不能让“继续”按钮实际偷偷从头开始。
页面只发出开始和取消命令。任务服务负责调用平台适配层,平台适配层再管理 Native 会话与回调。这样离开采集页不会把任务误认成成功,也便于给失败路径写确定性测试。
取消是一段过程,不是一个布尔值
用户点击取消后,先进入 cancelling,停止接受新的输入,再调用平台提供的停止或取消入口。只有收到终止结果并完成资源回收后,才进入 cancelled。某个算法阶段若暂不支持取消,应向用户说明仍在等待安全结束,而不是立即销毁底层仍使用的数据。
回调可能晚到。收到事件时同时检查任务号和当前阶段:上一任务的成功事件不能覆盖新任务;已经取消的任务也不能继续把产物加入展厅。应用代次只能阻止接收过期结果,不能代替系统算法实际停止。
产物提交采用临时文件再转正
重建输出先放到任务临时目录。校验至少包括文件存在、长度合理、格式可识别以及展示端能够打开。文件头通过不等于高斯内容有效,因此最后一步应通过实际 GS 加载器执行一次验证。
算法完成 → 关闭输出句柄 → 校验产物 → 尝试加载 → 转入正式模型目录 → 更新目录索引 → 显示“已保存”正式索引只指向成功提交的模型。若应用在校验阶段退出,下次启动扫描自己的任务目录,展示“待恢复检查”;不要把所有临时文件都直接删除,否则可能丢失已完成但尚未登记的计算结果。
重建实验应同时评价完整度和使用成本
固定摆件、采集路径与设备,保存输入数量、计算耗时、峰值内存、输出大小和可浏览视角。用同一轨迹检查背面缺失、漂浮噪点和边缘破碎;只拍一个好看的正面截图无法说明模型完整。
第一轮 Demo 可以只有一件摆件、一个任务与一个模型列表,但必须执行取消、失败重试和重新进入。正式验收时,应从任务日志找到输出文件,再从模型列表打开同一个产物,形成采集到展示的对应关系。
项目落地可先完成任务记录和展示校验,再接采集输入,最后接真实重建调用。这样每一步都有可观察结果,重建算法出错时也能区分输入问题、系统能力问题和产物管理问题。
中断恢复先保证文件一致,再讨论续算
| 中断位置 | 下次进入的动作 | 禁止发生的结果 |
|---|---|---|
| 采集中 | 检查输入是否完整,允许重新采集 | 把不完整输入显示为可重建 |
| 计算中 | 查询真实任务;没有恢复能力就提示重新计算 | 用旧进度冒充继续计算 |
| 已输出未登记 | 校验原产物,通过后补登记 | 重复生成同一模型条目 |
与直接覆盖正式模型相比,临时目录提交多占一段磁盘空间,却能保留旧版本并隔离失败产物。磁盘不足时先拒绝新任务,不删除已收藏模型腾空间。实现次序是登记协议、异常恢复检查、真实采集与重建;第一阶段用受控中断核对索引,最后才比较重建质量。
参考:官方 3DGS 端侧重建课程、空间渲染接口。
采集包怎样进入 Native 会话
实验工程提供独立预检脚本scripts/verify-reconstruction-input.cjs:在送帧前检查清单、RGB字节长度、内参基本约束、单位四元数、递增的纳秒时间戳和累计内存预算,并输出文件哈希。完整字段映射见采集包合同。结构检查不能证明相机标定真实,也不能替代系统重建。
当前Native示例导出PLY,第01、12篇已提供PLY导入入口,保留扩展名后交给GSPlugin。当前SDK导出枚举包含PLY和MP4,未列GLB。导入代码的衔接不代表完整流程已经运行通过:仍需支持重建的设备、真实采集包和实际产物,验证保存回调成功后能否加载展示。
服务层将状态和 Native 命令分开,送帧前检查采集包描述。文件包导入与现场采集是不同入口,导入成功之后仍必须检查会话返回值与导出文件。
import{fileIo}from'@kit.CoreFileKit';import{reconCommand,reconPushFrame}from'libspatial_audio.so';interfaceCaptureFrame{rgbFile:string;metadata:number[];}interfaceCapturePackage{frames:CaptureFrame[];}exportclassReconstructionService{privateclosed:boolean=false;asynccommand(operation:number,directory:string=''):Promise<string>{if(this.closed&&operation!==7){thrownewError('页面已关闭');}returnreconCommand(operation,directory);}asyncimportFrames(directory:string,report:(message:string)=>void):Promise<string>{constmanifest=awaitfileIo.stat(`${directory}/frames.json`);if(manifest.size>1024*1024){thrownewError('采集元数据超过 1 MiB');}consttext=awaitfileIo.readText(`${directory}/frames.json`);constinput=JSON.parse(text)asCapturePackage;if(!input||!Array.isArray(input.frames)||input.frames.length<1||input.frames.length>1000){thrownewError('frames 须包含 1 至 1000 帧');}for(leti=0;i<input.frames.length;i++){if(this.closed){thrownewError('采集包导入已取消');}constframe=input.frames[i];if(!frame||typeofframe.rgbFile!=='string'||!/^[a-zA-Z0-9_-]+\.rgb$/.test(frame.rgbFile)||!Array.isArray(frame.metadata)||frame.metadata.length!==22){thrownewError(`第${i+1}帧的文件名或参数无效`);}constfile=awaitfileIo.open(`${directory}/${frame.rgbFile}`,fileIo.OpenMode.READ_ONLY);letrgb:ArrayBuffer;try{constinfo=awaitfileIo.stat(file.fd);if(info.size<3||info.size>4096*4096*3){thrownewError('RGB 帧大小超出范围');}rgb=newArrayBuffer(info.size);constcount=awaitfileIo.read(file.fd,rgb);if(count!==info.size){thrownewError('RGB 帧未完整读入');}}finally{awaitfileIo.close(file);}if(this.closed){thrownewError('采集包导入已取消');}constresult=awaitreconPushFrame(rgb,frame.metadata);if(!this.closed){report(result);}}return'采集包已送入会话';}close():void{this.closed=true;}}| 操作或边界 | 应检查的结果 |
|---|---|
| 帧描述缺字段 | 阻止送帧并定位缺失字段 |
| 会话执行失败 | 保留平台错误,不产生假模型 |
| 保存与销毁相遇 | 等待保存完成,继续验证设备回调时序 |