Reconheça texto em imagens (OCR) em Rust
Crie um programa de linha de comando em Rust que recebe o nome de um arquivo de imagem e imprime o texto que o Tesseract reconhece. Comece com uma imagem gerada que contém um número de fatura conhecido e depois teste sua própria imagem. O mesmo programa inclui um caminho opcional em escala de cinza e retorna um status de saída diferente de zero quando não consegue reconhecer texto.
Este passo a passo usa arquivos locais e dados do idioma inglês. Um resultado correto na amostra confirma que os bindings de Rust, as bibliotecas nativas e o modelo funcionam juntos; isso não comprova a precisão em recibos, textos manuscritos ou layouts de página complexos.
Pré-requisitos
- Linux e Bash, com Rust e Cargo instalados.
- Tesseract e Leptonica nativos, incluindo seus cabeçalhos e bibliotecas de desenvolvimento.
pkg-config, um compilador C/C++ e libclang para os bindings nativos.- Dados do idioma inglês,
eng.traineddata. - ImageMagick 7 e a fonte Liberation Sans para gerar a imagem de exemplo.
O exemplo foi executado com Rust/Cargo 1.98.1, Tesseract 5.5.3, Leptonica 1.87.0 e ImageMagick
7.1.2-31. Use esse toolchain neste passo a passo. A versão mínima de Rust declarada pelo próprio
crate image é a 1.88.0, mas esse não é um
mínimo testado para este projeto completo.
Instalação do Tesseract
O crate de Rust não instala o Tesseract em si. Siga o guia de instalação nativa da sua distribuição e instale as bibliotecas de desenvolvimento, além do mecanismo e do modelo de inglês.
No Ubuntu/Debian, os nomes de pacote relevantes incluem libtesseract-dev, libleptonica-dev e
tesseract-ocr-eng. Os pacotes da distribuição podem fornecer versões diferentes do toolchain testado.
As instruções executáveis aqui cobrem o Linux, não uma configuração verificada para macOS ou Windows. Verifique as dependências nativas antes de criar o projeto:
rustc --version &&
cargo --version &&
tesseract --version &&
pkg-config --modversion tesseract lept &&
tesseract --list-langs &&
magick -version
A lista de idiomas deve incluir eng. Se o pkg-config não encontrar tesseract ou lept, resolva o
pacote de desenvolvimento ausente ou o caminho de busca dele antes de compilar. A lista de fontes do
ImageMagick (magick -list font) deve incluir Liberation-Sans.
Configuração do projeto
Escolha um diretório fora de um workspace do Cargo já existente. Cole este bloco para criar um novo
projeto binário. Ele se recusa a usar um diretório rust-ocr já existente e mantém você no
diretório original:
(cargo new --bin --edition 2021 --vcs none rust-ocr)
Dentro de rust-ocr, substitua todo o Cargo.toml por:
[package]
name = "rust-ocr"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "=1.0.104"
image = { version = "=0.25.10", default-features = false, features = ["jpeg", "png"] }
tempfile = "=3.27.0"
tesseract = "=0.15.2"
O primeiro build cria Cargo.lock. Mantenha-o junto com sua aplicação e use --locked nos
builds seguintes para que o Cargo recuse mudanças na resolução de dependências. As bibliotecas
nativas e os dados de idioma são instalados separadamente; o lockfile não fixa suas versões.
Implementação básica de OCR
Substitua rust-ocr/src/main.rs por este programa completo:
use std::path::PathBuf;
use anyhow::{bail, ensure, Context, Result};
use tesseract::{PageSegMode, Tesseract};
const USAGE: &str = "Usage: rust-ocr <image> [--grayscale]";
fn main() -> Result<()> {
let mut arguments = std::env::args_os().skip(1);
let input = PathBuf::from(arguments.next().context(USAGE)?);
let grayscale = match arguments.next() {
None => false,
Some(flag) if flag == "--grayscale" => true,
_ => bail!(USAGE),
};
ensure!(arguments.next().is_none(), USAGE);
let input = input.canonicalize().context("Could not open input image")?;
let temporary_directory = if grayscale {
Some(tempfile::tempdir().context("Could not create temporary directory")?)
} else {
None
};
let image_path = match &temporary_directory {
Some(directory) => {
let decoded = image::open(&input).context("Could not decode input image")?;
ensure!(
matches!(decoded.color(), image::ColorType::L8 | image::ColorType::Rgb8),
"Grayscale mode requires opaque 8-bit PNG or JPEG"
);
let processed = directory.path().join("processed.png");
decoded.grayscale().save(&processed)
.context("Could not write grayscale image")?;
processed.canonicalize().context("Could not resolve temporary image")?
}
None => input,
};
let filename = image_path.to_str().context("Image path must be valid UTF-8")?;
let mut ocr = Tesseract::new(None, Some("eng"))
.context("Could not initialize English OCR; check eng.traineddata")?
.set_image(filename)
.context("Could not decode input image")?;
ocr.set_page_seg_mode(PageSegMode::PsmSingleBlock);
let text = ocr.get_text().context("Recognition failed")?;
ensure!(!text.trim().is_empty(), "No text recognized");
print!("{text}");
Ok(())
}
None permite que o Tesseract localize o diretório do modelo instalado; Some("eng") seleciona
o inglês explicitamente. A API do binding carrega a
imagem com set_image e retorna o texto UTF-8 reconhecido por meio de get_text. Os erros se
propagam para fora de main em vez de serem impressos e tratados como sucesso.
Execute com um texto conhecido
A partir do diretório que contém rust-ocr, cole este bloco. O subshell dele mantém seu
diretório de trabalho e as opções do shell inalterados, inclusive quando uma etapa falha:
(
cd rust-ocr &&
cargo build &&
magick -size 900x180 xc:white -font Liberation-Sans -pointsize 64 \
-fill black -gravity center -annotate +0+0 'INVOICE 12345' PNG24:input.png &&
cargo run --quiet --locked -- input.png
)
As opções de anotação do ImageMagick criam um PNG de 8 bits opaco e legível. No ambiente testado, o stdout contém:
INVOICE 12345
Este comando substitui rust-ocr/input.png quando é executado novamente com sucesso. Reserve esse nome de
arquivo para a amostra, não para uma imagem que você queira manter. Se o build falhar, a cadeia
&& não gera a imagem nem executa um executável mais antigo. O Tesseract também pode
imprimir diagnósticos no stderr.
Tratamento de diferentes formatos de imagem
Para testar o caminho em escala de cinza com a mesma amostra:
(cd rust-ocr && cargo run --quiet --locked -- input.png --grayscale)
Ele produz o mesmo texto reconhecido. Esse caminho decodifica PNG ou JPEG por meio de
image, converte os pixels para escala de cinza e grava um PNG no próprio diretório
temporário, sem modificar a entrada. Ele rejeita deliberadamente imagens com canal alfa e de 16 bits,
em vez de descartar a transparência silenciosamente ou alterar os intervalos de amostra. Use imagens
de 8 bits opacas e na orientação correta neste passo a passo; outros formatos e a correção automática
de orientação estão fora do escopo.
O TempDir permanece ativo até o reconhecimento terminar. O destrutor dele tenta fazer a
limpeza tanto em caso de sucesso quanto em retornos de erro comuns. Conforme a
documentação no código-fonte do tempfile, um
encerramento forçado pode deixar arquivos para trás, e erros de limpeza no destrutor não são
reportados. Isso não é uma garantia de exclusão segura.
Configuração avançada de OCR
O programa seleciona PsmSingleBlock, o modo 6 do Tesseract, porque a amostra é um único bloco de
texto. Ele não solicita detecção de orientação e sistema de escrita, então esse caminho precisa dos
dados eng, mas não dos dados osd.
Escolha a segmentação com base no layout real, em vez de presumir que um modo serve para todas as
imagens; o guia de qualidade do Tesseract
explica as alternativas.
A conversão para escala de cinza é uma opção para comparar, não uma melhoria de precisão garantida. O Tesseract já faz pré-processamento internamente. Letras borradas, inclinação e baixo contraste continuam exigindo uma entrada melhor ou um processamento específico para a tarefa. Uma lista de caracteres permitidos também excluiria pontuação legítima, por isso o exemplo não aplica nenhuma.
Boas práticas de OCR em Rust
Trate um encerramento com sucesso como “algum texto foi reconhecido”, não como “o texto está correto”. Compare valores importantes com a imagem antes de usá-los. Uma entrada em branco é um erro nesta CLI, embora um resultado vazio possa ser um comportamento válido para um mecanismo de OCR. Teste isso explicitamente:
(
cd rust-ocr &&
magick -size 900x180 xc:white PNG24:blank.png &&
cargo run --quiet --locked -- blank.png
)
Este bloco substitui a amostra blank.png e encerra com status diferente de zero e a mensagem
No text recognized. Outras falhas têm soluções diferentes:
- Um nome de arquivo ausente ou um argumento extra exibe a mensagem de uso. Coloque
--grayscaledepois da imagem. - Uma imagem inexistente reporta Could not open input image.
- Uma falha do decodificador reporta Could not decode input image.
- A ausência dos dados de inglês reporta Could not initialize English OCR; check eng.traineddata.
A decodificação não é uma verificação de integridade do arquivo. Um JPEG danificado ainda pode ser decodificado, especialmente no caminho em escala de cinza, e gerar texto incompleto em vez de um erro do decodificador. Confira o resultado com o original; esta CLI não certifica que uma imagem esteja íntegra.
Se o modelo de inglês estiver instalado em um local personalizado, TESSDATA_PREFIX deve indicar o
diretório existente que contém eng.traineddata, não o próprio arquivo. Essa é a busca usada pelo
Tesseract 5.5.3.
Verifique o diretório listado por tesseract --list-langs; uma substituição desatualizada na variável de
ambiente pode fazer o programa Rust apontar para um diretório de modelos diferente.
Tratamento de vários idiomas
Esta CLI seleciona deliberadamente apenas o inglês. Instalar modelos adicionais não altera essa seleção. O Tesseract oferece suporte a identificadores de idioma combinados, mas uma aplicação multilíngue também precisa desses modelos e de imagens representativas para avaliar os resultados. Consulte a documentação de configuração de idiomas ao estendê-la; a amostra em inglês não verifica o reconhecimento multilíngue.
Conclusão
Para usar sua própria imagem, substitua input.png no comando de execução pelo nome do arquivo
dela, colocando entre aspas os caminhos que contêm espaços. Guarde a imagem original para poder
comparar o texto reconhecido com ela. Se o seu próximo passo for um fluxo de trabalho de documentos
hospedado, em vez de uma CLI local, confira o
serviço de processamento de documentos da Transloadit.
