ABBYY FineReader에서 IronOCR로 마이그레이션하기
이 가이드는 .NET 개발자가 ABBYY FineReader 엔진 SDK를 IronOCR 로 교체하는 모든 단계를 안내합니다. 이 문서에서는 COM 종속성 및 SDK 설치 프로그램 아티팩트를 제거하는 기계적인 단계, ABBYY API를IronOCR의 해당 기능에 매핑하는 방법, 그리고 실제 ABBYY 통합에서 가장 흔히 발견되는 패턴에 대한 변경 전후 코드 예제를 다룹니다. 마이그레이션은 ABBYY의 Enterprise 비용과 배포 복잡성이 더 이상 프로젝트 요구 사항과 일치하지 않는다고 결정한 팀을 대상으로 합니다.
ABBYY FineReader에서 마이그레이션해야 하는 이유는 무엇일까요?
ABBYY FineReader Engine은 뛰어난 OCR 플랫폼이지만, 그 아키텍처는 전담 인프라 팀을 갖춘 Enterprise Windows 환경에 맞춰 설계되었습니다. .NET 팀의 실제 작업량이 송장 처리, 계약서 디지털화 또는 스캔한 양식 추출과 같은 작업일 경우, 해당 아키텍처는 자산이 아니라 오히려 부담이 됩니다.
COM 상호 운용성 관련 부채는 시간이 지날수록 누적됩니다. 모든 ABBYY .NET 통합은 COM 상호 운용성 계층을 거쳐 실행됩니다. COM 개체는 명시적 수명 주기 관리를 필요로 합니다: finally 블록에서 생성, 초기화, 처리 후 닫아야 하며, 그렇지 않으면 프로세스가 메모리를 누수합니다. ABBYY와 관련된 모든 코드 경로에는 이 패턴이 적용됩니다. 2~3년에 걸친 기능 추가 과정에서 이러한 생명주기 의식은 서비스 클래스, 백그라운드 작업자 및 요청 처리기를 통해 전파됩니다. 결과는 모든 OCR 관련 클래스에서 30-50%의 보일러플레이트를 차지하며, IronTesseract으로 전환하면 완전히 사라집니다.
SDK 설치 프로그램이 최신 배포 패턴을 차단합니다. ABBYY는 바이너리, 언어 데이터, 런타임 파일 및 라이선스 파일을 하드코딩된 경로에 배치하는 Windows SDK 설치 프로그램을 통해 배포합니다. ABBYY를 사용하는 서비스를 컨테이너화하려면 해당 설치 프로그램 출력에서 300MB 이상의 커스텀 베이스 이미지를 만드는 것 또는 시작 시 라이선스 파일을 포함하는 볼륨을 마운트하는 수밖에 없습니다. 이러한 접근 방식은 표준 Kubernetes나 클라우드 네이티브 파이프라인에 적합하지 않습니다. IronOCR은 NuGet 패키지로, 모든 다른 종속성을 가져오는 dotnet restore과 동일한 방식으로 완전한 OCR 엔진을 가져옵니다.
페이지당 라이선스 방식은 처리량에 따라 비용을 발생시킵니다. ABBYY의 볼륨 기반 라이선스 모델은 포함된 임계값을 초과하는 페이지 처리량에 따라 요금을 부과합니다. 출시 당시 월 5만 건의 문서를 처리하던 애플리케이션이 2년 후 50만 건에 도달했을 때, OCR 비용은 성공에 정비례하여 증가했습니다.IronOCR라이선스에 대해 고정 요금을 부과합니다. 즉, 한 달에 200만 페이지를 처리하는 팀과 2천 페이지를 처리하는 팀이 지불하는 라이선스 비용이 동일합니다.
언어 데이터는 수동 배포 조정이 필요합니다. ABBYY 언어 팩은 SDK 런타임 디렉터리에 파일로 존재합니다. 언어를 추가한다는 것은 올바른 데이터 파일을 식별하고, 모든 배포 대상의 올바른 경로에 복사하고, CI/CD 스크립트를 업데이트하여 해당 파일을 포함시키는 것을 의미합니다. IronOCR에서 프랑스어를 추가하는 것은 dotnet add package IronOcr.Languages.French로, 패키지 관리자가 나머지를 처리합니다.
라이선스 파일 오류가 예고 없이 프로덕션에 영향을 미칩니다. ABBYY 라이선스는 .lic 및 .key 파일로 존재하며, loader.GetEngineObject() 실행 시 특정 디스크 경로에 있어야 합니다. 새로운 프로덕션 서버에서 해당 파일이 누락된 경우(잘못된 배포 스크립트, 파일 복사 실패, 권한 문제 등) 시작 시 오류가 발생합니다. 라이선스가 만료된 경우에도 동일한 오류가 발생합니다. IronOCR의 라이선스는 시작 코드에 할당되는 문자열 키로, 모든 비밀 관리자에 저장 가능하며 애플리케이션이 트래픽을 받아들이기 전에 IronOcr.License.IsValidLicense에서 유효성을 검증합니다.
스레드 안전성은 단일 공유 엔진 인스턴스를 필요로 합니다. ABBYY의 엔진은 여러 스레드에서의 CreateFRDocument 호출에 있어서 간단히 스레드 안전하지 않습니다. 실제 운영 환경에서는 잠금 전략 또는 프로세서 풀을 사용합니다. IronOCR의 IronTesseract은 무상태입니다: 스레드당 하나의 인스턴스를 시작하고 락 없이 동시에 인식을 실행하며 완료 후 처리합니다.
근본적인 문제
// ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
var loader = new EngineLoader();
var engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin", // Breaks on every new machine
@"C:\Program Files\ABBYY SDK\License" // Fails if .lic file is missing
);
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("English");
// ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
var loader = new EngineLoader();
var engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin", // Breaks on every new machine
@"C:\Program Files\ABBYY SDK\License" // Fails if .lic file is missing
);
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("English");
' ABBYY: COM loader + license path validation + profile load — before a single pixel of OCR runs
Dim loader As New EngineLoader()
Dim engine = loader.GetEngineObject(
"C:\Program Files\ABBYY SDK\FineReader Engine\Bin", ' Breaks on every new machine
"C:\Program Files\ABBYY SDK\License" ' Fails if .lic file is missing
)
engine.LoadPredefinedProfile("DocumentConversion_Accuracy")
Dim langParams = engine.CreateLanguageParams()
langParams.Languages.Add("English")
// IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
// IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var ocr = new IronTesseract();
Imports IronOcr
' IronOCR: the entire initialization
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim ocr As New IronTesseract()
IronOCR과 ABBYY FineReader: 기능 비교
다음 표는 이번 마이그레이션을 평가하는 팀에 필요한 기능을 다룹니다.
| 기능 | ABBYY FineReader 엔진 | IronOCR |
|---|---|---|
| 설치 | SDK 설치 프로그램(Windows) | dotnet add package IronOcr |
| 인수 | 영업 담당자에게 문의하십시오 (4-12주 소요). | 셀프 서비스 NuGet |
| 라이센스 모델 | Enterprise, 서버별 또는 페이지별 | 영구 라이선스, $999에 단일 지불 $2,999 |
| 라이선스 관리 | .lic + .key 파일이 디스크에 존재함 |
코드 또는 환경 변수의 문자열 키 |
| .NET 통합 | COM 상호 운용성 | 네이티브 .NET |
| COM 종속성 | 예 | 아니요 |
| 스레드 안전성 | 잠금 전략이 필요합니다 | 완전 (스레드당 하나의 IronTesseract) |
| 지원되는 언어 | 190개 이상 | 125+ |
| 언어 설치 | SDK 경로에 있는 런타임 데이터 파일 | NuGet 언어 패키지 |
| PDF 입력 | 예 (CreatePDFFile를 통해) |
예 (네이티브, input.LoadPdf()) |
| 검색 가능한 PDF 출력 | 예 (내보내기 파이프라인) | 예 (result.SaveAsSearchablePdf()) |
| 자동 전처리 | 프로필 기반 | 내장 기능 (왜곡 보정, 노이즈 제거, 대비 조정, 이진화, 선명도 향상) |
| 영역 기반 OCR | 영역 객체 (CreateZone, SetBounds) |
CropRectangle 매개변수 |
| 바코드 판독 | 예 | 예 (ocr.Configuration.ReadBarCodes = true) |
| 크로스 플랫폼 | 윈도우, 리눅스, macOS | 윈도우, 리눅스, macOS, Docker, Azure, AWS |
| Docker 배포 | 사용자 지정 기본 이미지가 필요합니다. | 표준 .NET 베이스 이미지 + libgdiplus |
| 자신감 점수 | 예 | 예 (result.Confidence) |
| 첫 OCR 결과 도출 시간 | 4~12주 (조달) | 당일 |
빠른 시작: ABBYY FineReader에서IronOCR로 마이그레이션
1단계: NuGet 패키지 교체
ABBYY FineReader Engine에는 NuGet 패키지가 없습니다. SDK를 제거하고 프로젝트 파일에서 수동 어셈블리 참조를 삭제하여 해당 문제를 해결하십시오.
<Reference Include="FREngine">
<HintPath>C:\Program Files\ABBYY SDK\FineReader Engine\Bin\FREngine.dll</HintPath>
</Reference>
<Reference Include="FREngine">
<HintPath>C:\Program Files\ABBYY SDK\FineReader Engine\Bin\FREngine.dll</HintPath>
</Reference>
그런 후 Visual Studio의 참조 노드에서 FREngine.dll COM 상호 운용 참조를 제거하거나 프로젝트 파일에서 해당 항목을 직접 삭제하세요. NuGet 에서IronOCR설치하세요.
dotnet add package IronOcr
단계 2: 네임스페이스 업데이트
// Before (ABBYY)
using FREngine;
using ABBYY.FineReader;
// After (IronOCR)
using IronOcr;
// Before (ABBYY)
using FREngine;
using ABBYY.FineReader;
// After (IronOCR)
using IronOcr;
Imports IronOcr
단계 3: 라이선스 초기화
애플리케이션 시작 시 OCR 호출 전에 다음 코드를 한 번만 추가하세요.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
운영 환경에 배포할 경우 환경 변수 또는 비밀 관리자에 키를 저장하세요.
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
Imports System
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
코드 마이그레이션 예제
Windows 서비스의 엔진 수명 주기와 상태 비저장 IronTesseract 비교
ABBYY의 엔진 초기화 절차는 EngineLoader 및 IEngine 객체 생성 비용이 높기 때문에 서비스 래퍼에 속합니다. 대부분의 실제 운영 환경에서는 엔진을 명시적인 시작 및 종료 메서드를 가진 싱글턴 서비스로 래핑합니다.
ABBYY FineReader 접근 방식:
using FREngine;
public class DocumentOcrService : IHostedService, IDisposable
{
private IEngine _engine;
public Task StartAsync(CancellationToken cancellationToken)
{
// Step 1: Create loader — requires COM 상호 운용성 registration
var loader = new EngineLoader();
// Step 2: Load engine from SDK path — throws if license files are missing
_engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
@"C:\Program Files\ABBYY SDK\License"
);
// Step 3: Load profile before any recognition work
_engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
return Task.CompletedTask;
}
public string ProcessDocument(string imagePath)
{
// Document must be created and destroyed per call
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close(); // Memory leaks if omitted
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_engine = null; // COM cleanup
return Task.CompletedTask;
}
public void Dispose() => _engine = null;
}
using FREngine;
public class DocumentOcrService : IHostedService, IDisposable
{
private IEngine _engine;
public Task StartAsync(CancellationToken cancellationToken)
{
// Step 1: Create loader — requires COM 상호 운용성 registration
var loader = new EngineLoader();
// Step 2: Load engine from SDK path — throws if license files are missing
_engine = loader.GetEngineObject(
@"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
@"C:\Program Files\ABBYY SDK\License"
);
// Step 3: Load profile before any recognition work
_engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
return Task.CompletedTask;
}
public string ProcessDocument(string imagePath)
{
// Document must be created and destroyed per call
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close(); // Memory leaks if omitted
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_engine = null; // COM cleanup
return Task.CompletedTask;
}
public void Dispose() => _engine = null;
}
Imports FREngine
Imports System.Threading
Imports System.Threading.Tasks
Public Class DocumentOcrService
Implements IHostedService, IDisposable
Private _engine As IEngine
Public Function StartAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StartAsync
' Step 1: Create loader — requires COM 상호 운용성 registration
Dim loader As New EngineLoader()
' Step 2: Load engine from SDK path — throws if license files are missing
_engine = loader.GetEngineObject(
"C:\Program Files\ABBYY SDK\FineReader Engine\Bin",
"C:\Program Files\ABBYY SDK\License"
)
' Step 3: Load profile before any recognition work
_engine.LoadPredefinedProfile("DocumentConversion_Accuracy")
Return Task.CompletedTask
End Function
Public Function ProcessDocument(imagePath As String) As String
' Document must be created and destroyed per call
Dim document = _engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
Return document.PlainText.Text
Finally
document.Close() ' Memory leaks if omitted
End Try
End Function
Public Function StopAsync(cancellationToken As CancellationToken) As Task Implements IHostedService.StopAsync
_engine = Nothing ' COM cleanup
Return Task.CompletedTask
End Function
Public Sub Dispose() Implements IDisposable.Dispose
_engine = Nothing
End Sub
End Class
IronOCR 접근 방식:
using IronOcr;
public class DocumentOcrService
{
// 아니요 startup, no shutdown, no COM lifecycle
// IronTesseract is stateless — create per call or reuse per thread
public string ProcessDocument(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}
using IronOcr;
public class DocumentOcrService
{
// 아니요 startup, no shutdown, no COM lifecycle
// IronTesseract is stateless — create per call or reuse per thread
public string ProcessDocument(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}
Imports IronOcr
Public Class DocumentOcrService
' 아니요 startup, no shutdown, no COM lifecycle
' IronTesseract is stateless — create per call or reuse per thread
Public Function ProcessDocument(imagePath As String) As String
Return (New IronTesseract()).Read(imagePath).Text
End Function
End Class
IronTesseract은 엔진 수명 주기가 없습니다. 이 기능은 처음 사용할 때 내부적으로 초기화되며 명시적인 종료가 필요하지 않습니다. 호스팅된 서비스 래퍼, IEngine 필드, StopAsync 메서드 모두가 사라집니다. 애플리케이션이 동시에 문서를 처리하는 경우, 각 스레드가 고유의 IronTesseract 인스턴스를 생성합니다 - 락이 필요하지 않습니다. IronTesseract 설정 가이드는 TesseractVersion 및 Configuration 속성을 포함한 구성 옵션을 다룹니다.
언어 인식 설정
ABBYY 언어 구성은 매개변수가 있는 언어 이름 문자열을 추가하고, 이들을 엔진과 문서 처리 전에 연결해야 하는 LanguageParams 객체를 생성하는 것을 포함합니다. 추가되는 언어마다 해당 데이터 파일이 런타임 경로에 배포되어야 합니다.
ABBYY FineReader 접근 방식:
using FREngine;
// Engine must already be initialized with sdkPath and licensePath
private void ConfigureLanguages(IEngine engine, string[] languageCodes)
{
// Create language parameters object
var langParams = engine.CreateLanguageParams();
// Add each language — string names must match installed data file names
// Missing data file causes runtime failure
foreach (var lang in languageCodes)
{
langParams.Languages.Add(lang); // e.g., "English", "French", "German"
}
// Language params are associated at the profile level, not per-document
// Changing languages requires reloading profile or reinitializing engine
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
}
public string RecognizeFrenchDocument(IEngine engine, string imagePath)
{
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("French"); // Requires FrenchLanguage data files at runtime path
var document = engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
using FREngine;
// Engine must already be initialized with sdkPath and licensePath
private void ConfigureLanguages(IEngine engine, string[] languageCodes)
{
// Create language parameters object
var langParams = engine.CreateLanguageParams();
// Add each language — string names must match installed data file names
// Missing data file causes runtime failure
foreach (var lang in languageCodes)
{
langParams.Languages.Add(lang); // e.g., "English", "French", "German"
}
// Language params are associated at the profile level, not per-document
// Changing languages requires reloading profile or reinitializing engine
engine.LoadPredefinedProfile("DocumentConversion_Accuracy");
}
public string RecognizeFrenchDocument(IEngine engine, string imagePath)
{
var langParams = engine.CreateLanguageParams();
langParams.Languages.Add("French"); // Requires FrenchLanguage data files at runtime path
var document = engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
Imports FREngine
' Engine must already be initialized with sdkPath and licensePath
Private Sub ConfigureLanguages(engine As IEngine, languageCodes As String())
' Create language parameters object
Dim langParams = engine.CreateLanguageParams()
' Add each language — string names must match installed data file names
' Missing data file causes runtime failure
For Each lang In languageCodes
langParams.Languages.Add(lang) ' e.g., "English", "French", "German"
Next
' Language params are associated at the profile level, not per-document
' Changing languages requires reloading profile or reinitializing engine
engine.LoadPredefinedProfile("DocumentConversion_Accuracy")
End Sub
Public Function RecognizeFrenchDocument(engine As IEngine, imagePath As String) As String
Dim langParams = engine.CreateLanguageParams()
langParams.Languages.Add("French") ' Requires FrenchLanguage data files at runtime path
Dim document = engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
Return document.PlainText.Text
Finally
document.Close()
End Try
End Function
IronOCR 접근 방식:
using IronOcr;
// Single language — install IronOcr.Languages.French via NuGet first
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
var result = ocr.Read("french-document.jpg");
Console.WriteLine(result.Text);
// Multiple simultaneous languages — operator overload, no data file management
var multiOcr = new IronTesseract();
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English;
var multiResult = multiOcr.Read("multilingual-contract.jpg");
Console.WriteLine(multiResult.Text);
using IronOcr;
// Single language — install IronOcr.Languages.French via NuGet first
var ocr = new IronTesseract();
ocr.Language = OcrLanguage.French;
var result = ocr.Read("french-document.jpg");
Console.WriteLine(result.Text);
// Multiple simultaneous languages — operator overload, no data file management
var multiOcr = new IronTesseract();
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English;
var multiResult = multiOcr.Read("multilingual-contract.jpg");
Console.WriteLine(multiResult.Text);
Imports IronOcr
' Single language — install IronOcr.Languages.French via NuGet first
Dim ocr As New IronTesseract()
ocr.Language = OcrLanguage.French
Dim result = ocr.Read("french-document.jpg")
Console.WriteLine(result.Text)
' Multiple simultaneous languages — operator overload, no data file management
Dim multiOcr As New IronTesseract()
multiOcr.Language = OcrLanguage.French + OcrLanguage.German + OcrLanguage.English
Dim multiResult = multiOcr.Read("multilingual-contract.jpg")
Console.WriteLine(multiResult.Text)
언어 팩은 표준 NuGet 패키지 (dotnet add package IronOcr.Languages.French)로 설치됩니다. 수동으로 배포해야 할 데이터 파일이 없고, 경로 구성도 필요 없으며, 언어를 전환할 때 엔진을 다시 초기화할 필요도 없습니다. 다국어 가이드 에서는 언어 조합에 대해 설명하고, 언어 색인에는 사용 가능한 125개 이상의 팩 목록이 나와 있습니다.
다중 프레임 TIFF 처리
ABBYY는 프레임을 순차적으로 처리하고 각 프레임을 별도의 문서 페이지로 추가하는 방식으로 여러 페이지로 구성된 TIFF 파일을 처리합니다. 프레임 수는 TIFF 객체에서 가져와야 하며, 그 후 각 프레임을 문서 컨테이너에 개별적으로 추가해야 합니다.
ABBYY FineReader 접근 방식:
using FREngine;
public string ProcessMultiFrameTiff(IEngine engine, string tiffPath)
{
var document = engine.CreateFRDocument();
try
{
// Must add each frame individually — no automatic multi-frame handling
// Page count requires reading the TIFF metadata before processing
var imageInfo = engine.CreateImageInfo();
imageInfo.LoadImageFile(tiffPath);
int frameCount = imageInfo.FrameCount;
for (int i = 0; i < frameCount; i++)
{
// Each frame added with its frame index via image processing params
var imgParams = engine.CreateImageProcessingParams();
imgParams.FrameIndex = i;
document.AddImageFile(tiffPath, imgParams, null);
}
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
using FREngine;
public string ProcessMultiFrameTiff(IEngine engine, string tiffPath)
{
var document = engine.CreateFRDocument();
try
{
// Must add each frame individually — no automatic multi-frame handling
// Page count requires reading the TIFF metadata before processing
var imageInfo = engine.CreateImageInfo();
imageInfo.LoadImageFile(tiffPath);
int frameCount = imageInfo.FrameCount;
for (int i = 0; i < frameCount; i++)
{
// Each frame added with its frame index via image processing params
var imgParams = engine.CreateImageProcessingParams();
imgParams.FrameIndex = i;
document.AddImageFile(tiffPath, imgParams, null);
}
document.Process(null);
return document.PlainText.Text;
}
finally
{
document.Close();
}
}
Imports FREngine
Public Function ProcessMultiFrameTiff(engine As IEngine, tiffPath As String) As String
Dim document = engine.CreateFRDocument()
Try
' Must add each frame individually — no automatic multi-frame handling
' Page count requires reading the TIFF metadata before processing
Dim imageInfo = engine.CreateImageInfo()
imageInfo.LoadImageFile(tiffPath)
Dim frameCount As Integer = imageInfo.FrameCount
For i As Integer = 0 To frameCount - 1
' Each frame added with its frame index via image processing params
Dim imgParams = engine.CreateImageProcessingParams()
imgParams.FrameIndex = i
document.AddImageFile(tiffPath, imgParams, Nothing)
Next
document.Process(Nothing)
Return document.PlainText.Text
Finally
document.Close()
End Try
End Function
IronOCR 접근 방식:
using IronOcr;
// LoadImageFrames handles multi-frame TIFF automatically
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");
var ocr = new IronTesseract();
var result = ocr.Read(input);
// Per-page results accessible directly
foreach (var page in result.Pages)
{
Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters");
Console.WriteLine(page.Text);
}
using IronOcr;
// LoadImageFrames handles multi-frame TIFF automatically
using var input = new OcrInput();
input.LoadImageFrames("scanned-batch.tiff");
var ocr = new IronTesseract();
var result = ocr.Read(input);
// Per-page results accessible directly
foreach (var page in result.Pages)
{
Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters");
Console.WriteLine(page.Text);
}
Imports IronOcr
' LoadImageFrames handles multi-frame TIFF automatically
Using input As New OcrInput()
input.LoadImageFrames("scanned-batch.tiff")
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
' Per-page results accessible directly
For Each page In result.Pages
Console.WriteLine($"Frame {page.PageNumber}: {page.Text.Length} characters")
Console.WriteLine(page.Text)
Next
End Using
OcrInput.LoadImageFrames은 수동 반복 없이 다중 페이지 TIFF의 모든 프레임을 읽습니다. 결과는 페이지별 result.Pages를 통해 접근할 수 있으며, 여기에는 텍스트 좌표 데이터 및 각 프레임별 신뢰도도 포함됩니다. TIFF 입력 가이드는 멀티프레임 TIFF와 애니메이션 GIF 처리 방법을 모두 다룹니다.
병렬 배치 처리
ABBYY의 COM 기반 엔진은 다중 스레드에서 CreateFRDocument을 동시에 호출하는 것이 동기화 전략 없이 안전하지 않습니다. 일반적으로 프로덕션 배치 프로세서는 엔진 인스턴스 풀을 유지하거나 잠금을 통해 액세스를 직렬화합니다. 두 접근 방식 모두IronOCR제거하는 인프라를 추가합니다.
ABBYY FineReader 접근 방식:
using FREngine;
using System.Collections.Concurrent;
using System.Threading;
public class AbbyyBatchProcessor
{
// Pool required because engine is not safely concurrent
private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
private IEngine _engine;
public async Task<Dictionary<string, string>> ProcessBatchAsync(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// Must serialize — one document at a time through single engine
foreach (var imagePath in imagePaths)
{
await _engineLock.WaitAsync();
try
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
results[imagePath] = document.PlainText.Text;
}
finally
{
document.Close();
}
}
finally
{
_engineLock.Release();
}
}
return new Dictionary<string, string>(results);
}
}
using FREngine;
using System.Collections.Concurrent;
using System.Threading;
public class AbbyyBatchProcessor
{
// Pool required because engine is not safely concurrent
private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
private IEngine _engine;
public async Task<Dictionary<string, string>> ProcessBatchAsync(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// Must serialize — one document at a time through single engine
foreach (var imagePath in imagePaths)
{
await _engineLock.WaitAsync();
try
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
results[imagePath] = document.PlainText.Text;
}
finally
{
document.Close();
}
}
finally
{
_engineLock.Release();
}
}
return new Dictionary<string, string>(results);
}
}
Imports FREngine
Imports System.Collections.Concurrent
Imports System.Threading
Public Class AbbyyBatchProcessor
' Pool required because engine is not safely concurrent
Private ReadOnly _engineLock As New SemaphoreSlim(1, 1)
Private _engine As IEngine
Public Async Function ProcessBatchAsync(imagePaths As String()) As Task(Of Dictionary(Of String, String))
Dim results As New ConcurrentDictionary(Of String, String)()
' Must serialize — one document at a time through single engine
For Each imagePath In imagePaths
Await _engineLock.WaitAsync()
Try
Dim document = _engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
results(imagePath) = document.PlainText.Text
Finally
document.Close()
End Try
Finally
_engineLock.Release()
End Try
Next
Return New Dictionary(Of String, String)(results)
End Function
End Class
IronOCR 접근 방식:
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class OcrBatchProcessor
{
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// IronTesseract is thread-safe — one instance per thread, fully parallel
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract(); // Each thread owns its instance
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
});
return new Dictionary<string, string>(results);
}
}
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class OcrBatchProcessor
{
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// IronTesseract is thread-safe — one instance per thread, fully parallel
Parallel.ForEach(imagePaths, imagePath =>
{
var ocr = new IronTesseract(); // Each thread owns its instance
var result = ocr.Read(imagePath);
results[imagePath] = result.Text;
});
return new Dictionary<string, string>(results);
}
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Public Class OcrBatchProcessor
Public Function ProcessBatch(imagePaths As String()) As Dictionary(Of String, String)
Dim results = New ConcurrentDictionary(Of String, String)()
' IronTesseract is thread-safe — one instance per thread, fully parallel
Parallel.ForEach(imagePaths, Sub(imagePath)
Dim ocr = New IronTesseract() ' Each thread owns its instance
Dim result = ocr.Read(imagePath)
results(imagePath) = result.Text
End Sub)
Return New Dictionary(Of String, String)(results)
End Function
End Class
각 IronTesseract 인스턴스는 독립적입니다. Parallel.ForEach은 공유 상태, 락 혹은 직렬화 없이 사용 가능한 CPU 코어를 포화시킵니다. ABBYY 버전은 비동기 래퍼가 있음에도 불구하고 문서를 순차적으로 처리합니다.IronOCR버전은 이들을 완전히 병렬로 처리합니다. 멀티스레딩 예제는 시간 비교를 통해 이러한 패턴을 보여줍니다. 보다 높은 수준의 처리량 제어에 대해서는 속도 최적화 가이드를 참조하십시오.
문서 내보내기 파이프라인
ABBYY는 Export 메서드를 통해 다양한 내보내기 형식을 지원하며, FileExportFormatEnum 값을 사용합니다. DOCX, RTF 또는 일반 텍스트로 내보내기 위해서는 형식별 내보내기 매개변수 객체를 생성한 다음, 적절한 열거형 값 및 매개변수 객체와 함께 document.Export을 호출해야 합니다.
ABBYY FineReader 접근 방식:
using FREngine;
public class AbbyyExporter
{
private IEngine _engine;
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Export as plain text
document.Export(
Path.Combine(outputDir, baseName + ".txt"),
FileExportFormatEnum.FEF_TextUnicodeDefaults,
null
);
// Export as searchable PDF (requires PDF export params)
var pdfParams = _engine.CreatePDFExportParams();
pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced;
pdfParams.UseOriginalPaperSize = true;
document.Export(
Path.Combine(outputDir, baseName + ".pdf"),
FileExportFormatEnum.FEF_PDF,
pdfParams
);
// Export as DOCX
var docxParams = _engine.CreateDOCXExportParams();
document.Export(
Path.Combine(outputDir, baseName + ".docx"),
FileExportFormatEnum.FEF_DOCX,
docxParams
);
}
finally
{
document.Close();
}
}
}
using FREngine;
public class AbbyyExporter
{
private IEngine _engine;
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var document = _engine.CreateFRDocument();
try
{
document.AddImageFile(imagePath, null, null);
document.Process(null);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Export as plain text
document.Export(
Path.Combine(outputDir, baseName + ".txt"),
FileExportFormatEnum.FEF_TextUnicodeDefaults,
null
);
// Export as searchable PDF (requires PDF export params)
var pdfParams = _engine.CreatePDFExportParams();
pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced;
pdfParams.UseOriginalPaperSize = true;
document.Export(
Path.Combine(outputDir, baseName + ".pdf"),
FileExportFormatEnum.FEF_PDF,
pdfParams
);
// Export as DOCX
var docxParams = _engine.CreateDOCXExportParams();
document.Export(
Path.Combine(outputDir, baseName + ".docx"),
FileExportFormatEnum.FEF_DOCX,
docxParams
);
}
finally
{
document.Close();
}
}
}
Imports FREngine
Public Class AbbyyExporter
Private _engine As IEngine
Public Sub ExportToMultipleFormats(imagePath As String, outputDir As String)
Dim document = _engine.CreateFRDocument()
Try
document.AddImageFile(imagePath, Nothing, Nothing)
document.Process(Nothing)
Dim baseName As String = Path.GetFileNameWithoutExtension(imagePath)
' Export as plain text
document.Export(
Path.Combine(outputDir, baseName & ".txt"),
FileExportFormatEnum.FEF_TextUnicodeDefaults,
Nothing
)
' Export as searchable PDF (requires PDF export params)
Dim pdfParams = _engine.CreatePDFExportParams()
pdfParams.Scenario = PDFExportScenarioEnum.PDES_Balanced
pdfParams.UseOriginalPaperSize = True
document.Export(
Path.Combine(outputDir, baseName & ".pdf"),
FileExportFormatEnum.FEF_PDF,
pdfParams
)
' Export as DOCX
Dim docxParams = _engine.CreateDOCXExportParams()
document.Export(
Path.Combine(outputDir, baseName & ".docx"),
FileExportFormatEnum.FEF_DOCX,
docxParams
)
Finally
document.Close()
End Try
End Sub
End Class
IronOCR 접근 방식:
using IronOcr;
public class OcrExporter
{
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Plain text — direct property access
File.WriteAllText(
Path.Combine(outputDir, baseName + ".txt"),
result.Text
);
// Searchable PDF — one method call, no parameter objects
result.SaveAsSearchablePdf(
Path.Combine(outputDir, baseName + ".pdf")
);
// hOCR format — for document management systems
result.SaveAsHocrFile(
Path.Combine(outputDir, baseName + ".hocr")
);
}
}
using IronOcr;
public class OcrExporter
{
public void ExportToMultipleFormats(string imagePath, string outputDir)
{
var ocr = new IronTesseract();
var result = ocr.Read(imagePath);
string baseName = Path.GetFileNameWithoutExtension(imagePath);
// Plain text — direct property access
File.WriteAllText(
Path.Combine(outputDir, baseName + ".txt"),
result.Text
);
// Searchable PDF — one method call, no parameter objects
result.SaveAsSearchablePdf(
Path.Combine(outputDir, baseName + ".pdf")
);
// hOCR format — for document management systems
result.SaveAsHocrFile(
Path.Combine(outputDir, baseName + ".hocr")
);
}
}
Imports IronOcr
Imports System.IO
Public Class OcrExporter
Public Sub ExportToMultipleFormats(imagePath As String, outputDir As String)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(imagePath)
Dim baseName As String = Path.GetFileNameWithoutExtension(imagePath)
' Plain text — direct property access
File.WriteAllText(
Path.Combine(outputDir, baseName & ".txt"),
result.Text
)
' Searchable PDF — one method call, no parameter objects
result.SaveAsSearchablePdf(
Path.Combine(outputDir, baseName & ".pdf")
)
' hOCR format — for document management systems
result.SaveAsHocrFile(
Path.Combine(outputDir, baseName & ".hocr")
)
End Sub
End Class
IronOCR의 OcrResult은 .Text에 직접 접근할 수 있으며, 매개변수 객체나 형식 열거형 없이 출력 메서드를 제공합니다. SaveAsSearchablePdf 호출은 ABBYY의 세 단계 매개변수/내보내기 시퀀스에 비해 한 줄로 PDF 내보내기를 처리합니다. 검색 가능한 PDF 가이드에는 페이지 범위 옵션 및 압축 설정이 포함되어 있습니다. hOCR 내보내기 가이드는 위치 인식 OCR 출력을 사용하는 시스템을 위한 HOCR 형식을 다룹니다.
ABBYY FineReader API와IronOCR매핑 참조
| ABBYY FineReader 엔진 | IronOCR에 상응하는 |
|---|---|
new EngineLoader() |
필요하지 않음 |
loader.GetEngineObject(sdkPath, licensePath) |
new IronTesseract() |
engine.LoadPredefinedProfile("...") |
필수 사항이 아닙니다 (내부적으로 처리됩니다). |
engine.CreateLanguageParams() |
필요하지 않음 |
langParams.Languages.Add("French") |
ocr.Language = OcrLanguage.French |
langParams.Languages.Add("English") + langParams.Languages.Add("German") |
ocr.Language = OcrLanguage.English + OcrLanguage.German |
engine.CreateFRDocument() |
new OcrInput() |
engine.CreateFRDocumentFromImage(path, null) |
ocr.Read(path) |
document.AddImageFile(path, null, null) |
input.LoadImage(path) |
imageInfo.LoadImageFile(tiff) + frameCount 루프 |
input.LoadImageFrames(tiff) |
engine.CreatePDFFile() 그 후 pdfFile.Open(path, null, null) |
input.LoadPdf(path) |
document.Process(null) |
ocr.Read(input) |
document.PlainText.Text |
result.Text |
frDocument.Pages[i].PlainText.Text |
result.Pages[i].Text |
page.Layout.Blocks + BlockTypeEnum.BT_Table 검사 |
result.Pages + 단어 좌표 데이터 |
block.GetAsTableBlock() |
result.Pages[i].Lines (좌표 포함) |
engine.CreatePDFExportParams() |
필요하지 않음 |
document.Export(path, FEF_PDF, params) |
result.SaveAsSearchablePdf(path) |
document.Export(path, FEF_TextUnicodeDefaults, null) |
File.WriteAllText(path, result.Text) |
engine.CreateDOCXExportParams() + 내보내기 |
직접적인 지원은 제공되지 않습니다. |
document.Close() |
using에 의해 OcrInput에서 처리됨 |
_engine.GetLicenseInfo().ExpirationDate |
IronOcr.License.IsValidLicense |
라이선스 파일 (ABBYY.lic, ABBYY.key) |
IronOcr.License.LicenseKey = "key" |
engine.CreateZone() + zone.SetBounds(x, y, w, h) |
new CropRectangle(x, y, width, height) |
일반적인 마이그레이션 문제와 해결책
문제 1: SDK 제거 후 COM 등록 오류 발생
ABBYY: 프로젝트 참조에서 FREngine.dll를 제거한 후에도 여전히 Could not load type 'FREngine.EngineLoader' 또는 이전 네임스페이스를 유지한 클래스에서 COM 상호 운용 오류로 인해 빌드가 실패할 수 있습니다.
해결책: 참조를 제거하기 전에 모든 FREngine 및 ABBYY.FineReader 사용을 조사하십시오. IDisposable를 구현하여 특정 IEngine 필드를 무효화하는 모든 클래스는 그 폐기 논리를 OcrInput의 using 블록으로 교체해야 합니다:
// Before: explicit Close in finally
var document = _engine.CreateFRDocument();
try { document.Process(null); }
finally { document.Close(); }
// After: using pattern on OcrInput
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);
// Before: explicit Close in finally
var document = _engine.CreateFRDocument();
try { document.Process(null); }
finally { document.Close(); }
// After: using pattern on OcrInput
using var input = new OcrInput();
input.LoadImage(imagePath);
var result = new IronTesseract().Read(input);
Option Strict On
' Before: explicit Close in finally
Dim document = _engine.CreateFRDocument()
Try
document.Process(Nothing)
Finally
document.Close()
End Try
' After: using pattern on OcrInput
Using input As New OcrInput()
input.LoadImage(imagePath)
Dim result = New IronTesseract().Read(input)
End Using
문제 2: 인식 프로필에 상응하는 것이 없음
ABBYY: engine.LoadPredefinedProfile("DocumentConversion_Speed") 또는 engine.LoadPredefinedProfile("FieldLevelRecognition")을 호출하는 코드는 정확도와 처리량을 맞추기 위해 ABBYY 전용 프로필을 사용합니다. IronOCR에는 Profile라는 이름의 동등한 속성이 없습니다.
해결책: IronOCR은 IronTesseract.Configuration를 통해 동일한 절충점을 노출합니다. 속도 최적화를 위해 ocr.Configuration.TesseractVersion = TesseractVersion.Tesseract5(기본값)를 설정하고 전처리 필터를 줄이세요. 정확도를 최대한 높이려면 전체 전처리 파이프라인을 추가하십시오.
// Speed-optimized
var ocr = new IronTesseract();
// 아니요 preprocessing — fastest path
var result = ocr.Read("clean-document.jpg");
// Accuracy-optimized for difficult inputs
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("degraded-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
var result = ocr.Read(input);
// Speed-optimized
var ocr = new IronTesseract();
// 아니요 preprocessing — fastest path
var result = ocr.Read("clean-document.jpg");
// Accuracy-optimized for difficult inputs
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("degraded-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
var result = ocr.Read(input);
Imports IronTesseract
' Speed-optimized
Dim ocr As New IronTesseract()
' 아니요 preprocessing — fastest path
Dim result = ocr.Read("clean-document.jpg")
' Accuracy-optimized for difficult inputs
Dim ocr As New IronTesseract()
Using input As New OcrInput()
input.LoadImage("degraded-scan.jpg")
input.Deskew()
input.DeNoise()
input.Contrast()
input.Binarize()
Dim result = ocr.Read(input)
End Using
이미지 품질 보정 가이드에서는 어떤 필터가 어떤 입력 품질 문제를 해결하는지 설명합니다. 속도 최적화 가이드에서는 깨끗한 문서의 처리 시간을 줄이는 구성 속성을 다룹니다.
문제 3: 라이선스 파일 배포 단계가 CI/CD에 남아 있음
ABBYY: 빌드 파이프라인에는 일반적으로 ABBYY.lic 및 ABBYY.key을 안전한 저장소에서 배포 대상으로 복사하는 단계가 포함되어 있습니다. 마이그레이션 후 팀에서 이 단계를 제거하는 것을 잊어버리는 경우가 있어 더 이상 존재하지 않는 경로를 참조하는 사용되지 않는 배포 코드가 남게 됩니다.
해결책: 라이선스 파일 복사 단계를 완전히 삭제합니다. 이를 환경 변수 주입 단계로 대체하십시오.
# Remove these CI/CD steps after migration:
# - name: Copy ABBYY license files
# run: |
# cp $SECRETS_PATH/ABBYY.lic $DEPLOY_PATH/License/
# cp $SECRETS_PATH/ABBYY.key $DEPLOY_PATH/License/
# Add this instead (environment variable injection):
# - name: SetIronOCR license
# env:
# IRONOCR_LICENSE_KEY: ${{secrets.IRONOCR_LICENSE}}
# Remove these CI/CD steps after migration:
# - name: Copy ABBYY license files
# run: |
# cp $SECRETS_PATH/ABBYY.lic $DEPLOY_PATH/License/
# cp $SECRETS_PATH/ABBYY.key $DEPLOY_PATH/License/
# Add this instead (environment variable injection):
# - name: SetIronOCR license
# env:
# IRONOCR_LICENSE_KEY: ${{secrets.IRONOCR_LICENSE}}
그리고 애플리케이션 시작 시:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IRONOCR_LICENSE_KEY not set");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
?? throw new InvalidOperationException("IRONOCR_LICENSE_KEY not set");
Imports System
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY"), Throw New InvalidOperationException("IRONOCR_LICENSE_KEY not set"))
문제 4: 엔진이 스레드 안전하지 않음 — 기존 잠금 코드
ABBYY: 여러 스레드에서 ABBYY를 호출하는 애플리케이션은 일반적으로 SemaphoreSlim, lock 문 또는 스레드 로컬 엔진 인스턴스를 포함하여 COM 쓰레딩 문제를 피합니다. 이 동기화 코드는 ABBYY의 스레딩 모델에 특화된 것입니다.
해결 방법: ABBYY 호출을 감싸는 모든 동기화 코드를 삭제하십시오. IronOCR의 IronTesseract은 스레드당 인스턴스화가 안전합니다:
// Remove all of this:
// private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
// await _engineLock.WaitAsync();
// try { ... } finally { _engineLock.Release(); }
// Replace with:
Parallel.ForEach(documents, doc =>
{
var ocr = new IronTesseract(); // One per thread — no lock needed
results[doc.Id] = ocr.Read(doc.Path).Text;
});
// Remove all of this:
// private readonly SemaphoreSlim _engineLock = new SemaphoreSlim(1, 1);
// await _engineLock.WaitAsync();
// try { ... } finally { _engineLock.Release(); }
// Replace with:
Parallel.ForEach(documents, doc =>
{
var ocr = new IronTesseract(); // One per thread — no lock needed
results[doc.Id] = ocr.Read(doc.Path).Text;
});
Imports System.Threading.Tasks
Parallel.ForEach(documents, Sub(doc)
Dim ocr = New IronTesseract() ' One per thread — no lock needed
results(doc.Id) = ocr.Read(doc.Path).Text
End Sub)
Issue 5: CreateImageInfo / FrameCount Pattern for TIFF
ABBYY: engine.CreateImageInfo() 및 imageInfo.LoadImageFile()을 사용하여 TIFF 파일에서 프레임 수를 읽고 프레임을 순환하기 전에 내부적으로 프레임 열거를 처리하는 IronOCR에 직접적인 대응이 없습니다.
해결 방법: 프레임 카운팅 루프를 완전히 삭제합니다.
// Remove:
// var imageInfo = engine.CreateImageInfo();
// imageInfo.LoadImageFile(tiffPath);
// for (int i = 0; i < imageInfo.FrameCount; i++) { document.AddImageFile(...) }
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames("multi-page-scan.tiff");
var result = new IronTesseract().Read(input);
// result.Pages contains one entry per TIFF frame
// Remove:
// var imageInfo = engine.CreateImageInfo();
// imageInfo.LoadImageFile(tiffPath);
// for (int i = 0; i < imageInfo.FrameCount; i++) { document.AddImageFile(...) }
// Replace with:
using var input = new OcrInput();
input.LoadImageFrames("multi-page-scan.tiff");
var result = new IronTesseract().Read(input);
// result.Pages contains one entry per TIFF frame
Imports IronOcr
' Remove:
' Dim imageInfo = engine.CreateImageInfo()
' imageInfo.LoadImageFile(tiffPath)
' For i As Integer = 0 To imageInfo.FrameCount - 1
' document.AddImageFile(...)
' Next
' Replace with:
Using input As New OcrInput()
input.LoadImageFrames("multi-page-scan.tiff")
Dim result = New IronTesseract().Read(input)
' result.Pages contains one entry per TIFF frame
End Using
문제 6: DOCX 내보내기 기능에 직접적인 대응 기능이 없음
ABBYY: document.Export(path, FileExportFormatEnum.FEF_DOCX, docxParams)은 워드 문서를 생성합니다.IronOCR DOCX 출력 파일을 직접 생성하지 않습니다.
해결책:IronOCR검색 가능한 PDF 파일과 구조화된 텍스트 데이터를 생성합니다. DOCX 출력을 필요로 하는 워크플로의 경우, 실질적인 마이그레이션 경로는 검색 가능한 PDF를 생성한 후 하위 단계에서 변환하거나, Open XML SDK와 같은 라이브러리를 사용하여 구조화된 텍스트를 추출하고 DOCX로 저장하는 것입니다.
//IronOCR to searchable PDF (closest equivalent)
var result = new IronTesseract().Read(inputPath);
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"));
// Or extract structured text for downstream DOCX generation
foreach (var paragraph in result.Paragraphs)
{
Console.WriteLine(paragraph.Text);
// Write to DOCX via Open XML SDK or similar
}
//IronOCR to searchable PDF (closest equivalent)
var result = new IronTesseract().Read(inputPath);
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"));
// Or extract structured text for downstream DOCX generation
foreach (var paragraph in result.Paragraphs)
{
Console.WriteLine(paragraph.Text);
// Write to DOCX via Open XML SDK or similar
}
Imports IronOcr
' IronOCR to searchable PDF (closest equivalent)
Dim result = New IronTesseract().Read(inputPath)
result.SaveAsSearchablePdf(outputPath.Replace(".docx", ".pdf"))
' Or extract structured text for downstream DOCX generation
For Each paragraph In result.Paragraphs
Console.WriteLine(paragraph.Text)
' Write to DOCX via Open XML SDK or similar
Next
읽기 결과 가이드에서는 단락, 줄, 단어 및 문자 수준 좌표 데이터를 하위 처리에 활용하는 방법을 설명합니다.
ABBYY FineReader 마이그레이션 체크리스트
이동 전 작업
변경 작업을 하기 전에 코드베이스를 검토하십시오.
# Find all files using ABBYY namespaces
grep -r "using FREngine" --include="*.cs" .
grep -r "using ABBYY" --include="*.cs" .
# Find engine lifecycle patterns
grep -r "EngineLoader\|GetEngineObject\|LoadPredefinedProfile" --include="*.cs" .
# Find document lifecycle patterns
grep -r "CreateFRDocument\|document\.Close\|AddImageFile\|document\.Process" --include="*.cs" .
# Find language configuration
grep -r "CreateLanguageParams\|langParams\.Languages" --include="*.cs" .
# Find export calls
grep -r "FileExportFormatEnum\|CreatePDFExportParams\|document\.Export" --include="*.cs" .
# Find license file references in CI/CD and deployment scripts
grep -r "ABBYY\.lic\|ABBYY\.key" .
# Find all files using ABBYY namespaces
grep -r "using FREngine" --include="*.cs" .
grep -r "using ABBYY" --include="*.cs" .
# Find engine lifecycle patterns
grep -r "EngineLoader\|GetEngineObject\|LoadPredefinedProfile" --include="*.cs" .
# Find document lifecycle patterns
grep -r "CreateFRDocument\|document\.Close\|AddImageFile\|document\.Process" --include="*.cs" .
# Find language configuration
grep -r "CreateLanguageParams\|langParams\.Languages" --include="*.cs" .
# Find export calls
grep -r "FileExportFormatEnum\|CreatePDFExportParams\|document\.Export" --include="*.cs" .
# Find license file references in CI/CD and deployment scripts
grep -r "ABBYY\.lic\|ABBYY\.key" .
모든 IEngine 또는 IFRDocument 필드를 보유한 클래스를 문서화하십시오. 사용 중인 내보내기 형식을 확인하십시오. DOCX 출력에는 다른 접근 방식이 필요합니다(위의 6번 문제 참조).
코드 업데이트 작업
- 모든
.csproj파일에서FREngine.dll참조를 제거하세요. - ABBYY를 사용한 각 프로젝트에서
dotnet add package IronOcr을 실행하세요. - 애플리케이션 시작 시
IronOcr.License.LicenseKey = ...를 추가하세요 (Program.cs또는 시작 클래스) - 모든 비영어 언어에 대한 언어 NuGet 패키지를 설치하세요 (
dotnet add package IronOcr.Languages.French등). - 모든
EngineLoader,GetEngineObject,LoadPredefinedProfile호출을 삭제하세요. - 모든
CreateLanguageParams및langParams.Languages.Add호출을 삭제하세요. engine.CreateFRDocument()+document.AddImageFile()+document.Process()을new IronTesseract().Read(path)로 대체하세요.- 다중 프레임 TIFF 루프를
input.LoadImageFrames(tiffPath)로 대체하세요. document.PlainText.Text을result.Text로 대체하세요.frDocument.Pages[i].PlainText.Text을result.Pages[i].Text로 대체하세요.document.Export(..., FEF_PDF, pdfParams)을result.SaveAsSearchablePdf(path)로 대체하세요.- 모든
document.Close()호출을OcrInput의using블록으로 교체하세요. - ABBYY 엔진 액세스를 직렬화했던
SemaphoreSlim및 잠금 코드를 삭제하세요. engine.CreateZone()/zone.SetBounds()/page.Zones.Add()을input.LoadImage()에 전달되는new CropRectangle(x, y, width, height)로 대체하세요.- CI/CD 파이프라인에서 라이선스 파일 복사 단계를 제거합니다.
- Docker 이미지를 업데이트하세요 — SDK 설치 계층을 제거하고 Linux 대상에 대해
libgdiplus을 추가하세요.
마이그레이션 후 테스트
- 각 문서 유형(송장, 계약서, 스캔한 양식)의 대표 샘플을 사용하여 텍스트 추출 결과를 검증합니다.
- 다중 페이지 TIFF 처리 결과가 ABBYY에서 생성된 프레임 수와 동일한 페이지 수를 반환하는지 확인합니다.
- ABBYY 기준 비교에 사용된 것과 동일한 입력값을 사용하여 다국어 문서를 테스트합니다.
- 검색 가능한 PDF 출력물이 Adobe Reader 및 브라우저 PDF 뷰어에서 텍스트 검색이 가능한지 확인합니다.
- 프로덕션 동시 실행 수준으로 병렬 배치 프로세서를 실행하고 예외가 발생하지 않는지 확인합니다.
- 확인된 문서에서
result.Confidence을 확인하여 품질 게이트의 기준 임계값을 설정하세요. - 스테이징 배포 환경에서 환경 변수를 이용한 라이선스 키 초기화 테스트
- ABBYY SDK 볼륨 마운트 없이 Docker 이미지가 빌드되고 OCR이 실행되는지 확인합니다.
- 라이선스 파일 복사 단계를 제외하고 CI/CD 파이프라인이 완료되는지 확인합니다.
- 배치 프로세서에 메모리 프로파일러를 실행하여
OcrInput객체가 누출되지 않았는지 확인합니다 (확인using위치).
IronOCR로 마이그레이션할 때의 주요 이점
배포 복잡성이 10분의 1로 줄어듭니다. 이전에는 모든 ABBYY 배포 시 SDK 설치, 라이선스 파일 배치, 런타임 경로 구성, 그리고 애플리케이션 실행 전에 파일이 올바른 경로에 있는지 확인하는 검증 작업이 필요했습니다.IronOCR NuGet 종속성으로 배포됩니다. dotnet publish은 OCR 엔진이 포함된 독립적인 아티팩트를 생성합니다. Docker 배포 가이드 와 Azure 설정 가이드는 전체 구성 과정을 한 페이지에 모두 보여줍니다.
COM 상호 운영이 사라졌습니다. COM 계층을 제거하면 새 기계에서의 COM 등록 오류, 아파트 쓰레딩 불일치, RCW 수명 주기 버그, 그리고 모든 ABBYY 문서 처리 호출에 필요한 15-25 줄의 try/finally 보일러플레이트를 제거함으로써 런타임 오류 범주 전체를 제거합니다. 코드베이스 크기가 줄어듭니다. 오차면의 크기도 함께 줄어듭니다.
더 이상 문서량 증가가 예산 검토를 유발하지 않습니다. IronOCR의 영구 라이선스는 문서량에 제한이 없습니다. 첫 해에 월 1만 건의 문서를 처리하고 3년 차에 월 200만 건의 문서를 처리하는 애플리케이션의 OCR 라이선스 비용은 동일합니다. 페이지당 사용량 계산이나 초과 사용 요금 청구, 사용량 등급별 재협상은 없습니다. 라이선스 페이지에는 모든 등급이 표시됩니다. 2,999달러의 Professional 라이선스는 10명의 개발자가 배포 대상 수에 관계없이 모든 볼륨을 처리할 수 있도록 지원합니다.
크로스 플랫폼 배포로 새로운 인프라 옵션이 제공됩니다. ABBYY COM 레이어는 Windows를 필요로 합니다. 비용 절감이나 용량 증대를 위해 문서 처리를 리눅스 컨테이너로 이전하려던 팀들은 제지를 받았습니다.IronOCR동일한 NuGet 패키지를 사용하여 Windows, Linux 및 macOS에서 동일하게 실행됩니다. ABBYY에서 마이그레이션하면 애플리케이션 스택의 OCR 계층에서 Windows 제약 조건이 제거됩니다. Linux 배포 가이드 와 AWS 배포 가이드는 각 환경에 대한 전체 설정 과정을 다룹니다.
인프라 구축 작업 없이 병렬 처리량을 사용할 수 있습니다. ABBYY 엔진 접근을 직렬화하는 데 사용되던 잠금 전략이 사라졌습니다. IronTesseract 인스턴스는 독립적입니다: 스레드당 하나씩 가동하고 문서 배치에 대해 Parallel.ForEach을 실행하며 결과를 가져옵니다. 추가 코드 없이 사용 가능한 CPU 코어 수에 따라 처리량이 확장됩니다. 멀티스레딩 예제는 멀티코어 하드웨어에서 실제 성능 향상을 보여줍니다.
언어 구성은 패키지 참조입니다. ABBYY 통합에 독일어 또는 일본어 OCR 지원을 추가하려면 데이터 파일을 식별하고, 모든 대상 시스템의 런타임 경로에 배포하고, 파일이 누락되었을 때 오류를 처리해야 했습니다. IronOCR을 통해 dotnet add package IronOcr.Languages.German은 언어 팩을 버전 관리된, 재현 가능한 NuGet 종속성으로 추가합니다. 패키지 관리자는 모든 빌드에 데이터가 포함되도록 보장합니다. 사용자 지정 언어 팩 가이드에서는 특정 도메인을 위한 사용자 지정 언어 모델을 학습하고 배포하는 방법을 다룹니다.
자주 묻는 질문
ABBYY FineReader 엔진에서 IronOCR로 마이그레이션해야 하는 이유는 무엇인가요?
일반적인 동인으로는 COM 상호 운용의 복잡성 제거, 파일 기반 라이선스 관리 대체, 페이지당 과금 방지, Docker/컨테이너 배포 활성화, 표준 .NET 툴링과 통합되는 NuGet 네이티브 워크플로 채택 등이 있습니다.
ABBYY FineReader 엔진에서 IronOCR로 마이그레이션할 때 주요 코드 변경 사항은 무엇인가요?
ABBYY FineReader 초기화 시퀀스를 IronTesseract 인스턴스화로 대체하고, COM 수명 주기 관리(명시적 생성/로드/폐쇄 패턴)를 제거하며, 결과 속성 이름을 업데이트합니다. 그 결과 상용구 줄이 크게 줄어듭니다.
마이그레이션을 시작하려면 IronOCR을 어떻게 설치하나요?
패키지 관리자 콘솔에서 '설치-패키지 IronOcr'을 실행하거나 CLI에서 '닷넷 추가 패키지 IronOcr'을 실행하세요. 언어 팩은 별도의 패키지입니다: 예를 들어 프랑스어의 경우 '닷넷 추가 패키지 IronOcr.Languages.French'를 실행합니다.
IronOCR은 표준 비즈니스 문서에 대해 ABBYY FineReader 엔진의 OCR 정확도와 일치합니까?
IronOCR은 송장, 계약서, 영수증, 타이핑된 양식 등 표준 비즈니스 콘텐츠에 대해 높은 정확도를 달성합니다. 이미지 전처리 필터(데스큐, 노이즈 제거, 대비 향상)는 품질이 저하된 입력에 대한 인식률을 더욱 향상시킵니다.
ABBYY FineReader 엔진이 별도로 설치하는 언어 데이터는 어떻게 처리하나요?
IronOCR의 언어 데이터는 NuGet 패키지로 배포됩니다. '닷넷 추가 패키지 IronOcr.Languages.German'은 독일어 지원을 설치합니다. 수동 파일 배치나 디렉터리 경로는 필요하지 않습니다.
ABBYY FineReader 엔진에서 IronOCR로 마이그레이션하려면 배포 인프라를 변경해야 합니까?
IronOCR은 ABBYY FineReader 엔진보다 인프라 변경이 더 적게 필요합니다. SDK 바이너리 경로, 라이선스 파일 배치 또는 라이선스 서버 구성이 필요하지 않습니다. NuGet 패키지에는 전체 OCR 엔진이 포함되어 있으며 라이선스 키는 애플리케이션 코드에 설정된 문자열입니다.
마이그레이션 후 IronOCR 라이선싱은 어떻게 구성하나요?
애플리케이션 시작 코드에 IronOcr.License.LicenseKey = "YOUR-KEY"를 할당하세요. Docker 또는 Kubernetes에서 키를 환경 변수로 저장하고 시작 시 키를 읽습니다. 트래픽을 수락하기 전에 License.IsValidLicense를 사용하여 유효성을 검사하세요.
IronOCR은 ABBYY FineReader와 동일한 방식으로 PDF를 처리할 수 있나요?
예. IronOCR은 원본 및 스캔한 PDF를 모두 읽습니다. IronTesseract를 인스턴스화하고, 입력이 PDF 경로 또는 OcrPdfInput인 경우 ocr.Read(input)를 호출하고, OcrResult 페이지를 반복하면 됩니다. 별도의 PDF 렌더링 파이프라인이 필요하지 않습니다.
IronOCR은 대량 처리에서 스레딩을 어떻게 처리하나요?
IronTesseract는 스레드별로 인스턴스화해도 안전합니다. Parallel.ForEach 또는 Task 풀에서 스레드당 하나의 인스턴스를 스핀업하고, OCR을 동시에 실행하고, 완료되면 각 인스턴스를 폐기하면 됩니다. 전역 상태나 잠금이 필요하지 않습니다.
IronOCR은 텍스트 추출 후 어떤 출력 형식을 지원하나요?
IronOCR은 텍스트, 단어 좌표, 신뢰도 점수, 페이지 구조를 포함한 구조화된 결과를 반환합니다. 내보내기 옵션에는 일반 텍스트, 검색 가능한 PDF, 다운스트림 처리를 위한 구조화된 결과 개체가 포함됩니다.
워크로드 확장을 위해 ABBYY FineReader 엔진보다 IronOCR 가격이 더 예측 가능한가요?
IronOCR은 페이지당 또는 볼륨당 요금이 없는 정액제 영구 라이선스를 사용합니다. 10,000페이지를 처리하든 1,000만 페이지를 처리하든 라이선스 비용은 일정하게 유지됩니다. 볼륨 및 팀 라이선스 옵션은 IronOCR 가격 페이지에서 확인할 수 있습니다.
ABBYY FineReader 엔진에서 IronOCR로 마이그레이션한 후 기존 테스트는 어떻게 되나요?
추출된 텍스트 콘텐츠에 대해 어설트하는 테스트는 마이그레이션 후에도 계속 통과해야 합니다. API 호출 패턴 또는 COM 개체 수명 주기의 유효성을 검사하는 테스트는 IronOCR의 더 간단한 초기화 및 결과 모델을 반영하도록 업데이트해야 합니다.

