...

Система оптического распознавания на базе ELMA CE + Tesseract OCR

Тема в разделе "Примеры реальных проектов", создана пользователем ahkyamov, 21 ноя 2019.

  1. ahkyamov

    ahkyamov Member

    Пришло мне тут в голову реализовать систему оптического распознавания (OCR) на базе бесплатных компонентов. В качестве основной задачи системе вменяется извлекать из сканированных документов данные. В сильно упрощенном варианте можно использовать эту систему для оцифровки текстов договоров, но это не интересно :)
    На первом этапе данные будут извлекаться по статичному шаблону. Да, это накладывает ряд ограничений:
    • мы не сможем качественно распознавать фотографии, так как на фотографии изображение искажается во всех измерениях
    • Нам доступно только распознавание документов по жесткому шаблону (сюда легко попадают всевозможные типовые документы/справки: паспорт, СНИЛС, свой вариант), но для начала уже неплохо
    Фактически задачи первого этапа мной уже выполнены, поэтому в ближайшее время в этом треде я выложу информацию, что и как мне удалось реализовать, что бы каждый из читателей смог повторить это, а может и улучшить для своих целей.
    После недолгих раздумий выбрана связка компонентов GhostScript (растеризация PDF, то есть превращение PDF в пригодный для распознавания через Tesseract OCR формат) + standalone Tesseract OCR (распознавание) + ELMA CE (реализация логики)

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

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

    Итак, скачиваем и устанавливаем необходимые компоненты, а именно:
    https://www.ghostscript.com/download/gsdnld.html тут берем GhostScript (у меня версия 9.27x64)
    https://github.com/UB-Mannheim/tesseract/wiki тут берем Tesseract (https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-w64-setup-v5.0.0-alpha.20190708.exe). "tesseract-ocr-w64-setup-v5.0.0-alpha.20190708.exe (64 bit) resp" - я взял этот. Основная домашняя Tesseract OCR тут https://github.com/tesseract-ocr/tesseract/wiki. При установке выбираем необходимые языки, они скачаются автоматически. Я выбрал для своих экспериментов Russian и English. Если на сервере, где будет устанавливаться всё это великолепие, нет интернета, можно скачать traineddata для нужных языков тут https://github.com/tesseract-ocr/tessdata_best
    https://www.elma-bpm.ru/community/ - тут берем ELMA CE
     
  2. ahkyamov

    ahkyamov Member

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

    Создаем объектную модель в Дизайнере ELMA, я создал на первом этапе 4 сущности: Справочник настроек модуля (OCRSettings), Шаблон распознавания (OCRTemplate), Элемент распознавания (OCRItem) и Результат распознавания (OCRResult).
    Сразу обращу внимание, что OCRItem содержит в себе ссылки на OCRTemplate и OCRResult, а значит эти объекты надо создать до того, как добавить соответствующие атрибуты в свойства OCRItem, опубликовать их и перезапустить сервер. Иначе будут ошибки при попытке опубликовать OCRItem.
    Выглядит это всё примерно так:
    [​IMG]
    [​IMG]

    [​IMG]
    [​IMG]

    [​IMG]
    [​IMG]

    [​IMG]
    [​IMG]
     
    Последнее редактирование: 22 ноя 2019
  3. ahkyamov

    ahkyamov Member

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

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

    Для этого создаем процесс, я назвал его "Template Management Process", класс "P_TemplateManagementProces", таблица в БД "P_TemplateManagementProces" (здесь и далее привожу эти сведения, так как они используются в коде, и если вы решите не особо задумываясь копировать это решение к себе, лучше скопировать и это всё, чем потом обжечься с тем что что-то не работает. Картинки будут, но с картинок текст не скопировать :) ).

    Для процесса в папочке конфигурации (в моем случае C:\ELMA3-CE\UserConfigs\MyConfig) создаем такую вот структуру: \WebApplication\Views\Shared\Forms\Workflow\P_TemplateManagementProces (у меня в результате получилось C:\ELMA3-CE\UserConfigs\MyConfig\WebApplication\Views\Shared\Forms\Workflow\P_TemplateManagementProces). В крайней папке создаем файлик EditTemplate.cshtml, который отвечает за всю магию. У меня в нем следующий код (приложу файлом, ибо не лезет в сообщение 10к символов ну никак)
    https://forum.elma-bpm.ru/attachments/470/

    Нам потребуется для работы 3 контекстных переменных:
    1. Initiator (тип User) - кто запустил процесс, будем его записывать в шаблон что б в случае чего бить по руками за плохой шаблон
    2. Template (тип OCRTemplate) - шаблон, который мы редактируем
    3. TemplateZonesText (тип Текст) - служебная переменная что бы из нашего редактора переносить данные в шаблон

    И нам понадобится следующий код в сценарии для сохранения шаблона:
    Код:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using EleWise.ELMA.API;
    using EleWise.ELMA.Model.Common;
    using EleWise.ELMA.Model.Entities;
    using EleWise.ELMA.Model.Managers;
    using EleWise.ELMA.Model.Types.Settings;
    using EleWise.ELMA.Model.Entities.ProcessContext;
    using Context = EleWise.ELMA.Model.Entities.ProcessContext.P_TemplateManagementProces;
    
    namespace EleWise.ELMA.Model.Scripts
    {
    /// <summary>
    /// Модуль сценариев процесса "Template Management Process"
    /// </summary>
    /// <example>
    /// <![CDATA[
    /// >>>>>>>>>>>>>>>ВАЖНАЯ ИНФОРМАЦИЯ!!!<<<<<<<<<<<<<<<
    /// Данный редактор создан для работы с PublicAPI.
    /// PublicAPI предназначен для разработки сценариев ELMA.
    /// Например, с помощью PublicAPI можно добавить комментарий к документу:
    /// //Загружаем документ
    /// var doc = PublicAPI.Docflow.Document.Load(56);
    /// //Добавляем комментарий
    /// PublicAPI.Docflow.Document.AddComment(doc, "тут ваш комментарий");
    ///
    /// Более подробно про PublicAPI вы можете узнать тут: http://www.elma-bpm.ru/kb/article-642ApiRoot.html
    ///
    /// Если же вам нужна более серьёзная разработка, выходящая за рамки PublicAPI, используйте
    /// сторонние редакторы кода, такие как SharpDevelop и VisualStudio.
    /// Информацию по запуску кода в стороннем редакторе вы можете найти тут:
    /// http://www.elma-bpm.ru/kb/article-837.html
    /// ]]>
    /// </example>
    public partial class P_TemplateManagementProces_Scripts : EleWise.ELMA.Workflow.Scripts.ProcessScriptBase<Context>
    {
    const long XScale = 840;
    const long YScale = 1188;
    /// <summary>
    /// getTemplate
    /// </summary>
    /// <param name="context">Контекст процесса</param>
    public virtual void getTemplate (Context context)
    {
    if (context.Template == null)
    {
    return;
    }
    context.Template.Zones.Clear();
    var zoneList = context.TemplateZonesText.Split(new char[] {';'});
    foreach(var zoneText in zoneList)
    {
    if (zoneText.Contains("|"))
    {
    var args = zoneText.Split(new char[] { '|' });
    var zone = PublicAPI.Objects.UserObjects.UserOCRTemplate.Zones.Create();
    zone.Name = args[1];
    zone.X = OCR.OCRHelper.GetValueForTemplate(Convert.ToInt64(args[2]), XScale);
    zone.Y = OCR.OCRHelper.GetValueForTemplate(Convert.ToInt64(args[3]), YScale);
    zone.Width = OCR.OCRHelper.GetValueForTemplate(Convert.ToInt64(args[4]), XScale);
    zone.Height = OCR.OCRHelper.GetValueForTemplate(Convert.ToInt64(args[5]), YScale);
    context.Template.Zones.Add(zone);
    }
    }
    }
    }
    }
    

    Эти и остальные настройки отражены в картинках:
    [​IMG]
    [​IMG]
    [​IMG]
    [​IMG]
    [​IMG]
    [​IMG]
     

    Вложения:

  4. ahkyamov

    ahkyamov Member

    Ну и некоторые результаты:
    [​IMG]
    [​IMG]

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

    [​IMG]
    [​IMG]
    [​IMG]
     
  5. g.zykov

    g.zykov New Member

    Указано, что можно использовать для паспорта. Без какой-либо сложной предобработки, качество распознавания плохое в большинстве случаев с реальными примерами паспортов. Каким образом это исправилось? Самописный алгоритм? Сторонние библиотеки не вижу в описании.
     
  6. ahkyamov

    ahkyamov Member

    Предобработка через ImageMagic, сейчас уже делается замена на предобработку unmanaged кодом (реализуются готовые правила предобработки)
     
  7. ahkyamov

    ahkyamov Member

    то что будет размещено здесь эти этапы в любом случае не затронет, так как это тема отдельного разговора
     
  8. ahkyamov

    ahkyamov Member

    Совсем нет времени выложить более менее полное описание, поэтому сейчас закину просто код процесса распознавания и пару скринов. Карта процесса распознавания
    2020-02-25_09-28-56.png
     
  9. ahkyamov

    ahkyamov Member

    Контекст процесса распознавания
    2020-02-25_09-30-31.png
     
  10. ahkyamov

    ahkyamov Member

  11. ahkyamov

    ahkyamov Member

    Глобальный модуль (в сценарии процесса код менеджеров более актуален, глобальный модуль из-за требований перезапуска сервера я временно перестал обновлять, поэтому в этом посте AS IS) 2020-02-26_11-33-07.png

    Ну и во вложении код классов глобального модуля
     

    Вложения:

    1 это нравится
  12. neelix

    neelix New Member

    Подскажите пожалуйста, с чем связана ошибка при публикации глобального модуля GhostScriptManager ?
     

    Вложения:

    • err.jpg
      err.jpg
      Размер файла:
      31,5 КБ
      Просмотров:
      7
  13. ahkyamov

    ahkyamov Member

    просит добавить ссылку на сборку EleWise.ELMA.Common.dll
    upload_2020-8-5_9-49-1.png
     
  14. neelix

    neelix New Member

    Теперь вот такая ошибка.
     

    Вложения:

    • bl3rrwq.jpg
      bl3rrwq.jpg
      Размер файла:
      112,6 КБ
      Просмотров:
      6
  15. ahkyamov

    ahkyamov Member

    надо код из сценариев процесса взять, объектная модель менялась
    тут будет актуальный код для объектной модели
     

Поделиться: