iOS平台YUV420格式图像转RGB的Flutter实现问题求助
Flutter iOS平台YUV420(NV12)转RGB图像解决方案
针对iOS平台CameraImage的2平面NV12格式(Y平面 + UV交错平面),以下是正确的转换实现,解决图像偏绿的问题:
完整转换代码
import 'package:camera/camera.dart'; import 'package:image/image.dart' as img; img.Image convertYUV420ToImage(CameraImage cameraImage) { final width = cameraImage.width; final height = cameraImage.height; // Y平面数据 final yPlane = cameraImage.planes[0]; final yRowStride = yPlane.bytesPerRow; final yBytes = yPlane.bytes; // UV平面数据(NV12格式:U、V交错存储) final uvPlane = cameraImage.planes[1]; final uvRowStride = uvPlane.bytesPerRow; final uvPixelStride = uvPlane.bytesPerPixel!; // iOS下该值为2 final uvBytes = uvPlane.bytes; final image = img.Image(width: width, height: height); for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { // 计算Y像素索引 final yIndex = h * yRowStride + w; final y = yBytes[yIndex]; // 计算UV像素对应的索引(每个UV对应2x2的Y像素) final uvH = h ~/ 2; final uvW = w ~/ 2; final uvIndex = uvH * uvRowStride + uvW * uvPixelStride; // 提取U和V值:NV12格式中U在前,V在后,各占1字节 final u = uvBytes[uvIndex]; final v = uvBytes[uvIndex + 1]; // 将YUV转换为RGB并设置像素 image.data!.setPixel(w, h, yuvToRgb(y, u, v)); } } return image; } // 标准YUV420(BT.601)转RGB公式,处理U/V的偏移 int yuvToRgb(int y, int u, int v) { // 将无符号字节转换为有符号值(U/V范围:-128 ~ 127) final yInt = y & 0xFF; final uInt = (u & 0xFF) - 128; final vInt = (v & 0xFF) - 128; // 计算RGB分量 int r = (yInt + 1.370705 * vInt).round(); int g = (yInt - 0.698001 * vInt - 0.337633 * uInt).round(); int b = (yInt + 1.732446 * uInt).round(); // 确保分量在0-255范围内 r = r.clamp(0, 255); g = g.clamp(0, 255); b = b.clamp(0, 255); // 组装为ARGB格式(alpha设为255) return (0xFF << 24) | (r << 16) | (g << 8) | b; }
关键修正点
UV数据提取方式
iOS的CameraImage采用NV12格式,UV平面是U和V交错存储,每个UV对占2字节(U在前,V在后),而非单个字节存储UV的高低位。之前的代码错误地从单个字节拆分U/V,导致色彩完全偏移。YUV转RGB的偏移处理
原始的U/V字节是无符号的(0-255),但标准YUV420中U/V的实际范围是-128到127,必须先减去128转换为有符号值,否则计算出的RGB分量会偏差,导致图像偏绿。索引计算优化
按行优先遍历,先处理高度再处理宽度,逻辑更清晰;同时确保UV索引对应正确的2x2 Y像素块。
平台兼容建议
可以通过判断cameraImage.planes.length来区分Android(3平面,YUV420_888)和iOS(2平面,NV12),实现跨平台统一转换:
img.Image convertCameraImageToImage(CameraImage cameraImage) { if (cameraImage.planes.length == 3) { // Android平台的3平面转换逻辑(使用你原有的Android代码) return convertYUV420Android(cameraImage); } else if (cameraImage.planes.length == 2) { // iOS平台的NV12转换逻辑 return convertYUV420ToImage(cameraImage); } else { throw Exception("不支持的图像格式"); } }
内容的提问来源于stack exchange,提问作者Tclha
相关产品推荐
相关产品推荐

