Android方式使用Camera.setPreviewCallback中的Bitmaps

时间:2013-01-25 14:05:16

标签: android

camera.setPreviewCallback(new Camera.PreviewCallback() {
    private long timestamp=0;
    public synchronized void onPreviewFrame(byte[] data, Camera camera) {
        Log.e("CameraTest","Time Gap = "+(System.currentTimeMillis()-timestamp));
        timestamp=System.currentTimeMillis();

        Bitmap mFaceBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
        if (mFaceBitmap!=null) FaceDetection.calculate(mFaceBitmap);

        camera.addCallbackBuffer(data);
        return;
    }
});

我有一个摄影机视图,在一个简单的视图前面(我可以画一些东西)。 当我能找到一个人的脸时,我想画在View的前面。 但是mFaceBitmap永远都会返回null,为什么呢?

如果这是一个坏主意,我该怎样才能更好地做到这一点?

2 个答案:

答案 0 :(得分:2)

设置相机时,需要设置预览尺寸和预览格式。这里有一些示例代码可以提供粗略的想法:

int previewFormat = 0;
for (int format : parameters.getSupportedPreviewFormats()) {
  if (format == FORMAT_NV21) {
    previewFormat = FORMAT_NV21;
  } else if (previewFormat == 0 && (format == FORMAT_JPEG || format == FORMAT_RGB_565)) {
    previewFormat = format;
  }
}

// TODO: Iterate on supported preview sizes and pick best one
parameters.setPreviewSize(previewSize.width, previewSize.height);

if (previewFormat != 0) {
  parameters.setPreviewFormat(previewFormat);
} else {
  // Error on unsupported format
}

现在在回调中,您可以执行以下操作:

@Override
public void onPreviewFrame(byte[] data, Camera camera) {
  Bitmnap bitmap;
  if (previewFormat == FORMAT_NV21) {
    int[] previewPixels = new int[previewSize.width * previewSize.height];
    decodeYUV420SP(previewPixels, data, previewSize.width, previewSize.height);
    bitmap = Bitmap.createBitmap(rgbPixels, previewSize.width, previewSize.height, Bitmap.Config.RGB_565);
  } else if (previewFormat == FORMAT_JPEG || previewFormat == FORMAT_RGB_565) {
    // RGB565 and JPEG
    BitmapFactory.Options opts = new BitmapFactory.Options();
    opts.inDither = true;
    opts.inPreferredConfig = Bitmap.Config.RGB_565;
    bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts);
  }
}

最后,转换

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

答案 1 :(得分:1)

不幸的是,您无法使用Bitmap.decodeByteArray将相机的预览输出转换为位图。

decodeByteArray用于将JPEG / PNG / etc图像转换为位图,它无法知道预览回调中的数据是什么样的,因为它是一个简单的像素值原始数组,没有标识头

您必须自己进行转换。有很多方法可以做到这一点,效率各不相同 - 我会在这里写出最简单的方法,但它也可能是最慢的。

来自摄像机的数据字节数组以某种特定的像素格式进行编码,该格式由Camera.Parameters.setPreviewFormat指定。如果您尚未调用此方法,则默认格式为NV21。 NV21保证可以在所有Android设备上运行;在Android版本&gt; = 3.0上,YV12格式也可以保证有效。

这两种都是YUV格式,意味着颜色被编码为亮度(亮度)通道和两个色度(颜色)通道。在Bitmap(主要是setPixels)上设置像素值的功能需要RGB颜色空间中的信息,因此需要进行转换。此外,NV21和YV12都对色度通道进行子采样 - 例如,如果您有640x480图像,亮度通道中将有640x480像素,但两个色度通道中只有320x240像素。

这意味着您需要创建一个正确大小的新int []数组,然后遍历byte []数据数组,收集一组Y,U和V值,将它们转换为RGB,以及将它们写入int []数组,然后在目标位图上调用setPixels。您需要的颜色转换矩阵是JPEG YCbCr-> RGB矩阵,您可以在Wikipedia找到它。您可以在fourcc找到有关NV21或YV12布局的示例

如果你真的不想搞砸所有这些,你也可以使用YuvImage课,虽然是以迂回的方式。只要您使用的是NV21格式,就可以从预览数据构造YuvImage实例,然后将JPEG格式保存到ByteArrayOutputStream。然后,您可以从流中获取byte [],并使用Bitmap.decodeByteArray将其解码为位图。这是一个完全不必要的往返JPEG往返,所以它效率很低,可能导致质量损失,但它只需要几行代码。

在最新版本的Android中,您还可以使用Renderscript有效地执行此转换。您需要将数据复制到分配中,然后使用YUV to RGB script intrinsic进行转换。

最后,您可以将数据和目标位图传递到JNI代码,您可以在其中直接访问Bitmap,并使用C或C ++编写转换函数。这需要很多脚手架,但效率很高。