9

OpenCVのAndroidカメラのサンプルコードに戸惑っています。SurfaceHolder.Callback彼らは、メソッド内に次の行を実装して配置するカスタムクラスを作成しsurfaceChangedます。

mCamera.setPreviewDisplay(null);

のAndroidドキュメントのsetPreviewDisplay説明:

このメソッドは、startPreview()の前に呼び出す必要があります。唯一の例外は、startPreview()が呼び出される前にプレビューサーフェスが設定されていない(またはnullに設定されていない)場合、このメソッドをnull以外のパラメーターで1回呼び出して、プレビューサーフェスを設定できることです。(これにより、カメラのセットアップとサーフェスの作成を並行して実行できるため、時間を節約できます。)プレビューの実行中は、プレビューサーフェスが変更されない場合があります。

異常なことに、OpenCVのコードはsetPreviewDisplaynull以外のSurfaceHolderで呼び出すことはありません。正常に動作しますが、を使用して画像の回転を変更しても動作しsetDisplayOrientationません。この行がなくても同じ結果が得られるため、この行も何も実行していないように見えます。

の代わりにsetPreviewDisplaySurfaceHolderを指定して呼び出すと、画像は回転しますが、画像処理の結果は含まれません。後で電話するときにも受け取ります。surfaceChangednullIllegalArgumentExceptionlockCanvas

どうしたの?

これは、コードの(おそらく)最も関連性の高い部分であり、わずかに簡略化され、メソッドがインライン化されています。これがフルバージョンです。

クラス定義

public abstract class SampleViewBase extends SurfaceView 
    implements SurfaceHolder.Callback, Runnable {

カメラを開いたとき

mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() {
    public void onPreviewFrame(byte[] data, Camera camera) {
        synchronized (SampleViewBase.this) {
            System.arraycopy(data, 0, mFrame, 0, data.length);
            SampleViewBase.this.notify(); 
        }
        camera.addCallbackBuffer(mBuffer);
    }
});

表面が変化したとき

/* Now allocate the buffer */
mBuffer = new byte[size];
/* The buffer where the current frame will be copied */
mFrame = new byte [size];
mCamera.addCallbackBuffer(mBuffer);

try {
    mCamera.setPreviewDisplay(null);
} catch (IOException e) {
    Log.e(TAG, "mCamera.setPreviewDisplay/setPreviewTexture fails: " + e);
}

[...]

/* Now we can start a preview */
mCamera.startPreview();

runメソッド

public void run() {
    mThreadRun = true;
    Log.i(TAG, "Starting processing thread");
    while (mThreadRun) {
        Bitmap bmp = null;

        synchronized (this) {
            try {
                this.wait();
                bmp = processFrame(mFrame);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        if (bmp != null) {
            Canvas canvas = mHolder.lockCanvas();
            if (canvas != null) {
                canvas.drawBitmap(bmp, (canvas.getWidth() - getFrameWidth()) / 2, 
                    (canvas.getHeight() - getFrameHeight()) / 2, null);
                mHolder.unlockCanvasAndPost(canvas);
            }
        }
    }
    Log.i(TAG, "Finishing processing thread");
}
4

2 に答える 2

6

私はこの同じ問題に遭遇しました。を使用する代わりにSurfaceView.Callback、クラスをサブクラス化しましたJavaCameraView。ここで私のライブ顔検出と描画サンプルを参照してください。処理の前に、デバイスの向きに従ってカメラから出てくるマトリックスを回転させるのは簡単でした。リンクされたコードの関連する抜粋:

@Override
    public Mat onCameraFrame(Mat inputFrame) {
        int flipFlags = 1;
        if(display.getRotation() == Surface.ROTATION_270) {
            flipFlags = -1;
            Log.i(VIEW_LOG_TAG, "Orientation is" + getRotation());
        }
        Core.flip(inputFrame, mRgba, flipFlags);
        inputFrame.release();
    Imgproc.cvtColor(mRgba, mGray, Imgproc.COLOR_RGBA2GRAY);
        if (mAbsoluteFaceSize == 0) {
            int height = mGray.rows();
            if (Math.round(height * mRelativeFaceSize) > 0) {
                mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize);
            }
        }
    }
于 2013-03-26T21:19:15.887 に答える
3

OpenCV 自体を使用して回転の問題を解決しました。このコードを使用して画面の回転を修正する必要があることを確認した後、生のカメラ画像に回転行列を適用します (YUV から RGB に変換した後)。

Point center = new Point(mFrameWidth/2, mFrameHeight/2);
Mat rotationMatrix = Imgproc.getRotationMatrix2D(center, totalRotation, 1);

[...]

Imgproc.cvtColor(mYuv, mIntermediate, Imgproc.COLOR_YUV420sp2RGBA, 4);
Imgproc.warpAffine(mIntermediate, mRgba, rotationMatrix, 
    new Size(mFrameHeight, mFrameWidth));

別の問題は、setPreviewDisplay(null)一部の電話で空白の画面が表示されることです。hereから取得し、このバグレポートとこの SO の質問を利用したソリューションは表示の「偽の」 SurfaceViewをプレビュー表示に渡して開始させますが、実際にはオーバーレイされたカスタムビューに出力を表示します。カメラビュー。setContentView()したがって、アクティビティのを呼び出した後、次のonCreate()コードを挿入します。

if (VERSION.SDK_INT < VERSION_CODES.HONEYCOMB) {
    final SurfaceView fakeView = new SurfaceView(this);
    fakeView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
    fakeView.setZOrderMediaOverlay(false);

    final CameraView cameraView = (CameraView) this.findViewById(R.id.cameraview); 
    cameraView.setZOrderMediaOverlay(true); 
    cameraView.fakeView = fakeView;
}

次に、プレビュー表示を設定するときに、次のコードを使用します。

try {
    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB)
        mCamera.setPreviewTexture(new SurfaceTexture(10));
    else
        mCamera.setPreviewDisplay(fakeView.getHolder());
} catch (IOException e) {
    Log.e(TAG, "mCamera.setPreviewDisplay fails: "+ e);
}

Honeycomb 以降のみを開発している場合は、setPreviewDisplay(null) を置き換えて完了mCamera.setPreviewTexture(new SurfaceTexture(10));です。 setDisplayOrientation()ただし、これを行ってもまだ機能しないため、回転行列ソリューションを使用する必要があります。

于 2013-03-28T02:12:05.790 に答える