web-dev-qa-db-fra.com

Affichage de l'image YUV dans Android

Dans mon application, nous devons afficher l'image vidéo reçue du serveur vers notre application Android,
Le serveur envoie des données vidéo à 50 images par seconde, les ayant encodées dans WebM, c'est-à-dire utilisant libvpx pour encoder et décoder les images,

Maintenant, après avoir décodé à partir de libvpx ses données YUV, que nous pouvons afficher sur la disposition de l'image,

l'implémentation actuelle est quelque chose comme ça,

Dans le code JNI/Native C++, nous convertissons les données YUV en données RVB dans Android framework, appelant

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

Pour créer l'image bitmap,

pour afficher l'image sur imageView en utilisant le code suivant,

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

Ma requête est, dans l'ensemble, cette fonction prend environ 40 ms, existe-t-il un moyen de l'optimiser,
1 - Existe-t-il un moyen d'afficher les données YUV sur imageView?

2 - Existe-t-il un autre moyen de créer une image (image bitmap) à partir de données RVB,

3 - Je crois que je crée toujours une image, mais je suppose que je ne devrais créer le bitmap qu'une seule fois et faire/fournir toujours un nouveau tampon, au fur et à mesure que nous recevons.
veuillez partager vos opinions.

26
Amitg2k12

Le code suivant résout votre problème et cela peut prendre moins de temps sur les données au format Yuv car la classe YuvImage est fournie avec Android-SDK.

Vous pouvez essayer ça,

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

ou

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);
    }
}
}
41
Hitesh Patel

Créez un bitmap après avoir obtenu la largeur et la hauteur dans onCreate.

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

Et en onPreviewFrame.

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

Et

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

Et en bonus.

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

finalBitmap = Bitmap.createBitmap(editedBitmap, 0, 0, widthPreview, heightPreview, matrix, true);
1
Xar E Ahmer

Sur la base de la réponse acceptée, j'ai pu trouver un moyen beaucoup plus rapide de faire la conversion YUV en RVB en utilisant la méthode de conversion intrinsèque RenderScript. J'ai trouvé l'exemple direct ici: Yuv2RgbRenderScript .

Il peut être aussi simple que de copier la méthode convertYuvToRgbIntrinsic dans la classe RenderScriptHelper pour remplacer le decodeYUV420SP que Hitesh Patel donne dans sa réponse. De plus, vous devrez initialiser un objet RenderScript (l'exemple se trouve dans la classe MainActivity).

Et n'oubliez pas d'ajouter dans le graddle du projet l'utilisation du script de rendu (dans la page Android vous pouvez trouver le moyen de le faire).

0
S. serra