IRONSOFTWAREHOME
동영상

NET Maui에서 OCR을 수행하는 방법

칸나오팟 우돈판트
Kannapat Udonpant
Updated: 2026년 6월 28일

이 가이드는 .NET 개발자가 LEADTOOLS OCR에서 IronOCR 로 완전히 마이그레이션하는 과정을 안내합니다. 이 문서에서는 NuGet 패키지 교체부터 전체 코드 마이그레이션까지 모든 단계를 다루며, LEADTOOLS의 초기화 절차, 다중 네임스페이스 구조 및 파일 기반 라이선스 배포 모델의 영향을 가장 많이 받는 패턴에 대한 마이그레이션 전후 예시를 제공합니다.

Leadtools OCR에서 마이그레이션해야 하는 이유는 무엇일까요?

LEADTOOLS OCR은 1990년대로 거슬러 올라가는 역사를 가진 Enterprise 이미지 처리 플랫폼에 포함되어 제공되며, API 또한 이러한 계보를 반영합니다. 최소 작동 구성에는 4개의 NuGet 패키지, 4개의 네임스페이스, 알려진 경로에 배포된 2개의 물리적 라이선스 파일, 엔진 전에 초기화되는 코덱 계층, 3가지 옵션 중에서 선택하는 엔진 유형, 그리고 런타임 바이너리를 메모리에 로드하는 차단 시작 호출이 필요합니다. 이 모든 과정이 문자 하나가 인식되기 전에 실행됩니다.IronOCR로 마이그레이션하는 팀은 해당 계층 전체를 제거합니다. 패키지 하나, 네임스페이스 하나, 라이선스 키를 설정하는 단 한 줄만 있으면 됩니다.

컨테이너에서 파일 기반의 라이선스 배포 중단. LEADTOOLS는 두 개의 물리적 파일, LEADTOOLS.LICLEADTOOLS.LIC.KEY,을 필요로 하며, 애플리케이션을 실행하는 모든 기계에서 특정 경로에서 접근 가능해야 합니다. Docker 컨테이너에서 이러한 파일은 이미지에 포함시키거나(레이어 기록에 노출) 런타임에 마운트해야 합니다(모든 오케스트레이션 환경에서 볼륨 조정 필요). Azure Functions와 AWS Lambda는 별도의 해결 방법 없이는 라이선스 파일을 배포할 수 있는 메커니즘을 제공하지 않습니다. CI/CD 파이프라인은 시작 호출에서 사용하는 정확한 경로에 파일이 있어야 합니다. 그렇지 않으면 애플리케이션이 단 하나의 문서도 처리하기 전에 오류가 발생합니다.IronOCR두 파일 모두를 환경 변수, Kubernetes 시크릿 또는 Azure Key Vault 참조에 맞는 문자열로 바꿉니다.

하나의 작업을 위한 네 개의 패키지. 작동하는 리드툴즈 OCR 프로젝트는 최소한 Leadtools, Leadtools.Ocr, Leadtools.CodecsLeadtools.Forms.DocumentWriters가 필요합니다. 비밀번호로 보호된 PDF 지원은 구매한 번들에 포함되지 않을 수 있는 추가 Leadtools.Pdf 모듈을 필요로 합니다. 각 패키지는 반드시 존재해야 하고, 호환되어야 하며, 동일한 버전으로 제공되어야 합니다.IronOCR하나의 NuGet 패키지로 제공됩니다. PDF 입력, 검색 가능한 PDF 출력, 전처리, 바코드 판독 등 모든 기능이 포함되어 있습니다.

엔진 수명 주기는 유지보수 표면을 생성합니다. LEADTOOLS는 OCR 엔진을 IDisposable 서비스 클래스에 감싸고 있습니다. 이는 관용적 .NET 이유가 아닌, 애플리케이션이 오류를 생성하지 않도록 Dispose() 이전에 Shutdown() 호출을 해야 하기 때문입니다. LEADTOOLS 배치 프로세서의 생산 구현은 일반적으로 누적되는 RasterImage 인스턴스를 보상하기 위해 문서 청크 사이에 GC.Collect() / GC.WaitForPendingFinalizers() 호출을 포함합니다. IronOCR는 표준 using 블록을 사용합니다. OcrInput는 처분이 필요한 유일한 객체입니다.

번들 관련 혼란이 실제 운영 환경에서 발생합니다. LEADTOOLS는 가격 정보를 공개적으로 게시하지 않습니다. OCR 기능을 포함하는 문서 이미지 처리 SDK는 개발자 1인당 연간 약 3,000달러에서 8,000달러의 비용이 드는 것으로 추정됩니다. PDF 모듈 없이 OCR 모듈을 구매한 팀은 생산에서 비밀번호로 보호된 문서에 대해 RasterSupport.IsLocked()가 true를 반환할 때 간격을 발견합니다. OmniPage 엔진 정확도 등급을 사용하려면 LEADTOOLS 구매 외에 별도의 Kofax 라이선스 계약이 필요합니다.IronOCR각 등급별로 모든 기능에 대한 라이선스를 제공합니다. 별도의 공급업체와의 관계도 없고, 암호화된 문서를 위한 별도의 모듈도 없습니다.

초기화 비용이 콜드 스타트에 영향을 미칩니다. engine.Startup()는 런타임 파일을 메모리에 로드하는 블로킹 호출입니다. 콜드 스타트 ​​지연 시간이 중요한 서버리스 환경(Azure Functions, AWS Lambda 등)에서 인식 작업이 시작되기 전에 500~2000ms 동안 지속되는 초기화 과정은 구조적인 문제입니다.IronOCR지연 초기화를 사용합니다. IronTesseract 인스턴스는 최초 사용 시 초기화되며, 동일한 프로세스 내 후속 호출에는 초기화 비용이 전혀 발생하지 않습니다.

근본적인 문제

LEADTOOLS를 사용하려면 네 개의 네임스페이스, 네 개의 NuGet 패키지, 그리고 문자를 인식하기 전에 반드시 거쳐야 하는 시작 절차가 필요합니다.

// LEADTOOLS: Four namespaces, four packages, six steps before recognition
using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
using Leadtools.Forms.DocumentWriters;

RasterSupport.SetLicense(licPath, File.ReadAllText(keyPath)); // Step 1: two files on disk
var codecs = new RasterCodecs();                              // Step 2: codec layer
var engine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD); // Step 3: engine factory
engine.Startup(codecs, null, null, runtimePath);              // Step 4: blocking startup
// ... still need to create document, add page, call Recognize(), extract text
C#
// IronOCR: One namespace, one package, one line
using IronOcr;

IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var text = new IronTesseract().Read("document.jpg").Text;
C#

##IronOCR과 LEADTOOLS OCR: 기능 비교

다음 표는 두 라이브러리 간의 기능을 직접적으로 매핑합니다.

기능리드툴즈 OCRIronOCR
NuGet 패키지가 필요합니다.최소 4개 (PDF의 경우 더 많음)1 (IronOcr)
라이선스 메커니즘.LIC + .LIC.KEY 파일 쌍문자열 키
라이선스 배포모든 생산 장비에 있는 파일환경 변수 또는 설정
가격 모델개발자당 연간 3,000달러~15,000달러 이상(추정치)$999–$2,399 일회성 영구
엔진 초기화런타임 경로와 함께 수동 Startup()자동, 게으름
엔진 정지Shutdown() 전에 수동 Dispose()필요하지 않음
코덱 레이어모든 이미지 로딩에 필요한 RasterCodecs필요하지 않음
PDF 입력페이지별 래스터화 루프네이티브 LoadPdf()
비밀번호로 보호된 PDF별도의 Leadtools.Pdf 모듈 필요내장된 Password 매개변수
검색 가능한 PDF 출력DocumentWriter + PdfDocumentOptions + document.Save()result.SaveAsSearchablePdf()
전처리별도의 명령 클래스 (DeskewCommand, DespeckleCommand 등)OcrInput에 대한 내장 필터 메서드
여러 페이지로 구성된 TIFF 파일CodecsLoadByteOrder로 수동 프레임 반복input.LoadImageFrames()
구조화된 출력페이지 및 영역 수준페이지, 단락, 줄, 단어, 문자의 좌표
자신감 점수OcrPageRecognizeStatus enum백분율로서 result.Confidence
바코드 판독LEADTOOLS 바코드 모듈 별도 구매내장 (ReadBarCodes = true)
스레드 안전성세심한 관리가 필요합니다.(스레드당 하나 IronTesseract)으로 전체
지원되는 언어60~120 (엔진 종류에 따라 다름)NuGet 언어 패키지를 통해 125개 이상
언어 배포tessdata files or engine-bundled files언어별 NuGet 패키지
크로스 플랫폼플랫폼별 런타임 구성이 지원됩니다.윈도우, 리눅스, macOS, Docker, Azure, AWS
Docker 배포.KEY 파일은 마운트되거나 포함되어야 함표준 dotnet publish
상업적 지원

빠른 시작: LEADTOOLS OCR에서IronOCR로 마이그레이션

1단계: NuGet 패키지 교체

LEADTOOLS 패키지를 모두 제거하세요.

dotnet remove package Leadtools
dotnet remove package Leadtools.Ocr
dotnet remove package Leadtools.Codecs
dotnet remove package Leadtools.Forms.DocumentWriters
dotnet remove package Leadtools.Pdf
SHELL

NuGet 에서IronOCR설치하세요.

dotnet add package IronOcr

단계 2: 네임스페이스 업데이트

모든 LEADTOOLS using 지시어를 단일IronOCR임포트로 교체하십시오:

// Before (LEADTOOLS)
using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
using Leadtools.Forms.DocumentWriters;

// After (IronOCR)
using IronOcr;
C#

단계 3: 라이선스 초기화

모든 RasterSupport.SetLicense() 호출 및 .LIC / .LIC.KEY 파일 참조를 제거하십시오. 애플리케이션 시작 시IronOCR라이선스 키를 추가하세요.

// Single line replaces the entire file-based license setup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

// Production pattern: pull from environment variable or secrets manager
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");
C#

라이선스 키는 appsettings.json, Azure Key Vault, AWS Secrets Manager 또는 Kubernetes 시크릿과 같은 표준 .NET 비밀 관리 패턴에 저장할 수 있습니다. 애플리케이션 실행 파일과 함께 배포할 파일은 없습니다.

코드 마이그레이션 예제

엔진 시동 및 정지 수명주기 제거

LEADTOOLS는 엔진 수명 주기를 명시적으로 관리해야 하므로 엄격한 서비스 클래스 패턴을 요구합니다. 생성자는 엔진을 시작하고, Dispose()는 올바른 순서로 종료하며, Shutdown()Dispose() 전에 건너뛰는 모든 코드 경로는 런타임 오류를 생성합니다.

LEADTOOLS OCR 접근 방식:

using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;

// Service class required purely to manage engine lifecycle
public class LeadtoolsOcrService : IDisposable
{
    private IOcrEngine _engine;
    private RasterCodecs _codecs;
    private readonly string _runtimePath;

    public LeadtoolsOcrService(string licPath, string keyPath, string runtimePath)
    {
        _runtimePath = runtimePath;

        // License setup — two files, both must be present
        RasterSupport.SetLicense(licPath, File.ReadAllText(keyPath));

        // Codec layer — required before engine creation
        _codecs = new RasterCodecs();

        // Engine factory — engine type determines capability and cost
        _engine = OcrEngineManager.CreateEngine(OcrEngineType.LEAD);

        // Blocking startup — loads runtime into memory (500–2000ms)
        _engine.Startup(_codecs, null, null, _runtimePath);
    }

    public bool IsReady => _engine?.IsStarted ?? false;

    public string Process(string imagePath)
    {
        if (!IsReady)
            throw new InvalidOperationException("Engine not started");

        using var image = _codecs.Load(imagePath);
        using var doc = _engine.DocumentManager.CreateDocument();
        var page = doc.Pages.AddPage(image, null);
        page.Recognize(null);
        return page.GetText(-1);
    }

    public void Dispose()
    {
        // Order is mandatory: Shutdown before Dispose
        if (_engine?.IsStarted == true)
            _engine.Shutdown();

        _engine?.Dispose();
        _codecs?.Dispose();
    }
}
C#

IronOCR 접근 방식:

using IronOcr;

// No lifecycle management needed — the service class becomes trivial
public class OcrService
{
    private readonly IronTesseract _ocr = new IronTesseract();

    // Always ready — no IsStarted check needed
    public string Process(string imagePath) => _ocr.Read(imagePath).Text;

    // No Dispose() needed for the engine
    // No Startup(), no Shutdown(), no codec layer
}
C#

IronTesseract 인스턴스는 최초 사용 시 초기화됩니다. 생성자 인수가 없으며, 런타임 경로가 없으며, Startup() 호출이 없습니다. 위의 서비스 클래스는 전혀 IDisposable를 구현할 필요가 없습니다. 엔진은 상태가 없으며, 각 Read() 호출 내에서 사용되는 OcrInput 객체는 표준 using 패턴을 통해 자체 정리를 처리합니다. IronTesseract 설정 가이드에서는 언어 선택 및 프로덕션 시나리오에 대한 성능 튜닝을 포함한 구성 옵션을 다룹니다.

다중 프레임 TIFF 일괄 처리

LEADTOOLS는 코덱에서 총 프레임 수를 쿼리 한 다음, 각 _codecs.Load() 호출에서 명시적인 lastPage 매개변수로 반복하여 다중 프레임 TIFF 파일을 처리합니다. 모든 프레임 이미지는 수동으로 삭제해야 하며, 그렇지 않으면 메모리에 누적됩니다.

LEADTOOLS OCR 접근 방식:

using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;

public class LeadtoolsTiffBatchService
{
    private readonly IOcrEngine _engine;
    private readonly RasterCodecs _codecs;

    public List<string> ProcessMultiFrameTiff(string tiffPath)
    {
        var pageTexts = new List<string>();

        // Must query page count before iterating
        var info = _codecs.GetInformation(tiffPath, true);
        int frameCount = info.TotalPages;

        using var document = _engine.DocumentManager.CreateDocument();

        for (int frameNum = 1; frameNum <= frameCount; frameNum++)
        {
            // Load one frame at a time — must specify firstPage/lastPage
            using var frameImage = _codecs.Load(
                tiffPath,
                0,                            // bitsPerPixel
                CodecsLoadByteOrder.BgrOrGray,
                frameNum,                     // firstPage
                frameNum);                    // lastPage

            var page = document.Pages.AddPage(frameImage, null);
            page.Recognize(null);
            pageTexts.Add(page.GetText(-1));

            // GC pressure accumulates if disposal is missed on any frame
        }

        return pageTexts;
    }

    public Dictionary<string, List<string>> ProcessTiffDirectory(string directoryPath)
    {
        var results = new Dictionary<string, List<string>>();

        foreach (var tiffFile in Directory.GetFiles(directoryPath, "*.tiff"))
        {
            results[tiffFile] = ProcessMultiFrameTiff(tiffFile);

            // Manual GC between files to prevent memory growth
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        return results;
    }
}
C#

IronOCR 접근 방식:

using IronOcr;

public class TiffBatchService
{
    private readonly IronTesseract _ocr = new IronTesseract();

    public List<string> ProcessMultiFrameTiff(string tiffPath)
    {
        using var input = new OcrInput();
        input.LoadImageFrames(tiffPath);  // All frames loaded automatically

        var result = _ocr.Read(input);

        // Per-frame text available through result.Pages
        return result.Pages.Select(p => p.Text).ToList();
    }

    public Dictionary<string, List<string>> ProcessTiffDirectory(string directoryPath)
    {
        var results = new Dictionary<string, List<string>>();

        foreach (var tiffFile in Directory.GetFiles(directoryPath, "*.tiff"))
        {
            results[tiffFile] = ProcessMultiFrameTiff(tiffFile);
        }

        return results;
    }

    // Parallel processing across files — thread-safe out of the box
    public Dictionary<string, List<string>> ProcessTiffDirectoryParallel(string directoryPath)
    {
        var concurrentResults = new System.Collections.Concurrent.ConcurrentDictionary<string, List<string>>();
        var tiffFiles = Directory.GetFiles(directoryPath, "*.tiff");

        Parallel.ForEach(tiffFiles, tiffFile =>
        {
            using var input = new OcrInput();
            input.LoadImageFrames(tiffFile);
            var result = new IronTesseract().Read(input);
            concurrentResults[tiffFile] = result.Pages.Select(p => p.Text).ToList();
        });

        return new Dictionary<string, List<string>>(concurrentResults);
    }
}
C#

LoadImageFrames()는 한 번의 호출로 TIFF의 모든 프레임을 읽습니다. 프레임 수 쿼리도 없고, 루프도 없고, 프레임별로 명시적인 폐기 처리도 없습니다. 병렬 버전은 스레드당 하나의 IronTesseract 인스턴스를 생성하며, 이는 올바른 패턴입니다. 전체 스레딩 모델을 위해 멀티스레딩 예제를 참조하세요. TIFF 관련 입력 옵션에 대해서는 TIFF 및 GIF 입력 가이드에서 프레임 선택 및 다중 형식 처리에 대한 내용을 참조하십시오.

문서 작성기 파이프라인 간소화

LEADTOOLS 검색 가능한 PDF 생성에는 엔진에서 DocumentWriter 인스턴스를 구성하고, 출력 유형 및 오버레이 설정과 함께 PdfDocumentOptions 객체를 생성하고, SetOptions()를 통해 옵션을 적용한 다음, 포맷 enum과 함께 document.Save()을 호출해야 합니다. 각 단계는 별개의 객체이며 별도의 API 호출입니다.

LEADTOOLS OCR 접근 방식:

using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;
using Leadtools.Forms.DocumentWriters;

public class LeadtoolsDocumentWriterService
{
    private readonly IOcrEngine _engine;
    private readonly RasterCodecs _codecs;

    public void CreateSearchablePdfFromImages(string[] imagePaths, string outputPath)
    {
        using var document = _engine.DocumentManager.CreateDocument();

        foreach (var imagePath in imagePaths)
        {
            using var image = _codecs.Load(imagePath);
            var page = document.Pages.AddPage(image, null);
            page.Recognize(null);
        }

        // DocumentWriter configuration — four properties to set before save
        var pdfOptions = new PdfDocumentOptions
        {
            DocumentType = PdfDocumentType.Pdf,
            ImageOverText = true,     // Image layer visible, text layer searchable
            Linearized = false,
            Title = "Searchable Output"
        };

        // Apply options to the engine's writer instance
        _engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions);

        // Save with format enum — the format must match the options set above
        document.Save(outputPath, DocumentFormat.Pdf, null);
    }

    public void CreateSearchablePdfFromPdf(string inputPdfPath, string outputPath)
    {
        var pdfInfo = _codecs.GetInformation(inputPdfPath, true);
        using var document = _engine.DocumentManager.CreateDocument();

        for (int i = 1; i <= pdfInfo.TotalPages; i++)
        {
            using var pageImage = _codecs.Load(inputPdfPath, 0,
                CodecsLoadByteOrder.BgrOrGray, i, i);

            var page = document.Pages.AddPage(pageImage, null);
            page.Recognize(null);
        }

        var pdfOptions = new PdfDocumentOptions
        {
            DocumentType = PdfDocumentType.Pdf,
            ImageOverText = true,
            Title = Path.GetFileNameWithoutExtension(inputPdfPath)
        };

        _engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions);
        document.Save(outputPath, DocumentFormat.Pdf, null);
    }
}
C#

IronOCR 접근 방식:

using IronOcr;

public class SearchablePdfService
{
    private readonly IronTesseract _ocr = new IronTesseract();

    public void CreateSearchablePdfFromImages(string[] imagePaths, string outputPath)
    {
        using var input = new OcrInput();
        foreach (var imagePath in imagePaths)
            input.LoadImage(imagePath);

        var result = _ocr.Read(input);
        result.SaveAsSearchablePdf(outputPath);  // DocumentWriter pipeline: gone
    }

    public void CreateSearchablePdfFromPdf(string inputPdfPath, string outputPath)
    {
        using var input = new OcrInput();
        input.LoadPdf(inputPdfPath);

        var result = _ocr.Read(input);
        result.SaveAsSearchablePdf(outputPath);
    }

    // Get bytes directly — useful for streaming responses in ASP.NET
    public byte[] CreateSearchablePdfBytes(string inputPdfPath)
    {
        using var input = new OcrInput();
        input.LoadPdf(inputPdfPath);
        return _ocr.Read(input).SaveAsSearchablePdfBytes();
    }
}
C#

SaveAsSearchablePdf()는 전체 PdfDocumentOptions + SetOptions() + document.Save() 체인을 대체합니다. 이미지가 텍스트 위에 겹쳐지는 레이어 동작은 자동으로 이루어집니다. 검색 가능한 PDF 출력에 대한 전체 문서는 검색 가능한 PDF 사용 방법 가이드에서 출력 옵션을 다루고 , 검색 가능한 PDF 예제에서 ASP.NET 응답 스트림과의 통합을 보여줍니다.

다중 영역 필드 추출 마이그레이션

LEADTOOLS 영역 기반 OCR은 LeadRect 경계, OcrZoneTypeOcrZoneCharacterFilters 속성과 함께 OcrZone 객체를 사용합니다. 여러 영역을 한 페이지에 추가하고, 한 번의 page.Recognize() 호출에서 인식한 후, 영역 인덱스를 통해 추출합니다. 영역 인덱스는 삽입 순서와 일치하므로 추출 루프는 해당 순서를 유지해야 합니다.

LEADTOOLS OCR 접근 방식:

using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;

public class LeadtoolsFormFieldExtractor
{
    private readonly IOcrEngine _engine;
    private readonly RasterCodecs _codecs;

    // Invoice field extraction using named zones
    public InvoiceFields ExtractInvoiceFields(string invoicePath)
    {
        using var image = _codecs.Load(invoicePath);
        using var document = _engine.DocumentManager.CreateDocument();
        var page = document.Pages.AddPage(image, null);

        // Must clear auto-detected zones before adding custom ones
        page.Zones.Clear();

        // Zone definitions — index order matters for extraction
        var zoneDefinitions = new[]
        {
            new { Name = "InvoiceNumber", X = 450, Y = 80,  W = 200, H = 30 },
            new { Name = "InvoiceDate",   X = 450, Y = 115, W = 200, H = 30 },
            new { Name = "VendorName",    X = 50,  Y = 150, W = 300, H = 40 },
            new { Name = "TotalAmount",   X = 450, Y = 600, W = 200, H = 30 }
        };

        foreach (var def in zoneDefinitions)
        {
            var zone = new OcrZone
            {
                Bounds = new LeadRect(def.X, def.Y, def.W, def.H),
                ZoneType = OcrZoneType.Text,
                CharacterFilters = OcrZoneCharacterFilters.None,
                RecognitionModule = OcrZoneRecognitionModule.Auto
            };
            page.Zones.Add(zone);
        }

        page.Recognize(null);

        // Extract by index — must match insertion order exactly
        return new InvoiceFields
        {
            InvoiceNumber = page.Zones[0].Text?.Trim(),
            InvoiceDate   = page.Zones[1].Text?.Trim(),
            VendorName    = page.Zones[2].Text?.Trim(),
            TotalAmount   = page.Zones[3].Text?.Trim()
        };
    }
}

public class InvoiceFields
{
    public string InvoiceNumber { get; set; }
    public string InvoiceDate   { get; set; }
    public string VendorName    { get; set; }
    public string TotalAmount   { get; set; }
}
C#

IronOCR 접근 방식:

using IronOcr;

public class FormFieldExtractor
{
    private readonly IronTesseract _ocr = new IronTesseract();

    // Each field gets its own CropRectangle-scoped Read() call
    // No zone index management, no zone ordering dependency
    public InvoiceFields ExtractInvoiceFields(string invoicePath)
    {
        return new InvoiceFields
        {
            InvoiceNumber = ReadRegion(invoicePath, 450, 80,  200, 30),
            InvoiceDate   = ReadRegion(invoicePath, 450, 115, 200, 30),
            VendorName    = ReadRegion(invoicePath, 50,  150, 300, 40),
            TotalAmount   = ReadRegion(invoicePath, 450, 600, 200, 30)
        };
    }

    private string ReadRegion(string imagePath, int x, int y, int width, int height)
    {
        using var input = new OcrInput();
        input.LoadImage(imagePath, new CropRectangle(x, y, width, height));
        return _ocr.Read(input).Text.Trim();
    }

    // Batch: extract the same field from many invoices in parallel
    public Dictionary<string, string> ExtractInvoiceNumbersBatch(string[] invoicePaths)
    {
        var results = new System.Collections.Concurrent.ConcurrentDictionary<string, string>();

        Parallel.ForEach(invoicePaths, invoicePath =>
        {
            using var input = new OcrInput();
            input.LoadImage(invoicePath, new CropRectangle(450, 80, 200, 30));
            results[invoicePath] = new IronTesseract().Read(input).Text.Trim();
        });

        return new Dictionary<string, string>(results);
    }
}
C#

CropRectangleLoadImage()에 직접 전달하여 OcrZone 설정을 전부 대체합니다. 추적할 영역 인덱스가 없고, page.Zones.Clear() 호출이 필요 없으며, 인식 상태 확인도 필요하지 않습니다. 영역 기반 OCR 가이드는 단일 영역 및 다중 영역 추출 패턴을 다룹니다. 송장 필드 추출에 대한 전체 튜토리얼은 송장 OCR 튜토리얼을 참조하세요.

단어 좌표를 이용한 구조화된 데이터 추출

LEADTOOLS의 구조화된 출력은 페이지 및 영역 수준에서 작동합니다. 경계 상자 좌표로 단어 수준의 데이터를 얻으려면, 개발자는 인식된 영역 내에서 OcrWord 객체에 접근합니다. API를 사용하려면 인식 후 영역 컬렉션을 처리하고 영역별 단어 목록을 반복해야 합니다.

LEADTOOLS OCR 접근 방식:

using Leadtools;
using Leadtools.Ocr;
using Leadtools.Codecs;

public class LeadtoolsStructuredExtractor
{
    private readonly IOcrEngine _engine;
    private readonly RasterCodecs _codecs;

    public List<WordLocation> ExtractWordsWithLocations(string imagePath)
    {
        var wordLocations = new List<WordLocation>();

        using var image = _codecs.Load(imagePath);
        using var document = _engine.DocumentManager.CreateDocument();
        var page = document.Pages.AddPage(image, null);
        page.Recognize(null);

        // Access words through the zone collection
        foreach (OcrZone zone in page.Zones)
        {
            foreach (OcrWord word in zone.Words)
            {
                wordLocations.Add(new WordLocation
                {
                    Text       = word.Value,
                    X          = word.Bounds.X,
                    Y          = word.Bounds.Y,
                    Width      = word.Bounds.Width,
                    Height     = word.Bounds.Height,
                    Confidence = word.Confidence
                });
            }
        }

        return wordLocations;
    }
}

public class WordLocation
{
    public string Text       { get; set; }
    public int    X          { get; set; }
    public int    Y          { get; set; }
    public int    Width      { get; set; }
    public int    Height     { get; set; }
    public int    Confidence { get; set; }
}
C#

IronOCR 접근 방식:

using IronOcr;

public class StructuredExtractor
{
    private readonly IronTesseract _ocr = new IronTesseract();

    public List<WordLocation> ExtractWordsWithLocations(string imagePath)
    {
        var result = _ocr.Read(imagePath);

        // Five-level hierarchy: Pages > Paragraphs > Lines > Words > Characters
        return result.Pages
            .SelectMany(page => page.Paragraphs)
            .SelectMany(para => para.Lines)
            .SelectMany(line => line.Words)
            .Select(word => new WordLocation
            {
                Text       = word.Text,
                X          = word.X,
                Y          = word.Y,
                Width      = word.Width,
                Height     = word.Height,
                Confidence = (int)word.Confidence
            })
            .ToList();
    }

    // Paragraph-level extraction with position data
    public void PrintDocumentStructure(string imagePath)
    {
        var result = _ocr.Read(imagePath);
        Console.WriteLine($"Document confidence: {result.Confidence}%");

        foreach (var page in result.Pages)
        {
            Console.WriteLine($"Page {page.PageNumber}:");
            foreach (var paragraph in page.Paragraphs)
            {
                Console.WriteLine($"  Paragraph at ({paragraph.X}, {paragraph.Y}):");
                Console.WriteLine($"  {paragraph.Text}");
            }
        }
    }
}
C#

IronOCR의 결과 계층 구조는 Pages에서 시작하여 Paragraphs, Lines, WordsCharacters으로 내려갑니다. 각 수준은 X, Y, Width, Height, TextConfidence를 노출합니다. LEADTOOLS의 영역 기반 액세스 패턴이 사라집니다. 워드 데이터에 접근하기 위해 영역을 반복할 필요가 없습니다. 읽기 결과 사용 설명서는 좌표 접근 패턴을 포함한 전체 출력 모델을 다룹니다. OcrResult API 참조 문서에는 결과 계층 구조의 모든 속성이 설명되어 있습니다.

리드툴즈 OCR API와IronOCR매핑 참조

리드툴즈 OCRIronOCR
RasterSupport.SetLicense(licPath, keyContent)IronOcr.License.LicenseKey = "key"
new RasterCodecs()필요하지 않음
OcrEngineManager.CreateEngine(OcrEngineType.LEAD)new IronTesseract()
engine.Startup(codecs, null, null, runtimePath)필요하지 않음
engine.IsStarted필수 사항이 아닙니다 (항상 준비되어 있습니다).
engine.Shutdown()필요하지 않음
engine.Dispose()필요하지 않음
_codecs.Load(imagePath)input.LoadImage(imagePath)
_codecs.Load(path, 0, BgrOrGray, page, page)input.LoadPdf(path) 또는 input.LoadImageFrames(path)
_codecs.GetInformation(path, true).TotalPages필요 없음 — 자동
engine.DocumentManager.CreateDocument()필요하지 않음
document.Pages.AddPage(image, null)input.LoadImage(imagePath)
page.Recognize(null)ocr.Read(input) (인식은 Read()의 일부)
page.GetText(-1)result.Text
page.RecognizeStatusresult.Confidence (백분율)
OcrZone { Bounds = new LeadRect(x, y, w, h) }new CropRectangle(x, y, w, h)
page.Zones.Clear()필요하지 않음
page.Zones.Add(zone)input.LoadImage(path, cropRect)
zone.Words / word.Boundsresult.Pages[n].Words / word.X, word.Y
DeskewCommand().Run(image)input.Deskew()
DespeckleCommand().Run(image)input.DeNoise()
AutoBinarizeCommand().Run(image)input.Binarize()
ContrastBrightnessCommand().Run(image)input.Contrast()
new PdfDocumentOptions { ImageOverText = true }SaveAsSearchablePdf()에 의해 자동으로 처리됩니다.
engine.DocumentWriterInstance.SetOptions(format, opts)필요하지 않음
document.Save(path, DocumentFormat.Pdf, null)result.SaveAsSearchablePdf(path)
_codecs.Options.Pdf.Load.Password = passwordinput.LoadPdf(path, Password: password)
RasterSupport.IsLocked(RasterSupportType.Document)IronOcr.License.IsLicensed

일반적인 마이그레이션 문제와 해결책

문제 1: 라이선스 파일 경로 확인 실패

LEADTOOLS: RasterSupport.SetLicense()은 작업 디렉토리를 기준으로 .LIC.LIC.KEY 파일 경로를 해결하며, 이는 bin/Debug, bin/Release, Docker 컨테이너 및 IIS 애플리케이션 풀 간에 다릅니다. 일반적인 실패 모드는 개발에서 작동하지만, 작업 디렉토리가 변경되어 생산에서 "License file not found"를 던지는 경로입니다.

해결책: 두 라이선스 파일과 SetLicense() 호출을 완전히 삭제하십시오. 환경 변수에서 읽어오는 단일 문자열 할당으로 대체하세요.

// Remove this:
// RasterSupport.SetLicense(licPath, File.ReadAllText(keyPath));

// Replace with this:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
    ?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable not set");
C#

문자열 키는 모든 환경에서 동일하게 동작합니다. 데이터베이스 연결 문자열에 이미 사용 중인 동일한 비밀 관리자에 저장하십시오.

문제 2: 엔진 시동 실패 예외

LEADTOOLS: engine.Startup() 완료 전에 또는 engine.Shutdown() 호출 후에 (예: 종료 중 사용하는 이전 경합 조건에서) 인식 메서드를 호출하면 "엔진이 시작되지 않았습니다."라는 메시지와 함께 InvalidOperationException를 던집니다. 장기 서비스를 제공하는 클래스는 IsStarted 검사를 통해 이를 방지해야 합니다.

해결책: IronTesseract는 시작 호출이 필요 없으며 시작/중지 상태가 없습니다. IsStarted 보호자와 전체 수명 주기 서비스 클래스를 삭제할 수 있습니다:

// Remove the guard:
// if (!_engine.IsStarted)
//     throw new InvalidOperationException("Engine not started");

// IronTesseract is always ready — just call Read()
var result = _ocr.Read(imagePath);
C#

문제 3: 일괄 처리 시 래스터 이미지 메모리 누적 문제

LEADTOOLS: PDF 페이지 또는 이미지 파일을 반복하는 배치 처리는 루프에서 RasterImage 인스턴스를 생성합니다. 예외가 using 블록 종료 전에 발생하거나, 호출이 누락된 수동 폐기 패턴으로 인해 RasterImage를 처리하지 못하는 코드 경로가 발생하면, 해제되지 않은 이미지가 메모리에 축적됩니다. LEADTOOLS 생산 코드는 일반적으로 배치 사이에 보상 메커니즘으로 GC.Collect() / GC.WaitForPendingFinalizers() 호출을 포함합니다.

해결책: 모든 GC.Collect() 호출을 제거하십시오. OcrInput은IronOCR파이프라인에서 유일한 IDisposable이며, 표준 using 블록을 사용하여 각 배치 작업에 범위가 지정됩니다:

// Remove this pattern:
// GC.Collect();
// GC.WaitForPendingFinalizers();

// Replace with standard using scope:
foreach (var filePath in filePaths)
{
    using var input = new OcrInput();
    input.LoadImage(filePath);
    var text = _ocr.Read(input).Text;
    // input disposed here — no accumulation
}
C#

메모리 최적화에 대한 추가적인 지침은 메모리 할당 감소 관련 블로그 게시물을 참조하세요.

문제 4: DocumentWriterInstance 옵션이 호출 간에 유지됨

LEADTOOLS: engine.DocumentWriterInstance.SetOptions()는 공유 엔진 작성자 인스턴스의 상태를 수정합니다. 어떤 코드 경로가 PdfDocumentOptionsDocumentType = PdfDocumentType.PdfA로 설정하고 후속 호출이 document.Save()를 호출하기 전에 해당 옵션을 재설정하지 않으면, 이전 호출의 출력 형식이 유지됩니다. 이는 공유 엔진 인스턴스에 상태를 유지하는 부작용입니다.

**해결책:**IronOCR공유되는 기록자 상태가 없습니다. 각 SaveAsSearchablePdf() 호출은 독립적입니다.

// Remove the options setup:
// var pdfOptions = new PdfDocumentOptions { ... };
// _engine.DocumentWriterInstance.SetOptions(DocumentFormat.Pdf, pdfOptions);
// document.Save(outputPath, DocumentFormat.Pdf, null);

// Replace with:
result.SaveAsSearchablePdf(outputPath);
C#

각 호출은 이미지 오버레이와 검색 가능한 텍스트 레이어가 포함된 표준 PDF 출력을 생성합니다. 통화 간 재설정할 공유 옵션 상태가 없습니다.

문제 5: 잘못된 번들 - 런타임 시 모듈 누락

LEADTOOLS: 문서 이미징 SDK를 구매한 팀은 Leadtools.Pdf 유형을 사용할 수 없거나, 암호화된 PDF 페이지가 RasterExceptionRasterExceptionCode.FeatureNotSupported를 가진 예외를 던지는 것을 발견할 수 있습니다. 이 문제는 구매한 패키지에 PDF 모듈이 포함되어 있지 않은 경우에 발생하며, 프로덕션 환경에서 런타임 시에만 오류가 나타납니다.

**해결책:**IronOCR모든 라이선스 등급에 모든 기능을 포함합니다. 별도의 PDF 모듈, 암호화된 문서를 위한 애드온, 또는 더 높은 정확도의 엔진을 위한 부수적인 벤더는 없습니다. 단일 IronOcr 패키지를 설치한 후에는 추가 구매 없이 전체 기능 세트가 사용할 수 있습니다:

dotnet add package IronOcr

문제 6: 재정렬 후 영역 인덱스 불일치

LEADTOOLS: 영역 추출은 삽입 순서에 추출 논리를 연결하는 포지셔널 인덱싱 — page.Zones[0].Text, page.Zones[1].Text — 을 사용합니다. 변경된 양식 레이아웃에 맞춰 영역 정의 순서를 재정렬하면 이후의 모든 인덱스가 이동하여 추출이 자동으로 중단됩니다.

해결책: IronOCR는 필드별 CropRectangle와 함께 명명된 변수를 사용합니다. 각 필드는 독립적으로 범위가 지정되므로 필드 정의 순서를 변경해도 추출에는 영향을 미치지 않습니다.

// Each field is independent — reorder freely without breaking extraction
var invoiceNumber = ReadRegion(imagePath, 450, 80,  200, 30);
var invoiceDate   = ReadRegion(imagePath, 450, 115, 200, 30);
var vendorName    = ReadRegion(imagePath, 50,  150, 300, 40);
var totalAmount   = ReadRegion(imagePath, 450, 600, 200, 30);
C#

리드툴즈 OCR 마이그레이션 체크리스트

사전 마이그레이션

변경 작업을 수행하기 전에 코드베이스를 감사하여 LEADTOOLS 사용 부분을 모두 확인하십시오.

# Find all LEADTOOLS namespace imports
grep -rn "using Leadtools" --include="*.cs" .

# Find engine lifecycle calls
grep -rn "OcrEngineManager\|\.Startup(\|\.Shutdown()" --include="*.cs" .

# Find license file references
grep -rn "SetLicense\|LEADTOOLS\.LIC\|\.LIC\.KEY" --include="*.cs" .

# Find RasterCodecs usage
grep -rn "RasterCodecs\|_codecs\.Load\|GetInformation" --include="*.cs" .

# Find DocumentWriter usage
grep -rn "DocumentWriterInstance\|PdfDocumentOptions\|DocumentFormat\." --include="*.cs" .

# Find zone-based OCR
grep -rn "OcrZone\|page\.Zones\|LeadRect\|ZoneType" --include="*.cs" .

# Find GC workarounds to remove
grep -rn "GC\.Collect\|WaitForPendingFinalizers" --include="*.cs" .
SHELL

마이그레이션 후 검증 과정에서 동일한IronOCR기능이 테스트되도록 구매한 LEADTOOLS 번들(OCR 모듈, PDF 모듈 및 엔진 유형(LEAD, Tesseract 또는 OmniPage))을 기록해 두십시오.

코드 마이그레이션

  1. 모든 LEADTOOLS NuGet 패키지를 제거하십시오: Leadtools, Leadtools.Ocr, Leadtools.Codecs, Leadtools.Forms.DocumentWriters, Leadtools.Pdf
  2. IronOcr NuGet Install-Package
  3. 모든 LEADTOOLS using 지시어를 using IronOcr;로 교체하십시오
  4. 프로젝트 및 배포 아티팩트에서 .LIC.LIC.KEY 파일 제거
  5. 애플리케이션 시작 시 RasterSupport.SetLicense(licPath, keyContent)IronOcr.License.LicenseKey = "key"로 교체하십시오
  6. 전적으로 IOcrEngineRasterCodecs 수명 관리를 위해 존재하는 IDisposable 서비스 클래스를 모두 삭제하십시오
  7. OcrEngineManager.CreateEngine() + engine.Startup()new IronTesseract()으로 교체하십시오
  8. _codecs.Load(imagePath)using var input = new OcrInput() 블록 내의 input.LoadImage(imagePath)로 교체하십시오
  9. 다중 프레임 TIFF 페이지 루프를 input.LoadImageFrames(tiffPath)로 교체하십시오
  10. PDF 페이지 반복 루프를 input.LoadPdf(pdfPath)로 교체하십시오
  11. document.Pages.AddPage() + page.Recognize(null) + page.GetText(-1)ocr.Read(input).Text로 교체하십시오
  12. OcrZone + page.Zones.Add() 패턴을 input.LoadImage(path, new CropRectangle(x, y, w, h))로 교체하십시오
  13. PdfDocumentOptions + DocumentWriterInstance.SetOptions() + document.Save()result.SaveAsSearchablePdf(path)로 교체하십시오
  14. OcrInput 인스턴스에서 input.Deskew(), input.DeNoise(), input.Binarize()로 전처리 명령 클래스를 (DeskewCommand, DespeckleCommand, AutoBinarizeCommand) 교체하십시오
  15. LEADTOOLS 메모리 관리를 보상하기 위해 추가된 모든 GC.Collect() / GC.WaitForPendingFinalizers() 호출 제거

마이그레이션 이후

  • 대표적인 이미지 및 PDF 샘플에서 인식된 텍스트 출력이 LEADTOOLS 출력과 일치하는지 확인합니다.
  • result.Confidence을 사용하여 신뢰도 점수가 예상 범위 내에 있는지 확인하십시오
  • 다중 프레임 TIFF 처리 테스트에서 LEADTOOLS 프레임 반복 루프와 동일한 페이지 수가 생성되는지 확인합니다.
  • 검색 가능한 PDF 출력물이 PDF 리더(Adobe Acrobat 또는 이와 동등한 프로그램)에서 텍스트 검색이 가능한지 확인합니다.
  • 생산 송장 또는 양식의 정상 필드 값과 비교하여 영역 기반 필드 추출을 테스트합니다.
  • 별도의 Leadtools.Pdf 모듈 없이 비밀번호로 보호된 PDF 복호화가 작동하는지 확인하십시오
  • 모든 GC.Collect()를 먼저 제거한 후 부하 상태에서 배치 처리를 실행하고 메모리 성장이 없음을 확인하십시오
  • 라이선스 파일 없이 Docker 및 CI/CD 배포를 테스트하고, 환경 변수에서 "라이선스 키" 문자열이 올바르게 해석되는지 확인합니다.
  • Parallel.ForEach와 함께 병렬 처리를 테스트하여 스레드 안전성을 확인하십시오
  • 구조화된 데이터 추출 (result.Pages, page.Paragraphs, page.Words)이 올바른 좌표를 반환하는지 확인하십시오

##IronOCR로 마이그레이션할 때의 주요 이점

배포는 상태 없는 것이 됩니다. LEADTOOLS .LIC.LIC.KEY 파일은 모든 배포 환경에서 지니고 있어야 하는 아티팩트입니다. 라이선스 데이터를 이미지 기록에 노출하는 컨테이너는 라이선스 정보를 이미지에 포함시킵니다. 이를 장착하는 컨테이너는 부피 조정이 필요합니다.IronOCR로 마이그레이션한 후에는 라이선스가 환경 변수에 문자열로 저장됩니다. 배포 아티팩트는 표준 NuGet 참조입니다. 파일도 없고, 경로도 없고, 마운트 전략도 없습니다. Docker 배포 가이드Azure 배포 가이드는 컨테이너화된 환경을 위한 전체 설정 과정을 보여줍니다.

네 개의 패키지가 하나로 됩니다. 마이그레이션은 Leadtools, Leadtools.Ocr, Leadtools.Codecs, Leadtools.Forms.DocumentWriters, 옵션으로 Leadtools.Pdf를 단일 IronOcr 참조로 줄입니다. 전처리, 네이티브 PDF 입력, 검색 가능한 PDF 출력, 바코드 판독, 구조화된 데이터 추출, 125개 이상의 언어 지원 등 모든 기능이 하나의 패키지에 포함되어 있습니다. 기능 구성은 어떤 패키지를 구매했는지에 따라 달라지지 않습니다.

배치 처리는 수동 메모리 관리를 제거합니다. LEADTOOLS 배치 코드는 다중 문서 실행 중에 메모리 누적을 방지하기 위한 방어적인 GC.Collect() 호출 및 명시적인 RasterImage 폐기를 포함합니다. IronOCR의 OcrInputusing 블록과 범위가 지정되어 자동으로 정리를 처리합니다. 스레드 안전한 병렬 처리는 Parallel.ForEach로 수행됩니다—스레드당 하나의 IronTesseract 인스턴스—싱크로나이제이션 코드 없이 멀티코어 쓰루풋을 제공합니다. 생산 처리량 조정을 위한 속도 최적화 가이드를 참조하십시오.

예측 가능한 총비용. LEADTOOLS는 5인 개발자 팀 기준으로 첫 해 예상 비용이 15,000달러에서 40,000달러이며, 업데이트를 받으려면 라이선스 비용의 약 20~25%에 해당하는 연간 유지 보수 비용이 필요합니다.IronOCR Professional 2,999달러이며, 10명의 개발자와 10개의 배포 위치를 지원하는 일회성 영구 구매 상품입니다. 1년간의 업데이트가 포함되어 있습니다. 업데이트 기간 이후 계속 사용하시는 경우 추가 비용은 발생하지 않습니다. IronOCR 라이선스 페이지에는 영업 상담 없이 모든 등급별 가격이 직접 게시되어 있습니다.

영역 설정 없는 구조화된 데이터입니다. 마이그레이션 후, 단어, 라인, 및 문단 수준의 데이터가 OcrResult에서 직접 경계 상자 좌표와 함께 사용 가능합니다—영역 정의가 필요하지 않습니다. 다섯 단계의 계층 구조 (Pages, Paragraphs, Lines, Words, Characters) 각각이 위치 좌표와 요소별 신뢰도 점수를 표시합니다. 구조화된 데이터 추출을 위해 LEADTOOLS 영역 구성이 필요했던 애플리케이션은 더 간단한 API를 통해 더욱 풍부한 데이터를 얻을 수 있습니다. OCR 결과 기능 페이지에는 전체 출력 모델이 요약되어 있습니다.

참고해 주세요: Adobe Acrobat, Kofax OmniPage, LEADTOOLS 및 Tesseract는 각각 소유자의 등록 상표입니다. 이 사이트는 Adobe Inc., Apryse, Google, Kofax 또는 LEAD Technologies와 제휴, 지지, 또는 후원을 받지 않았습니다. 모든 제품명, 로고 및 브랜드는 해당 소유자의 자산입니다. 비교는 정보 제공 목적으로만 사용되며, 작성 시점에 공개적으로 이용 가능한 정보를 반영합니다.

관련 기사

Key in blue circle

무료 30일 체험 키를 즉시 받으세요.

Your trial license will be sent to your email address

제한 없음. 100% 무제한 이용. 신용카드 불필요.

bullet_checked신용카드나 계정 생성은 필요하지 않습니다.제한 없음. 100% 무제한 이용. 신용카드 불필요.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
부담 없는 무료 상담을 받아보세요
아래 양식을 작성하시거나 sales@ironsoftware.com으로 이메일을 보내주세요.
고객님의 정보는 항상 비밀로 유지됩니다.
전 세계 수백만 엔지니어들이 신뢰하는 제품입니다.
Iron Software의 고객 로고
지금 바로 30일 무료 체험판 키를 받으세요.
신용카드나 계정 생성은 필요하지 않습니다.