Handwriting Recognition System Implementation

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1 servicesAll 1566 services
Handwriting Recognition System Implementation
Medium
~3-5 business days
FAQ
AI Development Areas
AI Solution Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1212
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822

Реализация распознавания рукописного текста (Handwriting Recognition)

Рукописный текст — значительно сложнее машинопечатного: бесконечное разнообразие почерков, лигатуры (слитное написание), нечёткие границы между символами, вариативность нажима и угла. Современные HTR (Handwritten Text Recognition) системы достигли CER (Character Error Rate) < 5% на стандартных датасетах, но на реальных данных (медицинские карты, исторические документы, формы) CER обычно выше.

Современные подходы к HTR

TrOCR (Microsoft) — transformer encoder-decoder для OCR. Encoder: ViT-обработка изображения, Decoder: autoregressive генерация текста. State-of-the-art на IAM handwriting dataset: CER 2.89% (large model).

from transformers import TrOCRProcessor, VisionEncoderDecoderModel
from PIL import Image
import torch

class HandwritingRecognizer:
    def __init__(self, model_name: str = 'microsoft/trocr-large-handwritten'):
        self.processor = TrOCRProcessor.from_pretrained(model_name)
        self.model = VisionEncoderDecoderModel.from_pretrained(model_name)
        self.model.eval()
        self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
        self.model.to(self.device)

    @torch.no_grad()
    def recognize(self, image: Image.Image) -> str:
        """Распознавание одной строки текста"""
        pixel_values = self.processor(
            images=image,
            return_tensors='pt'
        ).pixel_values.to(self.device)

        generated_ids = self.model.generate(
            pixel_values,
            max_new_tokens=128,
            num_beams=4        # beam search для лучшего качества
        )

        return self.processor.batch_decode(
            generated_ids,
            skip_special_tokens=True
        )[0]

PaddleOCR для рукописного кириллического текста — существенно лучше чем TrOCR (который обучен преимущественно на английском):

from paddleocr import PaddleOCR

# Специальная модель для рукописного текста
ocr = PaddleOCR(
    use_angle_cls=True,
    lang='ru',
    rec_algorithm='SVTR_LCNet',     # лучше для рукописного
    rec_model_dir='./models/handwriting_rec'
)

Предобработка рукописных изображений

Рукописный текст требует более агрессивной предобработки:

import cv2
import numpy as np
from skimage import morphology

def preprocess_handwriting(image: np.ndarray) -> np.ndarray:
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Удаление фоновых линий (линованная бумага)
    horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (40, 1))
    horizontal_lines = cv2.morphologyEx(gray, cv2.MORPH_OPEN, horizontal_kernel)
    gray = cv2.subtract(gray, horizontal_lines)

    # Otsu бинаризация
    _, binary = cv2.threshold(gray, 0, 255,
                               cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

    # Удаление маленьких артефактов
    cleaned = morphology.remove_small_objects(
        binary.astype(bool), min_size=50
    ).astype(np.uint8) * 255

    return cleaned

Сегментация строк для многострочных документов

Перед распознаванием многострочный документ нужно разбить на строки:

def segment_lines(binary_image: np.ndarray) -> list[np.ndarray]:
    """Горизонтальная проекция для сегментации строк"""
    horizontal_projection = binary_image.sum(axis=1)

    # Находим промежутки между строками
    threshold = horizontal_projection.max() * 0.05
    in_line = horizontal_projection > threshold

    lines = []
    start = None
    for i, active in enumerate(in_line):
        if active and start is None:
            start = max(0, i - 5)
        elif not active and start is not None:
            end = min(len(in_line), i + 5)
            line_img = binary_image[start:end, :]
            if end - start > 10:  # минимальная высота строки
                lines.append(line_img)
            start = None

    return lines

Дообучение на корпоративных рукописных данных

Для специфического почерка (медицинские карты конкретной больницы, бланки конкретного предприятия) — fine-tuning:

  • Разметка 500–2000 строк через Label Studio или CVAT
  • Fine-tuning TrOCR или PaddleOCR rec_model
  • CER снижается с 15–25% до 5–10% на domain-specific данных
Датасет Язык CER SOTA
IAM Online/Offline English 2.89% (TrOCR-Large)
CVL Database English/German 3.1%
Bentham Collection English 4.5%
HWR200 (Russian) Russian ~8%
Задача Срок
Integration TrOCR для английского 1 неделя
Распознавание кириллического рукописного 2–3 недели
Fine-tuning под корпоративные документы 4–7 недель