Отображение YUV изображения в Android - PullRequest
26 голосов
/ 08 февраля 2012

В моем приложении нам нужно отобразить видеокадр, полученный с сервера на наше приложение для Android,
Сервер отправляет видеоданные с частотой 50 кадров в секунду, закодировав их в WebM, т.е. используя libvpx для кодирования и декодирования изображений,

Теперь после декодирования из libvpx получаемых данных YUV, которые мы можем отобразить поверх макета изображения,

текущая реализация выглядит примерно так:

В JNI / Native 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 - я считаю, что я всегда создаю изображение, но я полагаю, что я должен создавать только растровое изображениеодин раз и делайте / поставляйте новый буфер всегда, как и когда мы получили.
Пожалуйста, поделитесь своими взглядами.

Ответы [ 3 ]

41 голосов
/ 17 февраля 2012

Следующий код решит вашу проблему, и для данных формата 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(byte[] data, int mWidth, int mHeight)
{

int[] mIntArray = new int[mWidth*mHeight];

// Decode Yuv data to integer array
decodeYUV420SP(mIntArray, data, mWidth, mHeight);

//Initialize the bitmap, with the replaced color  
Bitmap bmp = Bitmap.createBitmap(mIntArray, mWidth, mHeight, Bitmap.Config.ARGB_8888);  

// Draw the bitmap with the replaced color  
iv.setImageBitmap(bmp);  

}

static public void decodeYUV420SP(int[] rgba, byte[] yuv420sp, int width,
    int height) {
final int frameSize = width * height;

for (int j = 0, yp = 0; j < height; j++) {
    int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
    for (int i = 0; i < width; i++, yp++) {
        int y = (0xff & ((int) yuv420sp[yp])) - 16;
        if (y < 0)
            y = 0;
        if ((i & 1) == 0) {
            v = (0xff & yuv420sp[uvp++]) - 128;
            u = (0xff & yuv420sp[uvp++]) - 128;
        }

        int y1192 = 1192 * y;
        int r = (y1192 + 1634 * v);
        int g = (y1192 - 833 * v - 400 * u);
        int b = (y1192 + 2066 * u);

        if (r < 0)
            r = 0;
        else if (r > 262143)
            r = 262143;
        if (g < 0)
            g = 0;
        else if (g > 262143)
            g = 262143;
        if (b < 0)
            b = 0;
        else if (b > 262143)
            b = 262143;

        // rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) &
        // 0xff00) | ((b >> 10) & 0xff);
        // rgba, divide 2^10 ( >> 10)
        rgba[yp] = ((r << 14) & 0xff000000) | ((g << 6) & 0xff0000)
                | ((b >> 2) | 0xff00);
    }
}
}
1 голос
/ 30 апреля 2015

Создание растрового изображения после получения ширины и высоты в onCreate.

editedBitmap = Bitmap.createBitmap(widthPreview, heightPreview,
                android.graphics.Bitmap.Config.ARGB_8888);

А в onPreviewFrame.

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

И Бонус.

if(cameraId==CameraInfo.CAMERA_FACING_FRONT)
{   
    matrix.setRotate(270F);
}

finalBitmap = Bitmap.createBitmap(editedBitmap, 0, 0, widthPreview, heightPreview, matrix, true);
0 голосов
/ 15 мая 2019

На основании принятого ответа я смог найти более быстрый способ преобразования YUV в RGB с использованием встроенного метода преобразования RenderScript.Я нашел прямой пример здесь: Yuv2RgbRenderScript .

Это может быть так же просто, как скопировать метод convertYuvToRgbIntrinsic в классе RenderScriptHelper, чтобы заменить decodeYUV420SP, который Хитеш Патель дал в своем ответе.Также вам нужно будет инициализировать объект RenderScript (пример находится в классе MainActivity).

И не забудьте добавить в проект Graddle использование скрипта рендеринга (на странице Android вы можете найти способ сделать это).

...