Oznacz obrazy za pomocą zestawu ML na Androida

Można użyć ML Zestaw etykiet do obiektów ujętych w obrazie, przy użyciu modelu na urządzeniu lub modelu chmury. Zobacz przegląd , aby dowiedzieć się o korzyściach płynących z każdego podejścia.

Zanim zaczniesz

  1. Jeśli jeszcze tego nie zrobiłeś, dodaj Firebase do swojego projektu na Androida .
  2. Dodaj zależności dla bibliotek ML Kit Android do modułu (ok szczebla) Gradle plików (zwykle app/build.gradle ):
    apply plugin: 'com.android.application'
    apply plugin: 'com.google.gms.google-services'
    
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-vision:24.0.3'
      implementation 'com.google.firebase:firebase-ml-vision-image-label-model:20.0.1'
    }
    
  3. Opcjonalne, ale zalecane : W przypadku korzystania z interfejsu API na urządzeniu, należy skonfigurować aplikację do automatycznego pobierania modelu ML do urządzenia po zainstalowaniu aplikacja ze Sklepu Play.

    Aby to zrobić, należy dodać następującą deklarację do swojej aplikacji AndroidManifest.xml pliku:

    <application ...>
      ...
      <meta-data
          android:name="com.google.firebase.ml.vision.DEPENDENCIES"
          android:value="label" />
      <!-- To use multiple models: android:value="label,model2,model3" -->
    </application>
    
    Jeśli nie pozwalają zainstalować czasie modelowych pobieranie, model zostanie pobrana przy pierwszym uruchomieniu czujki na urządzeniu. Żąda dokonać przed zakończeniu pobierania przyniesie żadnych rezultatów.
  4. Jeśli chcesz korzystać z modelu opartego na chmurze, a nie masz już włączone API chmurowej się do projektu, należy to zrobić teraz:

    1. Otwórz stronę ML Kit API konsoli Firebase.
    2. Jeśli nie masz już uaktualniony projekt do planu cenowego Blaze, kliknij przycisk Zmień , aby to zrobić. (Zostaniesz poproszony, aby uaktualnić tylko jeśli projekt nie jest na planie Blaze).

      Jedynie projekty Blaze szczebla mogą używać API chmurowej.

    3. Jeśli API w chmurze nie są już włączone, kliknij przycisk Włącz API chmurowej .

    Jeśli chcesz korzystać tylko z modelu na urządzeniu, można pominąć ten krok.

Teraz jesteś gotowy do obrazów etykiety z zastosowaniem zarówno modelu na urządzeniu lub model w chmurze.

1. Przygotować obraz wejściowy

Tworzenie FirebaseVisionImage obiektu z obrazu. Etykieciarki obrazu przebiega najszybciej w przypadku korzystania z map Bitmap lub, w przypadku korzystania z interfejsu API Camera2, JPEG-sformatowany media.Image , które są zalecane, jeśli to możliwe.

  • Aby utworzyć FirebaseVisionImage obiektu z media.Image obiektu, na przykład podczas wykonywania obrazu z kamery dla urządzenia, przechodzą media.Image przedmiotu i obrót obrazka do FirebaseVisionImage.fromMediaImage() .

    Jeśli używasz CameraX bibliotekę, OnImageCapturedListener i ImageAnalysis.Analyzer klas obliczyć wartość obrotu dla Ciebie, więc po prostu trzeba konwertować obrót jednego ML Kit ROTATION_ stałych przed wywołaniem FirebaseVisionImage.fromMediaImage() :

    Java

    private class YourAnalyzer implements ImageAnalysis.Analyzer {
    
        private int degreesToFirebaseRotation(int degrees) {
            switch (degrees) {
                case 0:
                    return FirebaseVisionImageMetadata.ROTATION_0;
                case 90:
                    return FirebaseVisionImageMetadata.ROTATION_90;
                case 180:
                    return FirebaseVisionImageMetadata.ROTATION_180;
                case 270:
                    return FirebaseVisionImageMetadata.ROTATION_270;
                default:
                    throw new IllegalArgumentException(
                            "Rotation must be 0, 90, 180, or 270.");
            }
        }
    
        @Override
        public void analyze(ImageProxy imageProxy, int degrees) {
            if (imageProxy == null || imageProxy.getImage() == null) {
                return;
            }
            Image mediaImage = imageProxy.getImage();
            int rotation = degreesToFirebaseRotation(degrees);
            FirebaseVisionImage image =
                    FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
            // Pass image to an ML Kit Vision API
            // ...
        }
    }
    

    Kotlin+KTX

    private class YourImageAnalyzer : ImageAnalysis.Analyzer {
        private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) {
            0 -> FirebaseVisionImageMetadata.ROTATION_0
            90 -> FirebaseVisionImageMetadata.ROTATION_90
            180 -> FirebaseVisionImageMetadata.ROTATION_180
            270 -> FirebaseVisionImageMetadata.ROTATION_270
            else -> throw Exception("Rotation must be 0, 90, 180, or 270.")
        }
    
        override fun analyze(imageProxy: ImageProxy?, degrees: Int) {
            val mediaImage = imageProxy?.image
            val imageRotation = degreesToFirebaseRotation(degrees)
            if (mediaImage != null) {
                val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation)
                // Pass image to an ML Kit Vision API
                // ...
            }
        }
    }
    

    Jeśli nie korzystać z biblioteki aparatu, który daje obracanie obrazka, można go obliczyć z obrotu urządzenia i orientacji czujnika aparatu w urządzeniu:

    Java

    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
    static {
        ORIENTATIONS.append(Surface.ROTATION_0, 90);
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }
    
    /**
     * 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, Context context)
            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);
    
        // On most devices, the sensor orientation is 90 degrees, but for some
        // devices it is 270 degrees. For devices with a sensor orientation of
        // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
        CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
        int sensorOrientation = cameraManager
                .getCameraCharacteristics(cameraId)
                .get(CameraCharacteristics.SENSOR_ORIENTATION);
        rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
    
        // Return the corresponding FirebaseVisionImageMetadata rotation value.
        int result;
        switch (rotationCompensation) {
            case 0:
                result = FirebaseVisionImageMetadata.ROTATION_0;
                break;
            case 90:
                result = FirebaseVisionImageMetadata.ROTATION_90;
                break;
            case 180:
                result = FirebaseVisionImageMetadata.ROTATION_180;
                break;
            case 270:
                result = FirebaseVisionImageMetadata.ROTATION_270;
                break;
            default:
                result = FirebaseVisionImageMetadata.ROTATION_0;
                Log.e(TAG, "Bad rotation value: " + rotationCompensation);
        }
        return result;
    }

    Kotlin+KTX

    private val ORIENTATIONS = SparseIntArray()
    
    init {
        ORIENTATIONS.append(Surface.ROTATION_0, 90)
        ORIENTATIONS.append(Surface.ROTATION_90, 0)
        ORIENTATIONS.append(Surface.ROTATION_180, 270)
        ORIENTATIONS.append(Surface.ROTATION_270, 180)
    }
    /**
     * 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, context: Context): 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)
    
        // On most devices, the sensor orientation is 90 degrees, but for some
        // devices it is 270 degrees. For devices with a sensor orientation of
        // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
        val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager
        val sensorOrientation = cameraManager
                .getCameraCharacteristics(cameraId)
                .get(CameraCharacteristics.SENSOR_ORIENTATION)!!
        rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360
    
        // Return the corresponding FirebaseVisionImageMetadata rotation value.
        val result: Int
        when (rotationCompensation) {
            0 -> result = FirebaseVisionImageMetadata.ROTATION_0
            90 -> result = FirebaseVisionImageMetadata.ROTATION_90
            180 -> result = FirebaseVisionImageMetadata.ROTATION_180
            270 -> result = FirebaseVisionImageMetadata.ROTATION_270
            else -> {
                result = FirebaseVisionImageMetadata.ROTATION_0
                Log.e(TAG, "Bad rotation value: $rotationCompensation")
            }
        }
        return result
    }

    Następnie przechodzi się media.Image przedmiotu oraz wartość rotacji do FirebaseVisionImage.fromMediaImage() :

    Java

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

    Kotlin+KTX

    val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
  • Aby utworzyć FirebaseVisionImage obiekt z pliku URI, przekaż kontekst aplikacji i plików URI FirebaseVisionImage.fromFilePath() . Jest to przydatne w przypadku korzystania z ACTION_GET_CONTENT zamiar skłonić użytkownika, aby wybrać zdjęcie z galerii ich aplikacji.

    Java

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

    Kotlin+KTX

    val image: FirebaseVisionImage
    try {
        image = FirebaseVisionImage.fromFilePath(context, uri)
    } catch (e: IOException) {
        e.printStackTrace()
    }
  • Aby utworzyć FirebaseVisionImage obiektu z ByteBuffer lub tablicy bajtów najpierw obliczenie obracanie obrazu, jak opisano powyżej dla media.Image wejścia.

    Następnie utwórz FirebaseVisionImageMetadata obiekt, który zawiera obraz na wysokość, szerokość, kolor, format kodowania i obrót:

    Java

    FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
            .setWidth(480)   // 480x360 is typically sufficient for
            .setHeight(360)  // image recognition
            .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
            .setRotation(rotation)
            .build();

    Kotlin+KTX

    val metadata = FirebaseVisionImageMetadata.Builder()
            .setWidth(480) // 480x360 is typically sufficient for
            .setHeight(360) // image recognition
            .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
            .setRotation(rotation)
            .build()

    Za pomocą buforu lub tablicę, a obiekt metadanych w celu utworzenia FirebaseVisionImage obiektu:

    Java

    FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
    // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);

    Kotlin+KTX

    val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
    // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
  • Aby utworzyć FirebaseVisionImage obiekt z Bitmap obiektu:

    Java

    FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

    Kotlin+KTX

    val image = FirebaseVisionImage.fromBitmap(bitmap)
    Obraz przedstawiony przez Bitmap obiektu musi być wyprostowany, bez dodatkowego obrotu wymagane.

2. Konfiguracja i uruchomienie Image Labeler

Do etykiety obiektów w obrazie, przechodzą FirebaseVisionImage obiekt do FirebaseVisionImageLabeler jest processImage metody.

  1. Po pierwsze, uzyskać instancję FirebaseVisionImageLabeler .

    Jeśli chcesz użyć obrazu Labeler na urządzenie:

    Java

    FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance()
        .getOnDeviceImageLabeler();
    
    // Or, to set the minimum confidence required:
    // FirebaseVisionOnDeviceImageLabelerOptions options =
    //     new FirebaseVisionOnDeviceImageLabelerOptions.Builder()
    //         .setConfidenceThreshold(0.7f)
    //         .build();
    // FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance()
    //     .getOnDeviceImageLabeler(options);
    

    Kotlin+KTX

    val labeler = FirebaseVision.getInstance().getOnDeviceImageLabeler()
    
    // Or, to set the minimum confidence required:
    // val options = FirebaseVisionOnDeviceImageLabelerOptions.Builder()
    //     .setConfidenceThreshold(0.7f)
    //     .build()
    // val labeler = FirebaseVision.getInstance().getOnDeviceImageLabeler(options)
    

    Jeśli chcesz użyć Labeler chmura obrazka:

    Java

    FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance()
        .getCloudImageLabeler();
    
    // Or, to set the minimum confidence required:
    // FirebaseVisionCloudImageLabelerOptions options =
    //     new FirebaseVisionCloudImageLabelerOptions.Builder()
    //         .setConfidenceThreshold(0.7f)
    //         .build();
    // FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance()
    //     .getCloudImageLabeler(options);
    

    Kotlin+KTX

    val labeler = FirebaseVision.getInstance().getCloudImageLabeler()
    
    // Or, to set the minimum confidence required:
    // val options = FirebaseVisionCloudImageLabelerOptions.Builder()
    //     .setConfidenceThreshold(0.7f)
    //     .build()
    // val labeler = FirebaseVision.getInstance().getCloudImageLabeler(options)
    

  2. Następnie przechodzi się obraz na processImage() sposobu:

    Java

    labeler.processImage(image)
        .addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionImageLabel>>() {
          @Override
          public void onSuccess(List<FirebaseVisionImageLabel> labels) {
            // Task completed successfully
            // ...
          }
        })
        .addOnFailureListener(new OnFailureListener() {
          @Override
          public void onFailure(@NonNull Exception e) {
            // Task failed with an exception
            // ...
          }
        });
    

    Kotlin+KTX

    labeler.processImage(image)
        .addOnSuccessListener { labels ->
          // Task completed successfully
          // ...
        }
        .addOnFailureListener { e ->
          // Task failed with an exception
          // ...
        }
    

3. Uzyskaj informacje o oznakowanych obiektów

Jeśli operacja się powiedzie etykietowanie obraz, wykaz FirebaseVisionImageLabel obiektów zostaną przekazane do słuchacza sukcesu. Każdy FirebaseVisionImageLabel obiekt reprezentuje coś, który został oznaczony w obrazie. Na każdej etykiecie, można uzyskać opis etykiety tekstowe, jego wiedzy Graph identyfikatorze jednostki (jeśli jest dostępny), a wynik ufności meczu. Na przykład:

Java

for (FirebaseVisionImageLabel label: labels) {
  String text = label.getText();
  String entityId = label.getEntityId();
  float confidence = label.getConfidence();
}

Kotlin+KTX

for (label in labels) {
  val text = label.text
  val entityId = label.entityId
  val confidence = label.confidence
}

Jak poprawić wydajność w czasie rzeczywistym

Jeśli chcesz obrazów etykiet w aplikacji w czasie rzeczywistym, należy przestrzegać następujących zasad, aby osiągnąć najlepsze klatek na sekundę:

  • Zawrotna szybkość połączenia do Labeler obrazu. Jeśli nowa rama wideo staje się dostępna, gdy etykieciarka obraz jest uruchomiony, usunąć klatkę.
  • Jeśli używasz wyjście Image Labeler do nakładanie grafiki na obrazie wejściowym, najpierw uzyskać wynik z ML Kit, a następnie renderować obraz i nakładki w jednym kroku. W ten sposób można renderować do powierzchni wyświetlacza tylko raz dla każdej ramki wejściowej.
  • Jeśli używasz API Camera2, wykonywanie zdjęć w ImageFormat.YUV_420_888 formacie.

    Jeśli używasz starszej API aparatu, wykonywanie zdjęć w ImageFormat.NV21 formacie.

Następne kroki