Распознавайте текст на изображениях с помощью ML Kit на iOS

Вы можете использовать ML Kit для распознавания текста на изображениях. ML Kit предлагает как универсальный API, подходящий для распознавания текста на изображениях, например, текста уличных вывесок, так и API, оптимизированный для распознавания текста документов. Универсальный API поддерживает как локальную, так и облачную модели. Распознавание текста документов доступно только в облачной модели. Сравнение облачной и локальной моделей см. в обзоре .

Прежде чем начать

  1. Если вы еще не добавили Firebase в свое приложение, сделайте это, следуя инструкциям в руководстве по началу работы .
  2. Включите библиотеки ML Kit в ваш Podfile:
    pod 'Firebase/MLVision', '6.25.0'
    # If using an on-device API:
    pod 'Firebase/MLVisionTextModel', '6.25.0'
    
    После установки или обновления модулей вашего проекта обязательно откройте свой проект Xcode, используя его .xcworkspace .
  3. В вашем приложении импортируйте Firebase:

    Быстрый

    import Firebase

    Objective-C

    @import Firebase;
  4. Если вы хотите использовать облачную модель и еще не включили облачные API для своего проекта, сделайте это сейчас:

    1. Откройте страницу API ML Kit в консоли Firebase .
    2. Если вы ещё не перевели свой проект на тарифный план Blaze, нажмите «Обновить» , чтобы сделать это. (Вам будет предложено обновиться только в том случае, если ваш проект не входит в тарифный план Blaze.)

      Только проекты уровня Blaze могут использовать облачные API.

    3. Если облачные API еще не включены, нажмите Включить облачные API .

    Если вы хотите использовать только модель на устройстве, вы можете пропустить этот шаг.

Теперь вы готовы начать распознавать текст на изображениях.

Правила ввода изображений

  • Для точного распознавания текста с помощью ML Kit входные изображения должны содержать текст, представленный достаточным количеством пикселей. В идеале для латинского текста каждый символ должен иметь размер не менее 16x16 пикселей. Для китайского, японского и корейского текста (поддерживаемых только облачными API) каждый символ должен иметь размер 24x24 пикселя. Для всех языков символы размером более 24x24 пикселя, как правило, не повышают точность распознавания.

    Например, изображение размером 640x480 пикселей может подойти для сканирования визитной карточки, занимающей всю её ширину. Для сканирования документа, напечатанного на бумаге формата Letter, может потребоваться изображение размером 720x1280 пикселей.

  • Плохая фокусировка изображения может снизить точность распознавания текста. Если результаты неудовлетворительны, попросите пользователя повторно сделать снимок.

  • Если вы распознаёте текст в приложении реального времени, вам также стоит учитывать общие размеры входных изображений. Изображения меньшего размера обрабатываются быстрее, поэтому для уменьшения задержки снимайте изображения с более низким разрешением (с учётом вышеуказанных требований к точности) и следите за тем, чтобы текст занимал как можно большую часть изображения. См. также раздел «Советы по повышению производительности в реальном времени» .


Распознавать текст на изображениях

Чтобы распознать текст на изображении с помощью модели на устройстве или в облаке, запустите распознаватель текста, как описано ниже.

1. Запустите распознаватель текста.

Передайте изображение как `UIImage` или `CMSampleBufferRef` в метод `process(_:completion:)` объекта `VisionTextRecognizer`:
  1. Получите экземпляр VisionTextRecognizer , вызвав onDeviceTextRecognizer или cloudTextRecognizer :

    Быстрый

    Чтобы использовать модель на устройстве:

    let vision = Vision.vision()
    let textRecognizer = vision.onDeviceTextRecognizer()

    Чтобы использовать облачную модель:

    let vision = Vision.vision()
    let textRecognizer = vision.cloudTextRecognizer()
    
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    let options = VisionCloudTextRecognizerOptions()
    options.languageHints = ["en", "hi"]
    let textRecognizer = vision.cloudTextRecognizer(options: options)

    Objective-C

    Чтобы использовать модель на устройстве:

    FIRVision *vision = [FIRVision vision];
    FIRVisionTextRecognizer *textRecognizer = [vision onDeviceTextRecognizer];

    Чтобы использовать облачную модель:

    FIRVision *vision = [FIRVision vision];
    FIRVisionTextRecognizer *textRecognizer = [vision cloudTextRecognizer];
    
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    FIRVisionCloudTextRecognizerOptions *options =
            [[FIRVisionCloudTextRecognizerOptions alloc] init];
    options.languageHints = @[@"en", @"hi"];
    FIRVisionTextRecognizer *textRecognizer = [vision cloudTextRecognizerWithOptions:options];
  2. Создайте объект VisionImage , используя UIImage или CMSampleBufferRef .

    Чтобы использовать UIImage :

    1. При необходимости поверните изображение так, чтобы его свойство imageOrientation было равно .up .
    2. Создайте объект VisionImage , используя правильно повёрнутый UIImage . Не указывайте метаданные поворота — необходимо использовать значение по умолчанию, .topLeft .

      Быстрый

      let image = VisionImage(image: uiImage)

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    Чтобы использовать CMSampleBufferRef :

    1. Создайте объект VisionImageMetadata , который определяет ориентацию данных изображения, содержащихся в буфере CMSampleBufferRef .

      Чтобы получить ориентацию изображения:

      Быстрый

      func imageOrientation(
          deviceOrientation: UIDeviceOrientation,
          cameraPosition: AVCaptureDevice.Position
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      Objective-C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      Затем создайте объект метаданных:

      Быстрый

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Objective-C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. Создайте объект VisionImage , используя объект CMSampleBufferRef и метаданные поворота:

      Быстрый

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. Затем передайте изображение методу process(_:completion:) :

    Быстрый

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

    Objective-C

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

2. Извлечь текст из блоков распознанного текста

Если операция распознавания текста выполнена успешно, возвращается объект [`VisionText`][VisionText]. Объект `VisionText` содержит полный текст, распознанный на изображении, и ноль или более объектов [`VisionTextBlock`][VisionTextBlock]. Каждый объект `VisionTextBlock` представляет собой прямоугольный блок текста, содержащий ноль или более объектов [`VisionTextLine`][VisionTextLine]. Каждый объект `VisionTextLine` содержит ноль или более объектов [`VisionTextElement`][VisionTextElement], которые представляют слова и словоподобные сущности (даты, числа и т. д.). Для каждого объекта `VisionTextBlock`, `VisionTextLine` и `VisionTextElement` можно получить распознанный в области текст и координаты границ этой области. Например:

Быстрый

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

Objective-C

NSString *resultText = result.text;
for (FIRVisionTextBlock *block in result.blocks) {
  NSString *blockText = block.text;
  NSNumber *blockConfidence = block.confidence;
  NSArray<FIRVisionTextRecognizedLanguage *> *blockLanguages = block.recognizedLanguages;
  NSArray<NSValue *> *blockCornerPoints = block.cornerPoints;
  CGRect blockFrame = block.frame;
  for (FIRVisionTextLine *line in block.lines) {
    NSString *lineText = line.text;
    NSNumber *lineConfidence = line.confidence;
    NSArray<FIRVisionTextRecognizedLanguage *> *lineLanguages = line.recognizedLanguages;
    NSArray<NSValue *> *lineCornerPoints = line.cornerPoints;
    CGRect lineFrame = line.frame;
    for (FIRVisionTextElement *element in line.elements) {
      NSString *elementText = element.text;
      NSNumber *elementConfidence = element.confidence;
      NSArray<FIRVisionTextRecognizedLanguage *> *elementLanguages = element.recognizedLanguages;
      NSArray<NSValue *> *elementCornerPoints = element.cornerPoints;
      CGRect elementFrame = element.frame;
    }
  }
}

Советы по улучшению производительности в реальном времени

Если вы хотите использовать модель на устройстве для распознавания текста в приложении реального времени, следуйте этим рекомендациям, чтобы добиться наилучшей частоты кадров:

  • Устраните вызовы распознавателя текста. Если во время работы распознавателя текста появляется новый видеокадр, отбросьте его.
  • Если вы используете выходные данные распознавателя текста для наложения графики на входное изображение, сначала получите результат из ML Kit, а затем визуализируйте изображение и наложение за один шаг. Таким образом, визуализация на поверхности дисплея выполняется только один раз для каждого входного кадра. См. примеры классов previewOverlayView и FIRDetectionOverlayView в демонстрационном примере приложения.
  • Рассмотрите возможность захвата изображений в более низком разрешении. Однако учитывайте требования API к размерам изображений.

Следующие шаги


Распознавать текст на изображениях документов

Чтобы распознать текст документа, настройте и запустите облачный распознаватель текста документа, как описано ниже.

API распознавания текста документов, описанный ниже, предоставляет интерфейс, предназначенный для более удобной работы с изображениями документов. Однако, если вы предпочитаете интерфейс API разреженного текста, вы можете использовать его для сканирования документов, настроив облачный распознаватель текста на использование модели плотного текста .

Чтобы использовать API распознавания текста документа:

1. Запустите распознаватель текста.

Передайте изображение как UIImage или CMSampleBufferRef в process(_:completion:) VisionDocumentTextRecognizer :

  1. Получите экземпляр VisionDocumentTextRecognizer , вызвав cloudDocumentTextRecognizer :

    Быстрый

    let vision = Vision.vision()
    let textRecognizer = vision.cloudDocumentTextRecognizer()
    
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    let options = VisionCloudDocumentTextRecognizerOptions()
    options.languageHints = ["en", "hi"]
    let textRecognizer = vision.cloudDocumentTextRecognizer(options: options)

    Objective-C

    FIRVision *vision = [FIRVision vision];
    FIRVisionDocumentTextRecognizer *textRecognizer = [vision cloudDocumentTextRecognizer];
    
    // Or, to provide language hints to assist with language detection:
    // See https://cloud.google.com/vision/docs/languages for supported languages
    FIRVisionCloudDocumentTextRecognizerOptions *options =
            [[FIRVisionCloudDocumentTextRecognizerOptions alloc] init];
    options.languageHints = @[@"en", @"hi"];
    FIRVisionDocumentTextRecognizer *textRecognizer = [vision cloudDocumentTextRecognizerWithOptions:options];
  2. Создайте объект VisionImage , используя UIImage или CMSampleBufferRef .

    Чтобы использовать UIImage :

    1. При необходимости поверните изображение так, чтобы его свойство imageOrientation было равно .up .
    2. Создайте объект VisionImage , используя правильно повёрнутый UIImage . Не указывайте метаданные поворота — необходимо использовать значение по умолчанию, .topLeft .

      Быстрый

      let image = VisionImage(image: uiImage)

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    Чтобы использовать CMSampleBufferRef :

    1. Создайте объект VisionImageMetadata , который определяет ориентацию данных изображения, содержащихся в буфере CMSampleBufferRef .

      Чтобы получить ориентацию изображения:

      Быстрый

      func imageOrientation(
          deviceOrientation: UIDeviceOrientation,
          cameraPosition: AVCaptureDevice.Position
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      Objective-C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      Затем создайте объект метаданных:

      Быстрый

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Objective-C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. Создайте объект VisionImage , используя объект CMSampleBufferRef и метаданные поворота:

      Быстрый

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  3. Затем передайте изображение методу process(_:completion:) :

    Быстрый

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

    Objective-C

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

2. Извлечь текст из блоков распознанного текста

Если операция распознавания текста выполнена успешно, возвращается объект VisionDocumentText . Объект VisionDocumentText содержит полный текст, распознанный на изображении, и иерархию объектов, отражающую структуру распознанного документа:

Для каждого объекта VisionDocumentTextBlock , VisionDocumentTextParagraph , VisionDocumentTextWord и VisionDocumentTextSymbol можно получить текст, распознанный в области, и ограничивающие координаты области.

Например:

Быстрый

let resultText = result.text
for block in result.blocks {
    let blockText = block.text
    let blockConfidence = block.confidence
    let blockRecognizedLanguages = block.recognizedLanguages
    let blockBreak = block.recognizedBreak
    let blockCornerPoints = block.cornerPoints
    let blockFrame = block.frame
    for paragraph in block.paragraphs {
        let paragraphText = paragraph.text
        let paragraphConfidence = paragraph.confidence
        let paragraphRecognizedLanguages = paragraph.recognizedLanguages
        let paragraphBreak = paragraph.recognizedBreak
        let paragraphCornerPoints = paragraph.cornerPoints
        let paragraphFrame = paragraph.frame
        for word in paragraph.words {
            let wordText = word.text
            let wordConfidence = word.confidence
            let wordRecognizedLanguages = word.recognizedLanguages
            let wordBreak = word.recognizedBreak
            let wordCornerPoints = word.cornerPoints
            let wordFrame = word.frame
            for symbol in word.symbols {
                let symbolText = symbol.text
                let symbolConfidence = symbol.confidence
                let symbolRecognizedLanguages = symbol.recognizedLanguages
                let symbolBreak = symbol.recognizedBreak
                let symbolCornerPoints = symbol.cornerPoints
                let symbolFrame = symbol.frame
            }
        }
    }
}

Objective-C

NSString *resultText = result.text;
for (FIRVisionDocumentTextBlock *block in result.blocks) {
  NSString *blockText = block.text;
  NSNumber *blockConfidence = block.confidence;
  NSArray<FIRVisionTextRecognizedLanguage *> *blockRecognizedLanguages = block.recognizedLanguages;
  FIRVisionTextRecognizedBreak *blockBreak = block.recognizedBreak;
  CGRect blockFrame = block.frame;
  for (FIRVisionDocumentTextParagraph *paragraph in block.paragraphs) {
    NSString *paragraphText = paragraph.text;
    NSNumber *paragraphConfidence = paragraph.confidence;
    NSArray<FIRVisionTextRecognizedLanguage *> *paragraphRecognizedLanguages = paragraph.recognizedLanguages;
    FIRVisionTextRecognizedBreak *paragraphBreak = paragraph.recognizedBreak;
    CGRect paragraphFrame = paragraph.frame;
    for (FIRVisionDocumentTextWord *word in paragraph.words) {
      NSString *wordText = word.text;
      NSNumber *wordConfidence = word.confidence;
      NSArray<FIRVisionTextRecognizedLanguage *> *wordRecognizedLanguages = word.recognizedLanguages;
      FIRVisionTextRecognizedBreak *wordBreak = word.recognizedBreak;
      CGRect wordFrame = word.frame;
      for (FIRVisionDocumentTextSymbol *symbol in word.symbols) {
        NSString *symbolText = symbol.text;
        NSNumber *symbolConfidence = symbol.confidence;
        NSArray<FIRVisionTextRecognizedLanguage *> *symbolRecognizedLanguages = symbol.recognizedLanguages;
        FIRVisionTextRecognizedBreak *symbolBreak = symbol.recognizedBreak;
        CGRect symbolFrame = symbol.frame;
      }
    }
  }
}

Следующие шаги