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

如何关联AVDepthData的内参、畸变与当前视频流?及校准疑问

相机校准内参缩放问题及解答

场景背景

我正在开发一款计算机视觉测试App,最终需要用到相机校准功能。

常规摄像头内参获取

创建捕获会话后,通过以下代码获取并打印内参矩阵:

let calibrationPayload = CMGetAttachment(sampleBuffer, key: kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix, attachmentModeOut: nil)
if let data = calibrationPayload as? Data {
    let matrix: matrix_float3x3 = data.withUnsafeBytes { $0.pointee }
    print(matrix)
}

在iPhone 13 Pro后置摄像头运行时,得到符合1920x1080分辨率的内参:

simd_float3x3([[4220.394, 0.0, 0.0], [0.0, 4220.394, 0.0], [941.9231, 533.648, 1.0]])

// 对应矩阵(符合1920x1080相机参数):
// [4220.394,    0.0,   941.9231]
// [   0.0,   4220.394, 533.648]
// [   0.0,      0.0,      1.0]

启用深度数据流后的内参变化

为获取镜头畸变数据,将设备类型改为.builtInDualCamera并启用深度数据流(仅AVDepthData缓冲区的cameraCalibrationData属性包含畸变数据),在深度捕获代理中打印畸变中心、查找表及内参:

guard let calibrationData = depthData.cameraCalibrationData else {
    return
}
print("Intrinsics = \(calibrationData.intrinsicMatrix)")

let distoCenter = calibrationData.lensDistortionCenter
print("Distortion center: \(distoCenter)")

// 打印LUT数据的更多代码

但此时获取的内参与之前差异极大,不符合1920x1080分辨率,两个内参矩阵间存在约2.20的缩放比例:

Intrinsics = simd_float3x3([[9284.896, 0.0, 0.0], [0.0, 9284.896, 0.0], [2072.8423, 1174.5812, 1.0]])

// 对应矩阵:
// [9284.896, 0.0, 2072.8423]
// [0.0, 9284.896,1174.5812]
// [0.0, 0.0, 1.0]

Distortion center: (2072.839599609375, 1174.5499267578125)

技术疑问解答

1. 2.20缩放比例的来源是什么?

这个缩放比例是深度摄像头的物理分辨率与当前捕获的RGB图像分辨率的比值。iPhone 13 Pro的后置深度摄像头(属于双摄系统)物理分辨率约为4032x2268,而当前捕获的RGB图像是1920x1080,计算下来4032/1920≈2.099,接近观察到的2.20(微小差异源于像素对齐或不同摄像头的裁剪逻辑)。

AVDepthData的cameraCalibrationData内参基于深度摄像头的原始物理分辨率生成,而常规RGB帧的内参基于设置的捕获分辨率(1920x1080)生成,因此两者存在固定缩放比例。

2. 是否可通过捕获会话查询预计算该比例,还是必须从内参焦距估算?

可以通过捕获会话直接查询获取,无需从内参估算,两种可靠方式:

  • 方式一:从AVCaptureDeviceFormat获取
    分别获取RGB摄像头和深度摄像头的格式分辨率,计算比例:
    // 获取当前RGB捕获格式的分辨率
    if let rgbFormat = captureSession.inputs.first(where: { $0.device.position == .back && $0.device.deviceType != .builtInTrueDepthCamera })?.activeFormat {
        let rgbSize = CMVideoFormatDescriptionGetDimensions(rgbFormat.formatDescription)
        let rgbWidth = CGFloat(rgbSize.width)
        let rgbHeight = CGFloat(rgbSize.height)
        
        // 获取深度摄像头的格式分辨率
        if let depthDevice = AVCaptureDevice.default(.builtInDualCamera, for: .depthData, position: .back),
           let depthFormat = depthDevice.activeFormat {
            let depthSize = CMVideoFormatDescriptionGetDimensions(depthFormat.formatDescription)
            let depthWidth = CGFloat(depthSize.width)
            let depthHeight = CGFloat(depthSize.height)
            
            let scaleX = rgbWidth / depthWidth
            let scaleY = rgbHeight / depthHeight
            // iPhone双摄的RGB和深度摄像头比例一致,scaleX和scaleY基本相等
        }
    }
    
  • 方式二:从AVCameraCalibrationData的pixelSize属性获取
    cameraCalibrationData.pixelSize返回深度摄像头的物理分辨率尺寸,直接和RGB捕获分辨率计算比例即可:
    guard let calibrationData = depthData.cameraCalibrationData else { return }
    let depthPixelSize = calibrationData.pixelSize
    let rgbCaptureSize = CGSize(width: 1920, height: 1080) // 实际捕获分辨率
    let scale = rgbCaptureSize.width / depthPixelSize.width
    

3. 应用LUT校正图像时,计算与畸变中心的距离是否需考虑该缩放?

需要考虑。因为畸变中心和LUT都是基于深度摄像头的原始分辨率定义的,而要校正的是RGB图像(1920x1080)的像素点,步骤如下:

  1. 将RGB图像中的像素坐标放大到深度摄像头的分辨率尺度(乘以缩放比例的倒数,即深度分辨率/RGB分辨率);
  2. 使用放大后的坐标和畸变中心计算距离,再查询LUT获取放大系数;
  3. 将校正后的坐标缩小回RGB图像的分辨率尺度(乘以缩放比例),得到最终校正后的像素位置。

或者,也可以提前将畸变中心和LUT的参考尺度转换到RGB图像的分辨率:

  • 畸变中心转换:convertedCenter = CGPoint(x: distoCenter.x * scale, y: distoCenter.y * scale)(scale为RGB分辨率/深度分辨率);
  • LUT的参考半径也按同样比例缩放,后续计算可直接使用RGB图像的像素坐标。

附AVCalibrationData.h校正示例代码(中文注释)

// AVCalibrationData.h中的校正点示例代码:
// 计算最大半径
float delta_ocx_max = MAX( opticalCenter.x, imageSize.width  - opticalCenter.x );
float delta_ocy_max = MAX( opticalCenter.y, imageSize.height - opticalCenter.y );
float r_max = sqrtf( delta_ocx_max * delta_ocx_max + delta_ocy_max * delta_ocy_max );
 
// 计算从光学中心到目标点的向量
float v_point_x = point.x - opticalCenter.x;
float v_point_y = point.y - opticalCenter.y;
 
// 计算目标点的半径
float r_point = sqrtf( v_point_x * v_point_x + v_point_y * v_point_y );
 
// 在提供的查找表中查询要应用的相对径向放大系数
float magnification;
const float *lookupTableValues = lookupTable.bytes;
NSUInteger lookupTableCount = lookupTable.length / sizeof(float);
 
if ( r_point < r_max ) {
  ...
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 23:44:59