如何调整Flutter人脸识别场景下被压缩的相机宽高比
解决Flutter相机宽高比拉伸导致人脸检测框偏移问题
你的问题根源在于直接将CameraPreview强制铺满屏幕宽高,没有遵循相机本身的宽高比,导致画面拉伸,进而人脸检测的原始坐标和屏幕显示坐标不匹配,出现框偏移的情况。
修复步骤:
1. 按相机原生宽高比渲染预览画面
替换代码中直接设置宽高的CameraPreview,改用AspectRatio组件适配相机真实比例,避免画面拉伸:
@override Widget build(BuildContext context) { print(_detectedFaces.isNotEmpty ? _detectedFaces[0] : 'No faces detected'); List<Widget> stackChildren = []; final size = MediaQuery.sizeOf(context); if (_controller != null) { // 获取相机原生宽高比 final cameraAspectRatio = _controller.value.aspectRatio; stackChildren.add(Positioned( top: 0.0, left: 0.0, width: size.width, height: size.height, // 用AspectRatio保证预览画面比例正确 child: AspectRatio( aspectRatio: cameraAspectRatio, child: CameraPreview(_controller), ), )); stackChildren.add( Positioned( top: 0.0, left: 0.0, width: size.width, height: size.height, // 将比例和屏幕尺寸传入,用于坐标转换 child: buildResult(cameraAspectRatio, size), ), ); } return Stack(children: stackChildren); }
2. 修正人脸检测框的坐标映射
在buildResult方法中,需要根据相机预览的实际显示区域,把人脸检测返回的原始坐标转换为屏幕上的正确位置:
Widget buildResult(double cameraAspectRatio, Size screenSize) { return CustomPaint( painter: FacePainter( detectedFaces: _detectedFaces, cameraAspectRatio: cameraAspectRatio, screenSize: screenSize, previewSize: _controller.value.previewSize!, ), ); } class FacePainter extends CustomPainter { final List<Face> detectedFaces; final double cameraAspectRatio; final Size screenSize; final Size previewSize; FacePainter({ required this.detectedFaces, required this.cameraAspectRatio, required this.screenSize, required this.previewSize, }); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.red ..style = PaintingStyle.stroke ..strokeWidth = 2.0; for (var face in detectedFaces) { // 计算预览画面在屏幕上的实际显示区域 Rect previewRect; if (cameraAspectRatio > screenSize.width / screenSize.height) { // 相机比例更宽,预览上下留黑边 final previewHeight = screenSize.width / cameraAspectRatio; final topOffset = (screenSize.height - previewHeight) / 2; previewRect = Rect.fromLTWH(0, topOffset, screenSize.width, previewHeight); } else { // 相机比例更窄,预览左右留黑边 final previewWidth = screenSize.height * cameraAspectRatio; final leftOffset = (screenSize.width - previewWidth) / 2; previewRect = Rect.fromLTWH(leftOffset, 0, previewWidth, screenSize.height); } // 将人脸原始坐标(基于相机分辨率)转换为屏幕坐标 final faceRect = face.boundingBox; // 计算缩放比例 final scaleX = previewRect.width / previewSize.width; final scaleY = previewRect.height / previewSize.height; final scaledLeft = faceRect.left * scaleX + previewRect.left; final scaledTop = faceRect.top * scaleY + previewRect.top; final scaledWidth = faceRect.width * scaleX; final scaledHeight = faceRect.height * scaleY; // 绘制修正后的人脸框 canvas.drawRect( Rect.fromLTWH(scaledLeft, scaledTop, scaledWidth, scaledHeight), paint, ); } } @override bool shouldRepaint(covariant FacePainter oldDelegate) { return oldDelegate.detectedFaces != detectedFaces; } }
关键说明:
- 相机预览分辨率和屏幕分辨率比例不同时,强制铺满会导致拉伸,
AspectRatio能保证画面比例正确,多余区域留黑边。 - 人脸检测返回的坐标基于相机原始预览尺寸,必须转换为屏幕上预览区域的坐标,才能让框准确对应到人脸上。
- 若应用支持横竖屏切换,需额外处理方向变化时的坐标转换逻辑。
内容的提问来源于stack exchange,提问作者Naeem Mujeeb
相关产品推荐
相关产品推荐

