在Android中从video图像获取帧

我已经实现了一个简单的应用程序,在屏幕上显示相机图片。 我现在喜欢做的是抓住一个框架,并将其作为位图处理。 从我所能发现的事情来看,这不是一件容易的事情。

我已经尝试使用onPreviewFrame方法获取当前帧作为字节数组,并尝试使用BitmapFactory类解码它,但它返回null。 该帧的格式是一个无标题的YUV,可以转换为位图,但在手机上需要很长的时间。 另外我已经读过onPreviewFrame方法在运行时有约束,如果应用程序可能会崩溃太长时间。

那么做这件事的正确方法是什么?

Solutions Collecting From Web of "在Android中从video图像获取帧"

在API 17+中,您可以使用“ScriptIntrinsicYuvToRGB”RenderScript从NV21转换为RGBA888。 这使您可以轻松处理预览帧而无需手动编码/解码帧:

@Override public void onPreviewFrame(byte[] data, Camera camera) { Bitmap bitmap = Bitmap.createBitmap(r.width(), r.height(), Bitmap.Config.ARGB_8888); Allocation bmData = renderScriptNV21ToRGBA888( mContext, r.width(), r.height(), data); bmData.copyTo(bitmap); } public Allocation renderScriptNV21ToRGBA888(Context context, int width, int height, byte[] nv21) { RenderScript rs = RenderScript.create(context); ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs)); Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(nv21.length); Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT); Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height); Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT); in.copyFrom(nv21); yuvToRgbIntrinsic.setInput(in); yuvToRgbIntrinsic.forEach(out); return out; } 

好的,我们最终做的是使用onPreviewFrame方法,并使用android帮助组中的方法在单独的Thread中解码数据。

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

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

我实际上尝试了以前的答案的代码发现Colorvalues不是确切的。 我检查了预览和camera.takePicture直接返回一个JPEG数组。 而颜色是非常不同的。 经过多一点search后,我发现另一个例子,将PreviewImage从YCrCb转换为RGB:

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

这个和takePicture()给出的颜色值完全一致。 我以为我应该在这里发表。 这是我得到这个代码的地方。 希望这可以帮助。

Tim的RenderScript解决scheme非常棒。 这里有两条评论:

  1. 创build和重用RenderScript rsAllocation in, out 。 每帧创build它们都会损害性能。
  2. RenderScript支持库可以帮助您支持Android 2.3。