JavaCamera2View更改预览分辨率

时间:2018-04-17 16:28:30

标签: android opencv opencv3.0 android-camera2 opencv4android

使用OpenCV Android教程我想更改预览分辨率。因此,我注释掉了calcPreviewSize函数,并将resoultion设置为640x480,支持并包含在相机特性中。预览分辨率的更改是成功的,但是应用程序崩溃,尽管给定的行不包含官方教程中的任何更改。

E/JavaCamera2View: createCaptureSession failed
               java.lang.IllegalStateException: Session has been closed; further changes are illegal.
                   at android.hardware.camera2.impl.CameraCaptureSessionImpl.checkNotClosed(CameraCaptureSessionImpl.java:607)
                   at android.hardware.camera2.impl.CameraCaptureSessionImpl.setRepeatingRequest(CameraCaptureSessionImpl.java:227)
                   at org.opencv.android.JavaCamera2View$3.onConfigured(JavaCamera2View.java:220)
                   at java.lang.reflect.Method.invoke(Native Method)
public class JavaCamera2View extends CameraBridgeViewBase {

private static final String LOGTAG = "JavaCamera2View";

private ImageReader mImageReader;
private int mPreviewFormat = ImageFormat.YUV_420_888;

public int widthP = 640;
public int heightP = 480;

public CameraDevice mCameraDevice;
private CameraCaptureSession mCaptureSession;
private CaptureRequest.Builder mPreviewRequestBuilder;
private String mCameraID;
private android.util.Size mPreviewSize = new android.util.Size(-1, -1);

private HandlerThread mBackgroundThread;
private Handler mBackgroundHandler;

public JavaCamera2View(Context context, int cameraId) {
    super(context, cameraId);
}

public JavaCamera2View(Context context, AttributeSet attrs) {
    super(context, attrs);
}

private void startBackgroundThread() {
    Log.i(LOGTAG, "startBackgroundThread");
    stopBackgroundThread();
    mBackgroundThread = new HandlerThread("OpenCVCameraBackground");
    mBackgroundThread.start();
    mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}

private void stopBackgroundThread() {
    Log.i(LOGTAG, "stopBackgroundThread");
    if (mBackgroundThread == null)
        return;
    mBackgroundThread.quitSafely();
    try {
        mBackgroundThread.join();
        mBackgroundThread = null;
        mBackgroundHandler = null;
    } catch (InterruptedException e) {
        Log.e(LOGTAG, "stopBackgroundThread", e);
    }
}

protected boolean initializeCamera() {
    Log.i(LOGTAG, "initializeCamera");
    CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
    try {
        String camList[] = manager.getCameraIdList();
        if (camList.length == 0) {
            Log.e(LOGTAG, "Error: camera isn't detected.");
            return false;
        }
        if (mCameraIndex == CameraBridgeViewBase.CAMERA_ID_ANY) {
            mCameraID = camList[0];
        } else {
            for (String cameraID : camList) {
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraID);
                if ((mCameraIndex == CameraBridgeViewBase.CAMERA_ID_BACK &&
                        characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK) ||
                    (mCameraIndex == CameraBridgeViewBase.CAMERA_ID_FRONT &&
                        characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT)
                ) {
                    mCameraID = cameraID;
                    break;
                }
            }
        }
        if (mCameraID != null) {
            //CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraID);
            //characteristics = manager.
            Log.i(LOGTAG, "Opening camera: " + mCameraID);
            manager.openCamera(mCameraID, mStateCallback, mBackgroundHandler);
        }
        return true;
    } catch (CameraAccessException e) {
        Log.e(LOGTAG, "OpenCamera - Camera Access Exception", e);
    } catch (IllegalArgumentException e) {
        Log.e(LOGTAG, "OpenCamera - Illegal Argument Exception", e);
    } catch (SecurityException e) {
        Log.e(LOGTAG, "OpenCamera - Security Exception", e);
    }
    return false;
}

private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {

    @Override
    public void onOpened(CameraDevice cameraDevice) {
        mCameraDevice = cameraDevice;
        createCameraPreviewSession();
    }

    @Override
    public void onDisconnected(CameraDevice cameraDevice) {
        cameraDevice.close();
        mCameraDevice = null;
    }

    @Override
    public void onError(CameraDevice cameraDevice, int error) {
        cameraDevice.close();
        mCameraDevice = null;
    }

};

private void createCameraPreviewSession() {
    final int w = widthP;//mPreviewSize.getWidth(), h = mPreviewSize.getHeight();
    final int h = heightP;
    Log.i(LOGTAG, "createCameraPreviewSession(" + w + "x" + h + ")");
    if (w < 0 || h < 0)
        return;
    try {
        if (null == mCameraDevice) {
            Log.e(LOGTAG, "createCameraPreviewSession: camera isn't opened");
            return;
        }
        if (null != mCaptureSession) {
            Log.e(LOGTAG, "createCameraPreviewSession: mCaptureSession is already started");
            return;
        }

        mImageReader = ImageReader.newInstance(w, h, mPreviewFormat, 2);
        mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = reader.acquireLatestImage();
                if (image == null)
                    return;

                // sanity checks - 3 planes
                Image.Plane[] planes = image.getPlanes();
                assert (planes.length == 3);
                assert (image.getFormat() == mPreviewFormat);

                // see also https://developer.android.com/reference/android/graphics/ImageFormat.html#YUV_420_888
                // Y plane (0) non-interleaved => stride == 1; U/V plane interleaved => stride == 2
                assert (planes[0].getPixelStride() == 1);
                assert (planes[1].getPixelStride() == 2);
                assert (planes[2].getPixelStride() == 2);

                ByteBuffer y_plane = planes[0].getBuffer();
                ByteBuffer uv_plane = planes[1].getBuffer();
                Mat y_mat = new Mat(h, w, CvType.CV_8UC1, y_plane);
                Mat uv_mat = new Mat(h / 2, w / 2, CvType.CV_8UC2, uv_plane);
                JavaCamera2Frame tempFrame = new JavaCamera2Frame(y_mat, uv_mat, w, h);
                deliverAndDrawFrame(tempFrame);
                tempFrame.release();
                image.close();
            }
        }, mBackgroundHandler);
        Surface surface = mImageReader.getSurface();

        mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        mPreviewRequestBuilder.addTarget(surface);

        mCameraDevice.createCaptureSession(Arrays.asList(surface),
            new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                    Log.i(LOGTAG, "createCaptureSession::onConfigured");
                    if (null == mCameraDevice) {
                        return; // camera is already closed
                    }
                    mCaptureSession = cameraCaptureSession;
                    try {
                        mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                                CaptureRequest.CONTROL_AF_MODE_OFF);
                        mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                                CaptureRequest.CONTROL_AE_MODE_OFF);

                        mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<Integer>(30,30));

                        mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, mBackgroundHandler);
                        Log.i(LOGTAG, "CameraPreviewSession has been started");
                    } catch (Exception e) {
                        Log.e(LOGTAG, "createCaptureSession failed", e);
                    }
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                    Log.e(LOGTAG, "createCameraPreviewSession failed");
                }
            },
            null
        );
    } catch (CameraAccessException e) {
        Log.e(LOGTAG, "createCameraPreviewSession", e);
    }
}

@Override
protected void disconnectCamera() {
    Log.i(LOGTAG, "closeCamera");
    try {
        CameraDevice c = mCameraDevice;
        mCameraDevice = null;
        if (null != mCaptureSession) {
            mCaptureSession.close();
            mCaptureSession = null;
        }
        if (null != c) {
            c.close();
        }
        if (null != mImageReader) {
            mImageReader.close();
            mImageReader = null;
        }
    } finally {
        stopBackgroundThread();
    }
}

boolean calcPreviewSize(final int width, final int height) {
    Log.i(LOGTAG, "calcPreviewSize: " + width + "x" + height);
    if (mCameraID == null) {
        Log.e(LOGTAG, "Camera isn't initialized!");
        return false;
    }
    CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
    try {
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(mCameraID);
        StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        int bestWidth = 0, bestHeight = 0;
        float aspect = (float) width / height;
        android.util.Size[] sizes = map.getOutputSizes(ImageReader.class);
        bestWidth = sizes[0].getWidth();
        bestHeight = sizes[0].getHeight();
        for (android.util.Size sz : sizes) {
            int w = sz.getWidth(), h = sz.getHeight();
            Log.d(LOGTAG, "trying size: " + w + "x" + h);
            if (width >= w && height >= h && bestWidth <= w && bestHeight <= h
                    && Math.abs(aspect - (float) w / h) < 0.2) {
                bestWidth = w;
                bestHeight = h;
            }
        }
        Log.i(LOGTAG, "best size: " + bestWidth + "x" + bestHeight);
        assert(!(bestWidth == 0 || bestHeight == 0));
        if (mPreviewSize.getWidth() == bestWidth && mPreviewSize.getHeight() == bestHeight)
            return false;
        else {
            mPreviewSize = new android.util.Size(widthP, heightP);
            return true;
        }
    } catch (CameraAccessException e) {
        Log.e(LOGTAG, "calcPreviewSize - Camera Access Exception", e);
    } catch (IllegalArgumentException e) {
        Log.e(LOGTAG, "calcPreviewSize - Illegal Argument Exception", e);
    } catch (SecurityException e) {
        Log.e(LOGTAG, "calcPreviewSize - Security Exception", e);
    }
    return false;
}

@Override
protected boolean connectCamera(int width, int height) {
    Log.i(LOGTAG, "setCameraPreviewSize(" + width + "x" + height + ")");
    startBackgroundThread();
    initializeCamera();
    try {
        boolean needReconfig = calcPreviewSize(width, height);
        mFrameWidth = mPreviewSize.getWidth();
        mFrameHeight = mPreviewSize.getHeight();

        if ((getLayoutParams().width == LayoutParams.MATCH_PARENT) && (getLayoutParams().height == LayoutParams.MATCH_PARENT))
            mScale = Math.min(((float)height)/mFrameHeight, ((float)width)/mFrameWidth);
        else
            mScale = 0;

        AllocateCache();

        if (needReconfig) {
            if (null != mCaptureSession) {
                Log.d(LOGTAG, "closing existing previewSession");
                mCaptureSession.close();
                mCaptureSession = null;
            }
            createCameraPreviewSession();
        }
    } catch (RuntimeException e) {
        throw new RuntimeException("Interrupted while setCameraPreviewSize.", e);
    }
    return true;
}

private class JavaCamera2Frame implements CvCameraViewFrame {
    @Override
    public Mat gray() {
        return mYuvFrameData.submat(0, mHeight, 0, mWidth);
    }

    @Override
    public Mat rgba() {
        if (mPreviewFormat == ImageFormat.NV21)
            Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGBA_NV21, 4);
        else if (mPreviewFormat == ImageFormat.YV12)
            Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGB_I420, 4); // COLOR_YUV2RGBA_YV12 produces inverted colors
        else if (mPreviewFormat == ImageFormat.YUV_420_888) {
            assert (mUVFrameData != null);
            Imgproc.cvtColorTwoPlane(mYuvFrameData, mUVFrameData, mRgba, Imgproc.COLOR_YUV420sp2BGRA);
            // TODO: Here we had to change vom Imgproc.COLOR_YUV2RGBA_NV21 to Imgproc.COLOR_YUV420sp2BGRA to get the correct colors.
        } else
            throw new IllegalArgumentException("Preview Format can be NV21 or YV12");

        return mRgba;
    }

    public JavaCamera2Frame(Mat Yuv420sp, int width, int height) {
        super();
        mWidth = width;
        mHeight = height;
        mYuvFrameData = Yuv420sp;
        mUVFrameData = null;
        mRgba = new Mat();
    }

    public JavaCamera2Frame(Mat Y, Mat UV, int width, int height) {
        super();
        mWidth = width;
        mHeight = height;
        mYuvFrameData = Y;
        mUVFrameData = UV;
        mRgba = new Mat();
    }

    public void release() {
        mRgba.release();
    }

    private Mat mYuvFrameData;
    private Mat mUVFrameData;
    private Mat mRgba;
    private int mWidth;
    private int mHeight;
};

}

1 个答案:

答案 0 :(得分:1)

从表面上看,修改后的calcPreviewSize()方法可能存在问题:即使预览尺寸不应更改,它也可能会返回 true

你会看到

I/JavaCamera2View best size: WWWWxHHH
在您的logcat中

,其中WWWW不是640而HHH不是480。

更改预览尺寸涉及关闭并重新打开 previewSession ,并需要时间。您使用的代码需要重复调​​用calcPreviewSize()才能返回 false

可能的解决方法是以这种方式重写方法:

boolean calcPreviewSize(final int width, final int height) {
    Log.i(LOGTAG, "calcPreviewSize: " + width + "x" + height);
    if (mCameraID == null) {
        Log.e(LOGTAG, "Camera isn't initialized!");
        return false;
    }
    if (mPreviewSize.getWidth() == widthP && mPreviewSize.getHeight() == heightP) {
        return false;
    }
    else {
        mPreviewSize = new android.util.Size(widthP, heightP);
        return true;
    }
}