简体   繁体   中英

OpenCV camera orientation issue

I have a simple project that just show the camera with org.opencv.android.JavaCameraView.

my problem is that in default the camera is on landscape mode and I can't change this cause I need to define CameraBridgeViewBase instead of a regular camera intent.

this is a part of my code:

XML code:

        <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 Code :

 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;
                }
            }
        };

So how can I change the default orientation?

Thanks!

Ok, I found this as a solution:

First I get into JavaCameraView.java class in the OpenCV Library - 2.4.5

and then in initializeCamera() function before mCamera.startPreview(); I added these 2 function:

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

and the first function implemented like this:

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();
    }
}

I just reminding that I work with OpenCV.

Hope this help someone.

I am using OpenCV 3.1, I fix it by apply transform when draw bitmap on deliverAndDrawFrame method of CameraBridgeViewBase class, Hope it helpful:

On 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...
}

You can get full source code here: https://gist.github.com/thongdoan/d73267eb58863f70c77d1288fe5cd3a4

The problem is that the code that paints doesn't check the camera params. The Mat is drawn on the Surface view in the function "deliverAndDrawFrame" in the class "CameraBridgeViewBase".

With a very simple modification in the CameraBridgeViewBase class, we can make a function that rotates the way the bitmap is drawn.

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);
        }
    }
}

I tried the most common solution that use the Core.flip function that rotates the Mat but consumes a lot of resources, this solution doesn't affect to the detection and doesn't affect to the performance, only changes the way the image is drawn on the canvas.

Hope this help.

Try this one on your onCameraFrame

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

Firstly, do not create an instance from the base class, instead get the instance from the extended class

//CameraBridgeViewBase mOpenCvCameraView;
JavaCameraView mOpenCvCameraView;

It is worth mentioning that the CameraBridgeViewBase.java has already a surface holder, so let's use it instead of creating a surface texture.

Hence secondly, edit the JavaCameraView.java inside the function initializeCamera() by replacing the surface texture with a surface holder

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);

The last step is to set the orientation WITHOUT adding any special functions. In the same function initializeCamera() just before startPreview() call setDisplayOrientation(degrees)

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

In OpenCV version 3.4.3

I fixed it by doing below changes in initializeCamera() method in JavaCameraView.java class.

setDisplayOrientation(mCamera, 0);

Steps to follow:

  1. Go to the JavaCameraView.java file.
  2. Search for "setDisplayOrientation"
  3. Set orientation angle to 0 (By default it was 90).

the android:screenOrientation value in the AndroidManifest.xml should help.

android:screenOrientation="portrait"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM