This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// imgLib -> Image package from https://pub.dartlang.org/packages/image | |
import 'package:image/image.dart' as imglib; | |
import 'package:camera/camera.dart'; | |
Future<List<int>> convertImagetoPng(CameraImage image) async { | |
try { | |
imglib.Image img; | |
if (image.format.group == ImageFormatGroup.yuv420) { | |
img = _convertYUV420(image); | |
} else if (image.format.group == ImageFormatGroup.bgra8888) { | |
img = _convertBGRA8888(image); | |
} | |
imglib.PngEncoder pngEncoder = new imglib.PngEncoder(); | |
// Convert to png | |
List<int> png = pngEncoder.encodeImage(img); | |
return png; | |
} catch (e) { | |
print(">>>>>>>>>>>> ERROR:" + e.toString()); | |
} | |
return null; | |
} | |
// CameraImage BGRA8888 -> PNG | |
// Color | |
imglib.Image _convertBGRA8888(CameraImage image) { | |
return imglib.Image.fromBytes( | |
image.width, | |
image.height, | |
image.planes[0].bytes, | |
format: imglib.Format.bgra, | |
); | |
} | |
// CameraImage YUV420_888 -> PNG -> Image (compresion:0, filter: none) | |
// Black | |
imglib.Image _convertYUV420(CameraImage image) { | |
var img = imglib.Image(image.width, image.height); // Create Image buffer | |
Plane plane = image.planes[0]; | |
const int shift = (0xFF << 24); | |
// Fill image buffer with plane[0] from YUV420_888 | |
for (int x = 0; x < image.width; x++) { | |
for (int planeOffset = 0; | |
planeOffset < image.height * image.width; | |
planeOffset += image.width) { | |
final pixelColor = plane.bytes[planeOffset + x]; | |
// color: 0x FF FF FF FF | |
// A B G R | |
// Calculate pixel color | |
var newVal = shift | (pixelColor << 16) | (pixelColor << 8) | pixelColor; | |
img.data[planeOffset + x] = newVal; | |
} | |
} | |
return img; | |
} |
@owjoh here @camsim99 claims that the issue with NV21 for Motorola and Xiaomi has been fixed in a recent CameraX implementation.
Alternatively, convertImage function could be tuned to handle single-plane images, but I don't know their actual layout.
can anyone help me with yuv420 to RGB conversion on iOS ?
About a few days of struggling with the CamerImage to Image conversion, I managed to improve the method to include paddnig on different devices. I tested on several devices, checking the conversion at different camera resolutions. And I think it works.
imglib.Image convertYUV420ToImage(CameraImage cameraImage) { final imageWidth = cameraImage.width; final imageHeight = cameraImage.height; final yBuffer = cameraImage.planes[0].bytes; final uBuffer = cameraImage.planes[1].bytes; final vBuffer = cameraImage.planes[2].bytes; final int yRowStride = cameraImage.planes[0].bytesPerRow; final int yPixelStride = cameraImage.planes[0].bytesPerPixel!; final int uvRowStride = cameraImage.planes[1].bytesPerRow; final int uvPixelStride = cameraImage.planes[1].bytesPerPixel!; final image = imglib.Image(imageWidth, imageHeight); for (int h = 0; h < imageHeight; h++) { int uvh = (h / 2).floor(); for (int w = 0; w < imageWidth; w++) { int uvw = (w / 2).floor(); final yIndex = (h * yRowStride) + (w * yPixelStride); // Y plane should have positive values belonging to [0...255] final int y = yBuffer[yIndex]; // U/V Values are subsampled i.e. each pixel in U/V chanel in a // YUV_420 image act as chroma value for 4 neighbouring pixels final int uvIndex = (uvh * uvRowStride) + (uvw * uvPixelStride); // U/V values ideally fall under [-0.5, 0.5] range. To fit them into // [0, 255] range they are scaled up and centered to 128. // Operation below brings U/V values to [-128, 127]. final int u = uBuffer[uvIndex]; final int v = vBuffer[uvIndex]; // Compute RGB values per formula above. int r = (y + v * 1436 / 1024 - 179).round(); int g = (y - u * 46549 / 131072 + 44 - v * 93604 / 131072 + 91).round(); int b = (y + u * 1814 / 1024 - 227).round(); r = r.clamp(0, 255); g = g.clamp(0, 255); b = b.clamp(0, 255); // Use 255 for alpha value, no transparency. ARGB values are // positioned in each byte of a single 4 byte integer // [AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB] final int argbIndex = h * imageWidth + w; image.data[argbIndex] = 0xff000000 | ((b << 16) & 0xff0000) | ((g << 8) & 0xff00) | (r & 0xff); } } return image; }
Does this work for iOS ?
Hi @prkhrv,
Please take a look at nv21 to Image
In my project, the solution is work for me.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@rraayy Any luck converting NV21 to Image? I'm having the same difficulties. The NV21 formatted CameraImage object only has one plane.