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

С помощью ML Kit можно распознавать текст на изображениях или видео, например текст на дорожном знаке. Основные характеристики этой функции:

Text Recognition v2 API
ОписаниеРаспознавать текст на изображениях и видео. Поддерживаются латиница, китайский, деванагари, японский и корейский алфавиты, а также широкий спектр языков.
Названия SDKGoogleMLKit/TextRecognition
GoogleMLKit/TextRecognitionChinese
GoogleMLKit/TextRecognitionDevanagari
GoogleMLKit/TextRecognitionJapanese
GoogleMLKit/TextRecognitionKorean
РеализацияОбъекты статически связываются с приложением во время сборки.
Влияние на размер приложенияОколо 38 МБ на каждый скрипт SDK
ЭффективностьВ реальном времени на большинстве устройств для SDK с латинским шрифтом, медленнее для других.

Попробовать

Подготовка

  1. Добавьте в Podfile следующие поды ML Kit:
    # 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. После установки или обновления Pods откройте проект Xcode, используя файл .xcworkspace. ML Kit поддерживается в Xcode версии 12.4 и выше.

1. Создайте экземпляр TextRecognizer.

Создайте экземпляр TextRecognizer, вызвав +textRecognizer(options:) и передав параметры, связанные с SDK, который вы объявили как зависимость выше:

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. Подготовьте исходное изображение

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

Создайте объект VisionImage, используя UIImage или CMSampleBuffer.

Если вы используете UIImage, выполните следующие действия:

  • Создайте объект VisionImage с UIImage. Убедитесь, что вы указали правильное значение параметра .orientation.

    Swift

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

    Objective-C

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

Если вы используете CMSampleBuffer, выполните следующие действия:

  • Укажите ориентацию изображения, содержащегося в CMSampleBuffer.

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

    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;
      }
    }
          
  • Создайте объект VisionImage, используя объект CMSampleBuffer и ориентацию:

    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. Обработка изображения

Затем передайте изображение методу 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. Извлечение текста из блоков распознанного текста

Если операция распознавания текста выполнена успешно, возвращается объект Text. Объект Text содержит полный текст, распознанный на изображении, и ноль или более объектов TextBlock.

Каждый объект TextBlock представляет собой прямоугольный текстовый блок, который может содержать ноль или более объектов TextLine. Каждый объект TextLine содержит ноль или более объектов TextElement, представляющих слова и подобные им сущности, например даты и числа.

Для каждого объекта TextBlock, TextLine и TextElement можно получить текст, распознанный в регионе, и координаты границ региона.

Пример:

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

Требования к исходным изображениям

  • Чтобы ML Kit мог точно распознать текст, на входных изображениях должно быть достаточно пикселей, представляющих текст. Желательно, чтобы каждый символ был не менее 16 x 16 пикселей. Как правило, увеличение размера символов свыше 24 x 24 пикселей не повышает точность распознавания.

    Например, изображение размером 640 x 480 хорошо подойдет для сканирования визитной карточки, занимающей всю ширину изображения. Чтобы отсканировать документ, напечатанный на бумаге формата Letter, может потребоваться изображение размером 720 x 1280 пикселей.

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

  • Если вы распознаете текст в приложении, работающем в реальном времени, учитывайте общие размеры входных изображений. Небольшие изображения обрабатываются быстрее. Чтобы уменьшить задержку, убедитесь, что текст занимает как можно большую часть изображения, и снимайте изображения с более низким разрешением (учитывая требования к точности, упомянутые выше). Подробнее о том, как повысить эффективность…

Советы по повышению эффективности

  • Для обработки кадров видео используйте синхронный API детектора results(in:). Вызовите этот метод из функции captureOutput(_, didOutput:from:) объекта AVCaptureVideoDataOutputSampleBufferDelegate, чтобы синхронно получить результаты из заданного кадра видео. Установите для параметра AVCaptureVideoDataOutput значение alwaysDiscardsLateVideoFrames, чтобы ограничить количество вызовов детектора.true Если во время работы детектора станет доступен новый кадр видео, он будет отброшен.
  • Если вы используете результаты работы детектора для наложения графики на входное изображение, сначала получите результат от ML Kit, а затем отрисуйте изображение и наложите графику за один шаг. Это позволит вам выполнять рендеринг на поверхности экрана только один раз для каждого обработанного входного кадра. Пример можно найти в функции updatePreviewOverlayViewWithLastFrame в образце быстрого запуска ML Kit.
  • Попробуйте снимать с более низким разрешением. Однако также учитывайте требования к размеру изображений в этом API.
  • Чтобы избежать снижения производительности, не запускайте одновременно несколько экземпляров TextRecognizer с разными параметрами скрипта.