android - 获取从相机传感器到当前设备方向的相对旋转

标签 android android-camera android-camera2 android-orientation

有几个示例可以获取从相机传感器到当前设备方向的相对旋转,例如用于使用它来校正相机预览或用于 MediaRecorder.setOrientationHint(int)

但是最新官方 github 相机示例中的最新方法的工作方式与旧方法不同(对于已弃用的camera2 API 和存档的camera2 示例中的方法)

这是我使用所有方法并记录所有结果的示例代码,我们可以看到函数 computeRelativeRotationCamera2New 返回 Surface.ROTATION_90Surface.ROTATION_270 的不同结果 显示旋转

那么正确的方法是什么?

class MainOrientation : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val names = listOf(
            "Surface.ROTATION_0",
            "Surface.ROTATION_90",
            "Surface.ROTATION_180",
            "Surface.ROTATION_270"
        )

        listOf( // test all possible device rotation
            Surface.ROTATION_0,
            Surface.ROTATION_90,
            Surface.ROTATION_180,
            Surface.ROTATION_270
        ).forEachIndexed { idx, displayRotation ->
            // get related orientation (between device orientation and its camera sensor)
            // for example for MediaRecorder.setOrientationHint(int)

            Log.d(TAG, "Display Surface Rotation is ${names[idx]}")
            Log.d(TAG, " ")

            // two different methods for camera2 API (based on Google GitHub samples)
            computeRelativeRotationCamera2New(this, displayRotation) // from current camera2 samples https://github.com/android/camera-samples
            computeRelativeRotationCamera2(this, displayRotation) // from archived camera2 sample https://github.com/googlearchive/android-Camera2Basic

            // deprecated camera API (based on Android Camera API documentation)
            computeRelativeRotationCameraDeprecated(displayRotation) // https://developer.android.com/reference/android/hardware/Camera.html

            Log.d(TAG, "-------------------------")

            /* results from logcat:

            Display Surface Rotation is Surface.ROTATION_0

            computeRelativeRotationCamera2New 90
            computeRelativeRotationCamera2 90
            computeRelativeRotationCameraDeprecated 90
            -------------------------
            Display Surface Rotation is Surface.ROTATION_90

            computeRelativeRotationCamera2New 180
            computeRelativeRotationCamera2 0
            computeRelativeRotationCameraDeprecated 0
            -------------------------
            Display Surface Rotation is Surface.ROTATION_180

            computeRelativeRotationCamera2New 270
            computeRelativeRotationCamera2 270
            computeRelativeRotationCameraDeprecated 270
            -------------------------
            Display Surface Rotation is Surface.ROTATION_270

            computeRelativeRotationCamera2New 0
            computeRelativeRotationCamera2 180
            computeRelativeRotationCameraDeprecated 180
            */
        }
    }

    /**
     * Computes rotation required to transform from the camera sensor orientation to the
     * device's current orientation in degrees.
     *
     * @param characteristics the [CameraCharacteristics] to query for the sensor orientation.
     * @param surfaceRotation the current device orientation as a Surface constant
     * @return the relative rotation from the camera sensor to the current device orientation.
     */
    // https://github.com/android/camera-samples/blob/master/CameraUtils/lib/src/main/java/com/example/android/camera/utils/OrientationLiveData.kt#L71
    // https://github.com/android/camera-samples/blob/master/Camera2Video/app/src/main/java/com/example/android/camera2/video/fragments/CameraFragment.kt#L273
    private fun computeRelativeRotationCamera2New(
        context: Context,
        surfaceRotation: Int
    ): Int {
        val characteristics = getCameraCharacteristics(context)

        val sensorOrientationDegrees =
            characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)!!

        val deviceOrientationDegrees = when (surfaceRotation) {
            Surface.ROTATION_0 -> 0
            Surface.ROTATION_90 -> 90
            Surface.ROTATION_180 -> 180
            Surface.ROTATION_270 -> 270
            else -> 0
        }

        // Reverse device orientation for front-facing cameras
        val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) ==
            CameraCharacteristics.LENS_FACING_FRONT
        ) 1 else -1

        // Calculate desired JPEG orientation relative to camera orientation to make
        // the image upright relative to the device orientation
        val result = (sensorOrientationDegrees - (deviceOrientationDegrees * sign) + 360) % 360
        Log.d(TAG, "computeRelativeRotationCamera2New $result")
        return result
    }

    // https://github.com/googlearchive/android-Camera2Video/blob/master/kotlinApp/Application/src/main/java/com/example/android/camera2video/Camera2VideoFragment.kt#L479
    private fun computeRelativeRotationCamera2(context: Context, surfaceRotation: Int) {
        // for computeRelativeRotationCamera2()
        val SENSOR_ORIENTATION_DEFAULT_DEGREES = 90
        val SENSOR_ORIENTATION_INVERSE_DEGREES = 270
        val DEFAULT_ORIENTATIONS = SparseIntArray().apply {
            append(Surface.ROTATION_0, 90)
            append(Surface.ROTATION_90, 0)
            append(Surface.ROTATION_180, 270)
            append(Surface.ROTATION_270, 180)
        }
       val INVERSE_ORIENTATIONS = SparseIntArray().apply {
            append(Surface.ROTATION_0, 270)
            append(Surface.ROTATION_90, 180)
            append(Surface.ROTATION_180, 90)
            append(Surface.ROTATION_270, 0)
        }
        when (getCameraCharacteristics(context).get(CameraCharacteristics.SENSOR_ORIENTATION)!!) {
            SENSOR_ORIENTATION_DEFAULT_DEGREES ->
                Log.d(
                    TAG,
                    "computeRelativeRotationCamera2 ${DEFAULT_ORIENTATIONS.get(surfaceRotation)}"
                )
            SENSOR_ORIENTATION_INVERSE_DEGREES ->
                Log.d(
                    TAG,
                    "computeRelativeRotationCamera2 ${INVERSE_ORIENTATIONS.get(surfaceRotation)}"
                )
        }
    }

    // https://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation%28int%29
    private fun computeRelativeRotationCameraDeprecated(
        surfaceRotation: Int
    ): Int {
        val cameraInfo = getCameraDeprecatedInfo()
        val degrees = when (surfaceRotation) {
            Surface.ROTATION_0 -> 0
            Surface.ROTATION_90 -> 90
            Surface.ROTATION_180 -> 180
            Surface.ROTATION_270 -> 270
            else -> 0
        }
        var result: Int
        if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (cameraInfo.orientation + degrees) % 360
            result = (360 - result) % 360 // compensate the mirror
        } else {  // back-facing
            result = (cameraInfo.orientation - degrees + 360) % 360
        }
        Log.d(TAG, "computeRelativeRotationCameraDeprecated $result")
        return result
    }

    companion object {
        // methods to get info about camera using deprecated camera and camera2 APIs

        private fun getCameraManager(context: Context) =
            context.getSystemService(Context.CAMERA_SERVICE) as CameraManager

        private fun getCameraId(manager: CameraManager): String {
            return manager.cameraIdList.first {
                val characteristics = manager.getCameraCharacteristics(it)
                val facing = characteristics.get(CameraCharacteristics.LENS_FACING)
                facing == CameraCharacteristics.LENS_FACING_BACK
            }
        }

        private fun getCameraCharacteristics(context: Context): CameraCharacteristics {
            val manager = getCameraManager(context)
            return manager.getCameraCharacteristics(getCameraId(manager))
        }

        private fun getCameraDeprecatedInfo(): Camera.CameraInfo {
            val cameraInfo = Camera.CameraInfo()
            Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, cameraInfo)
            return cameraInfo
        }

        private const val TAG = "orientation"
    }
}

最佳答案

测试了不同方法录制的视频,发现最新样本中的最新方法对于横向视频录制不正确,它播放视频是颠倒的

我决定更新使用已弃用的相机 api 的旧方法来使用camera2 api(适用于 21+ API)并在我的项目中使用它

fun computeRelativeRotation(
    context: Context,
    surfaceRotation: Int, // display rotation 
    cameraId: String
): Int {
    val characteristics = getCameraCharacteristics(context, cameraId)

    val sensorOrientationDegrees = characteristics.get(
        CameraCharacteristics.SENSOR_ORIENTATION
    )!!

    val degrees = when (surfaceRotation) {
        Surface.ROTATION_0 -> 0
        Surface.ROTATION_90 -> 90
        Surface.ROTATION_180 -> 180
        Surface.ROTATION_270 -> 270
        else -> 0
    }
    var result: Int
    val cameraFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
    if (cameraFacing == CameraCharacteristics.LENS_FACING_FRONT) {
        result = (sensorOrientationDegrees + degrees) % 360
        result = (360 - result) % 360 // compensate the mirror
    } else {  // back-facing
        result = (sensorOrientationDegrees - degrees + 360) % 360
    }
    return result
}

private fun getCameraCharacteristics(
    context: Context,
    cameraId: String
): CameraCharacteristics {
    return getCameraManager(context).getCameraCharacteristics(cameraId)
}

private fun getCameraManager(context: Context) =
    context.getSystemService(Context.CAMERA_SERVICE) as CameraManager

关于android - 获取从相机传感器到当前设备方向的相对旋转,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63174125/

相关文章:

android - 如何在android中拆分相机预览?

android - Camera2 仅适用于某些设备

Android ksoap2 发送空体信封?

java - 在 android studio 中为具有 spring security 的 spring boot 服务器端创建登录

java - 不理解 RxJava MaybeObserver (聊天通知不起作用)

android - 在 Camera2 API 中切换闪光灯

android - CameraX 或 android.hardware.camera2 中 setPreviewCallbackWithBuffer 的模拟

java - 无法在android中单击按钮时显示textview

c# - 如何使用 C# 从 android 相机获取图像或位图

android - 如何在Android上的Camera2 API中设置 '' CONTROL_AE_EXPOSURE_COMPENSATION''?