OpenCV4.10.0 sdkのjavaソース中に非推奨が指摘される 対策

2025.1.21 2021.3.3Coskx Lab  

1 概要

OpenCV sdkのandroidのjavaソースの一部が非推奨と警告が出るようになってきたので,対応します。

すでに非推奨となっているhardware cameraAPIを使わずにcamera2APIを使うプログラミングを対象にしています。
(JavaCameraView.javaを使わずにJavaCamera2View.javaを使っている)

問題になっているのは,次の2つです。

1.JavaCamera2View.java
  createCaptureSession()が API level 30 以降で非推奨
2.CameraBridgeViewBase.java
  WindowManagerのgetDefaultDisplay()が API level 30 以降で非推奨

それ故,OpenCV4.10.0-sdkのJavaCamera2View.javaとCameraBridgeViewBase.javaを書き換えた対策済の差し替え版を作成します。

修正したJavaCamera2View.javaとCameraBridgeViewBase.javaのダウンロード

2 使用環境

3 CameraDevice.createCaptureSessionが非推奨

Android11(API 30)以降を開発ターゲットにすると,OpenCVのJavaCamera2View(Camera2ライブラリ使用クラス)のCameraDevice.createCaptureSessionが非推奨となっており,警告が出ています。
そのままにしていても,当面は大丈夫と思いますが,「Developers」にて置き換えが指示されている別の形のコンストラクタに変更しました。

このJavaCamera2View classは重要なのでいずれOpenCV本家の記述が修正されると思いますが,それまでの間を過ごすための作業です。

3.1 対策前

非推奨警告が出るのはJavaCamera2View.javaのメソッドprivate void createCameraPreviewSession()内のCameraDevice.createCaptureSessionを作っているところです。

             OPenCV 4.10.0同梱のJavaCamera2View.java createCameraPreviewSession()

    private void createCameraPreviewSession() {
        final int w = mPreviewSize.getWidth(), h = mPreviewSize.getHeight();
        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);

                    RotatedCameraFrame tempFrame = new RotatedCameraFrame(new JavaCamera2Frame(image), mFrameRotation);
                    deliverAndDrawFrame(tempFrame);
                    tempFrame.mFrame.release();
                    tempFrame.release();
                    image.close();
                }
            }, mBackgroundHandler);
            Surface surface = mImageReader.getSurface();

            mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(mRequestTemplate);
            mPreviewRequestBuilder.addTarget(surface);

            mCameraDevice.createCaptureSession(Arrays.asList(surface),
                                               allocateSessionStateCallback(), null);
        } catch (CameraAccessException e) {
            Log.e(LOGTAG, "createCameraPreviewSession", e);
        }
    }

3.2 対策

https://developer.android.com/reference/android/hardware/camera2/CameraDevice
では
createCaptureSession(List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)
This method was deprecated in API level 30. Please use createCaptureSession(android.hardware.camera2.params.SessionConfiguration) for the full set of configuration options available.
となっています。
そこでSessionConfigurationを使うようにして,次のように置き換えます。

             修正後のJavaCamera2View.java createCameraPreviewSession()

    private void createCameraPreviewSession() {
        final int w = mPreviewSize.getWidth(), h = mPreviewSize.getHeight();
        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);

                    RotatedCameraFrame tempFrame = new RotatedCameraFrame(new JavaCamera2Frame(image), mFrameRotation);
                    deliverAndDrawFrame(tempFrame);
                    tempFrame.mFrame.release();
                    tempFrame.release();
                    image.close();
                }
            }, mBackgroundHandler);
            Surface surface = mImageReader.getSurface();

            mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(mRequestTemplate);
            mPreviewRequestBuilder.addTarget(surface);

            try {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                    mCameraDevice.createCaptureSession(
                            new SessionConfiguration(
                                    SessionConfiguration.SESSION_REGULAR,
                                    Collections.singletonList(new OutputConfiguration(surface)),
                                    Executors.newCachedThreadPool(),
                                    allocateSessionStateCallback()
                            ));
                } else { //for old devices (This section would be noted as deprecated.)
                    mCameraDevice.createCaptureSession(Collections.singletonList(surface),
                            allocateSessionStateCallback(), null);
                }
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }

        } catch (CameraAccessException e) {
            Log.e(LOGTAG, "createCameraPreviewSession", e);
        }
    }

4 CameraDevice.createCaptureSessionが非推奨

Android11(API 30)以降を開発ターゲットにすると,CameraBridgeViewBase.javaのWindowManagerのgetDefaultDisplay()が API level 30 以降で非推奨が非推奨となっており,警告が出ています。
そのままにしていても,当面は大丈夫と思いますが,修正変更しました。

このCameraBridgeViewBase classは重要なのでいずれOpenCV本家の記述が修正されると思いますが,それまでの間を過ごすための作業です。

4.1 対策前

非推奨警告が出るのはCameraBridgeViewBase.javaのメソッドprotected int getFrameRotation()内のscreenOrientationの値を得ているところです。

             OPenCV 4.10.0同梱のCameraBridgeViewBase.java getFrameRotation()

    protected int getFrameRotation(boolean cameraFacingFront, int cameraSensorOrientation) {
        WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        int screenOrientation = windowManager.getDefaultDisplay().getRotation();
        int screenRotation = 0;
        switch (screenOrientation) {
            case Surface.ROTATION_0:
                screenRotation = 0;
                break;
            case Surface.ROTATION_90:
                screenRotation = 90;
                break;
            case Surface.ROTATION_180:
                screenRotation = 180;
                break;
            case Surface.ROTATION_270:
                screenRotation = 270;
                break;
        }

        int frameRotation;
        if (cameraFacingFront) {
            frameRotation = (cameraSensorOrientation + screenRotation) % 360;
        } else {
            frameRotation = (cameraSensorOrientation - screenRotation + 360) % 360;
        }

        return frameRotation;
    }

4.2 対策

getDisplay()を使うようにして,次のように置き換えます。

             修正後のCameraBridgeViewBase.java getFrameRotation()

    protected int getFrameRotation(boolean cameraFacingFront, int cameraSensorOrientation) {
        int screenOrientation;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            screenOrientation = Objects.requireNonNull(((Activity) mContext).getDisplay()).getRotation();
        } else { //for old devices (This section would be noted as deprecated.)
            WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
            screenOrientation = windowManager.getDefaultDisplay().getRotation();
        }
        int screenRotation = 0;
        switch (screenOrientation) {
            case Surface.ROTATION_0:
                screenRotation = 0;
                break;
            case Surface.ROTATION_90:
                screenRotation = 90;
                break;
            case Surface.ROTATION_180:
                screenRotation = 180;
                break;
            case Surface.ROTATION_270:
                screenRotation = 270;
                break;
        }

        int frameRotation;
        if (cameraFacingFront) {
            frameRotation = (cameraSensorOrientation + screenRotation) % 360;
        } else {
            frameRotation = (cameraSensorOrientation - screenRotation + 360) % 360;
        }

        return frameRotation;
    }

5 まとめ

OpenCV4.10.0のソースのJavaCamera2View.javaがAndroid11(API 30)以降で非推奨メソッドが使用されているため, またCameraBridgeViewBase.java getFrameRotation()もAndroid11(API 30)以降で非推奨メソッドが使用されているため, 対応しました。
ただし,両者とも互換対応用記述が残るので,コンパイル時の警告は出ます。

なお次の7つのファイルは使われていないのであれば,無効にすると,コンパイル時のdeprecated警告表示は,かなりなくなります。
 Camera2Renderer.java
 CameraActivity.java
 CameraGLRendererBase.java
 CameraGLSurfaceView.java
 CameraRenderer.java
 JavaCameraView.java
 NativeCameraView.java