如何将Flutter CameraImage转换为色彩正常、无旋转的PNG/JPG?
解决CameraImage转JPG/PNG的灰度与旋转问题
以下是修复后的跨平台实现方案,同时解决色彩丢失和图片旋转问题:
import 'package:camera/camera.dart'; import 'package:image/image.dart' as imglib; // 转换CameraImage为PNG字节,支持色彩校正与旋转 Future<List<int>> convertCameraImageToPng(CameraImage image, int sensorOrientation) async { imglib.Image? convertedImage; if (image.format.group == ImageFormatGroup.yuv420) { convertedImage = _convertYUV420ToImage(image); } else if (image.format.group == ImageFormatGroup.bgra8888) { convertedImage = _convertBGRA8888ToImage(image); } if (convertedImage == null) { throw Exception('不支持的图像格式'); } // 根据相机传感器方向旋转图片 imglib.Image rotatedImage = _rotateImage(convertedImage, sensorOrientation); // 编码为PNG(如需JPG,替换为JpegEncoder()即可) return imglib.PngEncoder().encodeImage(rotatedImage); } // 转换BGRA8888格式的CameraImage为imglib.Image imglib.Image _convertBGRA8888ToImage(CameraImage image) { return imglib.Image.fromBytes( image.width, image.height, image.planes[0].bytes, format: imglib.Format.bgra, ); } // 正确转换YUV420_888格式为彩色图像 imglib.Image _convertYUV420ToImage(CameraImage image) { final int width = image.width; final int height = image.height; final yPlane = image.planes[0]; final uPlane = image.planes[1]; final vPlane = image.planes[2]; final yBytes = yPlane.bytes; final uBytes = uPlane.bytes; final vBytes = vPlane.bytes; final int yRowStride = yPlane.bytesPerRow; final int yPixelStride = yPlane.bytesPerPixel!; final int uvRowStride = uPlane.bytesPerRow; final int uvPixelStride = uPlane.bytesPerPixel!; final img = imglib.Image(width, height); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { final int yIndex = y * yRowStride + x * yPixelStride; // 计算UV通道索引(YUV420为4:2:0采样,UV分辨率是Y的一半) final int uvIndex = (y ~/ 2) * uvRowStride + (x ~/ 2) * uvPixelStride; final int yValue = yBytes[yIndex]; final int uValue = uBytes[uvIndex] - 128; final int vValue = vBytes[uvIndex] - 128; // YUV转RGB标准公式 int r = (yValue + 1.370705 * vValue).round(); int g = (yValue - 0.698001 * vValue - 0.337633 * uValue).round(); int b = (yValue + 1.732446 * uValue).round(); // 限制RGB值在0-255范围内,避免色彩溢出 r = r.clamp(0, 255); g = g.clamp(0, 255); b = b.clamp(0, 255); // 组装ARGB格式像素值(imglib库默认使用ARGB) img.data[y * width + x] = (0xFF << 24) | (r << 16) | (g << 8) | b; } } return img; } // 根据传感器方向旋转图片 imglib.Image _rotateImage(imglib.Image image, int sensorOrientation) { switch (sensorOrientation) { case 90: return imglib.copyRotate(image, 90); case 180: return imglib.copyRotate(image, 180); case 270: return imglib.copyRotate(image, 270); default: return image; } }
使用示例
// 假设已初始化CameraController实例_controller _controller?.startImageStream((CameraImage image) async { try { // 获取相机传感器的物理方向 final int sensorOrientation = _controller!.description.sensorOrientation; final pngBytes = await convertCameraImageToPng(image, sensorOrientation); // 后续可将字节写入文件或上传 // 示例:final file = File('路径/文件名.png'); await file.writeAsBytes(pngBytes); } catch (e) { print('图片转换失败: $e'); } });
关键修复说明
解决灰度问题:
原代码仅使用YUV420格式的Y亮度通道,完全忽略U、V色彩通道。修复后同时读取三个通道数据,通过标准YUV转RGB公式计算真实色彩值,确保输出彩色图片。解决旋转问题:
相机传感器物理方向通常与屏幕方向不一致(多数手机传感器为横置),通过获取相机的sensorOrientation属性,调用image库的旋转方法修正图片方向,保证输出与屏幕显示一致。跨平台兼容:
自动适配Android常用的YUV420格式和iOS常用的BGRA8888格式,依赖的camera和image库均为Flutter主流跨平台库,无需额外平台定制代码。
内容的提问来源于stack exchange,提问作者Kristi Jorgji
相关产品推荐
相关产品推荐

