使用 onPreviewFrame 到 运行 个 ML 模型
Using onPreviewFrame To run ML models
所以我正在使用旧相机 API(据我所知)来获取 previewFrame 回调,然后 运行 我拥有的一些机器学习模型。我已经确认,当我通过 onPictureTaken 回调拍摄照片时,如果给定解码的位图,机器学习模型就可以工作。现在在下面的示例中,我只是简单地在 ML Kit 的条形码扫描仪上作为基本案例进行测试,但我的自定义模型似乎也可以与 onPictureTaken 回调一起正常工作。
根据我收集到的信息,使用 onPreviewFrame 不一定是执行此操作的最佳方法,但为了获得快速示例游戏(和学习经验),我决定只走这条路。基于我从其他在线解决方案中尝试过的一切,我似乎无法让任何东西正常工作。下面的代码 returns 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 has detected that you seem to pass camera frames to the detector as a Bitmap object. This is inefficient. Please use YUV_420_888 format for camera2 API or NV21 format for (legacy) camera API and directly pass down the byte array to ML Kit.
with parameters.getPreviewFormat() 返回 17,即 NV21。我也尝试简单地将其更改为 ImageFormat。YUV_420_888 但这导致了以下非法参数异常:
only support ImageFormat.NV21 and ImageFormat.YUY2 for now
不要使用相机 API,请尝试使用 CameraX。它易于使用,只要从相机接收到帧,您就可以执行代码。在尝试将 ML 模型与相机集成时,我遇到了类似的错误,然后转向 CameraX。
Basically, we'll create an ImageAnalysis.Analyser
class through which we would get the Image
object ( frames ). Using an extension function, we will convert this Image
object to a YuvImage
.
您可以按照此codelab使用CameraX分析帧。您将创建一个扩展 ImageAnalysis.Analyser
class.
的 class
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.
代替直接传递 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)
}
你可以试试这个:)
所以我正在使用旧相机 API(据我所知)来获取 previewFrame 回调,然后 运行 我拥有的一些机器学习模型。我已经确认,当我通过 onPictureTaken 回调拍摄照片时,如果给定解码的位图,机器学习模型就可以工作。现在在下面的示例中,我只是简单地在 ML Kit 的条形码扫描仪上作为基本案例进行测试,但我的自定义模型似乎也可以与 onPictureTaken 回调一起正常工作。
根据我收集到的信息,使用 onPreviewFrame 不一定是执行此操作的最佳方法,但为了获得快速示例游戏(和学习经验),我决定只走这条路。基于我从其他在线解决方案中尝试过的一切,我似乎无法让任何东西正常工作。下面的代码 returns 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 has detected that you seem to pass camera frames to the detector as a Bitmap object. This is inefficient. Please use YUV_420_888 format for camera2 API or NV21 format for (legacy) camera API and directly pass down the byte array to ML Kit.
with parameters.getPreviewFormat() 返回 17,即 NV21。我也尝试简单地将其更改为 ImageFormat。YUV_420_888 但这导致了以下非法参数异常:
only support ImageFormat.NV21 and ImageFormat.YUY2 for now
不要使用相机 API,请尝试使用 CameraX。它易于使用,只要从相机接收到帧,您就可以执行代码。在尝试将 ML 模型与相机集成时,我遇到了类似的错误,然后转向 CameraX。
Basically, we'll create an
ImageAnalysis.Analyser
class through which we would get theImage
object ( frames ). Using an extension function, we will convert thisImage
object to aYuvImage
.
您可以按照此codelab使用CameraX分析帧。您将创建一个扩展 ImageAnalysis.Analyser
class.
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.
代替直接传递 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)
}
你可以试试这个:)