Jak zainstalować Tesseract OCR w systemie Windows w języku C#
Ten przewodnik przeprowadza programistów .NET przez proces zastępowaniaGoogle Cloud Visionsilnikiem IronOCR jako gotowym do użycia lokalnym silnikiem OCR. Obejmuje on usuwanie danych uwierzytelniających, zastąpienie parsowania adnotacji Protobuf, uproszczenie adnotacji wsadowych oraz przetwarzanie dokumentów wielostronicowych — cztery zmiany strukturalne, które stanowią większość prac związanych z migracją.
Dlaczego warto przejść zGoogle Cloud VisionOCR
Decyzja o migracji prawie zawsze wynika z jednego z dwóch powodów: audyt zgodności blokuje przesyłanie dokumentów w chmurze lub zarządzanie danymi uwierzytelniającymi GCP, zasobnikami GCS, asynchronicznym odpytywaniem i rozliczaniem za każdy obraz staje się droższe niż samo OCR.
Cykl życia klucza JSON konta usługi. Każde wdrożenie aplikacji — maszyna programistyczna, potok CI/CD, serwer stagingowy, serwer produkcyjny, kontener Docker, pod Kubernetes — wymaga tego samego pliku klucza JSON konta usługi. Plik zawiera klucz prywatny RSA. Nie może on nigdy trafić do systemu kontroli wersji, musi być rotowany zgodnie z harmonogramem, musi być chroniony uprawnieniami systemu plików i musi być aktualizowany we wszystkich środowiskach jednocześnie w momencie rotacji. Jeden skompromitowany klucz zapewnia dostęp do API do momentu ręcznego cofnięcia go w konsoli GCP.IronOCR zastępuje całą tę powierzchnię operacyjną pojedynczym ciągiem klucza licencyjnego ustawianym jednorazowo podczas uruchamiania aplikacji.
Rozliczanie na żądanie na dużą skalę. Przy cenie 1,50 USD za 1000 obrazów i 0,0015 USD za stronę PDF koszty są niewidoczne podczas tworzenia oprogramowania, a w fazie produkcyjnej stają się bolesne. Pipeline przetwarzania dokumentów obsługujący 200 000 stron miesięcznie kosztuje 300 USD miesięcznie samych opłat za API, nie licząc opłat za przechowywanie w GCS i kosztów przesyłu wychodzącego. Ta kwota 300 dolarów powtarza się co miesiąc bezterminowo. Licencja wieczysta IronOCR przekształca OCR z kosztu operacyjnego rozliczanego według rzeczywistego zużycia w pozycję kapitału trwałego, której eksploatacja w drugim lub trzecim roku nic nie kosztuje.
GCS Async Pipeline dla plików PDF.Google Cloud Visionnie akceptuje plików PDF jako bezpośredniego danych wejściowych API. Pelny potok przetwarzania wymaga drugiego pakietu NuGet (Google.Cloud.Storage.V1), skonfigurowanego kubełka GCS, asynchronicznego przesyłania, wywołania AsyncBatchAnnotateFilesAsync, pętli oczekującej, parsowania wyniku JSON z GCS i kroku sprzątającego. Ten potok obejmuje ponad 50 linii kodu zanim jakikolwiek tekst zostanie wyodrębniony.IronOCR odczytuje plik PDF w trzech wierszach, synchronicznie, bez żadnych zewnętrznych zależności.
Złączenie symboli Protobuf. Odpowiedź DOCUMENT_TEXT_DETECTION przechowuje tekst na poziomie symbolu w hierarchii Protobuf zawierającej strony, bloki, akapity, słowa i symbole. Odczyt tekstu z akapitu wymaga iteracji pięciu zagnieżdżonych pętli i wywołania .SelectMany(w => w.Symbols).Select(s => s.Text).IronOCR zwraca tekst akapitu jako paragraph.Text — typowaną właściwość ciągu znaków.
Domyślny limit 1 800 żądań na minutę. Przetwarzane wsady, które przekraczają domyślny limit, otrzymują odpowiedzi StatusCode.ResourceExhausted, które opóźniają przetwarzanie o 60 sekund za każde przekroczenie. Zwiększenie limitu wymaga złożenia wniosku w konsoli GCP i uzyskania zgody Google.IronOCR przetwarza dane lokalnie z prędkością dostępnych rdzeni procesora — nie ma żadnych limitów do zarządzania, nie trzeba czekać na zatwierdzenie, a logika ponownych prób nie jest wymagana do ograniczania przepustowości.
Brak obsługi trybu offline lub izolacji sieciowej. UsługaGoogle Cloud Visionwymaga połączenia internetowego z punktami końcowymi Google. Sieci z izolacją fizyczną, tajne centra danych i przemysłowe systemy sterowania nie mogą z niego korzystać na żadnym poziomie złożoności architektury.IronOCR działa bez połączenia z siecią po wstępnej weryfikacji licencji.
Podstawowy problem
Google Cloud Vision wymaga pliku klucza JSON na dysku przed uruchomieniem pierwszej linii kodu 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 uruchamia się na podstawie jednego ciągu znaków i działa całkowicie na komputerze lokalnym:
// 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 aGoogle Cloud VisionOCR: porównanie funkcji
Poniższa tabela przedstawia funkcje w sposób bezpośredni dla zespołów opracowujących uzasadnienie biznesowe migracji.
| Funkcja | Google Cloud Vision OCR | IronOCR |
|---|---|---|
| Miejsce przetwarzania | Google Cloud (praca zdalna) | Lokalnie (na miejscu) |
| Uwierzytelnianie | Klucz JSON konta usługowego + zmienna środowiskowa | Ciąg klucza licencyjnego |
| Plik wejściowy PDF | Przesyłanie do GCS + asynchroniczne API | input.LoadPdf() bezpośredni |
| Plik PDF chroniony hasłem | Nieobsługiwane | LoadPdf(path, Password: "...") |
| Wielostronicowy plik wejściowy w formacie TIFF | Ograniczone | input.LoadImageFrames() |
| Wynik w formacie PDF z możliwością wyszukiwania | Niedostępne | result.SaveAsSearchablePdf() |
| Dostęp do danych strukturalnych | Protobuf: Strony > Bloki > Akapity > WORDy > Symbole | result.Paragraphs, result.Lines, result.Words (typowane obiekty .NET) |
| Właściwości tekstu akapitu | Nie — wymaga łączenia symboli | paragraph.Text właściwość bezpośrednia |
| Wyniki pewności | Na symbol (wymaga pętli) | result.Confidence, word.Confidence |
| Automatyczne przetwarzanie wstępne obrazów | Brak (obsługuje to ML) | Wyrównanie, usuwanie szumów, kontrast, binarizacja, wyostrzanie |
| OCR oparte na regionie | Brak natywnego przycięcia | CropRectangle na OcrInput |
| Odczytywanie BarCode | Oddzielna funkcja API | ocr.Configuration.ReadBarCodes = true |
| Limity szybkości | 1800 żądań na minutę (domyślnie) | Brak (zależne od procesora) |
| Tryb offline / izolacja sieciowa | Nie | Tak |
| Koszt za dokument | 1,50 USD za 1000 obrazów; 0,0015 USD/strona PDF | Brak (Licencja wieczysta) |
| Obsługiwane języki | ~50 | 125+ |
| Autoryzacja FedRAMP | Brak uprawnień | Nie dotyczy (lokalnie) |
| Ścieżka zgodności z HIPAA | Wymagana umowa o współpracy biznesowej | Brak przetwarzania danych stron trzecich |
| Obsługa platformy .NET Framework | .NET Standard 2.0+ | .NET Framework 4.6.2+ oraz .NET 5/6/7/8/9 |
| Wymagane pakiety NuGet | Google.Cloud.Vision.V1 + Google.Cloud.Storage.V1 |
IronOcr tylko |
| Model cenowy | Rozliczenie według liczby żądań | Perpetual ($999 Lite / $1,499 Plus / $2,999 Professional / $5,999 Unlimited) |
Szybki start: Migracja zGoogle Cloud Vision OCRdo IronOCR
Krok 1: Zastąp pakiet NuGet
Usuń pakiety 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
Zainstaluj IronOCR ze strony pakietu NuGet:
dotnet add package IronOcr
Krok 2: Aktualizacja przestrzeni nazw
Zastąp przestrzenie nazw Google Cloud przestrzenią nazw 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
Krok 3: Inicjalizacja licencji
Dodaj inicjalizację licencji raz przy starcie aplikacji, przed utworzeniem jakiejkolwiek instancji IronTesseract:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
W środowisku produkcyjnym odczytaj klucz ze zmiennej środowiskowej lub menedżera sekretów:
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."))
Przykłady migracji kodu
Eliminacja konfiguracji poświadczeń konta usługowego
Inicjalizacja klientaGoogle Cloud Visionwygląda jak pojedynczy wiersz, ale wymaga rozbudowanej infrastruktury. Każdy programista dołączający do projektu, każde środowisko wdrożeniowe i każdy potok CI/CD wymaga pełnej konfiguracji poświadczeń przed zakończeniem działania konstruktóra bez generowania błędu.
Podejście 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
Podejście IronOCR:
using IronOcr;
// Prerequisites: set the license key once at app startup
// Nie JSON files, no environment variables beyond the key, no GCP Console configuration
// Nie 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
// Nie JSON files, no environment variables beyond the key, no GCP Console configuration
// Nie 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
' Nie JSON files, no environment variables beyond the key, no GCP Console configuration
' Nie 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
Operacyjna różnica jest konkretna:Google Cloud Visiongeneruje pięć kategorii RpcException w czasie wykonywania — PermissionDenied, ResourceExhausted, Unavailable, DeadlineExceeded i Unauthenticated — każda reprezentująca inny tryb awarii infrastruktury. Tryby awarii IronOCR to IOException (plik nie znaleziony lub zablokowany) i OcrException (błąd przetwarzania). Opcje konfiguracyjne można znaleźć w przewodniku konfiguracji IronTesseract, a szczegóły dotyczące licencji — na stronie produktu IronOCR.
Zastąpienie żądań adnotacji wsadowych różnymi typami funkcji
Google Cloud Vision obsługuje przetwarzanie wielu obrazów w jednym BatchAnnotateImagesRequest, każdy obraz skonfigurowany z listą typów Feature. Ten wzorzec używany jest, gdy jedno wywołanie musi zebrać zarówno wyniki TEXT_DETECTION, jak i DOCUMENT_TEXT_DETECTION, lub gdy wiele obrazów jest przesyłanych, aby zminimalizować obciążenie dla odbioru. Odpowiedź Protobuf wymaga dopasowania każdego AnnotateImageResponse z powrotem do oryginalnego zapytania na podstawie indeksu.
Podejście 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 Funkcja { Type = Feature.Types.Type.TextDetection },
new Funkcja { 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 Funkcja { Type = Feature.Types.Type.TextDetection },
new Funkcja { 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
Podejście 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 uruchamia partię zadań równolegle na rdzeniach procesora bez obciążania sieci. Nie ma limitu BatchSize, brak dopasowania indeksu odpowiedzi i brak obsługi błędów dla poszczególnych elementów z powodu niepowodzeń sieciowych lub poświadczeniowych. W przypadku zadania przetwarzania wielu obrazów w jeden logiczny dokument — np. zeskanowanych wielostronicowych formularzy przesłanych jako pojedyncze JPEGi — BatchAsDocument ładuje wszystkie obrazy do jednego OcrInput i zwraca scalony OcrResult z result.Pages zindeksowanymi do każdego obrazu źródłowego. Przykład wielowątkowości przedstawia wyniki testów wydajnościowych przetwarzania równoległego.
Migracja ekstrakcji adnotacji na poziomie słów w Protobuf WORD
Dane dotyczące ramki ograniczającej na poziomie słowa i poziomu pewności wGoogle Cloud Visionwymagają poruszania się po pełnej hierarchii Protobuf: Strony, następnie Bloki, następnie Akapity, następnie Słowa, a na końcu Symbole. Wyodrębnienie tekstu ze słowa wymaga konkatynacji Symboli z każdego Słowa — obiekt Word nie posiada bezpośredniej właściwości .Text. Koordynaty prostokątu ograniczającego są przechowywane jako BoundingPoly z listą Vertices zamiast dyskretnych pól X, Y, Szerokości, Wysokości.
Podejście 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
Podejście 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
Pętla konkatenacji symboli w wersjiGoogle Cloud Visionnie jest wyborem projektowym — jest wymagana przez schemat Protobuf. Word.Text nie jest właściwością w obiekcie odpowiedzi. Każdy zespół wykorzystujący API na poziomie słowa pisze równoważną pętlę. Kolekcja OcrResult.WordsIronOCR udostępnia jako pierwszorzędne właściwości: Text, X, Y, Width, Height i Confidence. Przewodnik po wynikach odczytu dokumentuje kompletny zestaw właściwości dostępnych na każdym poziomie szczegółowości.
Przetwarzanie wielostronicowych plików TIFF do formatu PDF z możliwością wyszukiwania
Google Cloud Vision traktuje wielostronicowe pliki TIFF jako sekwencyjny ciąg obrazów, z których każdy wymaga oddzielnego wywołania API. Nie ma pojedynczego wywołania API, które akceptuje plik TIFF i zwraca ustrukturyzowany wynik dla wszystkich klatek. Wygenerowanie pliku PDF z możliwością wyszukiwania na podstawie wynikówGoogle Cloud Visionwymaga osobnej biblioteki do generowania plików PDF — API zwraca wyłącznie tekst.
Podejście 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)
//Google Cloud Visionhas 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)
//Google Cloud Visionhas 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)
' Google Cloud Vision has no PDF output capability
Return pageTexts
End Function
End Class
Podejście 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);
//Google Cloud Visionhas 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);
//Google Cloud Visionhas 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)
' Google Cloud Vision has 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
WersjaGoogle Cloud Visionwymaga biblioteki System.Drawing do wyodrębniania klatek, tymczasowych plików JPEG zapisywanych na dysku, jednego wywołania API na klatkę (zużywającego limit proporcjonalnie do liczby klatek) oraz oddzielnej biblioteki PDF do generowania wyników innych niż zwykły tekst.IronOCR obsługuje wieloklatkowe TIFF-y natywnie poprzez LoadImageFrames, przetwarza wszystkie klatki w pojedynczym wywołaniu Read i produkuje przeszukiwalny wynik PDF poprzez SaveAsSearchablePdf. Dla zeskanowanych archiwalnych dokumentów TIFF, potok przetwarzania w ProcessLowQualityTiff rozwiązuje najczęstsze problemy jakościowe w jednym przejściu. Pełny opis API znajduje się w sekcji dotyczącej plików wejściowych TIFF i GIF oraz plików wyjściowych PDF z możliwością wyszukiwania.
Migracja partii z ograniczeniem szybkości i śledzeniem postępów
W skali produkcyjnej domyślny limit 1800 żądań na minutę wGoogle Cloud Visionwymaga zastosowania logiki ograniczania przepustowości lub ponawiania prób z wykładniczym opóźnieniem. Przetworzenie 5000 dokumentów w ramach jednego zadania nocnego wielokrotnie przekroczy limit. Każde przekroczenie limitu blokuje potok na obowiązkowe 60 sekund oczekiwania.IronOCR nie ma ograniczeń szybkości — przepustowość jest ograniczona jedynie przez rdzenie procesora i dostępne wątki.
Podejście 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
Podejście 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;
// Brak limitów stawek — 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;
// Brak limitów stawek — 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
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
' Brak limitów stawek — 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
WersjaGoogle Cloud Visionjest sekwencyjna, ponieważ żądania równoległe zwiększyłyby narażenie na limit częstotliwości. Każdy wyjątek ResourceExhausted dodaje pełną, 60-sekundową przerwę. Partia 5000 dokumentów, która 10 razy przekracza limit, powoduje 10 minut bezczynnego oczekiwania. Wersja IronOCR działa równolegle na wszystkich dostępnych rdzeniach bez czekania. Dla długotrwałych wsadów, API śledzenia postępu zapewnia wbudowane wywołania zwrotne postępu bez konieczności ręcznego okablowania Interlocked.Increment. Dla problemów z jakością obrazu w skanowanych wsadach, przewodnik korekcji jakości obrazu opisuje potok przetwarzania, który można dodać przed każdym wywołaniem Read.
MapowanieGoogle Cloud Vision OCRAPI do IronOCR– dokumentacja API
| Google Cloud Vision | IronOCR | Uwagi |
|---|---|---|
ImageAnnotatorClient.Create() |
new IronTesseract() |
Inicjalizacja klienta; brak potrzeby pliku uwierzytelniającego |
Image.FromFile(path) |
_ocr.Read(path) lub input.LoadImage(path) |
Dostępne bezpośrednie odczyty ścieżek na IronTesseract |
_client.DetectText(image) |
_ocr.Read(path).Text |
TEXT_DETECTION równoważnik |
_client.DetectDocumentText(image) |
_ocr.Read(path) |
DOCUMENT_TEXT_DETECTION równoważnik; tryb jest automatyczny |
response[0].Description |
result.Text |
Pełny tekst dokumentu |
TextAnnotation |
OcrResult |
Kontener wyników najwyższego poziomu |
annotation.Text |
result.Text |
Pełny tekst |
annotation.Pages[i] |
result.Pages[i] |
Dostęp na stronę |
page.Blocks[i].Paragraphs[j] |
result.Paragraphs[i] |
IronOCR udostępnia akapity jako płaską kolekcję |
paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text) |
paragraph.Text |
Właściwość bezpośredniego ciągu znaków; brak iteracji symboli |
word.BoundingBox.Vertices |
word.X, word.Y, word.Width, word.Height |
Dyskretne właściwości int zamiast listy wierzchołków |
word.Confidence |
word.Confidence |
Wskaźnik pewności dla poszczególnych słów |
page.Confidence |
result.Confidence |
Ogólna pewność wyniku |
Feature.Types.Type.DocumentTextDetection |
Automatyczne | IronOCR automatycznie wybiera tryb przetwarzania |
BatchAnnotateImagesRequest |
Parallel.ForEach + new IronTesseract() na wątek |
Równoległe przetwarzanie lokalne; brak limitu rozmiaru partii |
_client.BatchAnnotateImages(requests) |
new IronTesseract().Read(input) z wieloobrazowym OcrInput |
Pojedyncze wywołanie dla wielu obrazów |
AsyncBatchAnnotateFilesAsync() |
input.LoadPdf(); _ocr.Read(input) |
Przetwarzanie plików PDF odbywa się synchronicznie; nie jest wymagany GCS |
StorageClient.Create() |
Nie jest potrzebne | Brak zależności od GCS |
storageClient.UploadObjectAsync() |
Nie jest potrzebne | Pliki PDF są ładowane bezpośrednio ze ścieżki lokalnej lub strumienia |
operation.PollUntilCompletedAsync() |
Nie jest potrzebne | Przetwarzanie jest synchroniczne |
RpcException (StatusCode.ResourceExhausted) |
Nie dotyczy | Brak limitów stawek |
RpcException (StatusCode.PermissionDenied) |
Nie dotyczy | Brak uwierzytelniania w czasie wykonywania |
GOOGLE_APPLICATION_CREDENTIALS zmienna środowiskowa |
IronOcr.License.LicenseKey |
Przypisanie ciągu znaków, a nie ścieżki do pliku |
Typowe problemy związane z migracją i ich rozwiązania
Problem 1: Konstruktor generuje wyjątek bez GOOGLE_APPLICATION_CREDENTIALS
Google Cloud Vision: ImageAnnotatorClient.Create() wyrzuca RpcException z StatusCode.Unauthenticated lub StatusCode.PermissionDenied, jeśli zmienna środowiskowa nie jest ustawiona lub wskazuje na nieprawidłowy plik. Ten błąd występuje podczas uruchamiania, a nie przy pierwszym wywołaniu API, co oznacza, że cała aplikacja nie zostanie zainicjowana, jeśli w danym środowisku brakuje poświadczeń.
Rozwiązanie: Po usunięciu pakietów Google Cloud Vision, usuń wszystkie odniesienia do GOOGLE_APPLICATION_CREDENTIALS z konfiguracji środowiska, tajemnic potoku CI/CD, tajemnic Kubernetes i plików Docker Compose. Zastąp pojedynczą zmienną środowiskową 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."))
Problem 2: Kod konkatenacji symboli Protobuf przestaje działać po usunięciu przestrzeni nazw
Google Cloud Vision: Każde miejsce w twoim kodzie, gdzie tekst akapitu lub słowa był wyodrębniony przy użyciu .SelectMany(w => w.Symbols).Select(s => s.Text) spowoduje błędy kompilacji po usunięciu przestrzeni nazw Google.Cloud.Vision.V1. Te wywołania są rozłożone na wszystkie klasy pomocnicze lub usługowe, które wykorzystują odpowiedź API.
Rozwiązanie: Wyszukaj wszystkie wzorce SelectMany i w.Symbols w swoim kodzie i zastąp je bezpośrednim dostępem do właściwości obiektów wyników IronOCR. Przewodnik jak czytać wyniki obejmuje każdą dostępną właściwość na OcrResult, OcrResult.Page, OcrResult.Paragraph, OcrResult.Line i 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" .
Zastąp każde wystąpienie:
// 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
Problem 3: Kod przetwarzania plików PDF nie kompiluje się po usunięciu pliku Storage.V1
Google Cloud Vision: Po usunięciu Google.Cloud.Storage.V1 cały kod, który odnosi się do StorageClient, UploadObjectAsync, DeleteObjectAsync, AsyncAnnotateFileRequest, GcsSource, GcsDestination i PollUntilCompletedAsync, nie będzie się kompilować. Ten kod może obejmować wiele klas serwisowych i zazwyczaj reprezentuje największy pojedynczy blok zmian.
Rozwiązanie: Usuń cały potok GCS. Zastąp metodę asynchroniczną liczącą ponad 50 wierszy jej trzywierszowym odpowiednikiem w IronOCR. Dla kodu, który utrzymywał asynchroniczną sygnaturę dla zgodności z wywołującym, opakuj to w 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
Dla nowego kodu użyj natywnego wsparcia Asynchronicznego OCR zamiast otoczki Task.Run. Przewodnik dotyczący plików PDF obejmuje wybór zakresu stron oraz ładowanie plików PDF chronionych hasłem.
Problem 4: Logika ponownych prób w ramach limitu szybkości nie jest już potrzebna
Google Cloud Vision: Każdy kod, który przechwytuje RpcException z StatusCode.ResourceExhausted i implementuje wzorzec oczekiwania i ponawiania, był napisany w celu obsługi limitu 1 800 żądań na minutę. Ta logika ponownej próby może być wbudowana w oprogramowanie pośredniczące, etapy potoku lub pętle przetwarzania wsadowego.
Rozwiązanie: Usuń całą logikę ponownych prób związaną z błędami limitów.IronOCR przetwarza dane lokalnie bez zewnętrznych limitów. Kontrakt obsługi błędów zmienia się z pięciu przypadków RpcException na dwa:
// 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
Problem 5: Wielostronicowy plik TIFF wymaga pętli wyodrębniania ramek
Google Cloud Vision: Istniejący kod przetwarzający TIFF prawdopodobnie wyodrębnia klatki za pomocą System.Drawing.Image, zapisuje każdą klatkę jako JPEG w katalogu tymczasowym, przesyła każdy JPEG jako osobne wywołanie API, a następnie usuwa tymczasowe pliki. Ten wzorzec zużywa jedną jednostkę limitu na klatkę i może pozostawić osierocone pliki tymczasowe w przypadku awarii.
Rozwiązanie: Zastąp pętlę ekstrakcji klatki i zarządzanie plikami tymczasowymi za pomocą input.LoadImageFrames(). Cała pętla ramkowa System.Drawing została usunięta:
// 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)
Zapoznaj się z instrukcją dotyczącą plików TIFF i GIF, aby poznać opcje przetwarzania wielu klatek, w tym wybór zakresu klatek.
Problem 6: Błąd w obliczeniach wierzchołków BoundingPoly
Google Cloud Vision: Kod, który odczytywał współrzędne prostokąta ograniczającego z annotation.BoundingPoly.Vertices, obliczał X, Y, Szerokość i Wysokość za pomocą arytmetyki indeksu wierzchołka: vertices[0].X dla X, vertices[1].X - vertices[0].X dla Szerokości, vertices[2].Y - vertices[0].Y dla Wysokości. Po migracji, te wyrażenia nie mają równoważnika w IronOCR, ponieważ Vertices nie istnieje.
Rozwiązanie: Zastąp arytmetykę wierzchołków bezpośrednimi właściwościami typu int. Nie są wymagane żadne obliczenia:
// 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 kontrolna migracjiGoogle Cloud VisionOCR
Przed migracją
Przed wprowadzeniem jakichkolwiek zmian należy przeprowadzić audyt kodu źródłowego w celu zidentyfikowania wszystkich zależności od Google Cloud Vision:
# Find allGoogle Cloud Visionnamespace 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 allGoogle Cloud Visionnamespace 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" .
Uwagi dotyczące przygotowania przed rozpoczęciem:
- Wymień wszystkie zasoby GCS utworzone dla danych wejściowych/wyjściowych OCR — zaplanuj czyszczenie po migracji
- Zapisz adres e-mail konta usługowego, aby można było je wyłączyć w konsoli GCP po migracji
- Zidentyfikuj wszystkie środowiska, w których
GOOGLE_APPLICATION_CREDENTIALSjest skonfigurowane - Należy odnotować każdy kod, który odczytuje identyfikatory projektów GCP lub nazwy zasobników z konfiguracji — należy go usunąć po migracji
Migracja kodu
- Usuń pakiet NuGet
Google.Cloud.Vision.V1ze wszystkich projektów - Usuń pakiet NuGet
Google.Cloud.Storage.V1ze wszystkich projektów - Zainstaluj pakiet NuGet
IronOcrwe wszystkich projektach, które wykonują OCR - Dodaj inicjalizację
IronOcr.License.LicenseKeyprzy starcie aplikacji - Zastąp wszystkie importy
using Google.Cloud.Vision.V1importamiusing IronOcr - Zastąp wszystkie importy
using Google.Cloud.Storage.V1iusing Grpc.Core - Zastąp
ImageAnnotatorClient.Create()przeznew IronTesseract() - Usuń wszystkie metody potoku GCS (
StorageClient,UploadObjectAsync, adnotacja asynchroniczna, polling, pobieranie, usuwanie) - Zastąp
input.LoadPdf()dla wszystkich ścieżek przetwarzania PDF (usuwając asynchroniczną orkiestrację GCS) - Zastąp wszystkie pętle złączania symboli Protobuf bezpośrednim dostępem do właściwości
.Textna obiektachOcrResult - Zastąp obliczenia indeksu
BoundingPoly.Verticesprzezword.X,word.Y,word.Width,word.Height - Usuń wszystkie bloki przechwytywania
RpcExceptiondlaResourceExhausted,PermissionDenied,Unavailable,DeadlineExceedediUnauthenticated - Zastąp pętlę po klatkach TIFF za pomocą
input.LoadImageFrames() - Przekształć sekwencyjne pętle wsadowe na
Parallel.ForEachz instancjamiIronTesseractprzypadającymi na wątek - Usuń
GOOGLE_APPLICATION_CREDENTIALSze wszystkich konfiguracji środowiska, potoków CI/CD, plików Docker Compose i tajemnic Kubernetes
Po migracji
- Zweryfikuj, że żaden z typów
RpcExceptionlubGoogleApiExceptionnie jest nadal referencjonowany w kodzie obsługi błędów - Potwierdź, że
GOOGLE_APPLICATION_CREDENTIALSnie występuje we wszystkich konfiguracjach środowiska wdrożeniowego - Uruchom proces OCR na tym samym zestawie przykładowych dokumentów, co w środowisku produkcyjnym, i porównaj jakość wygenerowanego tekstu
- Przetestuj przetwarzanie plików PDF na dokumentach, które wcześniej były obsługiwane przez asynchroniczny potok GCS, i sprawdź, czy wynik tekstowy jest identyczny
- Testuj PDF-y zabezpieczone hasłem za pomocą
input.LoadPdf(path, Password: "...")— wcześniej nieobsługiwane - Testuj przetwarzanie wielostronicowych TIFF-ów używając
input.LoadImageFrames()i potwierdź, że wszystkie klatki są przetwarzane - Uruchom procesor wsadowy na reprezentatywnej próbce i sprawdź, czy jakość wyników odpowiada poprzednim wynikom
- Potwierdź, że wartości
result.Confidencemieszczą się w akceptowalnym zakresie dla twojego korpusu dokumentu - Zweryfikuj, że przeszukiwalny wynik PDF używając
result.SaveAsSearchablePdf()dla dokumentów, które wcześniej wymagały oddzielnej biblioteki PDF - Uruchom aplikację w środowisku bez połączenia z Internetem i sprawdź, czy OCR działa poprawnie
Kluczowe korzyści z migracji do IronOCR
Powierzchnia poświadczeń zredukowana do zera plików. Po migracji nie ma plików kluczy JSON, konfiguracji kubełków GCS, rol IAM, kont serwisowych i zmiennych środowiskowych GOOGLE_APPLICATION_CREDENTIALS w twojej infrastrukturze. Cała powierzchnia poświadczeń to jedna zmienna środowiskowa zawierająca ciąg znaków klucza licencyjnego. Rotacja kluczy, która była obowiązkową operacją okresową w Google Cloud Vision, nie jest już stosowana. W przypadku zespołów działających w wielu regionach lub korzystających z usług wielu dostawców chmury, zmniejszenie złożoności konfiguracji wdrożenia jest natychmiastowe.
Przetwarzanie plików PDF i TIFF bez zewnętrznych zależności. Asynchroniczny potok GCS oraz pętla ramek TIFF w System.Drawing zostały całkowicie usunięte. input.LoadPdf() i input.LoadImageFrames() są zamiennikami — oba synchroniczne, oba lokalne, oba zajmują trzy linie od wywołania do wyniku. Pliki PDF chronione hasłem, których przetwarzanie było niemożliwe w Google Cloud Vision, działają po dodaniu jednego dodatkowego parametru. Przewodnik dotyczący OCR plików PDF oraz przewodnik dotyczący danych wejściowych w formacie TIFF obejmują pełny interfejs API danych wejściowych.
Przetwarzanie wsadowe z prędkością procesora. Zniesienie limitu 1800 żądań na minutę oraz obowiązkowego 60-sekundowego czasu oczekiwania na ponowną próbę oznacza, że zadania wsadowe, które wcześniej podlegały ograniczeniom szybkości, teraz działają z prędkością dostępnych rdzeni procesora. Komputer z 16 rdzeniami przetwarza 16 dokumentów jednocześnie bez żadnej zewnętrznej akceptacji. Wzorzec Parallel.ForEach z instancjami IronTesseract przypadającymi na wątek jest bezpośrednim zamiennikiem dla ograniczonej pętli sekwencyjnej. Przewodnik optymalizacji szybkości opisuje opcje konfiguracji silnika, które dostosowują wydajność do określonych typów dokumentów.
Strukturalne dane bez Protobuf. Każdy OcrResult udostępnia Text, Confidence, Pages, Paragraphs, Lines, Words i Characters jako typowane właściwości .NET bez zależności od przestrzeni nazw Protobuf, bez konkatynacji symboli i bez arytmetyki wierzchołków dla prostokątów ograniczających. Kod, który wcześniej wymagał zagnieżdżonych pętli z 20 liniami do wyodrębnienia tekstu akapitu, zmniejsza się do result.Paragraphs.Select(p => p.Text). Dla przypadków użycia, które wymagają pozycjonowania na poziomie słowa do analizy układu dokumentu, dostępne są bezpośrednio word.X, word.Y, word.Width i word.Height. Strona wyników OCR zawiera wszystkie właściwości modelu wyników.
Wbudowana funkcja tworzenia plików PDF z możliwością wyszukiwania.Google Cloud Visionzwraca wyłącznie tekst — tworzenie plików PDF z możliwością wyszukiwania wymagało osobnej biblioteki do generowania plików PDF, co wiązało się z dodaniem kolejnej zależności NuGet, kolejnego API do opanowania oraz dodatkowej licencji do oceny. IronOCR's result.SaveAsSearchablePdf(outputPath) produkuje w pełni przeszukiwalny PDF z dowolnego wyniku OCR w jednej linii. Dla przepływów pracy archiwizacji dokumentów i procesów odkrywania prawnych, to eliminuje całą zależność. Przykładowy plik PDF z funkcją wyszukiwania ilustruje ten schemat od początku do końca.
Suwerenność danych dla branż podlegających regulacjom. Dokumenty przetwarzane przez IronOCR nigdy nie opuszczają serwera. W przypadku dokumentacji medycznej objętej HIPAA, danych technicznych podlegających kontroli ITAR, materiałów wykonawców z sektóra obronnego objętych CMMC, dokumentów prawnych objętych tajemnicą adwokacką oraz dokumentacji finansowej objętej PCI-DSS architektura lokalna całkowicie wyklucza kategorię zewnętrznych podmiotów przetwarzających dane z zakresu zgodności. Nie ma umowy o współpracy biznesowej do wynegocjowania, nie ma umowy o przetwarzaniu danych (DPA) do podpisania ani polityki Google dotyczącej przechowywania danych do przejrzenia. Centrum dokumentacji IronOCR obejmuje konfiguracje wdrożeniowe dla środowisk Docker, Linux, Azure i AWS, w których obowiązują wymagania dotyczące lokalizacji danych.
Często Zadawane Pytania
Dlaczego warto przejść z Google Cloud Vision API na IronOCR?
Typowe czynniki motywujące to eliminacja złożoności interoperacyjności COM, zastąpienie zarządzania licencjami opartego na plikach, uniknięcie rozliczeń za stronę, umożliwienie wdrażania w Dockerze/kontenerach oraz przyjęcie natywnego dla NuGet przepływu pracy, który integruje się ze standardowymi narzędziami .NET.
Jakie są główne zmiany w kodzie podczas migracji z Google Cloud Vision API do IronOCR?
Zastąp sekwencje inicjalizacji Google Cloud Vision instancjonowaniem IronTesseract, usuń zarządzanie cyklem życia COM (jawne wzorce Create/Load/Close) i zaktualizuj nazwy właściwości wyników. W rezultacie znacznie zmniejsza się liczba powtarzających się linii kodu.
Jak zainstalować IronOCR, aby rozpocząć migrację?
Uruchom polecenie „Install-Package IronOcr” w konsoli menedżera pakietów lub „dotnet add package IronOcr” w interfejsie CLI. Pakiety językowe są oddzielnymi pakietami: na przykład „dotnet add package IronOcr.Languages.French” dla języka francuskiego.
Czy IronOCR dorównuje dokładnością OCR interfejsowi API Google Cloud Vision w przypadku standardowych dokumentów biznesowych?
IronOCR zapewnia wysoką dokładność w przypadku standardowych treści biznesowych, w tym faktur, umów, paragonów i formularzy wypełnionych na komputerze. Filtry przetwarzania wstępnego obrazu (prostowanie, usuwanie szumów, wzmacnianie kontrastu) dodatkowo poprawiają rozpoznawanie w przypadku pogorszonej jakości danych wejściowych.
W jaki sposób IronOCR obsługuje dane językowe, które Google Cloud Vision API instaluje oddzielnie?
Dane językowe w IronOCR są dystrybuowane jako pakiety NuGet. Polecenie „dotnet add package IronOcr.Languages.German” instaluje obsługę języka niemiećkiego. Nie wymaga to ręcznego umieszczania plików ani podawania ścieżek katalogów.
Czy migracja z Google Cloud Vision API do IronOCR wymaga zmian w infrastrukturze wdrożeniowej?
IronOCR wymaga mniej zmian w infrastrukturze niż Google Cloud Vision API. Nie ma ścieżek binarnych SDK, lokalizacji plików licencyjnych ani konfiguracji serwerów licencyjnych. Pakiet NuGet zawiera kompletny silnik OCR, a klucz licencyjny jest ciągiem znaków ustawionym w kodzie aplikacji.
Jak skonfigurować licencjonowanie IronOCR po migracji?
W kodzie uruchamiającym aplikację przypisz IronOcr.License.LicenseKey = „YOUR-KEY”. W Dockerze lub Kubernetesie zapisz klucz jako zmienną środowiskową i odczytaj go podczas uruchamiania. Użyj License.IsValidLicense do sprawdzenia ważności przed przyjęciem ruchu.
Czy IronOCR może przetwarzać pliki PDF w taki sam sposób jak Google Cloud Vision?
Tak. IronOCR odczytuje zarówno natywne, jak i zeskanowane pliki PDF. Należy utworzyć instancję IronTesseract, wywołać ocr.Read(input), gdzie input jest ścieżką do pliku PDF lub obiektem OcrPdfInput, a następnie iterować strony OcrResult. Nie jest wymagany oddzielny proces renderowania plików PDF.
W jaki sposób IronOCR radzi sobie z wątkami podczas przetwarzania dużych ilości danych?
IronTesseract można bezpiecznie instancjonować dla każdego wątku. Uruchom jedną instancję na wątek w Parallel.ForEach lub puli zadań, uruchom OCR równolegle i usuń każdą instancję po zakończeniu. Nie jest wymagany żaden stan globalny ani blokowanie.
Jakie formaty wyjściowe obsługuje IronOCR po wyodrębnieniu tekstu?
IronOCR zwraca ustrukturyzowane wyniki, w tym tekst, współrzędne słów, wyniki pewności i strukturę strony. Opcje eksportu obejmują zwykły tekst, PDF z możliwością wyszukiwania oraz obiekty wyników ustrukturyzowanych do dalszego przetwarzania.
Czy ceny IronOCR są bardziej przewidywalne niż Google Cloud Vision API w przypadku skalowania obciążeń?
IronOCR stosuje licencję wieczystą z opłatą ryczałtową, bez opłat za stronę lub wolumen. Niezależnie od tego, czy przetwarzasz 10 000, czy 10 milionów stron, koszt licencji pozostaje stały. Opcje licencji wolumenowych i zespołowych znajdują się na stronie z cennikiem IronOCR.
Co stanie się z moimi istniejącymi testami po migracji z Google Cloud Vision API do IronOCR?
Testy sprawdzające wyodrębnioną treść tekstową powinny nadal przechodzić pomyślnie po migracji. Testy weryfikujące wzorce wywołań API lub cykl życia obiektów COM będą wymagały aktualizacji, aby odzwierciedlały prostszy model inicjalizacji i wyników IronOCR.

