Etiquetar imágenes con Firebase ML en Android

Puedes usar Firebase ML para etiquetar objetos reconocidos en una imagen. Consulte la descripción general para obtener información sobre las características de esta API.

Antes de que empieces

  1. Si aún no lo has hecho, agrega Firebase a tu proyecto de Android .
  2. En el archivo Gradle de su módulo (nivel de aplicación) (generalmente <project>/<app-module>/build.gradle.kts o <project>/<app-module>/build.gradle ), agregue la dependencia para Firebase ML Biblioteca de visión para Android. Recomendamos utilizar Firebase Android BoM para controlar el control de versiones de la biblioteca.
    dependencies {
       
    // Import the BoM for the Firebase platform
        implementation
    (platform("com.google.firebase:firebase-bom:32.8.0"))


       
    // Add the dependency for the Firebase ML Vision library
       
    // When using the BoM, you don't specify versions in Firebase library dependencies
       
    implementation 'com.google.firebase:firebase-ml-vision'
    }

    Al usar Firebase Android BoM , su aplicación siempre usará versiones compatibles de las bibliotecas de Firebase Android.

    Si elige no utilizar la BoM de Firebase, debe especificar cada versión de la biblioteca de Firebase en su línea de dependencia.

    Tenga en cuenta que si usa varias bibliotecas de Firebase en su aplicación, le recomendamos encarecidamente usar la BoM para administrar las versiones de la biblioteca, lo que garantiza que todas las versiones sean compatibles.

    dependencies {
       
    // Add the dependency for the Firebase ML Vision library
       
    // When NOT using the BoM, you must specify versions in Firebase library dependencies
       
    implementation 'com.google.firebase:firebase-ml-vision:24.1.0'
    }
    ¿Busca un módulo de biblioteca específico de Kotlin? A partir de octubre de 2023 (Firebase BoM 32.5.0) , tanto los desarrolladores de Kotlin como los de Java podrán depender del módulo de biblioteca principal (para más detalles, consulte las preguntas frecuentes sobre esta iniciativa ).
  3. Si aún no ha habilitado las API basadas en la nube para su proyecto, hágalo ahora:

    1. Abra la página API de Firebase ML de Firebase console.
    2. Si aún no ha actualizado su proyecto al plan de precios Blaze, haga clic en Actualizar para hacerlo. (Se le pedirá que actualice solo si su proyecto no está en el plan Blaze).

      Solo los proyectos de nivel Blaze pueden utilizar API basadas en la nube.

    3. Si las API basadas en la nube aún no están habilitadas, haga clic en Habilitar API basadas en la nube .

Ahora estás listo para etiquetar imágenes.

1. Prepare la imagen de entrada

Crea un objeto FirebaseVisionImage a partir de tu imagen. El etiquetador de imágenes se ejecuta más rápido cuando usa un Bitmap o, si usa la API de camera2, un media.Image con formato JPEG, que se recomiendan cuando sea posible.

  • Para crear un objeto FirebaseVisionImage a partir de un objeto media.Image , como al capturar una imagen desde la cámara de un dispositivo, pase el objeto media.Image y la rotación de la imagen a FirebaseVisionImage.fromMediaImage() .

    Si usa la biblioteca CameraX , las clases OnImageCapturedListener e ImageAnalysis.Analyzer calculan el valor de rotación por usted, por lo que solo necesita convertir la rotación a una de las constantes ROTATION_ de Firebase ML antes de llamar a FirebaseVisionImage.fromMediaImage() :

    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 Vision API
               
    // ...
           
    }
       
    }
    }
    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 Vision API
           
    // ...
       
    }
    }

    Si no utiliza una biblioteca de cámaras que le proporcione la rotación de la imagen, puede calcularla a partir de la rotación del dispositivo y la orientación del sensor de la cámara en el dispositivo:

    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
    }
    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;
    }

    Luego, pasa el objeto media.Image y el valor de rotación a FirebaseVisionImage.fromMediaImage() :

    val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
    FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
  • Para crear un objeto FirebaseVisionImage a partir de un URI de archivo, pase el contexto de la aplicación y el URI del archivo a FirebaseVisionImage.fromFilePath() . Esto es útil cuando usas un intent ACTION_GET_CONTENT para pedirle al usuario que seleccione una imagen de su aplicación de galería.
    val image: FirebaseVisionImage
    try {
        image
    = FirebaseVisionImage.fromFilePath(context, uri)
    } catch (e: IOException) {
        e
    .printStackTrace()
    }
    FirebaseVisionImage image;
    try {
        image
    = FirebaseVisionImage.fromFilePath(context, uri);
    } catch (IOException e) {
        e
    .printStackTrace();
    }
  • Para crear un objeto FirebaseVisionImage a partir de un ByteBuffer o una matriz de bytes, primero calcule la rotación de la imagen como se describe anteriormente para la entrada media.Image .

    Luego, crea un objeto FirebaseVisionImageMetadata que contenga la altura, el ancho, el formato de codificación de color y la rotación de la imagen:

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

    Utilice el búfer o matriz y el objeto de metadatos para crear un objeto FirebaseVisionImage :

    val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
    // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
    FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
    // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
  • Para crear un objeto FirebaseVisionImage a partir de un objeto Bitmap :
    val image = FirebaseVisionImage.fromBitmap(bitmap)
    FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
    La imagen representada por el objeto Bitmap debe estar en posición vertical, sin necesidad de rotación adicional.

2. Configure y ejecute el etiquetador de imágenes.

Para etiquetar objetos en una imagen, pase el objeto FirebaseVisionImage al método processImage de FirebaseVisionImageLabeler .

  1. Primero, obtenga una instancia de FirebaseVisionImageLabeler .

    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)

    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);

  2. Luego, pasa la imagen al método processImage() :

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

    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
           
    // ...
         
    }
       
    });

3. Obtener información sobre objetos etiquetados.

Si la operación de etiquetado de imágenes tiene éxito, se pasará una lista de objetos FirebaseVisionImageLabel al oyente exitoso. Cada objeto FirebaseVisionImageLabel representa algo que se etiquetó en la imagen. Para cada etiqueta, puede obtener la descripción del texto de la etiqueta, su ID de entidad del Gráfico de conocimiento (si está disponible) y la puntuación de confianza de la coincidencia. Por ejemplo:

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

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

Próximos pasos