在Android中显示YUV图像

在Android中显示YUV图像,android,image-processing,imageview,yuv,Android,Image Processing,Imageview,Yuv,在我的应用程序中,我们需要将从服务器接收到的视频帧显示到android应用程序, 服务器正在以每秒50帧的速度发送视频数据,已在WebM中编码,即使用libvpx对图像进行编码和解码 现在从libvpx解码后,它得到了YUV数据,我们可以在图像布局上显示 目前的实施是这样的, JNI /本地C++代码,我们将YUV数据转换为RGB数据 在Android框架中,调用 public Bitmap createImgae(byte[] bits, int width, int height, int

在我的应用程序中,我们需要将从服务器接收到的视频帧显示到android应用程序,
服务器正在以每秒50帧的速度发送视频数据,已在WebM中编码,即使用libvpx对图像进行编码和解码

现在从libvpx解码后,它得到了YUV数据,我们可以在图像布局上显示

目前的实施是这样的,

JNI /本地C++代码,我们将YUV数据转换为RGB数据 在Android框架中,调用

public Bitmap createImgae(byte[] bits, int width, int height, int scan) {
    Bitmap bitmap=null;
    System.out.println("video: creating bitmap");
    //try{

            bitmap = Bitmap.createBitmap(width, height,
                    Bitmap.Config.ARGB_8888);
            bitmap.copyPixelsFromBuffer(ByteBuffer.wrap(bits));     

    //}catch(OutOfMemoryError ex){

    //}
            System.out.println("video: bitmap created");
    return bitmap;
}  
要创建位图图像

要使用以下代码在imageView上显示图像

               img = createImgae(imgRaw, imgInfo[0], imgInfo[1], 1);
               if(img!=null && !img.isRecycled()){

                    iv.setImageBitmap(img);
                    //img.recycle();
                    img=null;
                    System.out.println("video: image displayed");
                }
我的问题是,总的来说,这个功能大约需要40毫秒,有没有办法对它进行优化,
1--是否有任何方法将YUV数据显示到imageView

2--是否有其他方法从RGB数据创建图像(位图图像)

3--我相信我一直在创建图像,但我想我应该只创建一次位图,并在收到时始终提供新的缓冲区。

请分享你的观点

下面的代码解决了您的问题,并且可能需要更少的时间处理Yuv格式的数据,因为YuvImage类是随Android SDK提供的

你可以试试这个

ByteArrayOutputStream out = new ByteArrayOutputStream();
YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, width, height, null);
yuvImage.compressToJpeg(new Rect(0, 0, width, height), 50, out);
byte[] imageBytes = out.toByteArray();
Bitmap image = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
iv.setImageBitmap(image);

void yourFunction(字节[]数据,整数mWidth,整数mHeight)
{
int[]mIntArray=新int[mWidth*mHeight];
//将Yuv数据解码为整数数组
decodeYUV420SP(最小数组、数据、最大宽度、最大高度);
//使用替换的颜色初始化位图
位图bmp=Bitmap.createBitmap(mIntArray、mWidth、mHeight、Bitmap.Config.ARGB_8888);
//用替换的颜色绘制位图
iv.设置图像位图(bmp);
}
静态公共无效解码yuv420sp(int[]rgba,字节[]yuv420sp,int-width,
整数高度){
最终整数帧大小=宽度*高度;
对于(int j=0,yp=0;j>1)*宽度,u=0,v=0;
对于(int i=0;i262143)
r=262143;
if(g<0)
g=0;
否则,如果(g>262143)
g=262143;
if(b<0)
b=0;
否则,如果(b>262143)
b=262143;
//rgb[yp]=0xff000000|((r>2)&
//0xff00)|((b>>10)和0xff);
//rgba,除以2^10(>>10)
rgba[yp]=((r2)| 0xff00);
}
}
}
在onCreate中获取宽度和高度后创建位图

在预览框架上的
中。

int[] rgbData = decodeGreyscale(aNv21Byte,widthPreview,heightPreview);
editedBitmap.setPixels(rgbData, 0, widthPreview, 0, 0, widthPreview, heightPreview);

private int[] decodeGreyscale(byte[] nv21, int width, int height) {
    int pixelCount = width * height;
    int[] out = new int[pixelCount];
    for (int i = 0; i < pixelCount; ++i) {
        int luminance = nv21[i] & 0xFF;
       // out[i] = Color.argb(0xFF, luminance, luminance, luminance);
        out[i] = 0xff000000 | luminance <<16 | luminance <<8 | luminance;//No need to create Color object for each.
    }
    return out;
}

基于公认的答案,我可以找到一种使用RenderScript intrinsict转换方法进行YUV到RGB转换的更快方法。我在这里找到了直接的例子:

它可以像复制RenderScriptHelper类中的convertYuvToRgbIntrinsic方法一样简单,以替换Hitesh Patel在回答中给出的decodeYUV420SP。此外,还需要初始化RenderScript对象(示例位于MainActivity类中)


别忘了在project graddle中添加渲染脚本的使用(在android页面中,您可以找到执行此操作的方法)。

另一种方法是使用,这比每次JPEG压缩时编码(和解码)更有效

fun yuvByteArrayToBitmap(bytes: ByteArray, width: Int, height: Int): Bitmap {
    val rs = RenderScript.create(this)

    val yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
    val yuvType = Type.Builder(rs, Element.U8(rs)).setX(bytes.size);
    val input = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);

    val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
    val output = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);

    input.copyFrom(bytes);

    yuvToRgbIntrinsic.setInput(input);
    yuvToRgbIntrinsic.forEach(output);

    val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    output.copyTo(bitmap)

    input.destroy()
    output.destroy()
    yuvToRgbIntrinsic.destroy()
    rs.destroy()

    return bitmap
}

bitmap.copyPixelsFromBuffer(ByteBuffer.wrap(位))是否将YUV转换为RGB?或者你是说这就是你在母语中所做的吗?这里有一种方法。我尝试了你的decodeYUV420SP()方法,但是它创建的图像不好。它充满了黄色和绿色的波浪。我做了这一个,它的作品:我不明白为什么rgba[yp]=。。。被移位8位。注释掉的行更正确。我得到了一个旋转的图像。有没有一种无损压缩(即不是JPEG)的方法?也看看我的问题,YuvImage对于这个目的是非常无用的,转换为JPG并返回到RGB每帧对于视频播放来说太慢了,更不用说JPG是有损格式,所以视频看起来会比原始视频更糟。实际上,这回答了一个不同的问题。
private int[] decodeGreyscale(byte[] nv21, int width, int height) {
    int pixelCount = width * height;
    int[] out = new int[pixelCount];
    for (int i = 0; i < pixelCount; ++i) {
        int luminance = nv21[i] & 0xFF;
       // out[i] = Color.argb(0xFF, luminance, luminance, luminance);
        out[i] = 0xff000000 | luminance <<16 | luminance <<8 | luminance;//No need to create Color object for each.
    }
    return out;
}
if(cameraId==CameraInfo.CAMERA_FACING_FRONT)
{   
    matrix.setRotate(270F);
}

finalBitmap = Bitmap.createBitmap(editedBitmap, 0, 0, widthPreview, heightPreview, matrix, true);
fun yuvByteArrayToBitmap(bytes: ByteArray, width: Int, height: Int): Bitmap {
    val rs = RenderScript.create(this)

    val yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
    val yuvType = Type.Builder(rs, Element.U8(rs)).setX(bytes.size);
    val input = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);

    val rgbaType = Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
    val output = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);

    input.copyFrom(bytes);

    yuvToRgbIntrinsic.setInput(input);
    yuvToRgbIntrinsic.forEach(output);

    val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    output.copyTo(bitmap)

    input.destroy()
    output.destroy()
    yuvToRgbIntrinsic.destroy()
    rs.destroy()

    return bitmap
}