如何关联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)的像素点,步骤如下:
- 将RGB图像中的像素坐标放大到深度摄像头的分辨率尺度(乘以缩放比例的倒数,即深度分辨率/RGB分辨率);
- 使用放大后的坐标和畸变中心计算距离,再查询LUT获取放大系数;
- 将校正后的坐标缩小回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
相关产品推荐
相关产品推荐

