You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Swift中将LiDAR深度图保存为LAS/LAZ文件?

从iOS 17.2的LiDAR深度图生成LAS/LAZ点云文件

你当前的代码只能保存2D深度图为TIFF,但LAS/LAZ是带空间三维坐标的点云格式,需要先把每个像素的深度值结合AR相机的参数转换成世界坐标系下的3D点,再写入LAS/LAZ格式。以下是具体实现步骤和代码:

核心原理

ARFrame的sceneDepth提供的是每个像素对应的深度(单位:米),但这些数据没有空间位置信息。需要通过:

  1. AR相机的内参(intrinsics):将像素坐标转换为相机坐标系下的3D点
  2. AR相机的外参(transform):将相机坐标系下的点转换为世界坐标系下的点

实现步骤

1. 获取深度像素数据与相机参数

首先从ARFrame中提取深度图的原始像素数据,同时获取相机的内参和位姿:

func session(_ session: ARSession, didUpdate frame: ARFrame) {
    guard let sceneDepth = frame.sceneDepth, let camera = frame.camera else { return }
    
    // 限制保存次数
    guard savedImagesCount < 3 else { return }
    
    // 获取深度图的CVBuffer
    let depthBuffer = sceneDepth.depthMap
    CVPixelBufferLockBaseAddress(depthBuffer, .readOnly)
    defer { CVPixelBufferUnlockBaseAddress(depthBuffer, .readOnly) }
    
    // 获取深度数据的指针与格式(iOS的sceneDepth是Float32类型,单位米)
    let width = CVPixelBufferGetWidth(depthBuffer)
    let height = CVPixelBufferGetHeight(depthBuffer)
    let bytesPerRow = CVPixelBufferGetBytesPerRow(depthBuffer)
    let depthData = unsafeBitCast(CVPixelBufferGetBaseAddress(depthBuffer), to: UnsafeMutablePointer<Float32>.self)
    
    // 相机内参(焦距、主点坐标)
    let intrinsics = camera.intrinsics
    let fx = intrinsics[0][0]
    let fy = intrinsics[1][1]
    let cx = intrinsics[2][0]
    let cy = intrinsics[2][1]
    
    // 相机位姿(将相机坐标系点转换为世界坐标系)
    let cameraTransform = camera.transform

2. 遍历像素生成3D点云

遍历每个有效深度像素,计算对应的世界坐标:

var pointCloud: [SIMD3<Float>] = []
    
    for y in 0..<height {
        for x in 0..<width {
            let index = y * (bytesPerRow / MemoryLayout<Float32>.stride) + x
            let depth = depthData[index]
            
            // 过滤无效深度值(深度为0或NaN时跳过)
            guard depth > 0, !depth.isNaN else { continue }
            
            // 相机坐标系下的3D点计算
            let xCam = (Float(x) - cx) * depth / fx
            let yCam = (Float(y) - cy) * depth / fy
            let zCam = depth
            let cameraSpacePoint = SIMD3<Float>(xCam, yCam, zCam)
            
            // 转换为世界坐标系
            let worldSpacePoint = cameraTransform * SIMD4<Float>(cameraSpacePoint, 1.0)
            pointCloud.append(SIMD3<Float>(worldSpacePoint.x, worldSpacePoint.y, worldSpacePoint.z))
        }
    }

3. 写入LAS/LAZ文件

iOS上可以使用LASlib(C++库,需通过CocoaPods或手动集成)来处理LAS/LAZ格式。以下是用LASlib写入LAS文件的示例:

// 保存LAS文件到Documents目录
    let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let lasFileURL = documentsDir.appendingPathComponent("pointCloud.las")
    
    // 初始化LAS写入器
    let lasWriter = LASwriter()
    lasWriter.open(lasFileURL.path)
    
    // 设置LAS文件头部信息
    var header = LASheader()
    header.point_count = UInt32(pointCloud.count)
    // 设置坐标范围(可选,优化点云查看器加载)
    if let minPoint = pointCloud.min(by: { $0.x < $1.x }), let maxPoint = pointCloud.max(by: { $0.x < $1.x }) {
        header.min_x = minPoint.x
        header.max_x = maxPoint.x
        header.min_y = minPoint.y
        header.max_y = maxPoint.y
        header.min_z = minPoint.z
        header.max_z = maxPoint.z
    }
    lasWriter.setHeader(&header)
    
    // 写入每个点
    for point in pointCloud {
        var lasPoint = LASpoint()
        lasPoint.setCoordinates(x: point.x, y: point.y, z: point.z)
        lasWriter.writePoint(&lasPoint)
    }
    
    // 关闭写入器
    lasWriter.close()
    
    print("LAS点云保存至:\(lasFileURL)")
    savedImagesCount += 1
}

注意事项

  • LAZ压缩:如果需要生成LAZ文件,只需将LASwriter的打开路径后缀改为.laz,LASlib会自动处理压缩(需确保库编译时启用了LASzip支持)。
  • 性能优化:遍历所有像素可能会占用较多资源,建议在后台队列处理,避免阻塞主线程。
  • 深度数据有效性:sceneDepth的深度值可能存在无效区域(如过远/过近的物体),需过滤0或NaN值。

内容的提问来源于stack exchange,提问作者Javide

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 14:13:11