2
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を返すのはなぜですか?

これが悪い考えである場合、どうすればこれを改善できますか?

4

2 に答える 2

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);
     }
   }
 }    
于 2013-01-25T19:47:31.837 に答える
1

残念ながら、Bitmap.decodeByteArray を使用してカメラのプレビュー出力をビットマップに変換することはできません。

decodeByteArray は JPEG/PNG/etc 画像をビットマップに変換するために設計されており、プレビュー コールバックのデータがどのようなものかを知る方法はありません。これは、識別ヘッダーのないピクセル値の単純な生の配列であるためです。

変換は自分で行う必要があります。これを行うには多くの方法があり、さまざまな程度の効率があります。ここでは最も簡単な方法を書きますが、おそらく最も遅い方法でもあります。

カメラからのデータ バイト配列は、 Camera.Parameters.setPreviewFormatで指定された特定のピクセル形式でエンコードされます。これを呼び出していない場合、デフォルトの形式はNV21です。NV21 は、すべての Android デバイスで動作することが保証されています。Android バージョン >= 3.0 では、YV12形式も動作することが保証されています。

これらはどちらも YUV 形式です。つまり、色は輝度 (明るさ) チャネルと 2 つの彩度 (色) チャネルとしてエンコードされます。Bitmap にピクセル値を設定する関数 (主に setPixels) は、代わりに RGB 色空間の情報を必要とするため、変換が必要です。さらに、NV21 と YV12 の両方がクロマ チャネルをサブサンプリングします。たとえば、640x480 の画像がある場合、ルミナンス チャネルには 640x480 ピクセルがありますが、2 つのクロマ チャネルには 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 を使用してこの変換を効率的に行うこともできます。データを Allocation にコピーしてから、YUV から RGB への組み込みスクリプトを使用して変換を行う必要があります。

最後に、データと宛先ビットマップを JNI コードに渡すことができます。JNI コードでは、ビットマップに直接アクセスし、そこに C または C++ で変換関数を記述できます。これには多くの足場が必要ですが、非常に効率的です。

于 2013-01-25T19:13:46.127 に答える