28.10.2026

Camelot против PyMuPDF: таблицы из текстового слоя PDF

Таблицу в цифровом PDF можно вытащить из текстового слоя без OCR. Проверили Camelot 2.0 (режим stream) против PyMuPDF find_tables на 36 страницах с таблицами: на МСФО-отчётности он точнее по содержимому, но ошибается там, где решение должно быть «таблицы нет».

Задача

Таблицы в отчётности (МСФО, отчёты ЦБ) лучше читать из текстового слоя PDF, чем через OCR: это быстрее и числа не теряются. Для этого нужен детектор, который отвечает на два вопроса: есть ли на странице таблица и какие у неё ячейки. Если детектор ложно видит таблицу там, где её нет, страница уходит по неверному пути. Поэтому главный критерий здесь — ложные срабатывания, а не только точность ячеек.

Данные и эталон

  • 36 цифровых PDF-страниц с таблицами: отчётность МСФО (Азот, Химпром, РусНефть, Россети, ОЗОН, М.Видео, IFRS, и файлы HEAD и GCHE), отчёты ЦБ, препринты arXiv.
  • Эталон — вывод LightOnOCR-3 на тех же страницах (на рисунках — правая колонка). Это не ручная разметка: на трёх страницах без таблиц (из десяти) и на страницах с таблицами, которые я смотрел, эталон верный, на отдельных страницах он спорный. Поэтому часть выводов ниже сделана по картинкам, а не по числам.
  • Метрика — F1 по словам внутри таблиц, сравнение с эталоном. Она грубая: find_tables теряет пробелы в ячейках (ЭкскурсионноеобслуживаниевКирилло), Camelot захватывает обычный текст страницы. Оба артефакта занижают F1.
  • Два прохода на одной машине (CPU), методы чередовались.

Результаты

Критерий PyMuPDF find_tables Camelot stream Camelot lattice
Ложные таблицы на страницах без таблиц (10 страниц) 0 10 3
PDF с запретом извлечения текста (7 страниц, все МСФО) открываются ошибка PDFTextExtractionNotAllowed ошибка PDFTextExtractionNotAllowed
Медиана F1, 25 страниц с таблицами в эталоне 0.43 0.35 0.00
Медиана F1, 36 страниц 0.27 0.06 0.00
Время на страницу (медиана) ~430 мс ~60 мс ~200 мс

Lattice на таблицах с линиями точен: F1 0.95–1.00 на 10 страницах (например, cbr, paper.pdf с таблицей 1, ОЗОН). Но большинство безлинейных таблиц МСФО он не находит вообще, поэтому медиана у него ноль.

Итог по измеренным числам: не брать Camelot ни в одном режиме как детектор таблиц. Stream даёт ложные таблицы на текстовых страницах, lattice их даёт меньше, но пропускает таблицы без линий. Оба падают на защищённых PDF.

Где Camelot ошибается

1. Ошибка на защищённых PDF. Семь страниц четырёх отчётов (HEAD, М.Видео, IFRS, GCHE) имеют запрет на извлечение текста. Camelot отказывается их открывать, PyMuPDF — нет. Это не редкость для корпоративной отчётности, поэтому Camelot нельзя ставить единственным путём.

Страница 44 отчёта ХЕД за 2025 год: ячейки PyMuPDF и ошибка Camelot

ХЕД, PDF-страница 44. Слева PyMuPDF: слова в ячейках склеены («Возмещениясвыплатами»), запятые в числах пропали (14,02% стало 1402%). В центре Camelot не открыл страницу. Справа эталон LightOn.

2. Текст страницы вместо таблицы. На странице 41 отчёта АО ГК «Азот» есть две мелкие таблицы, остальное — текст примечаний. Camelot принял весь текст страницы за таблицу (точность 0.19 при полноте 1.00).

Страница 41 отчёта АО ГК «Азот» за 2024 год: ячейки PyMuPDF и Camelot

Азот, PDF-страница 41. Слева PyMuPDF: три таблицы, слова склеены, есть пустые колонки. В центре первая «таблица» Camelot — текст примечаний, разложенный по двум колонкам. Справа эталон LightOn: текст и три таблицы совпадают со страницей.

3. Таблицы без линий. На странице 64 отчёта «РусНефть» четыре таблицы без сетки. Camelot вернул две, и ячейки соседних таблиц в них перемешаны. Отдельные строки при этом читаются верно, например «Кредиты и займы полученные (90 374) (27 963) (44 849)». find_tables нашёл все четыре, но склеил слова, и F1 на странице 0.13.

Страница 64 отчёта ПАО НК «РусНефть» за 2025 год: ячейки PyMuPDF и Camelot

РусНефть, PDF-страница 64. Слева PyMuPDF: четыре таблицы, слова склеены («Финансовыеактивы»). В центре Camelot: две таблицы, в которых ячейки соседних смешаны. Справа эталон LightOn: четыре таблицы с правильными числами.

4. Пример, где Camelot лучше. На странице 78 отчёта Россети (справедливая стоимость финансовых инструментов) Camelot дал F1 0.79 против 0.43 у find_tables. find_tables там склеивает слова в ячейках, Camelot сохраняет пробелы и числа с разрядами.

Страница 78 отчёта Россети за 2025 год: ячейки PyMuPDF и Camelot

Россети, PDF-страница 78. Слева PyMuPDF: одна таблица, слова склеены. В центре Camelot: вторая таблица (42 строки) содержит нужные строки и числа на местах; первая (8 строк) — шапка страницы и вводный абзац. Справа эталон LightOn.

5. Ложные таблицы на препринте. На двух страницах одного препринта arXiv (2610.22061) таблиц нет: текст в две колонки и блоки кода. find_tables не нашёл ни одной. Camelot разбил текст по ячейкам: на седьмой странице «таблица 1» — 18 строк, «таблица 2» — 79 строк, и в них обрывки предложений.

Страница 7 препринта arXiv 2610.22061: ячейки PyMuPDF (пусто) и Camelot

arXiv 2610.22061, PDF-страница 7. Слева PyMuPDF таблиц не нашёл. В центре Camelot: текст двух колонок разбит по ячейкам. Справа эталон LightOn.

Страница 6 препринта arXiv 2610.22061: ячейки PyMuPDF (пусто) и Camelot

arXiv 2610.22061, PDF-страница 6. Слева таблиц нет. В центре Camelot: текст разбит по ячейкам. Справа эталон LightOn.

6. Эталон на странице 5 отчёта GCHE. LightOn принял текстовый блок в две колонки за таблицу. Camelot там ничего не вернул из-за отказа на защищённом PDF, так что здесь спорный эталон, а не ошибка метода.

Вывод

  • Не брать Camelot как детектор таблиц: stream даёт ложные срабатывания на текстовых страницах, lattice пропускает безлинейные таблицы, оба падают на защищённых PDF.
  • Lattice годится только для таблиц с линиями, и то при условии, что кто-то заранее отличит такую страницу. Этот отбор не замерен.
  • Выигрыш по скорости (в 7 раз) и по ячейкам на числовых таблицах МСФО у stream реальный, но его нельзя получить, не отсекая ложные таблицы.
  • Непроверенная возможность: принимать таблицу Camelot только там, где find_tables тоже нашёл таблицу. Это должно убрать ложные срабатывания на текстовых страницах, но не замерено.

Ограничения

  • 36 страниц — малая выборка. Эталон — OCR, поэтому часть страниц сравнивается по картинкам, а не по цифрам.
  • Метрика по словам занижает оба метода; решения принимались по содержимому ячеек и по глазам.
  • Lattice измерен через бэкенд pdfium (pypdfium2), без ghostscript. Три ложные таблицы lattice на страницах без таблиц (препринты) глазами не проверялись.
  • Одна машина, один прогон времени на страницу.
  • [[table-structure]] — восстановление сетки ячеек по изображению (TableFormer, SLANet, TATR)
  • [[pdf-native-or-ocr]] — выбор между текстовым слоем и OCR для страницы
  • [[pdf-parsers]] — сравнение парсеров PDF по тексту и порядку чтения