Skip to content

macosnik/Recognize-text-from-image

Repository files navigation

Telegram-бот для распознавания текста на изображениях с использованием нейросетей

Введение в нейронные сети

Перед тем как перейти к описанию практической реализации проекта, важно понять основную технологию, лежащую в его основе — искусственные нейронные сети (ИНС).

Что такое нейронная сеть?

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

Ключевая аналогия: Ребенок и кошка

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

  1. Вы не объясняете ему сложных правил (есть усы, хвост, четыре лапы).
  2. Вы просто показываете много разных картинок с кошками и без них, каждый раз говоря: «Это кошка», «Это не кошка».
  3. Мозг ребенка сам, методом проб и ошибок, выявляет закономерности — какие сочетания форм, цветов и деталей соответствуют понятию «кошка».
  4. Чем больше примеров он увидит, тем точнее будет его распознавание, даже если он встретит кошку необычной породы или в странной позе.

Нейронная сеть обучается точно так же.

  • Данные — это примеры. Вместо картинок с кошками мы даем сети тысячи изображений с текстом и соответствующие им текстовые метки.
  • Обучение — это процесс настройки. Сеть начинает делать предположения, сравнивает их с правильным ответом и постепенно настраивает внутренние параметры (так называемые «веса»), чтобы уменьшать количество ошибок.
  • Результат — это обобщение. После обучения сеть не просто запоминает примеры, а выучивает общие принципы: как выглядят буквы «А», «Б», «В» в разных шрифтах, размерах и условиях освещения.

Из чего состоит эта «сеть»?

Базовый строительный блок — нейрон. Его можно представить как маленькую фабрику, которая:

  1. Получает на вход несколько сигналов (например, данные о яркости пикселей изображения).
  2. Взвешивает их (определяет, насколько важен каждый сигнал).
  3. Суммирует взвешенные сигналы.
  4. Принимает решение, достаточно ли сильная эта сумма, чтобы «активироваться» и передать сигнал дальше.

Сотни тысяч таких нейронов объединяются в слои:

  • Входной слой получает исходные данные (пиксели изображения).
  • Скрытые слои — это «мозг» сети. Именно в них происходят сложные вычисления по поиску закономерностей. Чем больше слоев, тем более сложные зависимости может обнаружить сеть (такие сети называют «глубокими»).
  • Выходной слой выдает конечный результат (например, распознанную букву).

Почему это применимо к распознаванию текста?

Задача распознавания текста на изображении идеально подходит для нейронных сетей, потому что:

  1. Это сложно прописать вручную. Невозможно написать простые правила для всех шрифтов, поворотов, освещения и повреждений текста.
  2. Есть огромное количество данных. Миллионы изображений с текстом можно использовать для обучения.
  3. Нейронная сеть может научиться игнорировать лишнее: фон, тени, узоры — и концентрироваться именно на контурах символов и слов.

1. Многослойный перцептрон (MLP)

1.1. Основная концепция и архитектура

Многослойный перцептрон Multi-Layer Perceptron, MLP — это класс искусственных нейронных сетей прямого распространения feedforward neural networks, состоящий из множества слоев нейронов. В отличие от однослойного перцептрона, MLP способен решать нелинейные задачи благодаря наличию скрытых слоев.

Базовая архитектура:

  • Входной слой Input Layer: получает исходные данные
  • Скрытые слои Hidden Layers: выполняют основную обработку информации
  • Выходной слой Output Layer: формирует конечный результат

Каждый нейрон в слое соединен со всеми нейронами следующего слоя (полносвязная сеть).

Рис. 1: Архитектура MLP сети

1.2. Математическая модель одного нейрона

Рассмотрим $i$ нейрон в l-м слое:

Входные сигналы:

$$ z_i^l = \sum_{j=1}^{n} \left[ w_{ij}^l \cdot a_j^{(l-1)} \right] + b_i^l $$

где:

  • $z_i^l$ - взвешенная сумма для нейрона $i$ в слое $l$
  • $w_{ij}^l$ - вес связи между $j$-м нейроном слоя $l-1$ и $i$-м нейроном слоя $l$
  • $a_j^{(l-1)}$ - активация $j$-го нейрона предыдущего слоя $(l-1)$
  • $b_i^l$ - смещение (bias) $i$-го нейрона в слое $l$
  • $n$ - количество нейронов в предыдущем слое $(l-1)$

Функция активации:

$$ a_i^l = \varphi(z_i^l) $$

где:

  • $a_i^l$ - активация (выходное значение) нейрона $i$ в слое $l$
  • $\varphi$ - функция активации
  • $z_i^l$ - взвешенная сумма входов нейрона $i$ в слое $l$

1.3. Функция активации ReLU (Rectified Linear Unit)

Функция:

$$ \text{ReLU}(x) = \max(0, x) $$

Производная:

$$ \text{ReLU}'(x) = \begin{cases} 1 & \text{если } x > 0 \\ 0 & \text{иначе} \end{cases} $$

1.4. Прямое распространение (Forward Propagation)

Процесс вычисления выхода сети для заданного входа:

Алгоритм:

  1. Инициализация: $a¹ = x$ (входной вектор)
  2. Для каждого слоя $l = 2$ до $L$:

$$ z^l = W^l \cdot a^{(l-1)} + b^l $$

$$ a^l = φ(z^l) $$

где:

  • $z^l$ - вектор взвешенных сумм слоя $l$
  • $W^l$ - матрица весов слоя $l$
  • $a^{(l-1)}$ - вектор активаций предыдущего слоя $(l-1)$
  • $b^l$ - вектор смещений слоя $l$
  • $φ$ - функция активации (применяется поэлементно)
  1. Результат: $y_pred = a^L$

где:

  • $W^l$ — матрица весов слоя $l$
  • $b^l$ — вектор смещений слоя $l$
  • $L$ — количество слоев

1.5. Функция потерь (Loss Function)

Для оценки ошибки сети используется функция потерь. Для задачи регрессии — среднеквадратичная ошибка (MSE):

$$ J(W,b) = \frac{1}{2m} \sum_{i=1}^{m} \lVert \hat{y}^{(i)} - y^{(i)} \rVert ^2 $$

Для задачи классификации — перекрестная энтропия:

$$ J(W,b) = -\frac{1}{m} \sum_{i=1}^{m} \left[ y^{(i)} \log(\hat{y}^{(i)}) + (1 - y^{(i)}) \log(1 - \hat{y}^{(i)}) \right] $$

1.6. Обратное распространение ошибки (Backpropagation)

Цель: вычислить градиенты функции потерь по параметрам сети для их последующего обновления.

Обозначения:

  • $δ_i^l$ — ошибка $i$-го нейрона в слое $l$
  • $\frac{\partial J}{\partial w_{ij}^l}$ - градиент по весу $w_{ij}^l$
  • $\frac{\partial J}{\partial b_i^l}$ - градиент по смещению $b_i^l$

Алгоритм:

  1. Вычисление ошибки выходного слоя:

$$ \delta^{L} = \nabla_{a}J \odot \varphi'(z^{L}) $$

где $⊙$ — поэлементное умножение (Hadamard product)

  1. Обратное распространение по слоям ($l = L-1$ до $2$):

$$ \delta^{l} = ((W^{(l+1)})^T \delta^{(l+1)}) \odot \varphi'(z^{l}) $$

  1. Вычисление градиентов:

$$ \frac{\partial J}{\partial w_{ij}^{l}} = a_j^{(l-1)} \delta_i^{l} $$

$$ \frac{\partial J}{\partial b_i^{l}} = \delta_i^{l} $$

1.7. Обучение сети (градиентный спуск)

Обновление параметров:

$$ w_{ij}^{l} = w_{ij}^{l} - \alpha \frac{\partial J}{\partial w_{ij}^{l}} $$

$$ b_i^{l} = b_i^{l} - \alpha \frac{\partial J}{\partial b_i^{l}} $$

где $α$ — скорость обучения learning rate

Пакетный градиентный спуск (Batch Gradient Descent):

  • Использует весь набор данных для одного обновления
  • Стабильная сходимость, но медленная для больших dataset ов

Стохастический градиентный спуск (Stochastic Gradient Descent):

  • Обновление после каждого примера
  • Быстрая сходимость, но высокая дисперсия

Батчевый градиентный спуск (Mini-batch Gradient Descent):

  • Компромиссный вариант batch size = 32-512
  • Наиболее популярный на практике

1.8. Практические аспекты и проблемы

Проблема исчезающих градиентов: В глубоких сетях с сигмоидой/tanh градиенты могут становиться очень маленькими, что останавливает обучение.

Решения:

  • Использование ReLU-активаций
  • Инициализация весов (Xavier, He)

Переобучение (Overfitting): Сеть запоминает обучающие данные, но плохо обобщает.

Методы борьбы:

  • Встряска весов (Dropout)
  • Ранняя остановка (early stopping)
  • Увеличение данных (data augmentation)

1.9. Пример реализации

Для обучения модели необходимо:

  • Алфавит состоящий из кириллицы, представленный в обоих регистрах, и цифры со специальными символами: АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя0123456789.,;:!?()-«»
  • Множество шрифтов для кириллицы, помещённые в папку fonts

Генерация материалов для обучения:

# dataset.py
from PIL import Image, ImageDraw, ImageFont
import numpy
import os

SYMBOLS = [i for i in "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя0123456789.,;:!?()-«»"]
FONTS = os.listdir("fonts")

x, y = [], []
done = 1

for symbol in SYMBOLS:
    for font in FONTS:
        font = ImageFont.truetype(os.path.join("fonts", font), 25)
        img = Image.new("L", (40, 40), color=0)
        draw = ImageDraw.Draw(img)
        draw.text((10, 10), symbol, font=font, fill=255)
        img = img.crop(img.getbbox())
        img = img.resize((20, 20))

        arr = numpy.array(img)
        arr = (arr > 100).astype(numpy.uint8)
        x.append(arr.flatten())
        y.append(symbol)

        print(f"\r{done}/{len(SYMBOLS) * len(FONTS)}", end="")
        done += 1

x = numpy.array(x, dtype=numpy.uint8)
y = numpy.array(y)

numpy.savez("dataset.npz", x=x, y=y)

print()

Код создаёт архив с паттернами (рисунки с символами - буквами, цифрами и спец. символами) и сохроняет в файл dataset.npz.

Просмотр содержимого dataset.npz:

# viewer.py
import numpy
import os

data = numpy.load("dataset.npz")
x, y = data["x"], data["y"]

i = 0
while True:
    os.system("clear")

    for row in x[i].reshape(20, 20):
        print("".join("██" if px == 1 else "  " for px in row))
    print(y[i])

    # input()
    i = (i + 1) % len(x)

Код показывает каждое сгенерированное изображение для обучения.

Обучение нейросети:

# train.py
import tensorflow
import numpy

data = numpy.load("dataset.npz")
x, y = data["x"], data["y"]

symbols = numpy.unique(y)
symbol_to_idx = {s: i for i, s in enumerate(symbols)}

y = numpy.array([symbol_to_idx[s] for s in y])
num_classes = len(symbols)
y = tensorflow.keras.utils.to_categorical(y, num_classes)

indexes = numpy.arange(len(x))
numpy.random.shuffle(indexes)
x = x[indexes]
y = y[indexes]

split = int(0.9 * len(x))
x_train, x_val = x[:split], x[split:]
y_train, y_val = y[:split], y[split:]

model = tensorflow.keras.Sequential([
    tensorflow.keras.layers.Input(shape=(400,)),
    tensorflow.keras.layers.Dense(512, activation="relu"),
    tensorflow.keras.layers.BatchNormalization(),
    tensorflow.keras.layers.Dropout(0.5),
    tensorflow.keras.layers.Dense(256, activation="relu"),
    tensorflow.keras.layers.BatchNormalization(),
    tensorflow.keras.layers.Dropout(0.5),
    tensorflow.keras.layers.Dense(128, activation="relu"),
    tensorflow.keras.layers.BatchNormalization(),
    tensorflow.keras.layers.Dropout(0.4),
    tensorflow.keras.layers.Dense(64, activation="relu"),
    tensorflow.keras.layers.Dropout(0.3),
    tensorflow.keras.layers.Dense(num_classes, activation="softmax")
])

model.compile(
    optimizer=tensorflow.keras.optimizers.Adam(learning_rate=0.001),
    loss="categorical_crossentropy",
    metrics=["accuracy"]
)

reduce_lr = tensorflow.keras.callbacks.ReduceLROnPlateau(
    monitor='val_accuracy',
    factor=0.1, 
    patience=3, 
    min_lr=0.000001
)

early_stopping = tensorflow.keras.callbacks.EarlyStopping(
    monitor='val_accuracy', 
    patience=20, 
    restore_best_weights=True
)

model.fit(x_train, y_train,
    validation_data=(x_val, y_val),
    epochs=300,
    batch_size=64,
    shuffle=True,
    callbacks=[early_stopping, reduce_lr],
    verbose=1
    )

model.save("model.keras")
numpy.savez("labels.npz", symbols=symbols)

Код обучает модель на основе подготовленных изображений с символами и сохроняет модель в model.npz. tensorflow предоставляет возможность быстро и удобно обучать нейросети на любой архитектуре.

Результат обучения:

# accuracy.py
import tensorflow
import numpy

data = numpy.load("dataset.npz")
x, y = data["x"], data["y"]

model = tensorflow.keras.models.load_model("model.keras")
labels = numpy.load("labels.npz")["symbols"]

predict = model.predict(x, batch_size=64, verbose=0)
predict = labels[numpy.argmax(predict, axis=1)]

for label in labels:
    mask = y == label
    total = mask.sum()
        
    correct = (predict[mask] == y[mask]).sum()
    acc = correct / total * 100
    
    predictions = predict[mask & (predict != y)]
    info, counts = numpy.unique(predictions, return_counts=True)
    
    info_list = []
    for info_label, count in zip(info, counts):
        info_list.append(f"{info_label}: {count / total * 100:.1f}%")
    
    info_list.sort(key=lambda x: float(x.split(": ")[1][:-1]), reverse=True)

    print(f"{label}: {correct}/{total}  -  {acc:.2f}%  ({', '.join(info_list)})")

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

1.10. Выводы

  • Модель отлично обобщает признаки символов и их распознаёт!
  • Мы умпешно создали архив с символами разным шрифтом на изображениях
  • Разобрались в работе нейросетей
  • Успешно обучили модель используя MLP архитектуру
  • Протестировали модель и посмотрели результаты

Но есть одна серьёзная проблемма. Классификация символов, состоящие из нескольких эллементов: ё, й, !, ; и т.д. Проблема связана с тем что для работы модели нужно давать изображения определённого формата и получать результат распознования только одного символа на изображении. А это значит что зоны символов нужно алгоритмически найти на изображении и обрезать их до символа, но сделать это когда символ состоит из нескольких эллементов крайне трудно и энергозатратно, что замедляет работу программы.

Поэтому для распознавания текста на изображении удобнее давать нейросети распознать целое слово в области изображения. Об этом способе рассказывается в след. главе.

2. Комбинированная модель (CNN + LSTM + CTC)

2.1. Основная концепция и архитектура

Модель, основанная на комбинации сверточных слоёв CNN, рекуррентных слоёв LSTM и функции потерь CTC, является одной из наиболее эффективных архитектур для распознавания текста на изображениях. В отличие от простых моделей, таких как MLP, данная архитектура способна учитывать пространственные особенности изображения, последовательную природу текста и неопределённость в выравнивании символов. Это делает её стандартом в современных OCR‑системах.

Основная идея заключается в том, чтобы:

  1. CNN извлекла визуальные признаки из изображения, превратив его в последовательность векторов.
  2. LSTM обработала эту последовательность, учитывая контекст слева и справа.
  3. CTC сопоставила выход модели с целевой строкой без необходимости точной разметки каждого символа.

Таким образом, модель решает задачу:

$$ X → F → H → Y $$

где:

$X$ — изображение

$F$ — признаки, извлечённые CNN

$H$ — контекстные представления LSTM

$Y$ — итоговая строка

Рис. 2: Архитектура комбинированной сети (CNN + LSTM + CTC)

2.2. Сверточная часть (CNN)

CNN — это фундамент модели. Она преобразует изображение в последовательность признаков, сохраняя важную информацию о форме, контрасте, изгибах и текстуре символов.

Пусть изображение имеет размер:

$$ X \in \mathbb{R}^{H \times W \times C} $$

После прохождения через CNN получаем:

$$ F = CNN(X), \quad F \in \mathbb{R}^{T \times D} $$

где:

$T$ — длина последовательности

$D$ — размерность признаков

CNN выполняет:

  1. Выделение контуров
  2. Подавление шума
  3. Извлечение локальных паттернов
  4. Формирование устойчивого представления символов

Это критично, так как реальные изображения текста часто содержат:

  • Шум
  • Размытие
  • Наклон
  • Низкое разрешение
  • Артефакты сжатия

2.3. Рекуррентная часть (LSTM)

После CNN мы получаем последовательность признаков, но каждый вектор сам по себе не содержит информации о соседних символах. Чтобы учитывать контекст, используется двунаправленный LSTM:

$$ H_t = BiLSTM(F_t), \quad H \in \mathbb{R}^{T \times H_d} $$

LSTM позволяет модели:

  1. Понимать, что символы образуют слова
  2. Различать похожие буквы по контексту
  3. Корректно интерпретировать последовательности разной длины

Например:

  • «П» и «Л» могут быть похожи по форме, но контекст делает их различимыми
  • «0» и «O» различаются по окружению
  • «1» и «I» становятся различимыми в зависимости от соседних символов

2.4. CTC Loss

CTC (Connectionist Temporal Classification) — ключевой элемент, позволяющий обучать модель без точной разметки символов по координатам.

CTC решает задачу сопоставления последовательности признаков $H$ с целевой строкой $Y$:

$$ \mathcal{L}_{CTC} = -\ln p(Y | H) $$

CTC допускает:

  • Повторяющиеся символы
  • Пропуски
  • Пустые промежутки
  • Различную длину входа и выхода

Например, последовательность:

--hhhee_ll_llooo---

CTC преобразует в:

hello

где «–» — пустой символ

Это делает обучение:

  • гибким
  • устойчивым
  • независимым от точной разметки

2.5. Преимущества модели

  1. Учитывает пространственные и последовательные зависимости

CNN анализирует изображение, LSTM — последовательность. Это даёт точность, недостижимую для MLP.

  1. Не требует покадровой разметки

CTC автоматически выравнивает символы.

  1. Работает с текстом любой длины

Ширина изображения не ограничивает модель.

  1. Устойчива к шумам

CNN выделяет устойчивые признаки даже при плохом качестве.

  1. Подходит для реальных OCR‑задач

Используется в:

  • Google OCR,
  • Tesseract 4+
  • PaddleOCR
  • EasyOCR
  1. Гарантирует высокую точность

При достаточном количестве данных модель достигает стабильных результатов и корректно распознаёт текст даже в сложных условиях.

2.6. Недостатки

  1. Сложнее и тяжелее, чем MLP

    Требует больше вычислительных ресурсов.

  2. Дольше обучается

    Особенно на больших датасетах.

  3. Требует больше данных

    Для устойчивой генерализации нужно много примеров.

  4. Реализация сложнее

Нужно правильно настроить:

  • CNN
  • LSTM
  • CTC-декодер
  • Препроцессинг изображений

2.7. Почему мы выбираем именно эту модель

Несмотря на сложность, данная архитектура идеально подходит для нашей задачи.

Она обеспечивает:

  • Высокую точность
  • Устойчивость к шумам
  • Корректную работу с текстом произвольной длины
  • Отсутствие необходимости в разметке символов по координатам
  • Стабильность на реальных данных

Обучение такой модели гарантирует корректное распознавание текста в большинстве практических случаев. Для нашего проекта это критически важно.

Мы будем использовать предобученную CNN+LSTM+CTC модель, так как она:

  • Уже оптимизирована
  • Протестирована
  • Показывает высокую точность
  • Полностью закрывает наши требования

Реализацию, подключение и инференс я опишу в отдельном разделе.

3. Использование готовых библиотек OCR и интеграция с Telegram‑ботом

В предыдущих главах мы разобрали архитектуру CNN + LSTM + CTC и поняли, почему она идеально подходит для задач распознавания текста. Теперь перейдём к практической части: выберем библиотеку, реализующую эту архитектуру, протестируем её, создадим собственный алгоритм постобработки текста и интегрируем всё это в Telegram‑бота.

3.1. Сравнение популярных OCR‑библиотек

Ниже приведена таблица сравнения наиболее распространённых библиотек OCR, которые используют современные нейросетевые архитектуры (включая CNN + LSTM + CTC):

Библиотека Архитектура Языки Простота Плюсы Минусы Вывод
Tesseract 4+ LSTM‑OCR много средняя классика, много гайдов, CLI чувствителен к качеству, сложный тюнинг подходит для простых задач
EasyOCR CNN + LSTM + CTC много (RU) высокая простая установка, готовые модели, Python требует PyTorch лучший выбор для нашего проекта
PaddleOCR CNN + RNN + CTC/Attention много средняя высокая точность, много моделей сложнее интеграция, тяжеловат хорош для крупных проектов
Google Vision проприетарные модели много высокая отличное качество, облачный сервис платно, зависимость от внешнего API не подходит для локального решения

Для нашего проекта важны:

  • Простота интеграции (Python‑скрипты, Telegram‑бот)
  • Поддержка русского языка
  • Готовая предобученная модель
  • Минимум возни с установкой

По этим критериям EasyOCR - наиболее удобный выбор. Она уже реализует архитектуру, близкую к CNN + LSTM + CTC, имеет предобученные модели и легко подключается в Python‑код.

3.2. Установка EasyOCR

Установка через pip:

pip install easyocr

Также потребуется torch (PyTorch). Если он не установлен, можно поставить так:

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu

(для CPU‑версии; для GPU будет другая команда, но в рамках нашего проекта достаточно CPU).

3.3. Простейший запуск EasyOCR

import easyocr

reader = easyocr.Reader(['ru'])
results = reader.readtext('image.jpg')

for coords, text, conf in results:
    print(text, conf)

3.4. Постобработка текста: группировка строк

EasyOCR возвращает текст фрагментами, и порядок может быть нарушен.

Поэтому мы используем собственный алгоритм группировки, который:

  • Вычисляет центры боксов
  • Сортирует по вертикали
  • Объединяет строки по горизонтали
  • Формирует читаемый текст
def group(results):
    if not results:
        return []

    texts, x, y, w, h = [], [], [], [], []

    for coordinate, text, _ in results:
        x_arr = [px[0] for px in coordinate]
        y_arr = [px[1] for px in coordinate]
        texts.append(text.strip())
        x.append(sum(x_arr) / 4.0)
        y.append(sum(y_arr) / 4.0)
        w.append(max(x_arr) - min(x_arr))
        h.append(max(y_arr) - min(y_arr))

    def sort_key_index(i):
        return (y[i], x[i])

    def sort_key_x(j):
        return x[j]

    sort_indexes = sorted(range(len(texts)), key=sort_key_index)

    lines, current_line = [], []

    for i in sort_indexes:
        if not current_line:
            current_line = [i]
            continue
        if abs(y[i] - y[current_line[-1]]) <= np.mean(h) * 0.6:
            current_line.append(i)
        else:
            current_line.sort(key=sort_key_x)
            lines.append(current_line)
            current_line = [i]

    if current_line:
        current_line.sort(key=sort_key_x)
        lines.append(current_line)

    return "\n".join([" ".join(texts[j] for j in line) for line in lines])

Этот алгоритм делает текст структурированным и удобным для чтения.

Пример извлечения текста на кириллице с изображения:

Рис. 3: Изображение с кириллицей

Зима
Наступила уже настоящая зима.
Земля была покрыта белоснежным ковром. Не осталось ни одного тёмного пятнышка. Даже голые берёзы, ольхи и рябины убрались инеем, точно серебристым пухом.
Они стояли, засыпанные снегом, как будто надели дорогую тёплую шубу...
Д.Н.Мамин-Сибиряк

Пример извлечения текста на латинице с изображения:

Рис. 4: Изображение с латиницей

Hello, boys and girls!
I'm Tom. I live in London. It was a lot of fun last week. It was the Day of the London. I and my mum were in the park. There were a lot of balloons, food and drink. It was very interesting there.
We were happy.
Write back and tell me about your Day of the City.
Send a picture!
Love,

3.5. Создание Telegram‑бота

Теперь создадим Telegram‑бота, который:

  1. Принимает изображение
  2. Распознаёт текст через EasyOCR
  3. Группирует строки через group()
  4. Отправляет результат пользователю

Полная реализация

import easyocr
import os
import numpy as np
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes

reader = easyocr.Reader(['ru'])

def group(results):
    if not results:
        return []

    texts, x, y, w, h = [], [], [], [], []

    for coordinate, text, _ in results:
        x_arr = [px[0] for px in coordinate]
        y_arr = [px[1] for px in coordinate]
        texts.append(text.strip())
        x.append(sum(x_arr) / 4.0)
        y.append(sum(y_arr) / 4.0)
        w.append(max(x_arr) - min(x_arr))
        h.append(max(y_arr) - min(y_arr))

    def sort_key_index(i):
        return (y[i], x[i])

    def sort_key_x(j):
        return x[j]

    sort_indexes = sorted(range(len(texts)), key=sort_key_index)

    lines, current_line = [], []

    for i in sort_indexes:
        if not current_line:
            current_line = [i]
            continue
        if abs(y[i] - y[current_line[-1]]) <= np.mean(h) * 0.6:
            current_line.append(i)
        else:
            current_line.sort(key=sort_key_x)
            lines.append(current_line)
            current_line = [i]

    if current_line:
        current_line.sort(key=sort_key_x)
        lines.append(current_line)

    return "\n".join([" ".join(texts[j] for j in line) for line in lines])


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("Привет! Отправь мне изображение, и я распознаю текст на нём.")

async def handle_photo(update: Update, context: ContextTypes.DEFAULT_TYPE):
    photo = update.message.photo[-1]
    file = await photo.get_file()
    file_path = f"dataset/temp_{len(os.listdir('dataset'))}.jpg"
    await file.download_to_drive(file_path)

    results = reader.readtext(file_path)
    text = group(results)
    os.remove(file_path)

    if text == []:
        await update.message.reply_text("Не удалось распознать текст 😔")
    else:
        await update.message.reply_text(text)

def main():
    telegram = Application.builder().token("TOKEN").build()

    telegram.add_handler(CommandHandler("start", start))
    telegram.add_handler(MessageHandler(filters.PHOTO, handle_photo))

    telegram.run_polling()

if __name__ == "__main__":
    os.system("mkdir dataset")
    main()

3.6. Итоговая схема работы

  1. Пользователь отправляет изображение боту
  2. EasyOCR распознаёт текст
  3. Алгоритм group() сортирует и объединяет строки
  4. Бот отправляет готовый читаемый текст пользователю

Таким образом, мы получили:

  • Локальную систему OCR
  • Основанную на CNN + LSTM + CTC
  • С постобработкой текста
  • Интегрированную в Telegram‑бота

Это завершённый рабочий проект, который можно развивать дальше.

4. Возможности дальнейшего развития

Проект уже выполняет полный цикл: OCRпостобработкаTelegram‑бот. Однако его можно расширять дальше:

  1. Улучшение качества OCR
  • Добавление новых языков.
  • Предобработка изображений (резкость, выравнивание, шумоподавление).
  • Использование GPU для ускорения.
  1. Расширение постобработки
  • Исправление типичных OCR‑ошибок.
  • Восстановление структуры текста (абзацы, списки).
  • Нормализация пунктуации и пробелов.
  1. Поддержка PDF
  • Извлечение страниц.
  • OCR каждой страницы.
  • Сбор текста в единый документ.
  1. Улучшение Telegram‑бота
  • Кнопки и меню.
  • Выбор языка распознавания.
  • Поддержка PDF, документов, сканов.
  1. Собственная модель
  • Сбор датасета.
  • Обучение своей CNN+LSTM+CTC модели.
  • Оптимизация под конкретные документы.
  1. Внедрение языковой модели
  • Исправление граматики.
  • Исправление синтаксиса.
  • Более точный распознанный текст.

Списибо! - macosnik

About

Telegram-бот для распознавания текста на изображениях с использованием нейросетей

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages