Как распознавать текст на изображениях с помощью 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, чтобы синхронно получить результаты для заданного кадра видео. Оставьте для параметра alwaysDiscardsLateVideoFrames в AVCaptureVideoDataOutput значение true, чтобы ограничить количество вызовов детектора. Если во время работы детектора станет доступен новый кадр видео, он будет отброшен.
  • Если вы используете выходные данные детектора для наложения графики на входное изображение, сначала получите результат от ML Kit, а затем визуализируйте изображение и наложение за один шаг. Это позволит вам выполнять рендеринг на поверхности экрана только один раз для каждого обработанного входного кадра. Пример можно найти в разделе updatePreviewOverlayViewWithLastFrame в руководстве по быстрому началу работы с ML Kit.
  • Попробуйте сделать снимки с более низким разрешением. Однако не забывайте о требованиях к размерам изображений, которые предъявляет этот API.
  • Чтобы избежать снижения производительности, не запускайте одновременно несколько экземпляров TextRecognizer с разными параметрами скрипта.