ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

HarmonyOS 7 Camera Kit + Spatial Recon Kit:3DGS 采集帧质检与中断续采检查点【鸿蒙心迹】

HarmonyOS 7 Camera Kit + Spatial Recon Kit:3DGS 采集帧质检与中断续采检查点【鸿蒙心迹】 3DGS 重建效果不稳定时我以前总是先查重建参数。后来才发现很多问题在“按下开始重建”之前就已经埋进去了模糊帧太多、视角重叠不足、曝光跳变、采集到一半退后台后又从头来。这次我把工程重点前移到采集阶段。一、这篇不再聊模型渲染而是只盯采集入口前面做 3DGS Demo 时我更关注模型怎么加载、怎么渲染、怎么分块。这次项目叫ReconCapture目标完全不同先保证送进空间重建链路的输入足够稳定。测试会话固定为sessionIdCAP-20260930-026 候选帧156 通过质检112 过滤帧44 检查点第 96 帧 恢复次数1 最终状态READY_FOR_RECON44 张被过滤的原因分成运动模糊21 重叠不足14 曝光异常9这里必须先说明一个边界。ReconCapture 的“检查点恢复”是我在应用采集阶段自己做的。它不是在声称 Spatial Recon Kit 的重建 Session 支持任意中断后继续跑。我的设计是Camera Kit 采集 ↓ 应用侧帧质检 ↓ 保存通过帧 相机元数据 检查点 ↓ 达到条件后 ↓ 新建 Spatial Recon 重建任务如果应用退到后台恢复的是“采集清单”不是已经开始执行的重建会话。这个区别非常重要不然“续采”和“续建”很容易被混成一件事。二、相机预配置先做能力检查不要默认设备都支持同一套参数Camera Kit Native API 提供预配置能力可以快速选择 720P、1080P、4K、高质量等预设和不同画幅比例。但官方文档明确要求在使用预配置之前先通过OH_CaptureSession_CanPreconfig或对应的比例检查接口确认设备支持情况。ReconCapture 的原则是采集规格可以降级但不能硬启动一个设备不支持的组合。我会优先尝试高质量 4:3不支持时回落到 1080P 4:3。伪代码逻辑是if(OH_CaptureSession_CanPreconfig(session,PRECONFIG_HIGH_QUALITY)){useHighQuality();}else{use1080P();}这层只解决相机能力匹配不负责判断某一帧“适不适合重建”。真正的帧质量门控在应用层。三、一张图能拍下来不代表它值得进入 3DGS 输入集ReconCapture 给每一张候选帧计算三个业务指标clarityScore overlapScore exposureScore它们不是 Spatial Recon Kit 官方要求必须提供的字段而是 Demo 自己的前置质量门。我没有追求一套复杂视觉算法而是先做足够可解释的规则clarityScore 0.72 overlapScore 0.55 exposureScore 0.65只要任何一项不通过这张图就不会写进重建输入 manifest。这段代码解决什么问题把“拍到了”与“允许进入重建输入集”分成两个状态。export interface FrameQuality { clarityScore: number overlapScore: number exposureScore: number } export interface QualityDecision { accepted: boolean reason?: BLUR | LOW_OVERLAP | BAD_EXPOSURE } export class FrameQualityGate { evaluate(q: FrameQuality): QualityDecision { if (q.clarityScore 0.72) { return { accepted: false, reason: BLUR } } if (q.overlapScore 0.55) { return { accepted: false, reason: LOW_OVERLAP } } if (q.exposureScore 0.65) { return { accepted: false, reason: BAD_EXPOSURE } } return { accepted: true } } }这里的阈值全部是 ReconCapture Demo 的测试参数不是官方固定门槛。真实项目应该拿自己的目标场景测试。扫室内客厅、展厅和户外建筑运动速度、纹理密度、光照条件差异都很大一组阈值不可能覆盖全部产品。四、质检结果不能只显示“通过 / 不通过”还要立刻反向指导采集采集页面如果只默默丢帧用户会很困惑明明拍了很久为什么有效帧一直不涨。所以 ReconCapture 把最近一次拒绝原因直接变成采集提示BLUR → 移动慢一点 LOW_OVERLAP → 回到上一个视角附近继续 BAD_EXPOSURE → 调整拍摄方向避免强逆光这样帧质检就不只是后台过滤器而是采集 UI 的反馈源。代码里会把候选数和接受数分开维护这段代码解决什么问题被过滤的帧不会增加有效进度同时把拒绝原因反馈给页面。private async handleFrame(sample: FrameSample): Promisevoid { this.candidateCount 1 const decision this.qualityGate.evaluate(sample.quality) if (!decision.accepted) { this.rejectedCount 1 this.lastRejectReason decision.reason ?? return } this.acceptedCount 1 await this.manifest.append(sample) if (this.acceptedCount % 24 0) { await this.saveCheckpoint() } }这里检查点按“有效帧”保存而不是按候选帧保存。因为如果用户连续晃动产生 30 张模糊帧我不希望检查点看起来前进了 30 帧实际重建输入却完全没有增长。DevEco Studio 图里同样能看到这组数据156 个候选、112 个接受、44 个拒绝。HiLog 里把三类过滤数量也拆开了方便判断问题到底来自手抖、路线走偏还是光照。五、检查点保存的不是一张图而是一份“可恢复采集清单”第 96 张有效帧时ReconCapture 保存了一次检查点。内容包括sessionId accepted frame list frame file path capture timestamp camera metadata reference acceptedCount lastCapturePoseHint这里不会把大图二进制直接塞进 Preferences。大文件已经落到应用自己的采集目录检查点只保存 manifest 和必要元数据。这段代码解决什么问题应用退后台或页面重建以后可以恢复已通过质检的帧清单不用从第 1 张重新采。export interface CaptureCheckpointData { sessionId: string acceptedCount: number framePaths: string[] savedAt: number } export class CaptureCheckpoint { async save(data: CaptureCheckpointData): Promisevoid { const content JSON.stringify(data) await this.fileStore.writeText( checkpoint/${data.sessionId}.json, content ) } async restore(sessionId: string): PromiseCaptureCheckpointData | undefined { const text await this.fileStore.readText( checkpoint/${sessionId}.json ) if (!text) { return undefined } return JSON.parse(text) as CaptureCheckpointData } }实际项目还要给 manifest 做版本号和完整性校验。如果检查点里写着 96 个文件但目录里只剩 94 个就不能直接恢复成 96。恢复阶段要重新校验文件是否存在、是否可读再计算真正的有效数量。六、APP_BACKGROUND 发生时我停止采集但不立刻启动重建本次测试在第 96 个有效帧以后切了一次后台。记录是interruptReason APP_BACKGROUND checkpoint 96 resumeCount 1我的做法是先停止继续拿新帧完成当前 manifest 落盘再释放相机采集资源。回到前台后读取检查点重新建立 Camera Kit 会话然后继续采集新帧。这里绝不能把旧 Camera Session 句柄序列化下来。句柄属于运行期资源检查点保存的是业务数据不是底层对象。恢复之后acceptedCount从校验后的 96 开始最终继续增长到 112。这就是所谓的“续采”。七、只有采集阶段结束才真正进入 Spatial Recon 重建当页面满足产品定义的输入条件以后状态从CAPTURING进入READY_FOR_RECON这时候才把最终 manifest 交给 Native Bridge由 C/C 侧根据 Spatial Recon Kit 正式创建空间重建会话。Spatial Recon Kit 的 C API 使用HMS_SpatialRecon_Session表示重建会话官方能力覆盖空间重建以及后续 3DGS 渲染链路。ReconCapture 不在 ArkTS 层伪造一组所谓“重建 API”而是只暴露自己的业务方法await reconNative.startFromManifest(manifestPath)这个reconNative是项目自定义 Node-API 封装内部才去调用官方 C API。这样 ArkTS 不需要持有 Native Session 指针生命周期也更清晰。八、为什么我宁愿过滤 44 帧也不想把 156 张全部塞进去“输入更多”不一定等于“重建更好”。这篇并不是在给 Spatial Recon Kit 的内部关键帧算法下结论。官方能力本身就会处理多角度输入并完成自己的重建逻辑。应用前置质检的价值是另一层尽量减少明显无价值或可能干扰体验的输入同时让用户在采集阶段及时修正动作。比如 21 张明显运动模糊帧对用户来说最有价值的信息不是“系统后面会不会自己剔除”而是现在就告诉他“移动慢一点”。这会直接改变采集体验。九、最终运行结果不是“156 帧采完了”而是“112 帧准备好了”最后一次测试CAP-20260930-026 候选帧156 通过质检112 过滤帧44 上次检查点第 96 帧 恢复次数1 中断原因APP_BACKGROUND 状态READY_FOR_RECON图三里把 44 张过滤帧继续拆成模糊 21 重叠不足 14 曝光异常 9这比一个“采集进度 100%”更有诊断价值。用户能知道哪些输入真正会进入后续重建开发者也能判断引导策略需不需要调整。十、清晰度、重叠度和曝光分数从哪里来为了避免把“质量评分”写成一个神秘黑盒我给 ReconCapture 的三项分数定义了明确来源。清晰度主要来自图像高频信息和边缘响应。如果连续多帧高频能量明显下降同时陀螺仪或相机运动信息显示用户正在快速移动这一帧大概率不适合作为输入。重叠度并不是简单比较两张图像素相似而是估计当前帧和最近已接受关键帧之间是否保留足够共同视觉区域。Demo 里用轻量特征匹配得到一个业务分数再结合视角变化判断是否“走得太快、跨度太大”。曝光分数则用亮度直方图和极暗 / 极亮像素比例做简单约束。这三项都是 ReconCapture 自己的工程指标并不是 Spatial Recon Kit 对外暴露的官方质量评分。我把它们设计成可替换接口export interface FrameQualityAnalyzer { analyze(framePath: string): PromiseFrameQuality }以后如果产品要换更好的清晰度算法不需要改采集状态机只替换 Analyzer。这种设计比把三段算法直接写进CapturePage.ets更适合长期维护。十一、检查点必须原子写入不能写到一半就算保存成功最早版本的saveCheckpoint()直接覆盖checkpoint/CAP-20260930-026.json如果刚写一半应用就被终止下次启动读到的是半截 JSON。后来我改成两阶段写 checkpoint.tmp ↓ flush / close ↓ rename 为正式 checkpoint.json只有重命名完成以后页面才显示“检查点已保存”。如果平台文件接口不保证你需要的原子语义也可以采用双文件版本checkpoint_A.json checkpoint_B.json每次写另一份并在头部保存version、savedAt和校验摘要。恢复时选最新且完整的一份。这段代码解决什么问题中断恰好发生在保存检查点过程中时不把损坏文件当成有效恢复点。interface CheckpointEnvelope { version: number sessionId: string acceptedCount: number framePaths: string[] savedAt: number } async function saveSafely(data: CheckpointEnvelope): Promisevoid { const temp checkpoint/${data.sessionId}.tmp const target checkpoint/${data.sessionId}.json await fileStore.writeText(temp, JSON.stringify(data)) await fileStore.replace(temp, target) }检查点不是“尽量记一下进度”而是恢复链路的依据。既然要依赖它就应该把它当成一份需要完整性的正式数据。十二、恢复以后第一件事不是继续拍而是重新验证环境从后台回来后我不会立刻把相机重新打开然后继续计数。恢复流程是读取检查点 ↓ 校验 96 个文件是否仍然存在 ↓ 校验当前相机能力 ↓ 重新建立 Camera Session ↓ 重新获取当前方向 / 画幅配置 ↓ 给用户一帧恢复提示 ↓ 继续采集为什么还要重新检查相机能力因为运行期资源已经释放设备状态可能变化。应用可能经历横竖屏切换、相机被其他应用占用、系统资源调整。旧 Session 的配置不能理所当然地套到新 Session 上。所以检查点只恢复业务事实已经接受哪些帧 最后保存到哪里 当前会话是谁至于新的相机资源永远重新创建。这种区分对 Native 能力尤其重要可序列化的是数据不可序列化的是句柄。十三、重复帧也是一种质量问题44 张过滤帧里我在截图中把 14 张归到“重叠不足”。实际实现里还有另一种相反问题重叠太高视角几乎没变。比如用户站在原地连拍 20 张清晰度很好、曝光也正常但信息增量很低。ReconCapture 会给这种帧设置一个TOO_SIMILAR诊断标记。当前文章为了让统计和图片保持一致把它合并到重叠策略内部没有单独增加第四类数字。产品提示会区分重叠不足 → 回到上一视角附近 信息重复 → 向侧边移动补充新视角这也是为什么“重叠分数”不能简单理解成越大越好。3DGS 采集需要的是连续而有变化的视角。真正的门控规则往往是一个区间而不是只设置一个最低值。十四、112 张有效帧也不是一个固定的完成门槛截图里最终接受 112 张以后进入READY_FOR_RECON这只是本次客厅 Demo 的产品条件。真实场景是否可以开始重建至少还应该综合有效帧数量 空间覆盖范围 视角分布 是否存在明显未覆盖区域 用户主动结束意图一个小物体桌面扫描和一个完整客厅不可能使用同一个固定帧数。所以 ReconCapture 的状态机里不会写if (acceptedCount 112) { READY_FOR_RECON }而是交给CaptureCompletenessEvaluatorconst completeness await evaluator.evaluate(manifest) if (completeness.ready) { this.state READY_FOR_RECON }112 只是本轮运行结果不是系统阈值。这种写法也方便后面扩展场景覆盖热力图、轨迹闭环等判断而不用把 CapturePage 继续堆大。十五、真正开始重建以后采集目录不要立刻删除进入 Spatial Recon 以后我会把当前 manifest 状态从CAPTURE_READY改成RECON_SUBMITTED但不会马上删除 112 张源图。因为重建可能失败。如果 Native 层返回设备不支持、输入异常、资源不足等错误用户不应该被迫重新绕客厅走一圈。更稳妥的做法是重建成功并生成目标产物 ↓ 校验结果可用 ↓ 再按产品策略决定是否清理源采集如果产品允许用户二次重建、调整参数源图甚至应该保留更久。采集数据的生命周期应该由业务结果决定而不是“调用 startFromManifest 成功返回”就立即删除。十六、我给采集链路加了一套比模型更早的 DFX以前查 3DGS 问题时我只有“重建失败”一条错误。现在 HiLog 会记录sessionCAP-20260930-026 candidate156 accepted112 rejected44 reject.blur21 reject.overlap14 reject.exposure9 checkpoint96 resumeCount1 interruptAPP_BACKGROUND stateREADY_FOR_RECON如果某个版本突然出现“有效帧率明显下降”我可以先看是哪类过滤上涨。比如blur 突然翻倍可能是采集 UI 动画或相机参数变化导致用户移动节奏变快overlap 过滤变多可能是引导路线有问题exposure 上涨可能是场景切换到窗边以后提示不够及时。这种诊断比最后只看模型质量更早也更容易复现。十七、这篇真正想保留的是“采集也是算法链路的一部分”很多 3DGS Demo 最容易展示的是最后的三维模型。但在真实产品里用户花时间最多的往往是模型出现之前那几分钟拿着手机走、对准、补角度、被打断、回来继续。如果这段体验没有状态、有问题不反馈、退后台就清零再强的重建能力也很难让普通用户顺利完成一次采集。所以这次我刻意把技术重心放到模型之前设备能力检查 → 帧质量门控 → 即时提示 → 检查点 → 中断恢复 → 完整性判断 → 交给 Spatial Recon它没有修改 Spatial Recon Kit 内部算法只是在系统能力之前加了一层更适合产品使用的采集工程。对我来说这和后处理优化一样重要。十八、这次把问题前移以后我留下了四个判断第一相机成功输出帧不代表这张帧值得进入 3DGS 输入集。第二中断续采是业务采集层能力不要和 Spatial Recon 重建会话恢复混为一谈。第三检查点应该保存可重建的业务事实而不是保存 Camera Session、Native 指针这类运行期对象。第四帧质检不仅用来丢数据还应该反向驱动用户采集动作。Spatial Recon Kit 已经提供了 3DGS 空间重建和渲染能力。真正把它做成一个稳定产品时模型之前的那几分钟采集体验同样重要。以前我总盯着“模型最后长什么样”这次更关心另一件事送进模型的 112 张图到底是不是用户这次采集里真正值得留下的 112 张。参考资料Spatial Recon Kit 简介 / APIhttps://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/api/spatial-recon-apiSpatial Recon Kit 术语https://developer.huawei.com/consumer/cn/doc/HarmonyOS-Guides/spatial-recon-glossaryCamera Kit Native 相机预配置https://developer.huawei.com/consumer/cn/doc/HarmonyOS-Guides/camera-preconfig-nativespatialRenderhttps://developer.huawei.com/consumer/en/doc/harmonyos-references/spatial-recon-spatialrender
返回列表