如何解决有没有办法裁剪Image / ImageProxy在传递给MLKit的分析器之前?
我正在将CameraX
的Analyzer用例与MLKit的BarcodeScanner
一起使用。我想裁剪从相机接收到的部分图像,然后再将其传递给扫描仪。
我现在正在做的是将ImageProxy
(我在分析器中收到的)转换为Bitmap
,将其裁剪然后传递给BarcodeScanner
。缺点是这不是一个非常快速和有效的过程。
运行此代码时,我还注意到我在Logcat中收到警告:
ML Kit检测到您似乎将相机镜框传递给了 检测器作为位图对象。这是低效的。请用 用于camera2 API的YUV_420_888格式(用于(旧版)相机的NV21格式) API,然后直接将字节数组传递给ML Kit。
最好不要进行ImageProxy
转换,但是如何裁剪要分析的矩形?
我已经尝试过的是设置cropRect
(imageProxy.image.cropRect)类的Image
字段,但这似乎并不影响最终结果。
解决方法
是的,如果您使用 ViewPort 并将视口设置为您的 UseCases(imageCapture or imageAnalysis as here https://developer.android.com/training/camerax/configuration),您只能获得有关裁剪矩形的信息,特别是如果您使用 ImageAnalysis(因为如果您使用 imageCapture,对于磁盘上的图像在保存之前被裁剪,它不适用于 ImageAnalysis,如果您使用 imageCapture 而不保存在磁盘上)和这里的解决方案我是如何解决这个问题的:
-
首先为用例设置视口,如下所示:https://developer.android.com/training/camerax/configuration
-
获取裁剪位图进行分析
override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null && mediaImage.format == ImageFormat.YUV_420_888) { croppedBitmap(mediaImage,imageProxy.cropRect).let { bitmap -> requestDetectInImage(InputImage.fromBitmap(bitmap,rotation)) .addOnCompleteListener { imageProxy.close() } } } else { imageProxy.close() } } private fun croppedBitmap(mediaImage: Image,cropRect: Rect): Bitmap { val yBuffer = mediaImage.planes[0].buffer // Y val vuBuffer = mediaImage.planes[2].buffer // VU val ySize = yBuffer.remaining() val vuSize = vuBuffer.remaining() val nv21 = ByteArray(ySize + vuSize) yBuffer.get(nv21,ySize) vuBuffer.get(nv21,ySize,vuSize) val yuvImage = YuvImage(nv21,ImageFormat.NV21,mediaImage.width,mediaImage.height,null) val outputStream = ByteArrayOutputStream() yuvImage.compressToJpeg(cropRect,100,outputStream) val imageBytes = outputStream.toByteArray() return BitmapFactory.decodeByteArray(imageBytes,imageBytes.size) }
可能会降低转换速度,但在我的设备上我没有注意到差异。我在compressToJpeg方法中设置了100的质量,但是mb如果设置的质量低可以提高速度,需要测试。
更新:21 年 5 月 2 日:
我找到了另一种方法,无需先转换为 jpeg,然后再转换为位图。这应该是一种更快的方式。
-
将视口设置为以前的。
-
将 YUV_420_888 转换为 NV21,然后进行裁剪和分析。
override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null && mediaImage.format == ImageFormat.YUV_420_888) { croppedNV21(mediaImage,imageProxy.cropRect).let { byteArray -> requestDetectInImage( InputImage.fromByteArray( byteArray,imageProxy.cropRect.width(),imageProxy.cropRect.height(),rotation,IMAGE_FORMAT_NV21,) ) .addOnCompleteListener { imageProxy.close() } } } else { imageProxy.close() } } private fun croppedNV21(mediaImage: Image,cropRect: Rect): ByteArray { val yBuffer = mediaImage.planes[0].buffer // Y val vuBuffer = mediaImage.planes[2].buffer // VU val ySize = yBuffer.remaining() val vuSize = vuBuffer.remaining() val nv21 = ByteArray(ySize + vuSize) yBuffer.get(nv21,vuSize) return cropByteArray(nv21,cropRect) } private fun cropByteArray(array: ByteArray,imageWidth: Int,cropRect: Rect): ByteArray { val croppedArray = ByteArray(cropRect.width() * cropRect.height()) var i = 0 array.forEachIndexed { index,byte -> val x = index % imageWidth val y = index / imageWidth if (cropRect.left <= x && x < cropRect.right && cropRect.top <= y && y < cropRect.bottom) { croppedArray[i] = byte i++ } } return croppedArray }
我从这里获得的第一个作物乐趣: Android: How to crop images using CameraX?
而且我还发现了另一种作物乐趣,似乎更复杂:
private fun cropByteArray(src: ByteArray,width: Int,height: Int,cropRect: Rect,): ByteArray {
val x = cropRect.left * 2 / 2
val y = cropRect.top * 2 / 2
val w = cropRect.width() * 2 / 2
val h = cropRect.height() * 2 / 2
val yUnit = w * h
val uv = yUnit / 2
val nData = ByteArray(yUnit + uv)
val uvIndexDst = w * h - y / 2 * w
val uvIndexSrc = width * height + x
var srcPos0 = y * width
var destPos0 = 0
var uvSrcPos0 = uvIndexSrc
var uvDestPos0 = uvIndexDst
for (i in y until y + h) {
System.arraycopy(src,srcPos0 + x,nData,destPos0,w) //y memory block copy
srcPos0 += width
destPos0 += w
if (i and 1 == 0) {
System.arraycopy(src,uvSrcPos0,uvDestPos0,w) //uv memory block copy
uvSrcPos0 += width
uvDestPos0 += w
}
}
return nData
}
我从这里获得的第二次收获乐趣: https://www.programmersought.com/article/75461140907/
如果有人能帮助改进代码,我会很高兴。
,您将使用 ImageProxy.SetCroprect 获取矩形,然后使用 CropRect 设置它。 例如,如果你有 imageProxy,你会做: ImageProxy.setCropRect(Rect) 然后你会做 ImageProxy.CropRect。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。