将Android OpenCV相机预览旋转为竖屏

43

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

问题在于,当我将方向更改为纵向时,图像会侧着出现。

我知道 我可以在处理图像之前旋转输入图像(因此仅保留横向方向),这很好并且有效,但并不能解决我的UI其余部分将处于错误方向的问题。

我还尝试使用 这个代码 将相机旋转90度,但似乎并没有起作用。

mCamera.setDisplayOrientation(90);

它要么没有效果,要么只是导致预览被黑掉。

有人用OpenCV成功地做到了这一点吗?我的类继承自JavaCameraView。 portrait image with sideways preview

编辑

我已经做出了改进,即在显示在CameraBridgeViewBase.java类中的图像内旋转了它。

在deliver和draw frame方法中:

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());
基本上,这只是将输入图像旋转如下

输入图像旋转90度

这已经更好了,但我显然希望它能够全屏显示。


我刚刚添加了一张图片来解释这个行为,之前我没有解释清楚。实际上,我正在将方向设置为纵向。 - Jameo
哪个样本?我想我已经尝试了所有的样本,它们似乎都是相同的(而且错误的)。 - Jameo
@Jameo 我目前遇到了同样的困难。我也是openCv的新手。我有一个想法(我不知道它是否有意义),但您觉得如果我们使用Android Camera API,例如surface view等,能否将摄像头整洁地显示出来(正确方向、全屏或按需求)并作为后端相机逻辑使用openCv处理数据。唯一的障碍可能是可能需要打开两个摄像头。我对这个想法还不太确定。欢迎评论。 - test
我也考虑过这个问题。问题是,我花了几天时间查看源代码,试图弄清楚CameraBridgeViewBase.java实际上是做什么的,但我仍然不太理解帧是如何转换的。我仍然没有一个好的解决方案,我的主要关注点是为什么setOrienation不起作用? - Jameo
不,这个问题仍然困扰着我。我最终只是将应用锁定为横向模式,并手动旋转任何用户界面元素。非常麻烦。但当时,来自openCV的人们正在谈论解决它,所以如果现在还没有解决,我会感到惊讶。 - Jameo
显示剩余10条评论
20个回答

1
如果你正在使用 openCV 2.4.9,请尝试以下操作: 1)将 opencv 教程-混合处理的内容复制到你的代码中; 2)纠正不匹配的错误(活动名称和可能的布局引用); 3)通过添加 android:screenOrientation ="landscape" 修改你的清单; 4)纠正较小的错误并运行!bbaamm(现在应该可以正常工作了)
注意:使用此方法时,当手机处于竖直位置时,状态栏会出现在右侧。 由于我们正在开发相机项目,建议从预览中删除状态栏。
希望对你有所帮助!

0

我使用CameraBridgeViewBase实现了竖屏方向,但是我不得不修改OpenCV中的JavaCameraView.java :( 实现思路如下:在相机初始化之后,执行以下操作

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

以及 setDisplayOrientation 方法

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

你好,我也遇到了方向问题。你能详细描述一下你的解决方案吗?我尝试添加了以下代码(JavaCamerView):setDisplayOrientation(mCamera, 90); mCamera.setPreviewDisplay(getHolder());在if (!initializeCamera(width, height)) return false;之后。然而,这导致预览屏幕变黑。 - dasAnderl ausMinga
3
这种方法确实为我纠正了方向,但是预览回调停止工作,并且还会持续抛出以下异常- - Gaurav Raj
1
E/SurfaceHolder: 锁定表面时出现异常 java.lang.IllegalArgumentException - Gaurav Raj

0

我认为没有办法在不进行像素操作的情况下实现这一点。但是,如果我们只是修改了所有这些像素被绘制到其中的矩阵。答案部分地在于CameraBridgeViewBase.java文件中。

1. 转到CameraBridgeViewBase类

2. 创建函数更新矩阵

private final Matrix mMatrix = new Matrix();
private void updateMatrix() {
float mw = this.getWidth();
float mh = this.getHeight();

float hw = this.getWidth() / 2.0f;
float hh = this.getHeight() / 2.0f;

float cw  = (float)Resources.getSystem().getDisplayMetrics().widthPixels; //Make sure to import Resources package
float ch  = (float)Resources.getSystem().getDisplayMetrics().heightPixels;

float scale = cw / (float)mh;
float scale2 = ch / (float)mw;
if(scale2 > scale){
    scale = scale2;
}

boolean isFrontCamera = mCameraIndex == CAMERA_ID_FRONT;

mMatrix.reset();
if (isFrontCamera) {
    mMatrix.preScale(-1, 1, hw, hh); //MH - this will mirror the camera
}
mMatrix.preTranslate(hw, hh);
if (isFrontCamera){
    mMatrix.preRotate(270);
} else {
    mMatrix.preRotate(90);
}
mMatrix.preTranslate(-hw, -hh);
mMatrix.preScale(scale,scale,hw,hh);
}

3. 重写 onMeasure 和 layout 函数

@Override
public void layout(int l, int t, int r, int b) {
  super.layout(l, t, r, b);
  updateMatrix();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  updateMatrix();
}

4. 替换现有的deliverAndDrawFrame函数

protected void deliverAndDrawFrame(CvCameraViewFrame frame) { //replaces existing deliverAndDrawFrame
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);
        int saveCount = canvas.save();
        canvas.setMatrix(mMatrix);

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

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

        if (mFpsMeter != null) {
            mFpsMeter.measure();
            mFpsMeter.draw(canvas, 20, 30);
        }
        getHolder().unlockCanvasAndPost(canvas);
    }
}
}

0
也许这能帮到某些人。在安卓9上测试过,使用了OpenCV343。现在可以在纵向和横向模式下全屏检测人脸。只需要在CameraBridgeViewBase类中进行一些小更改即可。
private final Matrix matrix = new Matrix();

......修改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) {
        int currentOrientation = getResources().getConfiguration().orientation;
        if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
            Canvas canvas = getHolder().lockCanvas();
            if (canvas != null) {
                canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
                if (BuildConfig.DEBUG)
                    Log.d(TAG, "mStretch value: " + mScale);

                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);
                }
                getHolder().unlockCanvasAndPost(canvas);
            }
        } else {
            Canvas canvas = getHolder().lockCanvas();
            if (canvas != null) {
                int saveCount = canvas.save();
                canvas.setMatrix(matrix);
                mScale = Math.max((float) canvas.getHeight() / mCacheBitmap.getWidth(), (float) canvas.getWidth() / mCacheBitmap.getHeight());
                canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);

                if (mScale != 0) {
                    canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
                            new Rect((int)((canvas.getWidth() - mCacheBitmap.getWidth()) - mCacheBitmap.getWidth())/2,
                                    (int)(canvas.getHeight() - mScale*mCacheBitmap.getHeight() - mScale*mCacheBitmap.getHeight()/2),
                                    (int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
                                    (int)((canvas.getHeight() - 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);
                }
                canvas.restoreToCount(saveCount);

                if (mFpsMeter != null) {
                    mFpsMeter.measure();
                    mFpsMeter.draw(canvas, 20, 30);
                }
                getHolder().unlockCanvasAndPost(canvas);
            }
        }
    }
}

在MainActivity中:

public Mat rotateMat(Mat matImage) {
    Mat rotated = matImage.t();
    Core.flip(rotated, rotated, 1);
    return rotated;
}

@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
    MatOfRect faces = new MatOfRect();
    int currentOrientation = getResources().getConfiguration().orientation;
    if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
        mRgba = inputFrame.rgba();
        mGray = inputFrame.gray();
        int height = mGray.rows();
        if (Math.round(height * 0.2) > 0) {
            mFaceSize = (int) Math.round(height * 0.2);
        }

        cascadeClassifier.detectMultiScale(mGray, faces, 1.1, 3, 2,
                new Size(mFaceSize, mFaceSize));
        Rect[] facesArray = faces.toArray();
        for (int i = 0; i < facesArray.length; i++) {
            rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
        }
    } else {

        mRgba = inputFrame.rgba();
        mGray = rotateMat(inputFrame.gray());

        if (mFaceSize == 0) {
            int height = mGray.cols();
            if (Math.round(height * 0.2) > 0) {
                mFaceSize = (int) Math.round(height * 0.2);
            }
        }
        Mat newMat = rotateMat(mRgba);
        if(!isBackCameraOn){
            flip(newMat, newMat, -1);
            flip(mGray, mGray, -1);
        }
        if (cascadeClassifier != null)
            cascadeClassifier.detectMultiScale(mGray, faces, 1.1, 3, 2, new Size(mFaceSize, mFaceSize));
        mGray.release();

        Rect[] facesArray = faces.toArray();
        for (int i = 0; i < facesArray.length; i++) {
            rectangle(newMat, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
        }

        Imgproc.resize(newMat, mRgba, new Size(mRgba.width(), mRgba.height()));

        newMat.release();
    }

    if(!isBackCameraOn){
        flip(mRgba, mRgba, 1);
        flip(mGray, mGray, 1);
    }


    return mRgba;
}

0

另一种解决方案。我认为这个更好。

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

            int currentOrientation = getResources().getConfiguration().orientation;
            if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
                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);
                }
            } else {

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

            if (mFpsMeter != null) {
                mFpsMeter.measure();
                mFpsMeter.draw(canvas, 20, 30);
            }
            getHolder().unlockCanvasAndPost(canvas);
        }
    }
}

和...

@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
    MatOfRect faces = new MatOfRect();
    int currentOrientation = getResources().getConfiguration().orientation;
    if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) { 
        mRgba = inputFrame.rgba();
        mGray = inputFrame.gray();

        int height = mGray.rows();
        if (Math.round(height * 0.2) > 0) {
            mFaceSize = (int) Math.round(height * 0.2);
        }

        cascadeClassifier.detectMultiScale(mGray, faces, 1.1, 3, 2,
                new Size(mFaceSize, mFaceSize));
        Rect[] facesArray = faces.toArray();
        for (int i = 0; i < facesArray.length; i++) {
            Point center = new Point(facesArray[i].x + facesArray[i].width / 2,
                    facesArray[i].y + facesArray[i].height / 2);
            rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
        }

    } else {
        mRgba = inputFrame.rgba();
        mGray = inputFrame.gray();

        Mat rotImage = Imgproc.getRotationMatrix2D(new Point(mRgba.cols() / 2,
                mRgba.rows() / 2), 90, 1.0);

        Imgproc.warpAffine(mRgba, mRgba, rotImage, mRgba.size());
        Imgproc.warpAffine(mGray, mGray, rotImage, mRgba.size());

        Core.flip(mRgba, mRgba, 1);
        Core.flip(mGray, mGray, 1);

        int height = mGray.rows();
        if (Math.round(height * 0.2) > 0) {
            mFaceSize = (int) Math.round(height * 0.2);
        }

        cascadeClassifier.detectMultiScale(mGray, faces, 1.1, 3, 2,
                new Size(mFaceSize, mFaceSize));
        Rect[] facesArray = faces.toArray();
        for (int i = 0; i < facesArray.length; i++) {
            Point center = new Point(facesArray[i].x + facesArray[i].width / 2,
                    facesArray[i].y + facesArray[i].height / 2);
            rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
        }
    }

    return mRgba;

0

我能够从JavaCameraView继承并进行一些修改,以支持纵向和其他方向。以下是在OpenCV 4.5.3(com.quickbirdstudios:opencv-contrib:4.5.3.0)中对我有效的方法:

package com.reactnativemapexplorer.opencv;

import android.content.Context;
import android.graphics.Bitmap;

import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.JavaCameraView;
import org.opencv.core.Core;
import org.opencv.core.Mat;

import java.lang.reflect.Field;

public class CustomJavaCameraView extends JavaCameraView {

    public enum Orientation {
        LANDSCAPE_LEFT,
        PORTRAIT,
        LANDSCAPE_RIGHT;
        boolean isLandscape() {
            return this == LANDSCAPE_LEFT || this == LANDSCAPE_RIGHT;
        }
        boolean isLandscapeRight() {
            return this == LANDSCAPE_RIGHT;
        }
    };

    // scale camera by this coefficient - using mScale seems to more performant than upsizing frame Mat
    // orientation is immutable because every attempt to change it dynamically failed with 'null pointer dereference' and similar exceptions
    // tip: re-creating camera from the outside should allow changing orientation
    private final Orientation orientation;

    public CustomJavaCameraView(Context context, int cameraId, Orientation orientation) {
        super(context, cameraId);
        this.orientation = orientation;
    }

    @Override
    protected void AllocateCache() {
        if (orientation.isLandscape()) {
            super.AllocateCache();
            return;
        }
        try {
            Field privateField = CameraBridgeViewBase.class.getDeclaredField("mCacheBitmap");
            privateField.setAccessible(true);
            privateField.set(this, Bitmap.createBitmap(mFrameHeight, mFrameWidth, Bitmap.Config.ARGB_8888));
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private class CvCameraViewFrameImplHack implements CvCameraViewFrame {
        private Mat rgbMat;
        public CvCameraViewFrameImplHack(Mat rgbMat) {
            this.rgbMat = rgbMat;
        }
        @Override
        public Mat rgba() {
            return this.rgbMat;
        }
        @Override
        public Mat gray() {
            return null;
        }
    }

    private Mat rotateToPortrait(Mat mat) {
        Mat transposed = mat.t();
        Mat flipped = new Mat();
        Core.flip(transposed, flipped, 1);
        transposed.release();
        return flipped;
    }

    private Mat rotateToLandscapeRight(Mat mat) {
        Mat flipped = new Mat();
        Core.flip(mat, flipped, -1);
        return flipped;
    }

    @Override
    protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
        Mat frameMat = frame.rgba();
        Mat rotated;
        if (orientation.isLandscape()) {
            if (orientation.isLandscapeRight()) {
                rotated = rotateToLandscapeRight(frameMat);
            } else {
                rotated = frameMat;
            }
            mScale = (float)getWidth() / (float)frameMat.width();
        } else {
            rotated = rotateToPortrait(frameMat);
            mScale = (float)getHeight() / (float)rotated.height();
        }
        CvCameraViewFrameImplHack hackFrame = new CvCameraViewFrameImplHack(rotated);

        super.deliverAndDrawFrame(hackFrame);
    }
}


0
感谢@Kaye Wrobleski的回答。 我已经扩展了它,以允许横向和纵向方向。 这基本上只是一些额外的代码,可以轻松地在默认代码(提供横向方向)和他的代码(提供纵向方向)之间切换。
将他的代码作为新方法插入到CameraBridgeViewBase.java中。
protected void deliverAndDrawFramePortrait(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();
            // Rotate canvas to 90 degrees
            canvas.rotate(90f, canvas.getWidth()/2, canvas.getHeight()/2);
            if (canvas != null) {
                canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
                Log.d(TAG, "mStretch value: " + mScale);

                if (mScale != 0) {
                    // 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);
                } else {
                    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() - bitmap.getWidth()) / 2),
                            (int)((canvas.getHeight() - bitmap.getHeight()) / 2),
                            (int)((canvas.getWidth() - bitmap.getWidth()) / 2 + bitmap.getWidth()),
                            (int)((canvas.getHeight() - bitmap.getHeight()) / 2 + bitmap.getHeight())), null);
                }

                if (mFpsMeter != null) {
                    mFpsMeter.measure();
                    mFpsMeter.draw(canvas, 20, 30);
                }
                getHolder().unlockCanvasAndPost(canvas);
            }
        }
    }

然后修改JavaCameraView.java文件

添加一个新变量来跟踪我们是处于纵向模式还是横向模式

private boolean portraitMode;

添加两个方法来设置方向模式

public void setLandscapeMode() {
        portraitMode = false;
    }
    public void setPortraitMode() {
        portraitMode = true;
    }

现在将这些行替换到JavaCameraView CameraWorkerClass的run()方法中

if (!mFrameChain[1 - mChainIdx].empty())
                        deliverAndDrawFrame(mCameraFrame[1 - mChainIdx]);

使用这些代码:

if (!mFrameChain[1 - mChainIdx].empty()) {
                        if (!portraitMode) {
                            deliverAndDrawFrame(mCameraFrame[1 - mChainIdx]);
                        } else {
                            deliverAndDrawFramePortrait(mCameraFrame[1 - mChainIdx]);
                        }
                    }

要在方向之间切换,只需在您的JavaCameraView对象上调用setLandscapeMode()或setPortraitMode()即可。

请注意,反向纵向和反向横向方向仍将颠倒。 您需要将它们旋转180度才能使它们正面朝上,这可以使用OpenCV的warpAffine()方法轻松完成。 请注意,在使用后置摄像头(LENS_FACING_BACK)时,纵向模式会将图像翻转。


0

"jaiprakashgogi" 开发者的答案对我有用。但问题是预览仍然只保存为横向。这意味着如果我们将预览设置为imageview,则会显示为横向。

上述解决方案适用于将预览显示为纵向,但不能持久保存为纵向。

我通过以下方式解决了这个问题。

  1. 将字节或矩阵数据转换为位图
  2. 将矩阵旋转90度并应用于位图
  3. 将位图转换为字节数组并保存它。

请在此处查看我的代码...

 public String writeToSDFile(byte[] data, int rotation){


    byte[]  portraitData=null;

   if(rotation==90){
       Log.i(TAG,"Rotation is : "+rotation);
       Bitmap bitmap= BitmapFactory.decodeByteArray(data,0,data.length);
       Matrix matrix = new Matrix();

       matrix.postRotate(90);

       Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap , 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
   portraitData=bitmapToByte(rotatedBitmap);


   }

    File dir=getDirectory();
    String imageTime=""+System.currentTimeMillis();

    String fileName=Constants.FILE_NAME+imageTime+"."+Constants.IMAGE_FORMAT;
    File file = new File(dir, fileName);

    try {
        FileOutputStream f = new FileOutputStream(file);

        if(rotation==90){
            f.write(portraitData);
        }else {
            f.write(data);
        }

        f.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        Log.i(TAG, "******* File not found. Did you" +
                " add a WRITE_EXTERNAL_STORAGE permission to the   manifest?");
    } catch (IOException e) {
        e.printStackTrace();
    }
    Log.i(TAG,"\n\nFile written to "+file);

    return fileName;
}

 // convert bitmap to Byte Array

  public byte[] bitmapToByte(Bitmap bitmap){

    ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
  bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);

    byte[] array=outputStream.toByteArray();
   return array;
}

它完全解决了我的问题。


-1

我不太清楚,但相机尺寸是根据屏幕宽度来决定的。因为屏幕宽度较小,在纵向方向上相机高度也很低。因此,相机分辨率也很低。预览图像会被放置在下面(预览图像旋转由CameraBridgeViewBase.java中相机图像的宽度和高度决定)。

解决方案是使用横向方向(在manifest.xml文件中作为Activity决定横向模式)。因此,由于屏幕宽度较高,高度也会很高,您的应用程序可以决定更高的分辨率。而且,您不必旋转相机图像,并始终处于全屏模式。但缺点是原点不同。我尝试了各种方法来获得纵向方向的高分辨率图像,但找不到办法。

我的应用程序:纵向方向

我的相机图像是720x480 / 横向方向1280x1080。


-3

按照此页面上的说明,在JavaCameraView.java中修改您的代码。

这很容易修复。

之前

Log.d(TAG, "startPreview");

mCamera.startPreview();

之后

Log.d(TAG, "startPreview");

setDisplayOrientation(mCamera, 90);

mCamera.setPreviewDisplay(getHolder());

mCamera.startPreview();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接