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?
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;
}
}
}
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.
La solution RenderScript de Tim est excellente. Deux commentaires ici cependant:
RenderScript rs
, et Allocation in, out
. Les créer à chaque image nuira aux performances.