摘要/简而言之:机器学习对象检测应用程序无法检测对象,因为图像未被analyze()
方法获取。
背景
我目前正在使用CameraX和Google ML Kit开发一款基于Java的移动应用程序。该应用程序的目的是通过实时摄像头预览来检测对象。我使用标题为“在Android上使用ML Kit检测和跟踪对象”的指南(基础模型选项)实现了ML Kit,以在应用程序中的连续帧内检测对象。
然而,在运行应用程序时,它在我的设备上启动,摄像头预览继续工作,但应用程序并未执行其预期的效果,即实际检测对象并在我的屏幕上显示。为了尝试解决这个问题,我找到了这个StackOverflow答案,它与这个问题非常相似。令我失望的是,该用户使用自定义模型(tflite)构建了他们的应用程序。这与我的不同,因为我使用的是基础模型。根据我的研究,这使用了ML Kit的设备内对象检测。所应用的代码仅限于上述文档中存在的代码。由于我的IDE(Android Studio)在语法上没有显示任何错误,我不确定为什么我的应用程序中似乎没有任何对象检测。以下是使用过的必要代码:
代码
public class MainActivity extends AppCompatActivity { private ListenableFuture<ProcessCameraProvider> cameraProviderFuture; private class YourAnalyzer implements ImageAnalysis.Analyzer { @Override @ExperimentalGetImage public void analyze(ImageProxy imageProxy) { Image mediaImage = imageProxy.getImage(); if (mediaImage != null) { InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees()); //将图像传递给ML Kit Vision API //... ObjectDetectorOptions options = new ObjectDetectorOptions.Builder() .setDetectorMode(ObjectDetectorOptions.STREAM_MODE) .enableClassification() // 可选 .build(); ObjectDetector objectDetector = ObjectDetection.getClient(options); objectDetector.process(image) .addOnSuccessListener( new OnSuccessListener<List<DetectedObject>>() { @Override public void onSuccess(List<DetectedObject> detectedObjects) { Log.d("TAG", "onSuccess" + detectedObjects.size()); for (DetectedObject detectedObject : detectedObjects) { Rect boundingBox = detectedObject.getBoundingBox(); Integer trackingId = detectedObject.getTrackingId(); for (DetectedObject.Label label : detectedObject.getLabels()) { String text = label.getText(); if (PredefinedCategory.FOOD.equals(text)) { } int index = label.getIndex(); if (PredefinedCategory.FOOD_INDEX == index) { } float confidence = label.getConfidence(); } } imageProxy.close(); } } ) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Log.d("TAG", "onFailure" + e); imageProxy.close(); } } ); } } } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); cameraProviderFuture = ProcessCameraProvider.getInstance(this); PreviewView previewView = findViewById(R.id.previewView); cameraProviderFuture.addListener(() -> { try { ProcessCameraProvider cameraProvider = cameraProviderFuture.get(); bindPreview(cameraProvider); } catch (ExecutionException | InterruptedException e) {} }, ContextCompat.getMainExecutor(this)); } void bindPreview(@NonNull ProcessCameraProvider cameraProvider) { PreviewView previewView = findViewById(R.id.previewView); Preview preview = new Preview.Builder() .build(); CameraSelector cameraSelector = new CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_BACK) .build(); preview.setSurfaceProvider(previewView.getSurfaceProvider()); ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() .setTargetResolution(new Size(1280,720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build(); imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(this), new YourAnalyzer()); Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner)this, cameraSelector, preview, imageAnalysis); }}
最终目标
如果需要任何视觉示例来理解预期效果应该是什么样,请看下面的图片。
更新 [2021年4月11日]: 在我尝试通过Log.d(..)
调试OnSuccess
方法以确定返回对象列表的大小时,AS控制台在运行应用程序的几秒钟内打印了D/TAG: onSuccess0
多达30次。这是否意味着应用程序没有检测到任何对象?自从我完全按照文档操作以来,这一直困扰着我。
更新 [2021年5月1日]: 从onSuccess
方法中删除了DetectedObject[] results = new DetectedObject[0];
这行代码。
for (DetectedObject detectedObject : results)
现在使用”detectedObjects”而不是”results”,以反映文档中的代码。然而,onSuccess
仍然记录D/TAG: onSuccess0
,这进一步增加了关于为什么方法未获取任何数据的问题。
回答:
根据@Steven提供的这个简化的Google ML Kit示例应用程序版本,我通过实现lambda表达式并简化代码解决了这个问题,如下所示;
objectDetector.process(image) .addOnSuccessListener(detectedObjects -> { Log.d("TAG", "onSuccess" + detectedObjects.size()); }) .addOnFailureListener(e -> Log.e("TAG", e.getLocalizedMessage())) .addOnCompleteListener(result -> imageProxy.close());
在进行此更改后运行程序,应用程序成功启动,我的logcat打印出D/TAG: onSuccess1
,表明确实检测到了一个对象!
然而,我确实想补充一点,这种微妙的代码编写差异让我想知道究竟是什么造成了这种差异。如果有人能解释为什么这段代码有效,而我之前发布的代码无效,我将非常感激这个解释。