Flutter App 中相机内参获取全流程
一句话总结
Flutter 本身无法拿到“真实相机内参”,必须通过 原生 Android / iOS API 获取,然后回传给 Flutter。
⚠️ 不同分辨率、不同摄像头、不同对焦状态,内参都会不同,需要动态获取或按比例调整。
1. 什么是相机内参
视觉里常用的内参矩阵 (K):
| 参数 | 含义 |
|---|---|
| fx, fy | 焦距(像素单位) |
| cx, cy | 主点(通常在图像中心附近) |
| skew | 一般为 0 |
工程实践中,基本关注 fx, fy, cx, cy 即可。
2. Flutter 层调用方法
Flutter 端通过 MethodChannel 调用原生接口。
方式 A:详细版本(用于上传 / 展示)
static const MethodChannel _channel = MethodChannel('camera_intrinsics_detailed');
Future<CameraIntrinsicsResult?> fetchCameraIntrinsics() async {
final result = await _channel.invokeMethod('getCameraIntrinsicsDetailed');
// 解析 fx, fy, cx, cy, method, errorMargin
}
方式 B:简单版本(仅用于文件名 / 简单用途)
static const MethodChannel _channel = MethodChannel('camera_intrinsics');
Future<CameraIntrinsics?> getCameraIntrinsics() async {
final result = await _channel.invokeMethod('getCameraIntrinsics');
// 只返回 fx, fy, cx, cy
}
3. Android 原生实现(Camera2 API)
1️⃣ 选择摄像头(优先后置)
val cameraManager = getSystemService(CAMERA_SERVICE) as CameraManager
val cameraIds = cameraManager.cameraIdList
var targetCameraId: String? = null
for (id in cameraIds) {
val characteristics = cameraManager.getCameraCharacteristics(id)
if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
CameraCharacteristics.LENS_FACING_BACK) {
targetCameraId = id
break
}
}
2️⃣ 获取内参(优先硬件校准)
val intrinsic = characteristics.get(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION)
val fx = intrinsic[0].toDouble()
val fy = intrinsic[1].toDouble()
val cx = intrinsic[2].toDouble()
val cy = intrinsic[3].toDouble()
- 硬件校准误差约 1%
- 若获取失败,可用传感器参数计算:
val sensorSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)
val pixelArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)
val focalLength = characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)[0]
val fx = focalLength * pixelArray.width / sensorSize.width
val fy = focalLength * pixelArray.height / sensorSize.height
val cx = pixelArray.width / 2.0
val cy = pixelArray.height / 2.0
计算值误差约 5%,90% 场景够用。
⚠️ 注意坑点
- 分辨率变化时需要重算 fx/fy
- 前后摄像头内参不同
- CameraX 默认隐藏内参,需要 Camera2Interop
4. iOS 原生实现(AVFoundation)
1️⃣ 获取摄像头和 activeFormat
guard let camera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { return }
let format = camera.activeFormat
let dimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription)
let width = Double(dimensions.width)
let height = Double(dimensions.height)
2️⃣ 获取内参(推荐顺序)
方法1:CameraCalibrationData(最准确,ARKit/TrueDepth 支持)
if let intrinsic = camera.cameraCalibrationData?.intrinsicMatrix {
let fx = intrinsic.columns.0.x
let fy = intrinsic.columns.1.y
let cx = intrinsic.columns.2.x
let cy = intrinsic.columns.2.y
}
方法2:FOV计算(估算)
let fovXRadians = camera.activeFormat.videoFieldOfView * .pi / 180
let fx = width / (2 * tan(fovXRadians/2))
let fy = fx * height / width
let cx = width / 2
let cy = height / 2
方法3:经验值估算(粗略)
let fx = width * 1.2
let fy = height * 1.2
let cx = width / 2
let cy = height / 2
5. Flutter 调用链总结
用户操作 / 页面启动
│
├─> Flutter 层调用 fetchCameraIntrinsics()
│ └─> MethodChannel
│ ├─> Android: Camera2 API
│ └─> iOS: AVCaptureDevice + CameraCalibrationData / FOV
│
└─> 返回 fx, fy, cx, cy,存储到 SharedPreferences / 本地缓存
6. 工程实用经验
-
不同分辨率:内参按比例缩放
-
不同摄像头:前后摄像头内参不同
-
Flutter 端:只做存储、计算、投影,核心获取依赖原生
-
原型阶段 / OpenCV 算法:可用近似值
fx ≈ fy ≈ max(imageWidth, imageHeight) cx ≈ imageWidth / 2 cy ≈ imageHeight / 2
7. 总结
- Flutter 本身无法直接获取相机内参
- Android 用 Camera2 API / LENS_INTRINSIC_CALIBRATION
- iOS 用 CameraCalibrationData / activeFormat / FOV
- 实际使用时需考虑分辨率、摄像头、对焦状态
- 工程上可先用近似值跑算法,后期按设备精确获取