Reconoce texto en imágenes con ML Kit en iOS

Puedes usar ML Kit para reconocer texto en imágenes o videos, como el texto de una señal de tránsito. Las principales características de esta función son las siguientes:

API de Text Recognition v2
DescripciónReconoce texto en imágenes o videos, admite escrituras latinas, chinas, devanagari, japonesas y coreanas, y una amplia variedad de idiomas.
Nombres de SDKGoogleMLKit/TextRecognition
GoogleMLKit/TextRecognitionChinese
GoogleMLKit/TextRecognitionDevanagari
GoogleMLKit/TextRecognitionJapanese
GoogleMLKit/TextRecognitionKorean
ImplementaciónLos recursos se vinculan de forma estática a tu app en el momento de la compilación.
Impacto en el tamaño de la appAproximadamente 38 MB por SDK de secuencias de comandos
RendimientoEn tiempo real en la mayoría de los dispositivos para el SDK de escritura latina y más lento para otros.

Probar

Antes de comenzar

  1. Incluye los siguientes pods del ML Kit en tu Podfile:
    # To recognize Latin script
    pod 'GoogleMLKit/TextRecognition', '8.0.0'
    # To recognize Chinese script
    pod 'GoogleMLKit/TextRecognitionChinese', '8.0.0'
    # To recognize Devanagari script
    pod 'GoogleMLKit/TextRecognitionDevanagari', '8.0.0'
    # To recognize Japanese script
    pod 'GoogleMLKit/TextRecognitionJapanese', '8.0.0'
    # To recognize Korean script
    pod 'GoogleMLKit/TextRecognitionKorean', '8.0.0'
    
  2. Después de instalar o actualizar los Pods de tu proyecto, abre el proyecto de Xcode con su .xcworkspace. El Kit de AA es compatible con Xcode 12.4 o versiones posteriores.

1. Crea una instancia de TextRecognizer.

Crea una instancia de TextRecognizer llamando a +textRecognizer(options:) y pasando las opciones relacionadas con el SDK que declaraste como dependencia anteriormente:

Swift

// When using Latin script recognition SDK
let latinOptions = TextRecognizerOptions()
let latinTextRecognizer = TextRecognizer.textRecognizer(options:options)

// When using Chinese script recognition SDK
let chineseOptions = ChineseTextRecognizerOptions()
let chineseTextRecognizer = TextRecognizer.textRecognizer(options:options)

// When using Devanagari script recognition SDK
let devanagariOptions = DevanagariTextRecognizerOptions()
let devanagariTextRecognizer = TextRecognizer.textRecognizer(options:options)

// When using Japanese script recognition SDK
let japaneseOptions = JapaneseTextRecognizerOptions()
let japaneseTextRecognizer = TextRecognizer.textRecognizer(options:options)

// When using Korean script recognition SDK
let koreanOptions = KoreanTextRecognizerOptions()
let koreanTextRecognizer = TextRecognizer.textRecognizer(options:options)

Objective-C

// When using Latin script recognition SDK
MLKTextRecognizerOptions *latinOptions = [[MLKTextRecognizerOptions alloc] init];
MLKTextRecognizer *latinTextRecognizer = [MLKTextRecognizer textRecognizerWithOptions:options];

// When using Chinese script recognition SDK
MLKChineseTextRecognizerOptions *chineseOptions = [[MLKChineseTextRecognizerOptions alloc] init];
MLKTextRecognizer *chineseTextRecognizer = [MLKTextRecognizer textRecognizerWithOptions:options];

// When using Devanagari script recognition SDK
MLKDevanagariTextRecognizerOptions *devanagariOptions = [[MLKDevanagariTextRecognizerOptions alloc] init];
MLKTextRecognizer *devanagariTextRecognizer = [MLKTextRecognizer textRecognizerWithOptions:options];

// When using Japanese script recognition SDK
MLKJapaneseTextRecognizerOptions *japaneseOptions = [[MLKJapaneseTextRecognizerOptions alloc] init];
MLKTextRecognizer *japaneseTextRecognizer = [MLKTextRecognizer textRecognizerWithOptions:options];

// When using Korean script recognition SDK
MLKKoreanTextRecognizerOptions *koreanOptions = [[MLKKoreanTextRecognizerOptions alloc] init];
MLKTextRecognizer *koreanTextRecognizer = [MLKTextRecognizer textRecognizerWithOptions:options];

2. Prepara la imagen de entrada

Pasa la imagen como un UIImage o un CMSampleBufferRef al método process(_:completion:) de TextRecognizer:

Crea un objeto VisionImage con un UIImage o un CMSampleBuffer.

Si usas una UIImage, sigue estos pasos:

  • Crea un objeto VisionImage con UIImage. Asegúrate de especificar la .orientation correcta.

    Swift

    let image = VisionImage(image: UIImage)
    visionImage.orientation = image.imageOrientation

    Objective-C

    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

Si usas una CMSampleBuffer, sigue estos pasos:

  • Especifica la orientación de los datos de imagen contenidos en CMSampleBuffer.

    Para obtener la orientación de la imagen, haz lo siguiente:

    Swift

    func imageOrientation(
      deviceOrientation: UIDeviceOrientation,
      cameraPosition: AVCaptureDevice.Position
    ) -> UIImage.Orientation {
      switch deviceOrientation {
      case .portrait:
        return cameraPosition == .front ? .leftMirrored : .right
      case .landscapeLeft:
        return cameraPosition == .front ? .downMirrored : .up
      case .portraitUpsideDown:
        return cameraPosition == .front ? .rightMirrored : .left
      case .landscapeRight:
        return cameraPosition == .front ? .upMirrored : .down
      case .faceDown, .faceUp, .unknown:
        return .up
      }
    }
          

    Objective-C

    - (UIImageOrientation)
      imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                             cameraPosition:(AVCaptureDevicePosition)cameraPosition {
      switch (deviceOrientation) {
        case UIDeviceOrientationPortrait:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored
                                                                : UIImageOrientationRight;
    
        case UIDeviceOrientationLandscapeLeft:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored
                                                                : UIImageOrientationUp;
        case UIDeviceOrientationPortraitUpsideDown:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored
                                                                : UIImageOrientationLeft;
        case UIDeviceOrientationLandscapeRight:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored
                                                                : UIImageOrientationDown;
        case UIDeviceOrientationUnknown:
        case UIDeviceOrientationFaceUp:
        case UIDeviceOrientationFaceDown:
          return UIImageOrientationUp;
      }
    }
          
  • Crea un objeto VisionImage con el objeto CMSampleBuffer y la orientación:

    Swift

    let image = VisionImage(buffer: sampleBuffer)
    image.orientation = imageOrientation(
      deviceOrientation: UIDevice.current.orientation,
      cameraPosition: cameraPosition)

    Objective-C

     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image.orientation =
       [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition:cameraPosition];

3. Procesa la imagen

Por último, pasa la imagen al método process(_:completion:):

Swift

textRecognizer.process(visionImage) { result, error in
  guard error == nil, let result = result else {
    // Error handling
    return
  }
  // Recognized text
}

Objective-C

[textRecognizer processImage:image
                  completion:^(MLKText *_Nullable result,
                               NSError *_Nullable error) {
  if (error != nil || result == nil) {
    // Error handling
    return;
  }
  // Recognized text
}];

4. Extrae texto de bloques de texto reconocido

Si la operación de reconocimiento de texto se ejecuta correctamente, se mostrará un objeto Text. Un objeto Text 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 TextLine. Cada objeto TextLine contiene cero o más objetos TextElement que representan palabras y entidades similares, como fechas y números.

Para cada objeto TextBlock, TextLine y TextElement, puedes obtener el texto reconocido en la región y las coordenadas que limitan la región.

Por ejemplo:

Swift

let resultText = result.text
for block in result.blocks {
    let blockText = block.text
    let blockLanguages = block.recognizedLanguages
    let blockCornerPoints = block.cornerPoints
    let blockFrame = block.frame
    for line in block.lines {
        let lineText = line.text
        let lineLanguages = line.recognizedLanguages
        let lineCornerPoints = line.cornerPoints
        let lineFrame = line.frame
        for element in line.elements {
            let elementText = element.text
            let elementCornerPoints = element.cornerPoints
            let elementFrame = element.frame
        }
    }
}

Objective-C

NSString *resultText = result.text;
for (MLKTextBlock *block in result.blocks) {
  NSString *blockText = block.text;
  NSArray<MLKTextRecognizedLanguage *> *blockLanguages = block.recognizedLanguages;
  NSArray<NSValue *> *blockCornerPoints = block.cornerPoints;
  CGRect blockFrame = block.frame;
  for (MLKTextLine *line in block.lines) {
    NSString *lineText = line.text;
    NSArray<MLKTextRecognizedLanguage *> *lineLanguages = line.recognizedLanguages;
    NSArray<NSValue *> *lineCornerPoints = line.cornerPoints;
    CGRect lineFrame = line.frame;
    for (MLKTextElement *element in line.elements) {
      NSString *elementText = element.text;
      NSArray<NSValue *> *elementCornerPoints = element.cornerPoints;
      CGRect elementFrame = element.frame;
    }
  }
}

Lineamientos para imágenes de entrada

  • Para que ML Kit reconozca texto con precisión, las imágenes de entrada deben contener texto representado con datos de píxeles suficientes. Lo ideal es que cada carácter tenga al menos 16 × 16 píxeles. Generalmente, no se obtiene un beneficio de exactitud cuando el tamaño de los caracteres es superior a 24 × 24 píxeles.

    Por ejemplo, una imagen de 640 × 480 puede funcionar bien para escanear una tarjeta de presentación que ocupa todo el ancho de la imagen. Para escanear un documento impreso en tamaño de papel carta, es posible que se requiera una imagen de 720 × 1,280 píxeles.

  • Un enfoque de imagen deficiente puede afectar la exactitud del reconocimiento de texto. Si no obtienes resultados aceptables, intenta pedirle al usuario que vuelva a capturar la imagen.

  • Si reconoces texto en una aplicación en tiempo real, te recomendamos tener en cuenta las dimensiones generales de las imágenes de entrada. Las imágenes más pequeñas se pueden procesar más rápido. Para reducir la latencia, asegúrate de que el texto ocupe la mayor parte de la imagen posible y captura imágenes con resoluciones más bajas (ten en cuenta los requisitos de exactitud mencionados anteriormente). Para obtener más información, consulta Sugerencias para mejorar el rendimiento.

Sugerencias para mejorar el rendimiento

  • Para procesar fotogramas de video, usa la API síncrona results(in:) del detector. Llama a este método desde la función captureOutput(_, didOutput:from:) de AVCaptureVideoDataOutputSampleBufferDelegate para obtener de forma síncrona los resultados del fotograma de video determinado. Mantén el alwaysDiscardsLateVideoFrames de AVCaptureVideoDataOutput como true para regular las llamadas al detector. Si hay un fotograma de video nuevo disponible mientras se ejecuta el detector, se ignorará.
  • Si usas la salida del detector para superponer gráficos en la imagen de entrada, primero obtén el resultado del Kit de AA y, luego, renderiza la imagen y la superposición en un solo paso. De esta manera, procesas la superficie de visualización solo una vez por cada fotograma de entrada procesado. Consulta updatePreviewOverlayViewWithLastFrame en la muestra de inicio rápido del Kit de ML para ver un ejemplo.
  • Considera capturar imágenes con una resolución más baja. Sin embargo, también ten en cuenta los requisitos de dimensiones de imágenes de esta API.
  • Para evitar una posible degradación del rendimiento, no ejecutes varias instancias de TextRecognizer con diferentes opciones de secuencias de comandos de forma simultánea.