ML сканер штрих-кода из потока предварительного просмотра камеры?
Я пытаюсь сделать сканер штрих-кода, используя комплект ML Детектор штрих-кода, API Camera2 и Kotlin. Относительно камеры2 я начинаю с Google Пример camera2basic Относительно ML kit Детектор штрих-кода я начинаю с doc: Сканирование штрих-кодов с помощью ML Kit на Android
В методе Camera2BasicFragment / createCameraPreviewSession я добавил
previewRequestBuilder.addTarget(imageReader!!.surface)
поэтому onImageAvailableListener вызывается каждый раз, когда изображение доступно.
В методе Camera2BasicFragment / setUpCameraOutputs я изменил ImageReader's 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()
}
В метаданных "ширина" и "высота" соответствуют указанным в документе ML ML, "формат" - это YV12 для обработки формата YUV.
и Barcode Reader является:
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, но НЕТ штрихового кода: barcodes
массив всегда пуст.
Может кто-нибудь помочь мне, пожалуйста?
1 ответ
Вам необходимо передать данные всех трех плоскостей в функцию FirebaseVisionImage.fromByteBuffer(). Ваш код просто передает первый (Y-самолет). Формат YV12 использует один буфер (массив), который содержит Y-данные, за которыми следуют U-данные, а затем V-данные.
Изображение содержит 3 отдельных буфера для трех значений (Y, U и V), однако для реального кадра нужны все три в одном буфере (массиве). Таким образом, вам нужно создать один буфер и скопировать в него содержимое трех плоскостей в правильном порядке в соответствии с форматом (YV12 или NV21) и передать этот буфер (массив).
См. Эту статью в Википедии, а также этот вопрос / ответ, в котором содержится дополнительная информация о преобразовании и компоновке формата 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()
}
Где ширина, высота и вращение зависят от настроек вашей камеры / предварительного просмотра и / или устройства чтения изображений.
С участием ImageReader.OnImageAvailableListener
, вы можете просто использовать FirebaseVisionImage#fromMediaImage(изображение изображения, вращение int) дляImageFormat YUV420_888
Как в документах:
Обратите внимание, что на данный момент мы поддерживаем только форматы JPEG / YUV_420_888. Если вы используете детекторы облачного зрения, рекомендуется формат JPEG; если вы используете детекторы на устройстве, YUV_420_888 будет более эффективным.
Настроить ImageReader
:
mImageReader = ImageReader.newInstance(mVideoSize!!.width,
mVideoSize!!.height,
ImageFormat.YUV_420_888, 3)
mImageReader!!.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler)
тогда:
private val mOnImageAvailableListener = ImageReader.OnImageAvailableListener { imageReader ->
val image = imageReader.acquireLatestImage()
try {
mFaceDetector!!
.detectInImage(
FirebaseVisionImage
.fromMediaImage(image))
.addOnSuccessListener { firebaseVisionFaces ->
if (firebaseVisionFaces.size > 0) {
Log.d(TAG, "onSuccess: FACE DETECTED")
}
}
image.close()
} catch (e: NullPointerException) {
Log.e(TAG, "onImageAvailable: Invalid image provided for detection", e)
}
}
Примечание: я использовал распознавание лиц, штрих-код можно использовать аналогичным образом.