将相机预览旋转到Portrait Android OpenCV相机

Jam*_*meo 36 android opencv android-camera

我正在尝试使用OpenCV 2.4.3.2创建一个相机应用程序并进行一些opencv处理.我希望它能够有多个UI方向,而不仅仅是Landscape.

问题是,当我将方向更改为纵向时,图像会侧向出现.

我知道我可以在进行图像处理之前旋转输入图像(因此只将方向保留为横向),这很好并且有效,但是没有解决我的其他UI将处于错误方向的问题.

我也尝试使用这段代码来旋转相机90deg,但它似乎没有用.

mCamera.setDisplayOrientation(90);
Run Code Online (Sandbox Code Playgroud)

它要么没有效果,要么有时只会导致预览变黑

有人用OpenCV成功完成了这项工作吗?我的类扩展自JavaCameraView. 侧面预览的肖像图像

编辑

我做了一些改进,就是我在OpenCV中旋转了图像,因为它在CameraBridgeViewBase.java类中显示.

在交付和绘制框架方法中:

if (canvas != null) {
            canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
            //canvas.drawBitmap(mCacheBitmap, (canvas.getWidth() - mCacheBitmap.getWidth()) / 2, (canvas.getHeight() - mCacheBitmap.getHeight()) / 2, null);
            //Change to support portrait view
            Matrix matrix = new Matrix();
            matrix.preTranslate((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,(canvas.getHeight() - mCacheBitmap.getHeight()) / 2);

            if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
                matrix.postRotate(90f,(canvas.getWidth()) / 2,(canvas.getHeight()) / 2);
            canvas.drawBitmap(mCacheBitmap, matrix, new Paint());
Run Code Online (Sandbox Code Playgroud)

...基本上,这只是像输入图像一样

输入图像旋转90

这样更好,但我显然希望这是全屏.

小智 12

我在尝试实现OpenCV时遇到了同样的问题.我能够通过对deliverAndDrawFrame方法进行以下更改来修复它.

  1. 旋转画布对象

    Canvas canvas = getHolder().lockCanvas();
    // Rotate canvas to 90 degrees
    canvas.rotate(90f, canvas.getWidth()/2, canvas.getHeight()/2);
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在绘制之前调整位图大小以适合整个画布大小

    // Resize
    Bitmap bitmap = Bitmap.createScaledBitmap(mCacheBitmap, canvas.getHeight(), canvas.getWidth(), true);
    // Use bitmap instead of mCacheBitmap
    canvas.drawBitmap(bitmap, new Rect(0,0,bitmap.getWidth(), bitmap.getHeight()), new Rect(
        (int)((canvas.getWidth() - mScale*bitmap.getWidth()) / 2),
        (int)((canvas.getHeight() - mScale*bitmap.getHeight()) / 2),
        (int)((canvas.getWidth() - mScale*bitmap.getWidth()) / 2 + mScale*bitmap.getWidth()),
        (int?((canvas.getHeight() - mScale*bitmap.getHeight()) / 2 + mScale*bitmap.getHeight()
      )), null);
    
    // Unlock canvas
    getHolder().unlockCanvasAndPost(canvas);
    
    Run Code Online (Sandbox Code Playgroud)


小智 10

实际上,你可以只做宽度或高度数学父(全屏).

if (canvas != null) {
        Bitmap bitmap = Bitmap.createScaledBitmap(mCacheBitmap, canvas.getHeight(), canvas.getWidth(), true);
        canvas.rotate(90,0,0);
        float scale = canvas.getWidth() / (float)bitmap.getHeight();
        float scale2 = canvas.getHeight() / (float)bitmap.getWidth();
        if(scale2 > scale){
            scale = scale2;
        }
        if (scale != 0) {
            canvas.scale(scale, scale,0,0);
        }
        canvas.drawBitmap(bitmap, 0, -bitmap.getHeight(), null);
Run Code Online (Sandbox Code Playgroud)

...

此外,您可以使预览大小大于屏幕.只需修改比例.


jai*_*ogi 9

我修改了CameraBridgeViewBase.java,如下所示:

protected Size calculateCameraFrameSize(List<?> supportedSizes, ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) {
    int calcWidth = 0;
    int calcHeight = 0;

    if(surfaceHeight > surfaceWidth){
        int temp = surfaceHeight;
        surfaceHeight = surfaceWidth;
        surfaceWidth = temp;
    }
Run Code Online (Sandbox Code Playgroud)

在函数"deliverAndDrawFrame"中:

            if (mScale != 0) {
                if(canvas.getWidth() > canvas.getHeight()) {
                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, rotateMe(canvas, mCacheBitmap), null);
                }
Run Code Online (Sandbox Code Playgroud)

其中rotateMe的定义如下:

private Matrix rotateMe(Canvas canvas, Bitmap bm) {
    // TODO Auto-generated method stub
    Matrix mtx=new Matrix();
    float scale = (float) canvas.getWidth() / (float) bm.getHeight();
    mtx.preTranslate((canvas.getWidth() - bm.getWidth())/2, (canvas.getHeight() - bm.getHeight())/2);
    mtx.postRotate(90,canvas.getWidth()/2, canvas.getHeight()/2);
    mtx.postScale(scale, scale, canvas.getWidth()/2 , canvas.getHeight()/2 );
    return mtx;
}
Run Code Online (Sandbox Code Playgroud)

预览FPS较慢,因为与横向模式相比,计算开销较小.

  • 这工作得很整齐.然而,我正在使用的面部检测示例则不再识别任何面部.它假设景观模式.有什么想法吗?谢谢 (3认同)

Ros*_*ses 8

与其他答案一样,我编写了我个人版本的DeliverAndDrawFrame(我还通过注释通知了我的代码开始和结束的位置):

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(0, android.graphics.PorterDuff.Mode.CLEAR);
            if (BuildConfig.DEBUG) {
                Log.d(TAG, "mStretch value: " + mScale);
            }

            // Start of the fix
            Matrix matrix = new Matrix();
            matrix.preTranslate( ( canvas.getWidth() - mCacheBitmap.getWidth() ) / 2f, ( canvas.getHeight() - mCacheBitmap.getHeight() ) / 2f );
            matrix.postRotate( 90f, ( canvas.getWidth()) / 2f, canvas.getHeight() / 2f );
            float scale = (float) canvas.getWidth() / (float) mCacheBitmap.getHeight();
            matrix.postScale(scale, scale, canvas.getWidth() / 2f , canvas.getHeight() / 2f );
            canvas.drawBitmap( mCacheBitmap, matrix, null );

            // Back to original OpenCV code
            if (mFpsMeter != null) {
                mFpsMeter.measure();
                mFpsMeter.draw(canvas, 20, 30);
            }

            getHolder().unlockCanvasAndPost(canvas);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

预览现在处于纵向模式,如您所见:

人像预览


小智 5

不幸的是,Opencv4Android不支持肖像相机.但是有一种方法可以克服它.1)编写自定义相机并将其方向设置为纵向.2)注册它的预览回调.3)在onPreviewFrame(byte[]data, Camera camera)创建Mat预览字节时:

Mat mat = new Mat(previewSize.height, previewSize.width, CvType.CV_8UC1);
mat.put(0, 0, data);

Core.transpose(mat, mat);
Core.flip(mat, mat, -1); // rotates Mat to portrait
Run Code Online (Sandbox Code Playgroud)

CvType 取决于相机使用的预览格式.

PS.不要忘记在完成后释放你创建的所有Mat实例.

PPS.最好在单独的线程上管理你的摄像头,以便在进行一些检测时不会超载UI线程.


1''*_*1'' 1

新的 OpenCV 类似乎CameraBridgeViewBase.java太高级了,并且没有对相机预览的布局提供足够的控制。看看我的示例代码,它基于一些较旧的 OpenCV 示例并使用纯 Android 代码。要使用传入的字节数组onPreviewFrameput()请将其转换为 Mat 并从 YUV 转换为 RGB:

mYuv = new Mat(previewHeight + previewHeight/2, previewWidth, CvType.CV_8UC1);
mYuv.put(0, 0, mBuffer);
Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420sp2RGBA, 4);
Run Code Online (Sandbox Code Playgroud)

您也许可以在互联网上找到旧的 OpenCV4Android 示例,尽管它们在几个版本前已被删除。但是,链接的示例代码和上面的代码片段应该足以帮助您入门。