Mud*_*rif 5 android android-architecture-components android-jetpack kotlin-coroutines android-camerax
我正在尝试将 ProxyImage 从 cameraX 分析器转换为 Bitmap 以使用 Tensor Flow 光分析图像。所以我实现了cameraX Analyze回调,它将图像作为proxyImage。我需要转换为位图的那个 proxyImage。如果我在 UI 线程上进行此对话,则会导致相机预览滞后。所以我想用 Coroutines 来做这件事。现在的问题是,每当我将 proxyImage 传递给协程以将其转换为后台线程上的位图时,它就会因“图像已关闭”的 IllegalStateException 而崩溃。
08-04 16:28:59.690 16185-16185/com.example.camerax E/libEGL: call to OpenGL ES API with no current context (logged once per thread)
08-04 16:29:00.849 16185-16308/com.example.camerax E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
Process: com.example.camerax, PID: 16185
java.lang.IllegalStateException: Image is already closed
at android.media.Image.throwISEIfImageIsInvalid(Image.java:68)
at android.media.ImageReader$SurfaceImage$SurfacePlane.getBuffer(ImageReader.java:787)
at androidx.camera.core.AndroidImageProxy$PlaneProxy.getBuffer(AndroidImageProxy.java:141)
at com.example.camerax.MainActivity.getImageFromProxy(MainActivity.kt:216)
at com.example.camerax.MainActivity.convertProxyImageToBitmap(MainActivity.kt:150)
at com.example.camerax.MainActivity.access$convertProxyImageToBitmap(MainActivity.kt:38)
at com.example.camerax.MainActivity$startCamera$3$1.invokeSuspend(MainActivity.kt:136)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:233)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
Run Code Online (Sandbox Code Playgroud)
我认为下一帧是引用前一帧,而应用程序在后台线程中将 proxyImage 转换为位图。我阅读了文档,他们说
从该方法返回后,图像引用将关闭。因此,该方法应完成分析或复制,而不是将图像引用传递到分析方法之外。
我在这里很困惑,当我们将图像传递到分析方法之外时,制作图像的副本意味着什么。我如何处理这种情况。下面是代码片段。
val imageAnalysisConfig = ImageAnalysisConfig.Builder()
.setTargetResolution(Size(1280, 720))
.build()
val imageAnalysis = ImageAnalysis(imageAnalysisConfig)
imageAnalysis.setAnalyzer { image: ImageProxy, _: Int ->
classifier = Classifier.create(this, Classifier.Model.FLOAT, Classifier.Device.CPU, 1)
CoroutineScope(Default).launch {
convertProxyImageToBitmap(image)
}
}
Run Code Online (Sandbox Code Playgroud)
将 proxyImage 转换为 Bitmap 的方法。
private fun getImageFromProxy(image: ImageProxy): Bitmap {
val yBuffer = image.planes[0].buffer // Y
val uBuffer = image.planes[1].buffer // U
val vBuffer = image.planes[2].buffer // V
val ySize = yBuffer.remaining()
val uSize = uBuffer.remaining()
val vSize = vBuffer.remaining()
val nv21 = ByteArray(ySize + uSize + vSize)
//U and V are swapped
yBuffer.get(nv21, 0, ySize)
vBuffer.get(nv21, ySize, vSize)
uBuffer.get(nv21, ySize + vSize, uSize)
val yuvImage = YuvImage(nv21, ImageFormat.NV21, image.width, image.height, null)
val out = ByteArrayOutputStream()
yuvImage.compressToJpeg(Rect(0, 0, yuvImage.width, yuvImage.height), 100, out)
val imageBytes = out.toByteArray()
return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
}
Run Code Online (Sandbox Code Playgroud)
预先感谢您的帮助。
我最近遇到了这个问题。如果我是正确的,则意味着您正在对imageafterimage.close()已被调用的内容执行某些操作。在您的情况下,它可能会在 内 lambda 块的末尾自动调用setAnalyzer { },但是当发生这种情况时,您仍然在使用协程进行一些异步工作。
您需要完全删除协程或将其包装在一个runBlocking { }块中以等待其完成,因为否则错误将不会消失(至少这就是我最近解决它的方法)。如果您将背压策略设置为STRATEGY_KEEP_ONLY_LATEST,理论上您可以在执行图像分析的代码块中花费所有您想要的时间,因为相机当前生成的帧将被丢弃,而不是不必要的等待。
如果与此同时您已更新项目以使用最新的 CameraX 版本并且正在使用专用类,请记住image.close()每次从重写的方法返回时都要调用自己analyze(),否则屏幕上的相机预览将永远冻结。
我很困惑,当我们将图像传递到分析方法之外时,复制图像意味着什么。我该如何处理这种情况。
我认为在这种情况下,您需要制作 的深层副本,image这意味着您创建一个新实例并将其所有内容和内部状态设置为原始实例的内容和内部状态,而不仅仅是对引用进行简单的分配。
| 归档时间: |
|
| 查看次数: |
2249 次 |
| 最近记录: |