Хотите узнать больше? посетите www.Shibumi-ai.com

Прочтите здесь новую версию этого сообщения.

вступление

OCR, или оптическое распознавание символов, является одной из наиболее ранних задач компьютерного зрения, поскольку в некоторых аспектах не требует глубокого обучения. Поэтому еще до бума глубокого обучения в 2012 году существовали различные реализации OCR, а некоторые даже относились к 1914 году (!).

Это заставляет многих думать, что задача OCR «решена», что это уже не проблема. Другое мнение, которое исходит из аналогичных источников, заключается в том, что OCR не требует глубокого обучения, или, другими словами, использование глубокого обучения для распознавания текста является излишним.

Любой, кто занимается компьютерным зрением или машинным обучением в целом, знает, что не существует такой вещи, как решенная задача, и этот случай не исключение. Напротив, OCR дает очень хорошие результаты только в очень конкретных случаях использования, но в целом все еще считается сложной задачей.

Кроме того, действительно существуют хорошие решения для определенных задач распознавания текста, не требующие глубокого обучения. Однако, чтобы действительно шагнуть вперед к лучшим и более общим решениям, необходимо глубокое обучение.

почему я пишу об оптическом распознавании текста?

Как и многие из моих работ / рецензий, это тоже началось как проект для клиента. Меня попросили решить конкретную задачу OCR.

Во время и после работы над этой задачей я сделал некоторые выводы и идеи, которыми стоит поделиться. Кроме того, после интенсивной работы над задачей трудно остановиться и выбросить ее, поэтому я продолжаю свое исследование и надеюсь найти еще лучшее и более обобщенное решение.

Что вы здесь найдете

В этом посте я рассмотрю некоторые стратегии, методы и логику, используемые для решения различных задач распознавания текста, и поделюсь некоторыми полезными подходами. В последней части мы рассмотрим реальную проблему с помощью кода. Это не следует рассматривать как исчерпывающий обзор (к сожалению), поскольку глубина, история и широта подходов слишком широки для такого рода сообщений в блоге.

Однако, как всегда, я не избавлю вас от ссылок на статьи, наборы данных, репозитории и другие соответствующие сообщения в блогах.

Типы OCR

Как указывалось ранее, OCR имеет несколько значений. В самом общем смысле это относится к извлечению текста из любого возможного изображения, будь то стандартная печатная страница из книги или случайное изображение с граффити на нем («в дикой природе»). В промежутках вы можете найти множество других задач, таких как чтение автомобильных номеров, капчи без использования роботов, уличные знаки и т. Д.

Хотя каждый из этих вариантов имеет свои сложности, очевидно, что задача «в дикой природе» - самая сложная.

Из этих примеров мы можем выделить некоторые атрибуты задач OCR:

  • Плотность текста: на печатной / письменной странице текст плотный. Однако, учитывая изображение улицы с одним дорожным знаком, текст получается разреженным.
  • Структура текста: текст на странице структурирован, в основном, в виде строгих строк, в то время как обычный текст может быть разбросан повсюду, с разным поворотом.
  • Шрифты: печатные шрифты проще, поскольку они более структурированы, чем зашумленные рукописные символы.
  • Тип символа. Текст может быть на разных языках, которые могут сильно отличаться друг от друга. Кроме того, структура текста может отличаться от цифр, например, номера домов и т. Д.
  • Артефакты: очевидно, что изображения на открытом воздухе намного шумнее, чем удобный сканер.
  • Расположение: некоторые задачи включают обрезанный / центрированный текст, в то время как в других текст может располагаться в произвольных местах изображения.

Наборы данных / Задачи

СВХН

Хорошее место для начала - набор данных SVHN, Street View House Numbers. Как следует из названия, это набор данных с номерами домов, извлеченный из просмотра улиц Google. Сложность задания средняя. Цифры бывают разных форм и стилей написания, однако каждый номер дома расположен в середине изображения, поэтому обнаружение не требуется. Изображения не очень высокого разрешения, и их расположение может быть немного необычным.

Номерные знаки

Еще одна распространенная проблема, которая не очень сложна и полезна на практике, - это распознавание автомобильных номеров. Эта задача, как и большинство задач распознавания текста, требует обнаружения номерного знака, а затем распознавания его символов. Поскольку форма пластины относительно постоянна, некоторые подходы используют простой метод изменения формы до фактического распознавания цифр. Вот несколько примеров из Интернета:

  1. OpenALPR - очень надежный инструмент, не требующий глубокого обучения, для распознавания автомобильных номеров разных стран.
  2. Это репо предусматривает реализацию модели CRNN (о которой будет сказано ниже) для распознавания корейских номерных знаков.
  3. Supervise.ly, компания, предоставляющая услуги по обработке данных, написала об обучении распознавателя автомобильных номеров с использованием искусственных данных, сгенерированных их инструментом (искусственные данные также будут обсуждаться далее)

CAPTCHA

Поскольку в Интернете полно роботов, обычной практикой отличать их от реальных людей являются задачи по зрению, в частности, чтение текста, также известное как CAPTCHA. Многие из этих текстов случайны и искажены, что затрудняет чтение компьютером. Я не уверен, кто разработал CAPTCHA, предсказал достижения в области компьютерного зрения, однако большинство современных текстовых CAPTCHA не так уж сложно решить, особенно если мы не пытаемся решить их все сразу.

Адам Гейтгей (Adam Geitgey) предлагает хорошее руководство по решению некоторых CAPTCHA с помощью глубокого обучения, которое включает в себя еще раз синтез искусственных данных.

PDF OCR

Наиболее распространенный сценарий для OCR - распечатанный / pdf OCR. Структурированный характер печатных документов значительно упрощает их анализ. Большинство инструментов OCR (например, Tesseract) в основном предназначены для решения этой задачи и достижения хорошего результата. Поэтому я не буду подробно останавливаться на этой задаче в этом посте.

OCR в дикой природе

Это самая сложная задача OCR, поскольку она вводит в OCR все общие проблемы компьютерного зрения, такие как шум, освещение и артефакты. Некоторыми соответствующими наборами данных для этой задачи являются кокосовый текст и набор данных SVT, который, опять же, использует изображения просмотра улиц для извлечения текста.

Синтезированный текст

SynthText - это не набор данных и, возможно, даже не задача, но хорошая идея для повышения эффективности обучения - создание искусственных данных. Использование случайных символов или слов на изображении будет казаться более естественным, чем любой другой объект, из-за плоской природы текста.

Ранее мы видели генерацию данных для более простых задач, таких как CAPTCHA и номерной знак. Генерация текста в дикой природе немного сложнее. В задачу входит рассмотрение информации о глубине изображения. К счастью, SynthText - прекрасная работа, которая принимает изображения с вышеупомянутыми аннотациями и разумно разбрасывает слова (из набора данных группы новостей).

Чтобы «посыпанный» текст выглядел реалистично и практично, библиотека SynthText использует для каждого изображения две маски: одну глубины, а другую - сегментации. Если вы хотите использовать свои собственные изображения, вам также следует добавить эти данные.

  • Рекомендуется проверить репо и сгенерировать несколько изображений самостоятельно. Обратите внимание, что в репозитории используется устаревшая версия opencv и maptlotlib, поэтому могут потребоваться некоторые модификации.

Мнист

Хотя это не совсем задача OCR, невозможно написать об OCR и не включить пример Mnist. Самая известная проблема компьютерного зрения на самом деле не является рассматриваемой задачей и задачей распознавания текста, поскольку она содержит один символ (цифру) за раз и только 10 цифр. Однако это может намекнуть, почему OCR считается простым. Кроме того, в некоторых подходах каждая буква будет обнаруживаться отдельно, и тогда становятся актуальными Mnist-подобные (классификационные) модели ץ

Стратегии

Как мы уже видели и подразумевали, распознавание текста в основном состоит из двух этапов. Во-первых, вы хотите определить появление текста (ов) на изображении, может быть оно плотным (как в печатном документе) или разреженным (как текст без полей).

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

  1. Классические техники компьютерного зрения.
  2. Специализированное глубокое обучение.
  3. Стандартный подход глубокого обучения (обнаружение).

Разберем каждый из них:

1. Классические методы компьютерного зрения

Как было сказано ранее, компьютерное зрение уже давно решает различные задачи распознавания текста. Вы можете найти множество примеров в Интернете:

  • Великий Адриан Роузбрук имеет огромное количество руководств на своем сайте, таких как этот один, этот один и другие.
  • Переполнение стека также имеет некоторые жемчужины, подобные этому.

Подход классического резюме обычно утверждает:

  1. Примените фильтры, чтобы персонажи выделялись на фоне.
  2. Примените определение контура, чтобы распознавать символы один за другим.
  3. Примените классификацию изображений, чтобы идентифицировать символы.

Понятно, что если вторая часть выполнена хорошо, то третья часть будет легкой как с сопоставлением с образцом, так и с машинным обучением (например, Mnist).

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

Но когда персонажи становятся ближе друг к другу, все начинает ломаться:

Я на собственном опыте обнаружил, что когда вы начинаете возиться с параметрами, вы можете уменьшить количество таких ошибок, но, к сожалению, вызвать другие. Другими словами, если ваша задача не проста, эти методы не подходят.

2. Специализированные подходы к глубокому обучению

Наиболее успешные подходы к глубокому обучению отличаются своей универсальностью. Однако, учитывая атрибуты, описанные выше, специализированные сети могут быть очень полезными.

Я рассмотрю здесь неисчерпывающий образец некоторых известных подходов и сделаю очень быстрое резюме статей, в которых они представлены. Как всегда, каждая статья начинается со слов «задача X (распознавание текста) привлекает внимание в последнее время» и подробно описывает их метод. Внимательное чтение статей покажет, что эти методы собраны из частей предыдущих работ по глубокому обучению / распознаванию текста.

Результаты также подробно отображены, однако из-за множества различий в дизайне (включая незначительные различия в наборах данных) фактическое сравнение совершенно невозможно. Единственный способ узнать, насколько эффективны эти методы для вашей задачи, - это получить их код (лучше к худшему: найти официальное репо, найти неофициальное, но высоко оцененное репо, реализовать самостоятельно) и опробовать на своих данных.

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

ВОСТОК

EAST (Эффективный точный детектор текста сцены) - это простой, но мощный подход для обнаружения текста. Использование специализированной сети.

В отличие от других методов, которые мы обсудим, он ограничен только обнаружением текста (а не фактическим распознаванием), однако из-за его надежности стоит упомянуть о нем.
Еще одним преимуществом является то, что он также был добавлен в open-CV библиотеки (начиная с версии 4), чтобы вы могли легко ее использовать (см. руководство здесь).
Сеть на самом деле является версией хорошо известной U-Net, которая хороша для обнаружения функции, которые могут отличаться по размеру. Базовая основа прямой связи (как указано в статье, см. Рисунок ниже) этой сети может очень - в документе используется PVANet, однако реализация opencv использует Resnet . Очевидно, его также можно предварительно обучить (например, с помощью imagenet). Как и в U-Net, функции извлекаются с разных уровней сети.

Наконец, сеть допускает два типа выходных повернутых ограничивающих рамок: либо стандартный ограничивающий прямоугольник с углом поворота (параметры 2X2 + 1), либо «четырехугольник», который представляет собой просто повернутый ограничивающий прямоугольник с координатами всех вершин.

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

CRNN

Сверточно-рекуррентная нейронная сеть - это статья 2015 года, в которой предлагается гибридная (или трибридная?) Сквозная архитектура, которая предназначена для захвата слов в трехэтапном подходе.

Идея заключается в следующем: первый уровень - это стандартная полностью сверточная сеть. Последний слой сети определяется как векторный слой и делится на «характерные столбцы». Посмотрите на изображении ниже, как каждая такая колонка с характеристиками предназначена для представления определенного раздела в тексте.

После этого столбцы функций передаются в двунаправленный LSTM, который выводит последовательность и предназначен для поиска взаимосвязей между символами.

Наконец, третья часть - это слой транскрипции. Его цель состоит в том, чтобы взять беспорядочную последовательность символов, в которой одни символы являются избыточными, а другие пустыми, и использовать вероятностный метод, чтобы объединить и разобраться в этом.

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

Эта статья достигает высокого (›95%) уровня точности с фиксированной текстовой лексикой и разной степени успеха без нее.

STN-net / ПОСМОТРЕТЬ

SEE - Полуконтролируемое сквозное распознавание текста в сценах - это работа Кристиана Барци. Он и его коллеги применяют действительно сквозную стратегию для обнаружения и распознавания текста. Они используют очень слабый надзор (который они называют полунадзором в ином, чем обычно, смысле). поскольку они тренируют сеть с помощью только текстовой аннотации (без ограничивающих рамок). Это позволяет им использовать больше данных, но делает их процедуру обучения довольно сложной, и они обсуждают различные приемы, чтобы заставить ее работать, например, отказ от обучения изображениям с более чем двумя строками текста (по крайней мере, на первых этапах обучения).

У статьи есть более ранняя версия, которая называется STN OCR. В заключительной статье исследователи усовершенствовали свои методы и представления, а также сделали больший акцент на универсальности своего подхода из-за высокого качества результатов.

Название STN-OCR намекает на стратегию использования пространственного преобразователя (= STN, никакого отношения к недавнему преобразователю Google).

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

Затем другая сеть прямого распространения с LSTM наверху (хм… кажется, мы уже видели это раньше) для распознавания текста.

Здесь исследователи подчеркивают важность использования resnet (они используют его дважды), поскольку он обеспечивает «сильное» распространение на ранние уровни. Однако в наши дни такая практика вполне приемлема.

В любом случае, это интересный подход.

3. Стандартный подход к глубокому обучению.

Как следует из заголовка, после обнаружения «слов» мы можем применить стандартные подходы к обнаружению глубокого обучения, такие как SSD, YOLO и Mask RCNN. Я не буду подробно останавливаться на этих подходах, поскольку в Интернете имеется множество информации.

Я должен сказать, что в настоящее время это мой любимый подход, поскольку то, что мне нравится в глубоком обучении, - это философия «от начала до конца», когда вы применяете сильную модель, которая с некоторой настройкой решит почти все проблемы. В следующем разделе этого поста мы увидим, как это работает.

Однако SSD и другие модели обнаружения сталкиваются с трудностями, когда речь идет о плотных, аналогичных классах, как описано здесь. Я нахожу это немного ироничным, поскольку на самом деле в моделях глубокого обучения гораздо труднее распознавать цифры и буквы, чем распознавать гораздо более сложные и сложные объекты, такие как собаки, кошки или люди. Они не стремятся достичь желаемой точности, и поэтому процветают специализированные подходы.

Практический пример

Итак, после всех разговоров, пора запачкать руки и попробовать себя в моделировании. Мы попробуем решить задачу СВХН. Данные SVHN содержат три разных набора данных: train, test и extra. Различия не очевидны на 100%, однако самый большой дополнительный набор данных (с ~ 500K выборок) включает изображения, которые легче распознать. Так что для этого дубля мы его воспользуемся.

Чтобы подготовиться к задаче, сделайте следующее:

  • Вам понадобится базовый графический процессор с Tensorflow≥1.4 и Keras≥2.
  • Клонируйте проект SSD_Keras отсюда.
  • Загрузите предварительно обученную модель SSD300 на наборе данных coco отсюда.
  • Скопируйте репозиторий этого проекта отсюда.
  • Загрузите файл extra.tar.gz, содержащий дополнительные изображения набора данных SVHN.
  • Обновите все соответствующие пути в json_config.json в этом репозитории проекта.

Чтобы эффективно следовать этому процессу, вы должны прочитать приведенную ниже инструкцию вместе с запуском записной книжки ssd_OCR.ipynb из репозитория проекта.

И… Вы готовы к старту!

Шаг 1: проанализируйте данные

Как ни крути, но золотого формата представления данных в задачах детектирования нет. Некоторые хорошо известные форматы: coco, via, pascal, xml. И многое другое. Например, набор данных SVHN аннотируется неясным форматом .mat. К счастью для нас, эта суть предоставляет изящный скрипт read_process_h5 для преобразования файла .mat в стандартный json, и вы должны пойти на шаг вперед и преобразовать его в формат паскаль, например:

def json_to_pascal(json, filename): #filename is the .mat file
    # convert json to pascal and save as csv
    pascal_list = []
    for i in json:
        for j in range(len(i['labels'])):
            pascal_list.append({'fname': i['filename'] 
            ,'xmin': int(i['left'][j]), 'xmax': int(i['left'][j]+i['width'][j])
            ,'ymin': int(i['top'][j]),  'ymax': int(i['top'][j]+i['height'][j])
            ,'class_id': int(i['labels'][j])})
    df_pascal = pd.DataFrame(pascal_list,dtype='str')
    df_pascal.to_csv(filename,index=False)
p = read_process_h5(file_path)
json_to_pascal(p, data_folder+'pascal.csv')

Теперь у нас должен быть файл pascal.csv, который является более стандартным и позволит нам продвигаться вперед. Если преобразование происходит слишком медленно, обратите внимание, что нам не нужны все образцы данных. ~ 10К будет достаточно.

Шаг 2: посмотрите данные

Перед тем, как начать процесс моделирования, вам лучше немного изучить данные. Я предлагаю только быструю функцию для проверки работоспособности, но я рекомендую вам провести дальнейший анализ:

def viz_random_image(df):
    file = np.random.choice(df.fname)
    im = skimage.io.imread(data_folder+file)
    annots =  df[df.fname==file].iterrows()
    plt.figure(figsize=(6,6))
    plt.imshow(im)
    current_axis = plt.gca()
    for box in annots:
        label = box[1]['class_id']
        current_axis.add_patch(plt.Rectangle(
            (box[1]['xmin'], box[1]['ymin']), box[1]['xmax']-box[1]['xmin'],
            box[1]['ymax']-box[1]['ymin'], color='blue', fill=False, linewidth=2))  
        current_axis.text(box[1]['xmin'], box[1]['ymin'], label, size='x-large', color='white', bbox={'facecolor':'blue', 'alpha':1.0})
        plt.show()

viz_random_image(df)

Для следующих шагов я предоставляю utils_ssd.py в репозитории, который облегчает обучение, загрузку веса и т. Д. Часть кода взята из репозитория SSD_Keras, который также широко используется.

Шаг 3: выбор стратегии

Как обсуждалось ранее, у нас есть много возможных подходов к решению этой проблемы. В этом руководстве я воспользуюсь стандартным подходом к обнаружению глубокого обучения и буду использовать модель обнаружения SSD. Мы будем использовать реализацию SSD keras отсюда. Это хорошая реализация PierreLuigi. Хотя у него меньше звезд на GitHub, чем у реализации rykov8, он кажется более обновленным и его легче интегрировать. Это очень важный момент, на который следует обратить внимание при выборе проекта, который вы собираетесь использовать. Другими хорошими вариантами будут модель YOLO и Mask RCNN.

Шаг 4. Загрузите и обучите модель SSD

Некоторые определения

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

Начните с импорта:

import os
import sys
import skimage.io
import scipy
import json
with open('json_config.json') as f:     json_conf = json.load(f)
ROOT_DIR = os.path.abspath(json_conf['ssd_folder']) # add here mask RCNN path
sys.path.append(ROOT_DIR)

import cv2
from utils_ssd import *
import pandas as pd
from PIL import Image

from matplotlib import pyplot as plt

%matplotlib inline
%load_ext autoreload
% autoreload 2

и еще несколько определений:

task = 'svhn'
labels_path = f'{data_folder}pascal.csv'
input_format = ['class_id','image_name','xmax','xmin','ymax','ymin' ]
    
df = pd.read_csv(labels_path)

Конфигурации модели:

class SVHN_Config(Config):
    batch_size = 8
    
    dataset_folder = data_folder
    task = task
    
    labels_path = labels_path

    input_format = input_format

conf=SVHN_Config()

resize = Resize(height=conf.img_height, width=conf.img_width)
trans = [resize]

Определите модель, загрузите вес

Как и в большинстве случаев глубокого обучения, мы не будем начинать обучение с нуля, а загрузим предварительно натренированные веса. В этом случае мы загрузим веса модели SSD, обученной на наборе данных COCO, который имеет 80 классов. Очевидно, что наша задача имеет только 10 классов, поэтому после загрузки весов мы восстановим верхний слой так, чтобы он имел нужное количество выходов. Делаем это в функции init_weights. Примечание: правильное количество выходов в этом случае составляет 44: 4 для каждого класса (координаты ограничивающего прямоугольника) и еще 4 для класса background / none.

learner = SSD_finetune(conf)
learner.get_data(create_subset=True)

weights_destination_path=learner.init_weights()

learner.get_model(mode='training', weights_path = weights_destination_path)
model = learner.model
learner.get_input_encoder()
ssd_input_encoder = learner.ssd_input_encoder

# Training schedule definitions
adam = Adam(lr=0.0002, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0) 
ssd_loss = SSDLoss(neg_pos_ratio=3, n_neg_min=0, alpha=1.0)
model.compile(optimizer=adam, loss=ssd_loss.compute_loss)

Определите загрузчики данных

train_annotation_file=f'{conf.dataset_folder}train_pascal.csv'
val_annotation_file=f'{conf.dataset_folder}val_pascal.csv'
subset_annotation_file=f'{conf.dataset_folder}small_pascal.csv'
batch_size=4
ret_5_elements={'original_images','processed_images','processed_labels','filenames','inverse_transform'}
train_generator = learner.get_generator(batch_size, trans=trans, anot_file=train_annotation_file,
                  encoder=ssd_input_encoder)
val_generator = learner.get_generator(batch_size,trans=trans, anot_file=val_annotation_file,
                 returns={'processed_images','encoded_labels'}, encoder=ssd_input_encoder,val=True)

5. Обучение модели.

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

learner.init_training()
history = learner.train(train_generator, val_generator, steps=100,epochs=80)

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

Репозиторий SSD_Keras обрабатывает сохранение модели почти после каждой эпохи, поэтому вы можете загрузить модели позже, просто изменив строку weights_destination_path, чтобы она равнялась пути

weights_destination_path = <path>

Если вы следовали моим инструкциям, вы сможете обучить модель. Ssd_keras предоставляет еще несколько функций, например, увеличение данных, различные загрузчики и оценщик. После короткой тренировки я достиг ›80 МАР.

Насколько высоко вы достигли?

Резюме

В этом посте мы обсудили различные проблемы и подходы в области распознавания текста. Как и многие проблемы в глубоком обучении / компьютерном зрении, здесь гораздо больше, чем кажется на первый взгляд. Мы видели много подзадач и несколько разных подходов к их решению, ни одна из которых в настоящее время не служит серебряной пулей. С другой стороны, мы убедились, что достичь предварительных результатов без особых хлопот не так уж и сложно.

Надеюсь, вам понравилось!