OpenCV相机定位问题

时间:2013-05-21 12:15:05

标签: android opencv android-orientation

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

那么我该如何更改默认方向?

谢谢!

7 个答案:

答案 0 :(得分:31)

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

首先我进入 OpenCV库中的JavaCameraView.java课程 - 2.4.5

然后在initializeCamera()函数mCamera.startPreview();之前添加了这两个函数:

            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。

希望这有助于某人。

答案 1 :(得分:9)

我正在使用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

答案 2 :(得分:7)

问题是绘制的代码不会检查相机参数。 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但消耗了大量资源,这个解决方案不影响检测并且不影响性能,只改变了方式图像在画布上绘制。

希望得到这个帮助。

答案 3 :(得分:6)

在你的onCameraFrame上尝试这个

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

答案 4 :(得分:1)

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

var player = new Vimeo.Player('player', { id:76979871, muted: true });
player.loadVideo(309565369).then(function(id) {
    player.setCurrentTime(30).then(function(seconds) {
        player.play().then(function() {
            console.log('the video was played');
        }).catch(function(error) {
            console.log(error);
        });
    }).catch(function(error) {
        console.log(error);
    });
}).catch(function(error) {
    console.log(error);
});

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

然后,通过使用表面固定器替换表面纹理,在函数initializeCamera()中编辑JavaCameraView.java

//CameraBridgeViewBase mOpenCvCameraView;
JavaCameraView mOpenCvCameraView;

最后一步是在不添加任何特殊功能的情况下设置方向。在相同的函数中,在startPreview()之前,调用setDisplayOrientation(degrees)

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

答案 5 :(得分:0)

在OpenCV版本3.4.3中

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

setDisplayOrientation(mCamera, 0);

要遵循的步骤:

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

答案 6 :(得分:-8)

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

  

机器人:screenOrientation =&#34;肖像&#34;