Wykrywaj, śledź i klasyfikuj obiekty za pomocą niestandardowego modelu klasyfikacji na Androidzie.

Za pomocą ML Kit możesz wykrywać i śledzić obiekty w kolejnych klatkach wideo.

Gdy przekazujesz obraz do ML Kit, wykrywa on maksymalnie 5 obiektów. wraz z położeniem każdego obiektu na obrazie. Podczas wykrywania obiektów w strumieni wideo, każdy obiekt ma unikalny identyfikator, którego możesz użyć do śledzenia od klatki do klatki.

Możesz użyć niestandardowego modelu klasyfikacji obrazów, aby sklasyfikować obiekty, które są – wykryto. Informacje o modelach niestandardowych z pakietem ML Kit wskazówki dotyczące wymagań zgodności, gdzie znaleźć już wytrenowane modele, oraz jak trenować własne modele.

Model niestandardowy można zintegrować na 2 sposoby. Możesz połączyć model według poprzez umieszczenie go w folderze z zasobami aplikacji lub pobranie dynamicznego z Firebase. W poniższej tabeli porównano 2 opcje.

Model w pakiecie Hostowany model
Model jest częścią pakietu APK aplikacji, co zwiększa jego rozmiar. Model nie jest częścią pakietu APK. Jest hostowany przez przesłanie do Systemów uczących się Firebase.
Model jest dostępny od razu, nawet gdy urządzenie z Androidem jest offline Model jest pobierany na żądanie
Nie potrzeba projektu Firebase Wymaga projektu Firebase
Aby zaktualizować model, musisz ponownie opublikować aplikację Przekazywanie aktualizacji modelu bez ponownego publikowania aplikacji
Brak wbudowanych testów A/B Łatwe testy A/B dzięki Zdalnej konfiguracji Firebase

Wypróbuj

Zanim zaczniesz

  1. W pliku build.gradle na poziomie projektu umieść dane Repozytorium Google Maven w środowiskach buildscript i Sekcje: allprojects.

  2. Dodaj zależności bibliotek ML Kit na Androida do biblioteki modułu plik Gradle na poziomie aplikacji, który zwykle ma wartość app/build.gradle:

    Aby dołączyć model do aplikacji:

    dependencies {
      // ...
      // Object detection & tracking feature with custom bundled model
      implementation 'com.google.mlkit:object-detection-custom:17.0.2'
    }
    

    Aby dynamicznie pobierać model z Firebase, dodaj linkFirebase zależność:

    dependencies {
      // ...
      // Object detection & tracking feature with model downloaded
      // from firebase
      implementation 'com.google.mlkit:object-detection-custom:17.0.2'
      implementation 'com.google.mlkit:linkfirebase:17.0.0'
    }
    
  3. Jeśli chcesz pobrać model, dodaj Firebase do swojego projektu na Androida, jeśli jeszcze nie zostało to zrobione. Nie jest to wymagane, gdy łączysz model w pakiet.

1. Wczytaj model

Skonfiguruj źródło modelu lokalnego

Aby połączyć model z aplikacją:

  1. Skopiuj plik modelu (zwykle kończący się na .tflite lub .lite) do Folder assets/. (Możliwe, że najpierw trzeba będzie utworzyć folder kliknij prawym przyciskiem myszy folder app/, a następnie kliknij Nowe > Folder > Folder Zasoby).

  2. Następnie dodaj do pliku build.gradle z aplikacją ten kod, aby mieć pewność, Gradle nie kompresuje pliku modelu podczas tworzenia aplikacji:

    android {
        // ...
        aaptOptions {
            noCompress "tflite"
            // or noCompress "lite"
        }
    }
    

    Plik z modelem zostanie dołączony do pakietu aplikacji i będzie dostępny dla ML Kit jako nieprzetworzony zasób.

  3. Utwórz obiekt LocalModel, podając ścieżkę do pliku modelu:

    Kotlin

    val localModel = LocalModel.Builder()
            .setAssetFilePath("model.tflite")
            // or .setAbsoluteFilePath(absolute file path to model file)
            // or .setUri(URI to model file)
            .build()

    Java

    LocalModel localModel =
        new LocalModel.Builder()
            .setAssetFilePath("model.tflite")
            // or .setAbsoluteFilePath(absolute file path to model file)
            // or .setUri(URI to model file)
            .build();

Skonfiguruj źródło modelu hostowanego w Firebase

Aby używać modelu hostowanego zdalnie, utwórz obiekt CustomRemoteModel przez FirebaseModelSource, określając nazwę przypisaną do modelu opublikował(a):

Kotlin

// Specify the name you assigned in the Firebase console.
val remoteModel =
    CustomRemoteModel
        .Builder(FirebaseModelSource.Builder("your_model_name").build())
        .build()

Java

// Specify the name you assigned in the Firebase console.
CustomRemoteModel remoteModel =
    new CustomRemoteModel
        .Builder(new FirebaseModelSource.Builder("your_model_name").build())
        .build();

Następnie uruchom zadanie pobierania modelu, określając warunki, które którym chcesz zezwolić na pobieranie. Jeśli nie ma modelu na urządzeniu lub jest on nowszy gdy dostępna będzie wersja modelu, zadanie asynchronicznie pobierze model z Firebase:

Kotlin

val downloadConditions = DownloadConditions.Builder()
    .requireWifi()
    .build()
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
    .addOnSuccessListener {
        // Success.
    }

Java

DownloadConditions downloadConditions = new DownloadConditions.Builder()
        .requireWifi()
        .build();
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
        .addOnSuccessListener(new OnSuccessListener() {
            @Override
            public void onSuccess(@NonNull Task task) {
                // Success.
            }
        });

Wiele aplikacji rozpoczyna zadanie pobierania w kodzie inicjowania, możesz to zrobić w dowolnym momencie, zanim trzeba będzie skorzystać z modelu.

2. Konfigurowanie detektora obiektów

Po skonfigurowaniu źródeł modelu skonfiguruj detektor obiektów dla z obiektem CustomObjectDetectorOptions. Możesz zmienić następujące ustawienia:

Ustawienia wykrywania obiektów
Tryb wykrywania STREAM_MODE (domyślna) | SINGLE_IMAGE_MODE

W STREAM_MODE (domyślnie) działa wykrywacz obiektów. z małym czasem oczekiwania, ale mogą one dawać niepełne wyniki (np. nieokreślone ramki ograniczające lub etykiety kategorii) na pierwszych kilku na wywołania detektora. Poza tym za STREAM_MODE przypisuje do obiektów identyfikatory śledzenia, których można używać śledzić obiekty w ramkach. Użyj tego trybu, jeśli chcesz śledzić lub gdy ważne jest małe opóźnienie, np. podczas przetwarzania strumieniowania wideo w czasie rzeczywistym.

W SINGLE_IMAGE_MODE detektor obiektów zwraca wynik po określeniu ramki ograniczającej obiektu. Jeśli także włącz klasyfikację, ponieważ zwraca wynik po ograniczeniu pole i etykieta kategorii są dostępne. W związku z tym opóźnienie wykrywania jest potencjalnie większe. Także za SINGLE_IMAGE_MODE, identyfikatory śledzenia nie są przypisane. Używaj w tym trybie, jeśli opóźnienia nie są krytyczne i nie chcesz częściowe wyniki.

Wykrywanie i śledzenie wielu obiektów false (domyślna) | true

Określa, czy należy wykryć i śledzić do pięciu obiektów, czy tylko najbardziej. widoczny obiekt (domyślnie).

Klasyfikowanie obiektów false (domyślna) | true

Określa, czy należy klasyfikować wykryte obiekty przy użyciu podanego niestandardowy model klasyfikatora. Aby użyć własnej klasyfikacji modelu, należy ustawić wartość true.

Próg ufności klasyfikacji

Minimalny wskaźnik ufności wykrytych etykiet. Jeśli zasada nie jest skonfigurowana, zostanie użyty próg klasyfikatora określony przez metadane modelu. Jeśli model nie zawiera żadnych metadanych lub określ próg klasyfikatora, domyślny próg równy 0,0 zostanie .

Maksymalna liczba etykiet na obiekt

Maksymalna liczba etykiet na obiekt, które detektor . Jeśli zasada nie jest skonfigurowana, używana jest wartość domyślna, czyli 10.

Interfejs API wykrywania i śledzenia obiektów jest zoptymalizowany pod kątem tych dwóch podstawowych zastosowań przypadki:

  • Wykrywanie na żywo i śledzenie najbardziej widocznego obiektu w kamerze wizjer.
  • Wykrywanie wielu obiektów na obrazie statycznym.

Aby skonfigurować interfejs API pod kątem tych przypadków użycia za pomocą modelu dołączonego lokalnie:

Kotlin

// Live detection and tracking
val customObjectDetectorOptions =
        CustomObjectDetectorOptions.Builder(localModel)
        .setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE)
        .enableClassification()
        .setClassificationConfidenceThreshold(0.5f)
        .setMaxPerObjectLabelCount(3)
        .build()

// Multiple object detection in static images
val customObjectDetectorOptions =
        CustomObjectDetectorOptions.Builder(localModel)
        .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
        .enableMultipleObjects()
        .enableClassification()
        .setClassificationConfidenceThreshold(0.5f)
        .setMaxPerObjectLabelCount(3)
        .build()

val objectDetector =
        ObjectDetection.getClient(customObjectDetectorOptions)

Java

// Live detection and tracking
CustomObjectDetectorOptions customObjectDetectorOptions =
        new CustomObjectDetectorOptions.Builder(localModel)
                .setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE)
                .enableClassification()
                .setClassificationConfidenceThreshold(0.5f)
                .setMaxPerObjectLabelCount(3)
                .build();

// Multiple object detection in static images
CustomObjectDetectorOptions customObjectDetectorOptions =
        new CustomObjectDetectorOptions.Builder(localModel)
                .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
                .enableMultipleObjects()
                .enableClassification()
                .setClassificationConfidenceThreshold(0.5f)
                .setMaxPerObjectLabelCount(3)
                .build();

ObjectDetector objectDetector =
    ObjectDetection.getClient(customObjectDetectorOptions);

Jeśli masz model hostowany zdalnie, musisz sprawdzić, czy został pobrane przed uruchomieniem. Stan pobierania modelu możesz sprawdzić za pomocą metody isModelDownloaded() menedżera modeli.

Chociaż musisz to potwierdzić tylko przed uruchomieniem wzorca do wykrywania, jeśli korzystają zarówno z modelu hostowanego zdalnie, jak i z pakietu lokalnego, może to sprawić, warto przeprowadzić tę kontrolę przy tworzeniu wystąpienia wzorca do wykrywania obrazów: utwórz detektora z modelu zdalnego, jeśli został pobrany, oraz z lokalnego w inny sposób.

Kotlin

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener { isDownloaded ->
    val optionsBuilder =
        if (isDownloaded) {
            CustomObjectDetectorOptions.Builder(remoteModel)
        } else {
            CustomObjectDetectorOptions.Builder(localModel)
        }
    val customObjectDetectorOptions = optionsBuilder
            .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
            .enableClassification()
            .setClassificationConfidenceThreshold(0.5f)
            .setMaxPerObjectLabelCount(3)
            .build()
    val objectDetector =
        ObjectDetection.getClient(customObjectDetectorOptions)
}

Java

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener(new OnSuccessListener() {
        @Override
        public void onSuccess(Boolean isDownloaded) {
            CustomObjectDetectorOptions.Builder optionsBuilder;
            if (isDownloaded) {
                optionsBuilder = new CustomObjectDetectorOptions.Builder(remoteModel);
            } else {
                optionsBuilder = new CustomObjectDetectorOptions.Builder(localModel);
            }
            CustomObjectDetectorOptions customObjectDetectorOptions = optionsBuilder
                .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
                .enableClassification()
                .setClassificationConfidenceThreshold(0.5f)
                .setMaxPerObjectLabelCount(3)
                .build();
            ObjectDetector objectDetector =
                ObjectDetection.getClient(customObjectDetectorOptions);
        }
});

Jeśli masz tylko model hostowany zdalnie, wyłącz powiązany z nim model funkcji – np. wyszarzenia lub ukrycia części interfejsu – potwierdzasz, że model został pobrany. Aby to zrobić, dołącz detektor do metody download() menedżera modeli:

Kotlin

RemoteModelManager.getInstance().download(remoteModel, conditions)
    .addOnSuccessListener {
        // Download complete. Depending on your app, you could enable the ML
        // feature, or switch from the local model to the remote model, etc.
    }

Java

RemoteModelManager.getInstance().download(remoteModel, conditions)
        .addOnSuccessListener(new OnSuccessListener() {
            @Override
            public void onSuccess(Void v) {
              // Download complete. Depending on your app, you could enable
              // the ML feature, or switch from the local model to the remote
              // model, etc.
            }
        });

3. Przygotowywanie obrazu wejściowego

Utwórz obiekt InputImage ze swojego obrazu. Wykrywacz obiektów działa bezpośrednio z Bitmap, NV21 ByteBuffer lub YUV_420_888 media.Image. Utworzenie InputImage z tych źródeł jest jest zalecany, jeśli masz do nich bezpośredni dostęp. Jeśli utworzysz InputImage z innych źródeł, będziemy obsługiwać konwersję wewnętrznie dla i praca może być mniej efektywna.

Możesz utworzyć InputImage z różnych źródeł, każdy z nich objaśniamy poniżej.

Korzystanie z: media.Image

Aby utworzyć InputImage z obiektu media.Image, np. podczas przechwytywania obrazu z z aparatu urządzenia, przekaż obiekt media.Image i obiekt obrazu w kierunku InputImage.fromMediaImage().

Jeśli używasz tagu CameraX, OnImageCapturedListener oraz ImageAnalysis.Analyzer klasy obliczają wartość rotacji dla Ciebie.

Kotlin

private class YourImageAnalyzer : ImageAnalysis.Analyzer {

    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image
        if (mediaImage != null) {
            val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
            // Pass image to an ML Kit Vision API
            // ...
        }
    }
}

Java

private class YourAnalyzer implements ImageAnalysis.Analyzer {

    @Override
    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
          // ...
        }
    }
}

Jeśli nie korzystasz z biblioteki aparatu, która określa kąt obrotu obrazu, może go obliczyć na podstawie stopnia obrotu urządzenia i orientacji aparatu czujnik w urządzeniu:

Kotlin

private val ORIENTATIONS = SparseIntArray()

init {
    ORIENTATIONS.append(Surface.ROTATION_0, 0)
    ORIENTATIONS.append(Surface.ROTATION_90, 90)
    ORIENTATIONS.append(Surface.ROTATION_180, 180)
    ORIENTATIONS.append(Surface.ROTATION_270, 270)
}

/**
 * Get the angle by which an image must be rotated given the device's current
 * orientation.
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Throws(CameraAccessException::class)
private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int {
    // Get the device's current rotation relative to its "native" orientation.
    // Then, from the ORIENTATIONS table, look up the angle the image must be
    // rotated to compensate for the device's rotation.
    val deviceRotation = activity.windowManager.defaultDisplay.rotation
    var rotationCompensation = ORIENTATIONS.get(deviceRotation)

    // Get the device's sensor orientation.
    val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager
    val sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION)!!

    if (isFrontFacing) {
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360
    } else { // back-facing
        rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360
    }
    return rotationCompensation
}

Java

private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
    ORIENTATIONS.append(Surface.ROTATION_0, 0);
    ORIENTATIONS.append(Surface.ROTATION_90, 90);
    ORIENTATIONS.append(Surface.ROTATION_180, 180);
    ORIENTATIONS.append(Surface.ROTATION_270, 270);
}

/**
 * Get the angle by which an image must be rotated given the device's current
 * orientation.
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private int getRotationCompensation(String cameraId, Activity activity, boolean isFrontFacing)
        throws CameraAccessException {
    // Get the device's current rotation relative to its "native" orientation.
    // Then, from the ORIENTATIONS table, look up the angle the image must be
    // rotated to compensate for the device's rotation.
    int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
    int rotationCompensation = ORIENTATIONS.get(deviceRotation);

    // Get the device's sensor orientation.
    CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE);
    int sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION);

    if (isFrontFacing) {
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360;
    } else { // back-facing
        rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360;
    }
    return rotationCompensation;
}

Następnie przekaż obiekt media.Image oraz wartość stopnia obrotu na InputImage.fromMediaImage():

Kotlin

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

InputImage image = InputImage.fromMediaImage(mediaImage, rotation);

Za pomocą identyfikatora URI pliku

Aby utworzyć InputImage obiektu z identyfikatora URI pliku, przekaż kontekst aplikacji oraz identyfikator URI pliku do InputImage.fromFilePath() Jest to przydatne, gdy użyj intencji ACTION_GET_CONTENT, aby zachęcić użytkownika do wyboru obraz z aplikacji Galeria.

Kotlin

val image: InputImage
try {
    image = InputImage.fromFilePath(context, uri)
} catch (e: IOException) {
    e.printStackTrace()
}

Java

InputImage image;
try {
    image = InputImage.fromFilePath(context, uri);
} catch (IOException e) {
    e.printStackTrace();
}

Przy użyciu: ByteBuffer lub ByteArray

Aby utworzyć InputImage obiektu z ByteBuffer lub ByteArray, najpierw oblicz wartość obrazu stopień obrotu zgodnie z wcześniejszym opisem dla danych wejściowych media.Image. Następnie utwórz obiekt InputImage z buforem lub tablicą oraz wysokość, szerokość, format kodowania kolorów i stopień obrotu:

Kotlin

val image = InputImage.fromByteBuffer(
        byteBuffer,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
)
// Or:
val image = InputImage.fromByteArray(
        byteArray,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
)

Java

InputImage image = InputImage.fromByteBuffer(byteBuffer,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
);
// Or:
InputImage image = InputImage.fromByteArray(
        byteArray,
        /* image width */480,
        /* image height */360,
        rotation,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
);

Korzystanie z: Bitmap

Aby utworzyć InputImage z obiektu Bitmap, wypełnij tę deklarację:

Kotlin

val image = InputImage.fromBitmap(bitmap, 0)

Java

InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);

Obraz jest reprezentowany przez obiekt Bitmap wraz z informacją o obróceniu w stopniach.

4. Uruchom detektor obiektów

Kotlin

objectDetector
    .process(image)
    .addOnFailureListener(e -> {...})
    .addOnSuccessListener(results -> {
        for (detectedObject in results) {
          // ...
        }
    });

Java

objectDetector
    .process(image)
    .addOnFailureListener(e -> {...})
    .addOnSuccessListener(results -> {
        for (DetectedObject detectedObject : results) {
          // ...
        }
    });

5. Uzyskiwanie informacji o obiektach oznaczonych etykietami

Jeśli wywołanie metody process() się powiedzie, do funkcji DetectedObject zostanie przekazana lista elementów DetectedObject słuchaczem sukcesu.

Każdy element DetectedObject zawiera te właściwości:

Ramka ograniczająca Rect, który wskazuje położenie obiektu w .
Identyfikator śledzenia Liczba całkowita, która identyfikuje obiekt na obrazach. Wartość null SINGLE_IMAGE_MODE.
Etykiety
Opis etykiety Opis tekstowy etykiety. Zwracany tylko wtedy, gdy TensorFlow Metadane modelu Lite zawierają opisy etykiet.
Indeks etykiety Indeks etykiety wśród wszystkich etykiet obsługiwanych przez do klasyfikatora.
Poziom ufności etykiet Wartość ufności klasyfikacji obiektu.

Kotlin

// The list of detected objects contains one item if multiple
// object detection wasn't enabled.
for (detectedObject in results) {
    val boundingBox = detectedObject.boundingBox
    val trackingId = detectedObject.trackingId
    for (label in detectedObject.labels) {
      val text = label.text
      val index = label.index
      val confidence = label.confidence
    }
}

Java

// The list of detected objects contains one item if multiple
// object detection wasn't enabled.
for (DetectedObject detectedObject : results) {
  Rect boundingBox = detectedObject.getBoundingBox();
  Integer trackingId = detectedObject.getTrackingId();
  for (Label label : detectedObject.getLabels()) {
    String text = label.getText();
    int index = label.getIndex();
    float confidence = label.getConfidence();
  }
}

Dbanie o wygodę użytkowników

Aby zadbać o wygodę użytkowników, przestrzegaj tych wytycznych:

  • Pomyślne wykrycie obiektu zależy od jego złożoności wizualnej. W są wykrywane, obiekty z niewielką liczbą funkcji wizualnych mogą wymagać aby zająć większą część obrazu. Należy dostarczyć użytkownikom wskazówki na temat tego, przechwytywanie danych wejściowych, które dobrze działają z rodzajami obiektów, które chcesz wykrywać.
  • Gdy używasz klasyfikacji, aby wykrywać obiekty, które nie wypadają do obsługiwanych kategorii, zastosować specjalną obsługę nieznanych obiektów.

Zapoznaj się też z Aplikacja prezentująca ML Kit Material Design oraz Material Design Kolekcja Wzorce funkcji opartych na systemach uczących się.

Improving performance

Jeśli chcesz używać wykrywania obiektów w aplikacji działającej w czasie rzeczywistym, postępuj zgodnie z tymi instrukcjami wytycznych dotyczących uzyskiwania najlepszej liczby klatek na sekundę:

  • Gdy używasz trybu strumieniowania w aplikacji działającej w czasie rzeczywistym, nie używaj wielu wykrywanie obiektów, ponieważ większość urządzeń nie jest w stanie wygenerować odpowiedniej liczby klatek na sekundę.

  • Jeśli używasz tagu Camera lub camera2 API, ograniczanie wywołań detektora. Jeśli nowy film ramka stanie się dostępna, gdy detektor będzie aktywny, upuść ją. Zobacz VisionProcessorBase w przykładowej aplikacji z krótkim wprowadzeniem.
  • Jeśli używasz interfejsu API CameraX, upewnij się, że strategia obciążenia wstecznego jest ustawiona na wartość domyślną . ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST Gwarantuje to, że do analizy zostanie dostarczony tylko 1 obraz naraz. Jeśli więcej obrazów generowane, gdy analizator jest zajęty, są usuwane automatycznie i nie są umieszczane w kolejce . Po zamknięciu analizowanego obrazu przez wywołanie ImageProxy.close(), zostanie wyświetlony następny najnowszy obraz.
  • Jeśli użyjesz danych wyjściowych detektora do nakładania grafiki na obrazu wejściowego, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładanie nakładek w jednym kroku. Powoduje to wyrenderowanie na powierzchni wyświetlania tylko raz na każdą ramkę wejściową. Zobacz CameraSourcePreview i . GraphicOverlay w przykładowej aplikacji z krótkim wprowadzeniem.
  • Jeśli korzystasz z interfejsu API Camera2, rób zdjęcia w Format: ImageFormat.YUV_420_888. Jeśli używasz starszej wersji interfejsu Camera API, rób zdjęcia w Format: ImageFormat.NV21.