16

私のカメラアプリはカメラのプレビューを画面に表示し、バックグラウンドでも処理します。関連するコードを以下に示します。可能な限り圧縮されています (たとえば、エラー処理やフィールド宣言は示されていません)。

public final class CameraView extends SurfaceView implements
          SurfaceHolder.Callback, Runnable, PreviewCallback {

    public CameraView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 

    void openCamera() {
        // Called from parent activity after setting content view to CameraView
        mCamera = Camera.open();
        mCamera.setPreviewCallbackWithBuffer(this);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        new Thread(this).start(); 

        // Set CameraView to the optimal camera preview size

        final Camera.Parameters params = mCamera.getParameters();
        final List<Camera.Size> sizes = params.getSupportedPreviewSizes();
        final int screenWidth = ((View) getParent()).getWidth();
        int minDiff = Integer.MAX_VALUE;
        Camera.Size bestSize = null;

        if (getResources().getConfiguration().orientation 
                == Configuration.ORIENTATION_LANDSCAPE) {
            // Find the camera preview width that best matches the
            // width of the surface.
            for (Camera.Size size : sizes) {
                final int diff = Math.abs(size.width - screenWidth);
                if (diff < minDiff) {
                    minDiff = diff;
                    bestSize = size;
                }
            }
        } else {
            // Find the camera preview HEIGHT that best matches the 
            // width of the surface, since the camera preview is rotated.
            mCamera.setDisplayOrientation(90);
            for (Camera.Size size : sizes) {
                final int diff = Math.abs(size.height - screenWidth);
                if (Math.abs(size.height - screenWidth) < minDiff) {
                    minDiff = diff;
                    bestSize = size;
                }
            }
        }

        final int previewWidth = bestSize.width;
        final int previewHeight = bestSize.height;

        ViewGroup.LayoutParams layoutParams = getLayoutParams();
        layoutParams.height = previewHeight;
        layoutParams.width = previewWidth;
        setLayoutParams(layoutParams);

        params.setPreviewFormat(ImageFormat.NV21);
        mCamera.setParameters(params);

        int size = previewWidth * previewHeight * 
            ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8;
        mBuffer = new byte[size];
        mCamera.addCallbackBuffer(mBuffer);

        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();
    }

    public void onPreviewFrame(byte[] data, Camera camera) {
        CameraView.this.notify();
    }

    public void run() {
        mThreadRun = true;
        while (mThreadRun) {
            synchronized (this) {
                this.wait();
                processFrame(mBuffer); // convert to RGB and rotate - not shown
            }
            // Request a new frame from the camera by putting 
            // the buffer back into the queue
            mCamera.addCallbackBuffer(mBuffer);
        }

        mHolder.removeCallback(this);
        mCamera.stopPreview();
        mCamera.setPreviewCallback(null);
        mCamera.release();
        mCamera = null;
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        mThreadRun = false;
    }
}

すべてのデバイスでカメラのプレビューが正しく表示され、ほとんどのデバイス (エミュレーター、Samsung Galaxy S3 など) で保存されているデータmBufferも正しく表示されます (もちろん、NV21 から RGB への変換と回転の後)。ただし、多くのデバイスは onPreviewFrame で正しいデータを提供しません。データは受信後に正しく RGB に変換されているはずなので、問題は に供給された生データにあるようmBufferです。YV12 (エイリアス YUV420p) カメラ プレビュー フォーマットに関連するこのバグ レポートに気付きましたが、古いデフォルトの NV21 (エイリアス YUV420sp) を使用しています。これは、互換性標準に従ってサポートする必要があります(7.5.3.2 の下部を参照)。 29ページの)。

たとえば、このシーンの場合 (ここでは、Samsung Galaxy Tab 2 のカメラ プレビューに示されています):

ここに画像の説明を入力

mBufferタブ 2 に渡されるデータは次のようになります。

ここに画像の説明を入力

Motorola Droid 4 では次のようになります。

ここに画像の説明を入力

すべてのデバイスで Android カメラのプレビュー データを取得する正しい方法は何ですか?

編集:の場合processFrame()、OpenCV を使用して RGB に変換し、回転させました。この回答この回答を参照してください。

4

3 に答える 3

8

唯一の問題は、プレビューの幅と高さを設定しなかったことです。

params.setPreviewSize(previewWidth, previewHeight);
mCamera.setParameters(params);

これは、配列に割り当てた高さと幅 (previewWidth * previewHeight に比例) が、返される実際のデータのサイズ (デフォルトのプレビューの幅とプレビューの高さに比例) よりもかなり大きくなる傾向があることを意味していました。一部の携帯電話では、デフォルトが previewWidth および previewHeight と同じサイズであったため、問題はありませんでした。

于 2013-07-19T22:20:16.323 に答える
4

これを試すこともできます

public void takeSnapPhoto() {
camera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        Camera.Parameters parameters = camera.getParameters();
        int format = parameters.getPreviewFormat();
        //YUV formats require more conversion
        if (format == ImageFormat.NV21 || format == ImageFormat.YUY2 || format == ImageFormat.NV16) {
            int w = parameters.getPreviewSize().width;
            int h = parameters.getPreviewSize().height;
            // Get the YuV image
            YuvImage yuv_image = new YuvImage(data, format, w, h, null);
            // Convert YuV to Jpeg
            Rect rect = new Rect(0, 0, w, h);
            ByteArrayOutputStream output_stream = new ByteArrayOutputStream();
            yuv_image.compressToJpeg(rect, 100, output_stream);
            byte[] byt = output_stream.toByteArray();
            FileOutputStream outStream = null;
            try {
                // Write to SD Card
                File file = createFileInSDCard(FOLDER_PATH, "Image_"+System.currentTimeMillis()+".jpg");
                //Uri uriSavedImage = Uri.fromFile(file);
                outStream = new FileOutputStream(file);
                outStream.write(byt);
                outStream.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
            }
        }
    }
});}
于 2014-02-19T11:37:20.337 に答える
2

プレビュー データに大きく依存する Barcode Scanner の作業から、太陽の下ですべてのバグを見たような気がします。私の提案は、単に呼び出さずsetPreviewFormat()にデフォルトを使用することです。幸いなことに、デフォルトはここで必要なものです。への呼び出しをボールアップするデバイスよりも、デフォルトの権利を取得できないデバイスの方が少ないようですsetPreviewFormat()。少なくともそれを試してみてください。

于 2013-07-18T22:41:44.200 に答える