使用onPreviewFrame运行ML模型

时间:2020-02-28 13:57:10

标签: java android android-camera

所以我正在使用旧版Camera API(据我所知)来获取PreviewFrame回调,然后运行我拥有的一些机器学习模型。我已经确认,当我通过onPictureTaken回调进行拍照时,如果给定了解码的位图,则机器学习模型可以工作。现在,在下面的示例中,我只是在ML Kit的条形码扫描器上作为基本案例进行了测试,但是我的自定义模型似乎也可以与onPictureTaken回调一起正常工作。

从我收集到的信息来看,使用onPreviewFrame不一定是实现此目的的最佳方法,但是为了获得快速的示例游戏(和学习经验),我决定走这条路。基于我从其他拥有在线解决方案的人那里尝试过的一切,我似乎无法正常工作。以下代码返回null:

@Override
public void onPreviewFrame(byte[] data, Camera camera) {
//        Log.d("onPreviewFrame bytes.length", String.valueOf(bytes.length));
//        final Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
//        Log.d("onPreviewFrame bmp.getHeight()", String.valueOf(bmp.getHeight()));

    Camera.Parameters parameters = camera.getParameters();
    int width = parameters.getPreviewSize().width;
    int height = parameters.getPreviewSize().height;


    Log.d("onPreviewFrame - width", String.valueOf(width));
    Log.d("onPreviewFrame - height", String.valueOf(height));
    Log.d("onPreviewFrame - parameters.getPreviewFormat()", String.valueOf(parameters.getPreviewFormat()));

    YuvImage yuv = new YuvImage(data, parameters.getPreviewFormat(), width, height, null);

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out);

//
//        byte[] bytes = out.toByteArray();
//        final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);


    byte[] bytes = yuv.getYuvData();
    final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

    extractBarcode(FirebaseVisionImage.fromBitmap(bitmap), bitmap);
}

这是我尝试过的其他东西:

@Override
public void onPreviewFrame(byte[] data, Camera camera) {
//        Log.d("onPreviewFrame bytes.length", String.valueOf(bytes.length));
//        final Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
//        Log.d("onPreviewFrame bmp.getHeight()", String.valueOf(bmp.getHeight()));

    Camera.Parameters parameters = camera.getParameters();
    int width = parameters.getPreviewSize().width;
    int height = parameters.getPreviewSize().height;


    Log.d("onPreviewFrame - width", String.valueOf(width));
    Log.d("onPreviewFrame - height", String.valueOf(height));

    YuvImage yuv = new YuvImage(data, parameters.getPreviewFormat(), width, height, null);

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out);


    byte[] bytes = out.toByteArray();
    final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

    extractBarcode(FirebaseVisionImage.fromBitmap(bitmap), bitmap);
}

不幸的是我遇到了这个错误:

ML Kit已检测到您似乎将相机帧作为位图对象传递给检测器。这是低效的。对于Camera2 API,请使用YUV_420_888格式;对于(旧版)相机API,请使用NV21格式,然后直接将字节数组传递给ML Kit。

使用parameters.getPreviewFormat()返回17,即NV21。我还尝试通过简单地将其更改为ImageFormat.YUV_420_888,但这导致以下非法参数异常:

目前仅支持ImageFormat.NV21和ImageFormat.YUY2

2 个答案:

答案 0 :(得分:3)

请尝试使用CameraX,而不要使用Camera API。它易于使用,并且只要从相机接收到一帧就可以执行代码。在尝试将ML模型与相机集成时,我遇到了类似的错误,然后转向CameraX。

基本上,我们将创建一个ImageAnalysis.Analyser类,通过该类将获得Image对象(frames)。使用扩展功能,我们会将这个Image对象转换为YuvImage

您可以按照此codelab使用CameraX分析帧。您将创建一个扩展ImageAnalysis.Analyser类的类。

class FrameAnalyser() : ImageAnalysis.Analyzer {

    override fun analyze(image: ImageProxy?, rotationDegrees: Int) {
        val yuvImage = image?.image?.toYuv() // The extension function
    }
}

创建扩展功能,将Image转换为YuvImage

private fun Image.toYuv(): YuvImage {
    val yBuffer = planes[0].buffer
    val uBuffer = planes[1].buffer
    val vBuffer = planes[2].buffer
    val ySize = yBuffer.remaining()
    val uSize = uBuffer.remaining()
    val vSize = vBuffer.remaining()
    val nv21 = ByteArray(ySize + uSize + vSize)
    yBuffer.get(nv21, 0, ySize)
    vBuffer.get(nv21, ySize, vSize)
    uBuffer.get(nv21, ySize + vSize, uSize)
    val yuvImage = YuvImage(nv21, ImageFormat.NV21, this.width, this.height, null)
    return yuvImage
}

您可以根据需要更改YUV图像格式。请参阅这些docs

答案 1 :(得分:0)

表示直接传递FirebaseVisionImage

extractBarcode(FirebaseVisionImage.fromBitmap(bitmap), bitmap);

您可以这样做

var bitmap = toARGBBitmap(ocrBitmap)
  extractBarcode(FirebaseVisionImage.fromBitmap(bitmap), bitmap);

 private fun toARGBBitmap(img: Bitmap): Bitmap {
        return img.copy(Bitmap.Config.ARGB_8888, true)
    }

您可以尝试以下方法:)