我正在尝试使用ML套件条形码检测器,camera2 API和Kotlin制作条形码扫描仪。关于camera2我是从谷歌样本开始camera2basic关于ML套件条码检测器,我从doc:Scan Barcodes with ML Kit on Android开始
在Camera2BasicFragment / createCameraPreviewSession方法中,我添加了
previewRequestBuilder.addTarget(imageReader!!.surface)
因此,每次图像可用时都会调用onImageAvailableListener。
在Camera2BasicFragment / setUpCameraOutputs方法中,我将ImageReader的ImageFormat.JPEG
更改为ImageFormat YUV420_888
,因此在onImageAvailableListener中,ImageReader提供了YUV图像
那么这是我的onImageAvailableListener:
private val onImageAvailableListener = ImageReader.OnImageAvailableListener {
val metadata = FirebaseVisionImageMetadata.Builder()
.setWidth(480) // 480x360 is typically sufficient for
.setHeight(360) // image recognition
.setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_YV12)
.setRotation(getRotationCompensation(cameraId, activity as Activity, context!!))
.build()
BarcodeReader(it.acquireNextImage(), detector, metadata, mListener).run()
}
在元数据中,'width'和'height'是在ML kit doc中建议的,'format'是YV12来处理YUV格式
和条形码阅读器是:
class BarcodeReader (private val image: Image,
private val detector: FirebaseVisionBarcodeDetector,
private val metadata: FirebaseVisionImageMetadata,
private val mListener: IBarcodeScanner) : Runnable {
override fun run() {
val visionImage = FirebaseVisionImage.fromByteBuffer(image.planes[0].buffer, metadata)
detector.detectInImage(visionImage)
.addOnSuccessListener { barcodes ->
// Task completed successfully
// [START_EXCLUDE]
// [START get_barcodes]
for (barcode in barcodes) {
val bounds = barcode.boundingBox
val corners = barcode.cornerPoints
val rawValue = barcode.rawValue
if (rawValue!=null)
mListener.onBarcode(rawValue)
}
// [END get_barcodes]
// [END_EXCLUDE]
}
.addOnFailureListener {
// Task failed with an exception
// ...
Log.d("barcode", "null")
}
image.close()
}
detector.detectInImage进入onSuccessListener但检测到NO BARCODE:barcodes
数组始终为空。
有人可以帮帮我吗?
您需要将所有三个平面的数据移交给FirebaseVisionImage.fromByteBuffer()函数。你的代码只是移交第一个(Y平面)。 YV12格式使用一个缓冲区(数组),其中包含Y数据,后跟U数据,后跟V数据。
Image包含3个独立的缓冲区,用于三个值(Y,U和V),但实际帧需要所有三个缓冲区(数组)。因此,您需要创建一个缓冲区并根据格式(YV12或NV21)以正确的顺序将三个平面的内容复制到其中,并移交该缓冲区(数组)。
请参阅this Wikipedia article以及此SO问题/答案,其中有关于YV12和NV21格式的转换和布局的更多信息。关于这些格式的另一个好消息来源是VideoLan wiki。
函数可能如下所示:
override fun onImageAvailable(reader: ImageReader) {
val image = imageReader?.acquireLatestImage() ?: return
val planes = image.planes
if (planes.size >= 3) {
val y = planes[0].buffer
val u = planes[1].buffer
val v = planes[2].buffer
val ly = y.remaining()
val lu = u.remaining()
val lv = v.remaining()
val dataYUV = ByteArray(ly + lu + lv)
y.get(dataYUV, 0, ly)
u.get(dataYUV, ly, lu)
v.get(dataYUV, ly + lu, lv)
val metadata = FirebaseVisionImageMetadata.Builder()
.setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_YV12)
.setWidth(width)
.setHeight(height)
.setRotation(rotation)
.build()
detector.detectInImage(FirebaseVisionImage.fromByteArray(dataYUV, metadata))
}
image.close()
}
宽度,高度和旋转取决于您的相机/预览和/或图像阅读器设置。