CameraX 实现摄像头旋转预览

问题

最近接到一个需求需要链接一台电子秤的2个usb摄像头,遇到了2个问题:一个是usb摄像头的链接访问,另一个是摄像头默认被旋转了90。下面记录下解决方案。

解决方案

usb链接问题

原有项目使用的是CameraX实现摄像头预览和图片抓取,所以这里也是用CameraX来实现usb摄像头链接。

  1. 布局文件layout.xml
<androidx.camera.view.PreviewView
        android:id="@+id/previewView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:scaleType="fillCenter"/>
  1. 预览页面LayoutActivity.kt
/**
  * 开始预览摄像头
  */
fun startCamera() {
    // 相机预览功能
    val preview = Preview.Builder()
        .build()
        .also { it.setSurfaceProvider(previewView.surfaceProvider) }

    // 捕获静态图像
    imageCapture = ImageCapture
        .Builder()
        .setTargetAspectRatio(AspectRatio.RATIO_16_9) //修改图片捕获尺寸为16:9和当前屏幕保持一致
        .setJpegQuality(60)
        .build()
    //
    cameraProviderFuture = ProcessCameraProvider.getInstance(this)
    cameraProviderFuture.addListener({
        val cameraProvider = cameraProviderFuture.get()
        cameraProvider.unbindAll() // 解绑所有已绑定的用例(避免重复绑定)
        cameraProvider.bindToLifecycle(
            // 绑定摄像头生命周期:将预览用例绑定到当前 Activity 的生命周期
            this as LifecycleOwner,
            getCameraSelector(this, 102, CameraSelector.LENS_FACING_FRONT),
            preview,
            imageCapture,
        )
    }, ContextCompat.getMainExecutor(this))
}

/**
 * 相机选择器
 * @param context 用于获取 cameraManager
 * @param cameraId 当 lensFacing 失效时通过 cameraId 来指定相机
 * @param lensFacing 指定前置还是后置摄像头
 */
fun getCameraSelector(context: Context, cameraId: Int, lensFacing: Int): CameraSelector? {
    // 相机选择器
    val cameraSelector = CameraSelector.Builder()
    // 判断是否有默认的相机
    val cameraProvider = ProcessCameraProvider.getInstance(context).get()
    val backCamera = cameraProvider.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA)
    val frontCamera = cameraProvider.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA)
    // 默认的前置和后置相机都没有,需要通过指定cameraId来获取;
    // cameraId可通过cameraManager.cameraIdList获取,具体是哪个id需要自己测试。
    if (!frontCamera && !frontCamera) {
        val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
        if (cameraManager.cameraIdList.isEmpty()) {
            return null
        }
  //            for (id in cameraManager.cameraIdList) {
  //                Log.i(TAG, "camera id:${id}")
  //                val characteristics = cameraManager.getCameraCharacteristics(id) // 获取摄像头特性
  //                val cameraLensFacing = characteristics.get(CameraCharacteristics.LENS_FACING)
  //                Log.i("TAG", "lensFacing:${cameraLensFacing == CameraMetadata.LENS_FACING_FRONT}") // 摄像头方向
  //                val sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)
  //                Log.i(TAG, "orientation: $sensorOrientation") // 摄像头旋转角度
  //            }
        cameraSelector.addCameraFilter(DeviceCameraFilter("$cameraId"))
    } else {
        cameraSelector.requireLensFacing(lensFacing)
    }
    return cameraSelector.build()
}

摄像头被旋转了90度

这个问题花了我不少时间来解决,通过AI查询给出的答案是通过 Preview#setTargetRotation() 来实现,但是我设置之后却没有效果。我尝试设置 PreviewView 的 rotation ,结果只有视图组建发生了旋转,相机的图片依然没有发生改变。最后通过外网搜索stackoverflow终于找到了答案:其实还是通过 Preview#setTargetRotation() 来实现,只过不需将 PreviewView#implementationMode 设置成 PreviewView.ImplementationMode.COMPATIBLE,原因在CameraX官网给出了说明。

fun startCamera(){
    // 必要设置,否则摄像头旋转无效
    previewView.implementationMode = PreviewView.ImplementationMode.COMPATIBLE
    val preview = Preview.Builder()
        .setTargetRotation(Surface.ROTATION_270) // 旋转相机
        .build()
        .also { it.setSurfaceProvider(previewView.surfaceProvider) }
    // 其他代码
}

对于usb摄像头的链接访问

usb摄像头、特别是一些双目摄像头使用CameraX访问会出现展示成黑白的情况,推荐使用开源项目UVCCamera

参考链接

stackoverflow

CameraX

UVCCamera

posted @ 2025-07-31 14:39  huscarter  阅读(141)  评论(0)    收藏  举报