web-dev-qa-db-fra.com

Extraire l'image en noir et blanc du format Android de l'appareil photo NV21

J'ai fait quelques recherches sur Google et je n'ai pas trouvé suffisamment d'informations sur ce format. Il s'agit du format par défaut pour l'aperçu de l'appareil photo. Quelqu'un peut-il suggérer de bonnes sources d'informations à ce sujet et comment extraire des données d'une photo/image d'aperçu avec ce format? Pour être plus précis, j'ai besoin d'extraire l'image en noir et blanc.

EDIT: On dirait que ce format est également appelé YCbCr 420 Semi Planar

31
Ethan

J'ai développé le code suivant pour convertir le NV21 en RVB, et cela fonctionne.

/**
 * Converts YUV420 NV21 to RGB8888
 * 
 * @param data byte array on YUV420 NV21 format.
 * @param width pixels width
 * @param height pixels height
 * @return a RGB8888 pixels int array. Where each int is a pixels ARGB. 
 */
public static int[] convertYUV420_NV21toRGB8888(byte [] data, int width, int height) {
    int size = width*height;
    int offset = size;
    int[] pixels = new int[size];
    int u, v, y1, y2, y3, y4;

    // i percorre os Y and the final pixels
    // k percorre os pixles U e V
    for(int i=0, k=0; i < size; i+=2, k+=2) {
        y1 = data[i  ]&0xff;
        y2 = data[i+1]&0xff;
        y3 = data[width+i  ]&0xff;
        y4 = data[width+i+1]&0xff;

        u = data[offset+k  ]&0xff;
        v = data[offset+k+1]&0xff;
        u = u-128;
        v = v-128;

        pixels[i  ] = convertYUVtoRGB(y1, u, v);
        pixels[i+1] = convertYUVtoRGB(y2, u, v);
        pixels[width+i  ] = convertYUVtoRGB(y3, u, v);
        pixels[width+i+1] = convertYUVtoRGB(y4, u, v);

        if (i!=0 && (i+2)%width==0)
            i+=width;
    }

    return pixels;
}

private static int convertYUVtoRGB(int y, int u, int v) {
    int r,g,b;

    r = y + (int)(1.402f*v);
    g = y - (int)(0.344f*u +0.714f*v);
    b = y + (int)(1.772f*u);
    r = r>255? 255 : r<0 ? 0 : r;
    g = g>255? 255 : g<0 ? 0 : g;
    b = b>255? 255 : b<0 ? 0 : b;
    return 0xff000000 | (b<<16) | (g<<8) | r;
}

Cette image aide à comprendre. YUV420 NV21

Si vous voulez simplement une image en niveaux de gris, c'est plus simple. Vous pouvez supprimer toutes les informations U et V et ne prendre que les informations Y. Le code pourrait être comme ceci:

/**
 * Converts YUV420 NV21 to Y888 (RGB8888). The grayscale image still holds 3 bytes on the pixel.
 * 
 * @param pixels output array with the converted array o grayscale pixels
 * @param data byte array on YUV420 NV21 format.
 * @param width pixels width
 * @param height pixels height
 */
public static void applyGrayScale(int [] pixels, byte [] data, int width, int height) {
    int p;
    int size = width*height;
    for(int i = 0; i < size; i++) {
        p = data[i] & 0xFF;
        pixels[i] = 0xff000000 | p<<16 | p<<8 | p;
    }
}

Pour créer votre Bitmap simplement:

Bitmap bm = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);

Où pixels est votre tableau int [].

81
Derzu

NV21 est fondamentalement YUV420 mais au lieu du format planaire où Y, U et V ont des plans indépendants, NV21 a 1 plan pour Luma et 2e plan pour Chroma. Le format ressemble à

YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY. . . .

VUVUVUVUVUVUVUVUVUVUVUVUVUVUVU VUVUVUVUVUVUVUVUVUVUVUVUVUVUVU. . . . .

12
Mayank

J'ai également eu beaucoup de maux de tête à cause de ce format d'aperçu.

Les meilleurs que j'ai pu trouver sont les suivants:

http://www.fourcc.org/yuv.php#NV21

http://v4l2spec.bytesex.org/spec/r5470.htm

Il semble que le composant Y soit le premier octet largeur * hauteur dans le tableau que vous obtenez.

Quelques liens plus informatifs:

http://msdn.Microsoft.com/en-us/library/ms867704.aspx#yuvformats_yuvsampling

http://msdn.Microsoft.com/en-us/library/ff538197 (v = vs.85) .aspx

J'espère que cela t'aides.

7
Max

Les données sont au format YUV420.
Si vous êtes uniquement intéressé par le canal monochrome, c'est-à-dire "noir et blanc", alors c'est le premier width x height octets du tampon de données que vous avez déjà.
Le canal Y est le plan d'image premier. C'est exactement le canal gris/intensité/luminosité, etc.

4
Adi Shavit

Voici le code pour extraire simplement les données d'image en niveaux de gris:

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);
    }
    return out;
}
3
Sam

Lorsque vous n'avez besoin que d'un aperçu de la caméra en niveaux de gris, vous pouvez utiliser un rendu très simple:

# pragma version(1)
# pragma rs Java_package_name(com.example.name)
# pragma rs_fp_relaxed

rs_allocation gIn;   // Allocation filled with camera preview data (byte[])
int previewwidth;    // camera preview width (int)

// the parallel executed kernel
void root(uchar4 *v_out, uint32_t x,uint32_t y){
   uchar c = rsGetElementAt_uchar(gIn,x+y*previewwidth); 
   *v_out = (uchar4){c,c,c,255};
}

Remarque: C'est pas plus rapide que ScriptIntrinsicYuvToRGB (et un ScriptIntrinsicColorMatrix suivant pour faire le RGBA-> gris), mais il fonctionne avec l'API 11+ (où les Intrinsics ont besoin d'Api 17+).

1
Matti81