송장 OCR 오픈 소스 비교: 최고의 도구를 찾아보세요
LEADTOOLS는 1990년부터 SDK 소프트웨어를 공급해왔으며, 그 OCR 통합은 그 유산을 완전히 이어받고 있습니다: 단일 문자를 인식하기 전에, 애플리케이션은 디스크에서 두 개의 바이너리 파일을 찾아야 하고, LEADTOOLS.LIC과 LEADTOOLS.LIC.KEY 사이의 쌍 인증, RasterCodecs 인스턴스 초기화, 세 가지 별개의 옵션 중에서 엔진 유형 선택, 실행 디렉터리 경로와 함께 engine.Startup() 호출을 수행해야 실제 인식 작업이 시작됩니다. 모든 운영 시스템에는 라이선스 파일이 특정 경로에 배포되어야 합니다. Docker컨테이너를 사용하려면 해당 파일을 마운트하거나 빌드해야 합니다. CI/CD 파이프라인은 파일이 올바른 위치에 있어야 하며, 그렇지 않으면 애플리케이션 시작 시 오류가 발생합니다. 또한 개발자가 잘못된 번들을 구매한 경우 OCR 모듈이 전혀 포함되지 않을 수 있습니다. LEADTOOLS는 OCR 기능을 이미지 코덱과 별도의 모듈로 판매하기 때문입니다.
리드툴즈 OCR 이해하기
LEADTOOLS는 1990년부터 상용 SDK 소프트웨어를 출시해 온 LEAD Technologies의 문서 이미지 처리 플랫폼입니다. OCR 기능은 문서 뷰어, 의료 영상(DICOM/PACS), 바코드 판독, 양식 인식, 주석 달기, PDF 조작 등 다양한 기능을 포함하는 툴킷의 여러 모듈 중 하나입니다. 이러한 아키텍처는 LEADTOOLS가 OCR 라이브러리가 아니라 OCR 기능을 포함하는 이미지 처리 플랫폼임을 의미합니다.
LEADTOOLS OCR의 주요 아키텍처 특징:
- 세 가지 개별 OCR 엔진: LEAD 독점 엔진(기본 라이선스에 포함), Tesseract 래퍼(tessdata 파일 필요), OmniPage 엔진(별도의 Kofax 라이선스 계약 및 다른 공급업체와의 협력 필요). 엔진 선택은 코드와 비용 모두에 영향을 미칩니다.
- 파일 기반 라이선스 배포: 두 파일 —
LEADTOOLS.LIC와LEADTOOLS.LIC.KEY는 런타임에 읽을 수 있어야 합니다. 경로는 하드코딩되거나 시작 시 해결됩니다. 경로 해결은 IIS 대 콘솔 호스트, Docker대 베어 메탈, 릴리스 빌드 대 디버그에서 다르게 동작합니다. RasterCodecs을 필요로 하는 중간 요소: 이미지 로딩은 OCR 엔진으로 직접 이동하지 않습니다. 모든 이미지 — PDF 페이지를 포함한다 —는 처음에RasterCodecs인스턴스를 통해 로드되며, 엔진이 시작되기 전에 초기화되어야 합니다.- 명시적 엔진 라이프사이클:
OcrEngineManager.CreateEngine()가 엔진을 생성하고,engine.Startup()가 그것을 메모리에 로드하면 (500–2000 ms),engine.Shutdown()는Dispose()전에 호출되어야 합니다. 종료 호출 순서를 잘못하면 오류가 발생합니다. - 번들 가격 책정의 복잡성: LEADTOOLS는 가격을 공개적으로 게시하지 않습니다. 귀하의 특정 요구 사항에 따라 견적을 받으려면 LEADTOOLS 영업 부서에 연락하십시오. 업데이트를 받기 위해 연간 유지보수 요금을 내야 합니다.
- 큰 배포 발자국: 생산 배포에는 여러 LEADTOOLS DLL,
OcrRuntime/디렉터리, 라이선스 파일이 포함되며, Tesseract 엔진을 사용하는 경우 선택적으로tessdata/폴더도 포함됩니다.
초기화 시퀀스
LEADTOOLS를 사용하려면 인식이 이루어지기 전에 특정 단계의 설정 과정을 거쳐야 합니다. 순서가 선택 사항이 아닙니다 — Startup()를 SetLicense() 전에 또는 RasterCodecs가 초기화되기 전에 호출하면 런타임 오류가 발생합니다:
using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
public class LeadtoolsOcrService : IDisposable
{
private IOcrEngine _ocrEngine;
private RasterCodecs _codecs;
public LeadtoolsOcrService()
{
// Step 1: Locate and validate both license files
RasterSupport.SetLicense(
@"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText(@"C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"));
// Step 2: Initialize image codec layer
_codecs = new RasterCodecs();
// Step 3: Select engine type — wrong choice means different behavior
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
// Step 4: Load runtime into memory (500–2000ms)
_ocrEngine.Startup(_codecs, null, null,
@"C:\LEADTOOLS\OCR\OcrRuntime");
}
public string ExtractText(string imagePath)
{
using var image = _codecs.Load(imagePath);
using var document = _ocrEngine.DocumentManager.CreateDocument();
var page = document.Pages.AddPage(image, null);
page.Recognize(null); // Recognition is not automatic
return page.GetText(-1);
}
public void Dispose()
{
_ocrEngine?.Shutdown(); // Must call before Dispose
_ocrEngine?.Dispose();
_codecs?.Dispose();
}
}
using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
public class LeadtoolsOcrService : IDisposable
{
private IOcrEngine _ocrEngine;
private RasterCodecs _codecs;
public LeadtoolsOcrService()
{
// Step 1: Locate and validate both license files
RasterSupport.SetLicense(
@"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText(@"C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"));
// Step 2: Initialize image codec layer
_codecs = new RasterCodecs();
// Step 3: Select engine type — wrong choice means different behavior
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
// Step 4: Load runtime into memory (500–2000ms)
_ocrEngine.Startup(_codecs, null, null,
@"C:\LEADTOOLS\OCR\OcrRuntime");
}
public string ExtractText(string imagePath)
{
using var image = _codecs.Load(imagePath);
using var document = _ocrEngine.DocumentManager.CreateDocument();
var page = document.Pages.AddPage(image, null);
page.Recognize(null); // Recognition is not automatic
return page.GetText(-1);
}
public void Dispose()
{
_ocrEngine?.Shutdown(); // Must call before Dispose
_ocrEngine?.Dispose();
_codecs?.Dispose();
}
}
Imports Leadtools
Imports Leadtools.Ocr
Imports Leadtools.Codecs
Imports System.IO
Public Class LeadtoolsOcrService
Implements IDisposable
Private _ocrEngine As IOcrEngine
Private _codecs As RasterCodecs
Public Sub New()
' Step 1: Locate and validate both license files
RasterSupport.SetLicense(
"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText("C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"))
' Step 2: Initialize image codec layer
_codecs = New RasterCodecs()
' Step 3: Select engine type — wrong choice means different behavior
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD)
' Step 4: Load runtime into memory (500–2000ms)
_ocrEngine.Startup(_codecs, Nothing, Nothing,
"C:\LEADTOOLS\OCR\OcrRuntime")
End Sub
Public Function ExtractText(imagePath As String) As String
Using image = _codecs.Load(imagePath)
Using document = _ocrEngine.DocumentManager.CreateDocument()
Dim page = document.Pages.AddPage(image, Nothing)
page.Recognize(Nothing) ' Recognition is not automatic
Return page.GetText(-1)
End Using
End Using
End Function
Public Sub Dispose() Implements IDisposable.Dispose
If _ocrEngine IsNot Nothing Then
_ocrEngine.Shutdown() ' Must call before Dispose
_ocrEngine.Dispose()
End If
If _codecs IsNot Nothing Then
_codecs.Dispose()
End If
End Sub
End Class
이는 최소한의 실행 가능한 구현입니다. 이는 라이선스 경로 실패, 엔진 상태 유효성 검사 또는 배치 처리에 필요한 메모리 관리(여기서 RasterImage 인스턴스를 해제하는 것을 잊으면 프로세스가 충돌할 때까지 메모리가 축적됨)에는 포함되지 않습니다.
IronOCR이해하기
IronOCR 최적화된 Tesseract 5 LSTM 엔진을 기반으로 구축된 상 for .NET OCR 라이브러리입니다. IronOCR은 OCR 기능에 특화된 제품이며, OCR을 하나의 모듈로 포함하는 이미지 처리 플랫폼이 아닙니다. 설계 목표는 개발자와 인식된 텍스트 사이에 있는 모든 인프라 오버헤드를 제거하는 것입니다.
IronOCR의 주요 특징:
- 단일 NuGet 패키지:
dotnet add package IronOcr는 필요한 모든 것이 포함된 네이티브 종속성과 함께 추가 런타임 디렉터리, tessdata 폴더, 배포할 라이선스 파일 없이 설치됩니다. - 문자열 기반 라이선싱: 한 줄로 라이선스 키를 할당할 수 있습니다. 키는 환경 변수,
appsettings.json, Azure Key Vault 또는 AWS Secrets Manager에서 제공될 수 있으며, 이미 사용 중인 비밀 관리 패턴을 사용할 수 있습니다. 디스크에 파일이 없습니다. - 엔진 라이프사이클 없음:
IronTesseract는 첫 사용 시 지연 로딩으로 초기화됩니다.Startup()호출이 없으며,Shutdown()호출이 없으며, 작업 간 엔진 자체를 해제할 필요도 없습니다. - 네이티브 PDF 입력: PDF는
OcrInput.LoadPdf()을 통해 직접 로드됩니다. 페이지별 배스터라이제이션 루프, 바이트 순서 지정, 중간RasterImage객체의 수동 해제도 없습니다. - 자동 전처리: 데스크유, 노이즈 제거, 대비 강화, 이진화, 해상도 정규화는
OcrInput에서 단일 메소드 호출로 가능합니다. 이 규칙들은 모든 페이지에 동시에 적용됩니다. - NuGet을 통해 125+ 언어: 각 언어는 별도의 NuGet 패키지 (
IronOcr.Languages.French등)로 제공됩니다. 관리할 tessdata 디렉터리가 없으며, 경로 구성도 필요 없습니다. - 기본적으로 스레드 안전:
IronTesseract인스턴스는 동시 사용 시 안전합니다. 병렬 처리는 동기화 코드를 필요로 하지 않습니다. - 영구 라이선스: $999 Lite / $1,499 Plus / $2,999 Professional / $5,999 Unlimited, 1년 업데이트가 포함된 일회성 구매.
기능 비교
| 기능 | 리드툴즈 OCR | IronOCR |
|---|---|---|
| 설정 복잡성 | 높음 — 4단계 초기화 시퀀스 | 낮음 — NuGet 패키지 1개 |
| 라이선스 배포 | 모든 기기의 두 파일 (.LIC + .LIC.KEY) |
문자열 키, 어디에든 저장 |
| 가격 모델 | 가격 책정을 위해 LEADTOOLS 영업 부서에 연락하십시오. | $5,999 일회성 영구 |
| PDF 지원 | 페이지별 수동 래스터화 | 네이티브 LoadPdf() |
| 전처리 | 수동 - 개별 이미지 처리 명령 | 내장 필터 메서드 |
| 엔진 수명 주기 | 수동 Startup() / Shutdown() 필요 |
자동, 게으름 |
| NuGet 패키지 | 다중(Leadtools, Leadtools.Ocr, Leadtools.Codecs, Leadtools.Pdf) | 하나님 (IronOcr) |
| 스레드 안전성 | 세심한 관리가 필요합니다. | 내장형 |
상세 기능 비교
| 기능 | 리드툴즈 OCR | IronOCR |
|---|---|---|
| 라이선스 | ||
| 라이선스 메커니즘 | .LIC + .LIC.KEY 파일 쌍 |
문자열 키 |
| 라이선스 배포 | 모든 생산 장비에 있는 파일 | 환경 변수 또는 설정 |
| 가격 투명성 | 판매 상담 필요 | 웹사이트에 게시됨 |
| 영구 라이선스 | 아니요 (연간 유지 보수 필요 없음) | 예 |
| 설정 및 설치 | ||
| NuGet 패키지가 필요합니다. | 최소 3~4페이지 (PDF 파일은 더 많이 필요) | 1 |
| 런타임 파일 필요 | 예 — OcrRuntime/ 디렉토리 |
아니요 |
| 엔진 초기화 | 런타임 경로 가이드에 의한 수동 Startup() |
None |
| 엔진 정지 | Shutdown() 전에 수동 Dispose() |
None |
| tessdata management | 테서랙트 엔진 옵션에 필요합니다. | 아니요 |
| OCR 기능 | ||
| 엔진 옵션 | LEAD, Tesseract, OmniPage (별도 라이선스 필요) | IronTesseract(최적화된 Tesseract 5 LSTM) |
| 언어 | 60~120 (엔진 종류에 따라 다름) | NuGet 통해 125개 이상 |
| 언어 배포 | tessdata files or engine-bundled | NuGet 언어 패키지 |
| 신뢰도 점수 | page.RecognizeStatus |
result.Confidence (백분율) |
| 구조화된 출력 | 페이지, 영역 수준 | 페이지, 단락, 줄, 단어, 문자 |
| 바코드 판독 | LEADTOOLS 바코드 모듈 별도 구매 | 내장 (ReadBarCodes = true) |
| PDF 처리 | ||
| PDF 입력 | 페이지별 래스터화 루프 | 네이티브 LoadPdf() |
| 비밀번호 PDF | Leadtools.Pdf 모듈 필요 (추가 라이선스) |
내장 Password 매개변수 |
| 검색 가능한 PDF 출력 | DocumentWriter 구성 + document.Save() |
result.SaveAsSearchablePdf() |
| 페이지 범위 선택 | 수동 루프 경계 | LoadPdfPages(path, start, end) |
| 전처리 | ||
| 디스큐 | DeskewCommand (수동으로 이미지별) |
input.Deskew() |
| 노이즈 제거 | DespeckleCommand (수동으로 이미지별) |
input.DeNoise() |
| 이진화 | AutoBinarizeCommand (수동으로 이미지별) |
input.Binarize() |
| 명암 대비 강화 | ContrastBrightnessCommand (수동) |
input.Contrast() |
| 해상도 향상 | 수동 회색조 + 리샘플링 명령 | input.EnhanceResolution(300) |
| 배포 | ||
| Docker | LIC/KEY 파일은 마운트되거나 베이크되어야 합니다. | 표준 dotnet publish |
| Linux | 네이티브 종속성으로 지원됩니다. | 지원됨, 종속성 포함 |
| 에어갭 | 예 | 예 |
| CI/CD 복잡성 | 라이선스 파일 및 런타임 경로는 모든 환경에서 사용됩니다. | 시크릿 관리자의 라이선스 키 |
라이선스 아키텍처: 파일 배포 방식 vs. 문자열 키 방식
LEADTOOLS와IronOCR의 라이선스 아키텍처 차이는 단순히 개발자 편의성의 문제만이 아니라 배포 파이프라인, 컨테이너 전략 및 운영 오버헤드에 직접적인 영향을 미칩니다.
LEADTOOLS 접근법
LEADTOOLS는 애플리케이션 시작 시 존재하고 읽을 수 있는 두 개의 물리적 파일을 필요로 합니다. .cs 소스 파일의 코드는 패턴을 확인합니다:
// From leadtools-migration-examples.cs
public void InitializeLicense()
{
// Option 1: Absolute paths — deployment dependent
string licPath = @"C:\LEADTOOLS\License\LEADTOOLS.LIC";
string keyPath = @"C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY";
// Option 2: Relative paths — working directory dependent
licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC");
keyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC.KEY");
// Read key file content (not the path — the content)
string key = File.ReadAllText(keyPath);
// Set license — silent failure on some error types
RasterSupport.SetLicense(licPath, key);
// Verify license is valid for the module you purchased
if (!RasterSupport.IsLocked(RasterSupportType.Document))
{
throw new InvalidOperationException("Document module not licensed");
}
}
// From leadtools-migration-examples.cs
public void InitializeLicense()
{
// Option 1: Absolute paths — deployment dependent
string licPath = @"C:\LEADTOOLS\License\LEADTOOLS.LIC";
string keyPath = @"C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY";
// Option 2: Relative paths — working directory dependent
licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC");
keyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC.KEY");
// Read key file content (not the path — the content)
string key = File.ReadAllText(keyPath);
// Set license — silent failure on some error types
RasterSupport.SetLicense(licPath, key);
// Verify license is valid for the module you purchased
if (!RasterSupport.IsLocked(RasterSupportType.Document))
{
throw new InvalidOperationException("Document module not licensed");
}
}
Imports System
Imports System.IO
Public Sub InitializeLicense()
' Option 1: Absolute paths — deployment dependent
Dim licPath As String = "C:\LEADTOOLS\License\LEADTOOLS.LIC"
Dim keyPath As String = "C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"
' Option 2: Relative paths — working directory dependent
licPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC")
keyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LEADTOOLS.LIC.KEY")
' Read key file content (not the path — the content)
Dim key As String = File.ReadAllText(keyPath)
' Set license — silent failure on some error types
RasterSupport.SetLicense(licPath, key)
' Verify license is valid for the module you purchased
If Not RasterSupport.IsLocked(RasterSupportType.Document) Then
Throw New InvalidOperationException("Document module not licensed")
End If
End Sub
고장 유형은 다양하며 각각 별도의 해결책이 필요합니다. 경로 해결은 IIS와 콘솔 호스트 간에 다르게 동작합니다. bin/Debug의 상대 경로는 bin/Release 또는 Docker컨테이너의 경로와 일치하지 않으므로 명시적으로 매핑되지 않는 한 그렇습니다. LIC 파일과 KEY 파일은 반드시 동일한 다운로드 경로에서 가져온 것이어야 합니다. 서로 다른 다운로드 경로에서 가져온 파일을 사용하면 "키가 라이선스 파일과 일치하지 않습니다"라는 오류가 발생합니다. 라이선스가 문서 번들만 포함하는 경우, 코드가 Recognition 번들의 기능을 사용하면 IsLocked()가 false를 반환하여 애플리케이션이 시작 시 차이를 처리해야 합니다.
제작 과정에서 흔히 발생하는 오류:
"License file not found at specified path"— 경로가 잘못된 디렉토리로 해결됨"License key does not match license file"— 다른 다운로드에서 파일이 다운로드되었거나 전송 중 손상됨"Document module not licensed"— 잘못된 번들을 구입하셨습니다"License has expired"— 평가 라이센스 만료 또는 유지보수 중단
IronOCR접근법
IronOCR 라이선스는 단일 문자열 할당 방식입니다. 파일 없음, 경로 확인 불가, 2단계 검증 불가:
// From application startup — store key using any secrets management pattern
IronOcr.License.LicenseKey = "IRONSUITE.YOUR-LICENSE-KEY";
// Production: pull from environment variable
IronOcr.License.LicenseKey =
Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// Or from ASP.NET configuration
IronOcr.License.LicenseKey = Configuration["IronOCR:LicenseKey"];
// From application startup — store key using any secrets management pattern
IronOcr.License.LicenseKey = "IRONSUITE.YOUR-LICENSE-KEY";
// Production: pull from environment variable
IronOcr.License.LicenseKey =
Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
// Or from ASP.NET configuration
IronOcr.License.LicenseKey = Configuration["IronOCR:LicenseKey"];
' From application startup — store key using any secrets management pattern
IronOcr.License.LicenseKey = "IRONSUITE.YOUR-LICENSE-KEY"
' Production: pull from environment variable
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
' Or from ASP.NET configuration
IronOcr.License.LicenseKey = Configuration("IronOCR:LicenseKey")
키는 Azure Key Vault, AWS Secrets Manager, Kubernetes Secrets 또는 Docker환경 변수에 저장할 수 있습니다. Docker 이미지에 포함할 파일은 없습니다. CI/CD 파이프라인 단계 중 라이선스 아티팩트를 빌드 에이전트로 복사하는 단계는 없습니다. IronOCR 라이선스 페이지에는 영업 담당자와의 통화 없이도 이용 가능한 라이선스 등급이 직접 명시되어 있습니다.
엔진 설정 및 상세 출력
기본 OCR 작업에 대한 LEADTOOLS와IronOCR의 코드 차이는 각 라이브러리의 API 철학을 잘 보여줍니다.
LEADTOOLS 접근법
최소 작업 가능한 LEADTOOLS 구현 (leadtools-vs-ironocr-examples.cs에서 직접 가져옴)은 텍스트가 반환되기 전에 10개의 별개의 작업이 필요합니다:
using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
public class LeadtoolsOcrService : IDisposable
{
private IOcrEngine _ocrEngine;
private RasterCodecs _codecs;
public LeadtoolsOcrService()
{
// Step 1: License files
RasterSupport.SetLicense(
@"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText(@"C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"));
// Step 2: Codec layer
_codecs = new RasterCodecs();
// Step 3: Engine factory
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
// Step 4: Engine startup (500–2000ms blocking call)
_ocrEngine.Startup(_codecs, null, null,
@"C:\LEADTOOLS\OCR\OcrRuntime");
}
public string ExtractText(string imagePath)
{
using var image = _codecs.Load(imagePath); // Step 5: Load via codecs
using var document = _ocrEngine.DocumentManager // Step 6: Create document
.CreateDocument();
var page = document.Pages.AddPage(image, null); // Step 7: Add page
page.Recognize(null); // Step 8: Explicit recognize
return page.GetText(-1); // Step 9: Extract text
}
public void Dispose()
{
_ocrEngine?.Shutdown(); // Step 10: Shutdown before dispose
_ocrEngine?.Dispose();
_codecs?.Dispose();
}
}
using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
public class LeadtoolsOcrService : IDisposable
{
private IOcrEngine _ocrEngine;
private RasterCodecs _codecs;
public LeadtoolsOcrService()
{
// Step 1: License files
RasterSupport.SetLicense(
@"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText(@"C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"));
// Step 2: Codec layer
_codecs = new RasterCodecs();
// Step 3: Engine factory
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
// Step 4: Engine startup (500–2000ms blocking call)
_ocrEngine.Startup(_codecs, null, null,
@"C:\LEADTOOLS\OCR\OcrRuntime");
}
public string ExtractText(string imagePath)
{
using var image = _codecs.Load(imagePath); // Step 5: Load via codecs
using var document = _ocrEngine.DocumentManager // Step 6: Create document
.CreateDocument();
var page = document.Pages.AddPage(image, null); // Step 7: Add page
page.Recognize(null); // Step 8: Explicit recognize
return page.GetText(-1); // Step 9: Extract text
}
public void Dispose()
{
_ocrEngine?.Shutdown(); // Step 10: Shutdown before dispose
_ocrEngine?.Dispose();
_codecs?.Dispose();
}
}
Imports Leadtools
Imports Leadtools.Ocr
Imports Leadtools.Codecs
Imports System.IO
Public Class LeadtoolsOcrService
Implements IDisposable
Private _ocrEngine As IOcrEngine
Private _codecs As RasterCodecs
Public Sub New()
' Step 1: License files
RasterSupport.SetLicense(
"C:\LEADTOOLS\License\LEADTOOLS.LIC",
File.ReadAllText("C:\LEADTOOLS\License\LEADTOOLS.LIC.KEY"))
' Step 2: Codec layer
_codecs = New RasterCodecs()
' Step 3: Engine factory
_ocrEngine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD)
' Step 4: Engine startup (500–2000ms blocking call)
_ocrEngine.Startup(_codecs, Nothing, Nothing,
"C:\LEADTOOLS\OCR\OcrRuntime")
End Sub
Public Function ExtractText(imagePath As String) As String
Using image = _codecs.Load(imagePath) ' Step 5: Load via codecs
Using document = _ocrEngine.DocumentManager ' Step 6: Create document
.CreateDocument()
Dim page = document.Pages.AddPage(image, Nothing) ' Step 7: Add page
page.Recognize(Nothing) ' Step 8: Explicit recognize
Return page.GetText(-1) ' Step 9: Extract text
End Using
End Using
End Function
Public Sub Dispose() Implements IDisposable.Dispose
_ocrEngine?.Shutdown() ' Step 10: Shutdown before dispose
_ocrEngine?.Dispose()
_codecs?.Dispose()
End Sub
End Class
DocumentManager.CreateDocument() / Pages.AddPage() / page.Recognize() / page.GetText() 체인은 축소 불가능합니다. 각 단계는 별도의 API 호출입니다. page.Recognize(null)을 page.GetText(-1) 전에 생략하면 빈 텍스트가 반환됩니다 — 페이지를 추가한다고 자동으로 인식이 실행되지 않습니다.
IronOCR접근법
동일한 비교 파일에서 가져온IronOCR해당 구현은 핵심 작업에 대해 한 줄이며 수명 주기 관리는 전혀 없습니다.
using IronOcr;
public class OcrService
{
public string ExtractText(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}
using IronOcr;
public class OcrService
{
public string ExtractText(string imagePath)
{
return new IronTesseract().Read(imagePath).Text;
}
}
Imports IronOcr
Public Class OcrService
Public Function ExtractText(imagePath As String) As String
Return New IronTesseract().Read(imagePath).Text
End Function
End Class
생산 사용 시 IronTesseract 인스턴스가 호출 간에 재사용되는 경우:
public class OcrService
{
private readonly IronTesseract _ocr = new IronTesseract();
public string ExtractText(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}
public class OcrService
{
private readonly IronTesseract _ocr = new IronTesseract();
public string ExtractText(string imagePath)
{
return _ocr.Read(imagePath).Text;
}
}
Imports IronTesseract
Public Class OcrService
Private ReadOnly _ocr As New IronTesseract()
Public Function ExtractText(imagePath As String) As String
Return _ocr.Read(imagePath).Text
End Function
End Class
Startup() 없음, Shutdown() 없음, 코덱 레이어 없음, 문서 컨테이너 없음, 명시적 인식 호출 없음. IronTesseract API 참조에는 전체 표면적이 나와 있습니다. 설치 가이드에서는 운영 환경에 맞는 구성 옵션을 다룹니다.
PDF 처리
PDF OCR은 LEADTOOLS의 페이지별 래스터화 모델이 가장 잘 드러나는 분야입니다. LEADTOOLS는 네이티브 PDF 인식 경로를 제공하지 않습니다 — 각 PDF 페이지는 RasterCodecs를 통해 래스터 이미지로 로드된 다음, IOcrDocument에 추가된 다음, 개별적으로 인식되고, 개별적으로 텍스트가 추출되어야 합니다.
LEADTOOLS 접근법
leadtools-pdf-processing.cs부터, 기본 PDF 워크플로우:
public string ExtractTextFromPdf(string pdfPath)
{
var text = new StringBuilder();
// Get page count first — separate call
var pdfInfo = _codecs.GetInformation(pdfPath, true);
int totalPages = pdfInfo.TotalPages;
using var document = _ocrEngine.DocumentManager.CreateDocument();
for (int pageNum = 1; pageNum <= totalPages; pageNum++)
{
// Load each page as a raster image — must dispose each
using var pageImage = _codecs.Load(
pdfPath,
0, // bitsPerPixel
CodecsLoadByteOrder.BgrOrGray,
pageNum, // firstPage
pageNum); // lastPage
var page = document.Pages.AddPage(pageImage, null);
page.Recognize(null);
text.AppendLine(page.GetText(-1));
}
return text.ToString();
}
public string ExtractTextFromPdf(string pdfPath)
{
var text = new StringBuilder();
// Get page count first — separate call
var pdfInfo = _codecs.GetInformation(pdfPath, true);
int totalPages = pdfInfo.TotalPages;
using var document = _ocrEngine.DocumentManager.CreateDocument();
for (int pageNum = 1; pageNum <= totalPages; pageNum++)
{
// Load each page as a raster image — must dispose each
using var pageImage = _codecs.Load(
pdfPath,
0, // bitsPerPixel
CodecsLoadByteOrder.BgrOrGray,
pageNum, // firstPage
pageNum); // lastPage
var page = document.Pages.AddPage(pageImage, null);
page.Recognize(null);
text.AppendLine(page.GetText(-1));
}
return text.ToString();
}
Imports System.Text
Public Function ExtractTextFromPdf(ByVal pdfPath As String) As String
Dim text As New StringBuilder()
' Get page count first — separate call
Dim pdfInfo = _codecs.GetInformation(pdfPath, True)
Dim totalPages As Integer = pdfInfo.TotalPages
Using document = _ocrEngine.DocumentManager.CreateDocument()
For pageNum As Integer = 1 To totalPages
' Load each page as a raster image — must dispose each
Using pageImage = _codecs.Load(pdfPath, 0, CodecsLoadByteOrder.BgrOrGray, pageNum, pageNum)
Dim page = document.Pages.AddPage(pageImage, Nothing)
page.Recognize(Nothing)
text.AppendLine(page.GetText(-1))
End Using
Next
End Using
Return text.ToString()
End Function
암호로 보호된 PDF 파일을 처리하는 것은 또 다른 종속성을 추가합니다. leadtools-pdf-processing.cs에서:
// Requires Leadtools.Pdf module — additional license
using Leadtools.Pdf;
public string ExtractFromEncryptedPdf(string pdfPath, string password)
{
var pdfFile = new PDFFile(pdfPath);
pdfFile.Password = password;
var loadOptions = new CodecsLoadOptions();
_codecs.Options.Pdf.Load.Password = password;
// ... same page iteration loop follows
}
// Requires Leadtools.Pdf module — additional license
using Leadtools.Pdf;
public string ExtractFromEncryptedPdf(string pdfPath, string password)
{
var pdfFile = new PDFFile(pdfPath);
pdfFile.Password = password;
var loadOptions = new CodecsLoadOptions();
_codecs.Options.Pdf.Load.Password = password;
// ... same page iteration loop follows
}
Imports Leadtools.Pdf
Public Function ExtractFromEncryptedPdf(ByVal pdfPath As String, ByVal password As String) As String
Dim pdfFile As New PDFFile(pdfPath)
pdfFile.Password = password
Dim loadOptions As New CodecsLoadOptions()
_codecs.Options.Pdf.Load.Password = password
' ... same page iteration loop follows
End Function
Leadtools.Pdf 네임스페이스는 별도의 모듈입니다. 개발팀이 OCR 모듈은 구매했지만 PDF 모듈은 구매하지 않은 경우, 별도의 라이선스 구매 없이는 암호화된 PDF 지원 기능을 사용할 수 없습니다.
검색 가능한 PDF 출력을 만들려면 저장하기 전에 DocumentWriter을 구성해야 합니다:
// From leadtools-pdf-processing.cs
var pdfOptions = new PdfDocumentOptions
{
DocumentType = PdfDocumentType.Pdf,
ImageOverText = true,
Linearized = false,
Title = Path.GetFileNameWithoutExtension(inputPdfPath)
};
_engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions);
document.Save(outputPdfPath, DocumentFormat.Pdf, null);
// From leadtools-pdf-processing.cs
var pdfOptions = new PdfDocumentOptions
{
DocumentType = PdfDocumentType.Pdf,
ImageOverText = true,
Linearized = false,
Title = Path.GetFileNameWithoutExtension(inputPdfPath)
};
_engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions);
document.Save(outputPdfPath, DocumentFormat.Pdf, null);
Imports System.IO
Dim pdfOptions As New PdfDocumentOptions With {
.DocumentType = PdfDocumentType.Pdf,
.ImageOverText = True,
.Linearized = False,
.Title = Path.GetFileNameWithoutExtension(inputPdfPath)
}
_engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions)
document.Save(outputPdfPath, DocumentFormat.Pdf, Nothing)
IronOCR접근법
IronOCR PDF 파일을 기본적으로 지원합니다. 기본 PDF, 암호로 보호된 PDF, 검색 가능한 PDF 출력 등 세 가지 시나리오 모두 각각 2~3줄로 간소화됩니다.
using IronOcr;
// Basic PDF — all pages, automatic handling
public string ExtractTextFromPdf(string pdfPath)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return new IronTesseract().Read(input).Text;
}
// Password-protected PDF — no additional module required
public string ExtractFromEncryptedPdf(string pdfPath, string password)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath, Password: password);
return new IronTesseract().Read(input).Text;
}
// 검색 가능한 PDF 출력 — one method call
public void CreateSearchablePdf(string inputPdfPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadPdf(inputPdfPath);
var result = new IronTesseract().Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}
using IronOcr;
// Basic PDF — all pages, automatic handling
public string ExtractTextFromPdf(string pdfPath)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath);
return new IronTesseract().Read(input).Text;
}
// Password-protected PDF — no additional module required
public string ExtractFromEncryptedPdf(string pdfPath, string password)
{
using var input = new OcrInput();
input.LoadPdf(pdfPath, Password: password);
return new IronTesseract().Read(input).Text;
}
// 검색 가능한 PDF 출력 — one method call
public void CreateSearchablePdf(string inputPdfPath, string outputPdfPath)
{
using var input = new OcrInput();
input.LoadPdf(inputPdfPath);
var result = new IronTesseract().Read(input);
result.SaveAsSearchablePdf(outputPdfPath);
}
Imports IronOcr
' Basic PDF — all pages, automatic handling
Public Function ExtractTextFromPdf(pdfPath As String) As String
Using input As New OcrInput()
input.LoadPdf(pdfPath)
Return New IronTesseract().Read(input).Text
End Using
End Function
' Password-protected PDF — no additional module required
Public Function ExtractFromEncryptedPdf(pdfPath As String, password As String) As String
Using input As New OcrInput()
input.LoadPdf(pdfPath, Password:=password)
Return New IronTesseract().Read(input).Text
End Using
End Function
' 검색 가능한 PDF 출력 — one method call
Public Sub CreateSearchablePdf(inputPdfPath As String, outputPdfPath As String)
Using input As New OcrInput()
input.LoadPdf(inputPdfPath)
Dim result = New IronTesseract().Read(input)
result.SaveAsSearchablePdf(outputPdfPath)
End Using
End Sub
PDF 입력 가이드는 페이지 범위 선택, 스트림 입력 및 페이지별 결과 접근 방법을 다룹니다. 검색 가능한 PDF 예시는 전체 출력 워크플로를 보여줍니다. 인식 전에 품질이 낮은 스캔 PDF를 전처리하려면 이미지 품질 보정 가이드를 참조하십시오.
전처리: 수동 명령 vs. 내장 필터
LEADTOOLS는 별도의 Leadtools.ImageProcessing 네임스페이스를 통해 이미지 처리 명령을 제공합니다. 이 명령은 이미지가 OCR 엔진에 전달되기 전에 각 RasterImage 인스턴스에 개별적으로 적용되어야 합니다.
LEADTOOLS 접근법
leadtools-pdf-processing.cs에서, PDF 페이지 전처리:
using Leadtools.ImageProcessing;
public string ProcessLowQualityPdf(string pdfPath)
{
var text = new StringBuilder();
var pdfInfo = _codecs.GetInformation(pdfPath, true);
using var document = _engine.DocumentManager.CreateDocument();
for (int i = 1; i <= pdfInfo.TotalPages; i++)
{
using var pageImage = _codecs.Load(pdfPath, 0,
CodecsLoadByteOrder.BgrOrGray, i, i);
// Each command is a separate instantiation and Run() call
var deskewCommand = new DeskewCommand();
deskewCommand.Run(pageImage);
var despeckleCommand = new DespeckleCommand();
despeckleCommand.Run(pageImage);
if (pageImage.BitsPerPixel > 8)
{
var grayscaleCommand = new GrayscaleCommand(8);
grayscaleCommand.Run(pageImage);
}
var binarizeCommand = new AutoBinarizeCommand();
binarizeCommand.Run(pageImage);
var page = document.Pages.AddPage(pageImage, null);
page.Recognize(null);
text.AppendLine(page.GetText(-1));
}
return text.ToString();
}
using Leadtools.ImageProcessing;
public string ProcessLowQualityPdf(string pdfPath)
{
var text = new StringBuilder();
var pdfInfo = _codecs.GetInformation(pdfPath, true);
using var document = _engine.DocumentManager.CreateDocument();
for (int i = 1; i <= pdfInfo.TotalPages; i++)
{
using var pageImage = _codecs.Load(pdfPath, 0,
CodecsLoadByteOrder.BgrOrGray, i, i);
// Each command is a separate instantiation and Run() call
var deskewCommand = new DeskewCommand();
deskewCommand.Run(pageImage);
var despeckleCommand = new DespeckleCommand();
despeckleCommand.Run(pageImage);
if (pageImage.BitsPerPixel > 8)
{
var grayscaleCommand = new GrayscaleCommand(8);
grayscaleCommand.Run(pageImage);
}
var binarizeCommand = new AutoBinarizeCommand();
binarizeCommand.Run(pageImage);
var page = document.Pages.AddPage(pageImage, null);
page.Recognize(null);
text.AppendLine(page.GetText(-1));
}
return text.ToString();
}
Imports Leadtools.ImageProcessing
Imports System.Text
Public Function ProcessLowQualityPdf(pdfPath As String) As String
Dim text As New StringBuilder()
Dim pdfInfo = _codecs.GetInformation(pdfPath, True)
Using document = _engine.DocumentManager.CreateDocument()
For i As Integer = 1 To pdfInfo.TotalPages
Using pageImage = _codecs.Load(pdfPath, 0, CodecsLoadByteOrder.BgrOrGray, i, i)
' Each command is a separate instantiation and Run() call
Dim deskewCommand As New DeskewCommand()
deskewCommand.Run(pageImage)
Dim despeckleCommand As New DespeckleCommand()
despeckleCommand.Run(pageImage)
If pageImage.BitsPerPixel > 8 Then
Dim grayscaleCommand As New GrayscaleCommand(8)
grayscaleCommand.Run(pageImage)
End If
Dim binarizeCommand As New AutoBinarizeCommand()
binarizeCommand.Run(pageImage)
Dim page = document.Pages.AddPage(pageImage, Nothing)
page.Recognize(Nothing)
text.AppendLine(page.GetText(-1))
End Using
Next
End Using
Return text.ToString()
End Function
각 전처리 단계는 명령 클래스의 인스턴스를 생성하고 이미지를 대상으로 .Run()를 호출해야 합니다. 개발자는 각 변환의 순서와 적용 가능성을 관리합니다. 이미지가 이미 이진인 경우, AutoBinarizeCommand는 품질을 저하할 수 있습니다. 조건부 BitsPerPixel 체크는 개발자의 책임입니다.
IronOCR접근법
IronOCR 전처리는 OcrInput 객체에 적용되며, 동시에 로드된 모든 페이지에 영향을 미칩니다:
using IronOcr;
public string ProcessLowQualityPdf(string pdfPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(pdfPath);
// Applied to all pages
input.Deskew();
input.DeNoise();
input.Binarize();
input.Contrast();
input.EnhanceResolution(300);
var result = ocr.Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%");
return result.Text;
}
using IronOcr;
public string ProcessLowQualityPdf(string pdfPath)
{
var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadPdf(pdfPath);
// Applied to all pages
input.Deskew();
input.DeNoise();
input.Binarize();
input.Contrast();
input.EnhanceResolution(300);
var result = ocr.Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%");
return result.Text;
}
Imports IronOcr
Public Function ProcessLowQualityPdf(pdfPath As String) As String
Dim ocr = New IronTesseract()
Using input As New OcrInput()
input.LoadPdf(pdfPath)
' Applied to all pages
input.Deskew()
input.DeNoise()
input.Binarize()
input.Contrast()
input.EnhanceResolution(300)
Dim result = ocr.Read(input)
Console.WriteLine($"Confidence: {result.Confidence}%")
Return result.Text
End Using
End Function
다섯 개의 전처리 단계는 모든 페이지에 균일하게 적용되며, 페이지별 루프가 없습니다. IronOCR의 자동 전처리도 기본적으로 모든 Read() 호출에서 실행되어, 명시적 필터 호출 없이 일반적인 스캔 문제를 처리합니다. 이미지 필터 튜토리얼에서는 전체 필터 카탈로그를 다룹니다. 저화질 스캔 예시는 까다로운 문서에 대한 전처리 전후의 차이를 보여줍니다.
API 매핑 참조
| 리드툴 API | IronOCR에 상응하는 |
|---|---|
RasterSupport.SetLicense(licPath, key) |
IronOcr.License.LicenseKey = "key" |
RasterCodecs |
OcrInput |
_codecs.Load(path) |
input.LoadImage(path) 또는 input.LoadPdf(path) |
_codecs.GetInformation(path, true).TotalPages |
자동 - 페이지 수 계산 필요 없음 |
OcrEngineManager.CreateEngine(OcrEngineType.LEAD) |
new IronTesseract() |
engine.Startup(_codecs, null, null, runtimePath) |
필요하지 않음 |
engine.Shutdown() |
필요하지 않음 |
engine.DocumentManager.CreateDocument() |
필요하지 않음 |
document.Pages.AddPage(image, null) |
input.LoadImage(path) |
page.Recognize(null) |
ocr.Read(input) (인식은 읽기의 일부입니다) |
page.GetText(-1) |
result.Text |
page.RecognizeStatus |
result.Confidence |
OcrZone와 Bounds = new LeadRect(x, y, w, h) |
new CropRectangle(x, y, w, h)가 input.LoadImage()에 전달됩니다 |
OcrZoneType.Text |
기본값 — 유형 지정이 필요하지 않습니다. |
page.Zones.Add(zone) |
input.LoadImage(path, cropRect) |
DeskewCommand().Run(image) |
input.Deskew() |
DespeckleCommand().Run(image) |
input.DeNoise() |
AutoBinarizeCommand().Run(image) |
input.Binarize() |
PdfDocumentOptions { ImageOverText = true } |
result.SaveAsSearchablePdf()에서 처리 |
_engine.DocumentWriterInstance.SetOptions(...) |
필요하지 않음 |
document.Save(path, DocumentFormat.Pdf, null) |
result.SaveAsSearchablePdf(path) |
CodecsLoadByteOrder.BgrOrGray |
필수 사항이 아닙니다. 자동으로 처리됩니다. |
_codecs.Options.Pdf.Load.Password = password |
input.LoadPdf(path, Password: password) |
팀이 LEADTOOLS OCR에서IronOCR로 전환을 고려할 때
이미지 처리 툴킷이 필요 없는 OCR 전용 프로젝트
LEADTOOLS는 문서 뷰어, 의료 영상, 양식 인식 및 OCR과 같은 여러 모듈이 통합 플랫폼에서 함께 작동해야 하는 팀에게 재정적, 아키텍처적으로 합리적인 선택입니다. 이미지와 PDF에서 텍스트를 추출해야 하는 경우 계산 방식이 달라집니다. LEADTOOLS 라이선스 비용은 견적을 위해 영업 담당자의 참여가 필요합니다 — 현재 가격표는 LEADTOOLS에 문의하십시오. 라이선스 비용 외에도 연간 유지보수가 필요합니다. Professional 계층에서 $2,999의 IronOCR은 10명의 개발자를 지원하며, 지속적인 사용을 위한 갱신 요구사항이 없습니다. LEADTOOLS 갱신 주기가 끝나고 비용을 재평가하는 팀들은 종종 번들 구성품 중에서 OCR 모듈만 사용했다는 사실을 발견합니다.
컨테이너 기반 및 서버리스 배포
LEADTOOLS의 파일 기반 라이선스는 최신 배포 아키텍처에서 주요 마찰 요인이 됩니다. Docker컨테이너에는 두 개의 물리적 라이선스 파일이 필요합니다. 이 파일은 이미지에 내장되어 레이어 기록에 노출되거나, 모든 오케스트레이션 환경에서 볼륨 조정을 통해 런타임에 마운트될 수 있습니다. Azure Functions와 AWS Lambda는 별도의 해결 방법 없이는 라이선스 파일을 배포할 수 있는 명확한 메커니즘이 없습니다. LEADTOOLS는 또한 런타임 파일을 메모리에 로드하는 블로킹 엔진 시작을 실행하여 콜드 스타트 시간에 500~2000ms를 추가합니다. 이는 지연 시간이 사용자 경험에 직접적인 영향을 미치는 서버리스 함수에 있어 중요한 문제입니다.IronOCR표준 NuGet 참조로 배포되고, 환경 변수에서 라이선스 키를 설정하며, 처음 사용할 때 지연 초기화됩니다. Docker 배포 가이드 와 AWS 배포 가이드는 일반적인 컨테이너화 시나리오에 대한 구체적인 내용을 다룹니다.
잘못된 묶음 문제
LEADTOOLS의 모듈 구조는IronOCR없는 문제, 즉 잘못된 등급을 구매하는 문제를 야기합니다. OCR 기능은 모든 LEADTOOLS 번들에 포함되어 있는 것은 아닙니다. 암호로 보호된 PDF를 지원하려면 별도의 PDF 모듈이 필요하며, 이 모듈은 모든 OCR 중심 라이선스에 포함되어 있지 않습니다. 더 높은 정확도를 제공하는 엔진 옵션을 사용하려면 LEADTOOLS 구매 외에 별도의 공급업체 계약이 필요합니다. 구매에 필요한 기능이 포함되어 있다고 생각했던 팀이 실제 운영 환경에서 실행 중에 그렇지 않다는 사실을 발견하게 되면, 해당 기능을 활성화하기 전에 영업팀과 재협상 과정을 거쳐야 합니다.IronOCR라이선스는 각 등급의 모든 기능을 포함합니다. 별도의 PDF 모듈도 없고, 비밀번호로 보호된 문서를 위한 추가 기능도 없으며, 더 높은 정확도의 엔진을 제공하는 다른 공급업체도 없습니다.
유지보수 및 자원 관리의 복잡성
LEADTOOLS의 엔진 수명주기는 단순한 설정 과정이 아니라 지속적인 유지보수 기반을 제공합니다. 엔진은 올바른 순서대로 시동하고, 사용하고, 끄고, 폐기해야 합니다. 해당 순서에서 벗어나면 오류가 발생합니다. LEADTOOLS 배치 처리 구현에서 발생하는 메모리 누수는 일반적으로 제대로 해제되지 않은 중간 이미지 객체, 처리 후 열린 상태로 남아 있는 문서 컨테이너, 또는 적절한 정리 없이 여러 번 생성된 엔진 인스턴스에서 비롯됩니다. 실제 운영 환경에서 사용되는 배치 프로세서는 이러한 문제를 해결하기 위해 문서 청크 사이에 강제 가비지 컬렉션을 호출하는 경우가 많은데, 이는 기본 객체 모델이 런타임과 충돌하고 있음을 시사합니다. 새벽 2시에 문서 처리 서비스의 메모리 증가 문제를 디버깅하는 팀은 종종 LEADTOOLS의 객체 해제 패턴에서 근본 원인을 발견하곤 합니다.IronOCR표준 .NET 해제 범위를 사용합니다. 입력 컨테이너만 명시적으로 해제해야 합니다. 엔진 자체는 상태를 저장하지 않으며 스레드 안전합니다.
다중 환경 배포 일관성
개발, 스테이징 및 프로덕션 환경 모두에서 LEADTOOLS 라이선스 파일이 일관된 경로에 있어야 하며, 애플리케이션이 시작 시 예상하는 정확한 경로에 런타임 디렉터리가 Plus 합니다. 환경 간의 불일치(예: 스테이징 서버의 런타임 디렉터리 경로가 프로덕션 서버와 드라이브 문자 하나만 다른 경우)는 해당 환경에 특정한 오류를 발생시키며, 이를 해결하려면 코드 또는 구성 변경이 필요합니다. IronOCR의 라이선스 키와 단일 NuGet 패키지는 모든 환경에서 동일하게 작동합니다. 라이선스 키는 유일하게 환경별로 구분되는 값이며, 모든 .NET 팀에서 데이터베이스 연결 문자열 및 API 키에 이미 사용하고 있는 것과 동일한 비밀 관리 패턴을 따릅니다.
일반적인 마이그레이션 고려사항
엔진 수명 주기 제거
LEADTOOLS 코드는 엔진의 생명주기를 관리해야 하므로 IDisposable을 구현하는 서비스 클래스에 엔진을 래핑합니다. 이번 마이그레이션으로 해당 요구 사항이 완전히 사라졌습니다.
// LEADTOOLS: Service class required for lifecycle management
public class LeadtoolsService : IDisposable
{
private IOcrEngine _engine;
private RasterCodecs _codecs;
public LeadtoolsService()
{
RasterSupport.SetLicense(licPath, key);
_codecs = new RasterCodecs();
_engine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
_engine.Startup(_codecs, null, null, runtimePath);
}
public string Process(string path)
{
using var image = _codecs.Load(path);
using var doc = _engine.DocumentManager.CreateDocument();
var page = doc.Pages.AddPage(image, null);
page.Recognize(null);
return page.GetText(-1);
}
public void Dispose()
{
_engine?.Shutdown();
_engine?.Dispose();
_codecs?.Dispose();
}
}
// IronOCR: 아니요 lifecycle to manage
public class OcrService
{
private readonly IronTesseract _ocr = new IronTesseract();
public string Process(string path) => _ocr.Read(path).Text;
}
// LEADTOOLS: Service class required for lifecycle management
public class LeadtoolsService : IDisposable
{
private IOcrEngine _engine;
private RasterCodecs _codecs;
public LeadtoolsService()
{
RasterSupport.SetLicense(licPath, key);
_codecs = new RasterCodecs();
_engine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);
_engine.Startup(_codecs, null, null, runtimePath);
}
public string Process(string path)
{
using var image = _codecs.Load(path);
using var doc = _engine.DocumentManager.CreateDocument();
var page = doc.Pages.AddPage(image, null);
page.Recognize(null);
return page.GetText(-1);
}
public void Dispose()
{
_engine?.Shutdown();
_engine?.Dispose();
_codecs?.Dispose();
}
}
// IronOCR: 아니요 lifecycle to manage
public class OcrService
{
private readonly IronTesseract _ocr = new IronTesseract();
public string Process(string path) => _ocr.Read(path).Text;
}
Imports System
Imports Leadtools
Imports Leadtools.Codecs
Imports Leadtools.Ocr
' LEADTOOLS: Service class required for lifecycle management
Public Class LeadtoolsService
Implements IDisposable
Private _engine As IOcrEngine
Private _codecs As RasterCodecs
Public Sub New()
RasterSupport.SetLicense(licPath, key)
_codecs = New RasterCodecs()
_engine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD)
_engine.Startup(_codecs, Nothing, Nothing, runtimePath)
End Sub
Public Function Process(path As String) As String
Using image = _codecs.Load(path)
Using doc = _engine.DocumentManager.CreateDocument()
Dim page = doc.Pages.AddPage(image, Nothing)
page.Recognize(Nothing)
Return page.GetText(-1)
End Using
End Using
End Function
Public Sub Dispose() Implements IDisposable.Dispose
If _engine IsNot Nothing Then
_engine.Shutdown()
_engine.Dispose()
End If
If _codecs IsNot Nothing Then
_codecs.Dispose()
End If
End Sub
End Class
' IronOCR: 아니요 lifecycle to manage
Public Class OcrService
Private ReadOnly _ocr As New IronTesseract()
Public Function Process(path As String) As String
Return _ocr.Read(path).Text
End Function
End Class
IronTesseract 인스턴스는 스레드 안전하며 싱글톤으로 공유할 수 있습니다. 엔진의 이점을 위해 서비스 클래스에서 IDisposable을 구현할 필요가 없습니다.
영역 기반 OCR 마이그레이션
LEADTOOLS 구역 구성은 OcrZone과 LeadRect 경계가 사용되며, 사용자 정의 구역을 추가하기 전에 자동 감지된 구역을 지워야 합니다. IronOCR는 CropRectangle을 LoadImage()에 직접 전달하여 사용합니다:
// LEADTOOLS zone setup
var zone = new OcrZone
{
Bounds = new LeadRect(x, y, width, height),
ZoneType = OcrZoneType.Text,
CharacterFilters = OcrZoneCharacterFilters.None,
RecognitionModule = OcrZoneRecognitionModule.Auto
};
page.Zones.Clear(); // Must clear auto-detected zones first
page.Zones.Add(zone);
//IronOCR equivalent
using var input = new OcrInput();
input.LoadImage(imagePath, new CropRectangle(x, y, width, height));
var text = new IronTesseract().Read(input).Text;
// LEADTOOLS zone setup
var zone = new OcrZone
{
Bounds = new LeadRect(x, y, width, height),
ZoneType = OcrZoneType.Text,
CharacterFilters = OcrZoneCharacterFilters.None,
RecognitionModule = OcrZoneRecognitionModule.Auto
};
page.Zones.Clear(); // Must clear auto-detected zones first
page.Zones.Add(zone);
//IronOCR equivalent
using var input = new OcrInput();
input.LoadImage(imagePath, new CropRectangle(x, y, width, height));
var text = new IronTesseract().Read(input).Text;
Imports Leadtools
Imports IronOcr
' LEADTOOLS zone setup
Dim zone As New OcrZone With {
.Bounds = New LeadRect(x, y, width, height),
.ZoneType = OcrZoneType.Text,
.CharacterFilters = OcrZoneCharacterFilters.None,
.RecognitionModule = OcrZoneRecognitionModule.Auto
}
page.Zones.Clear() ' Must clear auto-detected zones first
page.Zones.Add(zone)
' IronOCR equivalent
Using input As New OcrInput()
input.LoadImage(imagePath, New CropRectangle(x, y, width, height))
Dim text As String = New IronTesseract().Read(input).Text
End Using
영역 기반 OCR 가이드는 단일 영역 및 다중 영역 추출 패턴을 다룹니다. 지역별 작물 재배 예시는 송장 헤더 추출을 실제 사용 사례로 보여줍니다.
NuGet 패키지 정리
LEADTOOLS를 사용하려면 여러 패키지가 필요합니다. 마이그레이션 과정에서 기존 항목들이 모두 제거되고 하나가 추가됩니다.
# Remove LEADTOOLS packages
dotnet remove package Leadtools
dotnet remove package Leadtools.Ocr
dotnet remove package Leadtools.Codecs
dotnet remove package Leadtools.Pdf
# Add IronOCR
dotnet add package IronOcr
# Remove LEADTOOLS packages
dotnet remove package Leadtools
dotnet remove package Leadtools.Ocr
dotnet remove package Leadtools.Codecs
dotnet remove package Leadtools.Pdf
# Add IronOCR
dotnet add package IronOcr
구축에 필요한 공간 감소 효과가 상당합니다. 빌드 출력에서 사라짐: LEADTOOLS.LIC, LEADTOOLS.LIC.KEY, OcrRuntime/ 디렉토리, 여러 LEADTOOLS DLL, 그리고 Tesseract 엔진 옵션의 모든 tessdata/ 폴더. 남은 것은 하나의 패키지 참조뿐입니다. IronOCR NuGet 패키지에는 모든 기본 종속성이 포함되어 있습니다.
신뢰도 점수 접근
LEADTOOLS는 page.RecognizeStatus을 통해 인식 품질을 열거형으로 공개합니다 (OcrPageRecognizeStatus.Done은 완료를 나타내지만 정확성 수준은 아닙니다). IronOCR는 result.Confidence를 통해 직접 백분율을 제공합니다:
var result = new IronTesseract().Read("document.jpg");
Console.WriteLine($"Confidence: {result.Confidence}%");
// Branch on quality threshold
if (result.Confidence < 60)
{
// Apply additional preprocessing and retry
using var input = new OcrInput();
input.LoadImage("document.jpg");
input.Deskew();
input.DeNoise();
input.EnhanceResolution(300);
result = new IronTesseract().Read(input);
}
var result = new IronTesseract().Read("document.jpg");
Console.WriteLine($"Confidence: {result.Confidence}%");
// Branch on quality threshold
if (result.Confidence < 60)
{
// Apply additional preprocessing and retry
using var input = new OcrInput();
input.LoadImage("document.jpg");
input.Deskew();
input.DeNoise();
input.EnhanceResolution(300);
result = new IronTesseract().Read(input);
}
Imports System
Imports IronOcr
Dim result = New IronTesseract().Read("document.jpg")
Console.WriteLine($"Confidence: {result.Confidence}%")
' Branch on quality threshold
If result.Confidence < 60 Then
' Apply additional preprocessing and retry
Using input As New OcrInput()
input.LoadImage("document.jpg")
input.Deskew()
input.DeNoise()
input.EnhanceResolution(300)
result = New IronTesseract().Read(input)
End Using
End If
신뢰도 점수 가이드는 임계값 선택 및 품질 기반 재시도 패턴을 다룹니다.
IronOCR의 추가 기능
위에서 언급한 비교 사항 외에도IronOCR LEADTOOLS OCR에는 없거나 추가 모듈 구매가 필요한 기능이 포함되어 있습니다.
- OCR 동안 바코드 읽기:
ocr.Configuration.ReadBarCodes = true를 설정하고 바코드는 단일Read()패스로 텍스트와 함께 추출됩니다. LEADTOOLS 바코드 모듈 라이선스는 별도로 필요하지 않습니다. 바코드 판독 가이드 및 바코드 OCR 예시를 참조하세요. - 구조화된 데이터 추출:
result.Pages,result.Paragraphs,result.Lines,result.Words, 그리고result.Characters은 문서 전체 구조를 경계 상자와 요소별 신뢰도 점수와 함께 노출합니다. 읽기 결과 가이드는 전체 출력 모델을 다룹니다. - hOCR 내보내기:
result.SaveAsHocrFile()는 내포된 위치 데이터가 포함된 HTML을 생성하여 다운스트림 레이아웃 분석을 가능하게 합니다. hOCR 내보내기 가이드를 참조하세요. - 125개 이상의 언어 지원: 각 언어는 NuGet 패키지로 제공됩니다. tessdata 디렉터리가 없고, 파일 경로 구성도 없습니다. 전체 언어 목록 및 다국어 가이드를 참조하세요.
- 비동기 OCR:
await ocr.ReadAsync(input)은 ASP.NET 및 백그라운드 서비스에서 비차단 문서 처리를 위해 사용됩니다. 비동기 OCR 가이드를 참조하세요. - 특수 문서 인식: 여권, 차량 번호판, MICR 수표 및 필기 인식을 지원합니다. 특수 기능 페이지를 참조하십시오.
- 진행 상황 추적:
ocr.Configuration.ProgressCallback은 지속적으로 실행되는 대량 작업의 페이지별 진행 상황을 보고합니다. 진행 상황 추적 가이드를 참조하세요.
.NET 호환성 및 미래 준비
IronOCR .NET 6, .NET 7, .NET 8, .NET 9 및 .NET Standard 2.0을 대상으로 하며, .NET Standard 2.0은 .NET Framework 4.6.2 이상을 포함합니다. 크로스 플랫폼 지원은 Windows(x86 및 x64), Linuxx64, macOS, Azure App Service, AWS Lambda 및 Docker를 포함하며, 모든 네이티브 종속성은 NuGet 패키지에 포함되어 있습니다. 플랫폼별 구성은 필요하지 않습니다. 이 패키지는 런타임에 올바른 네이티브 바이너리를 선택합니다. LEADTOOLS는 크로스 플랫폼 .NET 배포를 지원하지만, 각 대상 환경에 대해 플랫폼별 런타임 파일과 해당 경로 구성이 필요합니다. Linux컨테이너 또는 macOS 개발 환경과 Windows 프로덕션 서버를 모두 대상으로 하는 팀의 경우, IronOCR의 단일 패키지 배포 방식을 통해 플랫폼별 구성 단계를 없앨 수 있습니다.
결론
LEADTOOLS OCR은 35년간의 개발 역사를 가진 광범위한 이미지 플랫폼 내에 존재하는 성숙한 기술입니다. 문서 뷰어, 의료 이미지 또는 양식 인식 기능을 위해 이미 LEADTOOLS를 표준으로 사용하는 조직의 경우, 기존 투자에 OCR 기능을 추가하는 것은 합리적인 결정입니다. 통합 비용은 이미 지불되었고, 통합 API 인터페이스는 그 자체로 가치가 있기 때문입니다.
이미지와 PDF에서 텍스트를 추출해야 하는 팀의 경우 계산 방식이 다릅니다. LEADTOOLS의 4단계 초기화 시퀀스, 파일 기반 라이선스 배포, 별도의 코덱 계층 및 명확한 엔진 수명 주기는 OCR 정확도나 기능을 제공하는 복잡성이 아닙니다. 이는 팀의 모든 개발자가 이해해야 하고, 모든 배포 환경이 수용해야 하며, 모든 CI/CD 파이프라인이 처리해야 하는 인프라 오버헤드입니다. 잘못된 번들 구매(PDF 모듈이 없는 OCR 모듈 또는 OmniPage 정확도 등급이 없는 LEAD 엔진)는 구매 시점이 아닌 실제 운영 환경에서 드러나는 숨겨진 오류를 발생시킵니다.
IronOCR은 단일 NuGet 패키지, 문자열 기반 라이선스, 그리고 한 줄 인식 경로를 통해 프로덕션 OCR에 중요한 기능(자동 전처리, 네이티브 PDF 지원, 암호로 보호된 문서 처리, 구조화된 출력 추출, 125개 이상의 언어 지원, 스레드 안전 병렬 처리)을 희생하지 않고도 오버헤드를 제거합니다. 가격 차이는 크다 — $2,999 영구 vs. LEADTOOLS의 다년간 라이선스 및 유지보수 비용. 이는 일회성 엔지니어링 도구 구매와 매년 예산 정당화가 필요한 지속적인 구독 간의 차이입니다.
처음에 제기된 문제점, 즉 모든 운영 환경에 라이선스 파일이 존재하고, 번들 구성이 복잡하며, 문자를 읽기 전에 초기화 절차를 거쳐야 한다는 점은 실제 배포 및 운영 비용을 반영합니다. 두 가지 옵션을 모두 검토하는 팀은 최종 결정을 내리기 전에 LEADTOOLS 초기화 시퀀스를 컨테이너 전략, CI/CD 파이프라인 및 시크릿 관리 방식과 비교하여 실행해야 합니다. 그 답은 종종 선택을 명확하게 해준다.
자주 묻는 질문
리드툴즈 OCR이란 무엇인가요?
리드툴즈 OCR은 개발자와 기업이 이미지와 문서에서 텍스트를 추출하는 데 사용하는 OCR 솔루션입니다. 이 솔루션은 .NET 애플리케이션 개발용 IronOCR과 함께 평가된 여러 OCR 옵션 중 하나입니다.
IronOCR은 .NET 개발자용 리드툴즈 OCR과 어떻게 다른가요?
IronOCR은 IronTesseract를 핵심 엔진으로 사용하는 NuGet 네이티브 .NET OCR 라이브러리입니다. LEADTOOLS OCR에 비해 배포가 간편하고(SDK 설치 프로그램 없음), 정액제 요금제를 제공하며, COM 상호 운용이나 클라우드 종속성이 없는 깔끔한 C# API를 제공합니다.
IronOCR이 LEADTOOLS OCR보다 설정이 더 쉬운가요?
IronOCR은 단일 NuGet 패키지를 통해 설치됩니다. SDK 설치 프로그램, 복사할 라이선스 파일, 등록할 COM 구성 요소 또는 관리해야 할 별도의 런타임 바이너리가 없습니다. 전체 OCR 엔진이 패키지에 번들로 제공됩니다.
LEADTOOLS OCR과 IronOCR에는 어떤 정확도 차이가 있나요?
IronOCR은 표준 비즈니스 문서, 송장, 영수증, 스캔 양식에 대해 높은 인식 정확도를 달성합니다. 품질이 많이 저하된 문서나 일반적이지 않은 스크립트의 경우 정확도는 소스 품질에 따라 달라집니다. IronOCR에는 이미지 전처리 필터가 포함되어 있어 저품질 입력에 대한 인식률을 향상시킵니다.
IronOCR은 PDF 텍스트 추출을 지원하나요?
예. IronOCR은 한 번의 호출로 원본 PDF와 스캔한 PDF 이미지 모두에서 텍스트를 추출합니다. 또한 여러 페이지의 TIFF 파일, 이미지, 스트림도 지원합니다. 스캔한 PDF의 경우 OCR은 페이지별 결과 개체를 사용하여 페이지별로 적용됩니다.
LEADTOOLS OCR 라이선싱은 IronOCR과 어떻게 다른가요?
IronOCR은 페이지당 또는 스캔당 요금이 없는 정액제 영구 라이선스를 사용합니다. 대량의 문서를 처리하는 조직은 문서 양에 관계없이 동일한 라이선스 비용을 지불합니다. 자세한 내용과 볼륨 가격은 IronOCR 라이선스 페이지에서 확인할 수 있습니다.
IronOCR 어떤 언어를 지원하나요?
IronOCR은 별도의 NuGet 언어 팩을 통해 127개 언어를 지원합니다. 언어를 추가하려면 '닷넷 추가 패키지 IronOcr.Languages.{Language}' 명령 하나만 있으면 됩니다. 수동으로 파일을 배치하거나 경로를 구성할 필요가 없습니다.
.NET 프로젝트에 IronOCR 설치하는 방법은 무엇인가요?
NuGet을 통해 설치합니다: 패키지 관리자 콘솔에서 '설치-패키지 IronOcr' 또는 CLI에서 '닷넷 추가 패키지 IronOcr'을 실행합니다. 추가 언어 팩도 같은 방법으로 설치됩니다. 기본 SDK 인스톨러가 필요하지 않습니다.
IronOCR은 리드툴즈 OCR과 달리 Docker 및 컨테이너화된 배포에 적합하나요?
예. IronOCR은 NuGet 패키지를 통해 Docker 컨테이너에서 작동합니다. 라이선스 키는 환경 변수를 통해 설정됩니다. OCR 엔진 자체에는 라이선스 파일, SDK 경로 또는 볼륨 마운트가 필요하지 않습니다.
LEADTOOLS OCR과 비교하여 구매하기 전에 IronOCR을 사용해 볼 수 있나요?
예. IronOCR 평가판 모드는 문서를 처리하고 출력물에 워터마크 오버레이가 포함된 OCR 결과를 반환합니다. 라이선스를 구매하기 전에 자신의 문서에서 정확성을 확인할 수 있습니다.
IronOCR은 텍스트 추출과 함께 바코드 판독을 지원하나요?
IronOCR은 텍스트 추출과 OCR에 중점을 둡니다. 바코드 판독을 위해 Iron Software는 동반 라이브러리로 IronBarcode를 제공합니다. 두 가지 모두 개별적으로 또는 Iron Suite 번들의 일부로 사용할 수 있습니다.
LEADTOOLS OCR에서 IronOCR로 쉽게 마이그레이션할 수 있나요?
리드툴즈 OCR에서 IronOCR로의 마이그레이션에는 일반적으로 초기화 시퀀스를 IronTesseract 인스턴스화로 대체하고, COM 수명 주기 관리를 제거하며, API 호출을 업데이트하는 작업이 포함됩니다. 대부분의 마이그레이션은 코드 복잡성을 크게 줄여줍니다.

