【问题标题】:OpenCV camera orientation issueOpenCV 相机方向问题
【发布时间】:2013-05-16 04:58:01
【问题描述】:

我有一个简单的项目,它只使用 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;
                }
            }
        };

那么如何更改默认方向?

谢谢!

【问题讨论】:

    标签: android opencv android-orientation


    【解决方案1】:

    好的,我发现这是一个解决方案:

    首先我进入 OpenCV 库 - 2.4.5

    中的 JavaCameraView.java

    然后在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。

    希望这对某人有所帮助。

    【讨论】:

    • 使用您的代码后,它工作得非常好。但是,在我实际的 android 程序中,我在 onCameraFrame 中有一些代码来修改相机的 RGBA 输出。显示在屏幕上不知何故保持不变。你知道原因吗?我尝试保存修改后的MAT,在保存的文件中看起来很好,但只是屏幕上的预览是错误的。
    • 我找到了另一个解决方案,您可以在这里看到:answers.opencv.org/question/20325/…
    • 您可以使用适用于每个 OpenCV 版本的我的解决方案:answers.opencv.org/question/20325/…
    • 提示您的手机似乎不支持摄像头或被屏蔽!
    • @user2235615 您的代码正在运行,但问题是框架在纵向时不是全屏并且看起来过度拉伸(更宽),或者它在横向时不起作用是否还有其他解决方案
    【解决方案2】:

    AndroidManifest.xml 中的 android:screenOrientation 值应该会有所帮助。

    android:screenOrientation="portrait"

    【讨论】:

    • 他询问的是相机的方向,而不是屏幕。
    • 我也有同样的问题,相机曾经处于纵向模式。我必须手动更改方向才能正确设置
    【解决方案3】:

    我使用的是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

    【讨论】:

    • 您好,感谢您的贡献。我认为与其粘贴整个课程,不如仅指出您所做的更改会更有用。
    • 对不起大家回复晚了,我编辑了我的答案
    • 这行得通,但不能缩放相机以适合我的屏幕。
    • @Jesús - 已经有一段时间了 - 我想出了一个对我有用的解决方案,并在这里写了一个小教程:heartbeat.fritz.ai/…
    【解决方案4】:

    问题在于绘制的代码没有检查相机参数。 Mat 在“CameraBridgeViewBase”类的函数“deliverAndDrawFrame”中绘制在 Surface 视图上。

    通过对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 函数旋转 Mat 但消耗大量资源,该解决方案不影响检测也不影响性能,只是改变了方式图像被绘制在画布上。

    希望对您有所帮助。

    【讨论】:

    • 这对我很有用,但不会考虑旋转后需要调整画布大小
    【解决方案5】:

    在你的 onCameraFrame 上试试这个

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

    【讨论】:

    • 这可行,但它会在几秒钟后关闭应用程序。不知道为什么,在控制台中看不到任何致命错误。
    • 应用程序崩溃可能与内存泄漏有关。必须在返回之前释放 mRgba。另外,创建 mRgbaT 作为全局变量解决了这个问题。
    【解决方案6】:

    首先,不要从基类创建实例,而是从扩展类中获取实例

    //CameraBridgeViewBase mOpenCvCameraView;
    JavaCameraView mOpenCvCameraView;
    

    值得一提的是,CameraBridgeViewBase.java 已经有一个表面保持器,所以让我们使用它来代替创建表面纹理。

    因此,其次,编辑函数 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();
    

    【讨论】:

    • 这可行,但它会阻止对象检测器工作。
    【解决方案7】:

    在 OpenCV 3.4.3 版中

    我通过对 JavaCameraView.java 类中的 initializeCamera() 方法进行以下更改来修复它。

    setDisplayOrientation(mCamera, 0);
    

    要遵循的步骤:

    1. 转到 JavaCameraView.java 文件。
    2. 搜索“setDisplayOrientation”
    3. 将方向角设置为 0(默认为 90)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-23
      相关资源
      最近更新 更多