Cómo instalar Tesseract OCR en Windows en c#
Esta guía acompaña a los desarrolladores .NET en el proceso de reemplazar Visión de Google Cloudcon IronOCR como motor OCR local. Cubre la eliminación de credenciales, el reemplazo del análisis de anotaciones Protobuf, la simplificación de anotaciones por lotes y el procesamiento de documentos de varias páginas: los cuatro cambios estructurales que representan la mayor parte del trabajo de migración.
¿Por qué migrar desde Visión de Google CloudOCR?
La decisión de migrar casi siempre comienza con una de dos constataciones: una auditoría de cumplimiento bloquea la transmisión de documentos en la nube, o la complejidad operativa de administrar las credenciales de GCP, los buckets de GCS, el sondeo asíncrono y la facturación por imagen se vuelve más costosa que el propio OCR.
Ciclo de vida de la clave JSON de la cuenta de servicio. Cada despliegue de su aplicación (máquina de desarrollo, canalización de CI/CD, servidor de pruebas, servidor de producción, contenedor Docker, pod de Kubernetes) requiere el mismo archivo de clave JSON de la cuenta de servicio. Este archivo contiene una clave privada RSA. Nunca debe entrar en el control de versiones, debe rotarse según un cronograma, debe estar protegido con permisos del sistema de archivos y debe actualizarse en todos los entornos simultáneamente cuando se produzca la rotación. Una clave comprometida otorga acceso a la API hasta que se revoca manualmente en la consola de GCP.IronOCR reemplaza toda esta superficie operativa con una única cadena de clave de licencia que se configura una sola vez al iniciar la aplicación.
Facturación por solicitud a gran escala. Con un coste de 1,50 $ por cada 1000 imágenes y 0,0015 $ por página PDF, los costes son imperceptibles durante el desarrollo y muy costosos en la producción. Un sistema de procesamiento de documentos que gestiona 200.000 páginas al mes cuesta 300 dólares al mes solo en tarifas de API, sin contar los cargos de almacenamiento de GCS ni los costes de salida de datos. Esos 300 dólares se repiten cada mes indefinidamente. La licencia perpetua de IronOCR convierte el OCR, que antes era un gasto operativo medido, en una partida de capital fijo cuyo funcionamiento no tiene coste alguno durante el segundo o tercer año.
Canalización asíncrona de GCS para archivos PDF. Visión de Google Cloudno acepta archivos PDF como entrada directa a la API. El pipeline completo requiere un segundo paquete de NuGet (Google.Cloud.Storage.V1), un bucket de GCS aprovisionado, una carga asíncrona, una llamada AsyncBatchAnnotateFilesAsync, un bucle de sondeo, análisis de salida JSON desde GCS, y un paso de limpieza. Ese pipeline abarca más de 50 líneas de código antes de que se haya extraído ningún texto.IronOCR lee un PDF en tres líneas, de forma síncrona y sin dependencias externas.
Concatenación de símbolos Protobuf. La respuesta DOCUMENT_TEXT_DETECTION almacena texto a nivel de símbolo dentro de una jerarquía de Protobuf de Páginas, Bloques, Párrafos, Palabras y Símbolos. Leer texto de párrafos requiere iterar cinco bucles anidados y llamar a .SelectMany(w => w.Symbols).Select(s => s.Text).IronOCR devuelve el texto de los párrafos como paragraph.Text — una propiedad de cadena tipada.
1,800 Solicitudes por Minuto Cuota Predeterminada. Las cargas de trabajo por lotes que superan la cuota predeterminada reciben respuestas StatusCode.ResourceExhausted que detienen el pipeline durante 60 segundos por cada exceso. Para aumentar la cuota, se requiere una solicitud a través de la consola de GCP y la aprobación de Google.IronOCR se procesa localmente a la velocidad de los núcleos de CPU disponibles; no hay cuotas que gestionar, ni aprobación que solicitar, ni lógica de reintento necesaria para limitar la velocidad.
No ofrece soporte sin conexión ni en entornos aislados. Visión de Google Cloudrequiere conexión a internet para acceder a los dispositivos de Google. Las redes aisladas, los centros de datos clasificados y los sistemas de control industrial no pueden utilizarlo en ningún nivel de complejidad arquitectónica.IronOCR funciona sin conectividad de red saliente después de la validación inicial de la licencia.
El problema fundamental
Google Cloud Vision requiere un archivo de clave JSON en el disco antes de que se ejecute la primera línea de código OCR:
// Google Cloud Vision: JSON key file deployed to every server before this line works
// GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
// Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create();
var image = Image.FromFile("document.jpg");
var response = _client.DetectText(image); // document leaves your infrastructure
string text = response[0].Description;
// Google Cloud Vision: JSON key file deployed to every server before this line works
// GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
// Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create();
var image = Image.FromFile("document.jpg");
var response = _client.DetectText(image); // document leaves your infrastructure
string text = response[0].Description;
' Google Cloud Vision: JSON key file deployed to every server before this line works
' GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
' Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create()
Dim image = Image.FromFile("document.jpg")
Dim response = _client.DetectText(image) ' document leaves your infrastructure
Dim text As String = response(0).Description
IronOCR comienza con una sola cadena de texto y se ejecuta completamente en la máquina local:
// IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
string text = new IronTesseract().Read("document.jpg").Text; // local, no cloud
// IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
string text = new IronTesseract().Read("document.jpg").Text; // local, no cloud
Imports IronOcr
' IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim text As String = New IronTesseract().Read("document.jpg").Text ' local, no cloud
IronOCR vs Visión de Google CloudOCR: Comparación de características
La tabla que aparece a continuación muestra directamente las características que pueden utilizar los equipos para elaborar el caso de negocio para la migración.
| Característica | OCR de Visión de Google Cloud | IronOCR |
|---|---|---|
| Ubicación de procesamiento | Google Cloud (remoto) | En el establecimiento (local) |
| Autenticación | Clave JSON de cuenta de servicio + variable de entorno | Cadena de clave de licencia |
| Entrada en PDF | Carga a GCS + API asíncrona | input.LoadPdf() direct |
| PDF protegido con contraseña | No soportado | LoadPdf(path, Password: "...") |
| Entrada TIFF de varias páginas | Limitado | input.LoadImageFrames() |
| Salida en PDF con capacidad de búsqueda | No disponible | result.SaveAsSearchablePdf() |
| Acceso a datos estructurados | Protobuf: Páginas > Bloques > Párrafos > Palabras > Símbolos | result.Paragraphs, result.Lines, result.Words (objetos tipados de .NET) |
| Propiedad del texto del párrafo | No — requiere concatenación de símbolos | paragraph.Text direct property |
| Puntuaciones de confianza | Por símbolo (requiere bucle) | result.Confidence, word.Confidence |
| preprocesamiento automático de imágenes | Ninguno (el aprendizaje automático lo gestiona) | Enderezar, Reducir ruido, Contraste, Binarizar, Enfocar |
| OCR basado en la región | No hay cultivos autóctonos | CropRectangle on OcrInput |
| Lectura de códigos de barras | Función de API independiente | ocr.Configuration.ReadBarCodes = true |
| límites de velocidad | 1800 solicitudes/minuto por defecto | Ninguno (limitado por la CPU) |
| Sin conexión / aislado de la red | No | Sí |
| Coste por documento | 1,50 dólares por cada 1.000 imágenes; $0.0015/página PDF | Ninguna (licencia perpetua) |
| Idiomas compatibles | ~50 | 125+ |
| Autorización FedRAMP | No autorizado | No aplicable (en las instalaciones) |
| Ruta de cumplimiento de HIPAA | Se requiere un Acuerdo de Asociado Comercial | No se realiza ningún manejo de datos por parte de terceros. |
| Compatibilidad con .NET Framework | .NET Standard 2.0 o superior | .NET Framework 4.6.2+ y .NET 5/6/7/8/9 |
| Se requieren paquetes NuGet | Google.Cloud.Vision.V1 + Google.Cloud.Storage.V1 |
IronOcr only |
| Modelo de precios | Facturación por consumo según solicitud | Perpetual ($999 Lite / $1,499 Plus / $2,999 Professional / $5,999 Unlimited) |
Inicio rápido: Migración de OCR de Visión de Google Clouda IronOCR
Paso 1: Sustituir el paquete NuGet
Elimine los paquetes de Google Cloud:
dotnet remove package Google.Cloud.Vision.V1
dotnet remove package Google.Cloud.Storage.V1
dotnet remove package Google.Cloud.Vision.V1
dotnet remove package Google.Cloud.Storage.V1
Instale IronOCR desde la página del paquete NuGet :
dotnet add package IronOcr
Paso 2: Actualizar los espacios de nombres
Reemplace los espacios de nombres de Google Cloud con el espacio de nombres IronOCR:
// Before (Google Cloud Vision)
using Google.Cloud.Vision.V1;
using Google.Cloud.Storage.V1;
using Google.Protobuf;
using Grpc.Core;
// After (IronOCR)
using IronOcr;
// Before (Google Cloud Vision)
using Google.Cloud.Vision.V1;
using Google.Cloud.Storage.V1;
using Google.Protobuf;
using Grpc.Core;
// After (IronOCR)
using IronOcr;
Imports IronOcr
Paso 3: Inicializar licencia
Agregue la inicialización de la licencia una vez al iniciar la aplicación, antes de que se cree cualquier instancia de IronTesseract:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
En producción, lea la clave desde una variable de entorno o un gestor de secretos:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable not set.");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable not set.");
Imports System
Imports IronOcr
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE"), Throw New InvalidOperationException("IRONOCR_LICENSE environment variable not set."))
Ejemplos de migración de código
Eliminación de la configuración de credenciales de la cuenta de servicio
La inicialización del cliente de Visión de Google Cloudparece una sola línea, pero requiere una amplia infraestructura previa. Cada desarrollador que se une al proyecto, cada entorno de despliegue y cada canalización de CI/CD necesita que la configuración completa de credenciales esté lista antes de que el constructor finalice sin generar errores.
Enfoque de Google Cloud Vision:
using Google.Cloud.Vision.V1;
// Prerequisites before this class can be instantiated:
// 1. GCP project created and Vision API enabled in GCP Console
// 2. Service account created with roles/cloudvision.user IAM role
// 3. JSON key file downloaded to every server that runs this code
// 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
// 5. JSON file excluded from source control via .gitignore
// 6. Key rotation schedule established (recommended: 90 days)
// 7. Separate credentials per environment (dev/staging/prod)
public class DocumentOcrService
{
private readonly ImageAnnotatorClient _client;
private readonly string _projectId;
public DocumentOcrService(string projectId)
{
_projectId = projectId;
// Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
_client = ImageAnnotatorClient.Create();
}
public string ReadDocument(string imagePath)
{
var image = Image.FromFile(imagePath);
var response = _client.DetectText(image);
return response.Count > 0 ? response[0].Description : string.Empty;
}
}
using Google.Cloud.Vision.V1;
// Prerequisites before this class can be instantiated:
// 1. GCP project created and Vision API enabled in GCP Console
// 2. Service account created with roles/cloudvision.user IAM role
// 3. JSON key file downloaded to every server that runs this code
// 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
// 5. JSON file excluded from source control via .gitignore
// 6. Key rotation schedule established (recommended: 90 days)
// 7. Separate credentials per environment (dev/staging/prod)
public class DocumentOcrService
{
private readonly ImageAnnotatorClient _client;
private readonly string _projectId;
public DocumentOcrService(string projectId)
{
_projectId = projectId;
// Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
_client = ImageAnnotatorClient.Create();
}
public string ReadDocument(string imagePath)
{
var image = Image.FromFile(imagePath);
var response = _client.DetectText(image);
return response.Count > 0 ? response[0].Description : string.Empty;
}
}
Imports Google.Cloud.Vision.V1
' Prerequisites before this class can be instantiated:
' 1. GCP project created and Vision API enabled in GCP Console
' 2. Service account created with roles/cloudvision.user IAM role
' 3. JSON key file downloaded to every server that runs this code
' 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
' 5. JSON file excluded from source control via .gitignore
' 6. Key rotation schedule established (recommended: 90 days)
' 7. Separate credentials per environment (dev/staging/prod)
Public Class DocumentOcrService
Private ReadOnly _client As ImageAnnotatorClient
Private ReadOnly _projectId As String
Public Sub New(projectId As String)
_projectId = projectId
' Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
_client = ImageAnnotatorClient.Create()
End Sub
Public Function ReadDocument(imagePath As String) As String
Dim image = Image.FromFile(imagePath)
Dim response = _client.DetectText(image)
Return If(response.Count > 0, response(0).Description, String.Empty)
End Function
End Class
Enfoque IronOCR:
using IronOcr;
// Prerequisites: set the license key once at app startup
// No JSON files, no environment variables beyond the key, no GCP Console configuration
// No key rotation, no IAM roles, no per-environment credential sets
public class DocumentOcrService
{
private readonly IronTesseract _ocr;
public DocumentOcrService()
{
// IronTesseract is ready immediately — no external validation required
_ocr = new IronTesseract();
}
public string ReadDocument(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}
using IronOcr;
// Prerequisites: set the license key once at app startup
// No JSON files, no environment variables beyond the key, no GCP Console configuration
// No key rotation, no IAM roles, no per-environment credential sets
public class DocumentOcrService
{
private readonly IronTesseract _ocr;
public DocumentOcrService()
{
// IronTesseract is ready immediately — no external validation required
_ocr = new IronTesseract();
}
public string ReadDocument(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}
Imports IronOcr
' Prerequisites: set the license key once at app startup
' No JSON files, no environment variables beyond the key, no GCP Console configuration
' No key rotation, no IAM roles, no per-environment credential sets
Public Class DocumentOcrService
Private ReadOnly _ocr As IronTesseract
Public Sub New()
' IronTesseract is ready immediately — no external validation required
_ocr = New IronTesseract()
End Sub
Public Function ReadDocument(imagePath As String) As String
Return _ocr.Read(imagePath).Text
End Function
End Class
La diferencia operativa es concreta: Visión de Google Cloudgenera cinco categorías de RpcException en tiempo de ejecución — PermissionDenied, ResourceExhausted, Unavailable, DeadlineExceeded, y Unauthenticated — cada uno representando un modo de fallo de infraestructura diferente. Los modos de fallo de IronOCR son IOException (archivo no encontrado o bloqueado) y OcrException (fallo de procesamiento). Consulte la guía de configuración de IronTesseract para conocer las opciones de configuración y la página del producto IronOCR para obtener detalles sobre la licencia.
Sustitución de las solicitudes de anotación por lotes con múltiples tipos de características
Google Cloud Vision admite agrupar múltiples imágenes en un solo BatchAnnotateImagesRequest, cada imagen configurada con una lista de tipos Feature. Este patrón se utiliza cuando una sola llamada necesita recoger tanto los resultados de TEXT_DETECTION como DOCUMENT_TEXT_DETECTION, o al enviar muchas imágenes para minimizar la sobrecarga de ida y vuelta. La respuesta de Protobuf requiere hacer coincidir cada AnnotateImageResponse con su solicitud original por índice.
Enfoque de Google Cloud Vision:
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public class BatchAnnotationService
{
private readonly ImageAnnotatorClient _client;
public BatchAnnotationService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> BatchAnnotateImages(string[] imagePaths)
{
// Build one request per image with TEXT_DETECTION feature
var requests = imagePaths.Select(path => new AnnotateImageRequest
{
Image = Image.FromFile(path),
Features =
{
new Característica { Type = Feature.Types.Type.TextDetection },
new Característica { Type = Feature.Types.Type.DocumentTextDetection }
}
}).ToList();
// Single round-trip for all images in the batch
var batchResponse = _client.BatchAnnotateImages(requests);
// Match responses back to requests by index
var results = new List<string>();
for (int i = 0; i < batchResponse.Responses.Count; i++)
{
var response = batchResponse.Responses[i];
if (response.Error != null)
{
// Per-item error in batch — must handle individually
results.Add($"Error on {imagePaths[i]}: {response.Error.Message}");
continue;
}
// Prefer DOCUMENT_TEXT_DETECTION full text if available
var fullText = response.FullTextAnnotation?.Text
?? response.TextAnnotations.FirstOrDefault()?.Description
?? string.Empty;
results.Add(fullText);
}
return results;
}
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public class BatchAnnotationService
{
private readonly ImageAnnotatorClient _client;
public BatchAnnotationService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> BatchAnnotateImages(string[] imagePaths)
{
// Build one request per image with TEXT_DETECTION feature
var requests = imagePaths.Select(path => new AnnotateImageRequest
{
Image = Image.FromFile(path),
Features =
{
new Característica { Type = Feature.Types.Type.TextDetection },
new Característica { Type = Feature.Types.Type.DocumentTextDetection }
}
}).ToList();
// Single round-trip for all images in the batch
var batchResponse = _client.BatchAnnotateImages(requests);
// Match responses back to requests by index
var results = new List<string>();
for (int i = 0; i < batchResponse.Responses.Count; i++)
{
var response = batchResponse.Responses[i];
if (response.Error != null)
{
// Per-item error in batch — must handle individually
results.Add($"Error on {imagePaths[i]}: {response.Error.Message}");
continue;
}
// Prefer DOCUMENT_TEXT_DETECTION full text if available
var fullText = response.FullTextAnnotation?.Text
?? response.TextAnnotations.FirstOrDefault()?.Description
?? string.Empty;
results.Add(fullText);
}
return results;
}
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Linq
Public Class BatchAnnotationService
Private ReadOnly _client As ImageAnnotatorClient
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Function BatchAnnotateImages(imagePaths As String()) As List(Of String)
' Build one request per image with TEXT_DETECTION feature
Dim requests = imagePaths.Select(Function(path) New AnnotateImageRequest With {
.Image = Image.FromFile(path),
.Features = {
New Feature With {.Type = Feature.Types.Type.TextDetection},
New Feature With {.Type = Feature.Types.Type.DocumentTextDetection}
}
}).ToList()
' Single round-trip for all images in the batch
Dim batchResponse = _client.BatchAnnotateImages(requests)
' Match responses back to requests by index
Dim results = New List(Of String)()
For i As Integer = 0 To batchResponse.Responses.Count - 1
Dim response = batchResponse.Responses(i)
If response.Error IsNot Nothing Then
' Per-item error in batch — must handle individually
results.Add($"Error on {imagePaths(i)}: {response.Error.Message}")
Continue For
End If
' Prefer DOCUMENT_TEXT_DETECTION full text if available
Dim fullText = If(response.FullTextAnnotation?.Text, response.TextAnnotations.FirstOrDefault()?.Description, String.Empty)
results.Add(fullText)
Next
Return results
End Function
End Class
Enfoque IronOCR:
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
public class BatchAnnotationService
{
public List<string> BatchAnnotateImages(string[] imagePaths)
{
var results = new ConcurrentDictionary<int, string>();
// Parallel processing — no batch size limit, no network round-trips
Parallel.For(0, imagePaths.Length, i =>
{
var ocr = new IronTesseract(); // thread-safe: one instance per thread
results[i] = ocr.Read(imagePaths[i]).Text;
});
// Reconstruct in original order
return Enumerable.Range(0, imagePaths.Length)
.Select(i => results[i])
.ToList();
}
public OcrResult BatchAsDocument(string[] imagePaths)
{
// Load all images into a single OcrInput for combined document output
using var input = new OcrInput();
foreach (var path in imagePaths)
input.LoadImage(path);
return new IronTesseract().Read(input);
}
}
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
public class BatchAnnotationService
{
public List<string> BatchAnnotateImages(string[] imagePaths)
{
var results = new ConcurrentDictionary<int, string>();
// Parallel processing — no batch size limit, no network round-trips
Parallel.For(0, imagePaths.Length, i =>
{
var ocr = new IronTesseract(); // thread-safe: one instance per thread
results[i] = ocr.Read(imagePaths[i]).Text;
});
// Reconstruct in original order
return Enumerable.Range(0, imagePaths.Length)
.Select(i => results[i])
.ToList();
}
public OcrResult BatchAsDocument(string[] imagePaths)
{
// Load all images into a single OcrInput for combined document output
using var input = new OcrInput();
foreach (var path in imagePaths)
input.LoadImage(path);
return new IronTesseract().Read(input);
}
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading.Tasks
Public Class BatchAnnotationService
Public Function BatchAnnotateImages(imagePaths As String()) As List(Of String)
Dim results = New ConcurrentDictionary(Of Integer, String)()
' Parallel processing — no batch size limit, no network round-trips
Parallel.For(0, imagePaths.Length, Sub(i)
Dim ocr = New IronTesseract() ' thread-safe: one instance per thread
results(i) = ocr.Read(imagePaths(i)).Text
End Sub)
' Reconstruct in original order
Return Enumerable.Range(0, imagePaths.Length) _
.Select(Function(i) results(i)) _
.ToList()
End Function
Public Function BatchAsDocument(imagePaths As String()) As OcrResult
' Load all images into a single OcrInput for combined document output
Using input As New OcrInput()
For Each path In imagePaths
input.LoadImage(path)
Next
Return New IronTesseract().Read(input)
End Using
End Function
End Class
IronOCR ejecuta el procesamiento por lotes en paralelo en todos los núcleos de la CPU sin sobrecarga de red. No hay límite BatchSize, no hay coincidencia de índice de respuesta, y no hay manejo de errores por artículo para fallos de red o credenciales. Para las cargas de trabajo que combinan múltiples imágenes en un solo documento lógico — por ejemplo, formularios de varias páginas escaneados enviados como JPEG individuales — BatchAsDocument carga todas las imágenes en un OcrInput y devuelve un OcrResult unificado con result.Pages indexado a cada imagen de entrada. El ejemplo de multihilo muestra pruebas de rendimiento para el procesamiento paralelo.
Migración de la extracción de anotaciones a nivel de palabra de Protobuf
Los datos de confianza y de cuadro delimitador a nivel de palabra de Visión de Google Cloudrequieren navegar por toda la jerarquía de Protobuf: Páginas, luego Bloques, luego Párrafos, luego Palabras y luego Símbolos. Extraer texto de palabras requiere concatenar Símbolos de cada Palabra — el objeto Word no tiene una propiedad .Text directa. Las coordenadas de los cuadros delimitadores se almacenan como un BoundingPoly con una lista de Vertices en lugar de como campos discretos de X, Y, Ancho, Altura.
Enfoque de Google Cloud Vision:
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, float Confidence);
public class WordLevelExtractor
{
private readonly ImageAnnotatorClient _client;
public WordLevelExtractor()
{
_client = ImageAnnotatorClient.Create();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var image = Image.FromFile(imagePath);
var annotation = _client.DetectDocumentText(image);
var words = new List<WordAnnotation>();
// Navigate: Pages -> Blocks -> Paragraphs -> Words
foreach (var page in annotation.Pages)
{
foreach (var block in page.Blocks)
{
foreach (var paragraph in block.Paragraphs)
{
foreach (var word in paragraph.Words)
{
// Word.Text does not exist — must concatenate Symbols
var text = string.Concat(word.Symbols.Select(s => s.Text));
// BoundingPoly has Vertices, not X/Y/Width/Height
var vertices = word.BoundingBox.Vertices;
int x = vertices[0].X;
int y = vertices[0].Y;
int width = vertices.Count > 1 ? vertices[1].X - vertices[0].X : 0;
int height = vertices.Count > 2 ? vertices[2].Y - vertices[0].Y : 0;
words.Add(new WordAnnotation(text, x, y, width, height, word.Confidence));
}
}
}
}
return words;
}
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, float Confidence);
public class WordLevelExtractor
{
private readonly ImageAnnotatorClient _client;
public WordLevelExtractor()
{
_client = ImageAnnotatorClient.Create();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var image = Image.FromFile(imagePath);
var annotation = _client.DetectDocumentText(image);
var words = new List<WordAnnotation>();
// Navigate: Pages -> Blocks -> Paragraphs -> Words
foreach (var page in annotation.Pages)
{
foreach (var block in page.Blocks)
{
foreach (var paragraph in block.Paragraphs)
{
foreach (var word in paragraph.Words)
{
// Word.Text does not exist — must concatenate Symbols
var text = string.Concat(word.Symbols.Select(s => s.Text));
// BoundingPoly has Vertices, not X/Y/Width/Height
var vertices = word.BoundingBox.Vertices;
int x = vertices[0].X;
int y = vertices[0].Y;
int width = vertices.Count > 1 ? vertices[1].X - vertices[0].X : 0;
int height = vertices.Count > 2 ? vertices[2].Y - vertices[0].Y : 0;
words.Add(new WordAnnotation(text, x, y, width, height, word.Confidence));
}
}
}
}
return words;
}
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Linq
Public Class WordAnnotation
Public Property Text As String
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
Public Property Confidence As Single
Public Sub New(text As String, x As Integer, y As Integer, width As Integer, height As Integer, confidence As Single)
Me.Text = text
Me.X = x
Me.Y = y
Me.Width = width
Me.Height = height
Me.Confidence = confidence
End Sub
End Class
Public Class WordLevelExtractor
Private ReadOnly _client As ImageAnnotatorClient
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Function ExtractWordAnnotations(imagePath As String) As List(Of WordAnnotation)
Dim image = Image.FromFile(imagePath)
Dim annotation = _client.DetectDocumentText(image)
Dim words As New List(Of WordAnnotation)()
' Navigate: Pages -> Blocks -> Paragraphs -> Words
For Each page In annotation.Pages
For Each block In page.Blocks
For Each paragraph In block.Paragraphs
For Each word In paragraph.Words
' Word.Text does not exist — must concatenate Symbols
Dim text = String.Concat(word.Symbols.Select(Function(s) s.Text))
' BoundingPoly has Vertices, not X/Y/Width/Height
Dim vertices = word.BoundingBox.Vertices
Dim x As Integer = vertices(0).X
Dim y As Integer = vertices(0).Y
Dim width As Integer = If(vertices.Count > 1, vertices(1).X - vertices(0).X, 0)
Dim height As Integer = If(vertices.Count > 2, vertices(2).Y - vertices(0).Y, 0)
words.Add(New WordAnnotation(text, x, y, width, height, word.Confidence))
Next
Next
Next
Next
Return words
End Function
End Class
Enfoque IronOCR:
using IronOcr;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, double Confidence);
public class WordLevelExtractor
{
private readonly IronTesseract _ocr;
public WordLevelExtractor()
{
_ocr = new IronTesseract();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var result = _ocr.Read(imagePath);
// Words are a flat collection — no hierarchy traversal, no symbol concatenation
return result.Words.Select(w => new WordAnnotation(
Text: w.Text, // direct string property
X: w.X,
Y: w.Y,
Width: w.Width,
Height: w.Height,
Confidence: w.Confidence // double, no conversion needed
)).ToList();
}
}
using IronOcr;
using System.Collections.Generic;
public record WordAnnotation(string Text, int X, int Y, int Width, int Height, double Confidence);
public class WordLevelExtractor
{
private readonly IronTesseract _ocr;
public WordLevelExtractor()
{
_ocr = new IronTesseract();
}
public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
{
var result = _ocr.Read(imagePath);
// Words are a flat collection — no hierarchy traversal, no symbol concatenation
return result.Words.Select(w => new WordAnnotation(
Text: w.Text, // direct string property
X: w.X,
Y: w.Y,
Width: w.Width,
Height: w.Height,
Confidence: w.Confidence // double, no conversion needed
)).ToList();
}
}
Imports IronOcr
Imports System.Collections.Generic
Public Class WordAnnotation
Public Property Text As String
Public Property X As Integer
Public Property Y As Integer
Public Property Width As Integer
Public Property Height As Integer
Public Property Confidence As Double
Public Sub New(text As String, x As Integer, y As Integer, width As Integer, height As Integer, confidence As Double)
Me.Text = text
Me.X = x
Me.Y = y
Me.Width = width
Me.Height = height
Me.Confidence = confidence
End Sub
End Class
Public Class WordLevelExtractor
Private ReadOnly _ocr As IronTesseract
Public Sub New()
_ocr = New IronTesseract()
End Sub
Public Function ExtractWordAnnotations(imagePath As String) As List(Of WordAnnotation)
Dim result = _ocr.Read(imagePath)
' Words are a flat collection — no hierarchy traversal, no symbol concatenation
Return result.Words.Select(Function(w) New WordAnnotation(
Text:=w.Text, ' direct string property
X:=w.X,
Y:=w.Y,
Width:=w.Width,
Height:=w.Height,
Confidence:=w.Confidence ' double, no conversion needed
)).ToList()
End Function
End Class
El bucle de concatenación de símbolos en la versión de Visión de Google Cloudno es una decisión de diseño, sino un requisito del esquema Protobuf. Word.Text no es una propiedad en el objeto de respuesta. Cada equipo que usa la API a nivel de palabra escribe un bucle equivalente. La colección OcrResult.Words de IronOCR expone Text, X, Y, Width, Height, y Confidence como propiedades de primera clase. La guía de resultados de lectura documenta el conjunto completo de propiedades disponibles en cada nivel de granularidad.
Procesamiento de archivos TIFF de varias páginas para generar un archivo PDF con capacidad de búsqueda.
Google Cloud Vision trata los archivos TIFF de varias páginas como una serie secuencial de imágenes, cada una de las cuales requiere una llamada a la API independiente. No existe una única llamada a la API que acepte un archivo TIFF y devuelva una salida estructurada para todos los fotogramas. Para generar un PDF con capacidad de búsqueda a partir de los resultados de Google Cloud Vision, se necesita una biblioteca de generación de PDF independiente; la API solo devuelve texto.
Enfoque de Google Cloud Vision:
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
using System.Drawing; // for TIFF frame extraction
using System.Drawing.Imaging;
public class TiffProcessingService
{
private readonly ImageAnnotatorClient _client;
public TiffProcessingService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> ProcessMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
// Load TIFF and extract frames manually using System.Drawing
using var tiff = System.Drawing.Image.FromFile(tiffPath);
var frameDimension = new FrameDimension(tiff.FrameDimensionsList[0]);
int frameCount = tiff.GetFrameCount(frameDimension);
for (int i = 0; i < frameCount; i++)
{
tiff.SelectActiveFrame(frameDimension, i);
// Save each frame to a temp file — Vision API does not accept TIFF frames directly
var tempPath = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg");
tiff.Save(tempPath, ImageFormat.Jpeg);
// One API call per frame — each call = one unit of quota
var visionImage = Google.Cloud.Vision.V1.Image.FromFile(tempPath);
var response = _client.DetectText(visionImage);
pageTexts.Add(response.FirstOrDefault()?.Description ?? string.Empty);
File.Delete(tempPath);
}
// Producing a searchable PDF requires a separate library (e.g., iTextSharp)
// Visión de Google Cloudhas no PDF output capability
return pageTexts;
}
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
using System.Drawing; // for TIFF frame extraction
using System.Drawing.Imaging;
public class TiffProcessingService
{
private readonly ImageAnnotatorClient _client;
public TiffProcessingService()
{
_client = ImageAnnotatorClient.Create();
}
public List<string> ProcessMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
// Load TIFF and extract frames manually using System.Drawing
using var tiff = System.Drawing.Image.FromFile(tiffPath);
var frameDimension = new FrameDimension(tiff.FrameDimensionsList[0]);
int frameCount = tiff.GetFrameCount(frameDimension);
for (int i = 0; i < frameCount; i++)
{
tiff.SelectActiveFrame(frameDimension, i);
// Save each frame to a temp file — Vision API does not accept TIFF frames directly
var tempPath = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg");
tiff.Save(tempPath, ImageFormat.Jpeg);
// One API call per frame — each call = one unit of quota
var visionImage = Google.Cloud.Vision.V1.Image.FromFile(tempPath);
var response = _client.DetectText(visionImage);
pageTexts.Add(response.FirstOrDefault()?.Description ?? string.Empty);
File.Delete(tempPath);
}
// Producing a searchable PDF requires a separate library (e.g., iTextSharp)
// Visión de Google Cloudhas no PDF output capability
return pageTexts;
}
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Drawing ' for TIFF frame extraction
Imports System.Drawing.Imaging
Imports System.IO
Public Class TiffProcessingService
Private ReadOnly _client As ImageAnnotatorClient
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Function ProcessMultiPageTiff(tiffPath As String) As List(Of String)
Dim pageTexts As New List(Of String)()
' Load TIFF and extract frames manually using System.Drawing
Using tiff As System.Drawing.Image = System.Drawing.Image.FromFile(tiffPath)
Dim frameDimension As New FrameDimension(tiff.FrameDimensionsList(0))
Dim frameCount As Integer = tiff.GetFrameCount(frameDimension)
For i As Integer = 0 To frameCount - 1
tiff.SelectActiveFrame(frameDimension, i)
' Save each frame to a temp file — Vision API does not accept TIFF frames directly
Dim tempPath As String = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg")
tiff.Save(tempPath, ImageFormat.Jpeg)
' One API call per frame — each call = one unit of quota
Dim visionImage As Google.Cloud.Vision.V1.Image = Google.Cloud.Vision.V1.Image.FromFile(tempPath)
Dim response = _client.DetectText(visionImage)
pageTexts.Add(If(response.FirstOrDefault()?.Description, String.Empty))
File.Delete(tempPath)
Next
End Using
' Producing a searchable PDF requires a separate library (e.g., iTextSharp)
' Visión de Google Cloudhas no PDF output capability
Return pageTexts
End Function
End Class
Enfoque IronOCR:
using IronOcr;
public class TiffProcessingService
{
private readonly IronTesseract _ocr;
public TiffProcessingService()
{
_ocr = new IronTesseract();
}
public string ProcessMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
// LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
return result.Text;
}
public void ProcessMultiPageTiffToSearchablePdf(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
// Visión de Google Cloudhas no equivalent — this single call produces a searchable PDF
result.SaveAsSearchablePdf(outputPdfPath);
}
public void ProcessLowQualityTiff(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
// Preprocessing before OCR improves accuracy on degraded scans
input.Deskew();
input.DeNoise();
input.Contrast();
var result = _ocr.Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}
}
using IronOcr;
public class TiffProcessingService
{
private readonly IronTesseract _ocr;
public TiffProcessingService()
{
_ocr = new IronTesseract();
}
public string ProcessMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
// LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
return result.Text;
}
public void ProcessMultiPageTiffToSearchablePdf(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
var result = _ocr.Read(input);
// Visión de Google Cloudhas no equivalent — this single call produces a searchable PDF
result.SaveAsSearchablePdf(outputPdfPath);
}
public void ProcessLowQualityTiff(string tiffPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);
// Preprocessing before OCR improves accuracy on degraded scans
input.Deskew();
input.DeNoise();
input.Contrast();
var result = _ocr.Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}
}
Imports IronOcr
Public Class TiffProcessingService
Private ReadOnly _ocr As IronTesseract
Public Sub New()
_ocr = New IronTesseract()
End Sub
Public Function ProcessMultiPageTiff(tiffPath As String) As String
Using input As New OcrInput()
' LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
input.LoadImageFrames(tiffPath)
Dim result = _ocr.Read(input)
Return result.Text
End Using
End Function
Public Sub ProcessMultiPageTiffToSearchablePdf(tiffPath As String, outputPdfPath As String)
Using input As New OcrInput()
input.LoadImageFrames(tiffPath)
Dim result = _ocr.Read(input)
' Visión de Google Cloudhas no equivalent — this single call produces a searchable PDF
result.SaveAsSearchablePdf(outputPdfPath)
End Using
End Sub
Public Sub ProcessLowQualityTiff(tiffPath As String, outputPdfPath As String)
Using input As New OcrInput()
input.LoadImageFrames(tiffPath)
' Preprocessing before OCR improves accuracy on degraded scans
input.Deskew()
input.DeNoise()
input.Contrast()
Dim result = _ocr.Read(input)
result.SaveAsSearchablePdf(outputPdfPath)
End Using
End Sub
End Class
La versión de Visión de Google Cloudrequiere System.Drawing para la extracción de fotogramas, archivos JPEG temporales que se escriben en el disco, una llamada a la API por fotograma (consumiendo una cuota proporcional al número de fotogramas) y una biblioteca PDF independiente para producir cualquier salida que no sea texto plano.IronOCR maneja TIFF de múltiples marcos de manera nativa a través de LoadImageFrames, procesa todos los marcos en una sola llamada Read, y produce una salida de PDF buscable a través de SaveAsSearchablePdf. Para documentos TIFF de archivo escaneado, el pipeline de preprocesamiento en ProcessLowQualityTiff aborda los problemas de calidad más comunes en una sola pasada. Consulte la documentación sobre entrada TIFF y GIF y salida PDF con capacidad de búsqueda para obtener la API completa.
Migración por lotes con límite de velocidad y seguimiento del progreso
A escala de producción, la cuota predeterminada de 1800 solicitudes por minuto de Visión de Google Cloudrequiere una lógica de limitación o de reintentos con retroceso exponencial. Procesar 5.000 documentos en una sola operación nocturna superará la cuota varias veces. Cada vez que se supera la cuota, el sistema se bloquea durante una espera obligatoria de 60 segundos.IronOCR no tiene límite de velocidad; la canalización solo está limitada por los núcleos de la CPU y los hilos disponibles.
Enfoque de Google Cloud Vision:
using Google.Cloud.Vision.V1;
using Grpc.Core;
using System.Collections.Generic;
public class ThrottledBatchProcessor
{
private readonly ImageAnnotatorClient _client;
private const int MaxRequestsPerMinute = 1800;
private const int RetryDelayMs = 60_000;
public ThrottledBatchProcessor()
{
_client = ImageAnnotatorClient.Create();
}
public async Task<Dictionary<string, string>> ProcessWithThrottlingAsync(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new Dictionary<string, string>();
int completed = 0;
foreach (var path in imagePaths)
{
bool succeeded = false;
while (!succeeded)
{
try
{
var image = Google.Cloud.Vision.V1.Image.FromFile(path);
var response = _client.DetectText(image);
results[path] = response.FirstOrDefault()?.Description ?? string.Empty;
succeeded = true;
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.ResourceExhausted)
{
// Rate limit exceeded — wait and retry
await Task.Delay(RetryDelayMs);
}
}
progress.Report((++completed, imagePaths.Length));
}
return results;
}
}
using Google.Cloud.Vision.V1;
using Grpc.Core;
using System.Collections.Generic;
public class ThrottledBatchProcessor
{
private readonly ImageAnnotatorClient _client;
private const int MaxRequestsPerMinute = 1800;
private const int RetryDelayMs = 60_000;
public ThrottledBatchProcessor()
{
_client = ImageAnnotatorClient.Create();
}
public async Task<Dictionary<string, string>> ProcessWithThrottlingAsync(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new Dictionary<string, string>();
int completed = 0;
foreach (var path in imagePaths)
{
bool succeeded = false;
while (!succeeded)
{
try
{
var image = Google.Cloud.Vision.V1.Image.FromFile(path);
var response = _client.DetectText(image);
results[path] = response.FirstOrDefault()?.Description ?? string.Empty;
succeeded = true;
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.ResourceExhausted)
{
// Rate limit exceeded — wait and retry
await Task.Delay(RetryDelayMs);
}
}
progress.Report((++completed, imagePaths.Length));
}
return results;
}
}
Imports Google.Cloud.Vision.V1
Imports Grpc.Core
Imports System.Collections.Generic
Imports System.Threading.Tasks
Public Class ThrottledBatchProcessor
Private ReadOnly _client As ImageAnnotatorClient
Private Const MaxRequestsPerMinute As Integer = 1800
Private Const RetryDelayMs As Integer = 60000
Public Sub New()
_client = ImageAnnotatorClient.Create()
End Sub
Public Async Function ProcessWithThrottlingAsync(
imagePaths As String(),
progress As IProgress(Of (completed As Integer, total As Integer))) As Task(Of Dictionary(Of String, String))
Dim results As New Dictionary(Of String, String)()
Dim completed As Integer = 0
For Each path In imagePaths
Dim succeeded As Boolean = False
While Not succeeded
Try
Dim image = Google.Cloud.Vision.V1.Image.FromFile(path)
Dim response = _client.DetectText(image)
results(path) = If(response.FirstOrDefault()?.Description, String.Empty)
succeeded = True
Catch ex As RpcException When ex.StatusCode = StatusCode.ResourceExhausted
' Rate limit exceeded — wait and retry
Await Task.Delay(RetryDelayMs)
End Try
End While
progress.Report((Threading.Interlocked.Increment(completed), imagePaths.Length))
Next
Return results
End Function
End Class
Enfoque IronOCR:
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class BatchProcessor
{
public Dictionary<string, string> ProcessBatch(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new ConcurrentDictionary<string, string>();
int completed = 0;
// Sin límites de tarifa — full parallelism, no retry logic needed
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
results[imagePath] = ocr.Read(imagePath).Text;
progress.Report((Interlocked.Increment(ref completed), imagePaths.Length));
});
return new Dictionary<string, string>(results);
}
public void ProcessBatchToSearchablePdfs(
string[] imagePaths,
string outputDirectory)
{
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
var outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(imagePath) + "-searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
});
}
}
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class BatchProcessor
{
public Dictionary<string, string> ProcessBatch(
string[] imagePaths,
IProgress<(int completed, int total)> progress)
{
var results = new ConcurrentDictionary<string, string>();
int completed = 0;
// Sin límites de tarifa — full parallelism, no retry logic needed
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
results[imagePath] = ocr.Read(imagePath).Text;
progress.Report((Interlocked.Increment(ref completed), imagePaths.Length));
});
return new Dictionary<string, string>(results);
}
public void ProcessBatchToSearchablePdfs(
string[] imagePaths,
string outputDirectory)
{
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
var outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(imagePath) + "-searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
});
}
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
Imports System.IO
Public Class BatchProcessor
Public Function ProcessBatch(imagePaths As String(), progress As IProgress(Of (completed As Integer, total As Integer))) As Dictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
Dim completed As Integer = 0
' Sin límites de tarifa — full parallelism, no retry logic needed
Parallel.ForEach(imagePaths, Sub(imagePath)
Dim ocr As New IronTesseract()
results(imagePath) = ocr.Read(imagePath).Text
progress.Report((Interlocked.Increment(completed), imagePaths.Length))
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
Public Sub ProcessBatchToSearchablePdfs(imagePaths As String(), outputDirectory As String)
Parallel.ForEach(imagePaths, Sub(imagePath)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(imagePath)
Dim outputPath = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(imagePath) & "-searchable.pdf")
result.SaveAsSearchablePdf(outputPath)
End Sub)
End Sub
End Class
La versión de Visión de Google Cloudes secuencial porque las solicitudes paralelas multiplicarían la exposición al límite de velocidad. Cada excepción ResourceExhausted agrega una pausa completa de 60 segundos. Un lote de 5.000 documentos que alcanza la cuota 10 veces añade 10 minutos de espera inactiva. La versión de IronOCR realiza la ejecución en paralelo en todos los núcleos disponibles sin tiempos de espera. Para lotes de larga duración, la API de seguimiento de progreso proporciona callbacks de progreso integrados sin requerir cableado Interlocked.Increment manual. Para problemas de calidad de imagen en escaneos por lotes, la guía de corrección de calidad de imagen cubre el pipeline de preprocesamiento que se puede agregar antes de cada llamada Read.
Referencia de mapeo de la API OCR de Visión de Google Clouda IronOCR
| Visión de Google Cloud | IronOCR | Notas |
|---|---|---|
ImageAnnotatorClient.Create() |
new IronTesseract() |
Inicialización del cliente; no se necesita archivo de credenciales |
Image.FromFile(path) |
_ocr.Read(path) or input.LoadImage(path) |
Lectura de ruta directa disponible en IronTesseract |
_client.DetectText(image) |
_ocr.Read(path).Text |
TEXT_DETECTION equivalent |
_client.DetectDocumentText(image) |
_ocr.Read(path) |
DOCUMENT_TEXT_DETECTION equivalent; el modo es automático |
response[0].Description |
result.Text |
Texto completo del documento |
TextAnnotation |
OcrResult |
Contenedor de resultados de nivel superior |
annotation.Text |
result.Text |
Cadena de texto completa |
annotation.Pages[i] |
result.Pages[i] |
Acceso por página |
page.Blocks[i].Paragraphs[j] |
result.Paragraphs[i] |
IronOCR expone los párrafos como una colección plana |
paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text) |
paragraph.Text |
Propiedad de cadena directa; sin iteración de símbolos |
word.BoundingBox.Vertices |
word.X, word.Y, word.Width, word.Height |
Propiedades de enteros discretos en lugar de una lista de vértices. |
word.Confidence |
word.Confidence |
Puntuación de confianza por palabra |
page.Confidence |
result.Confidence |
Confianza en el resultado general |
Feature.Types.Type.DocumentTextDetection |
Automático | IronOCR selecciona automáticamente el modo de procesamiento. |
BatchAnnotateImagesRequest |
Parallel.ForEach + new IronTesseract() por hilo |
Procesamiento local en paralelo; sin límite de tamaño de lote |
_client.BatchAnnotateImages(requests) |
new IronTesseract().Read(input) con OcrInput de múltiples imágenes |
Llamada única para entrada de múltiples imágenes |
AsyncBatchAnnotateFilesAsync() |
input.LoadPdf(); _ocr.Read(input) |
El procesamiento de archivos PDF es síncrono; No se requiere GCS |
StorageClient.Create() |
No es necesario | Sin dependencia de GCS |
storageClient.UploadObjectAsync() |
No es necesario | Los archivos PDF se cargan directamente desde una ruta local o un flujo de datos. |
operation.PollUntilCompletedAsync() |
No es necesario | El procesamiento es síncrono. |
RpcException (StatusCode.ResourceExhausted) |
No procede | Sin límites de tarifa |
RpcException (StatusCode.PermissionDenied) |
No procede | Sin autenticación en tiempo de ejecución |
GOOGLE_APPLICATION_CREDENTIALS variable de entorno |
IronOcr.License.LicenseKey |
Asignación de cadena, no ruta de archivo |
Problemas comunes de migración y soluciones
Problema 1: El constructor lanza una excepción sin GOOGLE_APPLICATION_CREDENTIALS
Google Cloud Vision: ImageAnnotatorClient.Create() lanza RpcException con StatusCode.Unauthenticated o StatusCode.PermissionDenied si la variable de entorno no está configurada o apunta a un archivo no válido. Este fallo ocurre al inicio, no en la primera llamada a la API, lo que significa que toda la aplicación no se inicializa si faltan credenciales en cualquier entorno.
Solución: Después de eliminar los paquetes de Google Cloud Vision, elimine todas las referencias a GOOGLE_APPLICATION_CREDENTIALS de las configuraciones de entorno, secretos del pipeline CI/CD, secretos de Kubernetes, y archivos de Docker Compose. Reemplace con una sola variable de entorno IRONOCR_LICENSE:
// Remove this from every deployment environment:
// GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
// Add this once at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable is required.");
// Remove this from every deployment environment:
// GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
// Add this once at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable is required.");
' Remove this from every deployment environment:
' GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
' Add this once at application startup:
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE"), Throw New InvalidOperationException("IRONOCR_LICENSE environment variable is required."))
Problema 2: El código de concatenación de símbolos de Protobuf deja de funcionar tras la eliminación del espacio de nombres.
Google Cloud Vision: Cada ubicación en su código donde se extraía texto de párrafos o palabras utilizando .SelectMany(w => w.Symbols).Select(s => s.Text) producirá errores de compilación después de que se elimine el namespace Google.Cloud.Vision.V1. Estas llamadas se distribuyen entre todas las clases de ayudantes o servicios que hayan consumido la respuesta de la API.
Solución: Busque todos los patrones SelectMany y w.Symbols en su código y reemplácelos con acceso directo de propiedad en objetos de resultados de IronOCR. La guía cómo leer resultados cubre cada propiedad disponible en OcrResult, OcrResult.Page, OcrResult.Paragraph, OcrResult.Line, y OcrResult.Word:
# Find all Protobuf symbol concatenation patterns
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
grep -rn "w\.Symbols\.Select" --include="*.cs" .
# Find all Protobuf symbol concatenation patterns
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
grep -rn "w\.Symbols\.Select" --include="*.cs" .
Reemplazar cada aparición:
// Before: symbol concatenation required by Protobuf schema
var text = string.Join("", paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text));
// After: direct property on OcrResult.Paragraph
var text = paragraph.Text;
// Before: symbol concatenation required by Protobuf schema
var text = string.Join("", paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text));
// After: direct property on OcrResult.Paragraph
var text = paragraph.Text;
' Before: symbol concatenation required by Protobuf schema
Dim text = String.Join("", paragraph.Words.SelectMany(Function(w) w.Symbols).Select(Function(s) s.Text))
' After: direct property on OcrResult.Paragraph
Dim text = paragraph.Text
Problema 3: El código de procesamiento de PDF no compila después de eliminar Storage.V1.
Google Cloud Vision: Después de eliminar Google.Cloud.Storage.V1, todo el código que haga referencia a StorageClient, UploadObjectAsync, DeleteObjectAsync, AsyncAnnotateFileRequest, GcsSource, GcsDestination, y PollUntilCompletedAsync fallará al compilar. Este código puede abarcar múltiples clases de servicio y típicamente representa el bloque singular más grande de cambios.
Solución: Elimine toda la canalización de GCS. Reemplace el método asíncrono de más de 50 líneas con su equivalente de tres líneas en IronOCR. Para el código que mantenía una firma async para compatibilidad del llamante, envuélvalo con Task.Run:
// Delete: StorageClient, GCS upload, AsyncBatchAnnotateFilesAsync,
// PollUntilCompletedAsync, output download, DeleteObjectAsync
// Replace with:
public async Task<string> ProcessPdfAsync(string pdfPath)
{
return await Task.Run(() =>
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return new IronTesseract().Read(input).Text;
});
}
// Delete: StorageClient, GCS upload, AsyncBatchAnnotateFilesAsync,
// PollUntilCompletedAsync, output download, DeleteObjectAsync
// Replace with:
public async Task<string> ProcessPdfAsync(string pdfPath)
{
return await Task.Run(() =>
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return new IronTesseract().Read(input).Text;
});
}
Imports System.Threading.Tasks
Public Async Function ProcessPdfAsync(pdfPath As String) As Task(Of String)
Return Await Task.Run(Function()
Using input As New OcrInput()
input.LoadPdf(pdfPath)
Return New IronTesseract().Read(input).Text
End Using
End Function)
End Function
Para código nuevo, utilice el soporte nativo de OCR asíncrono en lugar del envoltorio Task.Run. La guía de entrada de PDF abarca la selección del rango de páginas y la carga de archivos PDF protegidos con contraseña.
Problema 4: Ya no es necesaria la lógica de reintento con límite de velocidad.
Google Cloud Vision: Cualquier código que capture RpcException con StatusCode.ResourceExhausted e implemente un patrón de espera y reintento fue escrito para manejar la cuota de 1,800 solicitudes por minuto. Esta lógica de reintento puede estar integrada en middleware, pasos de la canalización o bucles de procesamiento por lotes.
Solución: Eliminar toda la lógica de reintento asociada a los errores de cuota.IronOCR realiza los procesos localmente sin necesidad de cuotas externas. El contrato del controlador de errores cambia de cinco casos RpcException a dos:
// Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
// Unavailable, DeadlineExceeded, Unauthenticated
//IronOCR error surface:
try
{
var result = new IronTesseract().Read(imagePath);
if (result.Confidence < 50)
input.DeNoise(); // add preprocessing for low-confidence results
return result.Text;
}
catch (IOException ex)
{
// File not found or locked
throw new InvalidOperationException($"Cannot read: {imagePath}", ex);
}
catch (IronOcr.Exceptions.OcrException ex)
{
// Processing failure — not a transient network error
throw new InvalidOperationException($"OCR failed: {ex.Message}", ex);
}
// Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
// Unavailable, DeadlineExceeded, Unauthenticated
//IronOCR error surface:
try
{
var result = new IronTesseract().Read(imagePath);
if (result.Confidence < 50)
input.DeNoise(); // add preprocessing for low-confidence results
return result.Text;
}
catch (IOException ex)
{
// File not found or locked
throw new InvalidOperationException($"Cannot read: {imagePath}", ex);
}
catch (IronOcr.Exceptions.OcrException ex)
{
// Processing failure — not a transient network error
throw new InvalidOperationException($"OCR failed: {ex.Message}", ex);
}
Imports IronOcr
Imports System.IO
' Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
' Unavailable, DeadlineExceeded, Unauthenticated
'IronOCR error surface:
Try
Dim result = New IronTesseract().Read(imagePath)
If result.Confidence < 50 Then
input.DeNoise() ' add preprocessing for low-confidence results
End If
Return result.Text
Catch ex As IOException
' File not found or locked
Throw New InvalidOperationException($"Cannot read: {imagePath}", ex)
Catch ex As IronOcr.Exceptions.OcrException
' Processing failure — not a transient network error
Throw New InvalidOperationException($"OCR failed: {ex.Message}", ex)
End Try
Problema 5: Los archivos TIFF de varias páginas requieren un bucle de extracción de fotogramas.
Google Cloud Vision: El código de procesamiento TIFF existente probablemente extrae marcos usando System.Drawing.Image, guarda cada marco como un JPEG en un directorio temporal, envía cada JPEG como una llamada API separada y elimina los archivos temporales después. Este patrón consume una unidad de cuota por fotograma y puede dejar archivos temporales huérfanos en caso de fallo.
Solución: Reemplace el bucle de extracción de marcos y la gestión de archivos temporales con input.LoadImageFrames(). Se elimina todo el bucle del marco System.Drawing:
// Remove: System.Drawing frame extraction, temp file writes, per-frame API calls
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames, no temp files
var result = new IronTesseract().Read(input);
// Remove: System.Drawing frame extraction, temp file writes, per-frame API calls
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames, no temp files
var result = new IronTesseract().Read(input);
Imports IronOcr
Dim input As New OcrInput()
input.LoadImageFrames(tiffPath) ' all frames, no temp files
Dim result = (New IronTesseract()).Read(input)
Consulte la guía de entrada TIFF y GIF para obtener información sobre las opciones de procesamiento de múltiples fotogramas, incluida la selección del rango de fotogramas.
Problema 6: Interrupción en los cálculos de vértices de BoundingPoly
Google Cloud Vision: El código que leía las coordenadas del cuadro delimitador de annotation.BoundingPoly.Vertices calculaba X, Y, Anchura y Altura a partir de la aritmética de índice de vértice: vertices[0].X para X, vertices[1].X - vertices[0].X para Anchura, vertices[2].Y - vertices[0].Y para Altura. Después de la migración, estas expresiones no tienen equivalente en IronOCR porque Vertices no existe.
Solución: Reemplazar la aritmética de vértices con propiedades enteras directas. No es necesario realizar ningún cálculo:
// Before: vertex index arithmetic
int x = word.BoundingBox.Vertices[0].X;
int y = word.BoundingBox.Vertices[0].Y;
int width = word.BoundingBox.Vertices[1].X - word.BoundingBox.Vertices[0].X;
int height = word.BoundingBox.Vertices[2].Y - word.BoundingBox.Vertices[0].Y;
// After: direct properties
int x = word.X;
int y = word.Y;
int width = word.Width;
int height = word.Height;
// Before: vertex index arithmetic
int x = word.BoundingBox.Vertices[0].X;
int y = word.BoundingBox.Vertices[0].Y;
int width = word.BoundingBox.Vertices[1].X - word.BoundingBox.Vertices[0].X;
int height = word.BoundingBox.Vertices[2].Y - word.BoundingBox.Vertices[0].Y;
// After: direct properties
int x = word.X;
int y = word.Y;
int width = word.Width;
int height = word.Height;
' Before: vertex index arithmetic
Dim x As Integer = word.BoundingBox.Vertices(0).X
Dim y As Integer = word.BoundingBox.Vertices(0).Y
Dim width As Integer = word.BoundingBox.Vertices(1).X - word.BoundingBox.Vertices(0).X
Dim height As Integer = word.BoundingBox.Vertices(2).Y - word.BoundingBox.Vertices(0).Y
' After: direct properties
Dim x As Integer = word.X
Dim y As Integer = word.Y
Dim width As Integer = word.Width
Dim height As Integer = word.Height
Lista de verificación para la migración de OCR de Google Cloud Vision
Pre-Migración
Antes de realizar cualquier cambio, revise el código fuente para identificar todas las dependencias de Google Cloud Vision:
# Find all Visión de Google Cloudnamespace imports
grep -rn "using Google.Cloud.Vision" --include="*.cs" .
grep -rn "using Google.Cloud.Storage" --include="*.cs" .
grep -rn "using Grpc.Core" --include="*.cs" .
# Find ImageAnnotatorClient usage
grep -rn "ImageAnnotatorClient" --include="*.cs" .
# Find GCS pipeline code
grep -rn "StorageClient\|UploadObjectAsync\|DeleteObjectAsync" --include="*.cs" .
grep -rn "AsyncBatchAnnotateFilesAsync\|PollUntilCompleted" --include="*.cs" .
# Find Protobuf symbol concatenation
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
# Find BoundingPoly vertex calculations
grep -rn "BoundingPoly\|BoundingBox\.Vertices" --include="*.cs" .
# Find rate limit retry handlers
grep -rn "ResourceExhausted\|StatusCode\." --include="*.cs" .
# Find environment variable references
grep -rn "GOOGLE_APPLICATION_CREDENTIALS" .
# Find all Visión de Google Cloudnamespace imports
grep -rn "using Google.Cloud.Vision" --include="*.cs" .
grep -rn "using Google.Cloud.Storage" --include="*.cs" .
grep -rn "using Grpc.Core" --include="*.cs" .
# Find ImageAnnotatorClient usage
grep -rn "ImageAnnotatorClient" --include="*.cs" .
# Find GCS pipeline code
grep -rn "StorageClient\|UploadObjectAsync\|DeleteObjectAsync" --include="*.cs" .
grep -rn "AsyncBatchAnnotateFilesAsync\|PollUntilCompleted" --include="*.cs" .
# Find Protobuf symbol concatenation
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
# Find BoundingPoly vertex calculations
grep -rn "BoundingPoly\|BoundingBox\.Vertices" --include="*.cs" .
# Find rate limit retry handlers
grep -rn "ResourceExhausted\|StatusCode\." --include="*.cs" .
# Find environment variable references
grep -rn "GOOGLE_APPLICATION_CREDENTIALS" .
Notas de inventario que deben completarse antes de comenzar:
- Enumerar todos los depósitos GCS creados para la entrada/salida de OCR; programar la limpieza después de la migración.
- Documenta el correo electrónico de la cuenta de servicio para que pueda desactivarse en la consola de GCP después de la migración.
- Identificar todos los entornos donde
GOOGLE_APPLICATION_CREDENTIALSestá configurado - Tenga en cuenta cualquier código que lea los ID de proyecto de GCP o los nombres de los buckets desde la configuración; elimínelo después de la migración.
Migración de código
- Eliminar el paquete de NuGet
Google.Cloud.Vision.V1de todos los proyectos - Eliminar el paquete de NuGet
Google.Cloud.Storage.V1de todos los proyectos - Instalar el paquete de NuGet
IronOcren todos los proyectos que realicen OCR - Añadir
IronOcr.License.LicenseKeyde inicialización al inicio de la aplicación - Reemplazar todas las importaciones
using Google.Cloud.Vision.V1conusing IronOcr - Reemplazar todas las importaciones
using Google.Cloud.Storage.V1yusing Grpc.Core - Reemplazar
ImageAnnotatorClient.Create()connew IronTesseract() - Eliminar todos los métodos del pipeline GCS (
StorageClient,UploadObjectAsync, anotación async, sondeo, descarga, eliminación) - Reemplazar
input.LoadPdf()para todos los caminos de procesamiento de PDF (eliminando la orquestación async de GCS) - Reemplazar todos los bucles de concatenación de símbolos de Protobuf con acceso directo de propiedad
.Texten objetosOcrResult - Reemplazar cálculos de índice
BoundingPoly.Verticesconword.X,word.Y,word.Width,word.Height - Remover todos los bloques de captura
RpcExceptionparaResourceExhausted,PermissionDenied,Unavailable,DeadlineExceeded, yUnauthenticated - Reemplazar bucle TIFF por marco con
input.LoadImageFrames() - Convertir bucles de lotes secuenciales a
Parallel.ForEachcon instanciasIronTesseractpor hilo - Remover
GOOGLE_APPLICATION_CREDENTIALSde todas las configuraciones de entorno, pipelines CI/CD, archivos de Docker Compose, y secretos de Kubernetes
Posmigración
- Verificar que no queden tipos
RpcExceptionoGoogleApiExceptionreferenciados en el código de manejo de errores - Confirmar que
GOOGLE_APPLICATION_CREDENTIALSno está presente en todas las configuraciones de entorno de implementación - Ejecute el proceso de OCR en el mismo conjunto de documentos de muestra utilizados en producción y compare la calidad del texto resultante.
- Pruebe el procesamiento de PDF en documentos previamente procesados por la canalización asíncrona de GCS y confirme que la salida de texto es idéntica.
- Probar archivos PDF protegidos por contraseña con
input.LoadPdf(path, Password: "...")— anteriormente no soportados - Probar procesamiento de TIFF de múltiples páginas usando
input.LoadImageFrames()y verificar que todos los marcos son procesados - Ejecute el procesador por lotes en una muestra representativa y confirme que la calidad de la salida coincide con los resultados anteriores.
- Confirmar que los valores de
result.Confidenceestán dentro del rango aceptable para su corpus de documentos - Verificar salida de PDF buscable utilizando
result.SaveAsSearchablePdf()para documentos que anteriormente requerían una biblioteca PDF separada - Ejecute la aplicación en un entorno sin conexión a internet saliente y confirme que el OCR funciona correctamente.
Principales ventajas de migrar a IronOCR
Superficie de credenciales reducida a cero archivos. Después de la migración, no hay archivos de clave JSON, ni configuraciones de bucket de GCS, ni roles de IAM, ni cuentas de servicio, ni variables de entorno GOOGLE_APPLICATION_CREDENTIALS en su infraestructura. Toda la información de credenciales se reduce a una variable de entorno que contiene una cadena de clave de licencia. La rotación de claves, que era una operación periódica obligatoria con Google Cloud Vision, ya no es un concepto aplicable. Para los equipos que operan en varias regiones o proveedores de nube, la reducción en la complejidad de la configuración de la implementación es inmediata.
Procesamiento de PDF y TIFF sin dependencias externas. Se eliminan por completo la canalización asíncrona de GCS y el bucle de fotogramas TIFF de System.Drawing. input.LoadPdf() y input.LoadImageFrames() son los reemplazos — ambos sincrónicos, ambos locales, ambos tres líneas desde la llamada hasta el resultado. Los archivos PDF protegidos con contraseña, algo imposible con Google Cloud Vision, funcionan con un único parámetro adicional. La guía de OCR para PDF y la guía de entrada TIFF cubren la API de entrada completa.
Procesamiento por lotes a velocidad de CPU. Al eliminar la cuota de 1800 solicitudes por minuto y los tiempos de espera obligatorios de 60 segundos para reintentos, los trabajos por lotes que antes estaban limitados por la velocidad ahora se ejecutan a la velocidad de los núcleos de procesador disponibles. Una máquina con 16 núcleos procesa 16 documentos simultáneamente sin necesidad de aprobación externa. El patrón Parallel.ForEach con instancias IronTesseract por hilo es el reemplazo directo para el bucle secuencial restringido. La guía de optimización de velocidad cubre opciones de configuración del motor que ajustan el rendimiento para tipos específicos de documentos.
Datos estructurados sin Protobuf. Cada OcrResult expone Text, Confidence, Pages, Paragraphs, Lines, Words, y Characters como propiedades tipadas de .NET sin dependencia del namespace de Protobuf, sin concatenación de símbolos, ni aritmética de vértices para cuadros delimitadores. El código que previamente requería bucles anidados de 20 líneas para extraer texto de párrafos se reduce a result.Paragraphs.Select(p => p.Text). Para casos de uso que necesitan posicionamiento a nivel de palabra para análisis de diseño de documentos, word.X, word.Y, word.Width, y word.Height están disponibles directamente. Los resultados del OCR incluyen documentos de página para cada propiedad del modelo de resultados.
Salida de PDF con capacidad de búsqueda integrada. Visión de Google Cloudsolo devuelve texto; para generar un PDF con capacidad de búsqueda se requería una biblioteca de generación de PDF independiente, lo que añadía otra dependencia de NuGet , otra API que aprender y una licencia adicional que evaluar. La result.SaveAsSearchablePdf(outputPath) de IronOCR produce un PDF completamente buscable a partir de cualquier resultado de OCR en una línea. Para los flujos de trabajo de archivo de documentos y los pipelines de descubrimiento legal, esto elimina una dependencia completa. El ejemplo de PDF con función de búsqueda demuestra el patrón de principio a fin.
Soberanía de datos para industrias reguladas. Los documentos procesados por IronOCR nunca salen del servidor. En el caso de los registros sanitarios cubiertos por la HIPAA, los datos técnicos controlados por la ITAR, los materiales de contratistas de defensa sujetos al ámbito de aplicación de la CMMC, los documentos legales con privilegio de confidencialidad entre abogado y cliente, y los registros financieros sujetos al ámbito de aplicación de la PCI-DSS, la arquitectura local elimina por completo la categoría de procesador de datos de terceros del ámbito de cumplimiento. No hay ningún acuerdo de asociación comercial que negociar, ningún acuerdo de protección de datos que firmar, ni ninguna política de retención de datos de Google que revisar. El centro de documentación de IronOCR abarca las configuraciones de implementación para entornos Docker, Linux, Azure y AWS donde se aplican requisitos de residencia de datos.
Preguntas Frecuentes
¿Por qué debería migrar de Google Cloud Vision API a IronOCR?
Los impulsores comunes incluyen la eliminación de la complejidad de la interoperabilidad COM, la sustitución de la gestión de licencias basada en archivos, la evitación de la facturación por página, la habilitación de la implementación de Docker/contenedores y la adopción de un flujo de trabajo nativo de NuGet que se integre con las herramientas .NET estándar.
¿Cuáles son los principales cambios en el código al migrar de Google Cloud Vision API a IronOCR?
Sustituya las secuencias de inicialización de Google Cloud Vision por la instanciación de IronTesseract, elimine la gestión del ciclo de vida COM (patrones explícitos Create/Load/Close) y actualice los nombres de las propiedades de los resultados. El resultado es un número significativamente menor de líneas repetitivas.
¿Cómo instalo IronOCR para comenzar la migración?
Ejecute 'Install-Package IronOcr' en la consola del gestor de paquetes o 'dotnet add package IronOcr' en la CLI. Los paquetes de idiomas son paquetes independientes: 'dotnet add package IronOcr.Languages.French' para el francés, por ejemplo.
¿IronOCR ofrece la misma precisión de reconocimiento óptico de caracteres que Google Cloud Vision API para documentos comerciales estándar?
IronOCR consigue una gran precisión para contenido empresarial estándar, como facturas, contratos, recibos y formularios mecanografiados. Los filtros de preprocesamiento de imágenes (eliminación de distorsiones, eliminación de ruido, mejora del contraste) mejoran aún más el reconocimiento en entradas degradadas.
¿Cómo gestiona IronOCR los datos lingüísticos que la API Google Cloud Vision instala por separado?
Los datos de idiomas en IronOCR se distribuyen como paquetes NuGet. dotnet add package IronOcr.Languages.German' instala la compatibilidad con el alemán. No es necesario colocar manualmente los archivos ni las rutas de los directorios.
¿Requiere la migración de Google Cloud Vision API a IronOCR cambios en la infraestructura de implementación?
IronOCR requiere menos cambios de infraestructura que Google Cloud Vision API. No hay rutas binarias del SDK, ubicaciones de archivos de licencia ni configuraciones del servidor de licencias. El paquete NuGet contiene el motor OCR completo, y la clave de licencia es una cadena establecida en el código de la aplicación.
¿Cómo configuro las licencias de IronOCR después de la migración?
Asigne IronOcr.License.LicenseKey = "YOUR-KEY" en el código de inicio de la aplicación. En Docker o Kubernetes, almacene la clave como una variable de entorno y léala en el inicio. Utilice License.IsValidLicense para validar antes de aceptar tráfico.
¿Puede IronOCR procesar archivos PDF del mismo modo que Google Cloud Vision?
Sí, IronOCR lee PDF nativos y escaneados. Instancie IronTesseract, llame a ocr.Read(input) donde input es una ruta PDF u OcrPdfInput, e itere las páginas OcrResult. No es necesario un proceso de renderizado de PDF independiente.
¿Cómo gestiona IronOCR los hilos en el procesamiento de grandes volúmenes?
IronTesseract puede instanciarse de forma segura por subproceso. Gire una instancia por hilo en un Parallel.ForEach o Task pool, ejecute OCR concurrentemente, y disponga de cada instancia cuando termine. No se requiere estado global o bloqueo.
¿Qué formatos de salida admite IronOCR tras la extracción de texto?
IronOCR devuelve resultados estructurados que incluyen texto, coordenadas de palabras, puntuaciones de confianza y estructura de páginas. Las opciones de exportación incluyen texto sin formato, PDF con opción de búsqueda y objetos de resultados estructurados para su procesamiento posterior.
¿Es el precio de IronOCR más predecible que el de Google Cloud Vision API para escalar cargas de trabajo?
IronOCR utiliza licencias perpetuas de tarifa plana sin cargos por página o volumen. Tanto si procesa 10.000 como 10 millones de páginas, el coste de la licencia permanece constante. Las opciones de licencias por volumen y por equipo se encuentran en la página de precios de IronOCR.
¿Qué ocurre con mis pruebas existentes después de migrar de Google Cloud Vision API a IronOCR?
Las pruebas que validan el contenido de texto extraído deben seguir superándose tras la migración. Las pruebas que validan patrones de llamada a API o el ciclo de vida de objetos COM deberán actualizarse para reflejar el modelo de inicialización y resultados más sencillo de IronOCR.

