Fine Tuning de um Modelo OCR: o que é, por que e como fazer

Conteúdos deste artigo:

Como já falamos anteriormente aqui no blog, o Reconhecimento Óptico de caracteres (OCR) é uma tecnologia que transforma imagens de texto em texto editável e pesquisável. Com a crescente digitalização de documentos – desde contratos até recibos e relatórios –, o OCR tem se tornado uma ferramenta essencial para automatizar a organização e análise de informações. No entanto, os modelos padrão de OCR muitas vezes enfrentam limitações quando se deparam com formatações de texto incomuns, ruídos visuais e outras variações específicas do contexto de uso.

Neste artigo, vamos explorar como realizar o fine-tuning de um modelo OCR, adaptando-o para superar essas limitações e atender às necessidades específicas de sua aplicação. Você aprenderá a preparar um conjunto de dados personalizado, anotando manualmente os caracteres presentes em suas imagens, e a treinar um modelo OCR ajustado para fornecer resultados mais precisos e eficientes.

O que é OCR e por que é importante?

OCR é uma sigla que vem do inglês e significa Optical Character Recognition – em tradução para o português, Reconhecimento Óptico de Caracteres. Esta tecnologia é capaz de converter imagens contendo texto em um formato digital legível, facilitando a organização e o armazenamento de informações.

Com o OCR, você pode processar e analisar longos documentos, contratos e relatórios de forma automática, tornando mais eficiente um trabalho que antes era realizado manualmente e de forma lenta.

Quais são as etapas de implementação de OCR?

O OCR possui quatro principais etapas:

1. Extração da Imagem

Consiste na aquisição dos dados de imagem e sua conversão em arquivos binários. Os dados de entrada podem ser fotos de documentos, arquivos digitalizados em PDF ou imagens de placas. Porém, modelos de machine learning necessitam de dados em um formato específico para realizar o treinamento. Assim, após a coleta das imagens, deve-se convertê-las em um formato binário.

2. Pré-processamento

O pré-processamento é a aplicação de um conjunto de técnicas de visão computacional que visam otimizar a performance da inteligência artificial. As técnicas incluem:

  • Linearizar o texto (caso o texto esteja rotacionado em relação à horizontal);
  • Destacar os contornos do texto e suavizar os ruídos no fundo da imagem;
  • Alterar a escala de cores da imagem.

3. Reconhecimento de Caracteres

O algoritmo de OCR realiza a leitura de cada caractere contido na imagem, reconhece características morfológicas e compara o resultado com uma lista de possíveis caracteres, retornando aquele que apresenta a maior semelhança.

4. Pós-processamento

Após o reconhecimento dos caracteres, o algoritmo realiza o pós-processamento. Essa fase começa com a verificação e correção de erros, ajustando caracteres mal reconhecidos usando contextos linguísticos e modelos de linguagem natural. Em seguida, os caracteres reconhecidos são organizados em um formato estruturado, segmentando o texto em linhas, palavras e parágrafos, para preservar a estrutura original do documento.

Além disso, o pós-processamento inclui a normalização dos dados, ajustando o formato do texto para padrões específicos, como remoção de espaços extras e correção de pontuações. Por fim, o texto processado é convertido em um formato legível e utilizável, pronto para ser integrado a sistemas automatizados ou bancos de dados. Dessa forma, o pós-processamento transforma dados brutos em informações valiosas, garantindo a máxima utilidade e precisão dos resultados do OCR.

Quais são as limitações dos modelos-padrão de OCR?

Apesar de o OCR promover maior eficiência nos processos, atualmente, com a variada gama de formatação de documentos, seu processamento tornou-se um desafio. Outras limitações incluem:

  • Suporte limitado para certas línguas e ortografias;
  • Dependência da qualidade da imagem extraída;
  • Falta de conhecimento do contexto da imagem;
  • Ruído no fundo da imagem.

Uma maneira de superar essas limitações é desenvolver um modelo customizado a partir do modelo padrão para a aplicação específica. A técnica de utilizar um modelo pré-treinado e adaptá-lo para um propósito específico é chamada de fine-tuning. A seguir, falaremos mais sobre como fazer o fine tuning de um modelo OCR.

Como realizar o fine tuning de um modelo OCR?

O fine-tuning de um modelo OCR é separado em duas principais etapas: a preparação do dataset e o treinamento do modelo. Vamos abordar um pouco mais sobre cada uma delas agora.

Primeira etapa do fine tuning de um modelo OCR: Reparação do dataset

A preparação do conjunto de dados tem como objetivo transformar as imagens para um formato que o algoritmo consiga processar. Essa etapa começa com o recorte da região de interesse nas imagens, ou seja, a eliminação das áreas onde não há a presença de caracteres.

Para isso, desenvolvemos o seguinte conjunto de funções:

def detect_text_bounding_box(img, output_folder:str=''):
 """
    Detecta texto na imagem usando EasyOCR.
    Args:
        img: A imagem na qual detectar o texto.
        output_folder (str, optional): O diretório para salvar imagens intermediárias. O padrão é uma string vazia.


    Returns:
        list: Uma lista de pontos de polígono representando o texto detectado.
    """
    cimg = img.copy()
    bbox_list, polygon_list = reader.detect(img)
    polygon_list = polygon_list[0]
    bbox_list = bbox_list[0]
    for bbox in bbox_list:
        x1, x2, y1, y2 = bbox
        polygon = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
        polygon_list.append(polygon)          
    return [np.rint(polygon).astype(int) for polygon in polygon_list]


def rearrange_src_pts(box, w_rect, h_rect):
    bl, tl, tr, br = box
    if w_rect < h_rect:
        aux = w_rect
        w_rect = h_rect
        h_rect = aux
        box = [br, bl, tl, tr]   
    src_pts = np.int0(box).astype("float32")
    return src_pts, w_rect, h_rect


def simple_warp_rectangle(img, points, output_folder:str=''):
    cimg = img.copy()


    rect = cv2.minAreaRect(points)
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    width = int(rect[1][0])
    height = int(rect[1][1])
    src_pts, width, height = rearrange_src_pts(box, width, height)
    # coordinate of the points in box points after the rectangle has been straightened
    # bottom left, top left, top right, bottom right
    dst_pts = np.array([[0, height-1],
                        [0, 0],
                        [width-1, 0],
                        [width-1, height-1]], dtype="float32")
    # the perspective transformation matrix
    M = cv2.getPerspectiveTransform(src_pts, dst_pts)
    # directly warp the rotated rectangle to get the straightened rectangle
    warped_img = cv2.warpPerspective(cimg, M, (width, height))
    return warped_img

Basicamente, a função detect_text_bounding_box recebe uma imagem como input e retorna as coordenadas do polígono da região que contém os caracteres. A partir dessas coordenadas, acionamos a função simple_warp_rectangle, que irá recortar a imagem apenas na região de interesse. Dessa forma, ao final desse passo, você possui apenas recortes das regiões das imagens que serão utilizadas para o treinamento do modelo.

Com as imagens recortadas, podemos começar a anotação de dados. Esse é o processo de escrever manualmente quais caracteres estão presentes em cada imagem. Para isso, utilizamos a biblioteca IPython e a função display_data.

A função display_data cria um prompt onde é possível visualizar a imagem e escrever o respectivo conjunto de caracteres presentes nela.

def display_data(data):
    label_dict = {}
    for i in data.iterrows():
        img_path = i[1]["path"]
        label = i[1]["label"]
        ipd.display(Image(filename=img_path))
        word_input = widgets.Text(value=label, placeholder='Type something', description='Word:', disabled=False)
        ipd.display(word_input)   
        # Key should be the relevant path only.
        label_dict[f"{img_path}"] = word_input # Store the object, so it can be changed after we run the cell.
    return label_dict

Por fim, deve-se dividir o conjunto de dados anotados entre treino e teste.

Treinamento do modelo OCR propriamente dito

Para realizar o treinamento do modelo OCR, é altamente recomendado utilizar um ambiente que possua processamento de GPU devido à intensidade computacional envolvida. O Google Colab é uma excelente opção gratuita que oferece esse recurso.

O primeiro passo é clonar o repositório da biblioteca EasyOCR, através do comando git clone. Após clonar o repositório, é necessário mudar o diretório de trabalho para o diretório onde o repositório foi clonado. Isso garantirá que estaremos no contexto correto para executar os scripts de treinamento.

!git clone https://github.com/JaidedAI/EasyOCR.git {path/to/save}
%cd {path/to/save}/trainer


import os
# Obtém o diretório de trabalho atual
current_working_directory = os.getcwd()
print(current_working_directory)

O próximo passo é importar as bibliotecas essenciais para o treinamento do modelo. Essas bibliotecas incluem funções para manipulação de dados, configuração do treinamento e execução do processo de treinamento propriamente dito.

import os
import torch.backends.cudnn as cudnn
import yaml
from train import train
from utils import AttrDict
import pandas as pd

Para configurar o processo de treinamento, basta utilizar a função get_config. Ela lê um arquivo YAML contendo todas as configurações necessárias para o treinamento, incluindo parâmetros do modelo, caminhos dos dados e outras configurações específicas. A função também prepara o conjunto de caracteres que o modelo deve reconhecer, baseado nos dados de treinamento fornecidos.

def get_config(file_path):
    with open(file_path, 'r', encoding="utf8") as stream:
        opt = yaml.safe_load(stream)
    opt = AttrDict(opt)
    if opt.lang_char == 'None':
        characters = ''
        for data in opt['select_data'].split('-'):
            csv_path = os.path.join(opt['train_data'], data, 'labels.csv')
            df = pd.read_csv(csv_path, sep='^([^,]+),', engine='python', usecols=['filename', 'words'], keep_default_na=False)
            all_char = ''.join(df['words'])
            characters += ''.join(set(all_char))
        characters = sorted(set(characters))
        opt.character= ''.join(characters)
    else:
        opt.character = opt.number + opt.symbol + opt.lang_char
    os.makedirs(f'./saved_models/{opt.experiment_name}', exist_ok=True)
    return opt

A partir dessa etapa, é necessário criar o arquivo de configuração dos parâmetros de treinamento. Este arquivo YAML contém todas as variáveis de configuração necessárias para o treinamento do modelo. Aqui está um exemplo de como deve ser configurado:

%%writefile config_files/custom_model.yaml
number: '0123456789'
symbol: "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ €"
lang_char: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
experiment_name: 'en_filtered'
train_data: 'all_data'
valid_data: 'all_data/en_val'
manualSeed: 1111
workers: 2
batch_size: 16 # 32
num_iter: 3000
valInterval: 1000
saved_model: '' #'saved_models/en_filtered/iter_300000.pth'
FT: False
optim: False # default is Adadelta
lr: 1.
beta1: 0.9
rho: 0.95
eps: 0.00000001
grad_clip: 5
#Data processing
select_data: 'en_train_filtered' # this is dataset folder in train_data
batch_ratio: '1'
total_data_usage_ratio: 1.0
batch_max_length: 34
imgH: 64
imgW: 600
rgb: False
contrast_adjust: False
sensitive: True
PAD: True
contrast_adjust: 0.0
data_filtering_off: False
# Model Architecture
Transformation: 'None'
FeatureExtraction: 'VGG'
SequenceModeling: 'BiLSTM'
Prediction: 'CTC'
num_fiducial: 20
input_channel: 1
output_channel: 256
hidden_size: 256
decode: 'greedy'
new_prediction: False
freeze_FeatureFxtraction: False
freeze_SequenceModeling: False

O arquivo YAML define vários parâmetros importantes: os caracteres que o modelo deve reconhecer (number, symbol, lang_char), o nome do experimento (experiment_name), os caminhos para os dados de treino e validação (train_data, valid_data), configurações de otimização e treinamento, além de detalhes da arquitetura do modelo.

Para realizar o fine-tuning de um modelo pré-treinado de OCR, você deve indicar o caminho para o modelo na variável saved_model. Nesta página, você encontra modelos pré-treinados para diferentes línguas.Com o arquivo de configuração pronto, podemos iniciar o treinamento do modelo. Para isso, carregamos a configuração e chamamos a função train.

config_filename = 'custom_model'
path_config_file = f"{path/to/save}/trainer/config_files/{config_filename}.yaml"
opt = get_config(path_config_file)
train(opt, amp=False)

Utilização do modelo

Após o treinamento, é necessário fazer o download dos arquivos de suporte e configurá-los com os mesmos valores utilizados durante a configuração do treinamento. Esses arquivos de suporte incluem um arquivo YAML e um script Python personalizados, que devem ser copiados para os diretórios corretos do EasyOCR.

!cp /support_files/custom_example.yaml /root/.EasyOCR/user_network/{custom_model_name}.yaml
!cp /support_files/custom_example.py /root/.EasyOCR/user_network/{custom_model_name}.py
!cp {path/to/save}/trainer/saved_models/{experiment_name}/best_accuracy.pth /root/.EasyOCR/model/{custom_model_name}.pth

Finalmente, para utilizar o modelo treinado, basta inicializar um leitor do EasyOCR com o modelo customizado e reconhecer texto em novas imagens.

custom_reader = easyocr.Reader(['en'], gpu=True, recog_network='custom_model')
custom_results = custom_reader.recognize(img)

Transforme a eficiência da sua organização com um modelo de OCR!

Se a sua organização deseja melhorar a eficiência do processamento de documentos, um modelo OCR pode ser a solução ideal. Com o OCR, você pode converter imagens contendo texto em um formato digital legível, facilitando a organização e o armazenamento de informações.

A BIX oferece soluções personalizadas de OCR para atender às suas necessidades específicas. Clique no banner abaixo e entre em contato para saber como podemos ajudar a aumentar a eficiência e a produtividade da sua organização!