Reconhecer texto em imagens com o Kit de ML no iOS

Você pode usar o Kit de ML para reconhecer texto em imagens ou vídeos, como o texto de uma placa de rua. As principais características desse recurso são:

API Text Recognition
DescriçãoReconhecer texto em script latino em imagens ou vídeos.
Nome do SDKGoogleMLKit/TextRecognition (version 2.2.0)
ImplementaçãoOs recursos são vinculados estaticamente ao app durante a compilação.
Impacto do tamanho do appCerca de 20 MB
DesempenhoEm tempo real na maioria dos dispositivos.

Faça um teste

  • Teste o app de exemplo para ver um exemplo de uso dessa API.
  • Teste o código por conta própria com o codelab (em inglês).

Antes de começar

  1. Inclua os seguintes pods de Kit de ML no seu Podfile:
    pod 'GoogleMLKit/TextRecognition','2.2.0'
    
  2. Depois de instalar ou atualizar os pods do projeto, abra o projeto do Xcode usando o .xcworkspace. O Kit de ML é compatível com a versão 12.4 ou superior do Xcode.

1. Criar uma instância de TextRecognizer

Crie uma instância de TextRecognizer chamando +textRecognizer:

Swift

let textRecognizer = TextRecognizer.textRecognizer()
      

Objective-C

MLKTextRecognizer *textRecognizer = [MLKTextRecognizer textRecognizer];
      

2. Preparar a imagem de entrada

Transmita a imagem como UIImage ou CMSampleBufferRef para o método process(_:completion:) do TextRecognizer:

Crie um objeto VisionImage usando um UIImage ou um CMSampleBuffer.

Se você usa um UIImage, siga estas etapas:

  • Crie um objeto VisionImage com o UIImage. Especifique o .orientation correto.

    Swift

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

    Objective-C

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

Se você usa um CMSampleBuffer, siga estas etapas:

  • Especifique a orientação dos dados da imagem contidos em CMSampleBuffer.

    Para ver a orientação da imagem:

    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;
      }
    }
          
  • Crie um objeto VisionImage usando o objeto CMSampleBuffer e a orientação:

    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. Processar a imagem

Em seguida, transmita a imagem para o 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. Extrair texto de blocos de texto reconhecidos

Se a operação de reconhecimento de texto for bem-sucedida, ela retornará um objeto Text. Um objeto Text contém o texto completo reconhecido na imagem e zero ou mais objetos TextBlock.

Cada TextBlock representa um bloco de texto retangular, que contém zero ou mais objetos TextLine. Cada objeto TextLine contém zero ou mais objetos TextElement, que representam palavras e entidades semelhantes, como datas e números.

Para cada objeto TextBlock, TextLine e TextElement, é possível obter o texto reconhecido na região e as coordenadas delimitadoras da região.

Exemplo:

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

Diretrizes de imagem de entrada

  • Para que o Kit de ML reconheça o texto com precisão, as imagens de entrada devem conter texto representado por dados de pixel suficientes. O ideal é que cada caractere tenha pelo menos 16 x 16 pixels. Geralmente, não há benefício de precisão para caracteres maiores que 24 x 24 pixels.

    Por exemplo, uma imagem de 640 x 480 pixels pode funcionar para digitalizar um cartão de visita que ocupe toda a largura da imagem. Para digitalizar um documento impresso em papel de tamanho carta, talvez seja necessária uma imagem de 720 x 1280 pixels.

  • O foco inadequado da imagem pode afetar a precisão do reconhecimento de texto. Se você não estiver conseguindo resultados aceitáveis, peça para o usuário recapturar a imagem.

  • Se você estiver fazendo reconhecimento de texto em um aplicativo em tempo real, considere as dimensões gerais das imagens de entrada. Imagens menores podem ser processadas mais rapidamente. Para reduzir a latência, verifique se o texto está ocupando o máximo possível da imagem e capture imagens em resoluções mais baixas, tendo em mente os requisitos de precisão mencionados acima. Para mais informações, consulte Dicas para melhorar o desempenho.

Dicas para melhorar o desempenho

  • Para processar frames de vídeo, use a API síncrona results(in:) do detector. Chame esse método da função captureOutput(_, didOutput:from:) de AVCaptureVideoDataOutputSampleBufferDelegate para receber resultados de maneira síncrona do frame de vídeo especificado. Mantenha o alwaysDiscardsLateVideoFrames de AVCaptureVideoDataOutput como true para limitar as chamadas para o detector. Se um novo frame de vídeo for disponibilizado enquanto o detector estiver em execução, ele será descartado.
  • Se você usar a saída do detector para sobrepor elementos gráficos na imagem de entrada, primeiro acesse o resultado do Kit de ML e, em seguida, renderize a imagem e a sobreposição em uma única etapa. Ao fazer isso, você renderiza a superfície de exibição apenas uma vez para cada frame de entrada processado. Consulte updatePreviewOverlayViewWithLastFrame no exemplo do guia de início rápido do Kit de ML para ver um exemplo.
  • Capture imagens em uma resolução menor. No entanto, lembre-se também dos requisitos de dimensão de imagem da API.