Como realizar OCR em placas de veículos em C#
Este guia destina-se a desenvolvedores .NET que estão migrando cargas de trabalho de OCR do SDKKofax OmniPageCapture (agora comercializado como Tungsten Automation) para o IronOCR . Abrange todo o processo de migração: remoção da dependência do instalador do SDK, eliminação da rotina do ciclo de vida do mecanismo, substituição dos padrões de reconhecimento baseados em zonas e modernização do tratamento de erros. Não é necessário ler o artigo comparativo previamente.
Por que migrar doKofax OmniPage(Tungsten)?
O SDK OmniPage Capture foi projetado para infraestrutura de gerenciamento de documentos Enterprise em uma era anterior ao NuGet, Docker e pipelines de CI/CD. Cada uma das suas escolhas arquitetônicas que faziam sentido em 2005 cria atrito em 2026.
O instalador do SDK não tem lugar em um ambiente NuGet . Todas as máquinas de desenvolvimento, agentes de compilação e servidores de produção que executam código OmniPage exigem que o instalador do SDK Tungsten seja executado antes da compilação. Não há referência de pacote .csproj para restaurar. A atualização da versão do SDK implica a execução do instalador novamente em toda a frota. Um novo desenvolvedor não pode executar o projeto sem entrar em contato com quem gerencia a licença do SDK e aguardar o acesso ao instalador.
O arquivo de licença é uma responsabilidade operacional. Um arquivo .lic deve estar presente em um caminho específico em cada máquina que chama engine.Initialize(). Implante em um novo servidor e esqueça o arquivo, e todas as chamadas de OCR falharão com uma exceção de licença — não com um erro de OCR. Usar licenças flutuantes e uma partição de rede entre o servidor de aplicação e o servidor de licença significa que toda chamada Initialize() falhará até que a conectividade seja restaurada, independentemente de se aquela máquina validou sua licença com sucesso ontem.
O gerenciamento do ciclo de vida do mecanismo vaza recursos em caminhos de erro. OmniPageEngine.Shutdown() deve ser chamado em todos os possíveis caminhos de código — incluindo manipuladores de exceção, retornos antecipados e timeouts — ou uma cadeira de licença flutuante permanece bloqueada até que o tempo de checkout do servidor de licença expire. Escrever de forma defensiva em torno dessa restrição significa encapsular todo ponto de integração em padrões IDisposable que existem apenas para proteger contra o fechamento do mecanismo sendo ignorado.
A identificação por hardware entra em conflito com as implementações modernas. A ativação do OmniPage está vinculada ao hardware. Reinicializações de contêineres, migrações de máquinas virtuais e escalonamento automático na nuvem envolvem alterações na identidade do hardware que acionam requisitos de reativação. Um modelo de implantação incompatível com o dimensionamento automático é um modelo de implantação incompatível com a infraestrutura em nuvem.
O preço por página é imprevisível em grande escala. Um pico no processamento de documentos — como a integração de um novo cliente ou o envio de um lote de documentos atrasados — gera uma fatura adicional que não foi prevista no orçamento. O IronOCR é perpétuo e ilimitado dentro do plano licenciado: sem cobrança por página, sem cota, sem faturas adicionais.
O processo de aquisição bloqueia o envio. O SDK da OmniPage está sujeito a restrições de venda. O acesso para avaliação, os preços e os termos do contrato exigem uma interação com a equipe de vendas que dura de 4 a 12 semanas. As equipes que desenvolvem recursos de OCR com prazos de entrega de produto apertados não podem esperar pelo ciclo de compras da Enterprise . dotnet add package IronOcr resolve em 30 segundos. Consulte a página de licenciamento da IronOCR para preços publicados e de autoatendimento — $999 Lite até US$ 2.999 Enterprise, todos perpétuos.
O problema fundamental
O OmniPage exige uma cerimônia de inicialização completa antes da leitura do primeiro byte e uma cerimônia de encerramento após o último — cada ponto de chamada deve gerenciar ambas:
// OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
using var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic"); // File must exist here
engine.Initialize(); // Network call to license server — can fail for license reasons, not OCR reasons
var document = engine.CreateDocument();
document.AddPage("invoice.jpg");
document.Recognize(new RecognitionSettings { Language = "English" });
string text = document.GetText();
document.Dispose(); // Must not be skipped
engine.Shutdown(); // Must not be skipped — omitting this locks a floating seat
// OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
using var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic"); // File must exist here
engine.Initialize(); // Network call to license server — can fail for license reasons, not OCR reasons
var document = engine.CreateDocument();
document.AddPage("invoice.jpg");
document.Recognize(new RecognitionSettings { Language = "English" });
string text = document.GetText();
document.Dispose(); // Must not be skipped
engine.Shutdown(); // Must not be skipped — omitting this locks a floating seat
Imports OmniPage
' OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
Using engine As New OmniPageEngine()
engine.SetLicenseFile("C:\Program Files\OmniPage\license.lic") ' File must exist here
engine.Initialize() ' Network call to license server — can fail for license reasons, not OCR reasons
Dim document = engine.CreateDocument()
document.AddPage("invoice.jpg")
document.Recognize(New RecognitionSettings With {.Language = "English"})
Dim text As String = document.GetText()
document.Dispose() ' Must not be skipped
engine.Shutdown() ' Must not be skipped — omitting this locks a floating seat
End Using
// IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // At app startup — once, ever
var text = new IronTesseract().Read("invoice.jpg").Text;
// IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // At app startup — once, ever
var text = new IronTesseract().Read("invoice.jpg").Text;
Imports IronOcr
' IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY" ' At app startup — once, ever
Dim text = New IronTesseract().Read("invoice.jpg").Text
A versão OmniPage possui oito etapas distintas, duas chamadas de limpeza obrigatórias, uma dependência de rede e uma dependência do sistema de arquivos. A versão IronOCR possui duas.
IronOCR vsKofax OmniPage(Tungsten): Comparação de Recursos
A tabela abaixo abrange as funcionalidades mais relevantes para as equipes que avaliam essa migração.
| Recurso | SDKKofax OmniPage | IronOCR |
|---|---|---|
| Método de instalação | Instalador SDK personalizado (sem NuGet) | dotnet add package IronOcr |
| Hora da primeira chamada de OCR | 4 a 12 semanas (aquisição) + horas (configuração) | 30 segundos |
| Mecanismo de licenciamento | Arquivo .lic no disco + servidor de licença opcional |
Chave de string na inicialização do aplicativo |
| Identificação de hardware | Sim (reativação na migração da VM) | Não |
| Servidor de licenças necessário | Sim (para licenças flutuantes) | Não |
| Cobrança por tempo de execução por página | Disponível (variável) | Não |
| Preços publicados | Não (contacte o departamento de vendas) | Sim ($999 / US$ 1.499 / US$ 2.999 perpétuo) |
| Taxas de manutenção anuais | 18–25% do custo da licença | Opcional |
| Gestão do ciclo de vida do motor | Requerido (Initialize / Shutdown) |
Não é necessário |
| Windows x64 | Sim | Sim |
| Linux | Sim (adicionado em janeiro de 2026) | Sim (todas as versões) |
| macOS | Não | Sim |
| Docker / Kubernetes | Difícil (instalador + arquivo de licença na imagem) | Simples (somente pacote NuGet ) |
| Azure / AWS Lambda | Complexo (acessibilidade do servidor de licenças) | Direto |
| formatos de entrada de imagem | Sim | JPG, PNG, BMP, TIFF, GIF e muito mais |
| Entrada nativa de PDF | Sim (pode exigir licença de módulo) | Sim (integrado, sem necessidade de licença adicional) |
| TIFF de várias páginas | Sim | Sim |
| Saída em PDF pesquisável | Sim | Sim (result.SaveAsSearchablePdf()) |
| Pré-processamento automático | Configuração do objeto Settings | API de pipeline integrada + explícita |
| Idiomas suportados | 120+ | Mais de 125 (pacotes NuGet separados) |
| Saída de dados estruturados | Sim (coordenadas da palavra) | Páginas, parágrafos, linhas, palavras, caracteres com coordenadas |
| Pontuação de confiança | Sim | Sim (result.Confidence, por palavra) |
| Leitura de código de barras | Módulo adicional (licença separada) | Embutido (ocr.Configuration.ReadBarCodes = true) |
| Segurança da rosca | Complexo (restrições de compartilhamento de motor) | Completo (um IronTesseract por thread) |
| Suporte a pipelines de CI/CD | Requer instalador em cada agente. | Padrão dotnet restore |
Guia rápido: Migração doKofax OmniPagepara o IronOCR
Passo 1: Substitua o SDK por um pacote NuGet.
O OmniPage não possui nenhum pacote NuGet para remover. Remova as referências manuais de DLL do arquivo .csproj e desinstale o SDK das máquinas de desenvolvimento quando a migração estiver completa. Em seguida, instale o IronOCR:
dotnet add package IronOcr
O pacote NuGet IronOCR reúne o mecanismo de OCR, os filtros de pré-processamento e os componentes de tempo de execução. Sem instalador separado, sem gerenciamento nativo de DLLs, sem registro de componentes.
Etapa 2: Atualizar Namespaces
// Before (Kofax OmniPage)
using Kofax.OmniPage.CSDK;
using Kofax.OmniPageCSDK;
using CSDK;
// After (IronOCR)
using IronOcr;
// Before (Kofax OmniPage)
using Kofax.OmniPage.CSDK;
using Kofax.OmniPageCSDK;
using CSDK;
// After (IronOCR)
using IronOcr;
Imports IronOcr
Etapa 3: Inicializar a licença
Adicione uma linha no início da aplicação. Isso substitui o caminho do arquivo .lic, a configuração do servidor de licença e a chamada engine.Initialize():
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Armazene a chave em uma variável de ambiente (IRONOCR_LICENSE_KEY) ou appsettings.json em vez de no código-fonte. A chave é lida offline — nenhuma chamada de rede ocorre em tempo de execução.
Exemplos de migração de código
Substituição do caminho de erro de inicialização do mecanismo
O aspecto mais perigoso do modelo de ciclo de vida do OmniPage não é o caminho feliz — é o caminho do erro. Qualquer exceção lançada entre engine.Initialize() e engine.Shutdown() pode deixar uma cadeira de licença flutuante bloqueada se Shutdown() não for alcançado. O código de produção exige blocos try/finally em torno de cada chamada de processamento de documento:
Abordagem Kofax OmniPage:
// OmniPage: try/finally required everywhere to protect license seat release
public string ProcessInvoice(string imagePath)
{
var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic");
try
{
engine.Initialize(); // Contacts license server — failure here locks nothing
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(new RecognitionSettings { Language = "English" });
return document.GetText();
}
catch (RecognitionException ex)
{
// Log, rethrow — but Shutdown must still be called
throw new OcrProcessingException("Recognition failed", ex);
}
finally
{
document.Dispose(); // Inner finally: release document
}
}
catch (LicenseValidationException ex)
{
throw new OcrProcessingException("License validation failed", ex);
}
finally
{
engine.Shutdown(); // Outer finally: release license seat — MUST execute
}
}
// OmniPage: try/finally required everywhere to protect license seat release
public string ProcessInvoice(string imagePath)
{
var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic");
try
{
engine.Initialize(); // Contacts license server — failure here locks nothing
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(new RecognitionSettings { Language = "English" });
return document.GetText();
}
catch (RecognitionException ex)
{
// Log, rethrow — but Shutdown must still be called
throw new OcrProcessingException("Recognition failed", ex);
}
finally
{
document.Dispose(); // Inner finally: release document
}
}
catch (LicenseValidationException ex)
{
throw new OcrProcessingException("License validation failed", ex);
}
finally
{
engine.Shutdown(); // Outer finally: release license seat — MUST execute
}
}
Imports System
' OmniPage: try/finally required everywhere to protect license seat release
Public Function ProcessInvoice(imagePath As String) As String
Dim engine As New OmniPageEngine()
engine.SetLicenseFile("C:\Program Files\OmniPage\license.lic")
Try
engine.Initialize() ' Contacts license server — failure here locks nothing
Dim document = engine.CreateDocument()
Try
document.AddPage(imagePath)
document.Recognize(New RecognitionSettings With {.Language = "English"})
Return document.GetText()
Catch ex As RecognitionException
' Log, rethrow — but Shutdown must still be called
Throw New OcrProcessingException("Recognition failed", ex)
Finally
document.Dispose() ' Inner finally: release document
End Try
Catch ex As LicenseValidationException
Throw New OcrProcessingException("License validation failed", ex)
Finally
engine.Shutdown() ' Outer finally: release license seat — MUST execute
End Try
End Function
Abordagem IronOCR:
// IronOCR: Não license seat to release, no engine to shut down
public string ProcessInvoice(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
var ocr = new IronTesseract();
var result = ocr.Read(input);
return result.Text;
// OcrInput disposal is automatic — no license implications on any code path
}
// IronOCR: Não license seat to release, no engine to shut down
public string ProcessInvoice(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
var ocr = new IronTesseract();
var result = ocr.Read(input);
return result.Text;
// OcrInput disposal is automatic — no license implications on any code path
}
Imports IronOcr
Public Function ProcessInvoice(imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
Return result.Text
' OcrInput disposal is automatic — no license implications on any code path
End Using
End Function
O bloco using em OcrInput lida com a limpeza de memória para os dados de imagem carregados. Não existe try/finally para proteger uma cadeira de licença. Uma exceção em qualquer ponto deste método não terá efeitos colaterais fora do escopo do próprio método. Consulte o guia de configuração do IronTesseract para obter opções de configuração, incluindo instâncias locais de thread.
Migração de reconhecimento baseado em zonas
O principal mecanismo de extração estruturada do OmniPage é a definição de zonas: as regiões do documento são declaradas com limites de coordenadas e tipo de reconhecimento por zona (OCR, ICR, OMR, código de barras). Desenvolvedores definem objetos FormZone por modelo de documento e os passam ao mecanismo de reconhecimento. A IronOCR usa CropRectangle para alcançar a mesma extração direcionada sem o gerenciamento de modelos ou declarações de tipo de zona:
Abordagem Kofax OmniPage:
// OmniPage: Zone-based form extraction with template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Define zones per document template
var zones = new List<FormZone>
{
new FormZone { Name = "InvoiceNumber", Type = "OCR",
X = 520, Y = 80, Width = 200, Height = 30 },
new FormZone { Name = "InvoiceDate", Type = "OCR",
X = 520, Y = 120, Width = 200, Height = 30 },
new FormZone { Name = "TotalAmount", Type = "OCR",
X = 520, Y = 580, Width = 200, Height = 30 },
new FormZone { Name = "VendorName", Type = "OCR",
X = 50, Y = 80, Width = 300, Height = 40 }
};
var template = new FormTemplate { Name = "StandardInvoice", Zones = zones };
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(invoicePath);
document.ApplyTemplate(template);
document.Recognize(settings);
var fields = new Dictionary<string, string>();
foreach (var zone in zones)
fields[zone.Name] = document.GetZoneText(zone.Name);
document.Dispose();
engine.Shutdown();
return fields;
}
// OmniPage: Zone-based form extraction with template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Define zones per document template
var zones = new List<FormZone>
{
new FormZone { Name = "InvoiceNumber", Type = "OCR",
X = 520, Y = 80, Width = 200, Height = 30 },
new FormZone { Name = "InvoiceDate", Type = "OCR",
X = 520, Y = 120, Width = 200, Height = 30 },
new FormZone { Name = "TotalAmount", Type = "OCR",
X = 520, Y = 580, Width = 200, Height = 30 },
new FormZone { Name = "VendorName", Type = "OCR",
X = 50, Y = 80, Width = 300, Height = 40 }
};
var template = new FormTemplate { Name = "StandardInvoice", Zones = zones };
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(invoicePath);
document.ApplyTemplate(template);
document.Recognize(settings);
var fields = new Dictionary<string, string>();
foreach (var zone in zones)
fields[zone.Name] = document.GetZoneText(zone.Name);
document.Dispose();
engine.Shutdown();
return fields;
}
Imports System
Imports System.Collections.Generic
' OmniPage: Zone-based form extraction with template management
Public Function ExtractInvoiceFields(invoicePath As String) As Dictionary(Of String, String)
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
' Define zones per document template
Dim zones As New List(Of FormZone) From {
New FormZone With {.Name = "InvoiceNumber", .Type = "OCR", .X = 520, .Y = 80, .Width = 200, .Height = 30},
New FormZone With {.Name = "InvoiceDate", .Type = "OCR", .X = 520, .Y = 120, .Width = 200, .Height = 30},
New FormZone With {.Name = "TotalAmount", .Type = "OCR", .X = 520, .Y = 580, .Width = 200, .Height = 30},
New FormZone With {.Name = "VendorName", .Type = "OCR", .X = 50, .Y = 80, .Width = 300, .Height = 40}
}
Dim template As New FormTemplate With {.Name = "StandardInvoice", .Zones = zones}
Dim settings As New RecognitionSettings With {.Language = "English"}
Dim document = engine.CreateDocument()
document.AddPage(invoicePath)
document.ApplyTemplate(template)
document.Recognize(settings)
Dim fields As New Dictionary(Of String, String)()
For Each zone In zones
fields(zone.Name) = document.GetZoneText(zone.Name)
Next
document.Dispose()
engine.Shutdown()
Return fields
End Using
End Function
Abordagem IronOCR:
// IronOCR: CropRectangle targets specific regions — no template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
var fields = new Dictionary<string, string>();
var ocr = new IronTesseract();
// Extract each field by reading only the target region
var fieldRegions = new Dictionary<string, CropRectangle>
{
["InvoiceNumber"] = new CropRectangle(520, 80, 200, 30),
["InvoiceDate"] = new CropRectangle(520, 120, 200, 30),
["TotalAmount"] = new CropRectangle(520, 580, 200, 30),
["VendorName"] = new CropRectangle(50, 80, 300, 40)
};
foreach (var (fieldName, region) in fieldRegions)
{
using var input = new OcrInput();
input.LoadImage(invoicePath, region);
fields[fieldName] = ocr.Read(input).Text.Trim();
}
return fields;
}
// IronOCR: CropRectangle targets specific regions — no template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
var fields = new Dictionary<string, string>();
var ocr = new IronTesseract();
// Extract each field by reading only the target region
var fieldRegions = new Dictionary<string, CropRectangle>
{
["InvoiceNumber"] = new CropRectangle(520, 80, 200, 30),
["InvoiceDate"] = new CropRectangle(520, 120, 200, 30),
["TotalAmount"] = new CropRectangle(520, 580, 200, 30),
["VendorName"] = new CropRectangle(50, 80, 300, 40)
};
foreach (var (fieldName, region) in fieldRegions)
{
using var input = new OcrInput();
input.LoadImage(invoicePath, region);
fields[fieldName] = ocr.Read(input).Text.Trim();
}
return fields;
}
Imports System.Collections.Generic
' IronOCR: CropRectangle targets specific regions — no template management
Public Function ExtractInvoiceFields(invoicePath As String) As Dictionary(Of String, String)
Dim fields As New Dictionary(Of String, String)()
Dim ocr As New IronTesseract()
' Extract each field by reading only the target region
Dim fieldRegions As New Dictionary(Of String, CropRectangle) From {
{"InvoiceNumber", New CropRectangle(520, 80, 200, 30)},
{"InvoiceDate", New CropRectangle(520, 120, 200, 30)},
{"TotalAmount", New CropRectangle(520, 580, 200, 30)},
{"VendorName", New CropRectangle(50, 80, 300, 40)}
}
For Each kvp In fieldRegions
Dim fieldName = kvp.Key
Dim region = kvp.Value
Using input As New OcrInput()
input.LoadImage(invoicePath, region)
fields(fieldName) = ocr.Read(input).Text.Trim()
End Using
Next
Return fields
End Function
Cada CropRectangle especifica (x, y, width, height) em pixels. O mecanismo OCR processa apenas a região selecionada, e não a página inteira — a mesma vantagem de eficiência que o processamento baseado em zonas proporciona no OmniPage. O guia de OCR baseado em região cobre padrões de seleção de coordenadas, incluindo como extrair várias regiões de um único carregamento de imagem usando a API de múltiplas regiões da OcrInput.
Migração de saída de dados estruturados
O OmniPage expõe a estrutura do documento por meio de um pipeline de exportação proprietário: as configurações de formatação são aplicadas a um documento reconhecido e a saída é gravada em um arquivo em um formato especificado (RTF, XML, CSV, PDF pesquisável). Acessar coordenadas em nível de palavra requer iterar um iterador de resultados com consultas de posição explícitas. O IronOCR expõe os mesmos dados estruturados diretamente no objeto de resultado, sem uma etapa de exportação secundária:
Abordagem Kofax OmniPage:
// OmniPage: Structured output requires format configuration and file export
public void ExtractStructuredContent(string imagePath, string outputDir)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(imagePath);
document.Recognize(settings);
// Word-level coordinates through result iterator
var iterator = document.GetResultIterator(ResultIteratorLevel.Word);
while (iterator.MoveNext())
{
string word = iterator.GetText();
var bounds = iterator.GetBoundingBox();
double confidence = iterator.GetConfidence();
Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%");
}
// Structured export requires separate output format configuration
var outputSettings = new OutputSettings
{
Format = OutputFormat.XML,
IncludeCoordinates = true,
IncludeConfidence = true
};
document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings);
document.Dispose();
engine.Shutdown();
}
// OmniPage: Structured output requires format configuration and file export
public void ExtractStructuredContent(string imagePath, string outputDir)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(imagePath);
document.Recognize(settings);
// Word-level coordinates through result iterator
var iterator = document.GetResultIterator(ResultIteratorLevel.Word);
while (iterator.MoveNext())
{
string word = iterator.GetText();
var bounds = iterator.GetBoundingBox();
double confidence = iterator.GetConfidence();
Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%");
}
// Structured export requires separate output format configuration
var outputSettings = new OutputSettings
{
Format = OutputFormat.XML,
IncludeCoordinates = true,
IncludeConfidence = true
};
document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings);
document.Dispose();
engine.Shutdown();
}
Imports System
Imports System.IO
Public Sub ExtractStructuredContent(imagePath As String, outputDir As String)
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
Dim settings As New RecognitionSettings With {.Language = "English"}
Dim document = engine.CreateDocument()
document.AddPage(imagePath)
document.Recognize(settings)
' Word-level coordinates through result iterator
Dim iterator = document.GetResultIterator(ResultIteratorLevel.Word)
While iterator.MoveNext()
Dim word As String = iterator.GetText()
Dim bounds = iterator.GetBoundingBox()
Dim confidence As Double = iterator.GetConfidence()
Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%")
End While
' Structured export requires separate output format configuration
Dim outputSettings As New OutputSettings With {
.Format = OutputFormat.XML,
.IncludeCoordinates = True,
.IncludeConfidence = True
}
document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings)
document.Dispose()
engine.Shutdown()
End Using
End Sub
Abordagem IronOCR:
// IronOCR: Structured data directly on OcrResult — no export step
public void ExtractStructuredContent(string imagePath)
{
var result = new IronTesseract().Read(imagePath);
Console.WriteLine($"Confidence: {result.Confidence:F1}%");
Console.WriteLine($"Pages: {result.Pages.Length}");
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]");
Console.WriteLine(paragraph.Text);
foreach (var word in paragraph.Words)
{
// Per-word confidence and coordinates — no iterator needed
Console.WriteLine(
$" Word: '{word.Text}' " +
$"at ({word.X},{word.Y}) " +
$"conf: {word.Confidence:F1}%");
}
}
}
}
// IronOCR: Structured data directly on OcrResult — no export step
public void ExtractStructuredContent(string imagePath)
{
var result = new IronTesseract().Read(imagePath);
Console.WriteLine($"Confidence: {result.Confidence:F1}%");
Console.WriteLine($"Pages: {result.Pages.Length}");
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]");
Console.WriteLine(paragraph.Text);
foreach (var word in paragraph.Words)
{
// Per-word confidence and coordinates — no iterator needed
Console.WriteLine(
$" Word: '{word.Text}' " +
$"at ({word.X},{word.Y}) " +
$"conf: {word.Confidence:F1}%");
}
}
}
}
Public Sub ExtractStructuredContent(imagePath As String)
Dim result = New IronTesseract().Read(imagePath)
Console.WriteLine($"Confidence: {result.Confidence:F1}%")
Console.WriteLine($"Pages: {result.Pages.Length}")
For Each page In result.Pages
For Each paragraph In page.Paragraphs
Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]")
Console.WriteLine(paragraph.Text)
For Each word In paragraph.Words
' Per-word confidence and coordinates — no iterator needed
Console.WriteLine(
$" Word: '{word.Text}' " &
$"at ({word.X},{word.Y}) " &
$"conf: {word.Confidence:F1}%")
Next
Next
Next
End Sub
A hierarquia do objeto OcrResult — Páginas, Parágrafos, Linhas, Palavras, Caracteres — fornece os mesmos dados posicionais que o iterador de resultado da OmniPage expõe, mas como um gráfico de objeto navegável em vez de um cursor sequencial. Os níveis de confiança estão disponíveis para resultados, páginas, parágrafos e palavras, sem necessidade de configuração adicional. O guia de resultados de leitura abrange todas as propriedades de dados estruturados, e o guia de pontuações de confiança abrange padrões de validação por palavra.
Migração de processamento TIFF de várias páginas
O fluxo de trabalho TIFF de várias páginas do OmniPage exige a extração página por página do contêiner TIFF, com uma chamada de reconhecimento e descarte de documento separados para cada página. Isso cria tanto código repetitivo quanto uma área de gerenciamento de recursos proporcional ao número de páginas. O IronOCR carrega arquivos TIFF com vários quadros em uma única chamada:
Abordagem Kofax OmniPage:
// OmniPage: Page-by-page TIFF extraction with per-page lifecycle
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Open TIFF and iterate frames
var tiffContainer = engine.OpenTiff(tiffPath);
int pageCount = tiffContainer.GetPageCount();
var settings = new RecognitionSettings { Language = "English" };
for (int i = 0; i < pageCount; i++)
{
var page = tiffContainer.GetPage(i); // Extract frame
var document = engine.CreateDocument();
try
{
document.AddPage(page);
document.Recognize(settings);
pageTexts.Add(document.GetText());
}
finally
{
document.Dispose(); // Dispose per page to manage memory
}
}
tiffContainer.Dispose();
engine.Shutdown();
return pageTexts;
}
// OmniPage: Page-by-page TIFF extraction with per-page lifecycle
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Open TIFF and iterate frames
var tiffContainer = engine.OpenTiff(tiffPath);
int pageCount = tiffContainer.GetPageCount();
var settings = new RecognitionSettings { Language = "English" };
for (int i = 0; i < pageCount; i++)
{
var page = tiffContainer.GetPage(i); // Extract frame
var document = engine.CreateDocument();
try
{
document.AddPage(page);
document.Recognize(settings);
pageTexts.Add(document.GetText());
}
finally
{
document.Dispose(); // Dispose per page to manage memory
}
}
tiffContainer.Dispose();
engine.Shutdown();
return pageTexts;
}
Imports System.Collections.Generic
' OmniPage: Page-by-page TIFF extraction with per-page lifecycle
Public Function ExtractFromMultiPageTiff(tiffPath As String) As List(Of String)
Dim pageTexts As New List(Of String)()
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
' Open TIFF and iterate frames
Dim tiffContainer = engine.OpenTiff(tiffPath)
Dim pageCount As Integer = tiffContainer.GetPageCount()
Dim settings As New RecognitionSettings With {.Language = "English"}
For i As Integer = 0 To pageCount - 1
Dim page = tiffContainer.GetPage(i) ' Extract frame
Dim document = engine.CreateDocument()
Try
document.AddPage(page)
document.Recognize(settings)
pageTexts.Add(document.GetText())
Finally
document.Dispose() ' Dispose per page to manage memory
End Try
Next
tiffContainer.Dispose()
engine.Shutdown()
End Using
Return pageTexts
End Function
Abordagem IronOCR:
// IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // All frames loaded automatically
var result = new IronTesseract().Read(input);
// Each TIFF frame becomes a page in the result
return result.Pages.Select(page => page.Text).ToList();
}
// IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // All frames loaded automatically
var result = new IronTesseract().Read(input);
// Each TIFF frame becomes a page in the result
return result.Pages.Select(page => page.Text).ToList();
}
Imports System.Collections.Generic
Imports IronOcr
' IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
Public Function ExtractFromMultiPageTiff(tiffPath As String) As List(Of String)
Using input As New OcrInput()
input.LoadImageFrames(tiffPath) ' All frames loaded automatically
Dim result = New IronTesseract().Read(input)
' Each TIFF frame becomes a page in the result
Return result.Pages.Select(Function(page) page.Text).ToList()
End Using
End Function
LoadImageFrames lê cada quadro do contêiner TIFF em uma única chamada. O resultado expõe um OcrResult.Page por quadro, preservando a estrutura página por página sem exigir um loop de iteração manual ou limpeza por página. Para fluxos de trabalho de lote TIFF em produção, consulte o guia de entrada para TIFF e GIF.
Migração para processamento paralelo seguro para threads
O mecanismo do OmniPage é um recurso compartilhado e com estado. Compartilhar uma instância OmniPageEngine entre threads requer sincronização externa porque múltiplos threads chamando CreateDocument() simultaneamente podem produzir um estado intercalado. O padrão seguro — uma instância do motor por thread — conflita com o custo pesado de inicialização do motor. Instâncias da IronOCR não carregam estado compartilhado: crie uma IronTesseract por thread com zero de sobrecarga de coordenação:
Abordagem Kofax OmniPage:
// OmniPage: Shared engine with locking to serialize document operations
public ConcurrentDictionary<string, string> ProcessBatchWithEngine(
string[] imagePaths, string licensePath)
{
var results = new ConcurrentDictionary<string, string>();
var engineLock = new object();
// One engine — document operations must be serialized
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
Parallel.ForEach(imagePaths, imagePath =>
{
lock (engineLock) // Serialize: one recognition at a time
{
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(settings);
results[imagePath] = document.GetText();
}
finally
{
document.Dispose();
}
}
});
engine.Shutdown();
return results;
}
// OmniPage: Shared engine with locking to serialize document operations
public ConcurrentDictionary<string, string> ProcessBatchWithEngine(
string[] imagePaths, string licensePath)
{
var results = new ConcurrentDictionary<string, string>();
var engineLock = new object();
// One engine — document operations must be serialized
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
Parallel.ForEach(imagePaths, imagePath =>
{
lock (engineLock) // Serialize: one recognition at a time
{
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(settings);
results[imagePath] = document.GetText();
}
finally
{
document.Dispose();
}
}
});
engine.Shutdown();
return results;
}
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
' OmniPage: Shared engine with locking to serialize document operations
Public Function ProcessBatchWithEngine(imagePaths As String(), licensePath As String) As ConcurrentDictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
Dim engineLock As New Object()
' One engine — document operations must be serialized
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
Dim settings As New RecognitionSettings With {.Language = "English"}
Parallel.ForEach(imagePaths, Sub(imagePath)
SyncLock engineLock ' Serialize: one recognition at a time
Dim document = engine.CreateDocument()
Try
document.AddPage(imagePath)
document.Recognize(settings)
results(imagePath) = document.GetText()
Finally
document.Dispose()
End Try
End SyncLock
End Sub)
engine.Shutdown()
End Using
Return results
End Function
Abordagem IronOCR:
// IronOCR: One IronTesseract per thread — no locking, genuine parallelism
public ConcurrentDictionary<string, string> ProcessBatchInParallel(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
Parallel.ForEach(imagePaths, imagePath =>
{
// Each thread creates its own instance — no shared state, no locks
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = ocr.Read(input);
results[imagePath] = result.Text;
});
return results;
}
// IronOCR: One IronTesseract per thread — no locking, genuine parallelism
public ConcurrentDictionary<string, string> ProcessBatchInParallel(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
Parallel.ForEach(imagePaths, imagePath =>
{
// Each thread creates its own instance — no shared state, no locks
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = ocr.Read(input);
results[imagePath] = result.Text;
});
return results;
}
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Imports IronOcr
' IronOCR: One IronTesseract per thread — no locking, genuine parallelism
Public Function ProcessBatchInParallel(imagePaths As String()) As ConcurrentDictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
Parallel.ForEach(imagePaths, Sub(imagePath)
' Each thread creates its own instance — no shared state, no locks
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage(imagePath)
Dim result = ocr.Read(input)
results(imagePath) = result.Text
End Using
End Sub)
Return results
End Function
A versão OmniPage serializa todo o reconhecimento por meio de um bloqueio, anulando o paralelismo. A versão IronOCR processa documentos simultaneamente, sem necessidade de coordenação. Para cargas de trabalho em lote de alto rendimento, consulte o exemplo de multithreading e o guia de otimização de velocidade .
Geração de PDFs pesquisáveis a partir de arquivos digitalizados
A saída de PDF pesquisável da OmniPage exige montar um pipeline de reconhecimento com configuração explícita de OutputSettings e OutputFormat antes de salvar. O IronOCR gera PDFs pesquisáveis a partir de uma única chamada de método no objeto de resultado:
Abordagem Kofax OmniPage:
// OmniPage: Searchable PDF requires OutputSettings configuration before save
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var recognitionSettings = new RecognitionSettings
{
Language = "English",
AccuracyMode = "Maximum",
PreserveLayout = true
};
var outputSettings = new OutputSettings
{
Format = OutputFormat.SearchablePDF,
Compression = PDFCompression.Standard,
ImageQuality = 85,
EmbedFonts = true
};
foreach (var pdfPath in scannedPdfPaths)
{
var document = engine.OpenPDF(pdfPath);
document.RecognizeAll(recognitionSettings);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
document.SaveAs(outputPath, outputSettings);
document.Dispose();
}
engine.Shutdown();
}
// OmniPage: Searchable PDF requires OutputSettings configuration before save
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var recognitionSettings = new RecognitionSettings
{
Language = "English",
AccuracyMode = "Maximum",
PreserveLayout = true
};
var outputSettings = new OutputSettings
{
Format = OutputFormat.SearchablePDF,
Compression = PDFCompression.Standard,
ImageQuality = 85,
EmbedFonts = true
};
foreach (var pdfPath in scannedPdfPaths)
{
var document = engine.OpenPDF(pdfPath);
document.RecognizeAll(recognitionSettings);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
document.SaveAs(outputPath, outputSettings);
document.Dispose();
}
engine.Shutdown();
}
Imports System.IO
' OmniPage: Searchable PDF requires OutputSettings configuration before save
Public Sub ConvertArchiveToSearchable(scannedPdfPaths As String(), outputDirectory As String)
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
Dim recognitionSettings As New RecognitionSettings With {
.Language = "English",
.AccuracyMode = "Maximum",
.PreserveLayout = True
}
Dim outputSettings As New OutputSettings With {
.Format = OutputFormat.SearchablePDF,
.Compression = PDFCompression.Standard,
.ImageQuality = 85,
.EmbedFonts = True
}
For Each pdfPath As String In scannedPdfPaths
Dim document = engine.OpenPDF(pdfPath)
document.RecognizeAll(recognitionSettings)
Dim outputPath As String = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(pdfPath) & "_searchable.pdf")
document.SaveAs(outputPath, outputSettings)
document.Dispose()
Next
End Using
engine.Shutdown()
End Sub
Abordagem IronOCR:
// IronOCR: Searchable PDF output is one method call on the result
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
var ocr = new IronTesseract();
foreach (var pdfPath in scannedPdfPaths)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath); // All pages loaded automatically
var result = ocr.Read(input);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)");
}
}
// IronOCR: Searchable PDF output is one method call on the result
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
var ocr = new IronTesseract();
foreach (var pdfPath in scannedPdfPaths)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath); // All pages loaded automatically
var result = ocr.Read(input);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)");
}
}
Imports System.IO
Imports IronOcr
Public Sub ConvertArchiveToSearchable(scannedPdfPaths As String(), outputDirectory As String)
Dim ocr As New IronTesseract()
For Each pdfPath As String In scannedPdfPaths
Using input As New OcrInput()
input.LoadPdf(pdfPath) ' All pages loaded automatically
Dim result = ocr.Read(input)
Dim outputPath As String = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(pdfPath) & "_searchable.pdf")
result.SaveAsSearchablePdf(outputPath)
Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)")
End Using
Next
End Sub
SaveAsSearchablePdf incorpora uma camada de texto no PDF, tornando-o indexável em sistemas de gerenciamento de documentos sem alterar a aparência visual do digitalizado original. O guia de PDF pesquisável aborda as opções da camada de texto e o exemplo de OCR em PDF demonstra o processamento completo de PDFs digitalizados.
Referência de mapeamento da APIKofax OmniPagepara IronOCR
| Kofax OmniPage | Equivalente de IronOCR |
|---|---|
using Kofax.OmniPage.CSDK; |
using IronOcr; |
using Kofax.OmniPageCSDK; |
using IronOcr; |
using CSDK; |
using IronOcr; |
new OmniPageEngine() |
Não é necessário — nenhum objeto de motor |
engine.SetLicenseFile(path) |
IronOcr.License.LicenseKey = "key"; |
engine.Initialize() |
Não é necessário |
engine.Shutdown() |
Não é necessário |
engine.CreateDocument() |
new OcrInput() |
document.AddPage(imagePath) |
input.LoadImage(imagePath) |
engine.OpenPDF(pdfPath) |
input.LoadPdf(pdfPath) |
engine.OpenTiff(tiffPath) |
input.LoadImageFrames(tiffPath) |
document.Recognize(settings) |
new IronTesseract().Read(input) |
document.RecognizeAll(settings) |
new IronTesseract().Read(input) (todas as páginas de uma vez) |
document.GetText() |
result.Text |
document.GetZoneText(zoneName) |
input.LoadImage(path, cropRectangle) + result.Text |
document.Dispose() |
using var input = new OcrInput() (automático) |
iterator.GetText() |
result.Pages[n].Words[m].Text |
iterator.GetBoundingBox() |
result.Pages[n].Words[m].X / Y / Width / Height |
iterator.GetConfidence() |
result.Pages[n].Words[m].Confidence |
engine.LoadLanguageDictionary("German") |
dotnet add package IronOcr.Languages.German |
settings.PrimaryLanguage = "English" |
ocr.Language = OcrLanguage.English; |
settings.SecondaryLanguages = new[] {"German"} |
ocr.Language = OcrLanguage.English + OcrLanguage.German; |
settings.DeskewImage = true |
input.Deskew(); |
settings.DespeckleLevel = 2 |
input.DeNoise(); |
settings.ContrastEnhancement = true |
input.Contrast(); |
settings.AutoRotate = true |
input.Deskew(); (inclui correção de rotação) |
document.SaveAs(path, outputSettings) |
result.SaveAsSearchablePdf(path) |
OutputFormat.SearchablePDF |
result.SaveAsSearchablePdf(path) |
OutputFormat.XML (com coordenadas) |
graph de objetos result.Pages / .Paragraphs / .Words |
FormZone com limites de coordenadas |
new CropRectangle(x, y, width, height) |
| Contagem de licenças por página | Não aplicável |
Implantação de arquivo .lic |
Não aplicável |
| Configuração do servidor de licenças | Não aplicável |
Problemas e soluções comuns em migrações
Problema 1: Exceções de "Arquivo de Licença Não Encontrado" em Produção
Kofax OmniPage: Cada implantação requer que o arquivo .lic exista em um caminho específico acessível ao processo do aplicativo. Um pipeline de implantação que não copia explicitamente o arquivo de licença para o servidor de destino causa FileNotFoundException ou LicenseException na inicialização do mecanismo. As equipes de segurança frequentemente sinalizam a incorporação de arquivos .lic em imagens de contêiner ou o compromisso deles ao controle de versão.
Solução: O IronOCR lê sua licença a partir de uma string. Armazene a chave como uma variável de ambiente e leia-a na inicialização — sem arquivo para implantar, sem caminho para configurar:
// At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IronOCR license key not configured.");
// At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IronOCR license key not configured.");
Imports System
' At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY"), Throw New InvalidOperationException("IronOCR license key not configured."))
No Docker, passe --env IRONOCR_LICENSE_KEY=YOUR-KEY. Não Azure App Service, defina-o como uma Configuração do Aplicativo. Não Kubernetes, injete-o através de um Secret. Sem montagem de arquivos, sem configuração de caminhos, sem revisão de segurança para arquivos de licença incorporados.
Problema 2: Licenças flutuantes bloqueadas após falha no processo
Kofax OmniPage: Quando um processo de aplicação falha, é morto por um watchdog, ou sai via Environment.FailFast, engine.Shutdown() não é executado. As licenças flutuantes permanecem em uso até que o tempo limite do servidor de licenças expire — normalmente entre 30 e 60 minutos. Em um ambiente conteinerizado onde os pods são reiniciados frequentemente, esse comportamento esgota o conjunto de licenças.
Solução: O IronOCR não tem o conceito de assento de licença "reservado". A falha de um processo não libera recursos nem bloqueia nenhum sistema externo. O próximo processo inicia-se imediatamente, sem necessidade de esperar pela disponibilidade de lugares:
// IronOCR: Process crash has no license implications whatsoever
// Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg"); // Não seat to check out
// IronOCR: Process crash has no license implications whatsoever
// Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg"); // Não seat to check out
' IronOCR: Process crash has no license implications whatsoever
' Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim result = New IronTesseract().Read("document.jpg") ' Não seat to check out
Para serviços ASP.NET Core resilientes, consulte o guia de OCR assíncrono para obter padrões que se integram perfeitamente com serviços hospedados e realizam o desligamento correto.
Problema 3: Falha na criação da imagem Docker — o instalador não pode ser executado de forma não interativa
Kofax OmniPage: O instalador do SDK da OmniPage é um instalador GUI ou semi-interativo que não é executado de forma limpa em um contexto docker build. As equipes que tentam incorporar o SDK em uma imagem de contêiner encontram falhas no contrato de licença do instalador ou nas etapas de seleção de componentes. As soluções alternativas (opções de instalação silenciosa, cópias de DLL pré-extraídas) não estão documentadas e são específicas para cada versão.
Solução: A Dockerfile da IronOCR é de três linhas:
FROM mcr.microsoft.com/dotnet/aspnet:8.0
RUN apt-get update && apt-get install -y libgdiplus # Single Linux dependency
COPY --from=build /app/publish /app
WORKDIR /app
ENTRYPOINT ["dotnet", "YourApp.dll"]
libgdiplus é a única dependência do sistema. O pacote NuGet da IronOCR é restaurado por dotnet restore dentro da etapa de build como qualquer outra referência de pacote. O guia de implantação do Docker abrange imagens base Debian e Alpine, enquanto o guia de implantação do Linux abrange nomes de pacotes específicos de cada distribuição.
Problema 4: Reativação necessária após migração de VM ou escalonamento automático na nuvem
Kofax OmniPage: A ativação do OmniPage está vinculada à identidade do hardware. Ambientes em nuvem que migram máquinas virtuais entre hosts físicos, escalam automaticamente para novas instâncias ou substituem contêineres por novas imagens desencadeiam alterações na identidade do hardware que exigem reativação. Contatar o suporte da Tungsten para reativação durante um incidente aumenta o risco operacional.
Solução: A chave de licença do IronOCR é independente do hardware. A mesma chave funciona em qualquer máquina, qualquer contêiner, qualquer região da nuvem e qualquer número de instâncias com escalonamento automático dentro do nível licenciado. Sem reativação, sem contato com o suporte, sem tempo de inatividade:
// Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
bool valid = IronOcr.License.IsLicensed; // Verify without a network call
// Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
bool valid = IronOcr.License.IsLicensed; // Verify without a network call
' Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim valid As Boolean = IronOcr.License.IsLicensed ' Verify without a network call
Problema 5: O objeto RecognitionSettings não possui equivalente no IronOCR.
Kofax OmniPage: OmniPage utiliza um objeto RecognitionSettings (ou PreprocessingSettings dependendo da versão do SDK) para configurar o comportamento do motor por documento. As equipes que estão migrando esperam um objeto de configuração paralelo no IronOCR.
Solução: A IronOCR divide a configuração em duas superfícies: operações de pré-processamento em OcrInput e configurações do motor em IronTesseract. Não existe nenhum objeto de configurações para instanciar:
// OmniPage settings object →IronOCR method calls on OcrInput
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English; // RecognitionSettings.Language
// ocr.Configuration.TesseractVersion = ... // Engine version already optimized
using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew(); // settings.DeskewImage = true
input.DeNoise(); // settings.DespeckleLevel = 2
input.Contrast(); // settings.ContrastEnhancement = true
var result = ocr.Read(input);
// OmniPage settings object →IronOCR method calls on OcrInput
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English; // RecognitionSettings.Language
// ocr.Configuration.TesseractVersion = ... // Engine version already optimized
using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew(); // settings.DeskewImage = true
input.DeNoise(); // settings.DespeckleLevel = 2
input.Contrast(); // settings.ContrastEnhancement = true
var result = ocr.Read(input);
Imports IronOcr
' OmniPage settings object → IronOCR method calls on OcrInput
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.English ' RecognitionSettings.Language
' ocr.Configuration.TesseractVersion = ... ' Engine version already optimized
Using input As New OcrInput()
input.LoadImage(imagePath)
input.Deskew() ' settings.DeskewImage = true
input.DeNoise() ' settings.DespeckleLevel = 2
input.Contrast() ' settings.ContrastEnhancement = true
Dim result = ocr.Read(input)
End Using
O guia de correção da qualidade da imagem lista todos os métodos de pré-processamento disponíveis, com orientações sobre quais filtros se aplicam a quais perfis de qualidade de documento.
Problema 6: Os arquivos de modelo de zona não podem ser portados diretamente.
Kofax OmniPage: Os modelos de formulários da OmniPage armazenam definições de zona em arquivos de modelo proprietários .fdt ou .fpf que definem posições de campos, tipos de reconhecimento e regras de validação por classe de documento. Esses arquivos não podem ser importados pelo IronOCR.
Solução: Extraia os dados de coordenadas dos arquivos de modelo (eles são baseados em XML) e converta cada zona para um CropRectangle. Os nomes dos campos tornam-se chaves de dicionário; As coordenadas das zonas mapeiam diretamente para parâmetros (x, y, width, height):
// Convert OmniPage zone definition to IronOCR CropRectangle
// OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
//IronOCR equivalent:
var totalDueRegion = new CropRectangle(490, 612, 180, 28);
using var input = new OcrInput();
input.LoadImage(invoicePath, totalDueRegion);
string totalDue = new IronTesseract().Read(input).Text.Trim();
// Convert OmniPage zone definition to IronOCR CropRectangle
// OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
//IronOCR equivalent:
var totalDueRegion = new CropRectangle(490, 612, 180, 28);
using var input = new OcrInput();
input.LoadImage(invoicePath, totalDueRegion);
string totalDue = new IronTesseract().Read(input).Text.Trim();
Imports IronOcr
' Convert OmniPage zone definition to IronOCR CropRectangle
' OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
' IronOCR equivalent:
Dim totalDueRegion As New CropRectangle(490, 612, 180, 28)
Using input As New OcrInput()
input.LoadImage(invoicePath, totalDueRegion)
Dim totalDue As String = New IronTesseract().Read(input).Text.Trim()
End Using
Para documentos onde as zonas variam por página, carregue a mesma imagem com valores CropRectangle diferentes por região em vez de recarregar o arquivo. O exemplo de recorte de região demonstra múltiplas leituras de região de uma única fonte de imagem.
Lista de verificação para migração do Kofax OmniPage
Pré-migração
Identifique todos os pontos de integração do OmniPage no código-fonte:
# Find all OmniPage namespace references
grep -r "Kofax\|OmniPage\|CSDK" --include="*.cs" --include="*.csproj" .
# Find engine lifecycle calls
grep -r "Initialize\|Shutdown\|SetLicenseFile" --include="*.cs" .
# Find document and zone creation patterns
grep -r "CreateDocument\|AddPage\|FormZone\|RecognitionSettings\|PreprocessingSettings" --include="*.cs" .
# Find output format configuration
grep -r "OutputSettings\|OutputFormat\|SaveAs\|GetText" --include="*.cs" .
# Find license file path references
grep -r "\.lic\|license\.lic\|licensePath" --include="*.cs" --include="*.config" --include="*.json" .
# Find all OmniPage namespace references
grep -r "Kofax\|OmniPage\|CSDK" --include="*.cs" --include="*.csproj" .
# Find engine lifecycle calls
grep -r "Initialize\|Shutdown\|SetLicenseFile" --include="*.cs" .
# Find document and zone creation patterns
grep -r "CreateDocument\|AddPage\|FormZone\|RecognitionSettings\|PreprocessingSettings" --include="*.cs" .
# Find output format configuration
grep -r "OutputSettings\|OutputFormat\|SaveAs\|GetText" --include="*.cs" .
# Find license file path references
grep -r "\.lic\|license\.lic\|licensePath" --include="*.cs" --include="*.config" --include="*.json" .
Inventário antes de começar:
- Contar arquivos com importações de namespace OmniPage
- Liste todas as definições de
FormTemplateeFormZonee extraia seus dados de coordenadas - Identifique quais dicionários de idiomas estão carregados e mapeie para os pacotes NuGet
IronOcr.Languages.* - Observe quais formatos de saída são usados (PDF pesquisável, texto simples, XML) e seus equivalentes no IronOCR.
- Localize todas as referências de arquivos
.licem scripts de implantação e configuração
Migração de código
- Remova as referências de DLL da OmniPage de todos os arquivos
.csproj - Execute
dotnet add package IronOcrem cada projeto que realiza OCR - Adicione pacotes de idiomas para cada idioma em uso:
dotnet add package IronOcr.Languages.[Language] - Adicione
IronOcr.License.LicenseKey = ...;em cada ponto de entrada da aplicação (Program.cs, Startup.cs, ou host de serviço) - Substitua todos os
using Kofax.OmniPage.CSDK;,using CSDK;, e importações de namespace relacionadas porusing IronOcr; - Remova toda a construção
OmniPageEngine,SetLicenseFile, e chamadasInitialize - Remova todas as chamadas
engine.Shutdown()e as implementaçõesIDisposableque existem apenas para garantir o desligamento - Substitua
engine.CreateDocument()+document.AddPage()pornew OcrInput()+input.LoadImage() - Substitua
engine.OpenPDF()+ iteração por página porinput.LoadPdf()(todas as páginas em uma chamada) - Substitua
engine.OpenTiff()+ loops por quadro porinput.LoadImageFrames() - Substitua
document.Recognize(settings)pornew IronTesseract().Read(input) - Converta os dados de coordenadas
FormZonepara instânciasCropRectangle(x, y, width, height) - Substitua
document.GetZoneText()porinput.LoadImage(path, cropRectangle)+result.Textpor região - Substitua
document.SaveAs(path, outputSettings)para PDF pesquisável porresult.SaveAsSearchablePdf(path) - Substitua os padrões de iterador de resultados por travessia de propriedade
result.Pages/.Paragraphs/.Words - Remova objetos
PreprocessingSettingse substitua sinalizadores booleanos por chamadas de método explícitasinput.Deskew(),input.DeNoise(),input.Contrast() - Remova os caminhos dos arquivos de licença dos scripts de implantação, arquivos de configuração e modelos de infraestrutura como código.
Pós-migração
- Verificar a precisão do OCR em uma amostra representativa de mais de 100 documentos do acervo ativo — comparar linha por linha com a saída do OmniPage para faturas, contratos e formulários.
- Confirme que a extração de zona baseada em
CropRectangleretorna texto correspondente à saídaGetZoneText()da OmniPage para cada campo mapeado - Testar o processamento de PDFs com várias páginas: verificar a contagem de páginas, a ordem das páginas e a continuidade do texto.
- Teste o processamento TIFF de múltiplos quadros: confirme se todos os quadros foram processados e se a sequência dos quadros foi preservada.
- Verificar se o PDF pesquisável gerado pode ser indexado no sistema de gerenciamento de documentos utilizado (SharePoint, OpenText, etc.).
- Execute um teste de processamento paralelo com mais de 50 documentos simultâneos e confirme que não há problemas de segurança de threads.
- Teste a cobertura do pacote de idiomas: carregue cada dicionário de idioma usado anteriormente via
IronOcr.Languages.*e verifique a qualidade do reconhecimento - Confirme se as falhas do processo e as reinicializações do contêiner não exigem nenhuma ação de recuperação de licença.
- Execute a compilação Docker no agente CI — verifique se
dotnet restoreresolve IronOCR sem etapas de instalação - Validar se os níveis de confiança estão disponíveis por palavra e se correspondem às expectativas de qualidade de dados de qualquer fluxo de trabalho de validação subsequente.
- Verifique se o aplicativo inicia limpo sem o arquivo
.licpresente em qualquer caminho
Principais benefícios da migração para o IronOCR
A complexidade da implantação cai de semanas para minutos. Um projeto que anteriormente exigia acesso ao instalador do SDK, implantação de arquivo .lic, configuração de firewall para o servidor de licença, e coordenação da equipe de infraestrutura agora é implantado via dotnet restore. Um novo desenvolvedor clona o repositório e executa o projeto. Um novo servidor de produção é provisionado pelo pipeline de CI/CD. As imagens de contêiner são criadas sem etapas de instalação.
O risco de licença desaparece completamente. Não há assentos flutuantes para esgotar, sem tempos de espera de checkout, sem impressões digitais de hardware para reativar, e sem arquivos .lic para proteger nos pipelines de implantação. Uma falha de aplicativo às 3 da manhã não libera nem bloqueia nada. O técnico de plantão reinicia o processo; O OCR será retomado imediatamente. A página do produto IronOCR abrange todos os níveis de licenciamento.
A implantação multiplataforma torna-se um alvo padrão do NuGet . As máquinas de desenvolvimento macOS funcionam sem exceção de plataforma. Servidores Linux de produção não exigem uma versão do SDK de janeiro de 2026. Contêineres Docker são construídos a partir de uma imagem base padrão mcr.microsoft.com/dotnet/aspnet com uma dependência de sistema. A mesma referência de pacote IronOcr em .csproj produz uma build funcional no Windows, Linux e macOS. Consulte o guia de implantação do Azure e o guia de implantação da AWS para obter informações sobre a configuração específica da nuvem.
A capacidade de processamento paralelo escala com o hardware. A arquitetura de mecanismo compartilhado do OmniPage requer bloqueio, o que serializa o reconhecimento simultâneo. As instâncias IronTesseract sem estado da IronOCR escalam linearmente com os núcleos de CPU disponíveis. Dobrar o número de núcleos de um servidor de processamento em lote dobra a capacidade de processamento sem alterações de configuração ou licenciamento adicional.
O acesso a dados estruturados é imediato. OcrResult.Pages, Paragraphs, Lines, Words, e Characters expõem a estrutura completa do documento como um gráfico de objeto .NET navegável. A confiança e as coordenadas por palavra são propriedades de cada objeto de palavra. Não há necessidade de um pipeline de exportação secundário, configuração de formato ou arquivo de saída para acessar os dados posicionais.
O custo é fixo e previsível. A combinação da licença do SDK, da manutenção anual e das taxas de tempo de execução por página do OmniPage cria um custo que aumenta com o uso e se repete anualmente.IronOCR em $999–US$ 2.999 perpétuo é uma compra única. Um fluxo de trabalho de 500.000 páginas por ano, que gerava cobranças por página, recupera o custo da licença do IronOCR em poucas semanas. A central de IronOCR e os tutoriais do IronOCR estão disponíveis sem contrato de suporte.
Perguntas frequentes
Por que devo migrar do Kofax OmniPage para o IronOCR?
Entre os principais motivos, incluem-se a eliminação da complexidade da interoperabilidade COM, a substituição do gerenciamento de licenças baseado em arquivos, a eliminação da cobrança por página, a viabilização da implantação em Docker/contêineres e a adoção de um fluxo de trabalho nativo do NuGet que se integra às ferramentas padrão do .NET.
Quais são as principais alterações de código ao migrar do Kofax OmniPage para o IronOCR?
Substitua as sequências de inicialização do Kofax OmniPage pela instanciação do IronTesseract, remova o gerenciamento do ciclo de vida COM (padrões explícitos de Criação/Carregamento/Fechamento) e atualize os nomes das propriedades de resultado. O resultado é uma redução significativa no número de linhas de código repetitivo.
Como faço para instalar o IronOCR para iniciar a migração?
Execute 'Install-Package IronOcr' no Console do Gerenciador de Pacotes ou 'dotnet add package IronOcr' na CLI. Os pacotes de idiomas são pacotes separados: 'dotnet add package IronOcr.Languages.French' para francês, por exemplo.
O IronOCR atinge a mesma precisão de OCR que o Kofax OmniPage para documentos comerciais padrão?
O IronOCR alcança alta precisão para conteúdo comercial padrão, incluindo faturas, contratos, recibos e formulários digitados. Filtros de pré-processamento de imagem (correção de distorção, remoção de ruído, aprimoramento de contraste) melhoram ainda mais o reconhecimento em entradas de baixa qualidade.
Como o IronOCR lida com os dados de idioma que o Kofax OmniPage instala separadamente?
Os dados de idioma no IronOCR são distribuídos como pacotes NuGet. O comando 'dotnet add package IronOcr.Languages.German' instala o suporte ao alemão. Não é necessário inserir arquivos manualmente nem configurar caminhos de diretório.
A migração do Kofax OmniPage para o IronOCR exige alterações na infraestrutura de implantação?
O IronOCR requer menos alterações de infraestrutura do que o Kofax OmniPage. Não há caminhos binários do SDK, necessidade de instalar arquivos de licença ou configurar servidores de licença. O pacote NuGet contém o mecanismo OCR completo e a chave de licença é uma string definida no código do aplicativo.
Como configuro o licenciamento do IronOCR após a migração?
Atribua `IronOcr.License.LicenseKey = "YOUR-KEY"` no código de inicialização do aplicativo. No Docker ou Kubernetes, armazene a chave como uma variável de ambiente e leia-a na inicialização. Use `License.IsValidLicense` para validar a licença antes de aceitar o tráfego.
O IronOCR consegue processar PDFs da mesma forma que o Kofax OmniPage?
Sim. O IronOCR lê PDFs nativos e digitalizados. Instancie o IronTesseract, chame ocr.Read(input) onde input é um caminho para um PDF ou OcrPdfInput, e itere pelas páginas do OcrResult. Não é necessário um pipeline de renderização de PDF separado.
Como o IronOCR lida com multithreading em processamento de alto volume?
O IronTesseract pode ser instanciado com segurança por thread. Crie uma instância por thread em um Parallel.ForEach ou pool de Tasks, execute o OCR simultaneamente e descarte cada instância ao terminar. Não é necessário nenhum estado global ou bloqueio.
Quais formatos de saída o IronOCR suporta após a extração de texto?
O IronOCR retorna resultados estruturados, incluindo texto, coordenadas de palavras, níveis de confiança e estrutura da página. As opções de exportação incluem texto simples, PDF pesquisável e objetos de resultados estruturados para processamento posterior.
O preço do IronOCR é mais previsível do que o do Kofax OmniPage para cargas de trabalho escaláveis?
O IronOCR utiliza licenciamento perpétuo com preço fixo, sem cobranças por página ou volume. Independentemente de você processar 10.000 ou 10 milhões de páginas, o custo da licença permanece constante. As opções de licenciamento por volume e para equipes estão disponíveis na página de preços do IronOCR.
O que acontece com meus testes existentes após a migração do Kofax OmniPage para o IronOCR?
Os testes que verificam o conteúdo de texto extraído devem continuar a ser aprovados após a migração. Os testes que validam padrões de chamadas de API ou o ciclo de vida de objetos COM precisarão ser atualizados para refletir o modelo de inicialização e resultados mais simples do IronOCR.

