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
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.
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 [].
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. . . . .
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.
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.
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;
}
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+).