Yuv (NV21) 图像转换为位图
Yuv (NV21) image converting to bitmap
我正在尝试从相机预览中捕获图像并在其上进行绘图。问题是,我只有大约 3-4 fps 的绘图,一半的帧处理时间是从相机预览接收和解码 NV21 图像并转换为 bitmap。我有一个代码来完成这个任务,我在另一个堆栈问题上找到了它。好像不快,不知道怎么优化。在三星 Note 3 上大约需要 100-150 毫秒,图像大小为 1920x1080。我怎样才能让它工作得更快?
代码:
public Bitmap curFrameImage(byte[] data, Camera camera)
{
Camera.Parameters parameters = camera.getParameters();
int imageFormat = parameters.getPreviewFormat();
if (imageFormat == ImageFormat.NV21)
{
YuvImage img = new YuvImage(data, ImageFormat.NV21, prevSizeW, prevSizeH, null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
img.compressToJpeg(new android.graphics.Rect(0, 0, img.getWidth(), img.getHeight()), 50, out);
byte[] imageBytes = out.toByteArray();
return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
}
else
{
Log.i(TAG, "Preview image not NV21");
return null;
}
}
图像的最终格式必须是位图,这样我才能对其进行处理。我尝试将 Camera.Parameters.setPreviewFormat 设置为 RGB_565,但无法将相机参数分配给相机,我还读到 NV21 是唯一可用的格式。我不确定是否可以在这些格式更改中找到解决方案。
提前致谢。
感谢 Alex Cohn 帮助我加快转换速度。我实施了您建议的方法(RenderScript 内在函数)。此代码使用 RenderScript 内部函数制作,将 YUV 图像转换为位图的速度大约快 5 倍。以前的代码用了 100-150 毫秒。在 Samsung Note 3 上,这需要 15-30 左右。 如果有人需要做同样的任务,这里是代码:
这些将被使用:
private RenderScript rs;
private ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic;
private Type.Builder yuvType, rgbaType;
private Allocation in, out;
在我初始化的创建函数中..:[=13=]
rs = RenderScript.create(this);
yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
整个 onPreviewFrame 看起来像这样(这里我接收并转换图像):
if (yuvType == null)
{
yuvType = new Type.Builder(rs, Element.U8(rs)).setX(dataLength);
in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(prevSizeW).setY(prevSizeH);
out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
}
in.copyFrom(data);
yuvToRgbIntrinsic.setInput(in);
yuvToRgbIntrinsic.forEach(out);
Bitmap bmpout = Bitmap.createBitmap(prevSizeW, prevSizeH, Bitmap.Config.ARGB_8888);
out.copyTo(bmpout);
您可以获得更快的速度(使用 JellyBean 4.3、API18 或更高版本):
相机预览模式必须为NV21 !
在 "onPreviewFrame()" 上只做 :
aIn.copyFrom(data);
yuvToRgbIntrinsic.forEach(aOut);
aOut.copyTo(bmpout); // and of course, show the bitmap or whatever
不要在此处创建任何对象。
所有其他东西(创建 rs、yuvToRgbIntrinsic、分配、位图)做
在 onCreate() 方法中 before 开始相机预览。
rs = RenderScript.create(this);
yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
// you don´t need Type.Builder objects , with cameraPreviewWidth and cameraPreviewHeight do:
int yuvDatalength = cameraPreviewWidth*cameraPreviewHeight*3/2; // this is 12 bit per pixel
aIn = Allocation.createSized(rs, Element.U8(rs), yuvDatalength);
// of course you will need the Bitmap
bmpout = Bitmap.createBitmap(cameraPreviewWidth, cameraPreviewHeight, Bitmap.Config.ARGB_8888);
// create output allocation from bitmap
aOut = Allocation.createFromBitmap(rs,bmpout); // this simple !
// set the script´s in-allocation, this has to be done only once
yuvToRgbIntrinsic.setInput(aIn);
在 Nexus 7(2013,JellyBean 4.3)上,全高清 (1920x1080) 相机预览转换大约需要 0.007 秒(是,7 毫秒)。
OpenCV-JNI 从 Nv21 数据为 4160x3120 大小的图像构建 Mat 似乎比 renderscript(68 毫秒 - 不包括初始化时间)快 2 倍(38 毫秒)。如果我们需要缩小构造的位图,OpenCV-JNI 似乎是更好的方法,因为我们只对 Y 数据使用全尺寸。仅在构建 OpenCV 垫时,CbCr 数据将调整大小以缩小您的数据。
更好的方法是将 NV21 字节数组和 int 像素数组传递给 Jni。 Jni 端可能不需要数组复制。然后,使用开源libyuv库(https://chromium.googlesource.com/libyuv/libyuv/)将NV21转换为ARGB。在Java中,我们使用传入的像素数组构造位图。在 Jni 中,在 arm64 平台上,对于 4160x3120 大小的字节数组,从 NV21 到 ARGB 的转换仅需 ~4ms。
我正在尝试从相机预览中捕获图像并在其上进行绘图。问题是,我只有大约 3-4 fps 的绘图,一半的帧处理时间是从相机预览接收和解码 NV21 图像并转换为 bitmap。我有一个代码来完成这个任务,我在另一个堆栈问题上找到了它。好像不快,不知道怎么优化。在三星 Note 3 上大约需要 100-150 毫秒,图像大小为 1920x1080。我怎样才能让它工作得更快?
代码:
public Bitmap curFrameImage(byte[] data, Camera camera)
{
Camera.Parameters parameters = camera.getParameters();
int imageFormat = parameters.getPreviewFormat();
if (imageFormat == ImageFormat.NV21)
{
YuvImage img = new YuvImage(data, ImageFormat.NV21, prevSizeW, prevSizeH, null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
img.compressToJpeg(new android.graphics.Rect(0, 0, img.getWidth(), img.getHeight()), 50, out);
byte[] imageBytes = out.toByteArray();
return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
}
else
{
Log.i(TAG, "Preview image not NV21");
return null;
}
}
图像的最终格式必须是位图,这样我才能对其进行处理。我尝试将 Camera.Parameters.setPreviewFormat 设置为 RGB_565,但无法将相机参数分配给相机,我还读到 NV21 是唯一可用的格式。我不确定是否可以在这些格式更改中找到解决方案。
提前致谢。
感谢 Alex Cohn 帮助我加快转换速度。我实施了您建议的方法(RenderScript 内在函数)。此代码使用 RenderScript 内部函数制作,将 YUV 图像转换为位图的速度大约快 5 倍。以前的代码用了 100-150 毫秒。在 Samsung Note 3 上,这需要 15-30 左右。 如果有人需要做同样的任务,这里是代码:
这些将被使用:
private RenderScript rs;
private ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic;
private Type.Builder yuvType, rgbaType;
private Allocation in, out;
在我初始化的创建函数中..:[=13=]
rs = RenderScript.create(this);
yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
整个 onPreviewFrame 看起来像这样(这里我接收并转换图像):
if (yuvType == null)
{
yuvType = new Type.Builder(rs, Element.U8(rs)).setX(dataLength);
in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(prevSizeW).setY(prevSizeH);
out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
}
in.copyFrom(data);
yuvToRgbIntrinsic.setInput(in);
yuvToRgbIntrinsic.forEach(out);
Bitmap bmpout = Bitmap.createBitmap(prevSizeW, prevSizeH, Bitmap.Config.ARGB_8888);
out.copyTo(bmpout);
您可以获得更快的速度(使用 JellyBean 4.3、API18 或更高版本): 相机预览模式必须为NV21 !
在 "onPreviewFrame()" 上只做 :
aIn.copyFrom(data);
yuvToRgbIntrinsic.forEach(aOut);
aOut.copyTo(bmpout); // and of course, show the bitmap or whatever
不要在此处创建任何对象。
所有其他东西(创建 rs、yuvToRgbIntrinsic、分配、位图)做 在 onCreate() 方法中 before 开始相机预览。
rs = RenderScript.create(this);
yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
// you don´t need Type.Builder objects , with cameraPreviewWidth and cameraPreviewHeight do:
int yuvDatalength = cameraPreviewWidth*cameraPreviewHeight*3/2; // this is 12 bit per pixel
aIn = Allocation.createSized(rs, Element.U8(rs), yuvDatalength);
// of course you will need the Bitmap
bmpout = Bitmap.createBitmap(cameraPreviewWidth, cameraPreviewHeight, Bitmap.Config.ARGB_8888);
// create output allocation from bitmap
aOut = Allocation.createFromBitmap(rs,bmpout); // this simple !
// set the script´s in-allocation, this has to be done only once
yuvToRgbIntrinsic.setInput(aIn);
在 Nexus 7(2013,JellyBean 4.3)上,全高清 (1920x1080) 相机预览转换大约需要 0.007 秒(是,7 毫秒)。
OpenCV-JNI 从 Nv21 数据为 4160x3120 大小的图像构建 Mat 似乎比 renderscript(68 毫秒 - 不包括初始化时间)快 2 倍(38 毫秒)。如果我们需要缩小构造的位图,OpenCV-JNI 似乎是更好的方法,因为我们只对 Y 数据使用全尺寸。仅在构建 OpenCV 垫时,CbCr 数据将调整大小以缩小您的数据。
更好的方法是将 NV21 字节数组和 int 像素数组传递给 Jni。 Jni 端可能不需要数组复制。然后,使用开源libyuv库(https://chromium.googlesource.com/libyuv/libyuv/)将NV21转换为ARGB。在Java中,我们使用传入的像素数组构造位图。在 Jni 中,在 arm64 平台上,对于 4160x3120 大小的字节数组,从 NV21 到 ARGB 的转换仅需 ~4ms。