Migración de AWS Textract a IronOCR
Esta guía describe el proceso completo de migración de Texto de AWS a IronOCR para desarrolladores .NET que necesitan procesar documentos localmente sin depender de la nube. Abarca la eliminación de credenciales, la eliminación de la canalización asíncrona, la eliminación de S3 y las sustituciones de código específicas necesarias para trasladar cada operación de Textract a su equivalente en IronOCR.
¿Por qué migrar desde AWS Textract?
AWS Textract es un servicio gestionado muy capaz, pero su arquitectura impone costes, tanto financieros como operativos, que aumentan a medida que crece la carga de trabajo con sus documentos. Los equipos que desarrollan sistemas complejos de procesamiento de documentos tarde o temprano se topan con todos estos puntos de fricción.
La infraestructura de credenciales de AWS complica cada implementación. Cada entorno que ejecuta código de Textract necesita credenciales de AWS: usuario o rol de IAM, clave y secreto de acceso, configuración de región y, para el procesamiento de PDF, permisos de bucket de S3. Esto implica cinco pasos de configuración distintos antes de que se procese la primera página. Las implementaciones en producción requieren políticas de rotación de credenciales, inyección segura en contenedores Docker o pods de Kubernetes, y supervisión para AccessDeniedException cuando la desviación de política IAM causa fallos silenciosos.IronOCR requiere una sola cadena de caracteres: una clave de licencia, que se configura una sola vez al iniciar el sistema.
La fijación de precios por página no tiene techo. La tarifa base de $0.0015 por página para DetectDocumentText es el piso, no el costo. Cualquier documento con tablas activa AnalyzeDocument a $0.015 por página — diez veces la tarifa base. Los formularios añaden 0,05 dólares por página. Un flujo de trabajo con documentos mixtos, con un volumen de 100.000 páginas al mes y extracción de tablas, cuesta 18.000 dólares al año, y esa cifra se reinicia cada enero. La licencia IronOCR Professional tiene un coste único de 2.999 dólares. A partir de ese momento, el coste por página es cero, independientemente del volumen.
El pipeline asincrónico de PDF es una responsabilidad de mantenimiento. Procesar cualquier documento PDF de varias páginas a través de Textract requiere cinco fases distintas: carga en S3, StartDocumentTextDetection, bucle de sondeo, recuperación de resultados paginados, y limpieza de S3. Cada fase es un modo de fallo independiente que requiere su propio manejo de errores, estrategia de reintentos, y gestión de tiempos de espera. Los equipos que implementan este sistema en producción informan sistemáticamente que dedican más tiempo a su mantenimiento que a su desarrollo.IronOCR lee un PDF con solo dos líneas de código.
Sin acceso a Internet, no hay Textract. Los contenedores Docker en segmentos de red aislados, los servidores locales, los entornos de investigación aislados de la red y los sistemas industriales con restricciones de tráfico saliente comparten una característica: Textract no está disponible.IronOCR se instala como un paquete NuGet y funciona sin necesidad de realizar llamadas de red tras la descarga inicial del paquete.
Los datos salen de su infraestructura con cada llamada. Cada operación de OCR con Textract transmite el contenido del documento a los servidores de Amazon. Para las organizaciones sanitarias que procesan información sanitaria protegida (PHI), los contratistas de defensa que manejan información controlada no clasificada (CUI), los equipos legales que procesan comunicaciones privilegiadas y las instituciones financieras que procesan imágenes de tarjetas de pago, esto no es una opción de configuración, sino una restricción arquitectónica que prohíbe por completo el uso de Textract en entornos regulados. Los procesos de IronOCR se ejecutan en su hardware. No hay modo nube para desactivar; El procesamiento local es el único modo de funcionamiento.
Los límites de tasa restringen el rendimiento por lotes. El límite de TPS predeterminado de StartDocumentTextDetection es de 5 solicitudes por segundo. Los trabajos por lotes que procesan cientos de documentos requieren SemaphoreSlim limitación, retrocesos exponenciales en ProvisionedThroughputExceededException, y temporizadores de reabastecimiento de tasa. Para solicitar un aumento de TPS se requiere abrir un caso formal de soporte de AWS, sin garantía de resultado.IronOCR procesa tan rápido como lo permite la CPU local — un servidor de 16 núcleos procesa 16 documentos simultáneamente con un simple Parallel.ForEach, sin necesidad de negociación de nivel de servicio.
El problema fundamental
Cada despliegue de Textract comienza con esta ceremonia, antes de que se pueda procesar una sola página:
// Textract: five environment variables, an IAM role, and an S3 bucket
// — all required before the first OCR call
// Environment must have:
// AWS_ACCESS_KEY_ID=AKIA...
// AWS_SECRET_ACCESS_KEY=...
// AWS_DEFAULT_REGION=us-east-1
// IAM role: textract:DetectDocumentText, textract:AnalyzeDocument
// IAM role: s3:PutObject, s3:DeleteObject (for any PDF processing)
// S3 bucket: my-textract-staging-bucket (with lifecycle policy to prevent cost accumulation)
public class TextractOcrService
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client; // required for PDFs
public TextractOcrService()
{
// Fails with AmazonClientException if credentials not found in chain
_textractClient = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
_s3Client = new AmazonS3Client(Amazon.RegionEndpoint.USEast1);
}
}
IronOCR reemplaza toda la cadena de credenciales con una única asignación:
// IronOCR: one line, one string, done
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Or from environment (no IAM, no rotation, no S3)
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
##IronOCR vs AWS Textract: Comparación de características
La siguiente tabla muestra las capacidades de ambas bibliotecas en las dimensiones más relevantes para las decisiones de migración.
| Característica | Texto de AWS | IronOCR |
|---|---|---|
| Lugar de procesamiento | Amazon Cloud (obligatorio) | Solo para consumo local/en el establecimiento. |
| Se requiere Internet | Siempre | Nunca |
| Configuración de credenciales | Usuario/rol de IAM + bucket S3 opcional | Cadena de clave de licencia única |
| PDF de varias páginas | Requiere entorno de preparación S3 + trabajo asíncrono | Inyección sincrónica directa input.LoadPdf() |
| PDF protegido con contraseña | No soportado | input.LoadPdf(path, Password: "x") |
| TIFF multifotograma | No soportado | input.LoadImageFrames("multi.tiff") |
| Se requiere sondeo asíncrono. | Sí (para todos los PDF) | No — síncrono por defecto |
| Límites de tarifa | 5 TPS por defecto (StartDocumentTextDetection) | Ninguno — solo depende de la CPU |
| Costo por página | 0,0015–0,065 $ por página | $0 después de la compra de la licencia |
| Preprocesamiento automático | Interno, no configurable. | Corregir inclinación, Reducir ruido, Contraste, Binarizar, Enfocar, Escalar |
| Salida en PDF con capacidad de búsqueda | No disponible | result.SaveAsSearchablePdf() |
| Exportación hOCR | No disponible | result.SaveAsHocrFile() |
| Lectura de códigos de barras | No disponible | ocr.Configuration.ReadBarCodes = true |
| OCR basado en regiones | A través de AnalyzeDocument + relaciones de bloques | CropRectangle(x, y, width, height) |
| Resultados estructurados | Plano List<Block> filtrado por BlockType | Tipado Pages, Paragraphs, Lines, Words |
| Idiomas compatibles | Dominante del inglés (seleccione opciones adicionales) | Más de 125 paquetes de idiomas a través de NuGet. |
| Multilingüe simultáneo | No soportado | OcrLanguage.French + OcrLanguage.German |
| Implementación aislada de la red | No es posible | Totalmente compatible |
| HIPAA sin BAA | No es posible | Sin procesador externo: solo para instalación local. |
| Despliegue de Docker | Requiere credenciales de AWS inyectadas | Sin credenciales: paquete NuGet simple |
| Traducción multiplataforma | Gestionado por AWS (solo Linux) | Windows, Linux, macOS, Docker, Azure, AWS EC2 |
| Modelo de licencia | Facturación por página (gasto perpetuo) | Perpetuo de una sola vez ($999 / $1,499 / $2,999) |
Inicio rápido: Migración de Texto de AWS a IronOCR
Paso 1: Sustituir el paquete NuGet
Elimine los paquetes del SDK de AWS:
dotnet remove package AWSSDK.Textract
dotnet remove package AWSSDK.S3
dotnet remove package AWSSDK.Core
Instala IronOCR desde NuGet :
Paso 2: Actualizar los espacios de nombres
Reemplace todas las importaciones de espacios de nombres de AWS con el único espacio de nombres de IronOCR:
// Before (AWS Textract)
using Amazon.Textract;
using Amazon.Textract.Model;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Runtime;
// After (IronOCR)
using IronOcr;
Paso 3: Inicializar licencia
Agregue la inicialización de la licencia una sola vez al iniciar la aplicación. Elimine toda la configuración de variables de entorno de credenciales de AWS:
// Remove from startup:
// AWS_ACCESS_KEY_ID environment variable
// AWS_SECRET_ACCESS_KEY environment variable
// AWS_DEFAULT_REGION environment variable
// ~/.aws/credentials file entries
// IAM role bindings on the execution context
// Add instead:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
Ejemplos de migración de código
Reemplazo del constructor AmazonTextractClient y configuración de IAM
El cambio más visible en la migración es la inicialización del cliente. Textract requiere un cliente con inyección de dependencias y resolución de credenciales subyacente, lo que significa que tanto el cliente como todas sus dependencias deben estar preconfigurados en cada entorno de implementación. Cualquier desconfiguración falla silenciosamente hasta que la primera llamada a OCR lanza AmazonClientException.
Enfoque de AWS Textract:
// Requires: NuGet AWSSDK.Textract, AWSSDK.S3
// Requires: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_DEFAULT_REGION in environment
// Requires: IAM policy with textract:* and s3:PutObject, s3:DeleteObject
using Amazon.S3;
using Amazon.Textract;
public class DocumentOcrService
{
private readonly AmazonTextractClient _textractClient;
private readonly AmazonS3Client _s3Client;
private readonly string _stagingBucket;
public DocumentOcrService(IConfiguration config)
{
// Credential chain: environment → ~/.aws/credentials → EC2 instance profile
// Fails if none found — runtime exception, not compile-time
_textractClient = new AmazonTextractClient(Amazon.RegionEndpoint.USEast1);
_s3Client = new AmazonS3Client(Amazon.RegionEndpoint.USEast1);
_stagingBucket = config["Textract:StagingBucket"]; // bucket must exist and have permissions
}
public async Task<string> ReadImageAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
var response = await _textractClient.DetectDocumentTextAsync(
new DetectDocumentTextRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) }
});
return string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
}
Enfoque IronOCR:
// Requires: NuGet IronOcr
// Requires: one license key string — nothing else
using IronOcr;
public class DocumentOcrService
{
private readonly IronTesseract _ocr;
public DocumentOcrService()
{
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // or set at Program.cs startup
_ocr = new IronTesseract();
}
public string ReadImage(string imagePath)
{
// No credential chain, no region, no bucket — just read
return _ocr.Read(imagePath).Text;
}
}
La infraestructura completa de credenciales de AWS — roles de IAM, variables de entorno, archivos ~/.aws/credentials, políticas de ciclo de vida de cubos S3, y configuración de región — se elimina. El constructor DocumentOcrService pasa de un sitio de inyección de dependencia de 3 a una asignación de licencia única con modos de falla en tiempo de ejecución. Para obtener más detalles sobre los patrones de implementación, consulte la guía de configuración de IronTesseract .
Sustitución de StartDocumentTextDetection por el procesamiento de múltiples fotogramas TIFF
La API de trabajo asincrónico de Textract (StartDocumentTextDetection) es necesaria para cualquier documento multipágina. Un patrón común en los procesos de digitalización de documentos es la recepción de documentos escaneados como archivos TIFF multifotograma, es decir, un fotograma por página escaneada. Textract no puede aceptar archivos TIFF de entrada directamente; El documento debe convertirse a formato PDF y subirse a S3 antes de que pueda comenzar un trabajo.IronOCR lee archivos TIFF multifotograma de forma nativa.
Enfoque de AWS Textract:
// Textract cannot accept TIFF directly — requires PDF conversion + S3 + async job
// This shows the conversion overhead plus the minimum async pipeline
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<string> ProcessScannedTiffAsync(string tiffPath, string s3Bucket)
{
// Step 0: Convert TIFF to PDF first (Textract does not accept TIFF for async jobs)
// Requires a separate PDF conversion library — not shown here
var pdfPath = Path.ChangeExtension(tiffPath, ".pdf");
ConvertTiffToPdf(tiffPath, pdfPath); // external dependency required
// Step 1: Upload converted PDF to S3
var s3Key = $"staging/{Guid.NewGuid()}.pdf";
using (var stream = File.OpenRead(pdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = s3Bucket,
Key = s3Key,
InputStream = stream
});
}
try
{
// Step 2: Start async detection job
var startResp = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = s3Bucket, Name = s3Key }
}
});
// Step 3: Poll every 5 seconds — can block for 30–120 seconds on large files
GetDocumentTextDetectionResponse pollResp;
do
{
await Task.Delay(5000);
pollResp = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = startResp.JobId });
} while (pollResp.JobStatus == JobStatus.IN_PROGRESS);
if (pollResp.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Job failed: {pollResp.StatusMessage}");
// Step 4: Collect paginated results
var text = new StringBuilder();
string token = null;
do
{
var page = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest
{
JobId = startResp.JobId,
NextToken = token
});
foreach (var block in page.Blocks.Where(b => b.BlockType == BlockType.LINE))
text.AppendLine(block.Text);
token = page.NextToken;
} while (token != null);
return text.ToString();
}
finally
{
// Step 5: Clean up S3 — orphaned objects accumulate storage costs
await _s3Client.DeleteObjectAsync(s3Bucket, s3Key);
File.Delete(pdfPath); // clean up intermediate PDF
}
}
Enfoque IronOCR:
//IronOCR reads multi-frame TIFF directly — no conversion, no S3, no polling
using IronOcr;
public string ProcessScannedTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // reads all frames natively
var ocr = new IronTesseract();
var result = ocr.Read(input);
// Access per-page results if needed
foreach (var page in result.Pages)
Console.WriteLine($"Page {page.PageNumber}: {page.Words.Length} words detected");
return result.Text;
}
Se eliminan el paso de conversión de TIFF a PDF, la carga a S3, el bucle de sondeo asíncrono, la acumulación de resultados paginados y la limpieza de S3. La implementación de IronOCR lee los marcos directamente y proporciona acceso por página a través de result.Pages sin ninguna conversión de formato intermedio. La guía de entrada de archivos TIFF y GIF abarca la selección de fotogramas para los casos en los que solo se necesita un subconjunto de fotogramas.
Sustitución del almacenamiento temporal S3 por una salida PDF con capacidad de búsqueda.
Un caso de uso común de Textract es la conversión de archivos PDF escaneados a un formato que permita realizar búsquedas. El método Textract requiere subir cada PDF a S3, ejecutar la tarea asíncrona, recopilar el texto extraído y, a continuación, utilizar una biblioteca PDF independiente para incrustar ese texto como una capa que permita realizar búsquedas.IronOCR realiza el escaneo y genera un PDF con capacidad de búsqueda en una sola llamada.
Enfoque de AWS Textract:
// Textract extracts text but cannot produce searchable PDFs
// Requires: S3 upload + async job + separate PDF library to embed the text layer
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<string> ExtractTextForSearchableLayerAsync(
string scannedPdfPath,
string s3Bucket)
{
// Must upload to S3 — cannot pass PDF bytes directly for async jobs
var key = $"ocr-input/{Guid.NewGuid()}.pdf";
await using (var fs = File.OpenRead(scannedPdfPath))
{
await _s3Client.PutObjectAsync(new PutObjectRequest
{
BucketName = s3Bucket,
Key = key,
InputStream = fs,
ContentType = "application/pdf"
});
}
string jobId;
try
{
var startResp = await _textractClient.StartDocumentTextDetectionAsync(
new StartDocumentTextDetectionRequest
{
DocumentLocation = new DocumentLocation
{
S3Object = new S3Object { Bucket = s3Bucket, Name = key }
}
});
jobId = startResp.JobId;
}
catch
{
await _s3Client.DeleteObjectAsync(s3Bucket, key);
throw;
}
// Poll — minimum 5s wait; typical 15–60s for a 10-page PDF
GetDocumentTextDetectionResponse pollResp;
int pollAttempts = 0;
const int maxAttempts = 120; // 10 minute timeout
do
{
await Task.Delay(5000);
pollResp = await _textractClient.GetDocumentTextDetectionAsync(
new GetDocumentTextDetectionRequest { JobId = jobId });
if (++pollAttempts >= maxAttempts)
throw new TimeoutException("Textract job timed out after 10 minutes");
} while (pollResp.JobStatus == JobStatus.IN_PROGRESS);
await _s3Client.DeleteObjectAsync(s3Bucket, key); // cleanup regardless of outcome
if (pollResp.JobStatus != JobStatus.SUCCEEDED)
throw new Exception($"Textract job status: {pollResp.JobStatus} — {pollResp.StatusMessage}");
// Return extracted text — caller must use a separate library to produce searchable PDF
return string.Join("\n", pollResp.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
// Caller still needs: iTextSharp, PdfSharp, or similar to embed text layer
}
Enfoque IronOCR:
//IronOCR reads the PDF and produces a searchable PDF output directly
// No S3, no polling, no external PDF library needed
using IronOcr;
public void ConvertToSearchablePdf(string scannedPdfPath, string outputPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(scannedPdfPath);
var result = ocr.Read(input);
// Embed extracted text as searchable layer in one call
result.SaveAsSearchablePdf(outputPath);
Console.WriteLine($"Pages processed: {result.Pages.Length}");
Console.WriteLine($"Overall confidence: {result.Confidence:F1}%");
}
Se han eliminado la lógica de tiempo de espera de sondeo, el patrón de carga y limpieza de S3 y la dependencia de la biblioteca PDF externa. SaveAsSearchablePdf incrusta el texto reconocido directamente en el archivo de salida, haciendo que cada página escaneada sea completamente buscable por texto sin una segunda biblioteca. La guía en formato PDF, que permite realizar búsquedas, abarca la selección de páginas, las opciones de compresión y la inserción de metadatos. Para obtener información más general sobre los procesos de OCR de PDF, consulte la guía de entrada de PDF .
Sustitución del recorrido del grafo de bloques AnalyzeDocument por resultados de página estructurados
El AnalyzeDocument de Textract con TABLES y FORMS devuelve un List<Block> plano donde cada elemento — líneas, palabras, celdas, encabezados de tabla, claves de formulario, valores de formulario — es del mismo tipo Block distinguido solo por BlockType y vinculado por matrices de ID RelationshipType.CHILD. Reconstruir la estructura lógica de un documento requiere recorrer este grafo de relaciones.IronOCR devuelve una jerarquía tipificada: páginas, párrafos, líneas, palabras, cada una con acceso mediante coordenadas.
Enfoque de AWS Textract:
// AnalyzeDocument with TABLES returns blocks that must be graph-traversed
// to determine which words belong to which paragraphs
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<List<DocumentSection>> ExtractDocumentStructureAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
var response = await _textractClient.AnalyzeDocumentAsync(
new AnalyzeDocumentRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) },
FeatureTypes = new List<string> { "TABLES", "FORMS" }
// Note: AnalyzeDocument (TABLES) costs $0.015/page — 10x DetectDocumentText
});
var sections = new List<DocumentSection>();
// Filter LINE blocks for paragraph reconstruction
// LINE blocks are not grouped into paragraphs — must infer from Y proximity
var lineBlocks = response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.OrderBy(b => b.Geometry?.BoundingBox?.Top ?? 0)
.ToList();
// Group lines into pseudo-paragraphs by vertical gap heuristic
var currentParagraph = new List<Block>();
float? lastBottom = null;
const float paragraphGapThreshold = 0.02f; // 2% of page height
foreach (var line in lineBlocks)
{
var top = line.Geometry?.BoundingBox?.Top ?? 0;
var height = line.Geometry?.BoundingBox?.Height ?? 0;
if (lastBottom.HasValue && (top - lastBottom.Value) > paragraphGapThreshold)
{
if (currentParagraph.Any())
{
sections.Add(new DocumentSection
{
Text = string.Join(" ", currentParagraph.Select(b => b.Text)),
LineCount = currentParagraph.Count,
// Confidence: average of all LINE block confidence values
Confidence = (float)currentParagraph.Average(b => b.Confidence ?? 0)
});
currentParagraph.Clear();
}
}
currentParagraph.Add(line);
lastBottom = top + height;
}
if (currentParagraph.Any())
sections.Add(new DocumentSection
{
Text = string.Join(" ", currentParagraph.Select(b => b.Text)),
LineCount = currentParagraph.Count,
Confidence = (float)currentParagraph.Average(b => b.Confidence ?? 0)
});
return sections;
}
public class DocumentSection
{
public string Text { get; set; }
public int LineCount { get; set; }
public float Confidence { get; set; }
}
Enfoque IronOCR:
//IronOCR returns a typed hierarchy — paragraphs are first-class objects
// No block graph, no heuristic gap detection, no confidence averaging
using IronOcr;
public List<DocumentSection> ExtractDocumentStructure(string imagePath)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
var sections = new List<DocumentSection>();
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
sections.Add(new DocumentSection
{
Text = paragraph.Text,
LineCount = paragraph.Lines.Length,
// Coordinate access: exact pixel position on the page
LocationX = paragraph.X,
LocationY = paragraph.Y,
Width = paragraph.Width,
Height = paragraph.Height,
Confidence = paragraph.Confidence
});
}
}
return sections;
}
public class DocumentSection
{
public string Text { get; set; }
public int LineCount { get; set; }
public int LocationX { get; set; }
public int LocationY { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public double Confidence { get; set; }
}
Párrafos, líneas y palabras son directamente accesibles como colecciones tipadas con propiedades .X, .Y, .Width, .Height, y .Confidence. Sin normalización de cuadro delimitador, sin heurísticas de umbral de brecha, sin filtrado de tipo de bloque. La guía de lectura de resultados documenta toda la jerarquía OcrResult incluyendo el acceso a coordenadas a nivel de caracter. Para flujos de trabajo de facturas y recibos que dependen de esta estructura, consulte el tutorial de OCR de facturas .
Sustitución del procesamiento por lotes con limitación de velocidad por ejecución paralela sin restricciones
El procesamiento de imágenes por lotes es donde los límites de TPS de Textract producen el coste operativo más visible. La API sincrónica DetectDocumentText tiene límite de tasa; enviar más de 5 solicitudes por segundo produce ProvisionedThroughputExceededException. El manejo correcto por lotes requiere SemaphoreSlim limitación, lógica de reintentos con retroceso, y una cuidadosa contabilidad de las solicitudes en curso.IronOCR no tiene límite de velocidad; el rendimiento solo está limitado por los núcleos de CPU disponibles.
Enfoque de AWS Textract:
// Batch processing requires SemaphoreSlim throttle + retry on rate limit exceeded
using Amazon.Textract;
using Amazon.Textract.Model;
using System.Collections.Concurrent;
public async Task<Dictionary<string, string>> ProcessImageBatchAsync(
IReadOnlyList<string> imagePaths,
IProgress<(int Completed, int Total)> progress = null)
{
var results = new ConcurrentDictionary<string, string>();
var semaphore = new SemaphoreSlim(5); // 5 concurrent — Textract default TPS limit
var tasks = new List<Task>();
int completed = 0;
foreach (var path in imagePaths)
{
await semaphore.WaitAsync();
tasks.Add(Task.Run(async () =>
{
try
{
string text = null;
int retryCount = 0;
while (text == null && retryCount < 3)
{
try
{
var bytes = await File.ReadAllBytesAsync(path);
var response = await _textractClient.DetectDocumentTextAsync(
new DetectDocumentTextRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) }
});
text = string.Join("\n", response.Blocks
.Where(b => b.BlockType == BlockType.LINE)
.Select(b => b.Text));
}
catch (AmazonTextractException ex)
when (ex.ErrorCode == "ProvisionedThroughputExceededException")
{
// Exponential backoff on rate limit — blocks the entire thread
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, retryCount)));
retryCount++;
}
}
results[path] = text ?? string.Empty;
var done = Interlocked.Increment(ref completed);
progress?.Report((done, imagePaths.Count));
}
finally
{
semaphore.Release();
}
}));
}
await Task.WhenAll(tasks);
return results.ToDictionary(x => x.Key, x => x.Value);
}
Enfoque IronOCR:
// No rate limits, no semaphore, no retry logic — Parallel.ForEach saturates all cores
using IronOcr;
using System.Collections.Concurrent;
public Dictionary<string, string> ProcessImageBatch(
IReadOnlyList<string> imagePaths,
IProgress<(int Completed, int Total)> progress = null)
{
var ocr = new IronTesseract();
var results = new ConcurrentDictionary<string, string>();
int completed = 0;
Parallel.ForEach(imagePaths, path =>
{
var result = ocr.Read(path);
results[path] = result.Text;
var done = Interlocked.Increment(ref completed);
progress?.Report((done, imagePaths.Count));
});
return results.ToDictionary(x => x.Key, x => x.Value);
}
El SemaphoreSlim, el bucle de reintentos, el retroceso exponencial, y los bloques de captura ProvisionedThroughputExceededException se eliminan. IronTesseract es seguro para hilos y una única instancia compartida entre hilos maneja toda la carga paralela sin bloqueos. En una máquina con 8 núcleos, esto procesa 8 documentos simultáneamente sin ninguna configuración; en 32 núcleos, 32 simultáneamente. El ejemplo de multihilo muestra el patrón completo y la guía de optimización de velocidad abarca el ajuste de la configuración del motor para implementaciones centradas en el rendimiento.
Sustitución de la extracción de formularios de AnalyzeDocument por OCR de recorte basado en regiones
El AnalyzeDocument de Textract con FORMS devuelve bloques KEY_VALUE_SET vinculados por relaciones RelationshipType.VALUE. Reconstruir pares clave-valor de campos de formulario requiere filtrar por EntityTypes.Contains("KEY"), seguir IDs de relación VALUE, luego obtener bloques hijos WORD para ensamblar el texto. Para formularios de formato fijo donde las posiciones de los campos son conocidas, el CropRectangle de IronOCR extrae cada campo directamente sin recorrido de grafo de relaciones — y no cuesta nada por página.
Enfoque de AWS Textract:
// FORMS mode costs $0.05/page — the most expensive Textract tier
// Relationship graph traversal required to reconstruct key-value pairs
using Amazon.Textract;
using Amazon.Textract.Model;
public async Task<Dictionary<string, string>> ExtractInvoiceFieldsAsync(string imagePath)
{
var bytes = File.ReadAllBytes(imagePath);
// $0.05/page for FORMS analysis
var response = await _textractClient.AnalyzeDocumentAsync(
new AnalyzeDocumentRequest
{
Document = new Document { Bytes = new MemoryStream(bytes) },
FeatureTypes = new List<string> { "FORMS" }
});
var allBlocks = response.Blocks;
var fields = new Dictionary<string, string>();
// Find KEY blocks only
var keyBlocks = allBlocks.Where(b =>
b.BlockType == BlockType.KEY_VALUE_SET &&
b.EntityTypes?.Contains("KEY") == true);
foreach (var keyBlock in keyBlocks)
{
// Assemble key text from CHILD WORD blocks
var keyText = GetTextFromBlock(allBlocks, keyBlock);
// Find associated VALUE block via VALUE relationship
var valueBlockId = keyBlock.Relationships?
.FirstOrDefault(r => r.Type == RelationshipType.VALUE)?
.Ids.FirstOrDefault();
if (valueBlockId == null) continue;
var valueBlock = allBlocks.FirstOrDefault(b => b.Id == valueBlockId);
if (valueBlock == null) continue;
var valueText = GetTextFromBlock(allBlocks, valueBlock);
fields[keyText.TrimEnd(':')] = valueText;
}
return fields;
}
private string GetTextFromBlock(IList<Block> allBlocks, Block block)
{
if (block.Relationships == null) return string.Empty;
var childWordIds = block.Relationships
.Where(r => r.Type == RelationshipType.CHILD)
.SelectMany(r => r.Ids);
return string.Join(" ", allBlocks
.Where(b => b.BlockType == BlockType.WORD && childWordIds.Contains(b.Id))
.Select(b => b.Text));
}
Enfoque IronOCR:
// CropRectangle extracts each field zone directly — no relationship graph
// Works for any fixed-format form where field positions are known
using IronOcr;
// Define the field zones for your form template once
private static readonly Dictionary<string, CropRectangle> InvoiceFieldZones =
new Dictionary<string, CropRectangle>
{
{ "InvoiceNumber", new CropRectangle(450, 80, 300, 35) },
{ "InvoiceDate", new CropRectangle(450, 120, 300, 35) },
{ "VendorName", new CropRectangle(50, 160, 400, 35) },
{ "TotalAmount", new CropRectangle(450, 680, 200, 35) },
{ "DueDate", new CropRectangle(450, 720, 200, 35) }
};
public Dictionary<string, string> ExtractInvoiceFields(string imagePath)
{
var ocr = new IronTesseract();
var fields = new Dictionary<string, string>();
foreach (var zone in InvoiceFieldZones)
{
using var input = new OcrInput();
input.LoadImage(imagePath, zone.Value); // load only the defined region
var result = ocr.Read(input);
fields[zone.Key] = result.Text.Trim();
}
return fields;
}
La filtración de bloques KEY_VALUE_SET, el recorrido RelationshipType.VALUE, y el ayudante GetTextFromBlock se eliminan. Cada campo lee exactamente la región de píxeles que lo contiene, ni más ni menos. La guía de OCR basado en región cubre las coordenadas CropRectangle y cómo definir zonas a partir de mediciones de plantillas. Para flujos de trabajo específicos de facturas, la publicación del blog sobre OCR de facturas y el tutorial de escaneo de recibos muestran procesos completos de extracción de campos.
Referencia de mapeo de la API de Texto de AWS a IronOCR
| Texto de AWS | Equivalente a IronOCR |
|---|---|
AmazonTextractClient | IronTesseract |
AmazonS3Client | No es necesario |
DetectDocumentTextRequest | OcrInput con input.LoadImage() |
DetectDocumentTextResponse | OcrResult |
AnalyzeDocumentRequest (TABLES/FORMS) | OcrInput con CropRectangle opcional |
StartDocumentTextDetectionRequest | OcrInput con input.LoadPdf() — sincrónico, sin inicio de trabajo |
GetDocumentTextDetectionRequest | No aplica; los resultados son inmediatos. |
Document.Bytes | input.LoadImage(byteArray) o input.LoadImage(stream) |
S3Object (preparación de documentos) | Ruta de archivo o flujo: no se requiere área de preparación. |
Block (BlockType.LINE) | result.Lines (colección tipada) |
Block (BlockType.WORD) | result.Words (colección tipada) |
Block (BlockType.TABLE) | result.Words agrupado por proximidad de coordenadas |
Block (BlockType.KEY_VALUE_SET) | CropRectangle extracción de región por campo |
Block.Confidence | word.Confidence / result.Confidence |
Block.Geometry.BoundingBox | word.X, word.Y, word.Width, word.Height |
RelationshipType.CHILD recorrido | Acceso directo a propiedades en objetos de resultado tipados |
JobStatus.IN_PROGRESS | No aplicable — sin estado asíncrono |
JobStatus.SUCCEEDED | No aplicable — retorno sincrónico |
response.NextToken (paginación) | No aplicable — resultados no paginados |
ProvisionedThroughputExceededException | No aplicable — sin límites de TPS |
AccessDeniedException | No aplica: no hay cadena de credenciales |
input.LoadImageFrames() | Compatibilidad directa con TIFF multifotograma (sin equivalente en Textract) |
result.SaveAsSearchablePdf() | Salida PDF con capacidad de búsqueda (sin equivalente en Textract) |
Problemas comunes de migración y soluciones
Problema 1: Credenciales mal configuradas en nuevos entornos de implementación.
AWS Textract: El constructor AmazonTextractClient resuelve credenciales a partir de una cadena: variables de entorno, ~/.aws/credentials, perfil de instancia EC2, rol de tarea ECS. En un nuevo contenedor Docker o entorno CI, si ninguno de estos está configurado, el cliente se construye sin errores, pero cada llamada a la API lanza AmazonClientException: No credentials specified. El fallo es invisible hasta el momento de la ejecución.
Solución: Eliminar por completo la cadena de credenciales. Establezca la clave de licencia de IronOCR a partir de una variable de entorno que sea más sencilla de configurar y que no requiera permisos de IAM para su gestión:
// Single environment variable — no IAM, no rotation, no chain resolution
var key = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
if (string.IsNullOrEmpty(key))
throw new InvalidOperationException("IRONOCR_LICENSE environment variable is not set");
IronOcr.License.LicenseKey = key;
Problema 2: Puntos de llamadas asíncronas en todo el código fuente
AWS Textract: Debido a que todo el SDK es solo asincrónico (DetectDocumentTextAsync, StartDocumentTextDetectionAsync, GetDocumentTextDetectionAsync), todos los sitios de llamadas Textract propagan async Task<t> a través de la pila de llamadas. La migración a la API síncrona de IronOCR requiere una decisión en cada sitio.
Solución: Para los servicios de procesamiento en segundo plano y las acciones del controlador, las llamadas síncronas de IronOCR son seguras en los subprocesos en segundo plano. Envoltura en Task.Run solo cuando la cadena de llamadas existente debe permanecer asincrónica:
// If the call site must remain async (e.g., an ASP.NET controller action):
public async Task<IActionResult> ProcessUpload(IFormFile file)
{
using var ms = new MemoryStream();
await file.CopyToAsync(ms);
// Offload synchronous OCR to thread pool — keeps controller non-blocking
var text = await Task.Run(() =>
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(ms.ToArray());
return ocr.Read(input).Text;
});
return Ok(text);
}
Para procesadores por lotes y servicios en segundo plano que ya están funcionando en hilos no-UI, llama a ocr.Read() sincrónicamente — Task.Run agrega sobrecarga sin beneficio en esos contextos. La guía de OCR asíncrono abarca los patrones recomendados.
Problema 3: La lógica de limpieza del bucket de S3 está dispersa por todo el código.
AWS Textract: Cualquier código que se cargue en S3 para su procesamiento con Textract también debe eliminarse de S3 una vez que finalice el trabajo. Esta limpieza a menudo vive en bloques finally y a veces se omite en rutas de error, causando objetos huérfanos que acumulan costos de almacenamiento. Durante la migración, es fácil pasar por alto el código de limpieza de S3 porque no está relacionado conceptualmente con el OCR.
Solución: El patrón completo de carga y limpieza de S3 no tiene equivalente en IronOCR. Elimina todos los bloques de PutObjectAsync, DeleteObjectAsync y try/finally relacionados con S3 por completo.IronOCR lee directamente desde rutas o flujos locales:
// Before: upload → job → poll → extract → cleanup (50+ lines)
// After: two lines, no cleanup required
using var input = new OcrInput();
input.LoadPdf(localPdfPath);
var text = new IronTesseract().Read(input).Text;
Desactive el bucket de almacenamiento temporal de S3 después de la migración. La guía de entrada de PDF y la guía de entrada de flujo cubren todos los patrones de entrada que reemplazan el acceso a documentos por etapas de S3.
Problema 4: El código de recorrido de grafos de bloques no tiene un equivalente directo.
AWS Textract: El código que recorre Block.Relationships para reconstruir celdas de tablas, campos de formularios o agrupaciones de párrafos a partir de matrices de ID RelationshipType.CHILD es el código que más tiempo consume al migrar. No existe una sustitución uno a uno porque IronOCR devuelve colecciones tipificadas en lugar de un gráfico de relaciones.
Solución: Reemplace cada patrón de recorrido con la propiedad tipada apropiada. Las búsquedas de ID de relación se convierten en acceso directo a la propiedad:
// Before: filter by BlockType + traverse relationship IDs
var paragraphText = string.Join(" ", allBlocks
.Where(b => b.BlockType == BlockType.WORD &&
childIds.Contains(b.Id))
.Select(b => b.Text));
// After: direct paragraph text (no filtering, no relationship lookup)
foreach (var page in result.Pages)
foreach (var para in page.Paragraphs)
Console.WriteLine(para.Text); // already assembled
Para la reconstrucción de tablas que dependía de propiedades BlockType.CELL y ColumnIndex, utiliza agrupación de coordenadas de palabras en result.Words. La guía de lectura de tablas abarca el enfoque basado en coordenadas.
Problema 5: Bloques de captura de excepción ProvisionedThroughputExceededException
AWS Textract: Un código de procesamiento por lotes robusto captura AmazonTextractException con ErrorCode == "ProvisionedThroughputExceededException" e implementa reintentos con retroceso. Este código de error es un concepto específico de Textract que no tiene equivalente en IronOCR.
Solución: Elimina todos los bloques de captura ProvisionedThroughputExceededException.IronOCR no tiene límites de TPS. Reemplaza todo el patrón de lotes con limitación por Parallel.ForEach:
// Delete: SemaphoreSlim, retry loops, ProvisionedThroughputExceededException handlers
// Replace with:
Parallel.ForEach(imagePaths, path =>
{
var result = new IronTesseract().Read(path);
results[path] = result.Text;
});
Problema 6: Configuración de región integrada en los constructores del cliente
AWS Textract: new AmazonTextractClient(Amazon.RegionEndpoint.USEast1) codifica una región. Las implementaciones multirregionales requieren múltiples instancias de cliente o una selección dinámica de región. Cuando Textract añade compatibilidad con nuevas regiones, es necesario actualizar el código.
**Solución:**IronOCR no tiene concepto de región. Elimina todas las referencias Amazon.RegionEndpoint. El constructor IronTesseract no toma configuración de red — el procesamiento es local. En implementaciones multirregionales en la infraestructura de AWS, donde cada región ejecuta su propio sistema de cómputo, cada instancia de IronOCR procesa los documentos localmente dentro de ese límite de cómputo sin realizar llamadas entre regiones.
Lista de verificación de migración de AWS Textract
Pre-Migración
Auditar todo el uso de Textract y S3 SDK en el código fuente:
grep -rn "Amazon.Textract\|AmazonTextractClient\|DetectDocumentText" --include="*.cs" .
grep -rn "StartDocumentTextDetection\|GetDocumentTextDetection\|JobStatus" --include="*.cs" .
grep -rn "AmazonS3Client\|PutObjectRequest\|DeleteObjectAsync" --include="*.cs" .
grep -rn "BlockType\|RelationshipType\|KEY_VALUE_SET" --include="*.cs" .
grep -rn "ProvisionedThroughputExceededException\|AmazonTextractException" --include="*.cs" .
grep -rn "AWSSDK\|Amazon.RegionEndpoint" --include="*.csproj" .
Antes de escribir cualquier código de reemplazo, haga un inventario de lo siguiente:
- Cuenta todos los archivos que contienen
AmazonTextractClient— cada uno es un objetivo de reemplazo - Lista todos los sitios de llamadas
AnalyzeDocument— nota siTABLES,FORMS, o ambos son utilizados - Identifica todos los bucles de sondeo asincrónicos (
do { await Task.Delay } while JobStatus == IN_PROGRESS) - Encuentra todos los bloques de limpieza de S3
finally— estos se eliminan por completo, no se reemplazan - Cuenta todos los bloques de captura
ProvisionedThroughputExceededException— eliminados, no reemplazados - Nota toda la lógica de recorrido
BlockType.TABLE,BlockType.CELL,BlockType.KEY_VALUE_SET— cada uno necesita reemplazo de salida estructurada
Migración de código
- Elimina
AWSSDK.Textract,AWSSDK.S3, yAWSSDK.Corede todos los archivos.csproj - Ejecuta
dotnet add package IronOcren cada proyecto que realiza OCR - Añade
IronOcr.License.LicenseKey = ...una vez al inicio de la aplicación (Program.cso equivalente) - Elimina todas las declaraciones
using Amazon.Textract;,using Amazon.Textract.Model;,using Amazon.S3;,using Amazon.Runtime; - Añade
using IronOcr;a cada archivo que anteriormente importaba espacios de nombres de Textract - Reemplaza las llamadas al constructor
AmazonTextractClientconnew IronTesseract() - Reemplaza la instanciación
AmazonS3Clienty todas las llamadasPutObjectAsync/DeleteObjectAsynccon lecturas de ruta de archivo o flujo directas - Reemplaza todas las llamadas
DetectDocumentTextAsync:var text = ocr.Read(path).Text - Reemplaza todos los bucles de sondeo
StartDocumentTextDetectionAsync+ coninput.LoadPdf(path)+ocr.Read(input) - Reemplaza todas las canalizaciones TIFF de multi-marco (TIFF → PDF → S3 → asincrónico) con
input.LoadImageFrames(tiffPath) - Reemplaza las cadenas de filtros
BlockType.LINE/BlockType.WORDconresult.Lines/result.Words - Reemplaza el recorrido de relaciones
BlockType.KEY_VALUE_SETcon la extracción de zonasCropRectangle - Elimina todos los bloques de captura
ProvisionedThroughputExceededExceptiony limitadoresSemaphoreSlim - Reemplaza los patrones de lotes con limitación por
Parallel.ForEachusando una instancia compartidaIronTesseract - Elimine toda la configuración de variables de entorno de credenciales de AWS de los manifiestos de implementación y las canalizaciones de CI.
- Desactive los buckets de almacenamiento temporal de S3 una vez que se haya migrado y verificado todo el código de procesamiento.
Posmigración
- Verifica que la aplicación arranca limpiamente solo con
IRONOCR_LICENSEconfigurado y todas las variables de entorno de AWS eliminadas - Ejecutar el OCR en las mismas imágenes de muestra procesadas previamente por Textract y comparar el texto extraído para comprobar su precisión.
- Procesar directamente un PDF de varias páginas y verificar que todas las páginas se extraigan sin S3 ni sondeo asíncrono.
- Procesar un archivo TIFF multifotograma y verificar que el número de páginas coincida con la salida de Textract para el mismo documento.
- Prueba el procesamiento por lotes con un conjunto de más de 50 imágenes y confirma que no ocurre un equivalente
ProvisionedThroughputExceededException - Ejecute la aplicación en un contenedor Docker sin acceso a internet saliente y confirme que el OCR se completa correctamente.
- Verificar que el PDF con capacidad de búsqueda se abra correctamente en Adobe Reader y que permita la búsqueda de texto completo.
- Confirma que eliminar
AWS_ACCESS_KEY_IDyAWS_SECRET_ACCESS_KEYdel entorno de implementación no rompe nada - Pruebe cualquier flujo de trabajo de extracción de formularios o facturas comparándolo con la salida conocida de Textract para validar la precisión de los campos.
- Medir la latencia de procesamiento para un conjunto de documentos representativo y confirmar la eliminación del tiempo de espera mínimo de sondeo de 5 segundos.
Principales ventajas de migrar a IronOCR
Consolidación de la infraestructura en un único paquete NuGet . Tres paquetes del SDK de AWS, un bucket de S3 con políticas de ciclo de vida, roles de IAM en todos los entornos de implementación y procedimientos de rotación de credenciales de AWS se reemplazan por un solo paquete NuGet . El cambio de .csproj es dotnet remove package AWSSDK.Textract seguido por dotnet add package IronOcr. Todas las consecuencias derivadas de la gestión de credenciales de AWS (auditorías de seguridad, programas de rotación, higiene de variables de entorno, configuración de secretos de Docker) desaparecen con ella.
Coste total de propiedad predecible. El modelo de facturación por página genera costes variables que aumentan indefinidamente con el volumen de documentos. Tras migrar a IronOCR, el coste por página adicional procesada es cero, independientemente del volumen. Un equipo que procesa 200,000 páginas por mes a la tasa de AnalyzeDocument de Textract para tablas gasta $36,000 por año solo en OCR. La licencia IronOCR Enterprise , con un precio de 2999 dólares, permite recuperar ese coste en menos de 31 días gracias a la reducción de la facturación. Consulte la página de licencias de IronOCR para obtener información detallada sobre los niveles y los términos de redistribución de SaaS.
El procesamiento síncrono elimina la complejidad asíncrona de cinco fases. La carga a S3, el inicio del trabajo, el bucle de sondeo, la recopilación de resultados paginados y el bloque de limpieza final representan cinco modos de fallo independientes en la canalización de PDF de Textract. Tras la migración, un PDF se lee en dos líneas. El manejo de errores se reduce a un solo try/catch alrededor de la llamada ocr.Read(). La lógica de tiempo de espera de sondeo, el bucle do/while JobStatus == IN_PROGRESS, el acumulador de paginación nextToken — ninguno de estos conceptos existe en la base de código de IronOCR. La carga de mantenimiento disminuye en proporción al código que se eliminó.
Flexibilidad total en la implementación. Textract requiere acceso a internet en cada llamada de OCR.IronOCR solo requiere acceso a internet para descargar el paquete NuGet durante la instalación. Posteriormente, funciona en redes aisladas, contenedores Docker sin reglas de salida, servidores locales e instancias AWS EC2 sin acceso a Textract. El mismo archivo binario que se ejecuta en producción en Windows Server también se ejecuta en un contenedor Linux. 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 en contenedores que anteriormente tenían bloqueado el uso de Textract.
**La soberanía de los datos se logra mediante la arquitectura, no mediante la configuración.**IronOCR no tiene ningún modo de transmisión de red que se pueda desactivar; el procesamiento local es el único modo disponible. Los documentos procesados mediante IronOCR nunca salen del equipo anfitrión. Para las aplicaciones sanitarias que procesan información de salud protegida (PHI), las aplicaciones de defensa que procesan información controlada no clasificada (CUI) y las aplicaciones financieras que procesan imágenes de tarjetas de pago, esta es una solución de cumplimiento que no requiere negociación de acuerdos de asociación comercial (BAA), configuración de residencia de datos regional ni auditoría de las políticas de gestión de datos de Amazon. El ámbito de cumplimiento normativo es el límite de su propia infraestructura, que usted ya controla. La página del producto IronOCR proporciona un resumen completo de las características y la documentación de implementación para los equipos que realizan una revisión de cumplimiento.
Preprocesamiento configurable para el control de calidad de documentos. El preprocesamiento interno de Textract no es accesible ni configurable. Cuando un documento escaneado produce resultados deficientes, la única solución es aceptar el resultado o volver a escanearlo.IronOCR expone toda la canalización de preprocesamiento: Deskew(), DeNoise(), Contrast(), Binarize(), Sharpen(), Scale(), Dilate(), Erode(), y DeepCleanBackgroundNoise(). Los equipos que trasladaron documentos de Textract debido a los resultados poco fiables en escaneos difíciles pueden abordar la causa raíz directamente con filtros que coincidan con el defecto específico: rotación, ruido, bajo contraste o resolución insuficiente. La página de funciones de preprocesamiento y la guía de corrección de la calidad de la imagen documentan los filtros disponibles y sus casos de uso apropiados.
