CER для OCR: собрать текст и посчитать ошибки Python

Соберите текст страницы из результатов OCR и сравните его с эталоном. CER — расстояние Левенштейна, делённое на число символов непустого эталона. Из-за лишних вставок CER может быть больше 1. Правила очистки и порядка чтения заданы ниже.

Программа курса «Computer Vision Engineer: от детекции и Doc-AI до продакшна».

CER страницы по находкам движка

Задание

Реализуйте функцию page_cer(findings, reference). Она получает находки движка на одной странице и эталонный текст этой страницы, собирает из находок текст страницы и возвращает его CER — тот самый, которым в уроке мерили чтение.

findings — список кортежей (text, top, left): текст находки, верхняя и левая координаты её рамки в пикселях. Порядок находок в списке — тот, в котором их отдал движок, то есть никакой. reference — непустая строка эталона.

Снятие разметки. Текст находки может прийти с тегами: движки страницы отдают куски HTML. В каждой находке замените всё, что попадает под <...> — открывающую угловую скобку, хотя бы один символ кроме закрывающей и закрывающую, — на пробел. Затем разверните три сущности: &amp; в &, &lt; в <, &gt; в > — именно в этом порядке. После этого схлопните любые подряд идущие пробельные символы в один пробел и обрежьте пробелы по краям.

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

Расстояние правок. Число правок между собранным текстом и эталоном — это расстояние Левенштейна: наименьшее число вставок, удалений и замен одного символа, которым один текст приводится к другому. Готовой реализации в стандартной библиотеке нет, напишите её сами. Что дальше делают с этим числом, чтобы получить CER, сказано в уроке — там же сказано и почему у метрики бывают значения больше единицы.

Ответ. Дробное число.

Пример

findings = [
    ("<b>65,00O</b>", 30, 70),
    ("<td>Fish &amp; Chips</td>", 30, 12),
    ("<td>TOTAL</td>", 10, 12),
]
page_cer(findings, "TOTAL Fish & Chips 65,000")
# 0.04

Собранный текст — TOTAL Fish & Chips 65,00O: цена стоит справа от названия и потому идёт последней. Отличается он от эталона одной заменой — буква вместо нуля в цене.

Примечание

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

Что проверяется

Код проверок скрыт, так что вот полный список того, что в них разбирается:

  • формат ответа: дробное число, не меньше нуля;
  • входной список после вызова не изменился;
  • пример из условия;
  • находки идут в списке не в том порядке, в каком читаются;
  • находки с одинаковой верхней координатой;
  • находки, у которых совпадают обе координаты;
  • теги и сущности внутри текста находки;
  • подряд идущие пробелы и переводы строк внутри находки;
  • находка, от которой после снятия разметки ничего не осталось;
  • совпадение с эталоном символ в символ;
  • случай, когда движок вернул текста заметно больше, чем есть в эталоне;
  • пустой список находок и одна находка.
РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Связанные задачи