【问题标题】:How to properly implement camera2 realtime frame processing using RxJava?如何使用 RxJava 正确实现 camera2 实时帧处理?
【发布时间】:2018-11-06 11:53:22
【问题描述】:

我正在camera2上制作反应式包装器,我的目标是获取每一帧,然后传递给人脸识别。

所以,我在 setOnImageAvailableListener 上创建了一个包装方法

  fun createOnImageAvailableFlowable(imageReader: ImageReader, handler: Handler): Flowable<ImageReader> {
        return Flowable.create({ subscriber ->
            imageReader.setOnImageAvailableListener({
                if (!subscriber.isCancelled)
                    subscriber.onNext(it)
            }, handler)

            subscriber.setCancellable {
                imageReader.setOnImageAvailableListener(null, null)
            }
        }, BackpressureStrategy.LATEST)
    }

反应链如下所示:

 createOnImageAvailableFlowable(imageReader!!, null)
 .concatMap {
     it.acquireLatestImage()?.use { image ->
        val rotation = ReactiveCamera.getRotationCompensation(cameraId!!, this, applicationContext)
        val visionImage = FirebaseVisionImage.fromMediaImage(image, rotation)
        firebaseFaceDetector
          .detectInImage(visionImage)
          .toFlowable(BackpressureStrategy.LATEST)
          .map { list ->Optional(list)}
     } ?: Flowable.just(Optional(null))
 }
 ...

此代码有效,但由于所有工作都在主线程中执行,因此会导致预览表面出现一些滞后。这需要在单独的线程中执行。我天真的解决方案是在 concatMap 之前添加 observeOn 运算符:

createOnImageAvailableFlowable(imageReader!!, null)
.observeOn(Schedulers.io()) // doesn't switch thread
.concatMap {
 // still main thread
}
...

但这并不影响,所有工作仍在主线程中。如果我指定 concatMapEager 而不是 concatMap,则所有工作都在单独的线程中按预期工作,但帧会带来很大的延迟。

我做错了什么?在这种情况下,如何指示反应流在单独的线程中执行?在实时帧处理的情况下如何处理背压?

更新

我按照 Kiskae 的建议提供了自己的线程,但是现在,调度程序的线程中只发生了第一次发射,而其余的发射仍然在主线程中:

createOnImageAvailableFlowable(imageReader!!, null)
.subscribeOn(AndroidSchedulers.from(nonMainThread.looper))
.concatMap {
   val t = Thread.currentThread()
   val name = t.name
   Log.d(TAG, "current thread {$name}")
 ...
}

输出:

D/MainActivity: current thread {Camera2}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}
D/MainActivity: current thread {main}

【问题讨论】:

    标签: android concurrency rx-java rx-java2 android-camera2


    【解决方案1】:

    ImageReader.setOnImageAvailableListener的文档:

    Handler:应该在其上调用侦听器的处理程序,如果应该在调用线程的 Looper 上调用侦听器,则为 null。

    由于您在主循环器上订阅,它最终会使用主循环器设置回调,这会导致 concatMap 之前的所有处理始终发生在应用程序线程上。

    您可以通过提供处理程序而不是 null 或调用 subscribeOn 并提供基于处理程序的调度程序(如 RxAndroid 的 HandlerScheduler)来解决此问题。

    【讨论】:

    • 问题依旧,concatMap里面的代码在提供的线程中只执行了一次,然后切换到main。请查看更新后的问题
    • 这可能意味着默认的Looper 绑定到ImageReader 实例。您需要将createOnImageAvailableFlowable 调用的第二个参数替换为适当的处理程序,以确保在该处理程序上调用了侦听器。
    • 感谢您的回复,我的方法 firebaseFaceDetector.detectInImage(visionImage) 中的问题是,它是 firebase 的 detectInImage(visionImage).addOnSuccessListener{...} 的反应式包装器。此方法在不同线程中完成所有计算工作,然后自动在主线程上调用 addOnSuccessListener。这就是 concatMap 在主线程中被调用的原因。你有什么建议吗?
    猜你喜欢
    • 2017-07-15
    • 2015-06-30
    • 2013-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-03
    • 2011-10-08
    相关资源
    最近更新 更多