web-dev-qa-db-ja.com

OpenCVカメラの向きの問題

Org.opencv.Android.JavaCameraViewでカメラを表示するだけの簡単なプロジェクトがあります。

私の問題は、デフォルトではカメラが横向きモードになっていて、これを変更できないことです。通常のカメラインテントの代わりにCameraBridgeViewBaseを定義する必要があります。

これは私のコードの一部です:

XMLコード:

        <LinearLayout
            Android:layout_width="match_parent"
            Android:layout_height="match_parent" >

            <org.opencv.Android.JavaCameraView
                Android:layout_width="fill_parent"
                Android:layout_height="300dp"
                Android:visibility="gone"
                Android:id="@+id/HelloOpenCvView"
                opencv:show_fps="true"
                opencv:camera_id="1" />


        </LinearLayout>  

        <LinearLayout
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content" >


            <Button
                Android:id="@+id/BtnVideo"
                Android:layout_marginLeft="2dp"
                Android:layout_marginRight="2dp"                    
                Android:layout_width="0dp"
                style="@style/button"
                Android:layout_height="wrap_content"
                Android:layout_weight="1.00"
                Android:text="@string/videoBtn"
                Android:textSize="18dip" />


        </LinearLayout>   

Javaコード:

 CameraBridgeViewBase mOpenCvCameraView;
    Button VideoButton;
 protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        overridePendingTransition(0, 0);

        VideoButton = (Button) this.findViewById(R.id.BtnVideo);

        VideoButton.setOnClickListener(onClickListener);

        mOpenCvCameraView= (CameraBridgeViewBase) findViewById(R.id.HelloOpenCvView);
        mOpenCvCameraView.setVisibility(SurfaceView.INVISIBLE);

    } 

        private OnClickListener onClickListener = new OnClickListener() {

            @Override
            public void onClick(View v) {
                    switch (v.getId()){

                        case R.id.BtnVideo:
                            if(mOpenCvCameraView.getVisibility() == SurfaceView.VISIBLE)
                            {
                                mOpenCvCameraView.setVisibility(SurfaceView.INVISIBLE);
                            }
                            else
                            {
                                mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
                            }

                            break;
                        default :
                            break;
                    }

            }
        };


        public void onResume() {
            super.onResume();
            overridePendingTransition(0, 0);
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
        }
         public void onPause()
         {
             super.onPause();
             if (mOpenCvCameraView != null)
                 mOpenCvCameraView.disableView();
         }
         public void onDestroy() {
             super.onDestroy();
             if (mOpenCvCameraView != null)
                 mOpenCvCameraView.disableView();
         }
         public void onCameraViewStarted(int width, int height) {
         }

         public void onCameraViewStopped() {
         }
         public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
             return inputFrame.rgba();
         }

        private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
            @Override
            public void onManagerConnected(int status) {
                switch (status) {
                    case LoaderCallbackInterface.SUCCESS:
                    {
                        //Log.i(TAG, "OpenCV loaded successfully");
                        mOpenCvCameraView.enableView();
                    } break;
                    default:
                    {
                        super.onManagerConnected(status);
                    } break;
                }
            }
        };

では、どうすればデフォルトの向きを変更できますか?

ありがとう!

16
user2235615

OK、私はこれを解決策として見つけました:

まず、OpenCVライブラリの_JavaCameraView.Java_クラスに入ります-2.4.5

そしてinitializeCamera()の前のmCamera.startPreview();関数に、次の2つの関数を追加しました:

_            setDisplayOrientation(mCamera, 90);
            mCamera.setPreviewDisplay(getHolder());
_

そしてこのように実装された最初の関数:

_protected void setDisplayOrientation(Camera camera, int angle){
    Method downPolymorphic;
    try
    {
        downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
        if (downPolymorphic != null)
            downPolymorphic.invoke(camera, new Object[] { angle });
    }
    catch (Exception e1)
    {
        e1.printStackTrace();
    }
}
_

OpenCVで作業していることを思い出させます。

これが誰かを助けることを願っています。

33
user2235615

私はOpenCV 3.1を使用していますが、CameraBridgeViewBaseクラスのdeliverAndDrawFrameメソッドでビットマップを描画するときに変換を適用することで修正します。

CameraBridgeViewBase.Javaの場合:

//I added new field
private final Matrix mMatrix = new Matrix();

//added updateMatrix method 
private void updateMatrix() {
    float hw = this.getWidth() / 2.0f;
    float hh = this.getHeight() / 2.0f;
    boolean isFrontCamera = Camera.CameraInfo.CAMERA_FACING_FRONT == mCameraIndex;
    mMatrix.reset();
    if (isFrontCamera) {
        mMatrix.preScale(-1, 1, hw, hh);
    }
    mMatrix.preTranslate(hw, hh);
    if (isFrontCamera)
        mMatrix.preRotate(270);
    else
        mMatrix.preRotate(90);
    mMatrix.preTranslate(-hw, -hh);
}

//then We need call updateMatrix on layout
@Override
public void layout(int l, int t, int r, int b) {
    super.layout(l, t, r, b);
    updateMatrix();
}

//I think we should also call updateMatrix on measure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    updateMatrix();
}


//then We need update deliverAndDrawFrame
protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
    //....Origin Code...

    //Set matrix before OpenCV draw bitmap
    int saveCount = canvas.save();
    canvas.setMatrix(mMatrix);

    //Begin OpenCv Origin source
    if (mScale != 0) {
        canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
             new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2),
             (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2),
             (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
             (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null);
    } else {
         canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
             new Rect((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,
             (canvas.getHeight() - mCacheBitmap.getHeight()) / 2,
             (canvas.getWidth() - mCacheBitmap.getWidth()) / 2 + mCacheBitmap.getWidth(),
             (canvas.getHeight() - mCacheBitmap.getHeight()) / 2 + mCacheBitmap.getHeight()), null);
    }
    //End OpenCv Origin source

    //Restore canvas after draw bitmap
    canvas.restoreToCount(saveCount);

    //....Origin code...
}



//After that We can see that the camera preview is so small, the easiest way is change mScale Value (should we change mScale to "private" instead "protected" ?)
protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
    //....Origin Code...

    //Set matrix before OpenCV draw bitmap to screen
    int saveCount = canvas.save();
    canvas.setMatrix(mMatrix);


    //Change mScale to "Aspect to fill"
    mScale = Math.max((float) canvas.getHeight() / mCacheBitmap.getWidth(), (float) canvas.getWidth() / mCacheBitmap.getHeight());

    //Begin OpenCv Origin source
    if (mScale != 0) {
        canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
             new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2),
             (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2),
             (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
             (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null);
    } else {
         canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
             new Rect((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,
             (canvas.getHeight() - mCacheBitmap.getHeight()) / 2,
             (canvas.getWidth() - mCacheBitmap.getWidth()) / 2 + mCacheBitmap.getWidth(),
             (canvas.getHeight() - mCacheBitmap.getHeight()) / 2 + mCacheBitmap.getHeight()), null);
    }
    //End OpenCv Origin source

    //Restore canvas after draw bitmap
    canvas.restoreToCount(saveCount);

    //....Origin Code...
}

ここで完全なソースコードを取得できます: https://Gist.github.com/thongdoan/d73267eb58863f70c77d1288fe5cd3a4

9
Henry Doan

問題は、ペイントするコードがカメラのパラメーターをチェックしないことです。マットは、「CameraBridgeViewBase」クラスの「deliverAndDrawFrame」関数のサーフェスビューに描画されます。

CameraBridgeViewBaseクラスの非常に簡単な変更により、ビットマップの描画方法を回転させる関数を作成できます。

int userRotation= 0;

public void setUserRotation(int userRotation) {
    this.userRotation = userRotation;
}

/**
 * This method shall be called by the subclasses when they have valid
 * object and want it to be delivered to external client (via callback) and
 * then displayed on the screen.
 * @param frame - the current frame to be delivered
 */
protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
    Mat modified;

    if (mListener != null) {
        modified = mListener.onCameraFrame(frame);
    } else {
        modified = frame.rgba();
    }

    boolean bmpValid = true;
    if (modified != null) {
        try {
            Utils.matToBitmap(modified, mCacheBitmap);
        } catch(Exception e) {
            Log.e(TAG, "Mat type: " + modified);
            Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
            Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
            bmpValid = false;
        }
    }

    if (bmpValid && mCacheBitmap != null) {
        Canvas canvas = getHolder().lockCanvas();
        if (canvas != null) {
            canvas.drawColor(Color.parseColor("#8BC34A"), PorterDuff.Mode.SRC_IN);
 //this is the rotation part
            canvas.save();
            canvas.rotate(userRotation,  (canvas.getWidth()/ 2),(canvas.getHeight()/ 2));

            if (mScale != 0) {
                canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                     new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2),
                     (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2),
                     (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
                     (int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null);
            } else {
                 canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                     new Rect((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,
                     (canvas.getHeight() - mCacheBitmap.getHeight()) / 2,
                     (canvas.getWidth() - mCacheBitmap.getWidth()) / 2 + mCacheBitmap.getWidth(),
                     (canvas.getHeight() - mCacheBitmap.getHeight()) / 2 + mCacheBitmap.getHeight()), null);
            }

            if (mFpsMeter != null) {
                mFpsMeter.measure();
                mFpsMeter.draw(canvas, 20, 30);
            }
//remember to restore the canvas 
            canvas.restore();
            getHolder().unlockCanvasAndPost(canvas);
        }
    }
}

マットを回転させるが、多くのリソースを消費するCore.flip関数を使用する最も一般的なソリューションを試しました。このソリューションは検出に影響せず、パフォーマンスに影響せず、画像の描画方法を変更するだけですキャンバスに。

この助けを願っています。

8

あなたのonCameraFrameでこれを試してください

 mRgba = inputFrame.rgba();
 Mat mRgbaT = mRgba.t();
 Core.flip(mRgba.t(), mRgbaT, 1);
 Imgproc.resize(mRgbaT, mRgbaT, mRgba.size());
 return mRgbaT;
6

まず、基本クラスからインスタンスを作成せず、代わりに拡張クラスからインスタンスを取得します

//CameraBridgeViewBase mOpenCvCameraView;
JavaCameraView mOpenCvCameraView;

CameraBridgeViewBase.Javaにはすでにサーフェスホルダーがあるので、サーフェステクスチャを作成する代わりにそれを使用してみましょう。

したがって、2番目に、表面テクスチャを表面ホルダーに置き換えることにより、関数initializeCamera()内のJavaCameraView.Javaを編集します。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

    //mSurfaceTexture = new SurfaceTexture(MAGIC_TEXTURE_ID);
    //mCamera.setPreviewTexture(mSurfaceTexture);

    mCamera.setPreviewDisplay(getHolder());

} else
    mCamera.setPreviewDisplay(null);

最後のステップは、特別な機能を追加せずに向きを設定することです。同じ関数で、startPreview()がsetDisplayOrientation(degrees)を呼び出す直前のinitializeCamera()

/* Finally we are ready to start the preview */
Log.d(TAG, "startPreview");
mCamera.setDisplayOrientation(90);
mCamera.startPreview();
1
Sameh Yassin