前言

前面研讨在Android获取超广角摄像头:一些获取您的Android设备超广角才能的思路 – ()。后续笔者经过查询Android文档发现了一些新的思路,本文将经过文档说到的内容,总结一下自己关于获取超广角的新发现。

逻辑摄像头

概念

参阅 Multi-camera API#logical | Android Developers (google.cn)

在Android 9时,Android提出了多摄像头的概念,下图截取了有关逻辑摄像头的概念解说

从Android官方文档中寻找获取超广角的灵感

翻译一下大约意思是手机由于多摄像头的呈现,以后置摄像头模组为例,一个物理上的摄像头被称为物理摄像头。多个物理摄像头能够分成一组,每组能够用一个逻辑摄像头来表示

逻辑摄像头能够了解是在代码上关于多摄像头的抽象,比较常见的例子便是缩放,多摄像头的焦距能够支撑更大的缩放规模。

获取

获取逻辑摄像头的方法在文档中的示例代码也有说到,这儿再简略说一下

String[] cameraIds = cameraManager.getCameraIdList();
List<String> backCameraIds = new ArrayList<>();
for (String id : cameraIds) {
    CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(id);
    if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) != CameraCharacteristics.LENS_FACING_BACK)
        continue;
    backCameraIds.add(id);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    List<String> logicCameraIds = new ArrayList<>();
    for (String id : backCameraIds) {
        CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(id);
        int[] available = cameraCharacteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
        for (int i : available) {
            if (i == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
                logicCameraIds.add(id);
            }
        }
    }
}

上述代码展现的是咱们需求从后置摄像头中找到逻辑摄像头。ps:这个过程根据Camera2

  • 经过CameraCharacteristics.LENS_FACING_BACK筛选出后置摄像头
  • Android 9之后的支撑库中为咱们供给了CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA用于标识该摄像头是否为逻辑摄像头,经过CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES获取的数组中得知。

能够看到逻辑摄像头也是在getCameraIdList中获取的,只是经过特点区分。

核算视图角的不适配问题解读

在一些获取您的Android设备超广角才能的思路一文中有说到关于经过核算视图角推测出广角摄像头的计划。

Android 尝试适配广角镜头的计划 – ()

但在华为P40中会呈现不适配状况,原因在于cameraId = 0的摄像头,是一个逻辑摄像头,原则上是不需求参与核算的。

从Android官方文档中寻找获取超广角的灵感

上图是之前核算的成果,能够看终究的“back camera result”,假如咱们把0的成果排除掉再比较大小的话,终究成果应该是4最大,而4便是华为P40的超广角摄像头了。

多摄像头创立会话

参阅
Multi-camera API#multi-physical | Android Developers (google.cn)

Multi-camera API#pair-physical | Android Developers (google.cn)

接下来两个末节介绍的是在一个逻辑摄像头下的物理摄像头能够创立一个CameraCaptureSession进行预览、摄影。依靠的是CameraDevice.createCaptureSession(SessionConfiguration config)

还是以华为P40为例,咱们知道在逻辑摄像头0下,有物理摄像头2、4、6

从Android官方文档中寻找获取超广角的灵感

  • 咱们先默许2、4摄像头是直接能够使用的,不需求经过任何参数筛选
  • 将2、4摄像头创立成两个OutputConfiguration,当然每个OutputConfiguration都需求绑定一个Surface
    val physicalCameraId = 2
    val config = OutputConfiguration(surface)
    config.setPhysicalCameraId(physicalCameraId)
    
  • 再使用两个OutputConfiguration创立一个SessionConfiguration
    val executor = Executors.newCachedThreadPool()
    val sessionConfig = SessionConfiguration(
        SessionConfiguration.SESSION_REGULAR,
        outputConfigs,  // Array<OutputConfiguration>
        executor,
        captureStateCallback
    )
    
  • 终究便是创立CameraCaptureSession
    cameraDevice?.createCaptureSession(sessionConfig)
    

这样咱们就能够经过一个CameraCaptureSession对象管理两个摄像头预览了(2、4摄像头各有一个预览视图)。

这儿找了一个github的项目可供参阅:

Kurun-pan/android-multi-camera

ps:需求补充的是,物理摄像头的列表可经过逻辑摄像头的CameraCharacteristics#getPhysicalCameraIds()获取。

筛选广角镜头的思路

参阅 Multi-camera API#zoom | Android Developers (google.cn)

文档中还列举一个缩放的例子,其实经过上一节的内容,就能够获取到一个从正常视距到较小视距的规模,咱们能够经过这个实现一个缩放的效果。实际上便是两个预览视图的切换效果,能够是两个TextureView

要点看看原文中获取最短和最长焦距的方法

fun findShortLongCameraPair(manager: CameraManager, facing: Int? = null): DualCamera? {
  return findDualCameras(manager, facing).map {
    val characteristics1 = manager.getCameraCharacteristics(it.physicalId1)
    val characteristics2 = manager.getCameraCharacteristics(it.physicalId2)
    // Query the focal lengths advertised by each physical camera
    val focalLengths1 = characteristics1.get(
      CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS) ?: floatArrayOf(0F)
    val focalLengths2 = characteristics2.get(
      CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS) ?: floatArrayOf(0F)
    // Compute the largest difference between min and max focal lengths between cameras
    val focalLengthsDiff1 = focalLengths2.maxOrNull()!! - focalLengths1.minOrNull()!!
    val focalLengthsDiff2 = focalLengths1.maxOrNull()!! - focalLengths2.minOrNull()!!
    // Return the pair of camera IDs and the difference between min and max focal lengths
    if (focalLengthsDiff1 < focalLengthsDiff2) {
      Pair(DualCamera(it.logicalId, it.physicalId1, it.physicalId2), focalLengthsDiff1)
    } else {
      Pair(DualCamera(it.logicalId, it.physicalId2, it.physicalId1), focalLengthsDiff2)
    }
    // Return only the pair with the largest difference, or null if no pairs are found
  }.maxByOrNull { it.second }?.first
}

这儿终究出来的是在一个逻辑摄像头下具有最短、最长焦距的摄像头组合。当然,在华为P40上面输出的是物理摄像头6、4。6的焦距是最短的,查看官方参数是一枚长焦镜头,应该是用来做高倍数的变焦的。

所以其实这个方法并不是通用的,但笔者的意图只是获取到广角镜头,所以简略来说只需求知道哪个焦距是最短的即可。

还有一个需求留意的是,由于CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS拿到的是一个数组,所以上述代码用到了maxmin的比较。

畸变纠正问题

参阅 Multi-camera API#distortion | Android Developers (google.cn)

之前的文章也有说到,成功获取到超广角镜头之后的问题便是如何处理画面畸变的问题。这儿有提及到,部分设备会支撑CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY,这个能够进行校正。能够经过代码获取

int[] correctionAvailableModes = characteristics.get(
        CameraCharacteristics.DISTORTION_CORRECTION_AVAILABLE_MODES);
boolean supportsDistortionCorrection = false;   //  是否支撑失真校正
if (correctionAvailableModes != null) {
    for (int mode: correctionAvailableModes) {
        if (mode == CameraMetadata.DISTORTION_CORRECTION_MODE_HIGH_QUALITY) {
            supportsDistortionCorrection = true;
            break;
        }
    }
}

但笔者手上的华为P40并没有支撑,下图是摄像头2、4、6的信息

从Android官方文档中寻找获取超广角的灵感

总结

经过这篇Android官方文档,能够总结出几点:

  • Android 9之后有逻辑摄像头的概念,这也是为什么之前核算视图角不能精确确定超广角摄像头的原因
  • CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS获取的焦距是数组,之前的计划是直接拿第1位核算,或许会存在不精确的地方。
  • 假如顺畅获取到超广角进行预览、摄影了,还需求处理畸变的问题。

所有的这一切看起来都很美好,但是在笔者实测发现:小米、OPPO的系统(ps:这儿不清楚和系统版本是否有直接关系)是无法经过Camera2读取到多摄像头的,也便是说这个计划会直接gg。所以说等候手机厂商推出自己的计划或许才是正途,只是这个或许比较难。。。