web-dev-qa-db-fra.com

Obtention d'images à partir d'une image vidéo dans Android

J'ai implémenté une application simple qui montre l'image de la caméra à l'écran. Ce que j'aime faire maintenant, c'est saisir une seule image et la traiter comme bitmap. D'après ce que j'ai pu découvrir jusqu'à présent, ce n'est pas une chose facile à faire.

J'ai essayé d'utiliser la méthode onPreviewFrame avec laquelle vous obtenez l'image actuelle en tant que tableau d'octets et j'ai essayé de la décoder avec la classe BitmapFactory mais elle renvoie null. Le format du cadre est un YUV sans en-tête qui pourrait être traduit en bitmap mais cela prend trop de temps sur un téléphone. J'ai également lu que la méthode onPreviewFrame a des contraintes sur le runtime, si cela prend trop de temps l'application pourrait se bloquer.

Alors, quelle est la bonne façon de procéder?

32
Alexander Stolz

Ok, nous avons fini par utiliser la méthode onPreviewFrame et décoder les données dans un thread séparé en utilisant une méthode qui se trouve dans le groupe d'aide Android Android.

decodeYUV(argb8888, data, camSize.width, camSize.height);
Bitmap bitmap = Bitmap.createBitmap(argb8888, camSize.width,
                    camSize.height, Config.ARGB_8888);

...

// decode Y, U, and V values on the YUV 420 buffer described as YCbCr_422_SP by Android 
// David Manpearl 081201 
public void decodeYUV(int[] out, byte[] fg, int width, int height)
        throws NullPointerException, IllegalArgumentException {
    int sz = width * height;
    if (out == null)
        throw new NullPointerException("buffer out is null");
    if (out.length < sz)
        throw new IllegalArgumentException("buffer out size " + out.length
                + " < minimum " + sz);
    if (fg == null)
        throw new NullPointerException("buffer 'fg' is null");
    if (fg.length < sz)
        throw new IllegalArgumentException("buffer fg size " + fg.length
                + " < minimum " + sz * 3 / 2);
    int i, j;
    int Y, Cr = 0, Cb = 0;
    for (j = 0; j < height; j++) {
        int pixPtr = j * width;
        final int jDiv2 = j >> 1;
        for (i = 0; i < width; i++) {
            Y = fg[pixPtr];
            if (Y < 0)
                Y += 255;
            if ((i & 0x1) != 1) {
                final int cOff = sz + jDiv2 * width + (i >> 1) * 2;
                Cb = fg[cOff];
                if (Cb < 0)
                    Cb += 127;
                else
                    Cb -= 128;
                Cr = fg[cOff + 1];
                if (Cr < 0)
                    Cr += 127;
                else
                    Cr -= 128;
            }
            int R = Y + Cr + (Cr >> 2) + (Cr >> 3) + (Cr >> 5);
            if (R < 0)
                R = 0;
            else if (R > 255)
                R = 255;
            int G = Y - (Cb >> 2) + (Cb >> 4) + (Cb >> 5) - (Cr >> 1)
                    + (Cr >> 3) + (Cr >> 4) + (Cr >> 5);
            if (G < 0)
                G = 0;
            else if (G > 255)
                G = 255;
            int B = Y + Cb + (Cb >> 1) + (Cb >> 2) + (Cb >> 6);
            if (B < 0)
                B = 0;
            else if (B > 255)
                B = 255;
            out[pixPtr++] = 0xff000000 + (B << 16) + (G << 8) + R;
        }
    }

}

Lien: http://groups.google.com/group/Android-developers/browse_thread/thread/c85e829ab209ceea/3f180a16a4872b58?lnk=gst&q=onpreviewframe#3f180a16a4872b58

34
Alexander Stolz

J'ai en fait essayé le code étant donné que la réponse précédente a révélé que les valeurs de couleur ne sont pas exactes. Je l'ai vérifié en prenant à la fois l'aperçu et le camera.takePicture qui renvoie directement un tableau JPEG. Et les couleurs étaient très différentes. Après un peu plus de recherche, j'ai trouvé un autre exemple pour convertir le PreviewImage de YCrCb en RVB:

static public void decodeYUV420SP(int[] rgb, 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);
        }
    }
}

Les valeurs de couleur données par ceci et le takePicture () correspondent exactement. J'ai pensé que je devrais le poster ici. C'est de là que j'ai obtenu ce code. J'espère que cela vous aidera.

10
Codevalley

La solution RenderScript de Tim est excellente. Deux commentaires ici cependant:

  1. Créer et réutiliser RenderScript rs, et Allocation in, out. Les créer à chaque image nuira aux performances.
  2. bibliothèque de prise en charge de RenderScript peut vous aider à sauvegarder la prise en charge de Android 2.3.
2
Miao Wang