Yuv(NV21)图像转换为位图

时间:2016-03-06 12:14:57

标签: java android bitmap camera yuv

我正在尝试从相机预览中捕捉图像并对其进行绘制。问题是,我只有大约3-4 fps的绘图,一半的帧处理时间是从摄像机预览接收和解码NV21图像并转换为位图。我有一个代码来执行此任务,我在另一个堆栈问题上找到了该代码。它似乎并不快,但我不知道如何优化它。三星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是唯一可用的格式。我不确定,是否有可能在这些格式变化中找到解决方案。

提前谢谢。

3 个答案:

答案 0 :(得分:7)

感谢Alex Cohn帮助我更快地完成转换。我实现了您建议的方法(RenderScript内在函数)。此代码由RenderScript内在函数构成,将YUV图像转换为位图的速度提高约5倍。以前的代码花了100-150毫秒。在三星Note 3上,这需要15-30左右。 如果有人需要执行相同的任务,请输入以下代码:

将使用这些:

private RenderScript rs;
private ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic;
private Type.Builder yuvType, rgbaType;
private Allocation in, out;

在创建函数中,我初始化..:

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);

答案 1 :(得分:5)

你可以获得更快的速度(使用JellyBean 4.3,API18或更高版本): 相机预览模式必须为NV21!

在“onPreviewFrame()”上执行

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait as wait

driver = webdriver.PhantomJS(executable_path='/path/to/phantomJS')
driver.get('http://www.redflagdeals.com/search/#!/q=baby%20monitor')
xpath = '//a[@class = "offer_title"]'
wait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, xpath)))
offers = [link.get_attribute('textContent') for link in driver.find_elements_by_xpath(xpath)]

不要在这里创建任何对象。

所有其他东西(创建rs,yuvToRgbIntrinsic,分配,位图) 在开始摄像机预览之前的onCreate()方法中。

aIn.copyFrom(data);

yuvToRgbIntrinsic.forEach(aOut);

aOut.copyTo(bmpout);  // and of course, show the bitmap or whatever

在Nexus 7(2013,JellyBean 4.3)上,全高清(1920x1080)相机预览转换大约需要0.007秒(是,7毫秒)。

答案 2 :(得分:1)

OpenCV-JNI从416x3120尺寸图像的Nv21数据构建Mat似乎比renderscript(68毫秒 - 不包括初始化时间)快2倍(38毫秒)。如果我们需要调整构造的位图的大小,OpenCV-JNI似乎更好的方法,因为我们将仅对Y数据使用全尺寸。 CbCr数据将调整大小以缩小您的数据仅在OpenCV垫构建时。

更好的方法是将NV21字节数组和int像素数组传递给Jni。 Jni方面可能不需要数组副本。然后,使用开源libyuv库(https://chromium.googlesource.com/libyuv/libyuv/)将NV21转换为ARGB。在Java中,我们使用传递的像素数组来构造位图。在Jni中,在arm64平台上,对于4160x3120大小的字节数组,从NV21到ARGB的转换仅需约4ms。