OCR 도구를 사용하여 이미지에서 아랍어 텍스트를 추출하는 방법
이 가이드는 .NET 개발자가 GdPicture .NET OCR에서 IronOCR 로 완전히 마이그레이션하는 과정을 안내합니다. 이 문서에서는 모든 주요 OCR 워크플로에 대한 패키지 교체, 네임스페이스 변경 및 실질적인 전후 코드 패턴을 다루며, 특히 GdPicture의 리소스 관리 모델을 정의하는 정수 이미지 ID 수명 주기를 제거하는 데 중점을 둡니다. 비교 기사를 미리 읽을 필요는 없습니다.
GdPicture .NET 에서 마이그레이션해야 하는 이유는 무엇일까요?
GdPicture .NET 은 스캐너 통합, DICOM 지원, PDF 편집, 주석 달기 및 OCR 기능을 단일 공급업체에서 제공받아야 하는 팀을 위해 구축된 문서 이미지 처리 플랫폼입니다. OCR이 유일한 요구 사항일 경우, 플랫폼의 가격 책정 방식과 API 아키텍처는 시간이 지남에 따라 누적되는 마찰을 야기합니다.
기본 OCR 워크플로우용 플러그인 비용. 스캔한 PDF에서 텍스트를 추출하고 검색 가능한 결과물을 생성하려면 세 가지 라이선스 구성 요소가 필요합니다. 코어 SDK는 약 4,000달러, OCR 플러그인은 약 2,000달러, PDF 플러그인은 약 2,000달러입니다. 따라서 초기 비용은 8,000달러이며, 여기에 연간 유지 보수 비용 20%가 Plus . OCR 기능만 필요한 팀은 문서 이미지 처리 플랫폼의 전체 가격 구조를 그대로 감수해야 합니다. IronOCR는 이미지 OCR, PDF OCR, 전처리, 검색 가능한 PDF 출력과 같은 동일한 작업 흐름을 가격에 상관없이 전체 패키지에서 지원하며, $2,999 영구 라이선스로, 기능별 라이선스 결정이 필요 없습니다. 자세한 등급별 내용은 IronOCR 라이선스 페이지를 참조하십시오.
정수 이미지 ID 수명 주기. GdPicture을 통해 로드된 모든 이미지는 int을 반환합니다. 해당 정수를 OCR 작업에 전달하고 완료되면 ReleaseGdPictureImage을 호출합니다. 이 패턴은 IDisposable 이전에 존재했습니다. 제대로 따르면 효과가 있습니다. 누락될 경우 메모리 누수가 발생합니다. 매일 수백 건의 문서를 처리하는 운영 서비스에서 오류 분기에 대한 릴리스 호출이 한 번이라도 누락되면 진단하기 매우 어려운 메모리 사용량 증가가 발생합니다. IronOCR의 OcrInput은 IDisposable을 구현합니다. 따라서 using 문은 전체 정리 작업을 없애줍니다.
버전 특정 네임스페이스. GdPicture는 네임스페이스에 주요 버전 번호를 포함합니다: using GdPicture14. 다음 주요 릴리스로 업그레이드하기 위해서는 GdPicture 클래스를 참조하는 모든 소스 파일에서 해당 using 지침을 업데이트해야 합니다. 수십 개의 서비스에 걸쳐 OCR 기능이 분산된 대규모 애플리케이션의 경우, 찾기 및 바꾸기 작업은 몇 시간씩 소요되며 기능적인 개선도 가져오지 못합니다. IronOCR의 네임스페이스는 모든 주요 버전에서 IronOcr되었습니다.
외부 리소스 폴더 요구 사항. GdPicture OCR은 런타임에 ResourceFolder 언어 파일이 들어 있는 디렉터리를 가리키는 속성이 필요합니다. 이 경로는 개발 머신에서 작동하지만 디렉터리 구조가 존재하지 않는 Linux 서버, Docker 컨테이너, Azure App Service 배포 환경에서는 작동하지 않습니다. IronOCR은 NuGet 패키지에 영어 언어 지원을 번들 포함합니다; 추가 언어는 빌드 출력과 함께 제공되는 NuGet 패키지로 설치됩니다.
3-컴포넌트 초기화. GdPicture의 PDF OCR 워크플로는 GdPictureImaging, GdPictureOCR, GdPicturePDF - 개별적인 정리 요구 사항을 가진 세 가지 컴포넌트의 인스턴스 화가 필요하며 라이선스 매니저 등록 및 리소스 폴더 할당을 포함해야 합니다.IronOCR시작 시 한 줄의 코드와 호출 시 하나의 클래스가 필요합니다.
GdPicture OCR 인스턴스는 스레드 안전성을 기본적으로 지원하지 않습니다. 병렬 문서 처리를 위해서는 상태 손상을 방지하기 위해 신중한 인스턴스 관리 및 동기화가 필요합니다. IronOCR는 동시 사용을 위해 설계되었습니다: 스레드 당 하나의 IronTesseract을 생성하거나 로드 상태에서 단일 인스턴스를 공유 - 어떤 패턴이든 추가 동기화 코드 없이 작동합니다.
근본적인 문제
GdPicture는 각 이미지에 대해 런타임 관리 정수 핸들로 메모리를 할당합니다. TIFF 처리 루프의 모든 RenderPageToGdPictureImage 호출은 수동으로 해제해야 하는 새 할당을 생성합니다:
// GdPicture: every frame = new integer ID = manual release required
using var pdf = new GdPicturePDF();
pdf.LoadFromFile("multi-page.tiff", false);
var frameIds = new List<int>();
try
{
for (int i = 1; i <= pdf.GetPageCount(); i++)
{
pdf.SelectPage(i);
int frameId = pdf.RenderPageToGdPictureImage(200, false); // new allocation
if (frameId != 0) frameIds.Add(frameId);
// ... OCR call here ...
}
}
finally
{
foreach (var id in frameIds) _imaging.ReleaseGdPictureImage(id); // manual per-frame release
}
// GdPicture: every frame = new integer ID = manual release required
using var pdf = new GdPicturePDF();
pdf.LoadFromFile("multi-page.tiff", false);
var frameIds = new List<int>();
try
{
for (int i = 1; i <= pdf.GetPageCount(); i++)
{
pdf.SelectPage(i);
int frameId = pdf.RenderPageToGdPictureImage(200, false); // new allocation
if (frameId != 0) frameIds.Add(frameId);
// ... OCR call here ...
}
}
finally
{
foreach (var id in frameIds) _imaging.ReleaseGdPictureImage(id); // manual per-frame release
}
Imports System.Collections.Generic
' GdPicture: every frame = new integer ID = manual release required
Using pdf As New GdPicturePDF()
pdf.LoadFromFile("multi-page.tiff", False)
Dim frameIds As New List(Of Integer)()
Try
For i As Integer = 1 To pdf.GetPageCount()
pdf.SelectPage(i)
Dim frameId As Integer = pdf.RenderPageToGdPictureImage(200, False) ' new allocation
If frameId <> 0 Then frameIds.Add(frameId)
' ... OCR call here ...
Next
Finally
For Each id In frameIds
_imaging.ReleaseGdPictureImage(id) ' manual per-frame release
Next
End Try
End Using
IronOCR 생명주기를 완전히 제거합니다. OcrInput는 프레임 열거 및 정리를 처리합니다:
// IronOCR: the using statement handles everything
using var input = new OcrInput();
input.LoadImageFrames("multi-page.tiff");
var result = new IronTesseract().Read(input);
// IronOCR: the using statement handles everything
using var input = new OcrInput();
input.LoadImageFrames("multi-page.tiff");
var result = new IronTesseract().Read(input);
Imports IronOcr
Dim input As New OcrInput()
Using input
input.LoadImageFrames("multi-page.tiff")
Dim result = New IronTesseract().Read(input)
End Using
IronOCR과 GdPicture .NET: 기능 비교
아래 표는 OCR 중심 워크플로우를 위한 두 라이브러리 간의 기능 적용 범위를 보여줍니다.
| 기능 | GdPicture.NET | IronOCR |
|---|---|---|
| 설치 | 여러 개의 NuGet 패키지 | dotnet add package IronOcr |
| 라이선스 활성화 | LicenseManager.RegisterKEY() |
IronOcr.License.LicenseKey = "..." |
| 주요 업그레이드 시 네임스페이스 | 찾기 및 대체 필요 (GdPicture14 → 다음) |
변경되지 않음 (IronOcr) |
| 컴포넌트 초기화 | GdPictureImaging + GdPictureOCR + GdPicturePDF |
new IronTesseract() |
| 리소스 메모리 모델 | 수동 정수 ID 추적 및 릴리스 | IDisposable / using 명령문 |
| 메모리 누수 위험 | 높음 (누락된 ReleaseGdPictureImage) |
없음 (컴파일러 강제 사항 using) |
| 외부 리소스 폴더 | 필수 (_ocr.ResourceFolder = path) |
필수 사항이 아닙니다. 패키지에 포함되어 있습니다. |
| 이미지 OCR | 예 | 예 |
| PDF OCR | 예 (PDF 플러그인 필요) | 내장형, 추가 라이선스 필요 없음 |
| 여러 페이지로 구성된 TIFF 파일 | 수동 프레임 루프 + 프레임별 ID 정리 | input.LoadImageFrames() |
| 스트림 입력 | GdPictureImaging 스트림 오버로드를 통해 |
input.LoadImage(stream) |
| 검색 가능한 PDF 출력 | pdf.OcrPage() + pdf.SaveToFile() |
result.SaveAsSearchablePdf() |
| 디스큐 전처리 | 문서 이미지 플러그인이 필요합니다. | input.Deskew() – 내장 |
| 소음 제거 | 문서 이미지 플러그인이 필요합니다. | input.DeNoise() – 내장 |
| 언어 | 폴더에 있는 Tesseract 기반 학습 데이터 파일 | NuGet 패키지를 통해 125개 이상 |
| 다국어 OCR | 예 | OcrLanguage.French + OcrLanguage.German |
| 스레드 안전성 | 수동 인스턴스 관리 | 설계상 스레드 안전 |
| 비동기 OCR | 내장되어 있지 않음 | ReadAsync() |
| 바코드 판독 | 별도의 바코드 플러그인 | ocr.Configuration.ReadBarCodes = true |
| 구조화된 출력 | 인덱스 기반 블록/줄/단어 접근 | 타입 .Pages, .Words, .Characters |
| 자신감 점수 | GetOCRResultConfidence(resultId) |
result.Confidence |
| 크로스 플랫폼 | 윈도우, 리눅스, macOS | 윈도우, 리눅스, macOS, Docker, AWS, Azure |
| 진입 비용(PDF에서 OCR 처리) | 약 8,000달러 (코어 + OCR + PDF 플러그인 포함) | $999–$2,999 |
| 가격 모델 | 플러그인 기반 영구 라이선스 + 연간 20% 유지보수 비용 | 고정형 영구채, 연간 업데이트는 선택 사항입니다. |
| 상업적 지원 | 예 | 예 |
빠른 시작: GdPicture .NET 에서IronOCR로 마이그레이션
1단계: NuGet 패키지 교체
GdPicture 패키지를 제거하세요:
dotnet remove package GdPicture.NET
dotnet remove package GdPicture.NET.OCR
dotnet remove package GdPicture.NET.PDF
dotnet remove package GdPicture.NET
dotnet remove package GdPicture.NET.OCR
dotnet remove package GdPicture.NET.PDF
NuGet 패키지 페이지 에서IronOCR설치하세요.
dotnet add package IronOcr
영어 이외의 언어를 사용하려면 해당 언어 팩을 설치하세요.
dotnet add package IronOcr.Languages.French, IronOcr.Languages.German
단계 2: 네임스페이스 업데이트
GdPicture 네임스페이스를IronOCR네임스페이스로 바꾸십시오.
// Before (GdPicture)
using GdPicture14;
// After (IronOCR)
using IronOcr;
// Before (GdPicture)
using GdPicture14;
// After (IronOCR)
using IronOcr;
Imports IronOcr
단계 3: 라이선스 초기화
이 줄을 Program.cs 또는 Startup.cs의 OCR 호출 전에 배치하십시오:
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
GdPicture 라이선스 등록 차단을 완전히 제거하십시오.
// Remove this
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
// Remove this
LicenseManager lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
구매 전 평가를 위해 IronOCR 제품 페이지 에서 무료 평가판 키를 다운로드할 수 있습니다.
코드 마이그레이션 예제
다중 페이지 TIFF 프레임 처리
GdPicture에서 여러 페이지로 구성된 TIFF 파일을 처리하려면 PDF/이미징 API를 통해 각 프레임을 선택하고, 새 이미지 ID로 렌더링하고, OCR을 실행한 다음 해당 ID를 해제해야 합니다. finally 블록에서 해제되지 않은 단일 프레임은 DPI에 따라 10-50MB를 누출합니다.
GdPicture .NET 접근 방식:
using GdPicture14;
public class GdPictureTiffProcessor
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public string ExtractTextFromTiff(string tiffPath)
{
var text = new StringBuilder();
// Load TIFF through the imaging component
int tiffId = _imaging.CreateGdPictureImageFromFile(tiffPath);
if (tiffId == 0)
throw new Exception($"TIFF load failed: {_imaging.GetStat()}");
// Outer try: release the original TIFF handle
try
{
int frameCount = _imaging.GetPageCount(tiffId);
for (int i = 1; i <= frameCount; i++)
{
// Switch to frame — modifies the existing ID in place
_imaging.SelectPage(tiffId, i);
// Clone frame to a new image ID for OCR
int frameId = _imaging.CloneImage(tiffId);
if (frameId == 0) continue;
// Inner try: release each cloned frame ID
try
{
_ocr.SetImage(frameId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (!string.IsNullOrEmpty(resultId))
{
text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}");
}
}
finally
{
// Release cloned frame — critical for each iteration
_imaging.ReleaseGdPictureImage(frameId);
}
}
}
finally
{
// Release original TIFF handle
_imaging.ReleaseGdPictureImage(tiffId);
}
return text.ToString();
}
}
using GdPicture14;
public class GdPictureTiffProcessor
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public string ExtractTextFromTiff(string tiffPath)
{
var text = new StringBuilder();
// Load TIFF through the imaging component
int tiffId = _imaging.CreateGdPictureImageFromFile(tiffPath);
if (tiffId == 0)
throw new Exception($"TIFF load failed: {_imaging.GetStat()}");
// Outer try: release the original TIFF handle
try
{
int frameCount = _imaging.GetPageCount(tiffId);
for (int i = 1; i <= frameCount; i++)
{
// Switch to frame — modifies the existing ID in place
_imaging.SelectPage(tiffId, i);
// Clone frame to a new image ID for OCR
int frameId = _imaging.CloneImage(tiffId);
if (frameId == 0) continue;
// Inner try: release each cloned frame ID
try
{
_ocr.SetImage(frameId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (!string.IsNullOrEmpty(resultId))
{
text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}");
}
}
finally
{
// Release cloned frame — critical for each iteration
_imaging.ReleaseGdPictureImage(frameId);
}
}
}
finally
{
// Release original TIFF handle
_imaging.ReleaseGdPictureImage(tiffId);
}
return text.ToString();
}
}
Imports GdPicture14
Imports System.Text
Public Class GdPictureTiffProcessor
Private ReadOnly _imaging As GdPictureImaging
Private ReadOnly _ocr As GdPictureOCR
Public Function ExtractTextFromTiff(tiffPath As String) As String
Dim text As New StringBuilder()
' Load TIFF through the imaging component
Dim tiffId As Integer = _imaging.CreateGdPictureImageFromFile(tiffPath)
If tiffId = 0 Then
Throw New Exception($"TIFF load failed: {_imaging.GetStat()}")
End If
' Outer try: release the original TIFF handle
Try
Dim frameCount As Integer = _imaging.GetPageCount(tiffId)
For i As Integer = 1 To frameCount
' Switch to frame — modifies the existing ID in place
_imaging.SelectPage(tiffId, i)
' Clone frame to a new image ID for OCR
Dim frameId As Integer = _imaging.CloneImage(tiffId)
If frameId = 0 Then Continue For
' Inner try: release each cloned frame ID
Try
_ocr.SetImage(frameId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If Not String.IsNullOrEmpty(resultId) Then
text.AppendLine($"[Frame {i}] {_ocr.GetOCRResultText(resultId)}")
End If
Finally
' Release cloned frame — critical for each iteration
_imaging.ReleaseGdPictureImage(frameId)
End Try
Next
Finally
' Release original TIFF handle
_imaging.ReleaseGdPictureImage(tiffId)
End Try
Return text.ToString()
End Function
End Class
IronOCR 접근 방식:
using IronOcr;
public class IronOcrTiffProcessor
{
public string ExtractTextFromTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames loaded, all cleanup automatic
var result = new IronTesseract().Read(input);
// Per-frame text is available on result.Pages
foreach (var page in result.Pages)
Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}");
return result.Text;
}
}
using IronOcr;
public class IronOcrTiffProcessor
{
public string ExtractTextFromTiff(string tiffPath)
{
using var input = new OcrInput();
input.LoadImageFrames(tiffPath); // all frames loaded, all cleanup automatic
var result = new IronTesseract().Read(input);
// Per-frame text is available on result.Pages
foreach (var page in result.Pages)
Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}");
return result.Text;
}
}
Imports IronOcr
Public Class IronOcrTiffProcessor
Public Function ExtractTextFromTiff(tiffPath As String) As String
Using input As New OcrInput()
input.LoadImageFrames(tiffPath) ' all frames loaded, all cleanup automatic
Dim result = New IronTesseract().Read(input)
' Per-frame text is available on result.Pages
For Each page In result.Pages
Console.WriteLine($"[Frame {page.PageNumber}] {page.Text}")
Next
Return result.Text
End Using
End Function
End Class
LoadImageFrames는 멀티 페이지 TIFF의 모든 프레임을 OcrInput 파이프라인에 로드합니다. using 블록은 범위 끝에 있는 모든 프레임과 관련된 메모리를 처리합니다. 유지 해야 할 List<int>이 없으며, 중첩된 try/finally 블록이 필요하지 않습니다. TIFF 및 GIF 입력 가이드 에서는 프레임 선택 및 다중 형식 처리에 대해 자세히 설명합니다.
스트림 기반 입력으로 플러그인 초기화를 대체합니다.
서버 애플리케이션은 HTTP 업로드, 메시지 큐 또는 데이터베이스 블롭 등으로부터 파일 경로가 아닌 스트림 형태로 문서를 수신하는 경우가 많습니다. GdPicture은 GdPictureImaging을 통해 스트림을 로드해야 하며, 해당 과정 자체가 모든 작업 전에 구성 요소 초기화 시퀀스 및 리소스 폴더 구성을 요구합니다.
GdPicture .NET 접근 방식:
using GdPicture14;
public class GdPictureStreamOcr : IDisposable
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public GdPictureStreamOcr()
{
// Plugin initialization required before stream loading is possible
var lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
_imaging = new GdPictureImaging();
_ocr = new GdPictureOCR();
_ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR"; // path must exist at runtime
}
public string ExtractTextFromStream(Stream documentStream)
{
// Load stream into imaging component to get an image ID
int imageId = _imaging.CreateGdPictureImageFromStream(documentStream);
if (imageId == 0)
throw new Exception($"Stream load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
}
public void Dispose()
{
_ocr?.Dispose();
_imaging?.Dispose();
}
}
using GdPicture14;
public class GdPictureStreamOcr : IDisposable
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public GdPictureStreamOcr()
{
// Plugin initialization required before stream loading is possible
var lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
_imaging = new GdPictureImaging();
_ocr = new GdPictureOCR();
_ocr.ResourceFolder = @"C:\GdPicture\Resources\OCR"; // path must exist at runtime
}
public string ExtractTextFromStream(Stream documentStream)
{
// Load stream into imaging component to get an image ID
int imageId = _imaging.CreateGdPictureImageFromStream(documentStream);
if (imageId == 0)
throw new Exception($"Stream load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
}
public void Dispose()
{
_ocr?.Dispose();
_imaging?.Dispose();
}
}
IRON VB CONVERTER ERROR developers@ironsoftware.com
IronOCR 접근 방식:
using IronOcr;
public class IronOcrStreamOcr
{
public string ExtractTextFromStream(Stream documentStream)
{
using var input = new OcrInput();
input.LoadImage(documentStream); // stream accepted directly — no imaging component
return new IronTesseract().Read(input).Text;
}
}
using IronOcr;
public class IronOcrStreamOcr
{
public string ExtractTextFromStream(Stream documentStream)
{
using var input = new OcrInput();
input.LoadImage(documentStream); // stream accepted directly — no imaging component
return new IronTesseract().Read(input).Text;
}
}
Imports IronOcr
Public Class IronOcrStreamOcr
Public Function ExtractTextFromStream(documentStream As Stream) As String
Using input As New OcrInput()
input.LoadImage(documentStream) ' stream accepted directly — no imaging component
Return New IronTesseract().Read(input).Text
End Using
End Function
End Class
GdPicture 방식을 사용하려면 첫 번째 스트림을 소비하기 전에 세 개의 객체를 생성하고 파일 시스템 경로를 구성해야 합니다. IronOCR는 사전 설정 없이 OcrInput에서 스트림을 직접 수락합니다. 스트림 입력 가이드는 임시 파일 작성을 피하고자 하는 파이프라인 아키텍처를 위한 바이트 배열, MemoryStream, FileStream 패턴을 다룹니다.
비동기 OCR이 상태 코드 폴링을 대체합니다.
GdPicture OCR은 동기식입니다. ASP.NET Core 엔드포인트 또는 백그라운드 서비스에서 문서를 처리하는 애플리케이션은 Task.Run 내에서 RunOCR을 감싸서 요청 스레드를 차단하지 않도록 해야 하며, 스레드 경계를 넘어 이미지 ID 수명 주기를 관리해야 합니다. IronOCR는 ReadAsync를 통해 일류 비동기 지원을 제공합니다.
GdPicture .NET 접근 방식:
using GdPicture14;
using System.Threading.Tasks;
public class GdPictureAsyncWrapper
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
public async Task<string> ExtractTextAsync(string imagePath)
{
// Must acquire lock: GdPictureOCR is not thread-safe
await _lock.WaitAsync();
try
{
return await Task.Run(() =>
{
int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
throw new Exception($"Load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
});
}
finally
{
_lock.Release();
}
}
}
using GdPicture14;
using System.Threading.Tasks;
public class GdPictureAsyncWrapper
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
public async Task<string> ExtractTextAsync(string imagePath)
{
// Must acquire lock: GdPictureOCR is not thread-safe
await _lock.WaitAsync();
try
{
return await Task.Run(() =>
{
int imageId = _imaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
throw new Exception($"Load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
});
}
finally
{
_lock.Release();
}
}
}
Imports GdPicture14
Imports System.Threading.Tasks
Public Class GdPictureAsyncWrapper
Private ReadOnly _imaging As GdPictureImaging
Private ReadOnly _ocr As GdPictureOCR
Private ReadOnly _lock As New SemaphoreSlim(1, 1)
Public Async Function ExtractTextAsync(imagePath As String) As Task(Of String)
' Must acquire lock: GdPictureOCR is not thread-safe
Await _lock.WaitAsync()
Try
Return Await Task.Run(Function()
Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(imagePath)
If imageId = 0 Then
Throw New Exception($"Load failed: {_imaging.GetStat()}")
End If
Try
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If String.IsNullOrEmpty(resultId) Then
Throw New Exception($"OCR failed: {_ocr.GetStat()}")
End If
Return _ocr.GetOCRResultText(resultId)
Finally
_imaging.ReleaseGdPictureImage(imageId)
End Try
End Function)
Finally
_lock.Release()
End Try
End Function
End Class
IronOCR 접근 방식:
using IronOcr;
public class IronOcrAsyncService
{
private readonly IronTesseract _ocr = new IronTesseract();
public async Task<string> ExtractTextAsync(string imagePath)
{
// ReadAsync is natively async — no Task.Run wrapper, no lock required
var result = await _ocr.ReadAsync(imagePath);
return result.Text;
}
public async Task<string> ExtractFromStreamAsync(Stream stream)
{
using var input = new OcrInput();
input.LoadImage(stream);
var result = await _ocr.ReadAsync(input);
return result.Text;
}
}
using IronOcr;
public class IronOcrAsyncService
{
private readonly IronTesseract _ocr = new IronTesseract();
public async Task<string> ExtractTextAsync(string imagePath)
{
// ReadAsync is natively async — no Task.Run wrapper, no lock required
var result = await _ocr.ReadAsync(imagePath);
return result.Text;
}
public async Task<string> ExtractFromStreamAsync(Stream stream)
{
using var input = new OcrInput();
input.LoadImage(stream);
var result = await _ocr.ReadAsync(input);
return result.Text;
}
}
Imports IronOcr
Imports System.IO
Imports System.Threading.Tasks
Public Class IronOcrAsyncService
Private ReadOnly _ocr As New IronTesseract()
Public Async Function ExtractTextAsync(imagePath As String) As Task(Of String)
' ReadAsync is natively async — no Task.Run wrapper, no lock required
Dim result = Await _ocr.ReadAsync(imagePath)
Return result.Text
End Function
Public Async Function ExtractFromStreamAsync(stream As Stream) As Task(Of String)
Using input As New OcrInput()
input.LoadImage(stream)
Dim result = Await _ocr.ReadAsync(input)
Return result.Text
End Using
End Function
End Class
GdPicture 방법은 공유 GdPictureOCR 인스턴스에 대한 접근을 직렬화하기 위해 SemaphoreSlim이 필요하며, 호출 스레드에서 동기화 차단 작업을 이동시키기 위해 Task.Run이 필요하며, 해당 람다 내에서 전체 이미지 ID 수명 주기를 포함해야 합니다. IronOCR의 ReadAsync은 진정한 비차단 및 스레드 안전성을 제공합니다. 비동기 OCR 가이드에서는 ASP.NET Core 미들웨어 및 호스팅된 백그라운드 서비스와의 통합에 대해 다룹니다.
스레드 안전성을 고려한 병렬 배치 처리
스캔한 문서가 담긴 폴더를 최대한 빠르게 처리하려면 병렬 실행이 필요합니다. GdPicture은 스레드 당 하나의 GdPictureOCR 인스턴스가 필요합니다 - 단일 인스턴스를 공유하면 비결정적 실패를 초래합니다. 각 스레드별 인스턴스는 또한 자체 GdPictureImaging 구성 요소와 리소스 폴더 구성이 필요하기 때문에, 공장 패턴 없이는 스레드 풀 접근이 실현 불가능합니다.
GdPicture .NET 접근 방식:
using GdPicture14;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class GdPictureParallelBatch
{
private readonly string _resourceFolder = @"C:\GdPicture\Resources\OCR";
public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// Each thread must have its own component instances
Parallel.ForEach(imagePaths, imagePath =>
{
// Create per-thread instances — shared instances cause failures
using var threadImaging = new GdPictureImaging();
using var threadOcr = new GdPictureOCR();
threadOcr.ResourceFolder = _resourceFolder;
// Re-register license per thread (may be required depending on SDK version)
var lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
int imageId = threadImaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
{
results[imagePath] = $"ERROR: {threadImaging.GetStat()}";
return;
}
try
{
threadOcr.SetImage(imageId);
threadOcr.Language = "eng";
string resultId = threadOcr.RunOCR();
results[imagePath] = string.IsNullOrEmpty(resultId)
? $"ERROR: {threadOcr.GetStat()}"
: threadOcr.GetOCRResultText(resultId);
}
finally
{
threadImaging.ReleaseGdPictureImage(imageId);
}
});
return results;
}
}
using GdPicture14;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class GdPictureParallelBatch
{
private readonly string _resourceFolder = @"C:\GdPicture\Resources\OCR";
public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// Each thread must have its own component instances
Parallel.ForEach(imagePaths, imagePath =>
{
// Create per-thread instances — shared instances cause failures
using var threadImaging = new GdPictureImaging();
using var threadOcr = new GdPictureOCR();
threadOcr.ResourceFolder = _resourceFolder;
// Re-register license per thread (may be required depending on SDK version)
var lm = new LicenseManager();
lm.RegisterKEY("GDPICTURE-LICENSE-KEY");
int imageId = threadImaging.CreateGdPictureImageFromFile(imagePath);
if (imageId == 0)
{
results[imagePath] = $"ERROR: {threadImaging.GetStat()}";
return;
}
try
{
threadOcr.SetImage(imageId);
threadOcr.Language = "eng";
string resultId = threadOcr.RunOCR();
results[imagePath] = string.IsNullOrEmpty(resultId)
? $"ERROR: {threadOcr.GetStat()}"
: threadOcr.GetOCRResultText(resultId);
}
finally
{
threadImaging.ReleaseGdPictureImage(imageId);
}
});
return results;
}
}
Imports GdPicture14
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Public Class GdPictureParallelBatch
Private ReadOnly _resourceFolder As String = "C:\GdPicture\Resources\OCR"
Public Function ProcessBatch(imagePaths As String()) As ConcurrentDictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
' Each thread must have its own component instances
Parallel.ForEach(imagePaths, Sub(imagePath)
' Create per-thread instances — shared instances cause failures
Using threadImaging As New GdPictureImaging()
Using threadOcr As New GdPictureOCR()
threadOcr.ResourceFolder = _resourceFolder
' Re-register license per thread (may be required depending on SDK version)
Dim lm As New LicenseManager()
lm.RegisterKEY("GDPICTURE-LICENSE-KEY")
Dim imageId As Integer = threadImaging.CreateGdPictureImageFromFile(imagePath)
If imageId = 0 Then
results(imagePath) = $"ERROR: {threadImaging.GetStat()}"
Return
End If
Try
threadOcr.SetImage(imageId)
threadOcr.Language = "eng"
Dim resultId As String = threadOcr.RunOCR()
results(imagePath) = If(String.IsNullOrEmpty(resultId), $"ERROR: {threadOcr.GetStat()}", threadOcr.GetOCRResultText(resultId))
Finally
threadImaging.ReleaseGdPictureImage(imageId)
End Try
End Using
End Using
End Sub)
Return results
End Function
End Class
IronOCR 접근 방식:
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class IronOcrParallelBatch
{
public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// IronTesseract is thread-safe — one instance handles all threads
var ocr = new IronTesseract();
Parallel.ForEach(imagePaths, imagePath =>
{
try
{
results[imagePath] = ocr.Read(imagePath).Text;
}
catch (Exception ex)
{
results[imagePath] = $"ERROR: {ex.Message}";
}
});
return results;
}
}
using IronOcr;
using System.Collections.Concurrent;
using System.Threading.Tasks;
public class IronOcrParallelBatch
{
public ConcurrentDictionary<string, string> ProcessBatch(string[] imagePaths)
{
var results = new ConcurrentDictionary<string, string>();
// IronTesseract is thread-safe — one instance handles all threads
var ocr = new IronTesseract();
Parallel.ForEach(imagePaths, imagePath =>
{
try
{
results[imagePath] = ocr.Read(imagePath).Text;
}
catch (Exception ex)
{
results[imagePath] = $"ERROR: {ex.Message}";
}
});
return results;
}
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading.Tasks
Public Class IronOcrParallelBatch
Public Function ProcessBatch(imagePaths As String()) As ConcurrentDictionary(Of String, String)
Dim results As New ConcurrentDictionary(Of String, String)()
' IronTesseract is thread-safe — one instance handles all threads
Dim ocr As New IronTesseract()
Parallel.ForEach(imagePaths, Sub(imagePath)
Try
results(imagePath) = ocr.Read(imagePath).Text
Catch ex As Exception
results(imagePath) = $"ERROR: {ex.Message}"
End Try
End Sub)
Return results
End Function
End Class
GdPicture의 병렬 구현은 스레드당 세 개의 객체를 생성하며, 스레드별 라이선스 등록이 필요합니다. IronOCR는 동기화 오버헤드 없이 단일 IronTesseract 인스턴스에서 동시 읽기를 처리합니다. 멀티스레딩 예제는 문서 처리 작업 부하에서 고용량 처리량 벤치마크 및 Parallel.ForEach 패턴을 보여줍니다.
바이트 배열 입력 및 구조화된 단락 추출
데이터베이스나 객체 저장소에서 문서를 검색하는 애플리케이션은 파일 경로 대신 바이트 배열을 사용하는 경우가 많습니다. GdPicture은 바이트 배열을 스트림으로 변환하고 GdPictureImaging을 통해 로드해야 합니다. 결과에서 단락 수준의 구조화된 데이터를 추출하려면 블록/행 색인 계층 구조를 탐색해야 합니다.
GdPicture .NET 접근 방식:
using GdPicture14;
public class GdPictureByteArrayOcr
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
{
var paragraphs = new List<string>();
// Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
using var ms = new MemoryStream(imageBytes);
int imageId = _imaging.CreateGdPictureImageFromStream(ms);
if (imageId == 0)
throw new Exception($"Byte array load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
// Paragraph-level data requires iterating block structure
int blockCount = _ocr.GetOCRResultBlockCount(resultId);
for (int b = 0; b < blockCount; b++)
{
var blockText = new StringBuilder();
int lineCount = _ocr.GetOCRResultBlockLineCount(resultId, b);
for (int l = 0; l < lineCount; l++)
{
int wordCount = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l);
for (int w = 0; w < wordCount; w++)
{
blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w));
blockText.Append(" ");
}
}
string text = blockText.ToString().Trim();
if (!string.IsNullOrEmpty(text))
paragraphs.Add(text);
}
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
return paragraphs;
}
}
using GdPicture14;
public class GdPictureByteArrayOcr
{
private readonly GdPictureImaging _imaging;
private readonly GdPictureOCR _ocr;
public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
{
var paragraphs = new List<string>();
// Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
using var ms = new MemoryStream(imageBytes);
int imageId = _imaging.CreateGdPictureImageFromStream(ms);
if (imageId == 0)
throw new Exception($"Byte array load failed: {_imaging.GetStat()}");
try
{
_ocr.SetImage(imageId);
_ocr.Language = "eng";
string resultId = _ocr.RunOCR();
if (string.IsNullOrEmpty(resultId))
throw new Exception($"OCR failed: {_ocr.GetStat()}");
// Paragraph-level data requires iterating block structure
int blockCount = _ocr.GetOCRResultBlockCount(resultId);
for (int b = 0; b < blockCount; b++)
{
var blockText = new StringBuilder();
int lineCount = _ocr.GetOCRResultBlockLineCount(resultId, b);
for (int l = 0; l < lineCount; l++)
{
int wordCount = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l);
for (int w = 0; w < wordCount; w++)
{
blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w));
blockText.Append(" ");
}
}
string text = blockText.ToString().Trim();
if (!string.IsNullOrEmpty(text))
paragraphs.Add(text);
}
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
return paragraphs;
}
}
Imports GdPicture14
Imports System.IO
Imports System.Text
Public Class GdPictureByteArrayOcr
Private ReadOnly _imaging As GdPictureImaging
Private ReadOnly _ocr As GdPictureOCR
Public Function ExtractParagraphsFromBytes(imageBytes As Byte()) As List(Of String)
Dim paragraphs As New List(Of String)()
' Byte array must go through MemoryStream to reach CreateGdPictureImageFromStream
Using ms As New MemoryStream(imageBytes)
Dim imageId As Integer = _imaging.CreateGdPictureImageFromStream(ms)
If imageId = 0 Then
Throw New Exception($"Byte array load failed: {_imaging.GetStat()}")
End If
Try
_ocr.SetImage(imageId)
_ocr.Language = "eng"
Dim resultId As String = _ocr.RunOCR()
If String.IsNullOrEmpty(resultId) Then
Throw New Exception($"OCR failed: {_ocr.GetStat()}")
End If
' Paragraph-level data requires iterating block structure
Dim blockCount As Integer = _ocr.GetOCRResultBlockCount(resultId)
For b As Integer = 0 To blockCount - 1
Dim blockText As New StringBuilder()
Dim lineCount As Integer = _ocr.GetOCRResultBlockLineCount(resultId, b)
For l As Integer = 0 To lineCount - 1
Dim wordCount As Integer = _ocr.GetOCRResultBlockLineWordCount(resultId, b, l)
For w As Integer = 0 To wordCount - 1
blockText.Append(_ocr.GetOCRResultBlockLineWordText(resultId, b, l, w))
blockText.Append(" "c)
Next
Next
Dim text As String = blockText.ToString().Trim()
If Not String.IsNullOrEmpty(text) Then
paragraphs.Add(text)
End If
Next
Finally
_imaging.ReleaseGdPictureImage(imageId)
End Try
End Using
Return paragraphs
End Function
End Class
IronOCR 접근 방식:
using IronOcr;
public class IronOcrByteArrayOcr
{
public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // byte array accepted directly
var result = new IronTesseract().Read(input);
// Paragraphs are a first-class typed collection
return result.Paragraphs
.Select(p => p.Text)
.Where(t => !string.IsNullOrWhiteSpace(t))
.ToList();
}
}
using IronOcr;
public class IronOcrByteArrayOcr
{
public List<string> ExtractParagraphsFromBytes(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // byte array accepted directly
var result = new IronTesseract().Read(input);
// Paragraphs are a first-class typed collection
return result.Paragraphs
.Select(p => p.Text)
.Where(t => !string.IsNullOrWhiteSpace(t))
.ToList();
}
}
Imports IronOcr
Public Class IronOcrByteArrayOcr
Public Function ExtractParagraphsFromBytes(imageBytes As Byte()) As List(Of String)
Using input As New OcrInput()
input.LoadImage(imageBytes) ' byte array accepted directly
Dim result = New IronTesseract().Read(input)
' Paragraphs are a first-class typed collection
Return result.Paragraphs _
.Select(Function(p) p.Text) _
.Where(Function(t) Not String.IsNullOrWhiteSpace(t)) _
.ToList()
End Using
End Function
End Class
IronOCR는 중간 MemoryStream 없이 byte[]를 LoadImage에 직접 수락합니다. 결과는 블록/라인/단어 삼중 루프가 필요 없는 형식화된 LINQ-쿼리 가능한 컬렉션으로 .Paragraphs을 노출합니다. 결과 읽기 가이드에서는 송장 및 양식 처리 워크플로에 대한 좌표 액세스, 신뢰도 필터링 및 구조화된 출력 패턴을 다룹니다. 문서의 특정 영역을 스캔하려면 영역 기반 OCR을 참조하십시오.
GdPicture .NET API와IronOCR매핑 참조
| GdPicture.NET | IronOCR에 상응하는 |
|---|---|
using GdPicture14; |
using IronOcr; |
LicenseManager.RegisterKEY("key") |
IronOcr.License.LicenseKey = "key" |
new GdPictureImaging() |
필요 없음 – OcrInput 내부 |
new GdPictureOCR() |
new IronTesseract() |
new GdPicturePDF() |
필요 없음 – OcrInput.LoadPdf()이 처리함 |
_ocr.ResourceFolder = path |
필수 사항이 아닙니다. 리소스는 NuGet 에 포함되어 있습니다. |
imaging.CreateGdPictureImageFromFile(path) |
input.LoadImage(path) |
imaging.CreateGdPictureImageFromStream(stream) |
input.LoadImage(stream) |
imaging.CreateGdPictureImageFromBytes(bytes) |
input.LoadImage(bytes) |
imaging.CloneImage(tiffId)은 프레임 당 |
input.LoadImageFrames(tiffPath) |
imaging.ReleaseGdPictureImage(imageId) |
using var input = new OcrInput() – 자동 |
ocr.SetImage(imageId) |
필요 없음 – OcrInput이 이미지를 보유 |
ocr.Language = "eng" |
ocr.Language = OcrLanguage.English |
ocr.RunOCR() → resultId 문자열 |
ocr.Read(input) → 형식화된 OcrResult |
ocr.GetOCRResultText(resultId) |
result.Text |
ocr.GetOCRResultConfidence(resultId) |
result.Confidence |
ocr.GetOCRResultBlockCount(resultId) |
result.Pages[i].Paragraphs.Count |
ocr.GetOCRResultBlockLineWordText(resultId, b, l, w) |
result.Words[i].Text |
imaging.GetStat() / ocr.GetStat() |
표준 .NET 예외 |
각 호출 후 GdPictureStatus.OK 확인 |
필수 사항이 아닙니다. 예외 사항은 자동으로 전파됩니다. |
pdf.OcrPage("eng", resourcePath, "", 200) |
result.SaveAsSearchablePdf(outputPath) |
pdf.RenderPageToGdPictureImage(200, false) |
필수 사항이 아닙니다.IronOCR자체적으로 렌더링합니다. |
pdf.SelectPage(i) |
필수 사항이 아닙니다. 기본적으로 모든 페이지가 처리됩니다. |
pdf.GetPageCount() |
result.Pages.Count |
Task.Run(() => ocr.RunOCR()) + SemaphoreSlim |
await ocr.ReadAsync(input) |
일반적인 마이그레이션 문제와 해결책
문제 1: 리팩토링 후 코드에 이미지 ID 변수가 남아 있음
GdPicture.NET: 기존 코드는 메서드 상단에 int imageId을 선언하고, try/finally를 통해 추적하며, 여러 GdPicture 호출로 전달합니다. GdPicture 호출을 교체한 후, 해당 변수와 ReleaseGdPictureImage 호출은 고아가 된 죽은 코드가 됩니다.
해결 방법: 이미지 ID 패턴 전체를 삭제합니다. 선언, try, finally, 해제 호출을 제거하고 using var input = new OcrInput() 블록으로 교체합니다. 제거해야 할 모든 정리 호출을 찾기 위해 ReleaseGdPictureImage을 검색하십시오:
grep -rn "ReleaseGdPictureImage\|imageId\|resultId" --include="*.cs" .
grep -rn "ReleaseGdPictureImage\|imageId\|resultId" --include="*.cs" .
// Remove all of this
int imageId = _imaging.CreateGdPictureImageFromFile(path);
try
{
_ocr.SetImage(imageId);
string resultId = _ocr.RunOCR();
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
// Replace with
using var input = new OcrInput();
input.LoadImage(path);
return new IronTesseract().Read(input).Text;
// Remove all of this
int imageId = _imaging.CreateGdPictureImageFromFile(path);
try
{
_ocr.SetImage(imageId);
string resultId = _ocr.RunOCR();
return _ocr.GetOCRResultText(resultId);
}
finally
{
_imaging.ReleaseGdPictureImage(imageId);
}
// Replace with
using var input = new OcrInput();
input.LoadImage(path);
return new IronTesseract().Read(input).Text;
' Remove all of this
Dim imageId As Integer = _imaging.CreateGdPictureImageFromFile(path)
Try
_ocr.SetImage(imageId)
Dim resultId As String = _ocr.RunOCR()
Return _ocr.GetOCRResultText(resultId)
Finally
_imaging.ReleaseGdPictureImage(imageId)
End Try
' Replace with
Using input As New OcrInput()
input.LoadImage(path)
Return New IronTesseract().Read(input).Text
End Using
문제 2: 배포 환경에서 리소스 폴더 경로를 찾을 수 없음
GdPicture.NET: 개발 머신에서 설정된 경로는 해결되지만, 운영 환경에서는 실패합니다. 공통 증상은 빈 결과를 반환하는 무음의 OCR 실패 또는 누락된 파일을 지칭하지 않는 일반적인 GdPictureStatus 오류입니다.
솔루션: ResourceFolder 할당을 완전히 제거합니다.IronOCR NuGet 패키지에는 영어 지원 기능이 포함되어 있습니다. 추가 언어는 NuGet 패키지로 설치됩니다. 구성하거나 배포할 파일 시스템 경로가 없습니다.
# Remove the filesystem folder from deployment artifacts
# Add language NuGet packages to the project instead
:InstallCmd dotnet add package IronOcr.Languages.French, IronOcr.Languages.German
# Remove the filesystem folder from deployment artifacts
# Add language NuGet packages to the project instead
:InstallCmd dotnet add package IronOcr.Languages.French, IronOcr.Languages.German
문제 3: GdPictureStatus 오류 처리 방식이 예외 처리 방식으로 대체됨
GdPicture.NET: 모든 작업은 즉시 확인해야 하는 GdPictureStatus 열거형 값을 반환하거나 설정합니다. 코드는 if (status != GdPictureStatus.OK) 보호 코드로 밀집되어 있습니다. 일부 상태 코드는 일반적입니다 (예: Error, InvalidParameter) 루트 원인을 확인하기 위해 문서를 참조해야 합니다.
해결책:IronOCR형식화된 .NET 예외를 발생시킵니다. 상태 확인 코드를 try/catch 블록으로 대체하세요. 표준 IOException 및 FileNotFoundException은 입력 실패를 포함합니다; IronOcr.Exceptions.OcrException은 OCR 전용 오류를 다룹니다. IronTesseract 설정 가이드 에서 권장 오류 처리 패턴을 참조하십시오.
try
{
var result = new IronTesseract().Read(imagePath);
return result.Text;
}
catch (FileNotFoundException ex)
{
_logger.LogError("Input file missing: {Path}", ex.FileName);
throw;
}
catch (IronOcr.Exceptions.OcrException ex)
{
_logger.LogError("OCR processing failed: {Message}", ex.Message);
throw;
}
try
{
var result = new IronTesseract().Read(imagePath);
return result.Text;
}
catch (FileNotFoundException ex)
{
_logger.LogError("Input file missing: {Path}", ex.FileName);
throw;
}
catch (IronOcr.Exceptions.OcrException ex)
{
_logger.LogError("OCR processing failed: {Message}", ex.Message);
throw;
}
Imports IronOcr
Imports System.IO
Try
Dim result = New IronTesseract().Read(imagePath)
Return result.Text
Catch ex As FileNotFoundException
_logger.LogError("Input file missing: {Path}", ex.FileName)
Throw
Catch ex As IronOcr.Exceptions.OcrException
_logger.LogError("OCR processing failed: {Message}", ex.Message)
Throw
End Try
이슈 4: GdPicture14 네임스페이스의 여러 파일
GdPicture.NET: 네임스페이스의 버전 번호는 프로젝트 전역 using GdPicture14; 지시문이 수십 개의 파일에 존재함을 의미합니다. 마이그레이션 후, 이들은 모두 using IronOcr;로 교체되어야 합니다.
솔루션: 모든 .cs 파일에 대해 전역 찾기 및 교체를 사용한 다음, GdPicture 참조가 남아있지 않은지 확인하십시오:
# Find all files with GdPicture namespace
grep -rln "using GdPicture" --include="*.cs" .
# After replacing, verify nothing remains
grep -rn "GdPicture14\|GdPictureOCR\|GdPictureImaging\|GdPicturePDF" --include="*.cs" .
# Find all files with GdPicture namespace
grep -rln "using GdPicture" --include="*.cs" .
# After replacing, verify nothing remains
grep -rn "GdPicture14\|GdPictureOCR\|GdPictureImaging\|GdPicturePDF" --include="*.cs" .
문제 5: TIFF 프레임 카운트 로직
GdPicture.NET: TIFF 프레임을 반복하는 코드는 종종 파일이 로드된 방법에 따라 GdPictureImaging.GetPageCount(imageId) 및 GdPicturePDF.GetPageCount()를 오가며 혼합 호출합니다. 프레임 인덱스는 1부터 시작합니다.
솔루션: 모든 프레임을 자동으로 처리하는 input.LoadImageFrames(path)을 사용하십시오. 기존 코드가 특정 프레임만 처리하는 경우, 0부터 시작하는 인덱스 배열과 함께 input.LoadImageFrames(path, frameNumbers)을 사용하십시오. 각 프레임 결과는 역시 0부터 시작하는 result.Pages를 통해 접근합니다. TIFF 입력 가이드 에는 인덱스 동작 방식이 명시적으로 설명되어 있습니다.
문제 6: 전처리에는 문서 이미지 처리 플러그인이 필요합니다.
GdPicture.NET: Deskew 및 despeckle 작업은 별도의 라이선스 구매가 필요한 GdPictureDocumentImaging 플러그인에 속합니다. 플러그인 설치를 건너뛴 팀은 페이지가 기울어져 있거나 노이즈가 많은 스캔 문서에서 OCR 정확도 문제가 발생하는 경우가 많습니다.
해결책:IronOCR전처리 방법은 기본 패키지에 포함되어 있습니다. Deskew() 및 DeNoise()을 OcrInput에 직접 추가합니다. 이미지 품질 보정 가이드는 Contrast(), Sharpen(), Binarize(), 및 심하게 열화된 스캔을 위한 DeepCleanBackgroundNoise()을 포함한 모든 사용 가능한 필터를 다룹니다:
using var input = new OcrInput();
input.LoadImage("scanned-document.tiff");
input.Deskew(); // no separate plugin license
input.DeNoise();
input.Contrast();
var result = new IronTesseract().Read(input);
using var input = new OcrInput();
input.LoadImage("scanned-document.tiff");
input.Deskew(); // no separate plugin license
input.DeNoise();
input.Contrast();
var result = new IronTesseract().Read(input);
Imports IronOcr
Using input As New OcrInput()
input.LoadImage("scanned-document.tiff")
input.Deskew() ' no separate plugin license
input.DeNoise()
input.Contrast()
Dim result = New IronTesseract().Read(input)
End Using
GdPicture .NET 마이그레이션 체크리스트
사전 마이그레이션
변경 작업을 수행하기 전에 코드베이스를 감사하여 모든 GdPicture 종속성을 찾아내십시오.
# Find all GdPicture namespace imports
grep -rn "using GdPicture" --include="*.cs" .
# Find all image ID creation points
grep -rn "CreateGdPictureImageFromFile\|CreateGdPictureImageFromStream\|RenderPageToGdPictureImage\|CloneImage" --include="*.cs" .
# Find all release calls — these map to using block boundaries
grep -rn "ReleaseGdPictureImage" --include="*.cs" .
# Find all resource folder assignments
grep -rn "ResourceFolder" --include="*.cs" .
# Find all OCR result ID accesses
grep -rn "RunOCR\|GetOCRResult\|resultId" --include="*.cs" .
# Find all GdPictureStatus checks
grep -rn "GdPictureStatus\|GetStat()" --include="*.cs" .
# Count GdPicture-dependent files
grep -rln "GdPicture14" --include="*.cs" . | wc -l
# Find all GdPicture namespace imports
grep -rn "using GdPicture" --include="*.cs" .
# Find all image ID creation points
grep -rn "CreateGdPictureImageFromFile\|CreateGdPictureImageFromStream\|RenderPageToGdPictureImage\|CloneImage" --include="*.cs" .
# Find all release calls — these map to using block boundaries
grep -rn "ReleaseGdPictureImage" --include="*.cs" .
# Find all resource folder assignments
grep -rn "ResourceFolder" --include="*.cs" .
# Find all OCR result ID accesses
grep -rn "RunOCR\|GetOCRResult\|resultId" --include="*.cs" .
# Find all GdPictureStatus checks
grep -rn "GdPictureStatus\|GetStat()" --include="*.cs" .
# Count GdPicture-dependent files
grep -rln "GdPicture14" --include="*.cs" . | wc -l
코드 수정 전에 다음 사항을 기록해 두십시오.
GdPictureImaging,GdPictureOCR, 및GdPicturePDF참조가 포함된 파일- 서로 다른 이미지 ID 생성/릴리스 쌍은 총 몇 개입니까?
- 문서 이미지 플러그인(기울임 보정, 노이즈 제거) 사용 여부
- 리소스 폴더에 있는 언어
traineddata파일 - 어떤 배포 스크립트 또는 Docker 파일이 리소스 폴더 경로를 참조합니까?
코드 마이그레이션
- 프로젝트 파일에서 모든 GdPicture NuGet 패키지를 제거합니다.
- NuGet을 통해
IronOcr설치 - 리소스 폴더에 이전에 있던 각 언어에 대한
IronOcr.Languages.*Install-Package IronOcr.License.LicenseKey = "..."을Program.cs또는Startup.cs에 추가LicenseManager.RegisterKEY()호출 및 라이선스 매니저 객체 제거- 모든
GdPictureImaging필드 선언 및 생성자 초기화 제거 - 모든
GdPictureOCR필드 선언 및ResourceFolder할당 제거 - OCR 워크플로를 위해 사용된 모든
GdPicturePDF필드 선언 제거 - 각
int imageId = _imaging.CreateGdPictureImage*(...)블록을using var input = new OcrInput();로 교체하십시오. 입력.Load*(...)` - 각
_ocr.SetImage(imageId);를 교체합니다. _ocr.Language = "..."; string resultId = _ocr.RunOCR();withvar result = new IronTesseract().Read(input);` _ocr.GetOCRResultText(resultId)을result.Text으로 교체- 모든
_imaging.ReleaseGdPictureImage(imageId)호출 제거 GdPictureStatus검사를 try/catch 블록으로 교체- TIFF 프레임 루프를
input.LoadImageFrames(path)으로 교체 pdf.OcrPage(...)+pdf.SaveToFile(...)를result.SaveAsSearchablePdf(outputPath)으로 교체- 배포 스크립트 및 Docker 이미지에서 리소스 폴더 경로를 제거합니다.
- 모든 영향을 받은 파일에서
using GdPicture14;을using IronOcr;으로 업데이트
마이그레이션 이후
모든 코드 변경을 완료한 후, 프로덕션 환경에 배포하기 전에 다음 사항을 확인하십시오.
- 단일 이미지 OCR이 제거된 컴포넌트에서
NullReferenceException없이 예상 문자 반환 - 다중 페이지 TIFF 처리는 모든 프레임을 포함하고,
result.Pages을 통해 페이지별 텍스트를 생성합니다. - PDF OCR은 50개 이상의 문서를 일괄 처리해도 메모리 사용량 증가 없이 모든 페이지를 처리합니다.
- 스트림 입력은 중간 파일 작성 없이
MemoryStream및FileStream을 허용합니다. - 비동기 OCR은 스레드 풀을 차단하지 않고 ASP.NET Core 요청 처리기와 통합됩니다.
Parallel.ForEach으로 병렬 일괄 처리 시 모든 스레드에 대해 정확한 결과를 생성합니다.- 모든 언어 팩(프랑스어, 독일어 등) NuGet 패키지를 통해 올바르게 활성화됩니다.
- 전처리 필터(기울기 보정, 노이즈 제거)는 스캔 문서의 정확도를 향상시킵니다.
- 검색 가능한 PDF 출력물은 PDF 뷰어 및 텍스트 검색 애플리케이션에서 읽을 수 있습니다.
- 마이그레이션을 거친 후, 어떠한
.cs파일에도 GdPicture 네임스페이스 참조가 남아있지 않음 - 메모리 프로파일은 지속적인 부하 상태에서도 안정적인 사용량을 보여줍니다 (이미지 할당 누수 없음).
- 파일 시스템 경로 오류 없이 Linux 및 Docker 대상 환경에 배포가 성공적으로 완료됩니다.
IronOCR로 마이그레이션할 때의 주요 이점
컴파일러가 메모리 안전성을 보장합니다. GdPicture에서 개발자가 수동으로 관리해야 했던 이미지 ID 수명 주기가 완전히 사라집니다. OcrInput은 IDisposable을 구현하며, using 블록은 범위 끝에서의 정리를 강제합니다 - 예외 경로를 포함하여. 유지해야 할 List<int>이 없으며, 기억해야 할 finally 블록도 없고, 누락된 해제 호출로 인한 프로덕션 메모리 인시던트도 없습니다.
모든 OCR 상황을 위한 단일 패키지. 이미지 OCR, PDF OCR, 다중 페이지 TIFF 처리, 검색 가능한 PDF 생성, 전처리 필터, 바코드 읽기 및 125+ 언어 팩은 모두 IronOcr NuGet 패키지 및 해당 언어 동반자로 제공됩니다. 일반적인 워크플로우를 구현하기 위해 두 번째 또는 세 번째 라이선스를 구매해야 하는 기능 제한은 없습니다. 전체 기능 목록은 IronOCR 기능 개요를 참조하십시오.
네이티브 비동기 및 스레드 안전성. ReadAsync은 진정한 비동기 메서드로, Task.Run 래퍼가 아닙니다. IronTesseract는 동기화 없이 스레드 간에 안전하게 사용할 수 있습니다. 과거에 스레드 별 구성 요소 초기화 및 세마포어 직렬화가 필요했던 문서 처리 서비스가 Parallel.ForEach을 통해 단일 공유 인스턴스로 축소됩니다. 비동기 OCR 가이드는 ASP.NET Core 와 호스팅 서비스 패턴을 모두 다룹니다.
배포 구성이 전혀 필요 없습니다.IronOCR파일 시스템 경로, 외부 언어 파일, 네이티브 바이너리 배치, 배포 스크립트가 필요하지 않습니다. NuGet 복원 단계에서 애플리케이션에 필요한 모든 것을 제공합니다. Docker 이미지, Azure App Service 배포 및 Linux 서버는 모두 개발 환경과 동일하게 작동합니다. Docker 배포 가이드 와 Azure 배포 가이드는 실제 운영 환경에 적합한 구성을 보여줍니다.
버전 안정 네임스페이스. using IronOcr는 주요 버전 릴리스 간에 변경되지 않았습니다. NuGet 버전 번호는 표준 패키지 관리 모델을 통해 버전 관리를 처리합니다. 향후 주요 업그레이드 시 코드베이스 전체에서 네임스페이스 가져오기를 찾아 바꾸는 작업이 필요하지 않습니다.
예측 가능한 영구 라이선스. IronOCR는 $999 (Lite), $1,499 (Plus), $2,999 (Professional), 및 $5,999 (Unlimited) - 1년 업데이트를 포함하는 일회성 영구 구매로 가격이 책정되며, 연간 갱신 옵션이 제공됩니다. 모든 기능은 모든 등급에서 이용 가능합니다. 기능별 플러그인 비용이나 페이지당 비용이 없으며, 첫 해 이후에는 유지보수 의무도 없습니다. 이전에 OCR 전용 워크플로우를 위해 GdPicture 플러그인 라이선스에 8,000달러 이상을 지출했던 팀은 첫 번째 라이선스 주기 내에 그 비용을 회수할 수 있습니다. 자세한 가격 정보는 IronOCR 라이선스 페이지 에서 확인할 수 있습니다.
자주 묻는 질문
GdPicture.NET OCR에서 IronOCR로 마이그레이션해야 하는 이유는 무엇인가요?
일반적인 동인으로는 COM 상호 운용의 복잡성 제거, 파일 기반 라이선스 관리 대체, 페이지당 과금 방지, Docker/컨테이너 배포 활성화, 표준 .NET 툴링과 통합되는 NuGet 네이티브 워크플로 채택 등이 있습니다.
GdPicture.NET OCR에서 IronOCR로 마이그레이션할 때 주요 코드 변경 사항은 무엇인가요?
GdPicture.NET 초기화 시퀀스를 IronTesseract 인스턴스화로 대체하고, COM 수명 주기 관리(명시적 생성/로드/폐쇄 패턴)를 제거하고, 결과 속성 이름을 업데이트합니다. 그 결과 상용구 줄이 크게 줄어듭니다.
마이그레이션을 시작하려면 IronOCR을 어떻게 설치하나요?
패키지 관리자 콘솔에서 '설치-패키지 IronOcr'을 실행하거나 CLI에서 '닷넷 추가 패키지 IronOcr'을 실행하세요. 언어 팩은 별도의 패키지입니다: 예를 들어 프랑스어의 경우 '닷넷 추가 패키지 IronOcr.Languages.French'를 실행합니다.
IronOCR은 표준 비즈니스 문서에 대해 GdPicture.NET OCR의 OCR 정확도와 일치하나요?
IronOCR은 송장, 계약서, 영수증, 타이핑된 양식 등 표준 비즈니스 콘텐츠에 대해 높은 정확도를 달성합니다. 이미지 전처리 필터(데스큐, 노이즈 제거, 대비 향상)는 품질이 저하된 입력에 대한 인식률을 더욱 향상시킵니다.
IronOCR은 GdPicture.NET OCR이 별도로 설치하는 언어 데이터를 어떻게 처리하나요?
IronOCR의 언어 데이터는 NuGet 패키지로 배포됩니다. '닷넷 추가 패키지 IronOcr.Languages.German'은 독일어 지원을 설치합니다. 수동 파일 배치나 디렉터리 경로는 필요하지 않습니다.
GdPicture.NET OCR에서 IronOCR로 마이그레이션하려면 배포 인프라를 변경해야 하나요?
IronOCR은 GdPicture.NET OCR보다 인프라 변경이 더 적게 필요합니다. SDK 바이너리 경로, 라이선스 파일 배치 또는 라이선스 서버 구성이 필요하지 않습니다. NuGet 패키지에는 완전한 OCR 엔진이 포함되어 있으며 라이선스 키는 애플리케이션 코드에 설정된 문자열입니다.
마이그레이션 후 IronOCR 라이선싱은 어떻게 구성하나요?
애플리케이션 시작 코드에 IronOcr.License.LicenseKey = "YOUR-KEY"를 할당하세요. Docker 또는 Kubernetes에서 키를 환경 변수로 저장하고 시작 시 키를 읽습니다. 트래픽을 수락하기 전에 License.IsValidLicense를 사용하여 유효성을 검사하세요.
IronOCR은 GdPicture.NET과 동일한 방식으로 PDF를 처리할 수 있나요?
예. IronOCR은 원본 및 스캔한 PDF를 모두 읽습니다. IronTesseract를 인스턴스화하고, 입력이 PDF 경로 또는 OcrPdfInput인 경우 ocr.Read(input)를 호출하고, OcrResult 페이지를 반복하면 됩니다. 별도의 PDF 렌더링 파이프라인이 필요하지 않습니다.
IronOCR은 대량 처리에서 스레딩을 어떻게 처리하나요?
IronTesseract는 스레드별로 인스턴스화해도 안전합니다. Parallel.ForEach 또는 Task 풀에서 스레드당 하나의 인스턴스를 스핀업하고, OCR을 동시에 실행하고, 완료되면 각 인스턴스를 폐기하면 됩니다. 전역 상태나 잠금이 필요하지 않습니다.
IronOCR은 텍스트 추출 후 어떤 출력 형식을 지원하나요?
IronOCR은 텍스트, 단어 좌표, 신뢰도 점수, 페이지 구조를 포함한 구조화된 결과를 반환합니다. 내보내기 옵션에는 일반 텍스트, 검색 가능한 PDF, 다운스트림 처리를 위한 구조화된 결과 개체가 포함됩니다.
워크로드 확장을 위해 IronOCR 가격이 GdPicture.NET OCR보다 더 예측 가능한가요?
IronOCR은 페이지당 또는 볼륨당 요금이 없는 정액제 영구 라이선스를 사용합니다. 10,000페이지를 처리하든 1,000만 페이지를 처리하든 라이선스 비용은 일정하게 유지됩니다. 볼륨 및 팀 라이선스 옵션은 IronOCR 가격 페이지에서 확인할 수 있습니다.
GdPicture.NET OCR에서 IronOCR로 마이그레이션한 후 기존 테스트는 어떻게 되나요?
추출된 텍스트 콘텐츠에 대해 어설트하는 테스트는 마이그레이션 후에도 계속 통과해야 합니다. API 호출 패턴 또는 COM 개체 수명 주기의 유효성을 검사하는 테스트는 IronOCR의 더 간단한 초기화 및 결과 모델을 반영하도록 업데이트해야 합니다.

