Catch up on everthing we announced at this year's Firebase Summit. Learn more

Oznacz obrazy za pomocą zestawu ML na Androida

Możesz użyć ML Kit do etykietowania obiektów rozpoznanych na obrazie, używając modelu na urządzeniu lub modelu w chmurze. Zobacz przegląd , aby dowiedzieć się o korzyściach płynących z każdego podejścia.

Zanim zaczniesz

  1. Jeśli tego nie zrobiłeś, dodaj Firebase do projektu Android .
  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. Żądania złożone przed zakończeniem pobierania nie przyniosą żadnych wyników.
  4. Jeśli chcesz korzystać z modelu opartego na chmurze, a nie masz jeszcze włączonych interfejsów API opartych na chmurze dla swojego projektu, zrób to 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 o uaktualnienie tylko wtedy, gdy Twój projekt nie jest objęty planem Blaze).

      Tylko projekty na poziomie Blaze mogą korzystać z interfejsów API opartych na chmurze.

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

    Jeśli chcesz używać tylko modelu na urządzeniu, możesz pominąć ten krok.

Teraz możesz oznaczyć obrazy etykietami za pomocą modelu na urządzeniu lub modelu opartego na chmurze.

1. Przygotuj obraz wejściowy

Tworzenie FirebaseVisionImage obiektu z obrazu. Etykieciarki obrazu przebiega najszybciej w przypadku korzystania z 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() :

    Jawa

    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 korzystasz z biblioteki aparatu, która podaje obrót obrazu, możesz go obliczyć na podstawie obrotu urządzenia i orientacji czujnika aparatu w urządzeniu:

    Jawa

    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() :

    Jawa

    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.

    Jawa

    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:

    Jawa

    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:

    Jawa

    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:

    Jawa

    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. Skonfiguruj i uruchom program do etykietowania obrazów

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ć urządzenia do etykietowania obrazów na urządzeniu:

    Jawa

    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ć drukarki do etykietowania obrazów w chmurze:

    Jawa

    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:

    Jawa

    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 oznaczonych obiektach

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:

Jawa

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
}

Wskazówki dotyczące poprawy wydajności w czasie rzeczywistym

Jeśli chcesz opisywać obrazy w aplikacji działającej w czasie rzeczywistym, postępuj zgodnie z poniższymi wskazówkami, aby uzyskać najlepszą liczbę klatek na sekundę:

  • Ogranicz połączenia do drukarki etykiet obrazów. Jeśli nowa ramka wideo stanie się dostępna, gdy drukarka etykiet obrazu jest uruchomiona, upuść ramkę.
  • Jeśli używasz danych wyjściowych drukarki etykiet do nakładania grafiki na obraz wejściowy, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładkę w jednym kroku. W ten sposób renderujesz na powierzchnię wyświetlania tylko raz dla każdej klatki 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