Jak wykonać OCR tablic rejestracyjnych w języku C#
Ten przewodnik jest przeznaczony dla programistów .NET, którzy przenoszą zadania związane z OCR zKofax OmniPageCapture SDK (obecnie sprzedawanego jako Tungsten Automation) do IronOCR. Obejmuje to pełną ścieżkę migracji: usunięcie zależności od instalatora SDK, wyeliminowanie procedur związanych z cyklem życia silnika, zastąpienie wzorców rozpoznawania opartych na strefach oraz unowocześnienie obsługi błędów. Nie jest wymagana wcześniejsza lektura artykułu porównawczego.
Dlaczego warto przejść zKofax OmniPage(Tungsten)
OmniPage Capture SDK został zaprojektowany z myślą o infrastrukturze zarządzania dokumentami w przedsiębiorstwie w czasach przed pojawieniem się NuGet, Docker i potoków CI/CD. Każdy z wyborów architektonicznych, które miały sens w 2005 roku, powoduje tarcia w 2026 roku.
Instalator SDK nie ma miejsca w świecie NuGet. Każda maszyna programistyczna, agent kompilacji i serwer produkcyjny, na którym działa kod OmniPage, wymaga uruchomienia instalatora Tungsten SDK przed kompilacją. Nie ma .csproj referencji pakietu do przywrócenia. Aktualizacja wersji SDK oznacza ponowne uruchomienie instalatora na całej flocie. Nowy programista nie może uruchomić projektu bez skontaktowania się z osobą zarządzającą licencją SDK i oczekiwania na dostęp do instalatora.
Plik licencyjny jest operacyjną odpowiedzialnością. Plik .lic musi znajdować się na konkretnej ścieżce na każdej maszynie, która wywołuje engine.Initialize(). Wystarczy wdrożyć na nowym serwerze i zapomnieć o pliku, a każde wywołanie OCR zakończy się niepowodzeniem z wyjątkiem licencji — a nie błędem OCR. Użycie licencji pływających i podziały sieci między serwerem aplikacji a serwerem licencji oznacza, że każde wywołanie Initialize() się nie powiedzie, dopóki połączenie nie zostanie przywrócone, niezależnie od tego, czy maszyna pomyślnie zweryfikowała swoją licencję wczoraj.
Zarządzanie cyklem życia silnika powoduje przecieki zasobów na ścieżkach błędów. OmniPageEngine.Shutdown() musi być wywołany na każdej możliwej ścieżce kodu - włącznie z obsługą wyjątków, wczesnymi zwrotami i limitami czasowymi - w przeciwnym razie fotel licencji pływającej pozostanie zablokowany, dopóki nie wygaśnie limit czasowy wypożyczenia serwera licencji. Pisanie obronne wokół tego ograniczenia oznacza otaczanie każdego punktu integracji IDisposable wzorcami istniejącymi wyłącznie w celu ochrony przed pominięciem wyłączenia silnika.
Identyfikacja sprzętowa koliduje z nowoczesnym wdrażaniem. Aktywacja OmniPage jest powiązana ze sprzętem. Ponowne uruchamianie kontenerów, migracje maszyn wirtualnych i automatyczne skalowanie w chmurze wiążą się ze zmianami tożsamości sprzętu, które powodują konieczność ponownej aktywacji. Model wdrożenia niezgodny z autoskalowaniem to model wdrożenia niezgodny z infrastrukturą chmury.
Ceny za stronę są nieprzewidywalne w przypadku dużych projektów. Nagły wzrost liczby przetwarzanych dokumentów — na przykład w wyniku pozyskania nowego klienta lub przesłania partii zaległych zadań — generuje fakturę za przekroczenie limitu, którego nikt nie uwzględnił w budżecie.IronOCR jest bezterminowy i nieograniczony w ramach danego poziomu licencji: bez liczenia stron, bez limitów, bez faktur za przekroczenie limitu.
Proces zamówień blokuje wysyłkę. Pakiet OmniPage SDK jest dostępny wyłącznie w ramach sprzedaży. Dostęp do wersji testowej, ceny i warunki umowy wymagają kontaktu z działem sprzedaży, który trwa od 4 do 12 tygodni. Zespoły tworzące funkcje OCR, które mają termin dostawy produktu, nie mogą czekać na cykl zakupowy w firmie Enterprise. dotnet add package IronOcr rozwiązuje się w 30 sekund. Zobacz stronę licencyjną IronOCR po opublikowane, samoobsługowe ceny — $999 Lite do $2,999 Enterprise, wszystko wieczyste.
Podstawowy problem
OmniPage wymaga pełnej procedury inicjalizacji przed odczytaniem pierwszego bajtu oraz procedury zamknięcia po odczytaniu ostatniego — każde miejsce wywołania musi zarządzać obiema:
// OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
using var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic"); // File must exist here
engine.Initialize(); // Network call to license server — can fail for license reasons, not OCR reasons
var document = engine.CreateDocument();
document.AddPage("invoice.jpg");
document.Recognize(new RecognitionSettings { Language = "English" });
string text = document.GetText();
document.Dispose(); // Must not be skipped
engine.Shutdown(); // Must not be skipped — omitting this locks a floating seat
// OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
using var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic"); // File must exist here
engine.Initialize(); // Network call to license server — can fail for license reasons, not OCR reasons
var document = engine.CreateDocument();
document.AddPage("invoice.jpg");
document.Recognize(new RecognitionSettings { Language = "English" });
string text = document.GetText();
document.Dispose(); // Must not be skipped
engine.Shutdown(); // Must not be skipped — omitting this locks a floating seat
Imports OmniPage
' OmniPage: Ceremony required on EVERY entry point — init, process, shutdown
Using engine As New OmniPageEngine()
engine.SetLicenseFile("C:\Program Files\OmniPage\license.lic") ' File must exist here
engine.Initialize() ' Network call to license server — can fail for license reasons, not OCR reasons
Dim document = engine.CreateDocument()
document.AddPage("invoice.jpg")
document.Recognize(New RecognitionSettings With {.Language = "English"})
Dim text As String = document.GetText()
document.Dispose() ' Must not be skipped
engine.Shutdown() ' Must not be skipped — omitting this locks a floating seat
End Using
// IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // At app startup — once, ever
var text = new IronTesseract().Read("invoice.jpg").Text;
// IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"; // At app startup — once, ever
var text = new IronTesseract().Read("invoice.jpg").Text;
Imports IronOcr
' IronOCR: One NuGet package, one line at startup, one call to read
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY" ' At app startup — once, ever
Dim text = New IronTesseract().Read("invoice.jpg").Text
Wersja OmniPage składa się z ośmiu odrębnych etapów, dwóch obowiązkowych wywołań czyszczących, zależności sieciowej oraz zależności od systemu plików. Wersja IronOCR ma dwie.
IronOCR aKofax OmniPage(Tungsten): porównanie funkcji
Poniższa tabela przedstawia funkcje najbardziej istotne dla zespołów rozważających tę migrację.
| Funkcja | Kofax OmniPage SDK | IronOCR |
|---|---|---|
| Sposób instalacji | Niestandardowy instalator SDK (bez NuGet) | dotnet add package IronOcr |
| Czas do pierwszego wywołania OCR | 4–12 tygodni (zamówienie) + godziny (konfiguracja) | 30 sekund |
| Mechanizm licencji | Plik .lic na dysku + opcjonalny serwer licencji |
Klucz ciągu znaków przy uruchomieniu aplikacji |
| Identyfikacja sprzętu | Tak (reaktywacja po migracji maszyny wirtualnej) | Nie |
| Wymagany serwer licencji | Tak (w przypadku licencji pływających) | Nie |
| Rozliczenie według czasu pracy na stronę | Dostępne (zmienne) | Nie |
| Opublikowane ceny | Nie (skontaktuj się z działem sprzedaży) | Tak ($999 / $1,499 / $2,999 wieczyste) |
| Roczne opłaty za utrzymanie | 18–25% kosztu licencji | Opcjonalnie |
| Zarządzanie cyklem życia silnika | Wymagane (Initialize / Shutdown) |
Nie jest wymagane |
| Windows x64 | Tak | Tak |
| Linux | Tak (dodano w styczniu 2026 r.) | Tak (wszystkie wersje) |
| macOS | Nie | Tak |
| Docker / Kubernetes | Trudne (instalator + plik licencji na obrazku) | Proste (tylko pakiet NuGet) |
| Azure / AWS Lambda | Złożone (dostępność serwera licencji) | Proste |
| Formaty obrazów | Tak | JPG, PNG, BMP, TIFF, GIF i inne |
| Natywne wprowadzanie plików PDF | Tak (może wymagać licencji modułowej) | Tak (wbudowane, bez dodatkowej licencji) |
| Wielostronicowy plik TIFF | Tak | Tak |
| Wynik w formacie PDF z możliwością wyszukiwania | Tak | Tak (result.SaveAsSearchablePdf()) |
| Automatyczne przetwarzanie wstępne | Konfiguracja obiektu Settings | Wbudowany + jawny interfejs API potoku |
| Obsługiwane języki | 120+ | Ponad 125 (oddzielne pakiety NuGet) |
| Wyjście danych ustrukturyzowanych | Tak (koordynaty słów) | Strony, akapity, wiersze, słowa, znaki z współrzędnymi |
| Ocena pewności | Tak | Tak (result.Confidence, na słowo) |
| Odczytywanie BarCode | Moduł dodatkowy (oddzielna licencja) | Wbudowane (ocr.Configuration.ReadBarCodes = true) |
| Bezpieczeństwo wątków | Złożone (ograniczenia związane z współdzieleniem silnika) | Pełne (jeden IronTesseract na wątek) |
| Obsługa potoku CI/CD | Wymaga instalatora na każdym agencie | Standardowy dotnet restore |
Szybki start: Migracja zKofax OmniPagedo IronOCR
Krok 1: Zastąp SDK pakietem NuGet
OmniPage nie posiada pakietu NuGet, który można by usunąć. Usuń ręczne referencje DLL z pliku .csproj i odinstaluj SDK z maszyn deweloperskich po zakończeniu migracji. Następnie zainstaluj IronOCR:
dotnet add package IronOcr
Pakiet IronOCR NuGet zawiera silnik OCR, filtry przetwarzania wstępnego oraz komponenty środowiska uruchomieniowego. Bez oddzielnego instalatora, bez natywnego zarządzania bibliotekami DLL, bez rejestracji komponentów.
Krok 2: Aktualizacja przestrzeni nazw
// Before (Kofax OmniPage)
using Kofax.OmniPage.CSDK;
using Kofax.OmniPageCSDK;
using CSDK;
// After (IronOCR)
using IronOcr;
// Before (Kofax OmniPage)
using Kofax.OmniPage.CSDK;
using Kofax.OmniPageCSDK;
using CSDK;
// After (IronOCR)
using IronOcr;
Imports IronOcr
Krok 3: Inicjalizacja licencji
Dodaj jedną linię podczas uruchamiania aplikacji. Zastępuje to ścieżkę pliku .lic, konfigurację serwera licencji i wywołanie engine.Initialize():
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Przechowuj klucz w zmiennej środowiskowej (IRONOCR_LICENSE_KEY) lub appsettings.json zamiast w kodzie źródłowym. Klucz jest odczytywany w trybie offline — w czasie wykonywania nie następuje żadne połączenie sieciowe.
Przykłady migracji kodu
Błąd inicjalizacji silnika Zastąpienie ścieżki
Najbardziej niebezpiecznym aspektem modelu cyklu życia OmniPage nie jest ścieżka pomyślna — jest to ścieżka błędów. Każdy wyjątek rzucony między engine.Initialize() a engine.Shutdown() może pozostawić zablokowane miejsce licencji pływającej, jeśli Shutdown() nie zostanie osiągnięty. Kod produkcyjny wymaga bloków try/finally wokół każdego wywołania przetwarzania dokumentu:
Podejście Kofax OmniPage:
// OmniPage: try/finally required everywhere to protect license seat release
public string ProcessInvoice(string imagePath)
{
var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic");
try
{
engine.Initialize(); // Contacts license server — failure here locks nothing
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(new RecognitionSettings { Language = "English" });
return document.GetText();
}
catch (RecognitionException ex)
{
// Log, rethrow — but Shutdown must still be called
throw new OcrProcessingException("Recognition failed", ex);
}
finally
{
document.Dispose(); // Inner finally: release document
}
}
catch (LicenseValidationException ex)
{
throw new OcrProcessingException("License validation failed", ex);
}
finally
{
engine.Shutdown(); // Outer finally: release license seat — MUST execute
}
}
// OmniPage: try/finally required everywhere to protect license seat release
public string ProcessInvoice(string imagePath)
{
var engine = new OmniPageEngine();
engine.SetLicenseFile(@"C:\Program Files\OmniPage\license.lic");
try
{
engine.Initialize(); // Contacts license server — failure here locks nothing
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(new RecognitionSettings { Language = "English" });
return document.GetText();
}
catch (RecognitionException ex)
{
// Log, rethrow — but Shutdown must still be called
throw new OcrProcessingException("Recognition failed", ex);
}
finally
{
document.Dispose(); // Inner finally: release document
}
}
catch (LicenseValidationException ex)
{
throw new OcrProcessingException("License validation failed", ex);
}
finally
{
engine.Shutdown(); // Outer finally: release license seat — MUST execute
}
}
Imports System
' OmniPage: try/finally required everywhere to protect license seat release
Public Function ProcessInvoice(imagePath As String) As String
Dim engine As New OmniPageEngine()
engine.SetLicenseFile("C:\Program Files\OmniPage\license.lic")
Try
engine.Initialize() ' Contacts license server — failure here locks nothing
Dim document = engine.CreateDocument()
Try
document.AddPage(imagePath)
document.Recognize(New RecognitionSettings With {.Language = "English"})
Return document.GetText()
Catch ex As RecognitionException
' Log, rethrow — but Shutdown must still be called
Throw New OcrProcessingException("Recognition failed", ex)
Finally
document.Dispose() ' Inner finally: release document
End Try
Catch ex As LicenseValidationException
Throw New OcrProcessingException("License validation failed", ex)
Finally
engine.Shutdown() ' Outer finally: release license seat — MUST execute
End Try
End Function
Podejście IronOCR:
// IronOCR: Nie license seat to release, no engine to shut down
public string ProcessInvoice(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
var ocr = new IronTesseract();
var result = ocr.Read(input);
return result.Text;
// OcrInput disposal is automatic — no license implications on any code path
}
// IronOCR: Nie license seat to release, no engine to shut down
public string ProcessInvoice(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
var ocr = new IronTesseract();
var result = ocr.Read(input);
return result.Text;
// OcrInput disposal is automatic — no license implications on any code path
}
Imports IronOcr
Public Function ProcessInvoice(imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
Return result.Text
End Using
' OcrInput disposal is automatic — no license implications on any code path
End Function
Blok using na OcrInput obsługuje oczyszczanie pamięci dla załadowanych danych obrazowych. Nie istnieje try/finally, aby chronić miejsce licencji. Wyjątek w dowolnym miejscu tej metody nie ma skutków ubocznych poza zakresem samej metody. Opcje konfiguracyjne, w tym instancje lokalne dla wątków, można znaleźć w przewodniku konfiguracji IronTesseract.
Migracja oparta na rozpoznawaniu stref
Głównym mechanizmem ekstrakcji ustrukturyzowanych danych w OmniPage jest definiowanie stref: obszary dokumentu są deklarowane za pomocą granic współrzędnych i typu rozpoznawania dla każdej strefy (OCR, ICR, OMR, BarCode). Deweloperzy definiują obiekty FormZone na dokument szablonu i przekazują je do maszyny rozpoznawania.IronOCR używa CropRectangle, aby osiągnąć tę samą docelową ekstrakcję bez zarządzania szablonami lub deklaracji typów stref:
Podejście Kofax OmniPage:
// OmniPage: Zone-based form extraction with template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Define zones per document template
var zones = new List<FormZone>
{
new FormZone { Name = "InvoiceNumber", Type = "OCR",
X = 520, Y = 80, Width = 200, Height = 30 },
new FormZone { Name = "InvoiceDate", Type = "OCR",
X = 520, Y = 120, Width = 200, Height = 30 },
new FormZone { Name = "TotalAmount", Type = "OCR",
X = 520, Y = 580, Width = 200, Height = 30 },
new FormZone { Name = "VendorName", Type = "OCR",
X = 50, Y = 80, Width = 300, Height = 40 }
};
var template = new FormTemplate { Name = "StandardInvoice", Zones = zones };
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(invoicePath);
document.ApplyTemplate(template);
document.Recognize(settings);
var fields = new Dictionary<string, string>();
foreach (var zone in zones)
fields[zone.Name] = document.GetZoneText(zone.Name);
document.Dispose();
engine.Shutdown();
return fields;
}
// OmniPage: Zone-based form extraction with template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Define zones per document template
var zones = new List<FormZone>
{
new FormZone { Name = "InvoiceNumber", Type = "OCR",
X = 520, Y = 80, Width = 200, Height = 30 },
new FormZone { Name = "InvoiceDate", Type = "OCR",
X = 520, Y = 120, Width = 200, Height = 30 },
new FormZone { Name = "TotalAmount", Type = "OCR",
X = 520, Y = 580, Width = 200, Height = 30 },
new FormZone { Name = "VendorName", Type = "OCR",
X = 50, Y = 80, Width = 300, Height = 40 }
};
var template = new FormTemplate { Name = "StandardInvoice", Zones = zones };
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(invoicePath);
document.ApplyTemplate(template);
document.Recognize(settings);
var fields = new Dictionary<string, string>();
foreach (var zone in zones)
fields[zone.Name] = document.GetZoneText(zone.Name);
document.Dispose();
engine.Shutdown();
return fields;
}
Imports System
Imports System.Collections.Generic
' OmniPage: Zone-based form extraction with template management
Public Function ExtractInvoiceFields(invoicePath As String) As Dictionary(Of String, String)
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
' Define zones per document template
Dim zones As New List(Of FormZone) From {
New FormZone With {.Name = "InvoiceNumber", .Type = "OCR", .X = 520, .Y = 80, .Width = 200, .Height = 30},
New FormZone With {.Name = "InvoiceDate", .Type = "OCR", .X = 520, .Y = 120, .Width = 200, .Height = 30},
New FormZone With {.Name = "TotalAmount", .Type = "OCR", .X = 520, .Y = 580, .Width = 200, .Height = 30},
New FormZone With {.Name = "VendorName", .Type = "OCR", .X = 50, .Y = 80, .Width = 300, .Height = 40}
}
Dim template As New FormTemplate With {.Name = "StandardInvoice", .Zones = zones}
Dim settings As New RecognitionSettings With {.Language = "English"}
Dim document = engine.CreateDocument()
document.AddPage(invoicePath)
document.ApplyTemplate(template)
document.Recognize(settings)
Dim fields As New Dictionary(Of String, String)()
For Each zone In zones
fields(zone.Name) = document.GetZoneText(zone.Name)
Next
document.Dispose()
engine.Shutdown()
Return fields
End Using
End Function
Podejście IronOCR:
// IronOCR: CropRectangle targets specific regions — no template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
var fields = new Dictionary<string, string>();
var ocr = new IronTesseract();
// Extract each field by reading only the target region
var fieldRegions = new Dictionary<string, CropRectangle>
{
["InvoiceNumber"] = new CropRectangle(520, 80, 200, 30),
["InvoiceDate"] = new CropRectangle(520, 120, 200, 30),
["TotalAmount"] = new CropRectangle(520, 580, 200, 30),
["VendorName"] = new CropRectangle(50, 80, 300, 40)
};
foreach (var (fieldName, region) in fieldRegions)
{
using var input = new OcrInput();
input.LoadImage(invoicePath, region);
fields[fieldName] = ocr.Read(input).Text.Trim();
}
return fields;
}
// IronOCR: CropRectangle targets specific regions — no template management
public Dictionary<string, string> ExtractInvoiceFields(string invoicePath)
{
var fields = new Dictionary<string, string>();
var ocr = new IronTesseract();
// Extract each field by reading only the target region
var fieldRegions = new Dictionary<string, CropRectangle>
{
["InvoiceNumber"] = new CropRectangle(520, 80, 200, 30),
["InvoiceDate"] = new CropRectangle(520, 120, 200, 30),
["TotalAmount"] = new CropRectangle(520, 580, 200, 30),
["VendorName"] = new CropRectangle(50, 80, 300, 40)
};
foreach (var (fieldName, region) in fieldRegions)
{
using var input = new OcrInput();
input.LoadImage(invoicePath, region);
fields[fieldName] = ocr.Read(input).Text.Trim();
}
return fields;
}
Imports System.Collections.Generic
' IronOCR: CropRectangle targets specific regions — no template management
Public Function ExtractInvoiceFields(invoicePath As String) As Dictionary(Of String, String)
Dim fields As New Dictionary(Of String, String)()
Dim ocr As New IronTesseract()
' Extract each field by reading only the target region
Dim fieldRegions As New Dictionary(Of String, CropRectangle) From {
{"InvoiceNumber", New CropRectangle(520, 80, 200, 30)},
{"InvoiceDate", New CropRectangle(520, 120, 200, 30)},
{"TotalAmount", New CropRectangle(520, 580, 200, 30)},
{"VendorName", New CropRectangle(50, 80, 300, 40)}
}
For Each kvp In fieldRegions
Dim fieldName = kvp.Key
Dim region = kvp.Value
Using input As New OcrInput()
input.LoadImage(invoicePath, region)
fields(fieldName) = ocr.Read(input).Text.Trim()
End Using
Next
Return fields
End Function
Każdy CropRectangle określa (x, y, width, height) w pikselach. Silnik OCR przetwarza tylko wyznaczony obszar, a nie całą stronę — zapewnia to taką samą wydajność, jaką zapewnia przetwarzanie strefowe w programie OmniPage. Przewodnik po OCR oparty na regionach obejmuje wzorce wyboru współrzędnych, w tym jak wyodrębnić wiele regionów z jednego załadunku obrazu, korzystając z API wielu regionów OcrInput.
Migracja danych strukturalnych
OmniPage udostępnia strukturę dokumentu poprzez własny proces eksportu: ustawienia formatu są stosowane do rozpoznanego dokumentu, a wynik jest zapisywany do pliku w określonym formacie (RTF, XML, CSV, PDF z możliwością wyszukiwania). Dostęp do współrzędnych na poziomie WORD wymaga iteracji iteratora wyników z jawnymi zapytaniami o pozycję.IronOCR udostępnia te same dane strukturalne bezpośrednio w obiekcie wynikowym bez konieczności dodatkowego eksportu:
Podejście Kofax OmniPage:
// OmniPage: Structured output requires format configuration and file export
public void ExtractStructuredContent(string imagePath, string outputDir)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(imagePath);
document.Recognize(settings);
// Word-level coordinates through result iterator
var iterator = document.GetResultIterator(ResultIteratorLevel.Word);
while (iterator.MoveNext())
{
string word = iterator.GetText();
var bounds = iterator.GetBoundingBox();
double confidence = iterator.GetConfidence();
Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%");
}
// Structured export requires separate output format configuration
var outputSettings = new OutputSettings
{
Format = OutputFormat.XML,
IncludeCoordinates = true,
IncludeConfidence = true
};
document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings);
document.Dispose();
engine.Shutdown();
}
// OmniPage: Structured output requires format configuration and file export
public void ExtractStructuredContent(string imagePath, string outputDir)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
var document = engine.CreateDocument();
document.AddPage(imagePath);
document.Recognize(settings);
// Word-level coordinates through result iterator
var iterator = document.GetResultIterator(ResultIteratorLevel.Word);
while (iterator.MoveNext())
{
string word = iterator.GetText();
var bounds = iterator.GetBoundingBox();
double confidence = iterator.GetConfidence();
Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%");
}
// Structured export requires separate output format configuration
var outputSettings = new OutputSettings
{
Format = OutputFormat.XML,
IncludeCoordinates = true,
IncludeConfidence = true
};
document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings);
document.Dispose();
engine.Shutdown();
}
Imports System
Imports System.IO
Public Sub ExtractStructuredContent(imagePath As String, outputDir As String)
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
Dim settings As New RecognitionSettings With {.Language = "English"}
Dim document = engine.CreateDocument()
document.AddPage(imagePath)
document.Recognize(settings)
' Word-level coordinates through result iterator
Dim iterator = document.GetResultIterator(ResultIteratorLevel.Word)
While iterator.MoveNext()
Dim word As String = iterator.GetText()
Dim bounds = iterator.GetBoundingBox()
Dim confidence As Double = iterator.GetConfidence()
Console.WriteLine($"Word: {word} at ({bounds.X},{bounds.Y}) conf:{confidence:F1}%")
End While
' Structured export requires separate output format configuration
Dim outputSettings As New OutputSettings With {
.Format = OutputFormat.XML,
.IncludeCoordinates = True,
.IncludeConfidence = True
}
document.SaveAs(Path.Combine(outputDir, "output.xml"), outputSettings)
document.Dispose()
engine.Shutdown()
End Using
End Sub
Podejście IronOCR:
// IronOCR: Structured data directly on OcrResult — no export step
public void ExtractStructuredContent(string imagePath)
{
var result = new IronTesseract().Read(imagePath);
Console.WriteLine($"Confidence: {result.Confidence:F1}%");
Console.WriteLine($"Pages: {result.Pages.Length}");
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]");
Console.WriteLine(paragraph.Text);
foreach (var word in paragraph.Words)
{
// Per-word confidence and coordinates — no iterator needed
Console.WriteLine(
$" Word: '{word.Text}' " +
$"at ({word.X},{word.Y}) " +
$"conf: {word.Confidence:F1}%");
}
}
}
}
// IronOCR: Structured data directly on OcrResult — no export step
public void ExtractStructuredContent(string imagePath)
{
var result = new IronTesseract().Read(imagePath);
Console.WriteLine($"Confidence: {result.Confidence:F1}%");
Console.WriteLine($"Pages: {result.Pages.Length}");
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]");
Console.WriteLine(paragraph.Text);
foreach (var word in paragraph.Words)
{
// Per-word confidence and coordinates — no iterator needed
Console.WriteLine(
$" Word: '{word.Text}' " +
$"at ({word.X},{word.Y}) " +
$"conf: {word.Confidence:F1}%");
}
}
}
}
Public Sub ExtractStructuredContent(imagePath As String)
Dim result = New IronTesseract().Read(imagePath)
Console.WriteLine($"Confidence: {result.Confidence:F1}%")
Console.WriteLine($"Pages: {result.Pages.Length}")
For Each page In result.Pages
For Each paragraph In page.Paragraphs
Console.WriteLine($"[Paragraph at ({paragraph.X},{paragraph.Y})]")
Console.WriteLine(paragraph.Text)
For Each word In paragraph.Words
' Per-word confidence and coordinates — no iterator needed
Console.WriteLine(
$" Word: '{word.Text}' " &
$"at ({word.X},{word.Y}) " &
$"conf: {word.Confidence:F1}%")
Next
Next
Next
End Sub
Hierarchia obiektu OcrResult — Strony, Akapity, Linie, Słowa, Znaki — dostarcza te same dane pozycyjne, które iteracja wyników OmniPage ujawnia, ale jako nawigowalną strukturę obiektów zamiast sekwencyjnego kursora. Wyniki oceny pewności są dostępne na poziomie wyników, stron, akapitów i słów bez dodatkowej konfiguracji. Przewodnik po wynikach odczytu obejmuje wszystkie właściwości danych strukturalnych, a przewodnik po wynikach pewności obejmuje wzorce walidacji dla poszczególnych WORDów.
Migracja przetwarzania wielostronicowych plików TIFF
Wielostronicowy przepływ pracy OmniPage z plikami TIFF wymaga wyodrębniania stron pojedynczo z kontenera TIFF, z oddzielnym wywołaniem funkcji rozpoznawania i usuwaniem dokumentu dla każdej strony. Powoduje to powstanie zarówno szablonów, jak i obszaru zarządzania zasobami proporcjonalnego do liczby stron.IronOCRładuje wielo-ramkowe pliki TIFF za pomocą jednego wywołania:
Podejście Kofax OmniPage:
// OmniPage: Page-by-page TIFF extraction with per-page lifecycle
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Open TIFF and iterate frames
var tiffContainer = engine.OpenTiff(tiffPath);
int pageCount = tiffContainer.GetPageCount();
var settings = new RecognitionSettings { Language = "English" };
for (int i = 0; i < pageCount; i++)
{
var page = tiffContainer.GetPage(i); // Extract frame
var document = engine.CreateDocument();
try
{
document.AddPage(page);
document.Recognize(settings);
pageTexts.Add(document.GetText());
}
finally
{
document.Dispose(); // Dispose per page to manage memory
}
}
tiffContainer.Dispose();
engine.Shutdown();
return pageTexts;
}
// OmniPage: Page-by-page TIFF extraction with per-page lifecycle
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
var pageTexts = new List<string>();
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
// Open TIFF and iterate frames
var tiffContainer = engine.OpenTiff(tiffPath);
int pageCount = tiffContainer.GetPageCount();
var settings = new RecognitionSettings { Language = "English" };
for (int i = 0; i < pageCount; i++)
{
var page = tiffContainer.GetPage(i); // Extract frame
var document = engine.CreateDocument();
try
{
document.AddPage(page);
document.Recognize(settings);
pageTexts.Add(document.GetText());
}
finally
{
document.Dispose(); // Dispose per page to manage memory
}
}
tiffContainer.Dispose();
engine.Shutdown();
return pageTexts;
}
Imports System.Collections.Generic
' OmniPage: Page-by-page TIFF extraction with per-page lifecycle
Public Function ExtractFromMultiPageTiff(tiffPath As String) As List(Of String)
Dim pageTexts As New List(Of String)()
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
' Open TIFF and iterate frames
Dim tiffContainer = engine.OpenTiff(tiffPath)
Dim pageCount As Integer = tiffContainer.GetPageCount()
Dim settings As New RecognitionSettings With {.Language = "English"}
For i As Integer = 0 To pageCount - 1
Dim page = tiffContainer.GetPage(i) ' Extract frame
Dim document = engine.CreateDocument()
Try
document.AddPage(page)
document.Recognize(settings)
pageTexts.Add(document.GetText())
Finally
document.Dispose() ' Dispose per page to manage memory
End Try
Next
tiffContainer.Dispose()
engine.Shutdown()
End Using
Return pageTexts
End Function
Podejście IronOCR:
// IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // All frames loaded automatically
var result = new IronTesseract().Read(input);
// Each TIFF frame becomes a page in the result
return result.Pages.Select(page => page.Text).ToList();
}
// IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
public List<string> ExtractFromMultiPageTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // All frames loaded automatically
var result = new IronTesseract().Read(input);
// Each TIFF frame becomes a page in the result
return result.Pages.Select(page => page.Text).ToList();
}
Imports System.Collections.Generic
Imports IronOcr
' IronOCR: Multi-frame TIFF loaded in one call — all pages in one result
Public Function ExtractFromMultiPageTiff(tiffPath As String) As List(Of String)
Using input As New OcrInput()
input.LoadImageFrames(tiffPath) ' All frames loaded automatically
Dim result = New IronTesseract().Read(input)
' Each TIFF frame becomes a page in the result
Return result.Pages.Select(Function(page) page.Text).ToList()
End Using
End Function
LoadImageFrames odczytuje każdą klatkę z pojemnika TIFF w jednym wywołaniu. Wynik ujawnia jeden OcrResult.Page na klatkę, zachowując strukturę strona po stronie bez konieczności ręcznego pętlenia iteracyjnego lub oczyszczania na stronę. Dla produkcyjnych przepływów pracy wsadowych TIFF zobacz przewodnik po wejściu TIFF i GIF.
Migracja przetwarzania równoległego bezpiecznego dla wątków
Silnik OmniPage jest współdzielonym zasobem ze stanem. Dzielenie jednej instancji OmniPageEngine przez wątki wymaga zewnętrznej synchronizacji, ponieważ wiele wątków wywołujących CreateDocument() równocześnie może wywołać splątany stan. Bezpieczny wzorzec — jedna instancja silnika na wątek — stoi w konflikcie z dużym kosztem inicjalizacji silnika. Instancje IronOCR nie posiadają współdzielonego stanu: twórz jedną IronTesseract na wątek bez zera nakładu na koordynację:
Podejście Kofax OmniPage:
// OmniPage: Shared engine with locking to serialize document operations
public ConcurrentDictionary<string, string> ProcessBatchWithEngine(
string[] imagePaths, string licensePath)
{
var results = new ConcurrentDictionary<string, string>();
var engineLock = new object();
// One engine — document operations must be serialized
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
Parallel.ForEach(imagePaths, imagePath =>
{
lock (engineLock) // Serialize: one recognition at a time
{
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(settings);
results[imagePath] = document.GetText();
}
finally
{
document.Dispose();
}
}
});
engine.Shutdown();
return results;
}
// OmniPage: Shared engine with locking to serialize document operations
public ConcurrentDictionary<string, string> ProcessBatchWithEngine(
string[] imagePaths, string licensePath)
{
var results = new ConcurrentDictionary<string, string>();
var engineLock = new object();
// One engine — document operations must be serialized
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var settings = new RecognitionSettings { Language = "English" };
Parallel.ForEach(imagePaths, imagePath =>
{
lock (engineLock) // Serialize: one recognition at a time
{
var document = engine.CreateDocument();
try
{
document.AddPage(imagePath);
document.Recognize(settings);
results[imagePath] = document.GetText();
}
finally
{
document.Dispose();
}
}
});
engine.Shutdown();
return results;
}
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
' OmniPage: Shared engine with locking to serialize document operations
Public Function ProcessBatchWithEngine(imagePaths As String(), licensePath As String) As ConcurrentDictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
Dim engineLock As New Object()
' One engine — document operations must be serialized
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
Dim settings As New RecognitionSettings With {.Language = "English"}
Parallel.ForEach(imagePaths, Sub(imagePath)
SyncLock engineLock ' Serialize: one recognition at a time
Dim document = engine.CreateDocument()
Try
document.AddPage(imagePath)
document.Recognize(settings)
results(imagePath) = document.GetText()
Finally
document.Dispose()
End Try
End SyncLock
End Sub)
engine.Shutdown()
End Using
Return results
End Function
Podejście IronOCR:
// IronOCR: One IronTesseract per thread — no locking, genuine parallelism
public ConcurrentDictionary<string, string> ProcessBatchInParallel(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
Parallel.ForEach(imagePaths, imagePath =>
{
// Each thread creates its own instance — no shared state, no locks
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = ocr.Read(input);
results[imagePath] = result.Text;
});
return results;
}
// IronOCR: One IronTesseract per thread — no locking, genuine parallelism
public ConcurrentDictionary<string, string> ProcessBatchInParallel(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
Parallel.ForEach(imagePaths, imagePath =>
{
// Each thread creates its own instance — no shared state, no locks
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = ocr.Read(input);
results[imagePath] = result.Text;
});
return results;
}
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Imports IronOcr
' IronOCR: One IronTesseract per thread — no locking, genuine parallelism
Public Function ProcessBatchInParallel(imagePaths As String()) As ConcurrentDictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
Parallel.ForEach(imagePaths, Sub(imagePath)
' Each thread creates its own instance — no shared state, no locks
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage(imagePath)
Dim result = ocr.Read(input)
results(imagePath) = result.Text
End Using
End Sub)
Return results
End Function
Wersja OmniPage serializuje całe rozpoznawanie za pomocą blokady, co uniemożliwia równoległość. Wersja IronOCR przetwarza dokumenty równolegle, bez konieczności koordynacji. W przypadku obciążeń wsadowych o dużej przepustowości zapoznaj się z przykładem wielowątkowości oraz przewodnikiem po optymalizacji szybkości.
Generowanie plików PDF z możliwością wyszukiwania na podstawie zeskanowanego archiwum
Wyjście PDF wyszukiwalnego OmniPage wymaga złożenia potoku rozpoznawania z wyraźną konfiguracją OutputSettings i OutputFormat przed zapisaniem.IronOCR tworzy pliki PDF z możliwością wyszukiwania na podstawie pojedynczego wywołania metody na obiekcie wynikowym:
Podejście Kofax OmniPage:
// OmniPage: Searchable PDF requires OutputSettings configuration before save
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var recognitionSettings = new RecognitionSettings
{
Language = "English",
AccuracyMode = "Maximum",
PreserveLayout = true
};
var outputSettings = new OutputSettings
{
Format = OutputFormat.SearchablePDF,
Compression = PDFCompression.Standard,
ImageQuality = 85,
EmbedFonts = true
};
foreach (var pdfPath in scannedPdfPaths)
{
var document = engine.OpenPDF(pdfPath);
document.RecognizeAll(recognitionSettings);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
document.SaveAs(outputPath, outputSettings);
document.Dispose();
}
engine.Shutdown();
}
// OmniPage: Searchable PDF requires OutputSettings configuration before save
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
using var engine = new OmniPageEngine();
engine.SetLicenseFile(licensePath);
engine.Initialize();
var recognitionSettings = new RecognitionSettings
{
Language = "English",
AccuracyMode = "Maximum",
PreserveLayout = true
};
var outputSettings = new OutputSettings
{
Format = OutputFormat.SearchablePDF,
Compression = PDFCompression.Standard,
ImageQuality = 85,
EmbedFonts = true
};
foreach (var pdfPath in scannedPdfPaths)
{
var document = engine.OpenPDF(pdfPath);
document.RecognizeAll(recognitionSettings);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
document.SaveAs(outputPath, outputSettings);
document.Dispose();
}
engine.Shutdown();
}
Imports System.IO
' OmniPage: Searchable PDF requires OutputSettings configuration before save
Public Sub ConvertArchiveToSearchable(scannedPdfPaths As String(), outputDirectory As String)
Using engine As New OmniPageEngine()
engine.SetLicenseFile(licensePath)
engine.Initialize()
Dim recognitionSettings As New RecognitionSettings With {
.Language = "English",
.AccuracyMode = "Maximum",
.PreserveLayout = True
}
Dim outputSettings As New OutputSettings With {
.Format = OutputFormat.SearchablePDF,
.Compression = PDFCompression.Standard,
.ImageQuality = 85,
.EmbedFonts = True
}
For Each pdfPath As String In scannedPdfPaths
Dim document = engine.OpenPDF(pdfPath)
document.RecognizeAll(recognitionSettings)
Dim outputPath As String = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(pdfPath) & "_searchable.pdf")
document.SaveAs(outputPath, outputSettings)
document.Dispose()
Next
End Using
engine.Shutdown()
End Sub
Podejście IronOCR:
// IronOCR: Searchable PDF output is one method call on the result
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
var ocr = new IronTesseract();
foreach (var pdfPath in scannedPdfPaths)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath); // All pages loaded automatically
var result = ocr.Read(input);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)");
}
}
// IronOCR: Searchable PDF output is one method call on the result
public void ConvertArchiveToSearchable(string[] scannedPdfPaths, string outputDirectory)
{
var ocr = new IronTesseract();
foreach (var pdfPath in scannedPdfPaths)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath); // All pages loaded automatically
var result = ocr.Read(input);
string outputPath = Path.Combine(
outputDirectory,
Path.GetFileNameWithoutExtension(pdfPath) + "_searchable.pdf");
result.SaveAsSearchablePdf(outputPath);
Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)");
}
}
Imports System.IO
Imports IronOcr
Public Sub ConvertArchiveToSearchable(scannedPdfPaths As String(), outputDirectory As String)
Dim ocr As New IronTesseract()
For Each pdfPath As String In scannedPdfPaths
Using input As New OcrInput()
input.LoadPdf(pdfPath) ' All pages loaded automatically
Dim result = ocr.Read(input)
Dim outputPath As String = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(pdfPath) & "_searchable.pdf")
result.SaveAsSearchablePdf(outputPath)
Console.WriteLine($"Processed: {Path.GetFileName(pdfPath)} ({result.Pages.Length} pages)")
End Using
Next
End Sub
SaveAsSearchablePdf osadza warstwę tekstową w PDF, czyniąc ją indeksowalną w systemach zarządzania dokumentami, bez zmiany wyglądu oryginalnego skanu. Przewodnik w formacie PDF z funkcją wyszukiwania obejmuje opcje warstw tekstowych, a przykład OCR dla plików PDF ilustruje kompleksowe przetwarzanie zeskanowanych plików PDF.
Dokumentacja APIKofax OmniPagedo IronOCR
| Kofax OmniPage | Odpowiednik IronOCR |
|---|---|
using Kofax.OmniPage.CSDK; |
using IronOcr; |
using Kofax.OmniPageCSDK; |
using IronOcr; |
using CSDK; |
using IronOcr; |
new OmniPageEngine() |
Nie jest wymagane — brak obiektu silnika |
engine.SetLicenseFile(path) |
IronOcr.License.LicenseKey = "key"; |
engine.Initialize() |
Nie jest wymagane |
engine.Shutdown() |
Nie jest wymagane |
engine.CreateDocument() |
new OcrInput() |
document.AddPage(imagePath) |
input.LoadImage(imagePath) |
engine.OpenPDF(pdfPath) |
input.LoadPdf(pdfPath) |
engine.OpenTiff(tiffPath) |
input.LoadImageFrames(tiffPath) |
document.Recognize(settings) |
new IronTesseract().Read(input) |
document.RecognizeAll(settings) |
new IronTesseract().Read(input) (wszystkie strony na raz) |
document.GetText() |
result.Text |
document.GetZoneText(zoneName) |
input.LoadImage(path, cropRectangle) + result.Text |
document.Dispose() |
using var input = new OcrInput() (automatycznie) |
iterator.GetText() |
result.Pages[n].Words[m].Text |
iterator.GetBoundingBox() |
result.Pages[n].Words[m].X / Y / Width / Height |
iterator.GetConfidence() |
result.Pages[n].Words[m].Confidence |
engine.LoadLanguageDictionary("German") |
dotnet add package IronOcr.Languages.German |
settings.PrimaryLanguage = "English" |
ocr.Language = OcrLanguage.English; |
settings.SecondaryLanguages = new[] {"German"} |
ocr.Language = OcrLanguage.English + OcrLanguage.German; |
settings.DeskewImage = true |
input.Deskew(); |
settings.DespeckleLevel = 2 |
input.DeNoise(); |
settings.ContrastEnhancement = true |
input.Contrast(); |
settings.AutoRotate = true |
input.Deskew(); (obejmuje korekcję obrotową) |
document.SaveAs(path, outputSettings) |
result.SaveAsSearchablePdf(path) |
OutputFormat.SearchablePDF |
result.SaveAsSearchablePdf(path) |
OutputFormat.XML (ze współrzędnymi) |
result.Pages / .Paragraphs / .Words struktura obiektowa |
FormZone z granicami współrzędnych |
new CropRectangle(x, y, width, height) |
| Licencjonowanie na stronę | Nie dotyczy |
.lic wdrożenie pliku |
Nie dotyczy |
| Konfiguracja serwera licencji | Nie dotyczy |
Typowe problemy związane z migracją i ich rozwiązania
Problem 1: Wyjątki związane z brakiem pliku licencji w środowisku produkcyjnym
Kofax OmniPage: Każde wdrożenie wymaga, aby plik .lic istniał na konkretnej ścieżce dostępnej dla procesu aplikacji. Pipeline wdrożeniowy, który nie kopiuje wyraźnie pliku licencji na serwer docelowy, powoduje FileNotFoundException lub LicenseException podczas inicjalizacji silnika. Zespoły ds. bezpieczeństwa często zaznaczają osadzanie plików .lic w obrazach kontenerów lub ich commitowanie w kontrolę źródła.
Rozwiązanie:IronOCR odczytuje swoją licencję z ciągu znaków. Zapisz klucz jako zmienną środowiskową i odczytaj go podczas uruchamiania — bez konieczności wdrażania pliku ani konfigurowania ścieżki:
// At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IronOCR license key not configured.");
// At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IronOCR license key not configured.");
Imports System
' At application startup — reads IRONOCR_LICENSE_KEY from environment
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY"), Throw New InvalidOperationException("IronOCR license key not configured."))
W Dockerze przekaż --env IRONOCR_LICENSE_KEY=YOUR-KEY. W usłudze Azure App Service należy ustawić to jako ustawienie aplikacji. W Kubernetes należy wprowadzić to za pomocą Secret. Brak montowania plików, brak konfiguracji ścieżek, brak weryfikacji bezpieczeństwa wbudowanych plików licencyjnych.
Problem 2: Blokada licencji typu floating po awarii procesu
Kofax OmniPage: Kiedy proces aplikacji ulega awarii, jest zabijany przez watchdog, lub kończy się przez Environment.FailFast, engine.Shutdown() nie wykonuje się. Licencje typu floating pozostają wypożyczone do momentu wygaśnięcia limitu czasu serwera licencji — zazwyczaj 30–60 minut. W środowisku kontenerowym, gdzie pody często się restartują, takie zachowanie wyczerpuje pulę licencji.
Rozwiązanie:IronOCR nie obsługuje koncepcji wypożyczonych licencji. Awaria procesu nie zwalnia żadnych zasobów i nie blokuje żadnego systemu zewnętrznego. Kolejny proces rozpoczyna się natychmiast, bez czekania na dostępność miejsc:
// IronOCR: Process crash has no license implications whatsoever
// Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg"); // Nie seat to check out
// IronOCR: Process crash has no license implications whatsoever
// Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg"); // Nie seat to check out
' IronOCR: Process crash has no license implications whatsoever
' Start processing immediately after any failure
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim result = New IronTesseract().Read("document.jpg") ' Nie seat to check out
Aby uzyskać informacje na temat odpornych usług .NET Core, zapoznaj się z przewodnikiem dotyczącym asynchronicznego OCR, w którym opisano wzorce umożliwiające płynną integrację z usługami hostowanymi oraz płynne wyłączanie.
Problem 3: Błąd tworzenia obrazu Docker — instalator nie może działać w trybie nieinteraktywnym
Kofax OmniPage: Instalator OmniPage SDK jest interaktywnym lub półinteraktywnym instalatorem, który nie działa poprawnie w kontekście docker build. Zespoły próbujące wbudować SDK w obraz kontenera napotykają błędy w umowie licencyjnej instalatora lub na etapach wyboru komponentów. Rozwiązania alternatywne (parametry cichej instalacji, wstępnie wyodrębnione kopie bibliotek DLL) nie są udokumentowane i zależą od konkretnej wersji.
Rozwiązanie: Dockerfile z IronOCR to trzy linie:
FROM mcr.microsoft.com/dotnet/aspnet:8.0
RUN apt-get update && apt-get install -y libgdiplus # Single Linux dependency
COPY --from=build /app/publish /app
WORKDIR /app
ENTRYPOINT ["dotnet", "YourApp.dll"]
libgdiplus jest jedyną zależnością systemową. Pakiet NuGet IronOCR jest przywracany przez dotnet restore wewnątrz etapu budowy jak każda inna referencja pakietu. Przewodnik wdrażania Docker obejmuje zarówno obrazy bazowe Debian, jak i Alpine, a przewodnik wdrażania Linux obejmuje nazwy pakietów specyficzne dla danej dystrybucji.
Problem 4: Konieczna reaktywacja po migracji maszyny wirtualnej lub automatycznym skalowaniu w chmurze
Kofax OmniPage: Aktywacja OmniPage jest powiązana z identyfikatorem sprzętu. Środowiska chmurowe, które migrują maszyny wirtualne między hostami fizycznymi, automatycznie skalują się do nowych instancji lub zastępują kontenery nowymi obrazami, powodują zmiany tożsamości sprzętu, które wymagają ponownej aktywacji. Kontaktowanie się z pomocą techniczną Tungsten w celu ponownej aktywacji w trakcie zdarzenia zwiększa ryzyko operacyjne.
Rozwiązanie: Klucz licencyjny IronOCR jest niezależny od sprzętu. Ten sam klucz działa na dowolnej maszynie, w dowolnym kontenerze, w dowolnym regionie chmury oraz dla dowolnej liczby instancji skalowanych automatycznie w ramach licencjonowanego poziomu. Bez ponownej aktywacji, bez kontaktowania się z pomocą techniczną, bez przestojów:
// Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
bool valid = IronOcr.License.IsLicensed; // Verify without a network call
// Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
bool valid = IronOcr.License.IsLicensed; // Verify without a network call
' Identical startup code on any hardware topology
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim valid As Boolean = IronOcr.License.IsLicensed ' Verify without a network call
Problem 5: Obiekt RecognitionSettings nie ma odpowiednika w IronOCR
Kofax OmniPage: OmniPage używa obiektu RecognitionSettings (lub PreprocessingSettings w zależności od wersji SDK) do konfiguracji zachowania silnika na dokument. Zespoły migrujące oczekują obiektu konfiguracyjnego typu równoległego w IronOCR.
Rozwiązanie:IronOCR dzieli konfigurację na dwie powierzchnie: operacje wstępne na OcrInput i ustawienia silnika na IronTesseract. Nie ma obiektu ustawień do instancjonowania:
// OmniPage settings object →IronOCR method calls on OcrInput
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English; // RecognitionSettings.Language
// ocr.Configuration.TesseractVersion = ... // Engine version already optimized
using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew(); // settings.DeskewImage = true
input.DeNoise(); // settings.DespeckleLevel = 2
input.Contrast(); // settings.ContrastEnhancement = true
var result = ocr.Read(input);
// OmniPage settings object →IronOCR method calls on OcrInput
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.English; // RecognitionSettings.Language
// ocr.Configuration.TesseractVersion = ... // Engine version already optimized
using var input = new OcrInput();
input.LoadImage(imagePath);
input.Deskew(); // settings.DeskewImage = true
input.DeNoise(); // settings.DespeckleLevel = 2
input.Contrast(); // settings.ContrastEnhancement = true
var result = ocr.Read(input);
Imports IronOcr
' OmniPage settings object → IronOCR method calls on OcrInput
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.English ' RecognitionSettings.Language
' ocr.Configuration.TesseractVersion = ... ' Engine version already optimized
Using input As New OcrInput()
input.LoadImage(imagePath)
input.Deskew() ' settings.DeskewImage = true
input.DeNoise() ' settings.DespeckleLevel = 2
input.Contrast() ' settings.ContrastEnhancement = true
Dim result = ocr.Read(input)
End Using
W przewodniku po korekcji jakości obrazu wymieniono wszystkie dostępne metody przetwarzania wstępnego wraz z wskazówkami dotyczącymi tego, które filtry mają zastosowanie do poszczególnych profili jakości dokumentów.
Problem 6: Pliki szablonów stref nie mogą być przenoszone bezpośrednio
Kofax OmniPage: Szablony formularzy OmniPage przechowują definicje stref w zastrzeżonych plikach szablonów .fdt lub .fpf, które definiują pozycje pól, typy rozpoznawania i zasady walidacji dla klasy dokumentu. Pliki te nie mogą być importowane przez IronOCR.
Rozwiązanie: Wyodrębnij dane współrzędnych z plików szablonów (są oparte na XML) i przekształć każdą strefę na CropRectangle. Nazwy pól stają się kluczami słownika; współrzędne stref mapują bezpośrednio na parametry (x, y, width, height):
// Convert OmniPage zone definition to IronOCR CropRectangle
// OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
//IronOCR equivalent:
var totalDueRegion = new CropRectangle(490, 612, 180, 28);
using var input = new OcrInput();
input.LoadImage(invoicePath, totalDueRegion);
string totalDue = new IronTesseract().Read(input).Text.Trim();
// Convert OmniPage zone definition to IronOCR CropRectangle
// OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
//IronOCR equivalent:
var totalDueRegion = new CropRectangle(490, 612, 180, 28);
using var input = new OcrInput();
input.LoadImage(invoicePath, totalDueRegion);
string totalDue = new IronTesseract().Read(input).Text.Trim();
Imports IronOcr
' Convert OmniPage zone definition to IronOCR CropRectangle
' OmniPage zone: Name="TotalDue" X="490" Y="612" Width="180" Height="28"
' IronOCR equivalent:
Dim totalDueRegion As New CropRectangle(490, 612, 180, 28)
Using input As New OcrInput()
input.LoadImage(invoicePath, totalDueRegion)
Dim totalDue As String = New IronTesseract().Read(input).Text.Trim()
End Using
Dla dokumentów, w których strefy różnią się na stronę, załaduj ten sam obraz z różnymi wartościami CropRectangle na region, zamiast ponownie ładować plik. Przykład przycinania regionu demonstruje wiele odczytów regionów z jednego źródła obrazu.
Lista kontrolna migracji Kofax OmniPage
Przed migracją
Zidentyfikuj wszystkie punkty integracji OmniPage w kodzie źródłowym:
# Find all OmniPage namespace references
grep -r "Kofax\|OmniPage\|CSDK" --include="*.cs" --include="*.csproj" .
# Find engine lifecycle calls
grep -r "Initialize\|Shutdown\|SetLicenseFile" --include="*.cs" .
# Find document and zone creation patterns
grep -r "CreateDocument\|AddPage\|FormZone\|RecognitionSettings\|PreprocessingSettings" --include="*.cs" .
# Find output format configuration
grep -r "OutputSettings\|OutputFormat\|SaveAs\|GetText" --include="*.cs" .
# Find license file path references
grep -r "\.lic\|license\.lic\|licensePath" --include="*.cs" --include="*.config" --include="*.json" .
# Find all OmniPage namespace references
grep -r "Kofax\|OmniPage\|CSDK" --include="*.cs" --include="*.csproj" .
# Find engine lifecycle calls
grep -r "Initialize\|Shutdown\|SetLicenseFile" --include="*.cs" .
# Find document and zone creation patterns
grep -r "CreateDocument\|AddPage\|FormZone\|RecognitionSettings\|PreprocessingSettings" --include="*.cs" .
# Find output format configuration
grep -r "OutputSettings\|OutputFormat\|SaveAs\|GetText" --include="*.cs" .
# Find license file path references
grep -r "\.lic\|license\.lic\|licensePath" --include="*.cs" --include="*.config" --include="*.json" .
Sprawdź zasoby przed rozpoczęciem:
- Zliczanie plików z importami przestrzeni nazw OmniPage
- Wymienienie wszystkich definicji
FormTemplateiFormZoneoraz wyodrębnienie ich danych współrzędnych - Identyfikacja, które słowniki językowe są załadowane i mapowane na pakiety NuGet
IronOcr.Languages.* - Zwróć uwagę, jakie formaty wyjściowe są używane (PDF z możliwością wyszukiwania, zwykły tekst, XML) oraz ich odpowiedniki w IronOCR
- Zlokalizowanie wszystkich referencji plików
.licw skryptach wdrożeniowych i konfiguracji
Migracja kodu
- Usuń odniesienia DLL OmniPage ze wszystkich plików
.csproj - Uruchom
dotnet add package IronOcrw każdym projekcie, który wykonuje OCR - Dodaj pakiety językowe dla każdego używanego języka:
dotnet add package IronOcr.Languages.[Language] - Dodaj
IronOcr.License.LicenseKey = ...;w każdym punkcie wejścia aplikacji (Program.cs, Startup.cs lub host usługi) - Zastąp wszystkie
using Kofax.OmniPage.CSDK;,using CSDK;i powiązane importy przestrzeni nazw przezusing IronOcr; - Usuń wszystkie konstrukcje
OmniPageEngine,SetLicenseFilei wywołaniaInitialize - Usuń wszystkie wywołania
engine.Shutdown()i implementacjeIDisposableistniejące wyłącznie w celu zapewnienia zamknięcia - Zastąp
engine.CreateDocument()+document.AddPage()przeznew OcrInput()+input.LoadImage() - Zastąp
engine.OpenPDF()+ iterację na stronę przezinput.LoadPdf()(wszystkie strony w jednym wywołaniu) - Zastąp
engine.OpenTiff()+ pętle na klatkę przezinput.LoadImageFrames() - Zastąp
document.Recognize(settings)przeznew IronTesseract().Read(input) - Przekształć współrzędne
FormZonena instancjeCropRectangle(x, y, width, height) - Zastąp
document.GetZoneText()zinput.LoadImage(path, cropRectangle)+result.Textdla każdego regionu - Zastąp
document.SaveAs(path, outputSettings)dla wyszukiwalnego PDF przezresult.SaveAsSearchablePdf(path) - Zastąp wzorce iteratora wyników przez przejścia właściwościowe
result.Pages/.Paragraphs/.Words - Usuń obiekty
PreprocessingSettingsi zastąp flagi boolowskie wyraźnymi wywołaniami metodinput.Deskew(),input.DeNoise(),input.Contrast() - Usuń ścieżki do plików licencji ze skryptów wdrażania, plików konfiguracyjnych i szablonów infrastruktury jako kodu
Po migracji
- Sprawdź dokładność OCR na reprezentatywnej próbie ponad 100 dokumentów z rzeczywistego korpusu — porównaj z wynikami programu OmniPage wiersz po wierszu dla faktur, umów i formularzy
- Potwierdź, że ekstrakcja strefowa oparta na
CropRectanglezwraca tekst, który pasuje do wyjściaGetZoneText()OmniPage dla każdego mapowanego pola - Przetestuj przetwarzanie wielostronicowych plików PDF: sprawdź liczbę stron, kolejność stron i ciągłość tekstu
- Przetestuj przetwarzanie plików TIFF zawierających wiele klatek: upewnij się, że wszystkie klatki zostały przetworzone, a kolejność klatek została zachowana
- Sprawdź, czy pliki PDF z funkcją wyszukiwania są indeksowalne w używanym systemie zarządzania dokumentami (SharePoint, OpenText itp.)
- Przeprowadź test przetwarzania równoległego z ponad 50 dokumentami jednocześnie i upewnij się, że nie ma żadnych problemów z bezpieczeństwem wątków
- Przetestuj pokrycie pakietów językowych: załaduj każdy wcześniej używany słownik językowy przez
IronOcr.Languages.*i zweryfikuj jakość rozpoznawania - Upewnij się, że awarie procesu i ponowne uruchomienie kontenera nie wymagają żadnych działań związanych z odzyskiwaniem licencji
- Uruchom budowanie Docker na agencie CI — potwierdź, że
dotnet restorerozwiązuje IronOCR bez kroków instalacyjnych - Wyniki oceny pewności są dostępne dla każdego WORD-a i odpowiadają oczekiwaniom dotyczącym jakości danych w każdym dalszym etapie procesu walidacji
- Sprawdź, czy aplikacja uruchamia się bez obecności pliku
.licna jakiejkolwiek ścieżce
Kluczowe korzyści z migracji do IronOCR
Stopień skomplikowania wdrożenia spada z tygodni na minuty. Projekt, który wcześniej wymagał dostępu do instalatora SDK, wdrożenia pliku .lic, konfiguracji zapory sieciowej dla serwera licencji i koordynacji z zespołem infrastruktury, teraz wdraża się przez dotnet restore. Nowy programista klonuje repozytorium i uruchamia projekt. Nowy serwer produkcyjny jest udostępniany przez potok CI/CD. Obrazy kontenerów są kompilowane bez konieczności wykonywania czynności instalacyjnych.
Ryzyko licencyjne znika całkowicie. Nie ma pływających miejsc do wyczerpania, nie ma limitów czasowych na zakończenie, nie ma odcisków palców sprzętu do ponownej aktywacji i żadnych plików .lic do ochrony w potokach wdrożeniowych. Awaria aplikacji o 3 nad ranem nie powoduje zwolnienia ani zablokowania żadnych danych. Inżynier dyżurny ponownie uruchamia proces; Rozpoznawanie OCR zostanie wznowione natychmiast. Strona produktu IronOCR obejmuje wszystkie poziomy licencji.
Wdrażanie wielopłatformowe staje się standardowym celem NuGet. Komputery programistyczne z systemem macOS działają bez wyjątków platformowych. Serwery produkcyjne z systemem Linux nie wymagają wersji SDK ze stycznia 2026 r. Kontenery Docker budowane są z bazowego obrazu mcr.microsoft.com/dotnet/aspnet z jedną zależnością systemową. Ta sama referencja pakietowa IronOcr w .csproj produkuje działający build na Windows, Linux i macOS. Aby uzyskać informacje na temat konfiguracji specyficznej dla chmury, zapoznaj się z przewodnikiem wdrażania platformy Azure oraz przewodnikiem wdrażania AWS.
Równoległa przepustowość skaluje się wraz ze sprzętem. Architektura współdzielonego silnika OmniPage wymaga blokowania, które serializuje równoczesne rozpoznawanie. Stateless instancje IronTesseractIronOCR skalują się liniowo wraz z dostępnymi rdzeniami CPU. Podwojenie liczby rdzeni serwera przetwarzania wsadowego podwaja przepustowość bez zmian w konfiguracji i dodatkowych licencji.
Dostęp do danych strukturalnych jest natychmiastowy. OcrResult.Pages, Paragraphs, Lines, Words i Characters ujawniają kompletną strukturę dokumentu jako nawigowalną strukturę obiektów .NET. Pewność co do poszczególnych słów oraz współrzędne to właściwości każdego obiektu słowa. Nie ma dodatkowego potoku eksportu, konfiguracji formatu ani konieczności generowania plików wyjściowych w celu uzyskania dostępu do danych pozycyjnych.
Koszt jest stały i przewidywalny. Połączenie licencji SDK, rocznej konserwacji i opłat za wykonanie na stronę w ramach OmniPage tworzy koszt, który skaluje się wraz z wykorzystaniem i powtarza się co roku.IronOCR przy $999–$2,999 wieczyste to jednorazowy zakup. Przepustowość 500 000 stron rocznie, za które naliczane były opłaty za stronę, pozwala na zwrot kosztów licencji IronOCR w ciągu kilku tygodni. Centrum dokumentacji i samouczki IronOCR są dostępne bez umowy wsparcia technicznego.
Często Zadawane Pytania
Dlaczego warto przejść z Kofax OmniPage 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 Kofax OmniPage do IronOCR?
Zastąp sekwencje inicjalizacji Kofax OmniPage 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 programowi Kofax OmniPage 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 Kofax OmniPage 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 Kofax OmniPage do IronOCR wymaga zmian w infrastrukturze wdrożeniowej?
IronOCR wymaga mniej zmian w infrastrukturze niż Kofax OmniPage. 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 Kofax OmniPage?
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ż Kofax OmniPage 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 Kofax OmniPage 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.

