Como extrair texto árabe de imagens usando ferramentas de OCR
Este guia orienta os desenvolvedores .NET em uma migração completa do OCR GdPicture .NET para o IronOCR . Este documento aborda a troca de pacotes, as alterações de namespace e os padrões práticos de código antes e depois para cada fluxo de trabalho OCR principal, com foco especial na eliminação do ciclo de vida do ID de imagem inteiro que define o modelo de gerenciamento de recursos do GdPicture. Não é necessário ler o artigo comparativo previamente.
Por que migrar do GdPicture .NET?
GdPicture .NET é uma plataforma de digitalização de documentos criada para equipes que precisam de integração com scanners, suporte a DICOM, edição de PDF, anotações e OCR, tudo em um único fornecedor. Quando o OCR é o único requisito, o preço da plataforma e a arquitetura da API criam atritos que se acumulam ao longo do tempo.
Custo do Plugin para um Fluxo de Trabalho OCR Básico. Extrair texto de PDFs digitalizados e gerar resultados pesquisáveis requer três componentes de licença separados: o SDK principal por aproximadamente US$ 4.000, o Plugin OCR por aproximadamente US$ 2.000 e o Plugin PDF por aproximadamente US$ 2.000. Isso resulta em um custo inicial de US$ 8.000, Plus 20% de manutenção anual. Equipes que precisam apenas de OCR absorvem toda a estrutura de preços de uma plataforma de digitalização de documentos.IronOCR cobre o mesmo fluxo de trabalho — OCR de imagem, OCR de PDF, pré-processamento, saída de PDF pesquisável — de um único pacote por $999 até $2,999 perpétuos, sem decisões de licenciamento por recurso. Consulte a página de licenciamento do IronOCR para obter uma descrição completa dos níveis de licenciamento.
O Ciclo de Vida do ID de Imagem Inteiro. Toda imagem carregada através do GdPicture retorna um int. Você passa esse inteiro para operações de OCR, então chama ReleaseGdPictureImage com ele quando terminar. Esse padrão precede IDisposable. Funciona quando seguido corretamente; Vazamento de memória ocorre quando ocorre uma falha. Em serviços de produção que processam centenas de documentos diariamente, uma chamada de liberação perdida em um ramo com erro gera um aumento de memória que é realmente difícil de diagnosticar. O OcrInput do IronOCR implementa IDisposable — uma declaração using elimina todo o encargo de limpeza.
Namespace Específico de Versão. O GdPicture incorpora o número da versão principal em seu namespace: using GdPicture14. Atualizar para a próxima versão principal requer a atualização dessa diretiva using em cada arquivo fonte que referencia as classes GdPicture. Uma aplicação complexa com OCR distribuída por dezenas de serviços transforma essa tarefa de localizar e substituir em algo que leva várias horas e não traz nenhuma melhoria funcional. O namespace do IronOCR foi IronOcr em todas as versões principais.
Requisito de Pasta de Recursos Externos. O OCR do GdPicture requer uma propriedade ResourceFolder apontando para um diretório de arquivos de linguagem .traineddata em tempo de execução. Esse caminho funciona em uma máquina de desenvolvimento, mas quebra em servidores Linux, contêineres Docker, e implantações Azure App Service onde a estrutura de diretórios não existe. O IronOCR incorpora o suporte ao idioma inglês dentro do pacote NuGet; Idiomas adicionais são instalados como pacotes NuGet que acompanham o resultado da compilação.
Inicialização de Três Componentes. Um fluxo de trabalho de OCR de PDF no GdPicture requer instanciar GdPictureImaging, GdPictureOCR e GdPicturePDF — três componentes separados com requisitos individuais de descarte — além da inscrição do gerenciador de licenças e a atribuição da pasta de recursos. O IronOCR requer uma linha na inicialização e uma classe no momento da chamada.
Sem segurança de threads nativa. As instâncias de OCR do GdPicture não são thread-safe. O processamento paralelo de documentos exige um gerenciamento cuidadoso das instâncias e sincronização para evitar estados corrompidos. O IronOCR é projetado para uso simultâneo: crie um IronTesseract por thread ou compartilhe uma única instância sob carga — qualquer padrão funciona sem código de sincronização adicional.
O problema fundamental
O GdPicture aloca memória para cada imagem como um identificador inteiro gerenciado em tempo de execução. Cada chamada RenderPageToGdPictureImage em um loop de processamento TIFF cria uma nova alocação que deve ser liberada manualmente:
// GdPicture: every frame = new integer ID = manual release required
using var pdf = new GdPicturePDF();
pdf.LoadFromFile("multi-page.tiff", false);
var frameIds = new List<int>();
try
{
for (int i = 1; i <= pdf.GetPageCount(); i++)
{
pdf.SelectPage(i);
int frameId = pdf.RenderPageToGdPictureImage(200, false); // new allocation
if (frameId != 0) frameIds.Add(frameId);
// ... OCR call here ...
}
}
finally
{
foreach (var id in frameIds) _imaging.ReleaseGdPictureImage(id); // manual per-frame release
}
// GdPicture: every frame = new integer ID = manual release required
using var pdf = new GdPicturePDF();
pdf.LoadFromFile("multi-page.tiff", false);
var frameIds = new List<int>();
try
{
for (int i = 1; i <= pdf.GetPageCount(); i++)
{
pdf.SelectPage(i);
int frameId = pdf.RenderPageToGdPictureImage(200, false); // new allocation
if (frameId != 0) frameIds.Add(frameId);
// ... OCR call here ...
}
}
finally
{
foreach (var id in frameIds) _imaging.ReleaseGdPictureImage(id); // manual per-frame release
}
Imports System.Collections.Generic
' GdPicture: every frame = new integer ID = manual release required
Using pdf As New GdPicturePDF()
pdf.LoadFromFile("multi-page.tiff", False)
Dim frameIds As New List(Of Integer)()
Try
For i As Integer = 1 To pdf.GetPageCount()
pdf.SelectPage(i)
Dim frameId As Integer = pdf.RenderPageToGdPictureImage(200, False) ' new allocation
If frameId <> 0 Then frameIds.Add(frameId)
' ... OCR call here ...
Next
Finally
For Each id In frameIds
_imaging.ReleaseGdPictureImage(id) ' manual per-frame release
Next
End Try
End Using
O IronOCR elimina completamente o ciclo de vida. OcrInput lida com enumeração de quadros e limpeza:
// IronOCR: the using statement handles everything
using var input = new OcrInput();
input.LoadImageFrames("multi-page.tiff");
var result = new IronTesseract().Read(input);
// IronOCR: the using statement handles everything
using var input = new OcrInput();
input.LoadImageFrames("multi-page.tiff");
var result = new IronTesseract().Read(input);
Imports IronOcr
Dim input As New OcrInput()
Using input
input.LoadImageFrames("multi-page.tiff")
Dim result = New IronTesseract().Read(input)
End Using
IronOCR vs GdPicture .NET: Comparação de Recursos
A tabela abaixo mostra a cobertura entre as duas bibliotecas para fluxos de trabalho focados em OCR.
| Recurso | GdPicture.NET | IronOCR |
|---|---|---|
| Instalação | Vários pacotes NuGet | dotnet add package IronOcr |
| Ativação de licença | LicenseManager.RegisterKEY() |
IronOcr.License.LicenseKey = "..." |
| Namespace em atualização principal | Requer localizar-e-substituir (GdPicture14 → próximo) |
Inalterado (IronOcr) |
| Inicialização de componentes | GdPictureImaging + GdPictureOCR + GdPicturePDF |
new IronTesseract() |
| Modelo de memória de recursos | Rastreamento e liberação manual de IDs inteiros | declaração IDisposable / using |
| risco de vazamento de memória | Alto (faltando ReleaseGdPictureImage) |
Nenhum (enforçado pelo compilador via using) |
| Pasta de recursos externos | Necessário (_ocr.ResourceFolder = path) |
Não é necessário — incluído no pacote. |
| OCR de imagem | Sim | Sim |
| OCR de PDF | Sim (requer o plugin PDF) | Integrado, sem necessidade de licença adicional. |
| TIFF de várias páginas | Loop de quadros manual + limpeza de ID por quadro | input.LoadImageFrames() |
| Entrada de fluxo | Via sobrecarga de fluxo GdPictureImaging |
input.LoadImage(stream) |
| Saída em PDF pesquisável | pdf.OcrPage() + pdf.SaveToFile() |
result.SaveAsSearchablePdf() |
| Pré-processamento de correção de distorção | É necessário o plugin de digitalização de documentos. | input.Deskew() — embutido |
| Remoção de ruído | É necessário o plugin de digitalização de documentos. | input.DeNoise() — embutido |
| Línguas | Arquivos de dados treinados baseados em Tesseract na pasta | Mais de 125 pacotes NuGet |
| OCR multilíngue | Sim | OcrLanguage.French + OcrLanguage.German |
| Segurança da rosca | Gerenciamento manual de instâncias | Projetado para ser seguro contra roscas. |
| OCR assíncrono | Não embutido | ReadAsync() |
| Leitura de código de barras | Plugin de código de barras separado | ocr.Configuration.ReadBarCodes = true |
| Saída estruturada | Acesso baseado em índice a blocos/linhas/palavras | Digitado .Pages, .Words, .Characters |
| Pontuação de confiança | GetOCRResultConfidence(resultId) |
result.Confidence |
| Multiplataforma | Windows, Linux, macOS | Windows, Linux, macOS, Docker, AWS, Azure |
| Custo de entrada (OCR a partir de PDFs) | Aproximadamente US$ 8.000 (Núcleo + OCR + plugins de PDF) | $999–$2,999 |
| Modelo de preços | Licença perpétua baseada em plugins + manutenção de 20% ao ano. | Plano perpétuo, com atualizações anuais opcionais. |
| Apoio comercial | Sim | Sim |
Guia rápido: Migração do GdPicture .NET para o IronOCR
Passo 1: Substitua o pacote NuGet
Remova os pacotes GdPicture:
dotnet remove package GdPicture.NET
dotnet remove package GdPicture.NET.OCR
dotnet remove package GdPicture.NET.PDF
dotnet remove package GdPicture.NET
dotnet remove package GdPicture.NET.OCR
dotnet remove package GdPicture.NET.PDF
Instale o IronOCR a partir da página de pacotes NuGet :
dotnet add package IronOcr
Para idiomas além do inglês, instale o pacote de idiomas correspondente:
dotnet add package IronOcr.Languages.French, IronOcr.Languages.German
Etapa 2: Atualizar Namespaces
Substitua o namespace GdPicture pelo namespace IronOCR:
// Before (GdPicture)
using GdPicture14;
// After (IronOCR)
using IronOcr;
// Before (GdPicture)
using GdPicture14;
// After (IronOCR)
using IronOcr;
Imports IronOcr
Etapa 3: Inicializar a licença
Coloque esta linha em Program.cs ou Startup.cs, antes de qualquer chamada de OCR:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Remova completamente o bloco de registro de licença do GdPicture:
// Remove this
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
// Remove this
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
Uma chave de avaliação gratuita está disponível na página do produto IronOCR para que você possa avaliá-lo antes de comprar.
Exemplos de migração de código
Processamento de quadros TIFF de várias páginas
O processamento de arquivos TIFF com várias páginas no GdPicture exige a seleção de cada quadro por meio da API PDF/imagens, a renderização para um novo ID de imagem, a execução do OCR e a liberação do ID. Um único quadro que não é liberado no bloco finally vaza 10–50 MB dependendo do DPI.
Abordagem GdPicture .NET :
using GdPicture14;
public class GdPictureTiffProcessor
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public string ExtractTextFromTiff(string tiffPath)
{
var text = new StringBuilder();
// Load TIFF through the imaging component
int tiffId = _imaging.CreateGdPictureImageFromFile(tiffPath);
if (tiffId == 0)
throw new Exception($"TIFF load failed: {_imaging.GetStat()}");
// Outer try: release the original TIFF handle
try
{
int frameCount = _imaging.GetPageCount(tiffId);
for (int i = 1; i <= frameCount; i++)
{
// Switch to frame — modifies the existing ID in place
_imaging.SelectPage(tiffId, i);
// Clone frame to a new image ID for OCR
int frameId = _imaging.CloneImage(tiffId);
if (frameId == 0) continue;
// Inner try: release each cloned frame ID
try
{
_ocr.SetImage(frameId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (!string.IsNullOrEmpty(resultId))
{
text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}");
}
}
finally
{
// Release cloned frame — critical for each iteration
_imaging.ReleaseGdPictureImage(frameId);
}
}
}
finally
{
// Release original TIFF handle
_imaging.ReleaseGdPictureImage(tiffId);
}
return text.ToString();
}
}
using GdPicture14;
public class GdPictureTiffProcessor
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public string ExtractTextFromTiff(string tiffPath)
{
var text = new StringBuilder();
// Load TIFF through the imaging component
int tiffId = _imaging.CreateGdPictureImageFromFile(tiffPath);
if (tiffId == 0)
throw new Exception($"TIFF load failed: {_imaging.GetStat()}");
// Outer try: release the original TIFF handle
try
{
int frameCount = _imaging.GetPageCount(tiffId);
for (int i = 1; i <= frameCount; i++)
{
// Switch to frame — modifies the existing ID in place
_imaging.SelectPage(tiffId, i);
// Clone frame to a new image ID for OCR
int frameId = _imaging.CloneImage(tiffId);
if (frameId == 0) continue;
// Inner try: release each cloned frame ID
try
{
_ocr.SetImage(frameId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (!string.IsNullOrEmpty(resultId))
{
text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}");
}
}
finally
{
// Release cloned frame — critical for each iteration
_imaging.ReleaseGdPictureImage(frameId);
}
}
}
finally
{
// Release original TIFF handle
_imaging.ReleaseGdPictureImage(tiffId);
}
return text.ToString();
}
}
Imports GdPicture14
Imports System.Text
Public Class GdPictureTiffProcessor
Private ReadOnly _imaging As GdPictureImaging
Private ReadOnly _ocr As GdPictureOCR
Public Function ExtractTextFromTiff(tiffPath As String) As String
Dim text As New StringBuilder()
' Load TIFF through the imaging component
Dim tiffId As Integer = _imaging.CreateGdPictureImageFromFile(tiffPath)
If tiffId = 0 Then
Throw New Exception($"TIFF load failed: {_imaging.GetStat()}")
End If
' Outer try: release the original TIFF handle
Try
Dim frameCount As Integer = _imaging.GetPageCount(tiffId)
For i As Integer = 1 To frameCount
' Switch to frame — modifies the existing ID in place
_imaging.SelectPage(tiffId, i)
' Clone frame to a new image ID for OCR
Dim frameId As Integer = _imaging.CloneImage(tiffId)
If frameId = 0 Then Continue For
' Inner try: release each cloned frame ID
Try
_ocr.SetImage(frameId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If Not String.IsNullOrEmpty(resultId) Then
text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}")
End If
Finally
' Release cloned frame — critical for each iteration
_imaging.ReleaseGdPictureImage(frameId)
End Try
Next
Finally
' Release original TIFF handle
_imaging.ReleaseGdPictureImage(tiffId)
End Try
Return text.ToString()
End Function
End Class
Abordagem IronOCR:
using IronOcr;
public class IronOcrTiffProcessor
{
public string ExtractTextFromTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames loaded, all cleanup automatic
var result = new IronTesseract().Read(input);
// Per-frame text is available on result.Pages
foreach (var page in result.Pages)
Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}");
return result.Text;
}
}
using IronOcr;
public class IronOcrTiffProcessor
{
public string ExtractTextFromTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames loaded, all cleanup automatic
var result = new IronTesseract().Read(input);
// Per-frame text is available on result.Pages
foreach (var page in result.Pages)
Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}");
return result.Text;
}
}
Imports IronOcr
Public Class IronOcrTiffProcessor
Public Function ExtractTextFromTiff(tiffPath As String) As String
Using input As New OcrInput()
input.LoadImageFrames(tiffPath) ' all frames loaded, all cleanup automatic
Dim result = New IronTesseract().Read(input)
' Per-frame text is available on result.Pages
For Each page In result.Pages
Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}")
Next
Return result.Text
End Using
End Function
End Class
LoadImageFrames carrega cada quadro de um TIFF de várias páginas no pipeline OcrInput. O bloco using lida com toda a memória associada a cada quadro no final do escopo. Nenhum List<int> para manter, nenhum bloco try/finally necessário. O guia de entrada TIFF e GIF aborda em detalhes a seleção de quadros e o processamento de múltiplos formatos.
Inicialização de plugins para substituição de entrada baseada em fluxo
Os aplicativos de servidor frequentemente recebem documentos como fluxos em vez de caminhos de arquivo — de uploads HTTP, filas de mensagens ou blobs de banco de dados. O GdPicture requer o carregamento do fluxo através de GdPictureImaging, que por si só requer a sequência de inicialização do componente e a configuração da pasta de recursos que precedem cada operação.
Abordagem GdPicture .NET :
using GdPicture14;
public class GdPictureStreamOcr : IDisposable
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public GdPictureStreamOcr()
{
// Plugin initialization required before stream loading is possible
var lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
_imaging = new GdPictureImaging();
_ocr = new GdPictureOCR();
_ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR"; // path must exist at runtime
}
public string ExtractTextFromStream(Stream documentStream)
{
// Load stream into imaging component to get an image ID
int imageId = _imaging.CreateGdPictureImageFromStream(documentStream);
if (imageId == 0)
throw new Exception($"Stream load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
}
public void Dispose()
{
_ocr?.Dispose();
_imaging?.Dispose();
}
}
using GdPicture14;
public class GdPictureStreamOcr : IDisposable
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public GdPictureStreamOcr()
{
// Plugin initialization required before stream loading is possible
var lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
_imaging = new GdPictureImaging();
_ocr = new GdPictureOCR();
_ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR"; // path must exist at runtime
}
public string ExtractTextFromStream(Stream documentStream)
{
// Load stream into imaging component to get an image ID
int imageId = _imaging.CreateGdPictureImageFromStream(documentStream);
if (imageId == 0)
throw new Exception($"Stream load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
}
public void Dispose()
{
_ocr?.Dispose();
_imaging?.Dispose();
}
}
IRON VB CONVERTER ERROR developers@ironsoftware.com
Abordagem IronOCR:
using IronOcr;
public class IronOcrStreamOcr
{
public string ExtractTextFromStream(Stream documentStream)
{
using var input = new OcrInput();
input.LoadImage(documentStream); // stream accepted directly — no imaging component
return new IronTesseract().Read(input).Text;
}
}
using IronOcr;
public class IronOcrStreamOcr
{
public string ExtractTextFromStream(Stream documentStream)
{
using var input = new OcrInput();
input.LoadImage(documentStream); // stream accepted directly — no imaging component
return new IronTesseract().Read(input).Text;
}
}
Imports IronOcr
Public Class IronOcrStreamOcr
Public Function ExtractTextFromStream(documentStream As Stream) As String
Using input As New OcrInput()
input.LoadImage(documentStream) ' stream accepted directly — no imaging component
Return New IronTesseract().Read(input).Text
End Using
End Function
End Class
A abordagem GdPicture requer a construção de três objetos e a configuração de um caminho no sistema de arquivos antes que o primeiro fluxo possa ser consumido. O IronOCR aceita o fluxo diretamente em OcrInput sem configuração prévia. O guia de entrada de fluxo cobre array de bytes, MemoryStream, e padrões FileStream para arquiteturas de pipeline onde gravações de arquivos temporários são indesejáveis.
OCR assíncrono substituindo a sondagem de código de status
O OCR do GdPicture é síncrono. Aplicativos que processam documentos em endpoints ASP.NET Core ou serviços em segundo plano devem envolver RunOCR em Task.Run para evitar bloquear threads de solicitação — e então gerenciar o ciclo de vida do ID da imagem através da fronteira do thread. O IronOCR oferece suporte a async de primeira classe através de ReadAsync.
Abordagem GdPicture .NET :
using GdPicture14;
using System.Threading.Tasks;
public class GdPictureAsyncWrapper
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
public async Task<string> ExtractTextAsync(string imagePath)
{
// Must acquire lock: GdPictureOCR is not thread-safe
await _lock.WaitAsync();
try
{
return await Task.Run(() =>
{
int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
throw new Exception($"Load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
});
}
finally
{
_lock.Release();
}
}
}
using GdPicture14;
using System.Threading.Tasks;
public class GdPictureAsyncWrapper
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
public async Task<string> ExtractTextAsync(string imagePath)
{
// Must acquire lock: GdPictureOCR is not thread-safe
await _lock.WaitAsync();
try
{
return await Task.Run(() =>
{
int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
throw new Exception($"Load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
});
}
finally
{
_lock.Release();
}
}
}
Imports GdPicture14
Imports System.Threading.Tasks
Public Class GdPictureAsyncWrapper
Private ReadOnly _imaging As GdPictureImaging
Private ReadOnly _ocr As GdPictureOCR
Private ReadOnly _lock As New SemaphoreSlim(1, 1)
Public Async Function ExtractTextAsync(imagePath As String) As Task(Of String)
' Must acquire lock: GdPictureOCR is not thread-safe
Await _lock.WaitAsync()
Try
Return Await Task.Run(Function()
Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(imagePath)
If imageId = 0 Then
Throw New Exception($"Load failed: {_imaging.GetStat()}")
End If
Try
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If String.IsNullOrEmpty(resultId) Then
Throw New Exception($"OCR failed: {_ocr.GetStat()}")
End If
Return _ocr.GetOCRResultText(resultId)
Finally
_imaging.ReleaseGdPictureImage(imageId)
End Try
End Function)
Finally
_lock.Release()
End Try
End Function
End Class
Abordagem IronOCR:
using IronOcr;
public class IronOcrAsyncService
{
private readonly IronTesseract _ocr = new IronTesseract();
public async Task<string> ExtractTextAsync(string imagePath)
{
// ReadAsync is natively async — no Task.Run wrapper, no lock required
var result = await _ocr.ReadAsync(imagePath);
return result.Text;
}
public async Task<string> ExtractFromStreamAsync(Stream stream)
{
using var input = new OcrInput();
input.LoadImage(stream);
var result = await _ocr.ReadAsync(input);
return result.Text;
}
}
using IronOcr;
public class IronOcrAsyncService
{
private readonly IronTesseract _ocr = new IronTesseract();
public async Task<string> ExtractTextAsync(string imagePath)
{
// ReadAsync is natively async — no Task.Run wrapper, no lock required
var result = await _ocr.ReadAsync(imagePath);
return result.Text;
}
public async Task<string> ExtractFromStreamAsync(Stream stream)
{
using var input = new OcrInput();
input.LoadImage(stream);
var result = await _ocr.ReadAsync(input);
return result.Text;
}
}
Imports IronOcr
Imports System.IO
Imports System.Threading.Tasks
Public Class IronOcrAsyncService
Private ReadOnly _ocr As New IronTesseract()
Public Async Function ExtractTextAsync(imagePath As String) As Task(Of String)
' ReadAsync is natively async — no Task.Run wrapper, no lock required
Dim result = Await _ocr.ReadAsync(imagePath)
Return result.Text
End Function
Public Async Function ExtractFromStreamAsync(stream As Stream) As Task(Of String)
Using input As New OcrInput()
input.LoadImage(stream)
Dim result = Await _ocr.ReadAsync(input)
Return result.Text
End Using
End Function
End Class
A abordagem do GdPicture requer um SemaphoreSlim para serializar o acesso à instância compartilhada GdPictureOCR, além de um Task.Run para mover o trabalho de bloqueio síncrono do thread de chamada, além do ciclo de vida completo do ID da imagem dentro desse lambda. O ReadAsync do IronOCR é genuinamente não-bloqueante e seguro para threads. O guia de OCR assíncrono aborda a integração com middleware do ASP.NET Core e serviços hospedados em segundo plano.
Processamento paralelo em lotes com segurança de rosca
Processar uma pasta de documentos digitalizados o mais rápido possível requer execução em paralelo. O GdPicture requer uma instância GdPictureOCR por thread — compartilhar uma única instância causa falhas não determinísticas. Cada instância por thread também requer seu próprio componente GdPictureImaging e configuração da pasta de recursos, tornando as abordagens de pool de threads impraticáveis sem um padrão de fábrica.
Abordagem GdPicture .NET :
using GdPicture14;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class GdPictureParallelBatch
{
private readonly string _resourceFolder = @"C:\GdPicture\Resources\OCR";
public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// Each thread must have its own component instances
Parallel.ForEach(imagePaths, imagePath =>
{
// Create per-thread instances — shared instances cause failures
using var threadImaging = new GdPictureImaging();
using var threadOcr = new GdPictureOCR();
threadOcr.ResourceFolder = _resourceFolder;
// Re-register license per thread (may be required depending on SDK version)
var lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
int imageId = threadImaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
{
results[imagePath] = $"ERROR: {threadImaging.GetStat()}";
return;
}
try
{
threadOcr.SetImage(imageId);
threadOcr.Language = "eng";
string resultId = threadOcr.RunOCR();
results[imagePath] = string.IsNullOrEmpty(resultId)
? $"ERROR: {threadOcr.GetStat()}"
: threadOcr.GetOCRResultText(resultId);
}
finally
{
threadImaging.ReleaseGdPictureImage(imageId);
}
});
return results;
}
}
using GdPicture14;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class GdPictureParallelBatch
{
private readonly string _resourceFolder = @"C:\GdPicture\Resources\OCR";
public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// Each thread must have its own component instances
Parallel.ForEach(imagePaths, imagePath =>
{
// Create per-thread instances — shared instances cause failures
using var threadImaging = new GdPictureImaging();
using var threadOcr = new GdPictureOCR();
threadOcr.ResourceFolder = _resourceFolder;
// Re-register license per thread (may be required depending on SDK version)
var lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
int imageId = threadImaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
{
results[imagePath] = $"ERROR: {threadImaging.GetStat()}";
return;
}
try
{
threadOcr.SetImage(imageId);
threadOcr.Language = "eng";
string resultId = threadOcr.RunOCR();
results[imagePath] = string.IsNullOrEmpty(resultId)
? $"ERROR: {threadOcr.GetStat()}"
: threadOcr.GetOCRResultText(resultId);
}
finally
{
threadImaging.ReleaseGdPictureImage(imageId);
}
});
return results;
}
}
Imports GdPicture14
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Public Class GdPictureParallelBatch
Private ReadOnly _resourceFolder As String = "C:\GdPicture\Resources\OCR"
Public Function ProcessBatch(imagePaths As String()) As ConcurrentDictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
' Each thread must have its own component instances
Parallel.ForEach(imagePaths, Sub(imagePath)
' Create per-thread instances — shared instances cause failures
Using threadImaging As New GdPictureImaging()
Using threadOcr As New GdPictureOCR()
threadOcr.ResourceFolder = _resourceFolder
' Re-register license per thread (may be required depending on SDK version)
Dim lm As New LicenseManager()
lm.RegisterKEY("GDPICTURE-LICENSE-KEY")
Dim imageId As Integer = threadImaging.CreateGdPictureImageFromFile(imagePath)
If imageId = 0 Then
results(imagePath) = $"ERROR: {threadImaging.GetStat()}"
Return
End If
Try
threadOcr.SetImage(imageId)
threadOcr.Language = "eng"
Dim resultId As String = threadOcr.RunOCR()
results(imagePath) = If(String.IsNullOrEmpty(resultId), $"ERROR: {threadOcr.GetStat()}", threadOcr.GetOCRResultText(resultId))
Finally
threadImaging.ReleaseGdPictureImage(imageId)
End Try
End Using
End Using
End Sub)
Return results
End Function
End Class
Abordagem IronOCR:
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class IronOcrParallelBatch
{
public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// IronTesseract is thread-safe — one instance handles all threads
var ocr = new IronTesseract();
Parallel.ForEach(imagePaths, imagePath =>
{
try
{
results[imagePath] = ocr.Read(imagePath).Text;
}
catch (Exception ex)
{
results[imagePath] = $"ERROR: {ex.Message}";
}
});
return results;
}
}
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class IronOcrParallelBatch
{
public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// IronTesseract is thread-safe — one instance handles all threads
var ocr = new IronTesseract();
Parallel.ForEach(imagePaths, imagePath =>
{
try
{
results[imagePath] = ocr.Read(imagePath).Text;
}
catch (Exception ex)
{
results[imagePath] = $"ERROR: {ex.Message}";
}
});
return results;
}
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Public Class IronOcrParallelBatch
Public Function ProcessBatch(imagePaths As String()) As ConcurrentDictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
' IronTesseract is thread-safe — one instance handles all threads
Dim ocr As New IronTesseract()
Parallel.ForEach(imagePaths, Sub(imagePath)
Try
results(imagePath) = ocr.Read(imagePath).Text
Catch ex As Exception
results(imagePath) = $"ERROR: {ex.Message}"
End Try
End Sub)
Return results
End Function
End Class
A implementação paralela do GdPicture cria três objetos por thread e requer o registro de uma licença por thread. O IronOCR lida com leituras simultâneas de uma única instância IronTesseract sem sobrecarga de sincronização. O exemplo de multithreading demonstra benchmarks de rendimento e padrões Parallel.ForEach para cargas de trabalho de processamento de documentos de alto volume.
Entrada de matriz de bytes e extração de parágrafos estruturados
Aplicações que recuperam documentos de bancos de dados ou armazenamento de objetos geralmente trabalham com matrizes de bytes em vez de caminhos de arquivo. O GdPicture requer a conversão do array de bytes para um fluxo e o carregamento através de GdPictureImaging. Extrair dados estruturados em nível de parágrafo do resultado requer navegar pela hierarquia do índice de bloco/linha.
Abordagem GdPicture .NET :
using GdPicture14;
public class GdPictureByteArrayOcr
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
{
var paragraphs = new List<string>();
// Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
using var ms = new MemoryStream(imageBytes);
int imageId = _imaging.CreateGdPictureImageFromStream(ms);
if (imageId == 0)
throw new Exception($"Byte array load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
// Paragraph-level data requires iterating block structure
int blockCount = _ocr.GetOCRResultBlockCount(resultId);
for (int b = 0; b < blockCount; b++)
{
var blockText = new StringBuilder();
int lineCount = _ocr.GetOCRResultBlockLineCount(resultId, b);
for (int l = 0; l < lineCount; l++)
{
int wordCount = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l);
for (int w = 0; w < wordCount; w++)
{
blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w));
blockText.Append(" ");
}
}
string text = blockText.ToString().Trim();
if (!string.IsNullOrEmpty(text))
paragraphs.Add(text);
}
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
return paragraphs;
}
}
using GdPicture14;
public class GdPictureByteArrayOcr
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
{
var paragraphs = new List<string>();
// Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
using var ms = new MemoryStream(imageBytes);
int imageId = _imaging.CreateGdPictureImageFromStream(ms);
if (imageId == 0)
throw new Exception($"Byte array load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
// Paragraph-level data requires iterating block structure
int blockCount = _ocr.GetOCRResultBlockCount(resultId);
for (int b = 0; b < blockCount; b++)
{
var blockText = new StringBuilder();
int lineCount = _ocr.GetOCRResultBlockLineCount(resultId, b);
for (int l = 0; l < lineCount; l++)
{
int wordCount = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l);
for (int w = 0; w < wordCount; w++)
{
blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w));
blockText.Append(" ");
}
}
string text = blockText.ToString().Trim();
if (!string.IsNullOrEmpty(text))
paragraphs.Add(text);
}
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
return paragraphs;
}
}
Imports GdPicture14
Imports System.IO
Imports System.Text
Public Class GdPictureByteArrayOcr
Private ReadOnly _imaging As GdPictureImaging
Private ReadOnly _ocr As GdPictureOCR
Public Function ExtractParagraphsFromBytes(imageBytes As Byte()) As List(Of String)
Dim paragraphs As New List(Of String)()
' Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
Using ms As New MemoryStream(imageBytes)
Dim imageId As Integer = _imaging.CreateGdPictureImageFromStream(ms)
If imageId = 0 Then
Throw New Exception($"Byte array load failed: {_imaging.GetStat()}")
End If
Try
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If String.IsNullOrEmpty(resultId) Then
Throw New Exception($"OCR failed: {_ocr.GetStat()}")
End If
' Paragraph-level data requires iterating block structure
Dim blockCount As Integer = _ocr.GetOCRResultBlockCount(resultId)
For b As Integer = 0 To blockCount - 1
Dim blockText As New StringBuilder()
Dim lineCount As Integer = _ocr.GetOCRResultBlockLineCount(resultId, b)
For l As Integer = 0 To lineCount - 1
Dim wordCount As Integer = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l)
For w As Integer = 0 To wordCount - 1
blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w))
blockText.Append(" "c)
Next
Next
Dim text As String = blockText.ToString().Trim()
If Not String.IsNullOrEmpty(text) Then
paragraphs.Add(text)
End If
Next
Finally
_imaging.ReleaseGdPictureImage(imageId)
End Try
End Using
Return paragraphs
End Function
End Class
Abordagem IronOCR:
using IronOcr;
public class IronOcrByteArrayOcr
{
public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // byte array accepted directly
var result = new IronTesseract().Read(input);
// Paragraphs are a first-class typed collection
return result.Paragraphs
.Select(p => p.Text)
.Where(t => !string.IsNullOrWhiteSpace(t))
.ToList();
}
}
using IronOcr;
public class IronOcrByteArrayOcr
{
public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // byte array accepted directly
var result = new IronTesseract().Read(input);
// Paragraphs are a first-class typed collection
return result.Paragraphs
.Select(p => p.Text)
.Where(t => !string.IsNullOrWhiteSpace(t))
.ToList();
}
}
Imports IronOcr
Public Class IronOcrByteArrayOcr
Public Function ExtractParagraphsFromBytes(imageBytes As Byte()) As List(Of String)
Using input As New OcrInput()
input.LoadImage(imageBytes) ' byte array accepted directly
Dim result = New IronTesseract().Read(input)
' Paragraphs are a first-class typed collection
Return result.Paragraphs _
.Select(Function(p) p.Text) _
.Where(Function(t) Not String.IsNullOrWhiteSpace(t)) _
.ToList()
End Using
End Function
End Class
O IronOCR aceita byte[] diretamente em LoadImage sem o intermediário MemoryStream. O resultado expõe .Paragraphs como uma coleção LINQ-consultável digitada — não é necessário loop triplo de bloco/linha/palavra. O guia de resultados de leitura aborda o acesso a coordenadas, a filtragem de confiança e os padrões de saída estruturados para fluxos de trabalho de processamento de faturas e formulários. Para digitalizar áreas específicas de documentos, consulte OCR baseado em região .
Referência de mapeamento da API .NET do GdPicture para o IronOCR
| GdPicture.NET | Equivalente de IronOCR |
|---|---|
using GdPicture14; |
using IronOcr; |
LicenseManager.RegisterKEY("key") |
IronOcr.License.LicenseKey = "key" |
new GdPictureImaging() |
Não requerido — interno ao OcrInput |
new GdPictureOCR() |
new IronTesseract() |
new GdPicturePDF() |
Não requerido — o OcrInput.LoadPdf() lida com isso |
_ocr.ResourceFolder = path |
Não é necessário — recursos incluídos no NuGet. |
imaging.CreateGdPictureImageFromFile(path) |
input.LoadImage(path) |
imaging.CreateGdPictureImageFromStream(stream) |
input.LoadImage(stream) |
imaging.CreateGdPictureImageFromBytes(bytes) |
input.LoadImage(bytes) |
imaging.CloneImage(tiffId) por quadro |
input.LoadImageFrames(tiffPath) |
imaging.ReleaseGdPictureImage(imageId) |
using var input = new OcrInput() — automático |
ocr.SetImage(imageId) |
Não requerido — o OcrInput mantém a imagem |
ocr.Language = "eng" |
ocr.Language = OcrLanguage.English |
ocr.RunOCR() → string resultId |
ocr.Read(input) → digitado OcrResult |
ocr.GetOCRResultText(resultId) |
result.Text |
ocr.GetOCRResultConfidence(resultId) |
result.Confidence |
ocr.GetOCRResultBlockCount(resultId) |
result.Pages[i].Paragraphs.Count |
ocr.GetOCRResultBlockLineWordText(resultId, b, l, w) |
result.Words[i].Text |
imaging.GetStat() / ocr.GetStat() |
Exceções padrão do .NET |
Verificação GdPictureStatus.OK após cada chamada |
Não é necessário — as exceções se propagam. |
pdf.OcrPage("eng", resourcePath, "", 200) |
result.SaveAsSearchablePdf(outputPath) |
pdf.RenderPageToGdPictureImage(200, false) |
Não é necessário — o IronOCR renderiza internamente. |
pdf.SelectPage(i) |
Não é necessário — todas as páginas são processadas por padrão. |
pdf.GetPageCount() |
result.Pages.Count |
Task.Run(() => ocr.RunOCR()) + SemaphoreSlim |
await ocr.ReadAsync(input) |
Problemas e soluções comuns em migrações
Problema 1: Variáveis de ID de imagem permanecem no código após a refatoração.
GdPicture.NET: O código existente declara int imageId no topo dos métodos, rastreia-o através de try/finally, e o passa para várias chamadas GdPicture. Após substituir as chamadas GdPicture, essas variáveis e suas chamadas ReleaseGdPictureImage tornam-se código morto órfão.
Solução: Exclua todo o padrão de identificação da imagem. Substitua a declaração, o try, o finally, e a chamada de liberação por um bloco using var input = new OcrInput(). Procure por ReleaseGdPictureImage para encontrar cada chamada de limpeza que deve ser removida:
grep -rn "ReleaseGdPictureImage\|imageId\|resultId" --include="*.cs" .
grep -rn "ReleaseGdPictureImage\|imageId\|resultId" --include="*.cs" .
// Remove all of this
int imageId = _imaging.CreateGdPictureImageFromFile(path);
try
{
_ocr.SetImage(imageId);
string resultId = _ocr.RunOCR();
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
// Replace with
using var input = new OcrInput();
input.LoadImage(path);
return new IronTesseract().Read(input).Text;
// Remove all of this
int imageId = _imaging.CreateGdPictureImageFromFile(path);
try
{
_ocr.SetImage(imageId);
string resultId = _ocr.RunOCR();
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
// Replace with
using var input = new OcrInput();
input.LoadImage(path);
return new IronTesseract().Read(input).Text;
' Remove all of this
Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(path)
Try
_ocr.SetImage(imageId)
Dim resultId As String = _ocr.RunOCR()
Return _ocr.GetOCRResultText(resultId)
Finally
_imaging.ReleaseGdPictureImage(imageId)
End Try
' Replace with
Using input As New OcrInput()
input.LoadImage(path)
Return New IronTesseract().Read(input).Text
End Using
Problema 2: Caminho da pasta de recursos não encontrado no ambiente de implantação
GdPicture.NET: O caminho configurado em _ocr.ResourceFolder resolve-se na máquina de desenvolvimento, mas falha em produção. Sintomas comuns são falhas silenciosas de OCR retornando resultados vazios, ou erros genéricos GdPictureStatus que não nomeiam o arquivo ausente.
Solução: Remova totalmente a atribuição do ResourceFolder. O suporte para inglês está integrado no pacote NuGet do IronOCR. Idiomas adicionais são instalados como pacotes NuGet . Não existe nenhum caminho de sistema de arquivos para configurar ou implantar:
# Remove the filesystem folder from deployment artifacts
# Add language NuGet packages to the project instead
:InstallCmd dotnet add package IronOcr.Languages.French, IronOcr.Languages.German
# Remove the filesystem folder from deployment artifacts
# Add language NuGet packages to the project instead
:InstallCmd dotnet add package IronOcr.Languages.French, IronOcr.Languages.German
Problema 3: O tratamento de erros do GdPictureStatus foi substituído por exceções.
GdPicture.NET: Toda operação retorna ou define um valor enum GdPictureStatus que deve ser verificado imediatamente. O código está denso com proteções if (status != GdPictureStatus.OK). Alguns códigos de status são genéricos (e.g., Error, InvalidParameter) e requerem consultar a documentação para determinar a causa raiz.
Solução: O IronOCR gera exceções tipadas do .NET . Substitua as verificações de status por blocos try/catch. Erros de entrada são cobertos pelos padrões IOException e FileNotFoundException; IronOcr.Exceptions.OcrException cobre erros específicos de OCR. Consulte o guia de configuração do IronTesseract para obter padrões recomendados de tratamento de erros:
try
{
var result = new IronTesseract().Read(imagePath);
return result.Text;
}
catch (FileNotFoundException ex)
{
_logger.LogError("Input file missing: {Path}", ex.FileName);
throw;
}
catch (IronOcr.Exceptions.OcrException ex)
{
_logger.LogError("OCR processing failed: {Message}", ex.Message);
throw;
}
try
{
var result = new IronTesseract().Read(imagePath);
return result.Text;
}
catch (FileNotFoundException ex)
{
_logger.LogError("Input file missing: {Path}", ex.FileName);
throw;
}
catch (IronOcr.Exceptions.OcrException ex)
{
_logger.LogError("OCR processing failed: {Message}", ex.Message);
throw;
}
Imports IronOcr
Imports System.IO
Try
Dim result = New IronTesseract().Read(imagePath)
Return result.Text
Catch ex As FileNotFoundException
_logger.LogError("Input file missing: {Path}", ex.FileName)
Throw
Catch ex As IronOcr.Exceptions.OcrException
_logger.LogError("OCR processing failed: {Message}", ex.Message)
Throw
End Try
Problema 4: Namespace GdPicture14 em Múltiplos Arquivos
GdPicture.NET: O número da versão no namespace significa que existe uma diretiva using GdPicture14; em toda a extensão do projeto em dezenas de arquivos. Após a migração, todos estes devem ser substituídos por using IronOcr;.
Solução: Utilize uma busca e substituição global em todos os arquivos .cs, então verifique se não restam referências ao GdPicture:
# Find all files with GdPicture namespace
grep -rln "using GdPicture" --include="*.cs" .
# After replacing, verify nothing remains
grep -rn "GdPicture14\|GdPictureOCR\|GdPictureImaging\|GdPicturePDF" --include="*.cs" .
# Find all files with GdPicture namespace
grep -rln "using GdPicture" --include="*.cs" .
# After replacing, verify nothing remains
grep -rn "GdPicture14\|GdPictureOCR\|GdPictureImaging\|GdPicturePDF" --include="*.cs" .
Edição 5: Lógica de Contagem de Quadros TIFF
GdPicture.NET: O código que itera quadros TIFF muitas vezes mistura chamadas entre GdPictureImaging.GetPageCount(imageId) e GdPicturePDF.GetPageCount() dependendo de como o arquivo foi carregado. O índice do quadro é baseado em 1.
Solução: input.LoadImageFrames(path) lida com todos os quadros automaticamente. Se o seu código atual apenas processa quadros específicos, use input.LoadImageFrames(path, frameNumbers) com um array de índice baseado em zero. Acesse os resultados por quadro através de result.Pages, que também é indexado por zero. O guia de entrada TIFF documenta explicitamente o comportamento do índice.
Problema 6: O pré-processamento requer o plugin de digitalização de documentos.
GdPicture.NET: Operações de desalinhamento e remoção de manchas pertencem ao plugin GdPictureDocumentImaging, que requer compra de licença separada. Equipes que optaram por não usar o plugin frequentemente enfrentam problemas de precisão de OCR em documentos digitalizados com páginas distorcidas ou com ruído.
Solução: Os métodos de pré-processamento do IronOCR fazem parte do pacote básico. Adicione Deskew() e DeNoise() diretamente em OcrInput. O guia de correção de qualidade de imagem cobre todos os filtros disponíveis, incluindo Contrast(), Sharpen(), Binarize(), e DeepCleanBackgroundNoise() para digitalizações severamente degradadas:
using var input = new OcrInput();
input.LoadImage("scanned-document.tiff");
input.Deskew(); // no separate plugin license
input.DeNoise();
input.Contrast();
var result = new IronTesseract().Read(input);
using var input = new OcrInput();
input.LoadImage("scanned-document.tiff");
input.Deskew(); // no separate plugin license
input.DeNoise();
input.Contrast();
var result = new IronTesseract().Read(input);
Imports IronOcr
Using input As New OcrInput()
input.LoadImage("scanned-document.tiff")
input.Deskew() ' no separate plugin license
input.DeNoise()
input.Contrast()
Dim result = New IronTesseract().Read(input)
End Using
Lista de verificação para migração do GdPicture for .NET
Pré-migração
Antes de fazer alterações, audite o código-fonte para localizar todas as dependências do GdPicture:
# Find all GdPicture namespace imports
grep -rn "using GdPicture" --include="*.cs" .
# Find all image ID creation points
grep -rn "CreateGdPictureImageFromFile\|CreateGdPictureImageFromStream\|RenderPageToGdPictureImage\|CloneImage" --include="*.cs" .
# Find all release calls — these map to using block boundaries
grep -rn "ReleaseGdPictureImage" --include="*.cs" .
# Find all resource folder assignments
grep -rn "ResourceFolder" --include="*.cs" .
# Find all OCR result ID accesses
grep -rn "RunOCR\|GetOCRResult\|resultId" --include="*.cs" .
# Find all GdPictureStatus checks
grep -rn "GdPictureStatus\|GetStat()" --include="*.cs" .
# Count GdPicture-dependent files
grep -rln "GdPicture14" --include="*.cs" . | wc -l
# Find all GdPicture namespace imports
grep -rn "using GdPicture" --include="*.cs" .
# Find all image ID creation points
grep -rn "CreateGdPictureImageFromFile\|CreateGdPictureImageFromStream\|RenderPageToGdPictureImage\|CloneImage" --include="*.cs" .
# Find all release calls — these map to using block boundaries
grep -rn "ReleaseGdPictureImage" --include="*.cs" .
# Find all resource folder assignments
grep -rn "ResourceFolder" --include="*.cs" .
# Find all OCR result ID accesses
grep -rn "RunOCR\|GetOCRResult\|resultId" --include="*.cs" .
# Find all GdPictureStatus checks
grep -rn "GdPictureStatus\|GetStat()" --include="*.cs" .
# Count GdPicture-dependent files
grep -rln "GdPicture14" --include="*.cs" . | wc -l
Documente o seguinte antes de modificar o código:
- Quais arquivos contêm referências
GdPictureImaging,GdPictureOCR, eGdPicturePDF - Quantos pares distintos de criação/liberação de IDs de imagem existem?
- Se o plugin de processamento de imagens de documentos (desvio de distorção, remoção de ruído) está em uso.
- Quais arquivos de linguagem
traineddataestão na pasta de recursos - Quais scripts de implantação ou arquivos Docker fazem referência ao caminho da pasta de recursos?
Migração de código
- Remova todos os pacotes NuGet do GdPicture do arquivo de projeto.
- Instale
IronOcrvia NuGet - Instale pacotes
IronOcr.Languages.*para cada idioma previamente na pasta de recursos - Adicione
IronOcr.License.LicenseKey = "..."aProgram.csouStartup.cs - Remova a chamada
LicenseManager.RegisterKEY()e o objeto do gerenciador de licenças - Remova todas as declarações de campo
GdPictureImaginge a inicialização do construtor - Remova todas as declarações de campo
GdPictureOCRe a atribuiçãoResourceFolder - Remova todas as declarações de campo
GdPicturePDFusadas para fluxos de trabalho de OCR - Substitua cada bloco
int imageId = _imaging.CreateGdPictureImage*(...)porusing var input = new OcrInput(); input.Load*(...) - Substitua cada
_ocr.SetImage(imageId); _ocr.Language = "..."; string resultId = _ocr.RunOCR();withvar result = new IronTesseract().Read(input); - Substitua
_ocr.GetOCRResultText(resultId)porresult.Text - Remova todas as chamadas
_imaging.ReleaseGdPictureImage(imageId) - Substitua verificações
GdPictureStatuspor blocos try/catch - Substitua loops de quadros TIFF por
input.LoadImageFrames(path) - Substitua
pdf.OcrPage(...)+pdf.SaveToFile(...)porresult.SaveAsSearchablePdf(outputPath) - Remova o caminho da pasta de recursos dos scripts de implantação e das imagens do Docker.
- Atualize
using GdPicture14;parausing IronOcr;em todos os arquivos afetados
Pós-migração
Após concluir todas as alterações de código, verifique o seguinte antes de implantar em produção:
- OCR de imagem única retorna o texto esperado sem
NullReferenceExceptionde componentes removidos - O processamento de TIFF de várias páginas cobre todos os quadros e produz texto por página através de
result.Pages - O OCR de PDF processa todas as páginas sem aumentar o consumo de memória em lotes de mais de 50 documentos.
- A entrada de fluxo aceita
MemoryStreameFileStreamsem gravações de arquivos intermediários - O OCR assíncrono integra-se com os manipuladores de requisição do ASP.NET Core sem bloquear o pool de threads.
- O processamento em lote paralelo com
Parallel.ForEachproduz resultados precisos em todos os threads - Todos os pacotes de idiomas (francês, alemão, etc.) são ativados corretamente por meio de pacotes NuGet.
- Os filtros de pré-processamento (correção de distorção, redução de ruído) melhoram a precisão em documentos digitalizados.
- O PDF pesquisável pode ser lido por visualizadores de PDF e aplicativos de busca de texto.
- Nenhuma referência ao namespace GdPicture resta em qualquer arquivo
.csapós a migração - O perfil de memória mostra uso estável sob carga sustentada (sem vazamento de alocações de imagem).
- A implantação é bem-sucedida em destinos Linux e Docker sem erros de caminho do sistema de arquivos.
Principais benefícios da migração para o IronOCR
Segurança de memória imposta pelo compilador. O ciclo de vida do ID da imagem que o GdPicture exige que os desenvolvedores mantenham manualmente desaparece completamente. OcrInput implementa IDisposable, e blocos using reforçam a limpeza no final de cada escopo — incluindo caminhos de exceção. Nenhum List<int> para manter, nenhum bloco finally para lembrar, nenhum incidente de memória de produção por chamadas de liberação perdidas.
Pacote Único para Cada Cenário de OCR. OCR de imagem, OCR de PDF, processamento de TIFF de várias páginas, geração de PDF pesquisável, filtros de pré-processamento, leitura de códigos de barras e mais de 125 pacotes de idiomas estão todos disponíveis no pacote NuGet IronOcr e seus complementos de idioma. Não existe nenhuma funcionalidade que exija a compra de uma segunda ou terceira licença antes que um fluxo de trabalho comum se torne possível. Consulte a visão geral dos recursos do IronOCR para obter a lista completa de funcionalidades.
Async Nativo e Segurança de Thread. ReadAsync é um verdadeiro método async, não um invólucro Task.Run. IronTesseract é seguro para usar em threads sem sincronização. Serviços de processamento de documentos que anteriormente requeriam inicialização de componentes por thread e serialização de semáforo reduzem para uma única instância compartilhada com Parallel.ForEach. O guia de OCR assíncrono abrange padrões tanto do ASP.NET Core quanto de serviços hospedados.
Configuração de implantação zero. O IronOCR não requer caminhos de sistema de arquivos, arquivos de idioma externos, instalação de binários nativos nem scripts de implantação. A etapa de restauração do NuGet fornece tudo o que o aplicativo precisa. As imagens Docker, as implantações do Azure App Service e os servidores Linux funcionam de forma idêntica à máquina de desenvolvimento. O guia de implantação do Docker e o guia de implantação do Azure demonstram configurações prontas para produção.
Namespace Estável de Versão. using IronOcr não mudou através de lançamentos principais de versão. Os números de versão do NuGet gerenciam o versionamento por meio do modelo padrão de gerenciamento de pacotes. As futuras atualizações importantes não exigirão uma busca e substituição em todo o código-fonte da importação do namespace.
Licenciamento Perpétuo Previsível.IronOCR é vendido por $999 (Lite), $1,499 (Plus), $2,999 (Professional) e $5,999 (Unlimited) — compras perpétuas únicas que incluem um ano de atualizações, com renovação opcional anualmente. Todas as funcionalidades estão disponíveis em todos os planos. Não há plugins por funcionalidade, custos por página ou obrigação de manutenção após o primeiro ano. As equipes que anteriormente absorviam mais de US$ 8.000 em licenças do plugin GdPicture para um fluxo de trabalho exclusivamente de OCR recuperam essa diferença já no primeiro ciclo de licenciamento. Informações completas sobre preços estão disponíveis na página de licenciamento do IronOCR .
Perguntas frequentes
Por que devo migrar do OCR GdPicture.NET 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 OCR GdPicture.NET para o IronOCR?
Substitua as sequências de inicialização do GdPicture.NET pela instanciação do IronTesseract, remova o gerenciamento do ciclo de vida do 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 GdPicture.NET 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 GdPicture.NET OCR 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 OCR GdPicture.NET para o IronOCR requer alterações na infraestrutura de implantação?
O IronOCR requer menos alterações de infraestrutura do que o GdPicture.NET OCR. 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 GdPicture.NET?
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 GdPicture.NET OCR 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 OCR GdPicture.NET 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.

