web-dev-qa-db-ja.com

Android LおよびCamera2 APIを使用したカメラプレビュー画像データ処理

私はAndroidカメラからの入力画像を処理してユーザーに表示するAndroid $ ===アプリで作業しています。これはかなり簡単です。カメラでPreviewCallbackを登録しますsetPreviewCallbackWithBufferを持つオブジェクト。これは簡単で、古いカメラAPIでスムーズに動作します

public void onPreviewFrame(byte[] data, Camera cam) {
    // custom image data processing
}

新しいCamera2 APIを活用するためにアプリを移植しようとしていますが、それをどのように行うべきかわかりません。私は、ビデオを記録できるLプレビューサンプルのCamera2Videoに従いました。ただし、サンプルには画像データの直接転送がないため、画像ピクセルデータをどこで正確に取得し、どのように処理するかはわかりません。

誰かが私を助けたり、PreviewCallback in Android Lの機能を取得する方法、または表示する前にカメラからのプレビューデータを処理する方法を教えてください画面?(カメラオブジェクトにはプレビューコールバックはありません)

ありがとうございました!

39
bubo

Camera2 AP​​Iは現在のCamera AP​​Iとは非常に異なるため、ドキュメントを読むと役立つ場合があります。

適切な出発点は、camera2basicの例です。 Camera2 AP​​Iを使用し、ImageReaderを構成してJPEGイメージを取得し、ImageReader.OnImageAvailableListenerを登録してそれらのイメージを受信する方法を示します。

プレビューフレームを受け取るには、ImageReaderの表面をsetRepeatingRequestCaptureRequest.Builderに追加する必要があります。

また、ImageReaderの形式をYUV_420_888に設定する必要があります。これにより、8MPで30fpsが提供されます(Nexus 5では、8MPで30fpsが保証されます)。

27
VP.

@VPの答えは技術的に明確ですが、CameraからCamera2に初めて移動する場合は理解するのが難しいため、いくつかの答えをより消化しやすいものに組み合わせます。

https://github.com/googlesamples/Android-Camera2Basic を開始点として使用して、以下を変更します。

createCameraPreviewSession()Surfaceから新しいmImageReaderを初期化します

_Surface mImageSurface = mImageReader.getSurface();
_

_CaptureRequest.Builder_変数の出力ターゲットとしてその新しいサーフェスを追加します。 Camera2Basicサンプルを使用すると、変数はmPreviewRequestBuilderになります

_mPreviewRequestBuilder.addTarget(mImageSurface);
_

新しい行を含むスニペットを次に示します(@AngeloSのコメントを参照)。

_private void createCameraPreviewSession() {

    try {

        SurfaceTexture texture = mTextureView.getSurfaceTexture();
        assert texture != null;

        // We configure the size of default buffer to be the size of camera preview we want.
        texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());

        // This is the output Surface we need to start preview.
        Surface surface = new Surface(texture);

        //@AngeloS - Our new output surface for preview frame data
        Surface mImageSurface = mImageReader.getSurface();

        // We set up a CaptureRequest.Builder with the output Surface.
        mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

        //@AngeloS - Add the new target to our CaptureRequest.Builder
        mPreviewRequestBuilder.addTarget(mImageSurface);

        mPreviewRequestBuilder.addTarget(surface);

        ...
_

次に、setUpCameraOutputs()で、ImageReaderを初期化するときに、形式を_ImageFormat.JPEG_から_ImageFormat.YUV_420_888_に変更します。 (PS、よりスムーズな操作のためにプレビューサイズをドロップすることもお勧めします-Camera2の優れた機能の1つ)

_mImageReader = ImageReader.newInstance(largest.getWidth() / 16, largest.getHeight() / 16, ImageFormat.YUV_420_888, 2);
_

最後に、_ImageReader.OnImageAvailableListener_のonImageAvailable()メソッドでは、@ Kamalaの提案を使用してください。プレビューを閉じると、数フレーム後にプレビューが停止するためです。

_    @Override
    public void onImageAvailable(ImageReader reader) {

        Log.d(TAG, "I'm an image frame!");

        Image image =  reader.acquireNextImage();

        ...

        if (image != null)
            image.close();
    }
_
23
AngeloS

ImageReader.OnImageAvailableListenerクラスで、以下に示すように読み取り後に画像を閉じます(これにより、次のキャプチャのためにバッファが解放されます)。終了時に例外を処理する必要があります

      Image image =  imageReader.acquireNextImage();
      ByteBuffer buffer = image.getPlanes()[0].getBuffer();
      byte[] bytes = new byte[buffer.remaining()];
      buffer.get(bytes);
      image.close();
14
Kamala

同じものが必要だったので、例を使用して、カメラがプレビュー状態のときに新しい関数への呼び出しを追加しました。

_private CameraCaptureSession.CaptureCallback mCaptureCallback
            = new CameraCaptureSession.CaptureCallback()
    private void process(CaptureResult result) {
        switch (mState) {
            case STATE_PREVIEW: {
                    if (buttonPressed){
                        savePreviewShot();
                    }
                break;
            }
_

savePreviewShot()は、プレビューテンプレートを使用するように適合された元のcaptureStillPicture()の単純なリサイクルバージョンです。

_   private void savePreviewShot(){
        try {
            final Activity activity = getActivity();
            if (null == activity || null == mCameraDevice) {
                return;
            }
            // This is the CaptureRequest.Builder that we use to take a picture.
            final CaptureRequest.Builder captureBuilder =
                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            captureBuilder.addTarget(mImageReader.getSurface());

            // Orientation
            int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));

            CameraCaptureSession.CaptureCallback CaptureCallback
                    = new CameraCaptureSession.CaptureCallback() {

                @Override
                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
                                               TotalCaptureResult result) {
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss:SSS");
                    Date resultdate = new Date(System.currentTimeMillis());
                    String mFileName = sdf.format(resultdate);
                    mFile = new File(getActivity().getExternalFilesDir(null), "pic "+mFileName+" preview.jpg");

                    Log.i("Saved file", ""+mFile.toString());
                    unlockFocus();
                }
            };

            mCaptureSession.stopRepeating();
            mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    };
_
8
panonski

ImageReaderを初期化して、最大画像バッファが_2_である場合、reader.acquireLatestImage()内でonImageAvailable()を使用することをお勧めします。

acquireLatestImage()はImageReaderのキューから最新の画像を取得し、古い画像を削除するためです。この関数は、リアルタイム処理により適しているため、ほとんどのユースケースでacquireNextImage()を使用することをお勧めします。最大画像バッファは少なくとも_2_でなければなりません。

そして、処理後に画像をclose()することを忘れないでください。

2
nhoxbypass