Reconocer texto en imágenes con ML Kit en Android

Puede utilizar ML Kit para reconocer texto en imágenes. ML Kit tiene una API de uso general adecuada para reconocer texto en imágenes, como el texto de un letrero de calle, y una API optimizada para reconocer el texto de documentos. La API de propósito general tiene modelos tanto en el dispositivo como basados ​​en la nube. El reconocimiento de texto de documentos solo está disponible como modelo basado en la nube. Consulte la descripción general para obtener una comparación de los modelos en la nube y en el dispositivo.

Antes de que empieces

  1. Si aún no lo has hecho, agrega Firebase a tu proyecto de Android .
  2. Agregue las dependencias de las bibliotecas de Android ML Kit al archivo Gradle de su módulo (nivel de aplicación) (generalmente 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'

    }
  3. Opcional pero recomendado : si usa la API en el dispositivo, configure su aplicación para descargar automáticamente el modelo ML en el dispositivo después de instalarla desde Play Store.

    Para hacerlo, agregue la siguiente declaración al archivo AndroidManifest.xml de su aplicación:

    <application ...>
      ...
     
    <meta-data
         
    android:name="com.google.firebase.ml.vision.DEPENDENCIES"
         
    android:value="ocr" />
     
    <!-- To use multiple models: android:value="ocr,model2,model3" -->
    </application>
    Si no habilita las descargas de modelos durante la instalación, el modelo se descargará la primera vez que ejecute el detector en el dispositivo. Las solicitudes que realice antes de que se complete la descarga no producirán resultados.
  4. Si desea utilizar el modelo basado en la nube y aún no ha habilitado las API basadas en la nube para su proyecto, hágalo ahora:

    1. Abra la página API del kit de aprendizaje automático de Firebase console.
    2. Si aún no ha actualizado su proyecto a un plan de precios de 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 .

    Si desea utilizar solo el modelo del dispositivo, puede omitir este paso.

Ahora estás listo para comenzar a reconocer texto en imágenes.

Pautas de imagen de entrada

  • Para que ML Kit reconozca texto con precisión, las imágenes de entrada deben contener texto representado por suficientes datos de píxeles. Idealmente, para texto latino, cada carácter debe tener al menos 16x16 píxeles. Para texto en chino, japonés y coreano (solo compatible con las API basadas en la nube), cada carácter debe tener 24 x 24 píxeles. Para todos los idiomas, generalmente no existe ninguna ventaja en la precisión si los caracteres tienen un tamaño superior a 24 x 24 píxeles.

    Así, por ejemplo, una imagen de 640x480 podría funcionar bien para escanear una tarjeta de presentación que ocupe todo el ancho de la imagen. Para escanear un documento impreso en papel tamaño carta, es posible que necesite una imagen de 720 x 1280 píxeles.

  • Un enfoque deficiente de la imagen puede afectar la precisión del reconocimiento de texto. Si no obtiene resultados aceptables, intente pedirle al usuario que vuelva a capturar la imagen.

  • Si reconoce texto en una aplicación en tiempo real, es posible que también desee considerar las dimensiones generales de las imágenes de entrada. Las imágenes más pequeñas se pueden procesar más rápido, por lo tanto, para reducir la latencia, capture imágenes con resoluciones más bajas (teniendo en cuenta los requisitos de precisión anteriores) y asegúrese de que el texto ocupe la mayor parte posible de la imagen. Consulte también Sugerencias para mejorar el rendimiento en tiempo real .


Reconocer texto en imágenes

Para reconocer texto en una imagen utilizando un modelo en el dispositivo o basado en la nube, ejecute el reconocedor de texto como se describe a continuación.

1. Ejecute el reconocedor de texto.

Para reconocer texto 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 processImage de FirebaseVisionTextRecognizer .

  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_ del kit ML antes de llamar FirebaseVisionImage.fromMediaImage() :

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

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

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

      FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
      val 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.
      FirebaseVisionImage image;
      try {
          image
      = FirebaseVisionImage.fromFilePath(context, uri);
      } catch (IOException e) {
          e
      .printStackTrace();
      }
      val image: FirebaseVisionImage
      try {
          image
      = FirebaseVisionImage.fromFilePath(context, uri)
      } catch (e: IOException) {
          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:

      FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
             
      .setWidth(480)   // 480x360 is typically sufficient for
             
      .setHeight(360)  // image recognition
             
      .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
             
      .setRotation(rotation)
             
      .build();
      val metadata = 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 :

      FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
      val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
      // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
    • Para crear un objeto FirebaseVisionImage a partir de un objeto Bitmap :
      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
      val 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 FirebaseVisionTextRecognizer .

    Para utilizar el modelo en el dispositivo:

    FirebaseVisionTextRecognizer detector = FirebaseVision.getInstance()
           
    .getOnDeviceTextRecognizer();
    val detector = FirebaseVision.getInstance()
           
    .onDeviceTextRecognizer

    Para utilizar el modelo basado en la nube:

    FirebaseVisionTextRecognizer detector = FirebaseVision.getInstance()
           
    .getCloudTextRecognizer();
    // Or, to change the default settings:
    //   FirebaseVisionTextRecognizer detector = FirebaseVision.getInstance()
    //          .getCloudTextRecognizer(options);
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    FirebaseVisionCloudTextRecognizerOptions options = new FirebaseVisionCloudTextRecognizerOptions.Builder()
           
    .setLanguageHints(Arrays.asList("en", "hi"))
           
    .build();
    val detector = FirebaseVision.getInstance().cloudTextRecognizer
    // Or, to change the default settings:
    // val detector = FirebaseVision.getInstance().getCloudTextRecognizer(options)
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    val options
    = FirebaseVisionCloudTextRecognizerOptions.Builder()
           
    .setLanguageHints(listOf("en", "hi"))
           
    .build()
  3. Finalmente, pasa la imagen al método processImage :

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

2. Extraer texto de bloques de texto reconocido

Si la operación de reconocimiento de texto tiene éxito, se pasará un objeto FirebaseVisionText al oyente exitoso. Un objeto FirebaseVisionText contiene el texto completo reconocido en la imagen y cero o más objetos TextBlock .

Cada TextBlock representa un bloque rectangular de texto, que contiene cero o más objetos Line . Cada objeto Line contiene cero o más objetos Element , que representan palabras y entidades similares a palabras (fechas, números, etc.).

Para cada objeto TextBlock , Line y Element , puede obtener el texto reconocido en la región y las coordenadas delimitadoras de la región.

Por ejemplo:

String resultText = result.getText();
for (FirebaseVisionText.TextBlock block: result.getTextBlocks()) {
   
String blockText = block.getText();
   
Float blockConfidence = block.getConfidence();
   
List<RecognizedLanguage> blockLanguages = block.getRecognizedLanguages();
   
Point[] blockCornerPoints = block.getCornerPoints();
   
Rect blockFrame = block.getBoundingBox();
   
for (FirebaseVisionText.Line line: block.getLines()) {
       
String lineText = line.getText();
       
Float lineConfidence = line.getConfidence();
       
List<RecognizedLanguage> lineLanguages = line.getRecognizedLanguages();
       
Point[] lineCornerPoints = line.getCornerPoints();
       
Rect lineFrame = line.getBoundingBox();
       
for (FirebaseVisionText.Element element: line.getElements()) {
           
String elementText = element.getText();
           
Float elementConfidence = element.getConfidence();
           
List<RecognizedLanguage> elementLanguages = element.getRecognizedLanguages();
           
Point[] elementCornerPoints = element.getCornerPoints();
           
Rect elementFrame = element.getBoundingBox();
       
}
   
}
}
val resultText = result.text
for (block in result.textBlocks) {
   
val blockText = block.text
   
val blockConfidence = block.confidence
   
val blockLanguages = block.recognizedLanguages
   
val blockCornerPoints = block.cornerPoints
   
val blockFrame = block.boundingBox
   
for (line in block.lines) {
       
val lineText = line.text
       
val lineConfidence = line.confidence
       
val lineLanguages = line.recognizedLanguages
       
val lineCornerPoints = line.cornerPoints
       
val lineFrame = line.boundingBox
       
for (element in line.elements) {
           
val elementText = element.text
           
val elementConfidence = element.confidence
           
val elementLanguages = element.recognizedLanguages
           
val elementCornerPoints = element.cornerPoints
           
val elementFrame = element.boundingBox
       
}
   
}
}

Consejos para mejorar el rendimiento en tiempo real

Si desea utilizar el modelo en el dispositivo para reconocer texto en una aplicación en tiempo real, siga estas pautas para lograr las mejores velocidades de fotogramas:

  • Acelera las llamadas al reconocedor de texto. Si hay un nuevo fotograma de vídeo disponible mientras se ejecuta el reconocedor de texto, suelte el fotograma.
  • Si está utilizando la salida del reconocedor de texto para superponer gráficos en la imagen de entrada, primero obtenga el resultado del ML Kit, luego renderice la imagen y superpóngala en un solo paso. Al hacerlo, renderiza en la superficie de visualización solo una vez por cada cuadro de entrada.
  • Si utiliza la API Camera2, capture imágenes en formato ImageFormat.YUV_420_888 .

    Si utiliza la API de cámara anterior, capture imágenes en formato ImageFormat.NV21 .

  • Considere capturar imágenes a una resolución más baja. Sin embargo, también tenga en cuenta los requisitos de dimensión de imagen de esta API.

Próximos pasos


Reconocer texto en imágenes de documentos.

Para reconocer el texto de un documento, configure y ejecute el reconocedor de texto de documentos basado en la nube como se describe a continuación.

La API de reconocimiento de texto de documentos, que se describe a continuación, proporciona una interfaz que pretende ser más conveniente para trabajar con imágenes de documentos. Sin embargo, si prefiere la interfaz proporcionada por la API FirebaseVisionTextRecognizer , puede usarla para escanear documentos configurando el reconocedor de texto en la nube para usar el modelo de texto denso .

Para utilizar la API de reconocimiento de texto de documentos:

1. Ejecute el reconocedor de texto.

Para reconocer texto 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 processImage de FirebaseVisionDocumentTextRecognizer .

  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_ del kit ML antes de llamar FirebaseVisionImage.fromMediaImage() :

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

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

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

      FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
      val 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.
      FirebaseVisionImage image;
      try {
          image
      = FirebaseVisionImage.fromFilePath(context, uri);
      } catch (IOException e) {
          e
      .printStackTrace();
      }
      val image: FirebaseVisionImage
      try {
          image
      = FirebaseVisionImage.fromFilePath(context, uri)
      } catch (e: IOException) {
          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:

      FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
             
      .setWidth(480)   // 480x360 is typically sufficient for
             
      .setHeight(360)  // image recognition
             
      .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
             
      .setRotation(rotation)
             
      .build();
      val metadata = 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 :

      FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
      // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
      val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
      // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
    • Para crear un objeto FirebaseVisionImage a partir de un objeto Bitmap :
      FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
      val 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 FirebaseVisionDocumentTextRecognizer :

    FirebaseVisionDocumentTextRecognizer detector = FirebaseVision.getInstance()
           
    .getCloudDocumentTextRecognizer();
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    FirebaseVisionCloudDocumentRecognizerOptions options =
           
    new FirebaseVisionCloudDocumentRecognizerOptions.Builder()
                   
    .setLanguageHints(Arrays.asList("en", "hi"))
                   
    .build();
    FirebaseVisionDocumentTextRecognizer detector = FirebaseVision.getInstance()
           
    .getCloudDocumentTextRecognizer(options);

    val detector = FirebaseVision.getInstance()
           
    .cloudDocumentTextRecognizer
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    val options
    = FirebaseVisionCloudDocumentRecognizerOptions.Builder()
           
    .setLanguageHints(listOf("en", "hi"))
           
    .build()
    val detector
    = FirebaseVision.getInstance()
           
    .getCloudDocumentTextRecognizer(options)

  3. Finalmente, pasa la imagen al método processImage :

    detector.processImage(myImage)
           
    .addOnSuccessListener(new OnSuccessListener<FirebaseVisionDocumentText>() {
               
    @Override
               
    public void onSuccess(FirebaseVisionDocumentText result) {
                   
    // Task completed successfully
                   
    // ...
               
    }
           
    })
           
    .addOnFailureListener(new OnFailureListener() {
               
    @Override
               
    public void onFailure(@NonNull Exception e) {
                   
    // Task failed with an exception
                   
    // ...
               
    }
           
    });
    detector.processImage(myImage)
           
    .addOnSuccessListener { firebaseVisionDocumentText ->
               
    // Task completed successfully
               
    // ...
           
    }
           
    .addOnFailureListener { e ->
               
    // Task failed with an exception
               
    // ...
           
    }

2. Extraer texto de bloques de texto reconocido

Si la operación de reconocimiento de texto tiene éxito, devolverá un objeto FirebaseVisionDocumentText . Un objeto FirebaseVisionDocumentText contiene el texto completo reconocido en la imagen y una jerarquía de objetos que reflejan la estructura del documento reconocido:

Para cada objeto Block , Paragraph , Word y Symbol , puede obtener el texto reconocido en la región y las coordenadas delimitadoras de la región.

Por ejemplo:

String resultText = result.getText();
for (FirebaseVisionDocumentText.Block block: result.getBlocks()) {
   
String blockText = block.getText();
   
Float blockConfidence = block.getConfidence();
   
List<RecognizedLanguage> blockRecognizedLanguages = block.getRecognizedLanguages();
   
Rect blockFrame = block.getBoundingBox();
   
for (FirebaseVisionDocumentText.Paragraph paragraph: block.getParagraphs()) {
       
String paragraphText = paragraph.getText();
       
Float paragraphConfidence = paragraph.getConfidence();
       
List<RecognizedLanguage> paragraphRecognizedLanguages = paragraph.getRecognizedLanguages();
       
Rect paragraphFrame = paragraph.getBoundingBox();
       
for (FirebaseVisionDocumentText.Word word: paragraph.getWords()) {
           
String wordText = word.getText();
           
Float wordConfidence = word.getConfidence();
           
List<RecognizedLanguage> wordRecognizedLanguages = word.getRecognizedLanguages();
           
Rect wordFrame = word.getBoundingBox();
           
for (FirebaseVisionDocumentText.Symbol symbol: word.getSymbols()) {
               
String symbolText = symbol.getText();
               
Float symbolConfidence = symbol.getConfidence();
               
List<RecognizedLanguage> symbolRecognizedLanguages = symbol.getRecognizedLanguages();
               
Rect symbolFrame = symbol.getBoundingBox();
           
}
       
}
   
}
}
val resultText = result.text
for (block in result.blocks) {
   
val blockText = block.text
   
val blockConfidence = block.confidence
   
val blockRecognizedLanguages = block.recognizedLanguages
   
val blockFrame = block.boundingBox
   
for (paragraph in block.paragraphs) {
       
val paragraphText = paragraph.text
       
val paragraphConfidence = paragraph.confidence
       
val paragraphRecognizedLanguages = paragraph.recognizedLanguages
       
val paragraphFrame = paragraph.boundingBox
       
for (word in paragraph.words) {
           
val wordText = word.text
           
val wordConfidence = word.confidence
           
val wordRecognizedLanguages = word.recognizedLanguages
           
val wordFrame = word.boundingBox
           
for (symbol in word.symbols) {
               
val symbolText = symbol.text
               
val symbolConfidence = symbol.confidence
               
val symbolRecognizedLanguages = symbol.recognizedLanguages
               
val symbolFrame = symbol.boundingBox
           
}
       
}
   
}
}

Próximos pasos