2025, Oct 15 22:16
Как получить рамки символов в EasyOCR с помощью OpenCV
Пошагово показываем, как получить координаты и рамки символов из EasyOCR: бинаризация, связные компоненты, сканирование строки, пример кода на Python/OpenCV.
Координаты на уровне символов часто требуются, когда возможностей распознавания на уровне слов недостаточно: точное выделение, выравнивание или постобработка зависят от аккуратных рамок для каждого глифа. EasyOCR возвращает многоугольные рамки для слов или фраз и значение уверенности, но не выдаёт рамки для отдельных символов. Ниже показано, как закрыть этот пробел с помощью обработки изображений поверх результата EasyOCR.
Проблема
EasyOCR возвращает одну запись на обнаруженную текстовую область, включающую четырехугольную ограничивающую рамку, распознанную строку и оценку уверенности. Типичный элемент выглядит так:
[
    [
        [60, 88],
        [639, 88],
        [639, 124],
        [60, 124]
    ],
    "Some phrase",
    0.6820449765391986
]
Вопрос в том, можно ли получить рамки на уровне символов напрямую, без ручного вычисления позиции каждого символа внутри фразы.
Почему так происходит
EasyOCR изначально не предоставляет координаты на уровне символов. Вы получаете рамку на фразу, а не на глифы. Чтобы вывести рамки символов, нужно работать внутри каждой найденной области и сегментировать содержимое самостоятельно. Практичный подход: бинаризовать вырезанный фрагмент, найти связные компоненты и выбрать те, что пересекают горизонтальную среднюю линию (или базовую линию) текстовой строки. Горизонтальную ширину берём из каждой компоненты, а вертикальные границы наследуем из исходной области EasyOCR.
Решение
Процедура напрямую опирается на выход EasyOCR: вырезать область, преобразовать её в бинарное изображение, собрать связные компоненты, пройти по горизонтальной центральной линии, чтобы отобрать компоненты, принадлежащие строке текста, и собрать рамки символов из этих попаданий. Пример изображения для теста взят с этой страницы. Также есть релевантное обсуждение: github.com/JaidedAI/EasyOCR/issues/631.
import easyocr
import cv2
import numpy as np
# Запуск EasyOCR
ocr_engine = easyocr.Reader(['en'])
src_path   = 'testocr.png'
frame      = cv2.imread(src_path)
detections = ocr_engine.readtext(src_path)
annotated  = frame.copy()
# Перебор результатов OCR
for quad, snippet, score in detections:
    # 1) Обрезать обнаруженную область
    qarr = np.array(quad, dtype=np.int32)
    left,  top    = qarr[:, 0].min(), qarr[:, 1].min()
    right, bottom = qarr[:, 0].max(), qarr[:, 1].max()
    window = frame[top:bottom, left:right]
    # 2) Бинаризация и извлечение связных компонент
    mono = cv2.cvtColor(window, cv2.COLOR_BGR2GRAY)
    _, mask = cv2.threshold(mono, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
    n_tags, tagmap, props, centers = cv2.connectedComponentsWithStats(mask, connectivity=8)
    # 3) Просканировать горизонтальную середину области
    midrow = window.shape[0] // 2
    touched = set()
    for cx in range(window.shape[1]):
        tag = tagmap[midrow, cx]
        if tag != 0:
            touched.add(tag)
    # 4) Построить рамки символов: ширина из компонент, высота из исходной области
    for tag in touched:
        px, py, pw, ph, pa = props[tag]
        bounds = (px + left, top, pw, bottom - top)
        cv2.rectangle(annotated, (bounds[0], bounds[1]), (bounds[0] + bounds[2], bounds[1] + bounds[3]), (0, 255, 0), 1)
cv2.imwrite('output.png', annotated)
Как это работает
Рамка фразы, найденная EasyOCR, задаёт стабильный вертикальный диапазон для символов. Внутри этой области бинаризация и анализ связных компонент выявляют непрерывные «чернильные» пятна. Пройдя по горизонтальной середине региона, вы собираете только те компоненты, которые лежат на строке текста. Полученная по компоненте ширина определяет горизонтальные границы символа, а вертикальный размах берётся из рамки EasyOCR. Прямоугольники, нарисованные на копии исходного изображения, показывают рамки на уровне символов.
Почему это важно
Когда задача зависит от координат символов, одних рамок на уровне фразы недостаточно. Дополнение EasyOCR анализом связных компонент даёт практичный путь к геометрии символов без модификации движка OCR и без опоры на недоступные функции.
Итоги
Если вам нужны рамки символов из EasyOCR, используйте полигональные рамки фраз как опорные, бинаризуйте внутреннюю область, соберите связные компоненты, пройдитесь по горизонтальной середине для идентификации глифов и получите прямоугольники для каждого символа. Этот подход — рабочая базовая схема, которую можно адаптировать под ваши данные и ограничения.