You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在ARFragment中用ARCore Camera捕获无AR对象的图像?

嘿,我之前在做AR项目时也碰到过完全一样的问题——ARFragment接管相机后CameraX的分析器直接罢工,而且还得拿到不带3D对象的纯相机帧做目标检测。给你几个亲测有效的解决方案:

方案1:直接用ARCore的原始帧捕获(最推荐)

ARFragment本身依赖ARCore,而ARCore在渲染3D对象之前就会获取原始相机帧。我们可以直接监听ARSession的帧回调,拿到完全干净的相机图像,完全不会和ARFragment的相机逻辑冲突。

代码示例(Kotlin)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    // 监听ARSession的帧更新
    arFragment.arSceneView.session?.addFrameCallback(object : Frame.Callback {
        override fun onFrame(frame: Frame) {
            // 获取未渲染AR对象的原始相机图像
            val cameraImage = frame.acquireCameraImage()
            cameraImage?.use { image ->
                // 转成Bitmap用于目标检测(这里可以根据你的需求调整格式)
                val bitmap = image.toBitmap()
                
                // 在这里执行你的目标检测逻辑
                runObjectDetection(bitmap)
            }
        }
    })
}

// 辅助扩展函数:将ARCore CameraImage转成Bitmap
private fun CameraImage.toBitmap(): Bitmap {
    val yBuffer = planes[0].buffer // Y通道数据
    val uBuffer = planes[1].buffer // U通道数据
    val vBuffer = planes[2].buffer // V通道数据
    
    val ySize = yBuffer.remaining()
    val uSize = uBuffer.remaining()
    val vSize = vBuffer.remaining()
    
    val nv21 = ByteArray(ySize + uSize + vSize)
    yBuffer.get(nv21, 0, ySize)
    vBuffer.get(nv21, ySize, vSize)
    uBuffer.get(nv21, ySize + vSize, uSize)
    
    val yuvImage = YuvImage(nv21, ImageFormat.NV21, width, height, null)
    val outputStream = ByteArrayOutputStream()
    yuvImage.compressToJpeg(Rect(0, 0, width, height), 100, outputStream)
    return BitmapFactory.decodeByteArray(outputStream.toByteArray(), 0, outputStream.size())
}

为什么这个方案最好?

  • 完全避开CameraX和ARFragment的相机冲突问题,用的是ARCore原生的帧数据
  • 拿到的是未经过AR渲染的原始相机帧,绝对没有3D对象干扰
  • 性能更优,不需要额外的相机绑定逻辑
方案2:让CameraX与ARFragment共享ARCore相机

如果你坚持要用CameraX的分析器,可以配置CameraX使用ARCore提供的相机,实现两者共享相机资源,这样setAnalyzer就不会失效了。

步骤1:添加依赖

确保build.gradle里有这些依赖(版本可以根据你的项目调整):

implementation "androidx.camera:camera-core:1.3.0"
implementation "androidx.camera:camera-camera2:1.3.0"
implementation "com.google.ar:core:1.42.0"
implementation "androidx.camera:camera-extension:1.3.0"

步骤2:配置CameraX使用ARCore相机

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
    cameraProviderFuture.addListener({
        val cameraProvider = cameraProviderFuture.get()
        
        // 配置只选择支持AR的相机
        val arCameraSelector = CameraSelector.Builder()
            .addCameraFilter { availableCameras ->
                availableCameras.filter { cameraInfo ->
                    ArCameraProvider.isArCamera(requireContext(), cameraInfo)
                }
            }
            .build()
        
        // 配置图像分析用例(你的目标检测分析器)
        val imageAnalysis = ImageAnalysis.Builder()
            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
            .build()
            .also {
                it.setAnalyzer(Executors.newSingleThreadExecutor(), YourObjectDetectionAnalyzer())
            }
        
        // 绑定相机和用例,同时ARFragment会共享这个相机
        try {
            cameraProvider.unbindAll()
            cameraProvider.bindToLifecycle(
                viewLifecycleOwner,
                arCameraSelector,
                imageAnalysis
            )
        } catch (e: Exception) {
            Log.e("ARCameraX", "相机绑定失败", e)
        }
    }, ContextCompat.getMainExecutor(requireContext()))
}
方案3:临时禁用AR渲染捕获单帧

如果只是偶尔需要捕获一张无AR对象的图像,可以临时暂停AR渲染,然后获取当前帧:

// 捕获按钮点击事件
captureBtn.setOnClickListener {
    val sceneView = arFragment.arSceneView
    
    // 暂停AR渲染,避免3D对象被捕获
    sceneView.pauseRenderer()
    
    // 获取当前原始帧
    sceneView.session?.currentFrame?.let { frame ->
        frame.acquireCameraImage()?.use { image ->
            val bitmap = image.toBitmap()
            // 保存或处理图像
            saveBitmapToGallery(bitmap)
        }
    }
    
    // 恢复AR渲染
    sceneView.resumeRenderer()
}

重要注意事项

  • 调用acquireCameraImage()后一定要调用close()(或者用Kotlin的use语法),否则会造成内存泄漏
  • 目标检测逻辑一定要放在后台线程执行,避免阻塞AR渲染的主线程
  • 如果是Java项目,只需把Kotlin代码转成对应的Java语法即可

内容的提问来源于stack exchange,提问作者MrRobot9

火山引擎 最新活动