Reconocer puntos de referencia con Firebase ML en Android

Puedes usar Firebase ML para reconocer puntos de referencia conocidos en una imagen.

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 .

Configurar el detector de puntos de referencia

De forma predeterminada, el detector de nubes utiliza la versión STABLE del modelo y devuelve hasta 10 resultados. Si desea cambiar cualquiera de estas configuraciones, especifíquelas con un objeto FirebaseVisionCloudDetectorOptions .

Por ejemplo, para cambiar ambas configuraciones predeterminadas, cree un objeto FirebaseVisionCloudDetectorOptions como en el siguiente ejemplo:

val options = FirebaseVisionCloudDetectorOptions.Builder()
   
.setModelType(FirebaseVisionCloudDetectorOptions.LATEST_MODEL)
   
.setMaxResults(15)
   
.build()
FirebaseVisionCloudDetectorOptions options =
       
new FirebaseVisionCloudDetectorOptions.Builder()
               
.setModelType(FirebaseVisionCloudDetectorOptions.LATEST_MODEL)
               
.setMaxResults(15)
               
.build();

Para usar la configuración predeterminada, puede usar FirebaseVisionCloudDetectorOptions.DEFAULT en el siguiente paso.

Ejecute el detector de puntos de referencia

Para reconocer puntos de referencia en una imagen, cree un objeto FirebaseVisionImage a partir de un Bitmap , media.Image , ByteBuffer , una matriz de bytes o un archivo en el dispositivo. Luego, pase el objeto FirebaseVisionImage al método detectInImage de FirebaseVisionCloudLandmarkDetector .

  1. Crea un objeto FirebaseVisionImage a partir de tu imagen.

    • 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. Obtenga una instancia de FirebaseVisionCloudLandmarkDetector :

    val detector = FirebaseVision.getInstance()
       
    .visionCloudLandmarkDetector
    // Or, to change the default settings:
    // val detector = FirebaseVision.getInstance()
    //         .getVisionCloudLandmarkDetector(options)
    FirebaseVisionCloudLandmarkDetector detector = FirebaseVision.getInstance()
           
    .getVisionCloudLandmarkDetector();
    // Or, to change the default settings:
    // FirebaseVisionCloudLandmarkDetector detector = FirebaseVision.getInstance()
    //         .getVisionCloudLandmarkDetector(options);
  3. Finalmente, pasa la imagen al método detectInImage :

    val result = detector.detectInImage(image)
       
    .addOnSuccessListener { firebaseVisionCloudLandmarks ->
           
    // Task completed successfully
           
    // ...
       
    }
       
    .addOnFailureListener { e ->
           
    // Task failed with an exception
           
    // ...
       
    }
    Task<List<FirebaseVisionCloudLandmark>> result = detector.detectInImage(image)
           
    .addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionCloudLandmark>>() {
               
    @Override
               
    public void onSuccess(List<FirebaseVisionCloudLandmark> firebaseVisionCloudLandmarks) {
                   
    // Task completed successfully
                   
    // ...
               
    }
           
    })
           
    .addOnFailureListener(new OnFailureListener() {
               
    @Override
               
    public void onFailure(@NonNull Exception e) {
                   
    // Task failed with an exception
                   
    // ...
               
    }
           
    });

Obtenga información sobre los puntos de referencia reconocidos.

Si la operación de reconocimiento de puntos de referencia tiene éxito, se pasará una lista de objetos FirebaseVisionCloudLandmark al oyente exitoso. Cada objeto FirebaseVisionCloudLandmark representa un punto de referencia que se reconoció en la imagen. Para cada punto de referencia, puede obtener sus coordenadas delimitadoras en la imagen de entrada, el nombre del punto de referencia, su latitud y longitud, su ID de entidad del Gráfico de conocimiento (si está disponible) y la puntuación de confianza de la coincidencia. Por ejemplo:

for (landmark in firebaseVisionCloudLandmarks) {
   
val bounds = landmark.boundingBox
   
val landmarkName = landmark.landmark
   
val entityId = landmark.entityId
   
val confidence = landmark.confidence

   
// Multiple locations are possible, e.g., the location of the depicted
   
// landmark and the location the picture was taken.
   
for (loc in landmark.locations) {
       
val latitude = loc.latitude
       
val longitude = loc.longitude
   
}
}
for (FirebaseVisionCloudLandmark landmark: firebaseVisionCloudLandmarks) {

   
Rect bounds = landmark.getBoundingBox();
   
String landmarkName = landmark.getLandmark();
   
String entityId = landmark.getEntityId();
   
float confidence = landmark.getConfidence();

   
// Multiple locations are possible, e.g., the location of the depicted
   
// landmark and the location the picture was taken.
   
for (FirebaseVisionLatLng loc: landmark.getLocations()) {
       
double latitude = loc.getLatitude();
       
double longitude = loc.getLongitude();
   
}
}

Próximos pasos