【问题标题】:Using onPreviewFrame To run ML models使用 onPreviewFrame 运行 ML 模型
【发布时间】:2020-02-28 13:57:10
【问题描述】:

所以我正在使用旧的 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 检测到您似乎将相机帧作为位图对象传递给检测器。这是低效的。请使用 YUV_420_888 格式的 camera2 API 或 NV21 格式的 (legacy) camera API 并将字节数组直接传递给 ML Kit。

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

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

【问题讨论】:

    标签: java android android-camera


    【解决方案1】:

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

    基本上,我们将创建一个ImageAnalysis.Analyser 类,通过它我们可以获得Image 对象(框架)。使用扩展函数,我们将这个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

    【讨论】:

    • 有趣。给我几个小时让这件事继续下去,我会让你知道会发生什么。谢谢舒巴姆:D
    • 几个小时变成了几天(其他事情出现了)。所以我仍然遇到同样的问题+更多。所以现在为了可视化,我也将帧保存到本地存储,图像全部失真。此外,ML Kit 警告仍在发生。在分析时,我执行 imageproxy.getImage 然后将其转换为位图(具有 NV21 格式),然后在本地保存并使用 ML Kit 提取条形码。奇怪的是,虽然 ML Kit 的 OCR 似乎与图像完美结合.....
    【解决方案2】:

    直接传递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)
        }
    

    你可以试试这个:)

    【讨论】:

      猜你喜欢
      • 2023-01-25
      • 2020-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-23
      • 2018-11-11
      相关资源
      最近更新 更多