Система распознавания документов может найти на одном изображении сразу несколько областей, похожих на таблицы. Среди них может быть нужная нам таблица, другая таблица, блок реквизитов, рамка или просто удачно выровненный текст. И прежде чем запускать распознавание ячеек, нужно понять, какой именно из найденных регионов содержит целевую таблицу.В Smart Engines мы решили эту задачу необычным способом: превратили каждый табличный регион в строку, которая одновременно описывает его геометрию и текстовое содержимое, а затем стали идентифицировать нужную таблицу с помощью регулярных выражений. Получился быстрый детерминированный алгоритм, способный выполнять около 2000 идентификаций таблиц в секунду даже на обычном мобильном процессоре.Сегодня в статье мы расскажем, как устроено строковое представление таблицы, почему оно позволяет отказаться от набора хрупких эвристик и как более точная идентификация нужной таблицы влияет на итоговое качество распознавания документа. Читать далее
Система распознавания документов может найти на одном изображении сразу несколько областей, похожих на таблицы. Среди них может быть нужная нам таблица, другая таблица, блок реквизитов, рамка или просто удачно выровненный текст. И прежде чем запускать распознавание ячеек, нужно понять, какой именно из найденных регионов содержит целевую таблицу.
В Smart Engines мы решили эту задачу необычным способом: превратили каждый табличный регион в строку, которая одновременно описывает его геометрию и текстовое содержимое, а затем стали идентифицировать нужную таблицу с помощью регулярных выражений. Получился быстрый детерминированный алгоритм, способный выполнять около 2000 идентификаций таблиц в секунду даже на обычном мобильном процессоре.
Сегодня в статье мы расскажем, как устроено строковое представление таблицы, почему оно позволяет отказаться от набора хрупких эвристик и как более точная идентификация нужной таблицы влияет на итоговое качество распознавания документа.
Почему сложно идентифицировать нужную таблицуСовременная система распознавания документов может найти на изображении документа сразу несколько потенциальных табличных областей. Среди них встречаются настоящие таблицы, их фрагменты, декоративные рамки и случайно выровненный текст. Более того, кандидат может действительно оказаться таблицей — но не той, которую нам нужно распознавать.
Например, в счёте-фактуре помимо основной таблицы с товарами есть блок банковских реквизитов. Он тоже имеет строки, столбцы и границы и может быть даже шире основной таблицы. Поэтому просто найти на изображении «что-то похожее на таблицу» недостаточно — нужно идентифицировать именно целевую таблицу документа.
Традиционно для этого можно использовать простые эвристики: выбирать самый широкий регион, оценивать количество вертикальных и горизонтальных линий или искать ключевые слова. Но качество сканов, разрывы линий, искажения изображения и ошибки OCR делают такие правила ненадёжными.
Нам понадобился быстрый способ проверить каждый найденный регион и определить, соответствует ли он структуре именно той таблицы, которую мы ищем.
Как мы превращаем таблицу в строкуВ основе нашего подхода лежит простая идея. Вместо того чтобы анализировать сложную двумерную структуру каждой потенциальной таблицы, мы проецируем все элементы ее зоны на ось X. Получается одномерная строка, которая кодирует структуру таблицы слева направо.
На вход алгоритм получает три типа данных: прямоугольную зону таблицы, набор вертикальных линий внутри нее и слова, распознанные OCR. Все эти объекты проецируются на ширину таблицы: для линий и текста строятся отдельные гистограммы, где каждая позиция по оси X соответствует своему биту. Если в позиции есть вертикальная линия, бит получает метку ‘I’ с цифрой качества от 0 до 9, которая отражает высоту линии относительно высоты зоны таблицы. Например, строка "I5" означает линию, покрывающую 50–60% высоты таблицы. Если в этой позиции есть слово, соответствующий бит получает метку ‘x’. Пустые позиции обозначаются точками ‘.’. Затем гистограммы объединяются: для каждой позиции по оси X выбирается наиболее значимый символ (линия имеет приоритет над текстом, текст - над пустотой), и таким образом формируется единая строка-описание. Затем последовательные символы 'x' заменяются на реальный текст, а лишние точки удаляются. Все слова в текущем столбце объединяются через пробел. На рисунке ниже показан пример такого преобразования.

Таким образом, для каждого найденного табличного региона мы получаем компактную сигнатуру, описывающую одновременно его структуру и содержимое. Теперь задача идентификации целевой таблицы сводится к проверке того, соответствует ли эта строка ожидаемому шаблону.
Регулярные выражения как мощный инструмент валидацииТеперь, когда у нас есть строковое представление таблицы, мы можем проверить, действительно ли она соответствует тому, что мы ожидаем. Будем делать это с помощью регулярных выражений. Это стандартный и давно известный способ описания допустимых строковых паттернов, который благодаря своей гибкости и выразительности используется в самых разных областях.
В нашем случае регулярные выражения идеально подходят, потому что позволяют описать структуру таблицы одной строкой. Например, шаблон (I[6-9]){9,13} мгновенно идентифицирует таблицы с 9-13 столбцами, где каждая линия имеет высоту не менее 60% от высоты таблицы. А выражение $I[6-9]((?:(?!I[6-9]).)*ТОВАР.*?I[6-9]){9,13}$ одновременно проверяет и количество столбцов, и наличие ключевого слова “ТОВАР”, и всё это одной строкой, заменяющей десятки строк процедурного кода.
Особую ценность регулярные выражения представляют при работе с документами низкого качества. Нежадные квантификаторы (I\d.*?I\d) помогают находить линии даже при разрывах, поддержка Unicode позволяет работать с кириллицей, а альтернация (Номер|№) - учитывать вариативность заголовков и ошибки OCR.
В нашей системе мы используем два основных сценария проверки:
Идентификация по фиксированной структуре. Когда структура таблицы строго определена (как в типовых формах или счетах-фактурах), мы используем жесткие регулярные выражения, которые проверяют точное количество столбцов и их расположение. Например, шаблон $I[6-9]((?:(?!I[6-9]).)*?I[6-9]){9,13}$ проверяет, что в таблице ровно 9-13 столбцов с линиями высокого качества.
Идентификация по характерным столбцам. Этот сценарий применяется, когда нам известны категории столбцов, но их порядок может меняться от изображения к изображению. В этом случае мы задаем массив обязательных столбцов и проверяем их наличие, например: [‘I[6-9].*КОЛ-ВО.*I[6-9]’,‘I[6-9].*№\s?\d.*I[6-9]’]. Дополнительно можно проверить формат данных: например, что в столбце указана денежная сумма (\d+[\.,]\d{2}).
В результате вместо набора отдельных процедурных проверок получаем быстрое, надежное и компактное правило идентификации целевой таблицы.
Как это работает на реальных документахМы внедрили наш подход в систему Smart Document Engine и протестировали на двух типах документов, наиболее распространенных в российском делопроизводстве: актах и счетах-фактурах. Оба типа имеют простую табличную структуру, но при этом создают разные проблемы для идентификации.
Для экспериментов мы использовали реальные данные, собранные Smart Engines. Всего в датасете было 748 актов (607 таблиц) и 3442 счета-фактуры (2447 таблиц). Документы отражали разные реальные условия: различное качество сканов, освещение на фотографиях и другие артефакты съёмки. Каждая таблица была размечена вручную как двумерный массив с содержимым каждой ячейки.
Акты
Главная сложность актов в том, что их таблицы не имеют фиксированного формата: количество столбцов, их названия и порядок могут меняться. Раньше система полагалась на выбор наибольшей по ширине таблицы или поиск слов в заголовках. Но для более чем 100 типов документов поддерживать такой список правил очень сложно.
Для проверки нашего подхода мы построили регулярное выражение, которое ищет ключевые слова, типичные для актов. Причём некоторые слова обрезаны, чтобы учитывать возможные ошибки OCR:
.*(АРТИК|НАИМ|РАБОТ|УСЛУГ|ТОВАР|КОЛ|ЦЕНА|СУММА|РУБ).*

Результат: ошибки определения строк сократились на 23,53%, ошибки определения столбцов на 16%. Качество распознавания ячеек улучшилось на 1.23%.
Счета-фактуры
Здесь проблема другого рода. В счетах-фактурах есть блок с реквизитами получателя, который обрамлен рамкой, состоит из трёх колонок и визуально очень похож на таблицу. При этом он может быть такой же ширины или даже шире, чем основная таблица документа. Выбор наибольшей по ширине таблицы здесь не сработает.
Регулярное выражение ^(?:(?!((БАНК ПОЛУЧАТЕЛЯ)|(ПОЛУЧАТЕЛЬ)|(БИК))).)*$ исключает области, в которых встречаются ключевые слова из блока реквизитов.
Результат: ошибки определения строк сократились на 35,87%, ошибки определения столбцов - на 26,36%, а качество распознавания ячеек улучшилось на 9.97%. Хоть мы не вмешивались в работу OCR, качество выросло, потому что теперь распознаются те ячейки, которые действительно нужны.
2000 идентификаций в секундуИдентификация — промежуточный этап пайплайна, поэтому принципиально важно, чтобы он сам не становился вычислительно дорогим.
На ARMv8 64-bit наш алгоритм идентифицирует один табличный регион за 0,49 мс. Это около 2000 проверок кандидатов в секунду.
Для оценки вычислительной стоимости мы также проверили LLM-подход на Llama 3. На ПК с AMD Ryzen 5 5600X и 64 ГБ RAM обработка одного кандидата занимала 1–3 секунды. Сравнение проводилось на разном оборудовании и не является бенчмарком двух реализаций; оно показывает другое: использование большой модели для промежуточной идентификации табличного региона оказывается вычислительно избыточным.
Бонус: определяем тип документа по его таблицеМы решили пойти дальше и проверить, можно ли использовать наш подход не только для идентификации таблиц, но и для определения типа документа. Гипотеза была простой: структура таблицы (количество и порядок столбцов, наличие ключевых слов) настолько характерна для каждого типа документа, что по ней можно различить даже визуально похожие классы.
Это важно, потому что многие типы финансовых документов содержат одни и те же поля: отправитель, получатель, наименования товаров, итоговая сумма. Система, которая классифицирует документы по наличию или отсутствию этих полей, часто ошибается. Мы предположили, что структура таблицы - это тот признак, который остается уникальным даже при общих полях.
Для проверки мы выбрали 7 типов российских финансовых документов: акт приема-передачи прав, акт выполненных работ, товарно-транспортная накладная, общий акт, акт сверки, акт стоимости и объема работ, бухгалтерский баланс. В датасете было 73 изображения с неравномерным распределением (от 6 до 27 экземпляров на тип документа), что отражает реальную частоту встречаемости документов.
Для каждого типа мы составили регулярное выражение, которое выделяет его среди остальных (см. таблицу). Некоторые правила семантические (ищут ключевые слова в таблице), другие структурные (определяют необходимое количество столбцов или их формат).
Тип документа | Шаблон таблицы |
Акт приема-передачи прав | ^.*ПРОГРАММНЫЙ ПРОДУКТ.*$ |
Акт выполненных работ | ^.*((ОБЪЕМ)|(ОБЪЕКТ)).*$ |
Товарно-транспортная накладная | ^(.*I9\.* 14 \.*I9.*)|(.*ГРУЗ.*)$ |
Акт | .*(?:АРТИКУЛ|УСЛУГ|ТОВАР|СУММА).* |
Акт сверки | ^.*САЛЬДО НА.*$ |
Акт стоимости и объема работ | ^.*С НАЧАЛА ПРОВЕДЕНИЯ РАБОТ.*$ |
Бухгалтерский баланс | ^.*((НАИМЕНОВАНИЕ ПОКАЗАТЕЛЯ)|(АКТИВ)).*$ |
Мы сравнили два подхода: базовую классификацию документов по полям и классификацию по таблицам с помощью регулярных выражений. Полная таблица с точностью (precision), полнотой (recall) и F-мерой для каждого типа документа представлена ниже.
Тип документа | Базовая классификация | Классификация по таблицам | ||||
Precision | Recall | F1 | Precision | Recall | F1 | |
Акт приема-передачи прав | 0.4 | 0.67 | 0.5 | 0.55 | 1.0 | 0.7 |
Акт выполненных работ | 0.86 | 0.86 | 0.86 | 1.0 | 1.0 | 1.0 |
Товарно-транспортная накладная | 1.0 | 0.91 | 0.95 | 1.0 | 1.0 | 1.0 |
Акт | 0.87 | 0.78 | 0.82 | 1.0 | 0.81 | 0.89 |
Акт сверки | 1.0 | 0.86 | 0.92 | 1.0 | 1.0 | 1.0 |
Акт стоимости и объема работ | 0.89 | 0.89 | 0.89 | 1.0 | 1.0 | 1.0 |
Бухгалтерский баланс | 0.86 | 1.0 | 0.92 | 1.0 | 1.0 | 1.0 |
Результаты оказались впечатляющими. Для пяти из семи типов точность и полнота достигли 100%. Проблема осталась с общим актами: из-за разнообразия его табличных форматов не удалось составить одно регулярное выражение, которое покрывало бы все варианты. Особенно показателен случай акта приема-передачи прав: базовый метод давал точность всего 40% (система часто путала его с другими документами), а наш подход поднял точность до 55%, а полноту - с 67% до 100%. Это значит, что система перестала пропускать документы этого типа, а ошибки сократились почти вдвое.
Таким образом, если строка-описание таблицы соответствует шаблону, то мы знаем, что это за документ, и можем выбрать правильную стратегию для его дальнейшей обработки. Это не только быстрый, но и надежный инструмент классификации, который легко масштабируется на новые типы документов: достаточно добавить новое регулярное выражение.
Вместо заключенияВ эпоху нейросетей легко забыть простой инженерный принцип: хороший алгоритм начинается не с выбора инструмента, а с правильной постановки задачи. Нам не требовалось заново понимать весь документ — нужно было лишь определить, какой из найденных регионов содержит нужную таблицу.
Мы представили двумерную структуру таблицы в виде строки, сохранив её геометрию и содержание. И сложная на первый взгляд задача идентификации свелась к сопоставлению с шаблоном с помощью обычных регулярных выражений.
Возможно, в этом и заключается главный результат. Не всегда нужно усложнять алгоритм, чтобы решить сложную задачу. Иногда достаточно найти представление, в котором сама задача становится простой. А 2000 идентификаций таблиц в секунду — уже следствие.
P.S. Подписывайтесь на наш Telegram-канал! Там много интересного про распознавание паспорта и других документов.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Как мы описали 15 000 таблиц за полгода вместо 500 за год — и перестали писать документацию руками | 0 | 9.77 | 29-07-2026 |
| 2 | Российский разработчик получил патент США на распознавание документов в браузере | 0 | 7.41 | 04-08-2026 |
| 3 | Российский разработчик получил патент США на распознавание документов в браузере | 0 | 7.41 | 04-08-2026 |
| 4 | Российский разработчик получил патент США на распознавание документов в браузере | 0 | 7.41 | 04-08-2026 |
| 5 | 984 из 1000 паспортов без ошибок: разбираем точность ИИ | 0 | 7 | 26-06-2026 |
| 6 | От OCR к смыслу: как мы научили модель понимать, кто кому отец, мать, жених и свидетель | 0 | 7.57 | 27-05-2026 |
| 7 | Как я первый в Казахстане измерил ИИ видимость банков (и что из этого вышло) | 0 | 8.08 | 12-08-2026 |
| 8 | Когда контекстное окно кончается, а проект — нет | 5 | 7 | 23-06-2026 |
| 9 | Как желание быстрее читать чужой код превратилось в войну с недетерминизмом LLM | 0 | 5 | 28-06-2026 |