C#에서 차량 번호판에 대한 OCR을 수행하는 방법
이 가이드는 코팍스 옴니페이지 Capture SDK(현재 Tungsten Automation으로 판매됨)에서 IronOCR 로 OCR 워크로드를 이전하는 .NET 개발자를 위한 것입니다. 이 문서에서는 SDK 설치 프로그램 종속성 제거, 엔진 수명 주기 절차 제거, 영역 기반 인식 패턴 교체, 오류 처리 현대화 등 전체 마이그레이션 경로를 다룹니다. 비교 기사를 미리 읽을 필요는 없습니다.
Kofax OmniPage(텅스텐)에서 마이그레이션해야 하는 이유는 무엇입니까?
OmniPage Capture SDK는 NuGet , Docker, CI/CD 파이프라인이 등장하기 이전 시대의 Enterprise 문서 관리 인프라를 위해 설계되었습니다. 2005년에는 타당해 보였던 건축적 선택들이 2026년에는 오히려 마찰을 일으킨다.
SDK 설치 프로그램은 NuGet 환경에 적합하지 않습니다. OmniPage 코드를 실행하는 모든 개발 머신, 빌드 에이전트 및 프로덕션 서버는 컴파일 전에 Tungsten SDK 설치 프로그램을 실행해야 합니다. 복원할 .csproj 패키지 참조가 없습니다. SDK 버전을 업그레이드하려면 전체 시스템에 대해 설치 프로그램을 다시 실행해야 합니다. 새로운 개발자는 SDK 라이선스를 관리하는 담당자에게 연락하여 설치 프로그램 접근 권한을 받을 때까지 기다리지 않고는 프로젝트를 실행할 수 없습니다.
라이선스 파일은 운영 상의 책임입니다. .lic 파일은 모든 시스템에서 특정 경로에 있어야 하며, 이는 engine.Initialize()를 호출하는 경우에 해당합니다. 새 서버에 배포하고 파일을 삭제하면 모든 OCR 호출이 OCR 오류가 아닌 라이선스 예외와 함께 실패합니다. 부동 라이선스를 사용하고 애플리케이션 서버와 라이선스 서버 사이에 네트워크 분할이 있을 경우 모든 Initialize() 호출이 연결 복구 전까지 실패하게 되며, 어제 라이선스를 성공적으로 검증했더라도 상관 없습니다.
엔진 라이프사이클 관리가 오류 경로에서 자원을 누출합니다. OmniPageEngine.Shutdown()는 예외 처리기, 조기 반환, 타임아웃과 같은 모든 코드 경로에서 호출되어야 하며 그렇지 않으면 라이선스 서버의 체크아웃 타임아웃이 만료될 때까지 부동 라이선스 좌석이 잠긴 상태로 남습니다. 이 제약 조건을 방어적으로 작성하려면 엔진 종료가 생략되는 것을 방지하기 위한 IDisposable 패턴으로 모든 통합 지점을 감싸야 합니다.
하드웨어 지문 인식은 최신 배포 방식과 충돌합니다. OmniPage 활성화는 하드웨어와 연동됩니다. 컨테이너 재시작, VM 마이그레이션 및 클라우드 자동 확장은 모두 하드웨어 ID 변경을 수반하며, 이로 인해 재활성화 요구 사항이 발생합니다. 자동 확장과 호환되지 않는 배포 모델은 클라우드 인프라와 호환되지 않는 배포 모델입니다.
페이지당 가격 책정은 규모가 커질수록 예측하기 어렵습니다. 신규 고객 유치나 밀린 작업 일괄 처리와 같은 문서 처리량 급증은 누구도 예산에 반영하지 않았던 할당량을 초과하는 청구서를 발생시킵니다.IronOCR라이선스 등급 내에서 영구적으로 무제한 사용할 수 있습니다. 페이지당 사용량 제한이나 할당량, 초과 사용 요금 청구가 없습니다.
구매 프로세스로 인해 배송이 차단됩니다. OmniPage SDK는 판매 승인이 필요합니다. 평가판 접근 권한, 가격 및 계약 조건은 모두 4~12주간 진행되는 영업 상담을 통해 결정됩니다. 제품 출시 기한 내에 OCR 기능을 개발하는 팀은 Enterprise 조달 주기를 기다릴 수 없습니다. dotnet add package IronOcr 는 30초 안에 해결됩니다. IronOCR 라이선스 페이지에서 게시된 셀프 서비스 가격 — $999 Lite부터 $2,999의 Enterprise까지, 모두 영구적입니다.
근본적인 문제
OmniPage는 첫 번째 바이트를 읽기 전에 전체 초기화 과정을 거쳐야 하고, 마지막 바이트를 읽은 후에는 종료 과정을 거쳐야 합니다. 모든 호출 지점은 이 두 가지 과정을 모두 관리해야 합니다.
// 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
OmniPage 버전은 8개의 개별 단계, 2개의 필수 정리 호출, 네트워크 종속성 및 파일 시스템 종속성을 포함합니다.IronOCR버전에는 두 가지가 있습니다.
IronOCR과 Kofax OmniPage(텅스텐)의 기능 비교
아래 표는 이번 마이그레이션을 평가하는 팀에게 가장 중요한 기능들을 다룹니다.
| 기능 | 코팍스 옴니페이지 SDK | IronOCR |
|---|---|---|
| 설치 방법 | 사용자 지정 SDK 설치 프로그램( NuGet 미사용) | dotnet add package IronOcr |
| 첫 번째 OCR 호출 시간 | 4~12주(조달) + 시간(설치) | 30초 |
| 라이선스 메커니즘 | 디스크의 .lic 파일 + 선택적 라이선스 서버 |
앱 시작 시 문자열 키 |
| 하드웨어 지문 인식 | 예 (VM 마이그레이션 후 재활성화) | 아니요 |
| 라이선스 서버 필요 | 예 (유동 라이선스의 경우) | 아니요 |
| 페이지별 런타임 요금 청구 | 사용 가능 (가변적) | 아니요 |
| 공개된 가격 | 아니요 (영업 담당자에게 문의하세요) | 네 ($999 / $1,499 / $2,999 영구적) |
| 연간 유지 보수 비용 | 라이선스 비용의 18~25% | 선택적 |
| 엔진 수명주기 관리 | 필수 (Initialize / Shutdown) |
필요하지 않음 |
| 윈도우 x64 | 예 | 예 |
| Linux | 예 (2026년 1월 추가) | 예 (모든 버전) |
| macOS | 아니요 | 예 |
| Docker/Kubernetes | 난이도 높음 (설치 프로그램 + 라이선스 파일이 이미지에 포함됨) | 간단합니다 (NuGet 패키지만 해당) |
| Azure / AWS Lambda | 복잡함(라이선스 서버 연결 가능성) | 직관적 |
| 이미지 입력 형식 | 예 | JPG, PNG, BMP, TIFF, GIF 등 |
| 네이티브 PDF 입력 | 예 (모듈 라이선스가 필요할 수 있습니다) | 예 (내장되어 있으며, 추가 라이선스가 필요하지 않습니다) |
| 여러 페이지로 구성된 TIFF 파일 | 예 | 예 |
| 검색 가능한 PDF 출력 | 예 | 네 (result.SaveAsSearchablePdf()) |
| 자동 전처리 | 설정 객체 구성 | 내장형 + 명시적 파이프라인 API |
| 지원되는 언어 | 120+ | 125개 이상 (개별 NuGet 패키지) |
| 구조화된 데이터 출력 | 예 (단어 좌표) | 페이지, 단락, 줄, 단어, 문자의 좌표 |
| 자신감 점수 | 예 | 네 (result.Confidence, 단어당) |
| 바코드 판독 | 추가 기능 모듈(별도 라이선스) | 내장 (ocr.Configuration.ReadBarCodes = true) |
| 스레드 안전성 | 복잡한 (엔진 공유 제약 조건) | 전체 (1스레드 당 IronTesseract) |
| CI/CD 파이프라인 지원 | 모든 에이전트에 설치 프로그램이 필요합니다. | 표준 dotnet restore |
빠른 시작: Kofax OmniPage에서IronOCR로 마이그레이션
1단계: SDK를 NuGet 패키지로 교체합니다.
OmniPage에는 제거할 NuGet 패키지가 없습니다. 이동이 완료되면 .csproj 파일에서 수동 DLL 참조를 제거하고 개발 머신에서 SDK를 제거하십시오. 다음으로IronOCR설치하세요.
dotnet add package IronOcr
IronOCR NuGet 패키지에는 OCR 엔진, 전처리 필터 및 런타임 구성 요소가 포함되어 있습니다. 별도의 설치 프로그램이 필요 없고, 네이티브 DLL 관리 기능도 없으며, 구성 요소 등록도 필요 없습니다.
단계 2: 네임스페이스 업데이트
// 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
단계 3: 라이선스 초기화
애플리케이션 시작 시 한 줄 추가하십시오. 이는 .lic 파일 경로, 라이선스 서버 설정, engine.Initialize() 호출을 대체합니다:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
키를 환경 변수 (IRONOCR_LICENSE_KEY) 또는 appsettings.json에 저장하여 소스 코드 대신 사용하십시오. 키는 오프라인에서 읽어오므로 런타임에 네트워크 호출이 발생하지 않습니다.
코드 마이그레이션 예제
엔진 초기화 오류 경로 교체
OmniPage의 라이프사이클 모델에서 가장 위험한 측면은 정상적인 경로가 아니라 오류 경로입니다. engine.Initialize()와 engine.Shutdown() 사이에 발생한 예외는 Shutdown()에 도달하지 않으면 부동 라이선스 좌석을 잠긴 상태로 둘 수 있습니다. 실제 운영 환경에서는 모든 문서 처리 호출 주변에 try/finally 블록을 사용해야 합니다.
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
IronOCR 접근 방식:
// IronOCR: 아니요 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: 아니요 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
End Function
using 블록은 OcrInput에서 메모리 정리를 처리합니다. 라이선스 좌석을 보호할 try/finally가 존재하지 않습니다. 이 메서드 내에서 발생하는 예외는 해당 메서드의 범위 외에는 어떠한 부작용도 일으키지 않습니다. 스레드 로컬 인스턴스를 포함한 구성 옵션은 IronTesseract 설정 가이드를 참조하십시오.
영역 기반 인식 마이그레이션
OmniPage의 주요 구조화된 추출 메커니즘은 영역 정의입니다. 문서 영역은 좌표 경계와 영역별 인식 유형(OCR, ICR, OMR, 바코드)으로 선언됩니다. 개발자는 문서 템플릿 당 FormZone 개체를 정의하고 이를 인식 엔진에 전달합니다. IronOCR는 템플릿 관리나 영역 유형 선언 없이 동일한 대상 추출을 달성하기 위해 CropRectangle를 사용합니다:
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
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
각 CropRectangle는 픽셀 단위로 (x, y, width, height)를 지정합니다. OCR 엔진은 전체 페이지가 아닌 지정된 영역만 처리합니다. 이는 OmniPage에서 영역 기반 처리가 제공하는 것과 동일한 효율성 이점입니다. 영역 기반 OCR 가이드에는 여러 영역을 단일 이미지 로드에서 추출하기 위해 OcrInput의 다중 영역 API를 사용하는 방법과 좌표 선택 패턴이 포함되어 있습니다.
구조화된 데이터 출력 마이그레이션
OmniPage는 독자적인 내보내기 파이프라인을 통해 문서 구조를 노출합니다. 인식된 문서에 형식 설정이 적용되고, 지정된 형식(RTF, XML, CSV, 검색 가능한 PDF)의 파일로 출력됩니다. 단어 수준 좌표에 접근하려면 명시적인 위치 쿼리를 사용하여 결과 반복자를 순회해야 합니다.IronOCR별도의 내보내기 단계 없이 동일한 구조화된 데이터를 결과 객체에 직접 표시합니다.
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
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
OcrResult 개체의 계층 구조 — 페이지, 문단, 줄, 단어, 문자 — 는 탐색 가능한 객체 그래프로 처리되며, 순차 커서 대신 동일한 위치 데이터를 OmniPage의 결과 반복자가 노출시킵니다. 별도의 설정 없이 결과, 페이지, 단락 및 단어 수준에서 신뢰도 점수를 확인할 수 있습니다. 읽기 결과 가이드는 모든 구조화된 데이터 속성을 다루고, 신뢰도 점수 가이드는 단어별 유효성 검사 패턴을 다룹니다.
다중 페이지 TIFF 처리 마이그레이션
OmniPage의 다중 페이지 TIFF 워크플로는 TIFF 컨테이너에서 페이지별 추출을 필요로 하며, 페이지별로 별도의 인식 호출과 문서 폐기가 수행됩니다. 이로 인해 정형화된 코드가 생성될 뿐만 아니라 페이지 수에 비례하여 리소스 관리 영역이 늘어납니다.IronOCR단일 호출로 여러 프레임으로 구성된 TIFF 파일을 불러옵니다.
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
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는 TIFF 컨테이너의 모든 프레임을 단일 호출로 읽습니다. 결과는 프레임 당 하나의 OcrResult.Page을 노출하여 페이지별 구조를 보존하며 수동 반복 루프나 페이지별 정리가 필요하지 않습니다. 생산 TIFF 배치 워크플로에 대해서는 TIFF 및 GIF 입력 가이드를 참조하십시오.
스레드 안전 병렬 처리 마이그레이션
OmniPage의 엔진은 공유되는 상태 저장 리소스입니다. 스레드 간에 하나의 OmniPageEngine 인스턴스를 공유하려면 외부 동기화가 필요합니다. 여러 스레드가 동시에 CreateDocument()를 호출하면 얽힌 상태를 생성할 수 있기 때문입니다. 안전한 패턴 — 스레드 당 하나의 엔진 인스턴스 — 은 무거운 초기화 비용과 충돌합니다.IronOCR인스턴스는 공유 상태가 없으므로, 스레드 당 IronTesseract 를 하나 생성하여 조정 오버헤드를 없애십시오:
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
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
OmniPage 버전은 락을 통해 모든 인식을 직렬화하므로 병렬 처리가 불가능합니다.IronOCR버전은 별도의 조정 없이 문서를 동시에 처리합니다. 높은 처리량의 배치 워크로드에 대해서는 멀티스레딩 예제 및 속도 최적화 가이드를 참조하십시오.
스캔한 파일에서 검색 가능한 PDF 생성
OmniPage의 검색 가능한 PDF 출력을 위해서는 저장 전에 명시적인 OutputSettings 및 OutputFormat 설정을 사용하여 인식 파이프라인을 구성해야 합니다.IronOCR결과 객체에 대한 단일 메서드 호출을 통해 검색 가능한 PDF를 생성합니다.
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
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은 PDF에 텍스트 레이어를 삽입하여 문서 관리 시스템에서 인덱싱할 수 있게 하며, 원본 스캔의 시각적 외관은 변경하지 않습니다. 검색 가능한 PDF 가이드에서는 텍스트 레이어 옵션을 다루고, PDF OCR 예제에서는 스캔한 PDF를 처음부터 끝까지 처리하는 과정을 보여줍니다.
코팍스 옴니페이지 API와IronOCR매핑 참조
| 코팍스 옴니페이지 | IronOCR에 상응하는 |
|---|---|
using Kofax.OmniPage.CSDK; |
using IronOcr; |
using Kofax.OmniPageCSDK; |
using IronOcr; |
using CSDK; |
using IronOcr; |
new OmniPageEngine() |
필요 없음 - 엔진 객체가 없습니다. |
engine.SetLicenseFile(path) |
IronOcr.License.LicenseKey = "key"; |
engine.Initialize() |
필요하지 않음 |
engine.Shutdown() |
필요하지 않음 |
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) (모든 페이지 동시에) |
document.GetText() |
result.Text |
document.GetZoneText(zoneName) |
input.LoadImage(path, cropRectangle) + result.Text |
document.Dispose() |
using var input = new OcrInput() (자동) |
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(); (회전 보정 포함) |
document.SaveAs(path, outputSettings) |
result.SaveAsSearchablePdf(path) |
OutputFormat.SearchablePDF |
result.SaveAsSearchablePdf(path) |
OutputFormat.XML (좌표 포함) |
result.Pages / .Paragraphs / .Words 객체 그래프 |
FormZone 좌표 경계를 사용 |
new CropRectangle(x, y, width, height) |
| 페이지별 라이선스 계산 | 적용 안 됨 |
.lic 파일 배포 |
적용 안 됨 |
| 라이선스 서버 구성 | 적용 안 됨 |
일반적인 마이그레이션 문제와 해결책
문제 1: 운영 환경에서 발생하는 라이선스 파일 찾을 수 없음 예외
Kofax OmniPage: 모든 배포에는 애플리케이션 프로세스에서 접근 가능한 특정 경로에 .lic 파일이 존재해야 합니다. 배포 파이프라인에서 라이선스 파일을 대상 서버에 명시적으로 복사하지 않으면 엔진 초기화 시 FileNotFoundException 또는 LicenseException가 발생합니다. 보안 팀은 종종 컨테이너 이미지에 .lic 파일을 임베딩하거나 소스 컨트롤에 커밋하는 것을 경고합니다.
해결책:IronOCR라이선스 정보를 문자열에서 읽어옵니다. 환경 변수로 키를 저장하고 시작 시 읽어오세요. 배포할 파일도 없고, 구성할 경로도 없습니다.
// 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."))
Docker에서는 --env IRONOCR_LICENSE_KEY=YOUR-KEY를 전달하십시오. Azure App Service에서 애플리케이션 설정으로 설정하세요. Kubernetes에서는 Secret을 통해 주입합니다. 파일 마운트도 없고, 경로 구성도 없고, 내장 라이선스 파일에 대한 보안 검토도 없습니다.
문제 2: 프로세스 충돌 후 플로팅 라이선스 시트가 잠김
Kofax OmniPage: 애플리케이션 프로세스가 충돌하거나 워치독에 의해 종료되거나 Environment.FailFast 통해 종료되면, engine.Shutdown()는 실행되지 않습니다. 플로팅 라이선스 시트는 라이선스 서버의 시간 초과(일반적으로 30~60분)가 만료될 때까지 체크아웃된 상태로 유지됩니다. 컨테이너 환경에서 파드가 자주 재시작될 경우, 이러한 동작으로 인해 라이선스 풀이 고갈됩니다.
해결책:IronOCR에는 대여된 라이선스 시트라는 개념이 없습니다. 프로세스 충돌은 리소스를 해제하지 않으며 외부 시스템을 잠그지도 않습니다. 다음 절차는 좌석 확보를 기다릴 필요 없이 즉시 시작됩니다.
// 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"); // 아니요 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"); // 아니요 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") ' 아니요 seat to check out
안정적인 ASP.NET Core 서비스를 구축하려면 호스팅 서비스와 깔끔하게 통합되고 정상적으로 종료되는 패턴에 대한 내용 은 비동기 OCR 가이드를 참조하세요.
문제 3: Docker 이미지 빌드 실패 — 설치 프로그램이 비대화형으로 실행될 수 없음
Kofax OmniPage: OmniPage SDK 설치 프로그램은 GUI 또는 반대화면 설치 프로그램이며 docker build 환경에서 깔끔하게 실행되지 않습니다. SDK를 컨테이너 이미지에 통합하려는 팀은 설치 프로그램의 라이선스 계약 또는 구성 요소 선택 단계에서 오류를 겪습니다. 해결 방법(자동 설치 스위치, 미리 추출된 DLL 복사본)은 문서화되어 있지 않으며 버전별로 다릅니다.
Solution: IronOCR의 Dockerfile는 세 줄로 구성되어 있습니다:
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는 유일한 시스템 종속성입니다.IronOCR NuGet 패키지는 모든 다른 패키지 참조처럼 빌드 단계 내부의 dotnet restore에 의해 복원됩니다. Docker 배포 가이드는 Debian 및 Alpine 기본 이미지를 모두 다루고, Linux 배포 가이드는 배포판별 패키지 이름을 다룹니다.
문제 4: VM 마이그레이션 또는 클라우드 자동 확장 후 재활성화 필요
Kofax OmniPage: OmniPage 활성화는 하드웨어 ID와 연동됩니다. 물리적 호스트 간에 VM을 마이그레이션하거나, 새 인스턴스로 자동 확장하거나, 컨테이너를 새 이미지로 교체하는 클라우드 환경에서는 하드웨어 ID 변경이 발생하며, 이에 대한 재활성화가 필요합니다. 사고 발생 중에 Tungsten 지원팀에 연락하여 재활성화를 요청하는 것은 운영상의 위험을 초래합니다.
해결책: IronOCR의 라이선스 키는 하드웨어와 무관합니다. 동일한 키는 라이선스 티어 내의 모든 머신, 모든 컨테이너, 모든 클라우드 지역, 모든 수의 자동 확장 인스턴스에서 작동합니다. 재활성화 필요 없음, 고객 지원 문의 필요 없음, 시스템 다운타임 없음:
// 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
문제 5: RecognitionSettings 객체에IronOCR에 해당하는 객체가 없음
Kofax OmniPage: OmniPage는 각 문서별로 엔진 동작을 구성하기 위해 RecognitionSettings 객체 (또는 SDK 버전에 따라 PreprocessingSettings)를 사용합니다. 마이그레이션을 진행하는 팀은IronOCR에서 병렬 구성 객체를 기대합니다.
Solution: IronOCR는 구성을 두 가지 표면으로 분리합니다: OcrInput에서의 전처리 작업과 IronTesseract에서의 엔진 설정입니다. 인스턴스화할 설정 객체가 없습니다.
// 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
이미지 품질 보정 가이드 에는 사용 가능한 모든 전처리 방법이 나열되어 있으며, 어떤 필터가 어떤 문서 품질 프로필에 적용되는지에 대한 지침이 포함되어 있습니다.
문제 6: 영역 템플릿 파일을 직접 가져올 수 없습니다.
Kofax OmniPage: OmniPage 양식 템플릿은 독점적인 .fdt 또는 .fpf 템플릿 파일에 구역 정의를 저장하며, 문서 클래스별로 필드 위치, 인식 유형, 검증 규칙을 정의합니다. 이 파일들은IronOCR에서 가져올 수 없습니다.
Solution: 템플릿 파일(이는 XML 기반입니다)에서 좌표 데이터를 추출하고 각 구역을 CropRectangle로 변환하십시오. 필드 이름은 딕셔너리 키가 됩니다. 영역 좌표는 (x, y, width, height) 매개변수에 직접 매핑됩니다:
// Convert OmniPage zone definition toIronOCR 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 toIronOCR 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
페이지별로 영역이 다양한 문서의 경우 파일을 다시 로드하는 대신 각 지역별로 다른 CropRectangle 값을 사용하여 동일한 이미지를 로드하십시오. 영역 자르기 예제는 단일 이미지 소스에서 여러 영역을 읽는 방법을 보여줍니다.
코팍스 옴니페이지 마이그레이션 체크리스트
사전 마이그레이션
코드베이스에서 모든 OmniPage 통합 지점을 식별하십시오.
# 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" .
시작 전 재고 목록:
- OmniPage 네임스페이스를 가져온 파일 수 계산
- 모든
FormTemplate및FormZone정의를 나열하고 좌표 데이터를 추출하십시오 - 어떤 언어 사전이 로드되었는지 식별하고
IronOcr.Languages.*NuGet 패키지로 매핑하십시오 - 사용된 출력 형식(검색 가능한 PDF, 일반 텍스트, XML)과 해당IronOCR형식에 대한 대응값을 기록해 두십시오.
- 배포 스크립트와 구성에서 모든
.lic파일 참조를 찾으십시오
코드 마이그레이션
- 모든
.csproj파일에서 OmniPage DLL 참조 제거 - OCR을 수행하는 각 프로젝트에서
dotnet add package IronOcr실행 - 사용 중인 각 언어에 대한 언어 팩 추가:
dotnet add package IronOcr.Languages.[Language] - 각 애플리케이션 진입점(Program.cs, Startup.cs 또는 서비스 호스트)에
IronOcr.License.LicenseKey = ...;추가 - 모든
using Kofax.OmniPage.CSDK;,using CSDK;및 관련 네임스페이스 가져오기를using IronOcr;으로 대체 - 모든
OmniPageEngine생성,SetLicenseFile및Initialize호출 제거 - 모든
engine.Shutdown()호출 및 종료 보장을 위해 존재하는IDisposable구현 제거 engine.CreateDocument()+document.AddPage()을new OcrInput()+input.LoadImage()대체engine.OpenPDF()+ 페이지별 반복을input.LoadPdf()(모든 페이지를 한 번에 호출)로 대체engine.OpenTiff()+ 프레임별 루프를input.LoadImageFrames()로 대체document.Recognize(settings)을new IronTesseract().Read(input)으로 대체FormZone좌표 데이터를CropRectangle(x, y, width, height)인스턴스로 변환document.GetZoneText()을 지역별input.LoadImage(path, cropRectangle)+result.Text으로 대체- 검색 가능한 PDF를 위한
document.SaveAs(path, outputSettings)을result.SaveAsSearchablePdf(path)으로 대체 - 결과 반복자 패턴을
result.Pages/.Paragraphs/.Words속성 탐색으로 대체 - 모든
PreprocessingSettings객체를 제거하고 불리언 플래그를 명시적인input.Deskew(),input.DeNoise(),input.Contrast()메서드 호출로 대체 - 배포 스크립트, 구성 파일 및 인프라 코드 템플릿에서 라이선스 파일 경로를 제거합니다.
마이그레이션 이후
- 실제 데이터셋에서 추출한 100개 이상의 대표 샘플을 사용하여 OCR 정확도를 검증합니다. 송장, 계약서, 양식 등의 OmniPage 출력 결과와 각 줄을 비교합니다.
- OmniPage
GetZoneText()출력과 일치하는 텍스트를 반환하는CropRectangle기반의 영역 추출을 확인 - 다중 페이지 PDF 처리 테스트: 페이지 수, 페이지 순서 및 텍스트 연속성 확인
- 다중 프레임 TIFF 처리 테스트: 모든 프레임이 처리되고 프레임 순서가 유지되는지 확인합니다.
- 검색 가능한 PDF 출력물이 사용 중인 문서 관리 시스템(SharePoint, OpenText 등)에서 색인화 가능한지 확인합니다.
- 50개 이상의 문서를 동시에 처리하는 병렬 처리 테스트를 실행하고 스레드 안전성 문제가 없는지 확인합니다.
- 언어 팩 범위 테스트: 이전에 사용한 언어 사전을
IronOcr.Languages.*로 로드하고 인식 품질 확인 - 프로세스 충돌 및 컨테이너 재시작 시 라이선스 복구 조치가 필요하지 않은지 확인합니다.
- CI 에이전트에서 Docker 빌드 실행 — 설치 단계 없이 IronOCR를 해결하는지 확인
- 단어별 신뢰도 점수가 제공되는지 확인하고, 하위 검증 워크플로의 데이터 품질 기대치와 일치하는지 검증합니다.
- 어떤 경로에서도
.lic파일이 없는 상태에서 애플리케이션이 깔끔하게 시작되는지 확인
IronOCR로 마이그레이션할 때의 주요 이점
배포 복잡성이 몇 주에서 몇 분으로 하락합니다. 이전에 SDK 설치 프로그램 접근 권한, .lic 파일 배포, 라이선스 서버를 위한 방화벽 구성, 인프라 팀 조정이 필요했던 프로젝트가 이제 dotnet restore을 통해 배포됩니다. 새로운 개발자가 저장소를 복제하고 프로젝트를 실행합니다. CI/CD 파이프라인을 통해 새로운 프로덕션 서버가 프로비저닝됩니다. 컨테이너 이미지는 설치 프로그램 단계 없이 빌드됩니다.
라이선스 위험이 완전히 사라집니다. 소진해야 할 부동 좌석이 없으며, 대기해야 할 체크아웃 타임아웃도 없고, 재활성화할 하드웨어 지문도 없으며, 배포 파이프라인에서 보호해야 할 .lic 파일도 없습니다. 새벽 3시에 애플리케이션이 충돌해도 아무것도 해제되지 않고 아무것도 잠기지 않습니다. 당직 엔지니어가 프로세스를 다시 시작합니다. OCR이 즉시 재개됩니다. IronOCR 제품 페이지에는 모든 라이선스 등급이 나와 있습니다.
크로스 플랫폼 배포가 표준 NuGet 대상으로 추가됩니다. macOS 개발 환경에서는 플랫폼 예외 없이 작동합니다. Linux 운영 서버에는 2026년 1월 버전의 SDK가 필요하지 않습니다. 하나의 시스템 종속성이 있는 표준 mcr.microsoft.com/dotnet/aspnet 기본 이미지를 기반으로 Docker 컨테이너가 빌드됩니다. Windows, Linux, macOS에서 모두 작동하는 빌드를 생성하는 .csproj에 있는 동일한 IronOcr 패키지 참조 클라우드별 구성에 대한 자세한 내용은 Azure 배포 가이드 및 AWS 배포 가이드를 참조하세요.
병렬 처리량은 하드웨어에 따라 확장됩니다. OmniPage의 공유 엔진 아키텍처는 동시 인식을 직렬화하는 잠금 기능을 필요로 합니다. IronOCR의 무상태 IronTesseract 인스턴스는 사용 가능한 CPU 코어에 따라 선형적으로 확장됩니다. 배치 처리 서버의 코어 수를 두 배로 늘리면 구성 변경이나 추가 라이선스 없이 처리량이 두 배로 증가합니다.
구조화된 데이터 접근이 즉각적입니다. OcrResult.Pages, Paragraphs, Lines, Words, 및 Characters 는 완전한 문서 구조를 탐색 가능한 .NET 객체 그래프로 노출합니다. 단어별 신뢰도와 좌표는 각 단어 객체의 속성입니다. 위치 데이터에 접근하기 위해 별도의 내보내기 파이프라인, 형식 구성 또는 파일 출력이 필요하지 않습니다.
비용은 고정되어 있고 예측 가능합니다. OmniPage는 SDK 라이선스, 연간 유지 보수 비용, 페이지당 런타임 비용을 합산하여 사용량에 따라 비용이 증가하고 매년 반복됩니다. IronOCR는 $999–$2,999 영구적으로 한 번 구매합니다. 연간 50만 페이지 처리량을 보이는 워크플로우에서 페이지당 비용이 발생하던 방식이IronOCR라이선스 비용을 몇 주 만에 회수할 수 있게 되었습니다. IronOCR 문서 허브 와 튜토리얼은 지원 계약 없이 이용할 수 있습니다.
자주 묻는 질문
Kofax OmniPage에서 IronOCR로 마이그레이션해야 하는 이유는 무엇인가요?
일반적인 동인으로는 COM 상호 운용의 복잡성 제거, 파일 기반 라이선스 관리 대체, 페이지당 과금 방지, Docker/컨테이너 배포 활성화, 표준 .NET 툴링과 통합되는 NuGet 네이티브 워크플로 채택 등이 있습니다.
Kofax OmniPage에서 IronOCR로 마이그레이션할 때 주요 코드 변경 사항은 무엇인가요?
Kofax OmniPage 초기화 시퀀스를 IronTesseract 인스턴스화로 대체하고, COM 수명 주기 관리(명시적 생성/로드/폐쇄 패턴)를 제거하며, 결과 속성 이름을 업데이트합니다. 그 결과 상용구 줄이 현저히 줄어듭니다.
마이그레이션을 시작하려면 IronOCR을 어떻게 설치하나요?
패키지 관리자 콘솔에서 '설치-패키지 IronOcr'을 실행하거나 CLI에서 '닷넷 추가 패키지 IronOcr'을 실행하세요. 언어 팩은 별도의 패키지입니다: 예를 들어 프랑스어의 경우 '닷넷 추가 패키지 IronOcr.Languages.French'를 실행합니다.
IronOCR은 표준 비즈니스 문서에 대해 Kofax OmniPage의 OCR 정확도와 일치하나요?
IronOCR은 송장, 계약서, 영수증, 타이핑된 양식 등 표준 비즈니스 콘텐츠에 대해 높은 정확도를 달성합니다. 이미지 전처리 필터(데스큐, 노이즈 제거, 대비 향상)는 품질이 저하된 입력에 대한 인식률을 더욱 향상시킵니다.
IronOCR은 Kofax OmniPage가 별도로 설치하는 언어 데이터를 어떻게 처리하나요?
IronOCR의 언어 데이터는 NuGet 패키지로 배포됩니다. '닷넷 추가 패키지 IronOcr.Languages.German'은 독일어 지원을 설치합니다. 수동 파일 배치나 디렉터리 경로는 필요하지 않습니다.
Kofax OmniPage에서 IronOCR로 마이그레이션하려면 배포 인프라를 변경해야 하나요?
IronOCR은 Kofax OmniPage보다 인프라 변경이 덜 필요합니다. SDK 바이너리 경로, 라이선스 파일 배치 또는 라이선스 서버 구성이 필요하지 않습니다. NuGet 패키지에는 완전한 OCR 엔진이 포함되어 있으며 라이선스 키는 애플리케이션 코드에 설정된 문자열입니다.
마이그레이션 후 IronOCR 라이선싱은 어떻게 구성하나요?
애플리케이션 시작 코드에 IronOcr.License.LicenseKey = "YOUR-KEY"를 할당하세요. Docker 또는 Kubernetes에서 키를 환경 변수로 저장하고 시작 시 키를 읽습니다. 트래픽을 수락하기 전에 License.IsValidLicense를 사용하여 유효성을 검사하세요.
IronOCR은 Kofax OmniPage와 동일한 방식으로 PDF를 처리할 수 있나요?
예. IronOCR은 원본 및 스캔한 PDF를 모두 읽습니다. IronTesseract를 인스턴스화하고, 입력이 PDF 경로 또는 OcrPdfInput인 경우 ocr.Read(input)를 호출하고, OcrResult 페이지를 반복하면 됩니다. 별도의 PDF 렌더링 파이프라인이 필요하지 않습니다.
IronOCR은 대량 처리에서 스레딩을 어떻게 처리하나요?
IronTesseract는 스레드별로 인스턴스화해도 안전합니다. Parallel.ForEach 또는 Task 풀에서 스레드당 하나의 인스턴스를 스핀업하고, OCR을 동시에 실행하고, 완료되면 각 인스턴스를 폐기하면 됩니다. 전역 상태나 잠금이 필요하지 않습니다.
IronOCR은 텍스트 추출 후 어떤 출력 형식을 지원하나요?
IronOCR은 텍스트, 단어 좌표, 신뢰도 점수, 페이지 구조를 포함한 구조화된 결과를 반환합니다. 내보내기 옵션에는 일반 텍스트, 검색 가능한 PDF, 다운스트림 처리를 위한 구조화된 결과 개체가 포함됩니다.
워크로드 확장을 위해 IronOCR 가격이 Kofax OmniPage보다 더 예측 가능한가요?
IronOCR은 페이지당 또는 볼륨당 요금이 없는 정액제 영구 라이선스를 사용합니다. 10,000페이지를 처리하든 1,000만 페이지를 처리하든 라이선스 비용은 일정하게 유지됩니다. 볼륨 및 팀 라이선스 옵션은 IronOCR 가격 페이지에서 확인할 수 있습니다.
Kofax OmniPage에서 IronOCR로 마이그레이션한 후 기존 테스트는 어떻게 되나요?
추출된 텍스트 콘텐츠에 대해 어설트하는 테스트는 마이그레이션 후에도 계속 통과해야 합니다. API 호출 패턴 또는 COM 개체 수명 주기의 유효성을 검사하는 테스트는 IronOCR의 더 간단한 초기화 및 결과 모델을 반영하도록 업데이트해야 합니다.

