【问题标题】:Why are images not being acquired by the analyze() method?为什么分析()方法没有获取图像?
【发布时间】:2023-04-06 23:21:02
【问题描述】:

总结/TL;DR:ML 对象检测应用程序无法检测对象,因为图像不是通过 analyze() 方法获取的。



背景

我目前正在开发一个使用 Java 编写的 CameraX 和 Google ML Kit 的移动应用程序。该应用程序的目的是通过实时相机预览检测对象。我使用这个名为"Detect and track objects with ML Kit on Android"(基本模型选项)的指南实现了 ML Kit,以检测应用程序内连续帧中的对象。

但是,在运行该应用程序时,它会在我的设备上启动,并且相机预览会继续工作,但该应用程序并没有实现其实际检测对象并将其显示在我的屏幕上的预期效果。为了尝试解决这个问题,我发现this StackOverflow answer 与这个问题非常相似。令我沮丧的是,用户使用自定义模型 (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());
                //Pass image to an ML Kit Vision API
                //...

                ObjectDetectorOptions options =
                        new ObjectDetectorOptions.Builder()
                                .setDetectorMode(ObjectDetectorOptions.STREAM_MODE)
                                .enableClassification()  // Optional
                                .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 控制台打印了 @987654329 @ 在运行应用程序的几秒钟内最多 30 次。这是否意味着应用程序没有检测到任何对象?自从我完全遵循文档以来,这一直困扰着我。

更新 [2021 年 5 月 1 日]:DetectedObject[] results = new DetectedObject[0]; 行已从 onSuccess 方法中删除。

for (DetectedObject detectedObject : results) 现在使用“detectedObjects”而不是“results”来反映文档中存在的代码。然而,onSuccess 仍在记录D/TAG: onSuccess0,这进一步增加了为什么该方法没有获取任何数据的问题。

【问题讨论】:

    标签: java android-studio machine-learning android-camerax google-mlkit


    【解决方案1】:

    根据@Steven 提供的this minimalized version of the Google ML Kit sample app,我能够通过实现一个 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,表示确实检测到了一个对象!

    但是,我确实想补充一点,编写代码时如此细微的差异让我想知道究竟是什么差异造成的。如果有人能澄清为什么这段代码与我在 OP 中发布的代码相反,我将不胜感激。

    【讨论】:

      【解决方案2】:

      正如您链接的the other StackOverflow question 中所述,您需要绑定分析用例才能使其工作。

      帮助调试的一个提示是,您可以在 onSuccess 中添加一些 Log.d(..) 以检查返回对象列表的大小,并在 onFailure 中添加一些异常以打印出异常。然后在运行时,您可以使用 adb logcat 或 AS 日志选项卡来检查信息以确保运行正常。

      您可以进行的另一项改进是,您无需每帧创建一个新的对象检测器。您可以在分析方法之外创建一个并重用它。

      【讨论】:

      • 所以我尝试将分析用例完全绑定到我链接的 StackOverflow 问题的答案中,只是为了运行并让它运行但仍然无法正常工作。会有什么原因吗?另外,我怎么能Log.d(..)onSuccessonFailure?在阅读了您的答案后,我在过去的两天里一直在尝试这样做。我应该补充一点,我对编码还很陌生,所以任何指导都意味着世界。
      • 在您的代码中,您已经实现了 onSuccess 和 onFailure 方法。你可以调用 Log.d("TAG", "onSuccess " + detectedObjects.size());在 onSuccess 方法的第一行和 Log.d("TAG", "onFailure " + e);在 onFailure 方法的第一行。以下是关于 Log 的文档:developer.android.com/reference/android/util/… 要查看日志,请打开终端并在运行应用程序时运行“adb logcat -s "TAG"。有关 logcat 的更多信息:developer.android.com/studio/command-line/logcat
      • Log.d(..) in OnSuccess 打印出D/TAG: onSuccess0onFailure 在 logcat 中不打印任何内容。这意味着没有打印返回对象列表的大小,对吗?这意味着 ML Kit 一定不能检测到任何对象。
      • 感谢您查看日志。这意味着对象检测器中没有致命错误。你看到很多这样的日志吗?如果只有几个或只有一个,则可能是图像未正确关闭()因此被阻止。在 setAnalyzer 调用中,我看到您正在使用 mainExecutor。你能试试别的吗,例如一个单线程执行器docs.oracle.com/javase/7/docs/api/java/util/concurrent/…。另一件你可以验证的事情是,你发送到检测器的图像是否正确,通过将其绘制出来。
      • 不幸的是,它不仅仅是这些日志中的一个。事实上,它在运行应用程序的几秒钟内打印了超过 30 次D/TAG: onSuccess0。这一定意味着图像是正确的close(),对吗?至于使用 mainExecutor,我只是因为this portion of the documentation when implementing CameraX 以及this user's SO question 的代码来完善我的代码。
      猜你喜欢
      • 2012-11-28
      • 2011-03-15
      • 2011-05-05
      • 2022-09-22
      • 2013-12-22
      • 1970-01-01
      • 1970-01-01
      • 2010-12-21
      • 2019-04-15
      相关资源
      最近更新 更多