Alternativas a Nanonets OCR para España: IronOCR con cumplimiento AEAT, Facturae y LOPDGDD
El modelo de precios por página de Texto de AWS puede parecer económico a bajo volumen, pero los costos se acumulan indefinidamente a escala. Cada documento que procesa su aplicación sale de su red, viaja a un centro de datos de Amazon, es procesado por la infraestructura de Amazon y la factura se acumula indefinidamente. Para los equipos que evalúan opciones de OCR en .NET, la cuestión no es solo si Textract produce resultados precisos —lo hace—, sino si el modelo de coste por página, la transmisión obligatoria a la nube y la arquitectura de sondeo asíncrono para documentos de varias páginas se ajustan a lo que su aplicación realmente necesita.
Comprender AWS Textract
AWS Textract es el servicio de análisis de documentos gestionado por Amazon, accesible a través del SDK de AWS for .NET mediante el paquete AWSSDK.Textract NuGet. Funciona como una API en la nube: su aplicación envía los datos del documento a la infraestructura de Amazon y recibe resultados estructurados. El servicio requiere una cuenta de AWS, credenciales de IAM con permisos de Textract y una conexión a Internet para cada operación de OCR.
Textract ofrece varios modos de análisis distintos, cada uno con un precio independiente:
- DetectarTextoDocumento: Extracción básica de texto (vea precios de AWS Textract para las tarifas actuales por página)
- AnalizarDocumento (Tablas): Extracción de tablas estructuradas a una tarifa por página más alta que la extracción de texto básico
- AnalizarDocumento (Formularios): Extracción de formularios clave-valor a una tarifa por página más alta que la extracción de tabla
- AnalyzeExpense: Análisis de facturas y recibos a 0,01 $ por página
- AnalyzeID: Extracción de datos de documentos de identidad a 0,025 $ por página
- StartDocumentTextDetection / StartDocumentAnalysis: API asíncrona necesaria para cualquier PDF de varias páginas, que requiere un bucket de staging de S3, sondeo de trabajos y paginación de resultados
El modelo de resultado utiliza una lista plana de objetos Block con IDs de relación que deben recorrerse para reconstruir tablas, formularios o cualquier salida estructurada. Una extracción de tabla simple requiere iterar bloques BlockType.TABLE, encontrar bloques hijo BlockType.CELL a través de IDs de relación RelationshipType.CHILD, y luego obtener bloques BlockType.WORD para el texto de cada celda. Este modelo de gráfico de relaciones maneja estructuras de documentos complejas, pero no es ligero.
El canal S3-Async
El OCR de una sola imagen a través de DetectDocumentTextAsync puede pasar bytes de documento directamente en la solicitud. Los PDFs de varias páginas no pueden. Cualquier PDF requiere el proceso asíncrono completo:
// AWS Textract: PDF de varias páginas requires S3 + async job
public async Task<string> ProcessPdfAsync(string pdfPath)
{
// Step 1: Upload to S3 — credentials for two services required
var key = $"uploads/{Guid.NewGuid()}.pdf";
using (var fileStream = File.OpenRead(pdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = _bucketName,
Key = key,
InputStream = fileStream
});
}
try
{
// Step 2: Start async Textract job
var startResponse = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = _bucketName, Name = key }
}
});
var jobId = startResponse.JobId;
// Step 3: Poll every 5 seconds until complete
GetDocumentTextDetectionResponse getResponse;
do
{
await Task.Delay(5000);
getResponse = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId });
} while (getResponse.JobStatus == JobStatus.IN_PROGRESS);
if (getResponse.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Textract job failed: {getResponse.StatusMessage}");
// Step 4: Paginate through result blocks
var allText = new StringBuilder();
string nextToken = null;
do
{
var pageResponse = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest
{
JobId = jobId,
NextToken = nextToken
});
foreach (var block in pageResponse.Blocks.Where(b => b.BlockType == BlockType.LINE))
allText.AppendLine(block.Text);
nextToken = pageResponse.NextToken;
} while (nextToken != null);
return allText.ToString();
}
finally
{
// Step 5: Siempre clean up S3
await _s3Client.DeleteObjectAsync(_bucketName, key);
}
}
// AWS Textract: PDF de varias páginas requires S3 + async job
public async Task<string> ProcessPdfAsync(string pdfPath)
{
// Step 1: Upload to S3 — credentials for two services required
var key = $"uploads/{Guid.NewGuid()}.pdf";
using (var fileStream = File.OpenRead(pdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = _bucketName,
Key = key,
InputStream = fileStream
});
}
try
{
// Step 2: Start async Textract job
var startResponse = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = _bucketName, Name = key }
}
});
var jobId = startResponse.JobId;
// Step 3: Poll every 5 seconds until complete
GetDocumentTextDetectionResponse getResponse;
do
{
await Task.Delay(5000);
getResponse = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId });
} while (getResponse.JobStatus == JobStatus.IN_PROGRESS);
if (getResponse.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Textract job failed: {getResponse.StatusMessage}");
// Step 4: Paginate through result blocks
var allText = new StringBuilder();
string nextToken = null;
do
{
var pageResponse = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest
{
JobId = jobId,
NextToken = nextToken
});
foreach (var block in pageResponse.Blocks.Where(b => b.BlockType == BlockType.LINE))
allText.AppendLine(block.Text);
nextToken = pageResponse.NextToken;
} while (nextToken != null);
return allText.ToString();
}
finally
{
// Step 5: Siempre clean up S3
await _s3Client.DeleteObjectAsync(_bucketName, key);
}
}
Imports System
Imports System.IO
Imports System.Text
Imports System.Threading.Tasks
Imports Amazon.S3
Imports Amazon.Textract
Imports Amazon.Textract.Model
Public Class PdfProcessor
Private _s3Client As IAmazonS3
Private _textractClient As IAmazonTextract
Private _bucketName As String
Public Async Function ProcessPdfAsync(pdfPath As String) As Task(Of String)
' Step 1: Upload to S3 — credentials for two services required
Dim key As String = $"uploads/{Guid.NewGuid()}.pdf"
Using fileStream As FileStream = File.OpenRead(pdfPath)
Await _s3Client.PutObjectAsync(New PutObjectRequest With {
.BucketName = _bucketName,
.Key = key,
.InputStream = fileStream
})
End Using
Try
' Step 2: Start async Textract job
Dim startResponse = Await _textractClient.StartDocumentTextDetectionAsync(
New StartDocumentTextDetectionRequest With {
.DocumentLocation = New DocumentLocation With {
.S3Object = New S3Object With {.Bucket = _bucketName, .Name = key}
}
})
Dim jobId As String = startResponse.JobId
' Step 3: Poll every 5 seconds until complete
Dim getResponse As GetDocumentTextDetectionResponse
Do
Await Task.Delay(5000)
getResponse = Await _textractClient.GetDocumentTextDetectionAsync(
New GetDocumentTextDetectionRequest With {.JobId = jobId})
Loop While getResponse.JobStatus = JobStatus.IN_PROGRESS
If getResponse.JobStatus <> JobStatus.SUCCEEDED Then
Throw New Exception($"Textract job failed: {getResponse.StatusMessage}")
End If
' Step 4: Paginate through result blocks
Dim allText As New StringBuilder()
Dim nextToken As String = Nothing
Do
Dim pageResponse = Await _textractClient.GetDocumentTextDetectionAsync(
New GetDocumentTextDetectionRequest With {
.JobId = jobId,
.NextToken = nextToken
})
For Each block In pageResponse.Blocks.Where(Function(b) b.BlockType = BlockType.LINE)
allText.AppendLine(block.Text)
Next
nextToken = pageResponse.NextToken
Loop While nextToken IsNot Nothing
Return allText.ToString()
Finally
' Step 5: Siempre clean up S3
Await _s3Client.DeleteObjectAsync(_bucketName, key)
End Try
End Function
End Class
Esta es la implementación mínima viable para el procesamiento confiable de PDF — cinco fases distintas, dos clientes de servicio AWS, y lógica de limpieza en un bloque finally. La versión de producción completa, con el tratamiento adecuado de errores, la lógica de reintentos con límite de frecuencia y la gestión de tiempos de espera, tiene una extensión de entre 150 y 300 líneas.
Comprender IronOCR
IronOCR es una biblioteca IronOCR comercial de .NET que se ejecuta íntegramente en su infraestructura. Incorpora un motor Tesseract 5 optimizado con preprocesamiento automático de imágenes, compatibilidad nativa con PDF y una API síncrona que genera resultados directamente sin llamadas a servicios externos ni pasos intermedios.
Características clave de la arquitectura de IronOCR:
- Solo procesamiento local: ningún dato del documento sale de la máquina en la que se ejecuta la aplicación
- Un solo paquete NuGet:
dotnet add package IronOcrinstala todo, incluyendo binarios nativos - Preprocesamiento automático: la corrección de la inclinación, la eliminación de ruido, la mejora del contraste, la binarización y el escalado de resolución se realizan automáticamente en entradas de baja calidad
- Compatibilidad nativa con PDF: lee archivos PDF directamente a través de la ruta del archivo o un flujo sin necesidad de almacenamiento temporal en S3 ni tareas asíncronas
- Seguro para hilos: Una sola instancia
IronTesseractmaneja solicitudes concurrentes a través de hilos sin contención - Licenciamiento perpetuo: $999 Lite / $1,499 Plus / $2,999 Professional / $5,999 Unlimited — un pago, sin cargos por página, sin medidor de uso
- Más de 125 paquetes de idiomas: se instalan como paquetes NuGet independientes, se cargan localmente, sin llamadas de red
Comparación de características
| Característica | Texto de AWS | IronOCR |
|---|---|---|
| Lugar de procesamiento | Amazon Cloud (obligatorio) | Local / en las instalaciones |
| PDF de varias páginas | Requiere S3 + trabajo asíncrono | Llamada sincrónica directa |
| Modelo de costes | Por página (contacte a AWS para los precios actuales) | Licencia perpetua, sin tarifa por página |
| Se requiere conexión a Internet | Siempre | Nunca |
| Configuración de credenciales | Usuario/rol de IAM + S3 opcional | Cadena de clave de licencia única |
| Implementación aislada de la red | No es posible | Totalmente compatible |
| Compatibilidad con PDF cifrados | No soportado | Integrado (parámetro de contraseña) |
Comparación detallada de características
| Característica | Texto de AWS | IronOCR |
|---|---|---|
| Extracción de texto | ||
| OCR básico (imágenes) | Sí — DetectDocumentTextAsync |
Sí — ocr.Read(path) |
| PDF de varias páginas | Requiere S3 + sondeo asíncrono | Directo input.LoadPdf(path) |
| PDF protegido con contraseña | No soportado | input.LoadPdf(path, Password: "x") |
| Entrada de flujo | Sí (matriz de bytes en la solicitud) | Sí — input.LoadImage(stream) |
| Extracción estructurada | ||
| Extracción de tablas | AnalyzeDocument + recorrido de grafo de bloque |
Reconstrucción basada en la posición de las palabras WORD |
| Extracción de campos de formulario | AnalyzeDocument + bloques KEY_VALUE_SET |
Zonas CropRectangle basadas en región |
| Resultados a nivel de línea | Block filtrando por BlockType.LINE |
result.Lines colección directa |
| A nivel de WORD con coordenadas | Block filtrando por BlockType.WORD |
result.Words con .X, .Y, .Width |
| Puntuaciones de confianza | Confianza por bloque | Por palabra y general result.Confidence |
| Modelo de procesamiento | ||
| Sincrónico (imágenes) | Sí (solo una página) | Sí (todos los tipos de documentos) |
| Asíncrono | Requisitos para los archivos PDF | Opcional — envoltorio Task.Run() |
| Procesamiento por lotes | Requiere gestión de límites de velocidad (5 TPS por defecto) | Parallel.ForEach sin restricciones |
| Preprocesamiento | ||
| Corrección automática de la inclinación | No expuesto | input.Deskew() |
| Eliminación de ruido | Interno (no configurable) | input.DeNoise() |
| Mejora del contraste | Interno (no configurable) | input.Contrast() |
| Mejora de la resolución | Interno (no configurable) | input.EnhanceResolution(300) |
| Binarización | Interno | input.Binarize() |
| Formatos de salida | ||
| Texto sin formato | Sí | Sí |
| PDF con función de búsqueda | No | result.SaveAsSearchablePdf(path) |
| hOCR | No | result.SaveAsHocrFile(path) |
| JSON estructurado | A través de la serialización de bloques | result.Words / result.Lines |
| Despliegue | ||
| Local | No | Sí |
| Aislado físicamente | No | Sí |
| Docker | Sí (con credenciales de AWS inyectadas) | Sí (no se requieren credenciales) |
| AWS Lambda | Nativo | Se admite |
| Azure | Sí | Sí |
| Linux | Sí (gestionado por AWS) | Sí — get-started/linux/ |
| Cumplimiento | ||
| HIPAA | Requiere BAA con AWS | Sin procesador externo |
| RGPD | Los datos se transfieren a las regiones de AWS | Los datos permanecen dentro de los límites |
| ITAR | Prohibido sin autorización especial | Totalmente local |
| Aislado físicamente / Nivel 3 de CMMC | No es posible | Se admite |
Costo a escala
El modelo de precios por página es la restricción estructural definitoria de AWS Textract. Los costos que parecen pequeños por página se acumulan significativamente a lo largo de un flujo de trabajo de documento real.
Enfoque de AWS Textract
// Every call to this method costs money — per page, permanently
public async Task<string> DetectTextAsync(string imagePath)
{
var imageBytes = File.ReadAllBytes(imagePath); // Image leaves your network
var request = new DetectDocumentTextRequest
{
Document = new Document
{
Bytes = new MemoryStream(imageBytes)
}
};
var response = await _client.DetectDocumentTextAsync(request); // per-page charge
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
// Every call to this method costs money — per page, permanently
public async Task<string> DetectTextAsync(string imagePath)
{
var imageBytes = File.ReadAllBytes(imagePath); // Image leaves your network
var request = new DetectDocumentTextRequest
{
Document = new Document
{
Bytes = new MemoryStream(imageBytes)
}
};
var response = await _client.DetectDocumentTextAsync(request); // per-page charge
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
Imports System.IO
Imports System.Threading.Tasks
' Every call to this method costs money — per page, permanently
Public Async Function DetectTextAsync(imagePath As String) As Task(Of String)
Dim imageBytes = File.ReadAllBytes(imagePath) ' Image leaves your network
Dim request = New DetectDocumentTextRequest With {
.Document = New Document With {
.Bytes = New MemoryStream(imageBytes)
}
}
Dim response = Await _client.DetectDocumentTextAsync(request) ' per-page charge
Return String.Join(vbLf, response.Blocks _
.Where(Function(b) b.BlockType = BlockType.LINE) _
.Select(Function(b) b.Text))
End Function
Consulte la página de precios de AWS Textract para conocer las tarifas actuales por página. Las diferentes características del API (detección básica de texto, extracción de tablas, extracción de formularios) tienen diferentes tarifas. Un documento que contiene tablas y campos de formulario incurre en cargos más altos que la detección básica de texto, y los costos aumentan con el volumen sin límite superior y sin posibilidad de pagar por adelantado.
A volúmenes de página altos, los costos totales de tres años pueden ser sustanciales, y el contador sigue corriendo.
Enfoque de IronOCR
// One license. No per-page cost. Same code handles 1 page or 1,000,000.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var text = new IronTesseract().Read("document.jpg").Text;
// One license. No per-page cost. Same code handles 1 page or 1,000,000.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var text = new IronTesseract().Read("document.jpg").Text;
Imports IronOcr
' One license. No per-page cost. Same code handles 1 page or 1,000,000.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim text As String = New IronTesseract().Read("document.jpg").Text
La licencia Professional $2,999 cubre 10 desarrolladores, proyectos ilimitados y volumen de página ilimitado. Tras el primer año, el coste mensual por las páginas procesadas es cero. Para equipos que procesan volúmenes de página significativos, la licencia IronOCR se paga por sí misma rápidamente en comparación con los cargos continuos por página en la nube.
La página de licencias de IronOCR incluye detalles sobre los niveles de servicio, opciones de suscripción SaaS para escenarios de facturación basada en el uso y condiciones de redistribución para fabricantes de equipos originales (OEM).
Soberanía de datos y cumplimiento normativo
La arquitectura de Texto de AWS hace imposible una garantía: que tus documentos permanezcan dentro de tu infraestructura. Cada operación de OCR transmite el contenido del documento a los servidores de Amazon.
Enfoque de AWS Textract
// This code sends PHI, legal documents, financial records — whatever is in
// the file — to Amazon Web Services infrastructure
public async Task<string> ProcessSensitiveDocumentAsync(string documentPath)
{
var imageBytes = File.ReadAllBytes(documentPath);
// Data crosses your security perimeter here
var request = new DetectDocumentTextRequest
{
Document = new Document
{
Bytes = new MemoryStream(imageBytes)
}
};
// Amazon processes it; you receive text back
var response = await _client.DetectDocumentTextAsync(request);
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
// This code sends PHI, legal documents, financial records — whatever is in
// the file — to Amazon Web Services infrastructure
public async Task<string> ProcessSensitiveDocumentAsync(string documentPath)
{
var imageBytes = File.ReadAllBytes(documentPath);
// Data crosses your security perimeter here
var request = new DetectDocumentTextRequest
{
Document = new Document
{
Bytes = new MemoryStream(imageBytes)
}
};
// Amazon processes it; you receive text back
var response = await _client.DetectDocumentTextAsync(request);
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
Imports System.IO
Imports System.Threading.Tasks
Imports Amazon.Textract
Imports Amazon.Textract.Model
Public Class DocumentProcessor
Private _client As AmazonTextractClient
Public Sub New(client As AmazonTextractClient)
_client = client
End Sub
' This code sends PHI, legal documents, financial records — whatever is in
' the file — to Amazon Web Services infrastructure
Public Async Function ProcessSensitiveDocumentAsync(documentPath As String) As Task(Of String)
Dim imageBytes = File.ReadAllBytes(documentPath)
' Data crosses your security perimeter here
Dim request As New DetectDocumentTextRequest With {
.Document = New Document With {
.Bytes = New MemoryStream(imageBytes)
}
}
' Amazon processes it; you receive text back
Dim response = Await _client.DetectDocumentTextAsync(request)
Return String.Join(vbLf, response.Blocks _
.Where(Function(b) b.BlockType = BlockType.LINE) _
.Select(Function(b) b.Text))
End Function
End Class
AWS ofrece un Acuerdo de socio comercial HIPAApara entidades cubiertas, y las regiones de GovCloud proporcionan la autorización FedRAMP High. Estos marcos no modifican la arquitectura fundamental: los documentos salen de su infraestructura para cada operación. En el caso de los datos técnicos sujetos a la normativa ITAR, no se trata de una sutileza de cumplimiento, sino de una prohibición. Para las cargas de trabajo de nivel 3 del CMMC con CUI, la transmisión en la nube requiere autorizaciones específicas que la mayoría de los contratistas de defensa no poseen. Para los sistemas aislados (redes de investigación, entornos de control industrial, instalaciones clasificadas), Textract simplemente no está disponible.
AWS Textract está disponible en seis regiones: us-east-1, us-west-2, eu-west-1, eu-west-2, ap-southeast-1 y ap-southeast-2. Las organizaciones con requisitos de residencia de datos fuera de estas regiones no disponen de ninguna opción que cumpla con la normativa.
Enfoque de IronOCR
// IronOCR: document bytes never leave this process
public string ProcessSensitiveDocument(string documentPath)
{
// Processes entirely on local hardware — no network call
var ocr = new IronTesseract();
return ocr.Read(documentPath).Text;
}
// IronOCR: document bytes never leave this process
public string ProcessSensitiveDocument(string documentPath)
{
// Processes entirely on local hardware — no network call
var ocr = new IronTesseract();
return ocr.Read(documentPath).Text;
}
' IronOCR: document bytes never leave this process
Public Function ProcessSensitiveDocument(documentPath As String) As String
' Processes entirely on local hardware — no network call
Dim ocr As New IronTesseract()
Return ocr.Read(documentPath).Text
End Function
Dado que IronOCR se ejecuta localmente, encaja de forma natural en los flujos de trabajo del sector sanitario que procesan información médica protegida (PHI), en los sistemas de documentos legales que gestionan comunicaciones privilegiadas, en las aplicaciones financieras que manejan imágenes de tarjetas de pago y en los procesos de los contratistas de defensa que procesan información controlada (CUI). No hay ningún procesador externo que auditar, ningún acuerdo de negocios (BAA) que negociar ni ninguna restricción de residencia de datos que cumplir. El ámbito de cumplimiento es la propia infraestructura de su organización.
Para los equipos que implementan en la infraestructura de AWS pero necesitan procesamiento local,IronOCRse ejecuta en AWS (EC2, Lambda) sin ninguna dependencia de Textract: el procesamiento se realiza dentro de los límites de su propia cuenta de AWS en lugar de en el servicio gestionado de Amazon.
Sondeo asíncrono frente a procesamiento síncrono
La división arquitectónica entre las API síncronas (imagen única) y asíncronas (PDF de varias páginas) de Textract no es un detalle menor de la API. Determina cómo se crean los servicios, cómo se gestionan los errores y cuánto código deben leer y analizar los responsables del mantenimiento.
Enfoque de AWS Textract
// Full production-grade async processor for Textract PDF handling
public class TextractAsyncProcessor
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client;
private readonly string _bucketName;
private readonly TimeSpan _pollInterval = TimeSpan.FromSeconds(5);
private readonly TimeSpan _maxWaitTime = TimeSpan.FromMinutes(10);
public async Task<DocumentResult> ProcessDocumentAsync(
string localFilePath,
CancellationToken cancellationToken = default)
{
var s3Key = $"textract-uploads/{Guid.NewGuid()}{Path.GetExtension(localFilePath)}";
try
{
// Phase 1: Upload to S3
await UploadToS3Async(localFilePath, s3Key, cancellationToken);
// Phase 2: Start Textract job
var jobId = await StartTextractJobAsync(s3Key, cancellationToken);
// Phase 3: Poll until complete (up to 10 minutes)
var pollResult = await PollForCompletionAsync(jobId, cancellationToken);
if (!pollResult.Success)
throw new Exception($"Textract job failed: {pollResult.ErrorMessage}");
// Phase 4: Retrieve paginated results
return await GetAllResultsAsync(jobId, cancellationToken);
}
finally
{
// Phase 5: S3 cleanup — must succeed or storage costs accumulate
await DeleteFromS3Async(s3Key, cancellationToken);
}
}
private async Task<(bool Success, string ErrorMessage)> PollForCompletionAsync(
string jobId, CancellationToken cancellationToken)
{
var startTime = DateTime.UtcNow;
int pollCount = 0;
while (DateTime.UtcNow - startTime < _maxWaitTime)
{
cancellationToken.ThrowIfCancellationRequested();
var response = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId }, cancellationToken);
pollCount++;
switch (response.JobStatus)
{
case JobStatus.SUCCEEDED: return (true, null);
case JobStatus.FAILED: return (false, response.StatusMessage ?? "Unknown error");
case JobStatus.IN_PROGRESS:
await Task.Delay(_pollInterval, cancellationToken);
break;
default:
throw new Exception($"Unknown job status: {response.JobStatus}");
}
}
return (false, "Job timed out");
}
}
// Full production-grade async processor for Textract PDF handling
public class TextractAsyncProcessor
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client;
private readonly string _bucketName;
private readonly TimeSpan _pollInterval = TimeSpan.FromSeconds(5);
private readonly TimeSpan _maxWaitTime = TimeSpan.FromMinutes(10);
public async Task<DocumentResult> ProcessDocumentAsync(
string localFilePath,
CancellationToken cancellationToken = default)
{
var s3Key = $"textract-uploads/{Guid.NewGuid()}{Path.GetExtension(localFilePath)}";
try
{
// Phase 1: Upload to S3
await UploadToS3Async(localFilePath, s3Key, cancellationToken);
// Phase 2: Start Textract job
var jobId = await StartTextractJobAsync(s3Key, cancellationToken);
// Phase 3: Poll until complete (up to 10 minutes)
var pollResult = await PollForCompletionAsync(jobId, cancellationToken);
if (!pollResult.Success)
throw new Exception($"Textract job failed: {pollResult.ErrorMessage}");
// Phase 4: Retrieve paginated results
return await GetAllResultsAsync(jobId, cancellationToken);
}
finally
{
// Phase 5: S3 cleanup — must succeed or storage costs accumulate
await DeleteFromS3Async(s3Key, cancellationToken);
}
}
private async Task<(bool Success, string ErrorMessage)> PollForCompletionAsync(
string jobId, CancellationToken cancellationToken)
{
var startTime = DateTime.UtcNow;
int pollCount = 0;
while (DateTime.UtcNow - startTime < _maxWaitTime)
{
cancellationToken.ThrowIfCancellationRequested();
var response = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId }, cancellationToken);
pollCount++;
switch (response.JobStatus)
{
case JobStatus.SUCCEEDED: return (true, null);
case JobStatus.FAILED: return (false, response.StatusMessage ?? "Unknown error");
case JobStatus.IN_PROGRESS:
await Task.Delay(_pollInterval, cancellationToken);
break;
default:
throw new Exception($"Unknown job status: {response.JobStatus}");
}
}
return (false, "Job timed out");
}
}
Imports System
Imports System.IO
Imports System.Threading
Imports System.Threading.Tasks
Imports Amazon.Textract
Imports Amazon.S3
Imports Amazon.Textract.Model
' Full production-grade async processor for Textract PDF handling
Public Class TextractAsyncProcessor
Private ReadOnly _textractClient As AmazonTextractClient
Private ReadOnly _s3Client As AmazonS3Client
Private ReadOnly _bucketName As String
Private ReadOnly _pollInterval As TimeSpan = TimeSpan.FromSeconds(5)
Private ReadOnly _maxWaitTime As TimeSpan = TimeSpan.FromMinutes(10)
Public Async Function ProcessDocumentAsync(localFilePath As String, Optional cancellationToken As CancellationToken = Nothing) As Task(Of DocumentResult)
Dim s3Key = $"textract-uploads/{Guid.NewGuid()}{Path.GetExtension(localFilePath)}"
Try
' Phase 1: Upload to S3
Await UploadToS3Async(localFilePath, s3Key, cancellationToken)
' Phase 2: Start Textract job
Dim jobId = Await StartTextractJobAsync(s3Key, cancellationToken)
' Phase 3: Poll until complete (up to 10 minutes)
Dim pollResult = Await PollForCompletionAsync(jobId, cancellationToken)
If Not pollResult.Success Then
Throw New Exception($"Textract job failed: {pollResult.ErrorMessage}")
End If
' Phase 4: Retrieve paginated results
Return Await GetAllResultsAsync(jobId, cancellationToken)
Finally
' Phase 5: S3 cleanup — must succeed or storage costs accumulate
Await DeleteFromS3Async(s3Key, cancellationToken)
End Try
End Function
Private Async Function PollForCompletionAsync(jobId As String, cancellationToken As CancellationToken) As Task(Of (Success As Boolean, ErrorMessage As String))
Dim startTime = DateTime.UtcNow
Dim pollCount As Integer = 0
While DateTime.UtcNow - startTime < _maxWaitTime
cancellationToken.ThrowIfCancellationRequested()
Dim response = Await _textractClient.GetDocumentTextDetectionAsync(New GetDocumentTextDetectionRequest With {.JobId = jobId}, cancellationToken)
pollCount += 1
Select Case response.JobStatus
Case JobStatus.SUCCEEDED
Return (True, Nothing)
Case JobStatus.FAILED
Return (False, If(response.StatusMessage, "Unknown error"))
Case JobStatus.IN_PROGRESS
Await Task.Delay(_pollInterval, cancellationToken)
Case Else
Throw New Exception($"Unknown job status: {response.JobStatus}")
End Select
End While
Return (False, "Job timed out")
End Function
End Class
No se trata de un texto estándar que se pueda generar y olvidar. Cuando un trabajo de Textract falla en medio de la ejecución, la limpieza de S3 debe seguir ejecutándose. Cuando un trabajo agota el tiempo de espera tras 10 minutos, el autor de la llamada necesita un error claro. Cuando se produce una caída de la red durante el sondeo, la estrategia de reintento no debe crear trabajos duplicados. Cada uno de estos modos de fallo requiere un tratamiento explícito; la estructura mostrada anteriormente es la implementación mínima responsable.
El procesamiento por lotes añade otra capa: el límite TPS por defecto de Textract StartDocumentTextDetection es de 5 solicitudes por segundo. Procesar 100 documentos requiere una limitación SemaphoreSlim, un temporizador de reposición de tasa, y lógica de reintento para ProvisionedThroughputExceededException.
Enfoque de IronOCR
// IronOCR: same synchronous API regardless of document type or size
public string ProcessDocument(string filePath)
{
using var input = new OcrInput();
if (Path.GetExtension(filePath).Equals(".pdf", StringComparison.OrdinalIgnoreCase))
input.LoadPdf(filePath);
else
input.LoadImage(filePath);
return new IronTesseract().Read(input).Text;
}
// IronOCR: same synchronous API regardless of document type or size
public string ProcessDocument(string filePath)
{
using var input = new OcrInput();
if (Path.GetExtension(filePath).Equals(".pdf", StringComparison.OrdinalIgnoreCase))
input.LoadPdf(filePath);
else
input.LoadImage(filePath);
return new IronTesseract().Read(input).Text;
}
Imports System.IO
' IronOCR: same synchronous API regardless of document type or size
Public Function ProcessDocument(filePath As String) As String
Using input As New OcrInput()
If Path.GetExtension(filePath).Equals(".pdf", StringComparison.OrdinalIgnoreCase) Then
input.LoadPdf(filePath)
Else
input.LoadImage(filePath)
End If
Return New IronTesseract().Read(input).Text
End Using
End Function
No hay bucle de sondeo, ni seguimiento de ID de trabajo, ni bucket de S3, ni paginación de resultados. El mismo código gestiona tanto un único archivo JPEG como un PDF de 200 páginas. El procesamiento se completa o genera un error; no hay un estado intermedio de "en curso" que gestionar. Para el procesamiento por lotes, IronOCR es seguro para hilos y una sola instancia IronTesseract maneja Parallel.ForEach sin bloqueos o semáforos.
La guía de instalación de IronTesseract cubre la configuración, y la guía de entrada de PDF documenta la selección de rangos de páginas, los PDF protegidos con contraseña y la entrada basada en flujos para PDF recuperados de bases de datos o respuestas HTTP.
Sobrecarga de la gestión de credenciales
Para iniciar una operación de OCR con Texto de AWS es necesario configurar IAM antes de procesar una sola página.
Enfoque de AWS Textract
Antes de llamar a DetectDocumentTextAsync, un desarrollador debe:
- Crea una cuenta de AWS u obtén acceso a una ya existente
- Crear un usuario o rol IAM con permisos
textract:DetectDocumentTextytextract:AnalyzeDocument - Generar y almacenar de forma segura el ID de la clave de acceso y la clave de acceso secreta
- Configurar la resolución de credenciales: variables de entorno, archivo de credenciales de AWS o perfil de instancia de EC2
- Si se procesan PDFs: crear un bucket S3, configurar la política del bucket, añadir permisos
s3:PutObjectys3:DeleteObject - Implementar políticas de rotación de credenciales para cumplir con los estándares de seguridad
- Almacene las credenciales de forma segura en cada entorno de implementación: secretos de Docker, secretos de Kubernetes, AWS Secrets Manager o variables del canal de CI/CD
// Every environment needs these configured before this constructor succeeds
public TextractOcrService()
{
// Reads credentials from environment, ~/.aws/credentials, or IAM role
_client = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
}
// Every environment needs these configured before this constructor succeeds
public TextractOcrService()
{
// Reads credentials from environment, ~/.aws/credentials, or IAM role
_client = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
}
' Every environment needs these configured before this constructor succeeds
Public Sub New()
' Reads credentials from environment, ~/.aws/credentials, or IAM role
_client = New AmazonTextractClient(Amazon.RegionEndpoint.USEast1)
End Sub
Cuando las credenciales expiran, rotan o están mal configuradas, cada llamada de OCR falla con AmazonTextractException portando ErrorCode == "AccessDeniedException". En un sistema de producción, esto implica implementar bloques catch específicos para los errores de credenciales y supervisar las desviaciones de las políticas de IAM.
Enfoque de IronOCR
// One-time setup at application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Or from environment — recommended for deployments
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// One-time setup at application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Or from environment — recommended for deployments
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
' One-time setup at application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
' Or from environment — recommended for deployments
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
La clave de licencia es una cadena estática. No caduca durante su funcionamiento, no requiere rotación y no conlleva permisos que gestionar. Un contenedor Dockerque procesa documentos no necesita credenciales de AWS inyectadas, un rol de IAM vinculado a un contexto de ejecución ni acceso de red a AWS STS para la actualización de tokens.
La reducción total de sobrecarga de credenciales al cambiar de Textract a IronOCR: se han eliminado tres paquetes NuGet (AWSSDK.Textract, AWSSDK.S3, AWSSDK.Core), se han eliminado todas las variables de entorno AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_DEFAULT_REGION, y se han desmantelado las configuraciones de roles IAM y buckets S3. La guía de entrada de imágenes y la guía de entrada de flujos cubren toda la gama de métodos de entrada que sustituyen a los modelos de documentos de matriz de bytes y objetos S3 de Textract.
Referencia de mapeo de API
| API de Texto de AWS | Equivalente a IronOCR |
|---|---|
AmazonTextractClient |
IronTesseract |
AmazonS3Client |
No es necesario |
DetectDocumentTextRequest |
OcrInput |
DetectDocumentTextResponse |
OcrResult |
AnalyzeDocumentRequest |
OcrInput con CropRectangle para zonas |
StartDocumentTextDetectionRequest |
OcrInput — sincrónico, no es necesario iniciar |
GetDocumentTextDetectionRequest |
No es obligatorio — resultados inmediatos |
Document.Bytes |
input.LoadImage(bytes) o input.LoadImage(stream) |
S3Object (preparación de documentos) |
Cadena de ruta de archivo o flujo |
Block (BlockType.LINE) |
result.Lines |
Block (BlockType.WORD) |
result.Words |
Block (BlockType.TABLE) |
Agrupación de posición de palabras a través de result.Words |
Block (BlockType.KEY_VALUE_SET) |
Extracción de región CropRectangle |
Block.Confidence |
word.Confidence / result.Confidence |
JobStatus.SUCCEEDED |
No aplicable — retorno sincrónico |
JobStatus.IN_PROGRESS |
No aplicable — sin estado asíncrono |
response.NextToken (paginación) |
No aplicable — resultados no paginados |
ProvisionedThroughputExceededException |
No aplicable — sin límites de TPS |
client.DetectDocumentTextAsync(request) |
ocr.Read(path) |
client.AnalyzeDocumentAsync(request) |
ocr.Read(input) |
client.StartDocumentTextDetectionAsync(request) |
ocr.Read(input) |
client.GetDocumentTextDetectionAsync(request) |
No procede |
Cuando los equipos se plantean pasar de Texto de AWS a IronOCR
Cuando la factura mensual se convierte en una partida presupuestaria
Los equipos que empezaron a utilizar Textract con un volumen reducido suelen encontrarse con una situación concreta: la factura de AWS por el procesamiento OCR aparece en la revisión trimestral del presupuesto y alguien pregunta si este coste es fijo. No lo es. En volúmenes altos de páginas, los costos anuales de Textract pueden ser sustanciales — consulte la página de precios de AWS Textract para tarifas actuales. La licencia Professional de IronOCR en $2,999 un solo pago se paga rápidamente con volúmenes de páginas moderados a altos.
Cuando un requisito de cumplimiento bloquea el procesamiento en la nube
Las organizaciones sanitarias que implementan flujos de trabajo de digitalización de documentos suelen descubrir a mitad del proyecto que la información médica protegida (PHI) según la HIPAAno puede circular a través de servicios en la nube sin un acuerdo de negocios (BAA) y una revisión legal adicional, o que su equipo de seguridad prohíbe por completo la transmisión a la nube. Los contratistas de defensa que manejan planos técnicos, especificaciones o cualquier información clasificada (CUI) se enfrentan a restricciones de ITARy CMMC que excluyen a Texto de AWS de su consideración. Los bufetes de abogados que procesan comunicaciones privilegiadas tienen preocupaciones similares. No se trata de casos extremos teóricos de cumplimiento normativo, sino que aparecen con frecuencia en revisiones de adquisiciones, auditorías de seguridad y negociaciones contractuales.IronOCR procesa los datos localmente, por lo que la cuestión del cumplimiento normativo en relación con los datos de los documentos se reduce a si su propia infraestructura está incluida en el ámbito de aplicación, y no a si la infraestructura de Amazon lo está.
Cuando la complejidad del PDF asíncrono supera su valor
El proceso S3-async de cinco fases —carga, inicio del trabajo, sondeo, paginación de resultados, limpieza— no es técnicamente difícil de implementar. Es difícil de mantener, probar y operar. Cada fase es un punto de fallo. Los errores de carga en S3 requieren una lógica de reintento. Los fallos en las tareas de Textract requieren distinguir entre errores transitorios y permanentes. Los tiempos de espera de las consultas requieren un manejo de los tiempos de espera independiente de la cancelación. La paginación de resultados requiere acumular el estado a lo largo de múltiples llamadas a la API. Los fallos en la limpieza de S3 requieren una alerta, ya que los objetos huérfanos acumulan costes. Los equipos que han implementado este sistema en producción suelen dedicar más tiempo de ingeniería a su mantenimiento que el que invirtieron en su desarrollo. El equivalente de IronOCR— input.LoadPdf(path) seguido de ocr.Read(input) — elimina todas las cinco fases y sus modos de falla asociados.
Cuando los entornos de implementación carecen de acceso a Internet
Los contenedores Dockerque se ejecutan en segmentos de red aislados, los servidores locales sin conexión a Internet saliente, los entornos de investigación aislados físicamente y los sistemas industriales con estrictos controles de red comparten una característica: Texto de AWS no está disponible.IronOCR se instala como un paquete NuGet estándar y funciona sin necesidad de realizar llamadas de red después de la instalación. Los equipos que ejecutan aplicaciones .NET en estos entornos no tienen la opción Textract y necesitan una biblioteca que procese localmente. La guía de implementación de Docker y la guía de implementación de Linux cubren la configuración específica para entornos de contenedores.
Cuando la limitación de velocidad interrumpe los flujos de trabajo por lotes
El límite TPS por defecto StartDocumentTextDetection es de 5 solicitudes por segundo. Las llamadas sincrónicas DetectDocumentText también están limitadas por tasa. Los trabajos por lotes que procesan cientos o miles de documentos deben implementar limitación SemaphoreSlim, retroceso exponencial en ProvisionedThroughputExceededException, y temporizadores de reposición de tasa. AWS admite las solicitudes de aumento del límite de TPS, pero requieren justificación, revisión y no están garantizadas.IronOCR procesa las tareas tan rápido como lo permite la CPU local: un servidor de 32 núcleos procesa 32 documentos simultáneamente sin configuración de limitación de velocidad ni negociación del nivel de servicio.
Consideraciones comunes sobre la migración
Sustitución del gráfico de bloques por colecciones directas
Textract representa todos los resultados como una List<Block> plana donde líneas, palabras, celdas, tablas y pares clave-valor se distinguen por BlockType y se enlazan por matrices de IDs de relación.IronOCR proporciona colecciones de texto directamente escritas.
// Textract: filter flat block list by type
var lines = response.Blocks.Where(b => b.BlockType == BlockType.LINE);
var words = response.Blocks.Where(b => b.BlockType == BlockType.WORD);
// IronOCR: direct access to typed collections
var result = ocr.Read(imagePath);
var lines = result.Lines; // IEnumerable<OcrResult.OcrResultLine>
var words = result.Words; // IEnumerable<OcrResult.OcrResultWord>
foreach (var word in result.Words)
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) confidence {word.Confidence}%");
// Textract: filter flat block list by type
var lines = response.Blocks.Where(b => b.BlockType == BlockType.LINE);
var words = response.Blocks.Where(b => b.BlockType == BlockType.WORD);
// IronOCR: direct access to typed collections
var result = ocr.Read(imagePath);
var lines = result.Lines; // IEnumerable<OcrResult.OcrResultLine>
var words = result.Words; // IEnumerable<OcrResult.OcrResultWord>
foreach (var word in result.Words)
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) confidence {word.Confidence}%");
' Textract: filter flat block list by type
Dim lines = response.Blocks.Where(Function(b) b.BlockType = BlockType.LINE)
Dim words = response.Blocks.Where(Function(b) b.BlockType = BlockType.WORD)
' IronOCR: direct access to typed collections
Dim result = ocr.Read(imagePath)
Dim lines = result.Lines ' IEnumerable(Of OcrResult.OcrResultLine)
Dim words = result.Words ' IEnumerable(Of OcrResult.OcrResultWord)
For Each word In result.Words
Console.WriteLine($"'{word.Text}' at ({word.X},{word.Y}) confidence {word.Confidence}%")
Next
La guía de resultados estructurados cubre result.Pages, result.Paragraphs, result.Lines, result.Words y el acceso a coordenadas para construir procesamiento de documentos consciente del diseño.
Sustitución del procesamiento de PDF por etapas S3 con Direct LoadPdf
Cualquier código de Textract que se cargue en S3 antes de iniciar una tarea de detección puede sustituirse por una carga directa de PDF. Sin área de preparación, sin sincronización de carga, sin lógica de limpieza.
// Textract: upload to S3 → start job → poll → paginate → cleanup (50+ lines)
//IronOCR equivalent:
public string ProcessPdf(string pdfPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return ocr.Read(input).Text;
}
// Specific page ranges (no Textract equivalent without async job per range)
public string ProcessPdfPages(string pdfPath, int startPage, int endPage)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdfPages(pdfPath, startPage, endPage);
return ocr.Read(input).Text;
}
// Textract: upload to S3 → start job → poll → paginate → cleanup (50+ lines)
//IronOCR equivalent:
public string ProcessPdf(string pdfPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return ocr.Read(input).Text;
}
// Specific page ranges (no Textract equivalent without async job per range)
public string ProcessPdfPages(string pdfPath, int startPage, int endPage)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdfPages(pdfPath, startPage, endPage);
return ocr.Read(input).Text;
}
Imports IronOcr
Public Class PdfProcessor
' Textract: upload to S3 → start job → poll → paginate → cleanup (50+ lines)
' IronOCR equivalent:
Public Function ProcessPdf(pdfPath As String) As String
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadPdf(pdfPath)
Return ocr.Read(input).Text
End Using
End Function
' Specific page ranges (no Textract equivalent without async job per range)
Public Function ProcessPdfPages(pdfPath As String, startPage As Integer, endPage As Integer) As String
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadPdfPages(pdfPath, startPage, endPage)
Return ocr.Read(input).Text
End Using
End Function
End Class
Agregar preprocesamiento para documentos que produjeron baja confianza en Textract
El preprocesamiento de Textract es interno y no configurable. Cuando un documento escaneado produce resultados deficientes, las únicas opciones son volver a intentarlo o aceptar un resultado con baja fiabilidad.IronOCR expone directamente el proceso de preprocesamiento.
// For documents that returned low-confidence results from Textract
using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew(); // Fix rotation from scanner misalignment
input.DeNoise(); // Remove scanner noise artifacts
input.Contrast(); // Boost faint text
input.EnhanceResolution(300); // Scale to optimal OCR resolution
var result = new IronTesseract().Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%");
// For documents that returned low-confidence results from Textract
using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew(); // Fix rotation from scanner misalignment
input.DeNoise(); // Remove scanner noise artifacts
input.Contrast(); // Boost faint text
input.EnhanceResolution(300); // Scale to optimal OCR resolution
var result = new IronTesseract().Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%");
Imports IronOcr
Dim input As New OcrInput()
input.LoadImage("low-quality-scan.jpg")
input.Deskew() ' Fix rotation from scanner misalignment
input.DeNoise() ' Remove scanner noise artifacts
input.Contrast() ' Boost faint text
input.EnhanceResolution(300) ' Scale to optimal OCR resolution
Dim result = New IronTesseract().Read(input)
Console.WriteLine($"Confidence: {result.Confidence}%")
La guía de corrección de calidad de imagen y tutorial de filtros de imagen documentan toda la tubería de preprocesamiento y las combinaciones que funcionan mejor para tipos de documentos específicos. Para la interpretación de puntajes de confianza y el acceso a la confianza por elemento, la guía de puntuaciones de confianza cubre la propiedad result.Confidence y los valores de confianza por palabra.
Cómo gestionar el cambio de patrón de asíncrono a síncrono
El código existente de Textract es necesariamente async Task<t> durante todo el proceso porque el SDK es solo asincrónico. Las operaciones de IronOCR son síncronas. Para el código de la aplicación que ya tiene una cadena de llamadas asincrónica, envuelva la llamada IronOCR en Task.Run para mantener el límite asincrónico.
// Preserves async call site for minimal refactoring
public async Task<string> ExtractTextAsync(string path)
{
return await Task.Run(() => new IronTesseract().Read(path).Text);
}
// Preserves async call site for minimal refactoring
public async Task<string> ExtractTextAsync(string path)
{
return await Task.Run(() => new IronTesseract().Read(path).Text);
}
Imports System.Threading.Tasks
' Preserves async call site for minimal refactoring
Public Async Function ExtractTextAsync(path As String) As Task(Of String)
Return Await Task.Run(Function() New IronTesseract().Read(path).Text)
End Function
Se trata de un envoltorio práctico, no de un requisito. Para el procesamiento del lado del servidor, donde el código que realiza la llamada ya se encuentra en un hilo en segundo plano, se prefiere la llamada síncrona directa.
Funcionalidades adicionales de IronOCR
Más allá de los puntos de comparación mencionados anteriormente,IronOCR ofrece capacidades que no tienen equivalente en AWS Textract:
- Lectura de código de barras durante el OCR: Configure
ocr.Configuration.ReadBarCodes = truey los códigos de barras en el documento se extraen junto con el texto en un solo paso — no se requiere un paso de escaneo de códigos de barras separado - Seguimiento del progreso para trabajos largos : Suscríbase a los eventos de progreso para el procesamiento de varias páginas sin necesidad de consultar un servicio externo.
- Procesamiento de documentos escaneados : Flujo de trabajo optimizado para la salida típica de escáneres de oficina, incluyendo escaneos a doble cara y páginas con orientación mixta.
- Extracción simultánea multilingüe: Combine paquetes de idioma en el momento de la lectura —
OcrLanguage.French + OcrLanguage.German— sin cambio de nivel de API - Lectura de pasaportes e identificaciones : Canalización dedicada para zonas legibles por máquina en documentos de identidad, que extrae campos estructurados sin definición manual de regiones.
Compatibilidad con .NET y preparación para el futuro
IronOCR está diseñado para .NET 8 y .NET 9, con compatibilidad activa para proyectos .NET Standard 2.0 y .NET Framework versiones 4.6.2 a 4.8. La biblioteca distribuye binarios nativos para Windows x64, Windows x86, Linuxx64 y macOS a través de un único paquete NuGet , sin necesidad de cambiar el identificador de tiempo de ejecución ni de hacer referencia a paquetes específicos de la plataforma. El paquete AWSSDK.Textract de Texto de AWS admite los mismos objetivos modernos de .NET, pero el modelo de implementación incluye todo el árbol de dependencias del SDK de AWS, infraestructura de credenciales IAM y las restricciones arquitectónicas documentadas a lo largo de este artículo.IronOCR mantiene un desarrollo activo con lanzamientos periódicos que siguen de cerca las actualizaciones del motor Tesseract 5 y los avances del entorno de ejecución .NET , incluida la compatibilidad con .NET 10 cuando se lance.
Conclusión
AWS Textract e IronOCR resuelven el mismo problema —extraer texto de documentos en aplicaciones .NET con supuestos arquitectónicos fundamentalmente incompatibles. Textract parte de la base de que los documentos pueden salir de su red, que los costes del servicio en la nube aumentan linealmente con el volumen y que los PDF de varias páginas justifican una canalización asíncrona de cinco fases con almacenamiento temporal en S3.IronOCR parte de la base de que los documentos permanecen donde se procesan, que los costes de las licencias deben desvincularse del volumen y que el procesamiento de PDF debe requerir las mismas tres líneas de código que el procesamiento de imágenes.
La aritmética de costos es la línea divisoria más clara. A volúmenes bajos, las tarifas por página de Textract son manejables. A medida que el volumen crece, los costos anuales se componen de manera significativa. A grandes volúmenes de páginas con extracción de tablas, los costos multianuales de Textract pueden superar en gran medida incluso la Licencia Unlimited de IronOCR en $5,999. La matemática inicial se mantiene: el modelo por página se acumula rápidamente, y nunca se detiene.
La soberanía de los datos es la segunda restricción estructural. En lo que respecta a la carga de trabajo en los sectores sanitario, jurídico, financiero y gubernamental, la cuestión de dónde se procesan los documentos no es una cuestión de preferencia, sino un requisito de cumplimiento.IronOCR realiza los procesos localmente por diseño, no por configuración. No hay ningún "modo local" que se pueda activar; El procesamiento local es el único modo de funcionamiento. Eso simplifica la respuesta en materia de cumplimiento normativo: sus documentos permanecen en su infraestructura porque no hay otro lugar adonde puedan ir.
Para los equipos que evalúan el OCR a gran escala o que operan en entornos donde los datos de los documentos no pueden salir de la infraestructura interna, la documentación de IronOCR proporciona la referencia completa de la API, guías de implementación para Docker, AWS, Azurey Linux, y tutoriales que cubren toda la gama de casos de uso de OCR, desde la lectura básica de imágenes hasta la generación de PDF con capacidad de búsqueda y la extracción multilingüe.
(
Preguntas Frecuentes
¿Qué es Amazon Textract?
Amazon Textract es una solución de OCR utilizada por desarrolladores y empresas para extraer texto de imágenes y documentos. Es una de las diversas opciones de OCR evaluadas junto con IronOCR for .NET para el desarrollo de aplicaciones.
¿Cómo se compara IronOCR con Amazon Textract para desarrolladores .NET?
IronOCR es una biblioteca de OCR .NET nativa de NuGet que utiliza IronTesseract como motor principal. En comparación con Amazon Textract, ofrece una implementación más sencilla (sin instaladores de SDK), precios de tarifa plana y una API de C# limpia sin interoperabilidad COM ni dependencias de la nube.
¿Es IronOCR más fácil de configurar que Amazon Textract?
IronOCR se instala mediante un único paquete NuGet. No hay instaladores de SDK, archivos de licencia que copiar, componentes COM que registrar ni binarios de ejecución independientes que gestionar. Todo el motor de OCR está incluido en el paquete.
¿Qué diferencias de precisión existen entre Amazon Textract e IronOCR?
IronOCR logra una alta precisión de reconocimiento para documentos comerciales estándar, facturas, recibos y formularios escaneados. Para documentos muy degradados o escrituras poco comunes, la precisión varía en función de la calidad de la fuente. IronOCR incluye filtros de preprocesamiento de imágenes para mejorar el reconocimiento en entradas de baja calidad.
¿Es IronOCR compatible con la extracción de texto en PDF?
Sí. IronOCR extrae texto tanto de PDF nativos como de imágenes PDF escaneadas en una sola llamada. También admite archivos TIFF de varias páginas, imágenes y secuencias. En el caso de los PDF escaneados, el OCR se aplica página a página con objetos de resultado por página.
¿Qué diferencia hay entre las licencias de Amazon Textract y las de IronOCR?
IronOCR utiliza una licencia perpetua de tarifa plana sin cargos por página o por escaneo. Las organizaciones que procesan grandes volúmenes de documentos pagan el mismo coste de licencia independientemente del volumen. Encontrará más información y precios por volumen en la página de licencias de IronOCR.
¿Qué idiomas admite IronOCR?
IronOCR es compatible con 127 idiomas a través de paquetes de idiomas NuGet independientes. Para añadir un idioma, basta con ejecutar el comando 'dotnet add package IronOcr.Languages.{Language}'. No es necesaria la colocación manual de archivos ni la configuración de rutas.
¿Cómo instalo IronOCR en un proyecto .NET ?
Instalación a través de NuGet: install-Package IronOcr' en la consola del gestor de paquetes o 'dotnet add package IronOcr' en la CLI. Los paquetes de idiomas adicionales se instalan del mismo modo. No se requiere ningún instalador nativo del SDK.
¿Es IronOCR adecuado para Docker y las implementaciones en contenedores, a diferencia de Amazon Textract?
Sí. IronOCR funciona en contenedores Docker a través de su paquete NuGet. La clave de licencia se establece mediante una variable de entorno. No se requieren archivos de licencia, rutas de SDK ni montajes de volumen para el propio motor de OCR.
¿Puedo probar IronOCR antes de comprarlo, en comparación con Amazon Textract?
Sí. El modo de prueba de IronOCR procesa documentos y devuelve resultados de OCR con una marca de agua superpuesta en la salida. Puede verificar la precisión en sus propios documentos antes de adquirir una licencia.
¿Admite IronOCR la lectura de códigos de barras junto con la extracción de texto?
IronOCR se centra en la extracción de texto y el reconocimiento óptico de caracteres. Para la lectura de códigos de barras, Iron Software ofrece IronBarcode como biblioteca complementaria. Ambas están disponibles por separado o como parte del paquete Iron Suite.
¿Es fácil migrar de Amazon Textract a IronOCR?
La migración de Amazon Textract a IronOCR suele implicar la sustitución de las secuencias de inicialización por la instanciación de IronTesseract, la eliminación de la gestión del ciclo de vida de COM y la actualización de las llamadas a la API. La mayoría de las migraciones reducen significativamente la complejidad del código.

