Ole*_*sov 16 android android-image android-camera2 android-camerax google-mlkit
我将CameraX的分析器用例与 MLKit 的BarcodeScanner. 我想先裁剪从相机接收到的图像的一部分,然后再将其传递到扫描仪。
我现在正在做的是将ImageProxy(我在分析器中收到的)转换为Bitmap,裁剪它,然后将其传递给BarcodeScanner. 缺点是这不是一个非常快速和有效的过程。
我还注意到运行此代码时 Logcat 中收到的警告:
ML Kit 检测到您似乎将相机帧作为位图对象传递给检测器。这是低效的。请对camera2 API使用YUV_420_888格式,对(旧)相机API使用NV21格式,并将字节数组直接传递给ML Kit。
不进行转换固然很好ImageProxy,但是如何裁剪我想要分析的矩形呢?
我已经尝试过设置(imageProxy.image.cropRect) 类cropRect的字段Image,但它似乎并不影响最终结果。
是的,确实,如果您使用 ViewPort 并将视口设置为您的 UseCases(imageCapture 或 imageAnalysis,如此处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, 0, 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, 0, imageBytes.size)
}
Run Code Online (Sandbox Code Playgroud)
转换速度可能会有所损失,但在我的设备上我没有注意到差异。我在 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, 0, ySize)
vuBuffer.get(nv21, ySize, vuSize)
return cropByteArray(nv21, mediaImage.width, 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
}
Run Code Online (Sandbox Code Playgroud)
我从这里获得的第一个裁剪乐趣: Android:如何使用 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, nData, uvDestPos0, w) //uv memory block copy
uvSrcPos0 += width
uvDestPos0 += w
}
}
return nData
}
Run Code Online (Sandbox Code Playgroud)
我从这里获得的第二批乐趣: https ://www.programmersought.com/article/75461140907/
如果有人可以帮助改进代码,我会很高兴。
小智 -5
您可以使用 ImageProxy.SetCroprect 来获取矩形,然后使用 CropRect 来设置它。例如,如果您有 imageProxy,您将执行以下操作:ImageProxy.setCropRect(Rect),然后执行 ImageProxy.CropRect。
| 归档时间: |
|
| 查看次数: |
11247 次 |
| 最近记录: |