Migrating from Dynamsoft OCR to IronOCR
このガイドでは、 .NET開発者向けに、OCR.spaceのREST API統合を、単一のNuGetパッケージとして提供されるネイティブ.NETライブラリであるIronOCRに置き換える手順を説明します。 本書では、パッケージのスワップ、名前空間のクリーンアップ、およびRESTからローカルへの移行に特有の4つの具体的なコード移行シナリオ(マルチパートアップロードの削除、base64エンコーディングの削除、OCRエンジンの選択の置き換え、構造化データの抽出)について解説しています。 フェーズ1の比較記事を読んだ開発者は、このガイドが機能比較ではなく、移行の具体的な手順に焦点を当てていることに気づくでしょう。
OCR.spaceから移行する理由
OCR.spaceは、まさにニッチなニーズを満たしています。何もインストールすることなく、午後のひとときでOCRをテストしたい開発者にとって、費用のかからない実験環境を提供しているのです。 問題は、無料プランはプロトタイプ作成向けに設計されており、製品版向けには設計されていない点です。 .NETアプリケーションが実際の文書量、コンプライアンス要件、またはチーム開発へと移行すると、OCR.space統合のあらゆる特性がアプリケーションにとって不利に働くようになります。
NuGetパッケージがないということは、SDKもIntelliSenseも利用できないということです。OCR.spaceはRESTエンドポイントとドキュメントを提供します。 .NETとの統合(HTTPクライアントの構築、リクエストのシリアル化、レスポンスの逆シリアル化、エラー処理、再試行ロジックなど)は、すべて開発者の責任となります。 これは些細な不便ではない。 最小限の実行可能なクライアントは、最初のビジネスロジックメソッドを記述する前に、80行以上のインフラストラクチャコードを備えている。 そのコードは、あらゆる.NETコードベースにおけるあらゆるOCR.space統合において区別がなく、時間の経過とともにバグやメンテナンスの負担が蓄積されていく。
レート制限は、本番環境のアプリケーションに人為的な上限を課すものです。無料プランでは、IPアドレスごとに1分あたり60リクエスト、1日あたり500リクエストに制限されています。 どちらの限界も、乗り越えられない壁だ。 午前0時から翌日の午前0時までの間に500件を超えるリクエストを送信したアプリケーションは、カウンターがリセットされるまでエラー応答を受け取ります。 共有オフィスネットワークや共有CI/CD環境で稼働する本番システムは、営業時間終了前に1日の利用制限を使い果たしてしまう可能性があります。
ドキュメントは、通信のたびにお客様のインフラストラクチャから送信されます。OCR.spaceにはオンプレミス展開オプションはありません。 すべてのリクエストは、請求書、医療記録、契約書、身分証明書などの文書をOCR.spaceのクラウドサーバーに送信します。 HIPAA、GDPR、および機密文書の第三者への送信を禁止する社内データ分類ポリシーにより、契約上の規制に関係なく、OCR.spaceはアーキテクチャ的に互換性がありません。
無料プランでは、透かし入りの検索可能なPDFが生成されます。文書アーカイブシステム、コンプライアンスプラットフォーム、顧客向け文書ポータルなど、成果物として検索可能なPDFを出力するアプリケーションは、OCR.spaceの無料プランをこの目的で使用することはできません。 ウォーターマークは出力されるPDFファイルに埋め込まれており、有料プランに加入しない限り削除できません。
**購読料金は利用量に応じて上昇します。 OCR.space PROの年額$144のレベルは6年目までにIronOCRの$999のエントリ価格を超えます。ドキュメントのボリュームが増加し、無料階層のしきい値を超えるプロジェクトは、固定の永続ライセンスに対する積み重なるサブスクリプションコストに直面します。 $999 Liteライセンスは、1人の開発者および1つのデプロイ場所をカバーしており、任意のボリュームでのリクエストごとの料金はありません。 ライセンスレベルの詳細については、 IronOCRのライセンスページをご覧ください。
基本的な問題
OCR.spaceでは、1つのドキュメントを処理する前に、完全なHTTPクライアントを構築する必要があります。
// OCR.space: 80+ lines of infrastructure before business logic
public class OcrSpaceApiClient : IDisposable
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
private readonly SemaphoreSlim _rateLimiter; // You implement this
public OcrSpaceApiClient(string apiKey)
{
_httpClient = new HttpClient();
_httpClient.Timeout = TimeSpan.FromSeconds(120);
_rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/min
}
// ... 70+ more lines of HTTP plumbing follow
}
// OCR.space: 80+ lines of infrastructure before business logic
public class OcrSpaceApiClient : IDisposable
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
private readonly SemaphoreSlim _rateLimiter; // You implement this
public OcrSpaceApiClient(string apiKey)
{
_httpClient = new HttpClient();
_httpClient.Timeout = TimeSpan.FromSeconds(120);
_rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/min
}
// ... 70+ more lines of HTTP plumbing follow
}
Imports System
Imports System.Net.Http
Imports System.Threading
' OCR.space: 80+ lines of infrastructure before business logic
Public Class OcrSpaceApiClient
Implements IDisposable
Private ReadOnly _httpClient As HttpClient
Private ReadOnly _apiKey As String
Private ReadOnly _rateLimiter As SemaphoreSlim ' You implement this
Public Sub New(apiKey As String)
_httpClient = New HttpClient()
_httpClient.Timeout = TimeSpan.FromSeconds(120)
_rateLimiter = New SemaphoreSlim(60, 60) ' Free tier: 60/min
End Sub
' ... 70+ more lines of HTTP plumbing follow
Public Sub Dispose() Implements IDisposable.Dispose
_httpClient.Dispose()
_rateLimiter.Dispose()
End Sub
End Class
IronOCRはNuGetパッケージです。 クライアント側のコード全体は既に作成済みです。
// IronOCR: no client to build, no rate limiter to manage
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
// IronOCR: no client to build, no rate limiter to manage
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
Imports IronOcr
' IronOCR: no client to build, no rate limiter to manage
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim result = New IronTesseract().Read("document.jpg")
Console.WriteLine(result.Text)
IronOCRとOCR.spaceの比較:機能比較
以下の表は、OCR.spaceの概念と制約をIronOCRの同等の概念と制約に直接対応させたものです。
| フィーチャー | OCR.space | IronOCR |
|---|---|---|
| NuGetパッケージ | なし — REST APIのみ | IronOcr — ネイティブ.NET |
| SDK/インテリセンス | なし — 手動JSON | 完全な型付きAPI |
| カスタムモデルが必要 | なし | なし |
| 処理場所 | OCR.space クラウドサーバー | ローカル — 処理中 |
| インターネット依存 | すべての通話に必要 | None |
| エアギャップ展開 | サポートされていません | 完全サポート |
| レート制限 | 1分60円、1日500円(無料) | None |
| ファイルサイズ制限 | 5MB(無料枠) | 使用可能なメモリのみ |
| PDF入力 | はい(制限あり、5MB) | はい、ネイティブ、サイズ制限なし |
| 検索可能なPDF出力 | 無料プランでは透かしが入ります | クリーンな出力、全階層 |
| 自動前処理 | サーバーサイド、開発者による制御は不可 | 傾き補正、ノイズ除去、コントラスト調整、二値化、シャープ化 |
| 言語サポート | 約25言語 | NuGet言語パック経由で125種類以上 |
| 文書ごとに複数の言語 | サポートされていません | はい — OcrLanguage.French + OcrLanguage.German |
| 構造化された出力(単語、行) | プレーンテキストのみ | 座標付きのページ、段落、行、単語 |
| 単語レベルの信頼度スコア | 不可 | はい — word.Confidence |
| 地域ベースのOCR | サポートされていません | はい — CropRectangle |
| バーコード読み取り | サポートされていません | はい — ReadBarCodes = true |
| 検索可能なPDF生成 | 透かし入り(無料)、透かしなし(有料) | クリーンな出力 - すべてのライセンスティアに対応 |
| HIPAA / GDPRとの互換性 | リスク — 外部に送信されるデータ | はい、外部データ送信はありません |
| 価格設定モデル | 月額サブスクリプション | 一度限りの永久 |
| エントリー価格 | 月額12ドル(年間144ドル) | $999 一回限り |
| .NET互換性 | HttpClient — 任意 for .NET |
.NET 4.6.2以降、 .NET 5/6/7/8/9 |
| クロスプラットフォーム展開 | 発信インターネット接続が必要です | Windows、Linux、macOS、Docker、Azure、AWS |
クイックスタート:OCR.spaceからIronOCRへの移行
ステップ 1: NuGet パッケージを置き換える
OCR.spaceにはアンインストールするNuGetパッケージがありません。 プロジェクトからすべてのOCR.space関連のインフラストラクチャコードを削除: SemaphoreSlimレートリミッター、カスタム結果モデル、カスタム例外タイプ。これらすべてがIronOCRのNuGetパッケージに置き換えられます。
IronOCR NuGetページからIronOCRをインストールしてください。
dotnet add package IronOcr
ステップ 2: 名前空間の更新
OCR.spaceのHTTPおよびJSON名前空間を削除します。 IronOCR名前空間を追加します。
// Before (OCR.space — manually written infrastructure)
using System.Net.Http;
using System.Text.Json;
using System.Threading;
// After (IronOCR)
using IronOcr;
// Before (OCR.space — manually written infrastructure)
using System.Net.Http;
using System.Text.Json;
using System.Threading;
// After (IronOCR)
using IronOcr;
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading
Imports IronOcr
ステップ 3: ライセンスの初期化
ライセンスの初期化は、アプリケーション起動時に一度だけ行うようにしてください(リクエストごとではなく)。
// Program.cs or application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
// Program.cs or application startup
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
Imports IronOcr
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
コード移行の例
MultipartFormDataContent ファイルアップロードの置き換え
OCR.spaceはファイルバイトとAPIキーを使ってMultipartFormDataContentを構築し、クラウドエンドポイントにPOSTする必要があります。 このドキュメントは、呼び出しのたびにインフラストラクチャに残ります。
OCR.spaceのアプローチ:
// MultipartFormDataContent: file upload to cloud on every request
public async Task<string> UploadAndExtract(string imagePath)
{
using var content = new MultipartFormDataContent();
var imageBytes = File.ReadAllBytes(imagePath);
// Document is transmitted to OCR.space servers here
content.Add(new ByteArrayContent(imageBytes), "file", Path.GetFileName(imagePath));
content.Add(new StringContent(_apiKey), "apikey");
content.Add(new StringContent("eng"), "language");
content.Add(new StringContent("2"), "OCREngine"); // Select Engine 2
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
// Navigate JSON tree manually — no typed result
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
// MultipartFormDataContent: file upload to cloud on every request
public async Task<string> UploadAndExtract(string imagePath)
{
using var content = new MultipartFormDataContent();
var imageBytes = File.ReadAllBytes(imagePath);
// Document is transmitted to OCR.space servers here
content.Add(new ByteArrayContent(imageBytes), "file", Path.GetFileName(imagePath));
content.Add(new StringContent(_apiKey), "apikey");
content.Add(new StringContent("eng"), "language");
content.Add(new StringContent("2"), "OCREngine"); // Select Engine 2
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
response.EnsureSuccessStatusCode();
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
// Navigate JSON tree manually — no typed result
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class YourClassName
Private _apiKey As String
Private _httpClient As HttpClient
Public Async Function UploadAndExtract(imagePath As String) As Task(Of String)
Using content As New MultipartFormDataContent()
Dim imageBytes = File.ReadAllBytes(imagePath)
' Document is transmitted to OCR.space servers here
content.Add(New ByteArrayContent(imageBytes), "file", Path.GetFileName(imagePath))
content.Add(New StringContent(_apiKey), "apikey")
content.Add(New StringContent("eng"), "language")
content.Add(New StringContent("2"), "OCREngine") ' Select Engine 2
Dim response = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
response.EnsureSuccessStatusCode()
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc = JsonDocument.Parse(json)
' Navigate JSON tree manually — no typed result
Return doc.RootElement _
.GetProperty("ParsedResults")(0) _
.GetProperty("ParsedText") _
.GetString() OrElse String.Empty
End Using
End Using
End Function
End Class
IronOCRのアプローチ:
// OcrInput replaces the entire upload + JSON pipeline
public string ExtractFromFile(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath); // Stays local — no network call
var ocr = new IronTesseract();
var result = ocr.Read(input);
return result.Text; // Typed property — no JSON navigation
}
// OcrInput replaces the entire upload + JSON pipeline
public string ExtractFromFile(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath); // Stays local — no network call
var ocr = new IronTesseract();
var result = ocr.Read(input);
return result.Text; // Typed property — no JSON navigation
}
Imports IronTesseract
Public Function ExtractFromFile(ByVal imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath) ' Stays local — no network call
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
Return result.Text ' Typed property — no JSON navigation
End Using
End Function
MultipartFormDataContentのローカル代替です。 一貫したAPIを通じて、ファイルパス、バイト配列、ストリーム、および複数ページのTIFFファイルを受け付けます。 HttpClient、APIキーの注入、JSONナビゲーションはすべて完全に消えます。 画像入力の手順では、サポートされているすべての入力フォーマットについて説明しています。
Base64エンコーディングの排除
OCR.spaceの統合がファイルアップロードパラメータの代わりにFormUrlEncodedContentに埋め込みます。 IronOCRはエンコード処理なしで生のバイトデータを直接受け入れます。
OCR.spaceのアプローチ:
// base64Image parameter: read → encode → embed in form → POST → parse
public async Task<string> ExtractViaBase64(string imagePath)
{
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
string base64Image = Convert.ToBase64String(imageBytes); // Mandatory encoding step
// Embed as data URI — adds 33% overhead to payload size
string mimeType = "image/png";
string dataUri = $"data:{mimeType};base64,{base64Image}";
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", dataUri),
new KeyValuePair<string, string>("language", "eng"),
new KeyValuePair<string, string>("isOverlayRequired", "false")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
// base64Image parameter: read → encode → embed in form → POST → parse
public async Task<string> ExtractViaBase64(string imagePath)
{
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
string base64Image = Convert.ToBase64String(imageBytes); // Mandatory encoding step
// Embed as data URI — adds 33% overhead to payload size
string mimeType = "image/png";
string dataUri = $"data:{mimeType};base64,{base64Image}";
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", dataUri),
new KeyValuePair<string, string>("language", "eng"),
new KeyValuePair<string, string>("isOverlayRequired", "false")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class ImageProcessor
Private _apiKey As String
Private _httpClient As HttpClient
Public Sub New(apiKey As String, httpClient As HttpClient)
_apiKey = apiKey
_httpClient = httpClient
End Sub
' base64Image parameter: read → encode → embed in form → POST → parse
Public Async Function ExtractViaBase64(imagePath As String) As Task(Of String)
Dim imageBytes As Byte() = Await File.ReadAllBytesAsync(imagePath)
Dim base64Image As String = Convert.ToBase64String(imageBytes) ' Mandatory encoding step
' Embed as data URI — adds 33% overhead to payload size
Dim mimeType As String = "image/png"
Dim dataUri As String = $"data:{mimeType};base64,{base64Image}"
Dim formContent As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
New KeyValuePair(Of String, String)("apikey", _apiKey),
New KeyValuePair(Of String, String)("base64Image", dataUri),
New KeyValuePair(Of String, String)("language", "eng"),
New KeyValuePair(Of String, String)("isOverlayRequired", "false")
})
Dim response As HttpResponseMessage = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent)
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc As JsonDocument = JsonDocument.Parse(json)
Return doc.RootElement _
.GetProperty("ParsedResults")(0) _
.GetProperty("ParsedText") _
.GetString() OrElse String.Empty
End Using
End Function
End Class
IronOCRのアプローチ:
// LoadImage(bytes): raw bytes accepted directly — no encoding
public string ExtractFromBytes(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // なし Base64, no data URI, no overhead
var result = new IronTesseract().Read(input);
return result.Text;
}
// LoadImage(bytes): raw bytes accepted directly — no encoding
public string ExtractFromBytes(byte[] imageBytes)
{
using var input = new OcrInput();
input.LoadImage(imageBytes); // なし Base64, no data URI, no overhead
var result = new IronTesseract().Read(input);
return result.Text;
}
Imports IronOcr
Public Function ExtractFromBytes(imageBytes As Byte()) As String
Using input As New OcrInput()
input.LoadImage(imageBytes) ' なし Base64, no data URI, no overhead
Dim result = New IronTesseract().Read(input)
Return result.Text
End Using
End Function
IronOCRにはHTTPトランスポート層がないため、Base64エンコードのステップは存在しません。 生のバイトはOcrInput.LoadImage()に直接入ります。 データURIのオーバーヘッド(Base64エンコードによってペイロードサイズが約33%増加する)も解消される。 ストリーム入力ガイドは、Stream入力に同じパターンを示しており、これはバイトがファイルではなくアップロードハンドラやメモリバッファから来る場合に便利です。
OCRエンジンの選択を画像前処理に置き換える
OCR.spaceはOCREngineフォームパラメータを通じて2つのOCRエンジンを公開します: エンジン1は、複雑なレイアウトでは精度が低いがより高速です; エンジン2は、ほとんどの文書タイプにおいて処理速度は遅いものの、精度は高い。開発者は、文書の特性に基づいて、呼び出しごとにエンジンを選択する。 IronOCRは、最適化された単一のTesseract 5エンジンを実行しますが、エンジンモードを切り替えるのではなく、根本原因である文書の品質に対処する明示的な前処理フィルターを提供します。
OCR.spaceのアプローチ:
// OCREngine parameter: binary choice, no control over why accuracy differs
public async Task<string> ExtractWithEngineSelection(
string imagePath,
bool useHighAccuracyEngine = true)
{
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
new KeyValuePair<string, string>("language", "eng"),
// Engine 1 = faster, Engine 2 = higher accuracy — binary choice only
new KeyValuePair<string, string>("OCREngine", useHighAccuracyEngine ? "2" : "1"),
new KeyValuePair<string, string>("scale", "true"),
new KeyValuePair<string, string>("detectOrientation", "true")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
// OCREngine parameter: binary choice, no control over why accuracy differs
public async Task<string> ExtractWithEngineSelection(
string imagePath,
bool useHighAccuracyEngine = true)
{
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
new KeyValuePair<string, string>("language", "eng"),
// Engine 1 = faster, Engine 2 = higher accuracy — binary choice only
new KeyValuePair<string, string>("OCREngine", useHighAccuracyEngine ? "2" : "1"),
new KeyValuePair<string, string>("scale", "true"),
new KeyValuePair<string, string>("detectOrientation", "true")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent);
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class OCRService
Private _apiKey As String
Private _httpClient As HttpClient
Public Sub New(apiKey As String, httpClient As HttpClient)
_apiKey = apiKey
_httpClient = httpClient
End Sub
' OCREngine parameter: binary choice, no control over why accuracy differs
Public Async Function ExtractWithEngineSelection(imagePath As String, Optional useHighAccuracyEngine As Boolean = True) As Task(Of String)
Dim imageBytes As Byte() = Await File.ReadAllBytesAsync(imagePath)
Dim base64 As String = Convert.ToBase64String(imageBytes)
Dim formContent As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
New KeyValuePair(Of String, String)("apikey", _apiKey),
New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}"),
New KeyValuePair(Of String, String)("language", "eng"),
' Engine 1 = faster, Engine 2 = higher accuracy — binary choice only
New KeyValuePair(Of String, String)("OCREngine", If(useHighAccuracyEngine, "2", "1")),
New KeyValuePair(Of String, String)("scale", "true"),
New KeyValuePair(Of String, String)("detectOrientation", "true")
})
Dim response As HttpResponseMessage = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", formContent)
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc As JsonDocument = JsonDocument.Parse(json)
Return doc.RootElement _
.GetProperty("ParsedResults")(0) _
.GetProperty("ParsedText") _
.GetString() OrElse String.Empty
End Using
End Function
End Class
IronOCRのアプローチ:
// Preprocessing pipeline: fix the document, not the engine selection
public string ExtractWithPreprocessing(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
// Apply filters that match the document's specific quality issues
input.Deskew(); // Correct rotation — replaces detectOrientation
input.DeNoise(); // Remove noise from fax/photocopier artifacts
input.Contrast(); // Enhance contrast on low-quality scans
input.Scale(200); // Upscale small or low-DPI images
var ocr = new IronTesseract();
var result = ocr.Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%"); // なし equivalent in OCR.space
return result.Text;
}
// Preprocessing pipeline: fix the document, not the engine selection
public string ExtractWithPreprocessing(string imagePath)
{
using var input = new OcrInput();
input.LoadImage(imagePath);
// Apply filters that match the document's specific quality issues
input.Deskew(); // Correct rotation — replaces detectOrientation
input.DeNoise(); // Remove noise from fax/photocopier artifacts
input.Contrast(); // Enhance contrast on low-quality scans
input.Scale(200); // Upscale small or low-DPI images
var ocr = new IronTesseract();
var result = ocr.Read(input);
Console.WriteLine($"Confidence: {result.Confidence}%"); // なし equivalent in OCR.space
return result.Text;
}
Imports IronOcr
Public Class PreprocessingPipeline
' Preprocessing pipeline: fix the document, not the engine selection
Public Function ExtractWithPreprocessing(imagePath As String) As String
Using input As New OcrInput()
input.LoadImage(imagePath)
' Apply filters that match the document's specific quality issues
input.Deskew() ' Correct rotation — replaces detectOrientation
input.DeNoise() ' Remove noise from fax/photocopier artifacts
input.Contrast() ' Enhance contrast on low-quality scans
input.Scale(200) ' Upscale small or low-DPI images
Dim ocr As New IronTesseract()
Dim result = ocr.Read(input)
Console.WriteLine($"Confidence: {result.Confidence}%") ' なし equivalent in OCR.space
Return result.Text
End Using
End Function
End Class
OCR.spaceのOCREngineパラメータはドキュメントの品質のプロキシです — エンジン1がドキュメントで失敗した場合、開発者は異なるアルゴリズムが補うことを期待してエンジン2に切り替えます。 IronOCRの前処理パイプラインは品質の問題に直接対処します: Contrast()は低コントラストの複写からテキストを回復します。 結果のOCREngineスイッチングでは提供できません。画像品質矯正ガイドとフィルタウィザードは、各フィルタが異なるドキュメントタイプに与える影響を文書化しています。
通話ごとの言語切り替えなしの多言語OCR
OCR.spaceはAPI呼び出しごとに1つのlanguageパラメータを受け入れます。 複数の言語を含む文書の場合、各言語ごとに個別の処理が必要となり、結果は手動で統合されます。 IronOCRは、単一の読み取り操作でOcrLanguage値に使用し、複数の言語を同時に処理します。
OCR.spaceのアプローチ:
// OCR.space: one language per call — multi-language requires multiple requests
public async Task<string> ExtractMultiLanguage(string imagePath)
{
// First pass: English
string englishText = await ExtractWithLanguage(imagePath, "eng");
// Second pass: French (consumes another rate-limit slot, another API call)
string frenchText = await ExtractWithLanguage(imagePath, "fre");
// Manually merge results — no way to know which text belongs to which language
return $"{englishText}\n{frenchText}";
}
private async Task<string> ExtractWithLanguage(string imagePath, string langCode)
{
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
new KeyValuePair<string, string>("language", langCode) // One language per call
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
// OCR.space: one language per call — multi-language requires multiple requests
public async Task<string> ExtractMultiLanguage(string imagePath)
{
// First pass: English
string englishText = await ExtractWithLanguage(imagePath, "eng");
// Second pass: French (consumes another rate-limit slot, another API call)
string frenchText = await ExtractWithLanguage(imagePath, "fre");
// Manually merge results — no way to know which text belongs to which language
return $"{englishText}\n{frenchText}";
}
private async Task<string> ExtractWithLanguage(string imagePath, string langCode)
{
byte[] imageBytes = await File.ReadAllBytesAsync(imagePath);
string base64 = Convert.ToBase64String(imageBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}"),
new KeyValuePair<string, string>("language", langCode) // One language per call
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
string json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
return doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("ParsedText")
.GetString() ?? string.Empty;
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class OCRSpace
Private _apiKey As String
Private _httpClient As HttpClient
Public Async Function ExtractMultiLanguage(imagePath As String) As Task(Of String)
' First pass: English
Dim englishText As String = Await ExtractWithLanguage(imagePath, "eng")
' Second pass: French (consumes another rate-limit slot, another API call)
Dim frenchText As String = Await ExtractWithLanguage(imagePath, "fre")
' Manually merge results — no way to know which text belongs to which language
Return $"{englishText}{vbLf}{frenchText}"
End Function
Private Async Function ExtractWithLanguage(imagePath As String, langCode As String) As Task(Of String)
Dim imageBytes As Byte() = Await File.ReadAllBytesAsync(imagePath)
Dim base64 As String = Convert.ToBase64String(imageBytes)
Dim content = New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
New KeyValuePair(Of String, String)("apikey", _apiKey),
New KeyValuePair(Of String, String)("base64Image", $"data:image/png;base64,{base64}"),
New KeyValuePair(Of String, String)("language", langCode) ' One language per call
})
Dim response = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc = JsonDocument.Parse(json)
Return doc.RootElement _
.GetProperty("ParsedResults")(0) _
.GetProperty("ParsedText") _
.GetString() OrElse String.Empty
End Using
End Function
End Class
IronOCRのアプローチ:
// IronOCR: multiple languages in a single read — one pass, correct output
public string ExtractMultiLanguage(string imagePath)
{
var ocr = new IronTesseract();
// Combine languages with + operator — processed simultaneously
ocr.Language = OcrLanguage.English + OcrLanguage.French + OcrLanguage.German;
var result = ocr.Read(imagePath);
return result.Text; // Correctly interleaved multilingual output
}
// IronOCR: multiple languages in a single read — one pass, correct output
public string ExtractMultiLanguage(string imagePath)
{
var ocr = new IronTesseract();
// Combine languages with + operator — processed simultaneously
ocr.Language = OcrLanguage.English + OcrLanguage.French + OcrLanguage.German;
var result = ocr.Read(imagePath);
return result.Text; // Correctly interleaved multilingual output
}
Imports IronOcr
Public Function ExtractMultiLanguage(imagePath As String) As String
Dim ocr As New IronTesseract()
' Combine languages with + operator — processed simultaneously
ocr.Language = OcrLanguage.English + OcrLanguage.French + OcrLanguage.German
Dim result = ocr.Read(imagePath)
Return result.Text ' Correctly interleaved multilingual output
End Function
OCR.spaceの"1回の呼び出しで1言語のみ"という制約により、開発者はN言語のドキュメントに対してN回のAPI呼び出しを行い、結果をどのように整合させるかを推測せざるを得ない。 IronOCRは言語モデルを単一のエンジン処理に統合することで、後処理なしで正しくインターリーブされた出力を生成します。 言語パックはNuGetパッケージとしてインストールされます — IronOcr.Languages.German、など — そしてオフラインで動作します。複数言語のハウツーは、パックのインストールとすべての125以上のサポートされている言語に対する+演算子の構文をカバーしています。
単語座標を用いた構造化データ抽出
OCR.spaceはParsedResults[0].ParsedTextからプレーンテキストを返します。 単語レベルのデータ、境界ボックス、行境界、要素ごとの信頼度スコアは存在しません。 請求書の右上隅にある日付や、表の右下隅にある合計金額など、特定のフィールドを探す必要があるアプリケーションは、OCR.spaceの応答を基に構築できる構造的な基盤を持っていません。 IronOCRは、ページ、段落、行、単語、文字といった文書の階層構造を完全に提供し、それぞれにピクセル座標と信頼度スコアが付与されます。
OCR.spaceのアプローチ:
// OCR.space: plain text only — no structure, no coordinates
public async Task<string> ExtractInvoiceFields(string invoicePath)
{
byte[] invoiceBytes = await File.ReadAllBytesAsync(invoicePath);
string base64 = Convert.ToBase64String(invoiceBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:application/pdf;base64,{base64}"),
new KeyValuePair<string, string>("filetype", "PDF"),
new KeyValuePair<string, string>("language", "eng"),
// isOverlayRequired=true returns word boxes, but only as raw JSON coordinates
new KeyValuePair<string, string>("isOverlayRequired", "true")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
string json = await response.Content.ReadAsStringAsync();
// Navigate deeply-nested JSON to find word boxes — no typed models
using var doc = JsonDocument.Parse(json);
var overlay = doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("TextOverlay");
// Parse word coordinate arrays manually — fragile JSON path traversal
var wordData = new List<(string word, int x, int y)>();
foreach (var line in overlay.GetProperty("Lines").EnumerateArray())
{
foreach (var word in line.GetProperty("Words").EnumerateArray())
{
string wordText = word.GetProperty("WordText").GetString() ?? "";
int left = word.GetProperty("Left").GetInt32();
int top = word.GetProperty("Top").GetInt32();
wordData.Add((wordText, left, top));
}
}
// Reconstruct full text from raw JSON — still no typed result
return string.Join(" ", wordData.Select(w => w.word));
}
// OCR.space: plain text only — no structure, no coordinates
public async Task<string> ExtractInvoiceFields(string invoicePath)
{
byte[] invoiceBytes = await File.ReadAllBytesAsync(invoicePath);
string base64 = Convert.ToBase64String(invoiceBytes);
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("apikey", _apiKey),
new KeyValuePair<string, string>("base64Image", $"data:application/pdf;base64,{base64}"),
new KeyValuePair<string, string>("filetype", "PDF"),
new KeyValuePair<string, string>("language", "eng"),
// isOverlayRequired=true returns word boxes, but only as raw JSON coordinates
new KeyValuePair<string, string>("isOverlayRequired", "true")
});
var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);
string json = await response.Content.ReadAsStringAsync();
// Navigate deeply-nested JSON to find word boxes — no typed models
using var doc = JsonDocument.Parse(json);
var overlay = doc.RootElement
.GetProperty("ParsedResults")[0]
.GetProperty("TextOverlay");
// Parse word coordinate arrays manually — fragile JSON path traversal
var wordData = new List<(string word, int x, int y)>();
foreach (var line in overlay.GetProperty("Lines").EnumerateArray())
{
foreach (var word in line.GetProperty("Words").EnumerateArray())
{
string wordText = word.GetProperty("WordText").GetString() ?? "";
int left = word.GetProperty("Left").GetInt32();
int top = word.GetProperty("Top").GetInt32();
wordData.Add((wordText, left, top));
}
}
// Reconstruct full text from raw JSON — still no typed result
return string.Join(" ", wordData.Select(w => w.word));
}
Imports System.IO
Imports System.Net.Http
Imports System.Text.Json
Imports System.Threading.Tasks
Public Class InvoiceProcessor
Private _apiKey As String
Private _httpClient As HttpClient
Public Sub New(apiKey As String, httpClient As HttpClient)
_apiKey = apiKey
_httpClient = httpClient
End Sub
Public Async Function ExtractInvoiceFields(invoicePath As String) As Task(Of String)
Dim invoiceBytes As Byte() = Await File.ReadAllBytesAsync(invoicePath)
Dim base64 As String = Convert.ToBase64String(invoiceBytes)
Dim content As New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
New KeyValuePair(Of String, String)("apikey", _apiKey),
New KeyValuePair(Of String, String)("base64Image", $"data:application/pdf;base64,{base64}"),
New KeyValuePair(Of String, String)("filetype", "PDF"),
New KeyValuePair(Of String, String)("language", "eng"),
New KeyValuePair(Of String, String)("isOverlayRequired", "true")
})
Dim response As HttpResponseMessage = Await _httpClient.PostAsync("https://api.ocr.space/parse/image", content)
Dim json As String = Await response.Content.ReadAsStringAsync()
Using doc As JsonDocument = JsonDocument.Parse(json)
Dim overlay = doc.RootElement _
.GetProperty("ParsedResults")(0) _
.GetProperty("TextOverlay")
Dim wordData As New List(Of (word As String, x As Integer, y As Integer))()
For Each line In overlay.GetProperty("Lines").EnumerateArray()
For Each word In line.GetProperty("Words").EnumerateArray()
Dim wordText As String = word.GetProperty("WordText").GetString() OrElse ""
Dim left As Integer = word.GetProperty("Left").GetInt32()
Dim top As Integer = word.GetProperty("Top").GetInt32()
wordData.Add((wordText, left, top))
Next
Next
Return String.Join(" ", wordData.Select(Function(w) w.word))
End Using
End Function
End Class
IronOCRのアプローチ:
// IronOCR: full document hierarchy — typed, no JSON, no coordinates parsing
public void ExtractInvoiceFields(string invoicePath)
{
var ocr = new IronTesseract();
var result = ocr.Read(invoicePath);
// Access the full document hierarchy — all strongly typed
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}");
}
foreach (var word in page.Words)
{
// Word-level confidence — identify low-quality extractions
if (word.Confidence < 70)
Console.WriteLine($"Low confidence word '{word.Text}' at ({word.X}, {word.Y})");
}
}
// Or use region-based OCR to target specific invoice zones directly
var totalRegion = new CropRectangle(400, 700, 200, 50); // Bottom-right total field
using var input = new OcrInput();
input.LoadImage(invoicePath, totalRegion);
string totalText = ocr.Read(input).Text;
Console.WriteLine($"Invoice total: {totalText}");
}
// IronOCR: full document hierarchy — typed, no JSON, no coordinates parsing
public void ExtractInvoiceFields(string invoicePath)
{
var ocr = new IronTesseract();
var result = ocr.Read(invoicePath);
// Access the full document hierarchy — all strongly typed
foreach (var page in result.Pages)
{
foreach (var paragraph in page.Paragraphs)
{
Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}");
}
foreach (var word in page.Words)
{
// Word-level confidence — identify low-quality extractions
if (word.Confidence < 70)
Console.WriteLine($"Low confidence word '{word.Text}' at ({word.X}, {word.Y})");
}
}
// Or use region-based OCR to target specific invoice zones directly
var totalRegion = new CropRectangle(400, 700, 200, 50); // Bottom-right total field
using var input = new OcrInput();
input.LoadImage(invoicePath, totalRegion);
string totalText = ocr.Read(input).Text;
Console.WriteLine($"Invoice total: {totalText}");
}
Imports IronOcr
Public Sub ExtractInvoiceFields(invoicePath As String)
Dim ocr As New IronTesseract()
Dim result = ocr.Read(invoicePath)
' Access the full document hierarchy — all strongly typed
For Each page In result.Pages
For Each paragraph In page.Paragraphs
Console.WriteLine($"Paragraph at ({paragraph.X}, {paragraph.Y}): {paragraph.Text}")
Next
For Each word In page.Words
' Word-level confidence — identify low-quality extractions
If word.Confidence < 70 Then
Console.WriteLine($"Low confidence word '{word.Text}' at ({word.X}, {word.Y})")
End If
Next
Next
' Or use region-based OCR to target specific invoice zones directly
Dim totalRegion As New CropRectangle(400, 700, 200, 50) ' Bottom-right total field
Using input As New OcrInput()
input.LoadImage(invoicePath, totalRegion)
Dim totalText As String = ocr.Read(input).Text
Console.WriteLine($"Invoice total: {totalText}")
End Using
End Sub
OCR.spaceのisOverlayRequired=trueフラグはJSON単語座標を返しますが、レスポンス構造は、文字列キーのプロパティアクセスを伴うネストされたJSON配列をナビゲートする必要があります — 型付きモデルがなく、IntelliSenseがなく、レスポンス構造が変更されると壊れるパスの脆弱性があります。 IronOCRのresult.Linesは型付き.NETオブジェクトです。 CropRectangleアプローチは、ドキュメント全体を抽出し、座標でフィルタリングする代わりに、特定のドキュメント領域を直接対象としています。 読み取り結果の操作方法と領域ベースのOCRガイドでは、両方のパターンについて詳しく説明しています。
OCR.space API からIronOCRへのマッピングリファレンス
| OCR.space コンセプト | IronOCR相当値 |
|---|---|
| NuGetパッケージがありません | dotnet add package IronOcr |
HttpClient構築 |
不要 — HTTPレイヤーなし |
SemaphoreSlimレートリミッター |
不要 - レート制限なし |
FormUrlEncodedContent / MultipartFormDataContent |
OcrInput |
base64ImageデータURIパラメータ |
input.LoadImage(bytes) |
fileアップロードパラメータ |
input.LoadImage(path) |
apikeyヘッダー / フォームフィールド |
IronOcr.License.LicenseKey(起動時に一度) |
languageパラメータ(呼び出しごとに1つ) |
ocr.Language = OcrLanguage.English + OcrLanguage.French |
OCREngine=1(高速) |
デフォルトエンジン(最適化されたTesseract 5) |
OCREngine=2(高精度) |
input.Deskew(); input.DeNoise(); input.Contrast();. |
scale=trueパラメータ |
input.Scale(200) |
detectOrientation=trueパラメータ |
input.Deskew() |
isOverlayRequired=trueパラメータ |
result.Pages[n].Words(常に利用可能、型付き) |
isCreateSearchablePdf=trueパラメータ |
result.SaveAsSearchablePdf("output.pdf") |
filetype=PDFパラメータ |
input.LoadPdf(path) |
ParsedResults[0].ParsedText |
result.Text |
ParsedResults[n](ページごとのテキスト) |
result.Pages[n].Text |
TextOverlay.Lines[n].Words[n].WordText |
result.Pages[n].Words[n].Text |
TextOverlay.Lines[n].Words[n].Left/Top |
result.Pages[n].Words[n].X / .Y |
IsErroredOnProcessingJSONフラグ |
メッセージ付きの標準Exception |
FileParseExitCodeページごとのフラグ |
メッセージ付きの標準Exception |
| HTTP 429 リクエストが多すぎます | 該当なし - レート制限なし |
カスタムOcrResultPOCO(ユーザー定義) |
IronOcr.OcrResult(NuGet提供) |
カスタムOcrSpaceException(ユーザー定義) |
標準 for .NET例外タイプ |
一般的な移行の問題と解決策
問題1:HTTP専用に存在していた非同期コード
OCR.space: すべてのOCR呼び出しは、クラウドへのHTTP往復を含むためasyncです。 ネットワーク待機時にスレッドがブロックされるのを避けるため、サービスメソッド、コントローラーアクション、およびバックグラウンドジョブは非同期化されました。
解決策: IronOCRのRead()メソッドは同期です。 OCR.spaceの必要性から非同期にされたメソッドからawaitを削除します。 非ブロッキング実行が重要なASP.NET Coreコンテキストでは、同期呼び出しをTask.Run()でラップするか、非同期OCRガイドに記載されている非同期パターンを使用してください。 IronOCR呼び出しにawaitを反射的に追加しないでください — それは必須ではなく、非ウェブコンテキストで不必要なオーバーヘッドを追加します。
// Before: async because OCR.space required network I/O
public async Task<string> ProcessDocumentAsync(string path)
{
return await _ocrSpaceClient.ExtractTextAsync(path); // Network wait
}
// After: synchronous — no network, no async needed
public string ProcessDocument(string path)
{
return _ocr.Read(path).Text; // Local execution
}
// Before: async because OCR.space required network I/O
public async Task<string> ProcessDocumentAsync(string path)
{
return await _ocrSpaceClient.ExtractTextAsync(path); // Network wait
}
// After: synchronous — no network, no async needed
public string ProcessDocument(string path)
{
return _ocr.Read(path).Text; // Local execution
}
Imports System.Threading.Tasks
' Before: async because OCR.space required network I/O
Public Async Function ProcessDocumentAsync(path As String) As Task(Of String)
Return Await _ocrSpaceClient.ExtractTextAsync(path) ' Network wait
End Function
' After: synchronous — no network, no async needed
Public Function ProcessDocument(path As String) As String
Return _ocr.Read(path).Text ' Local execution
End Function
課題2:APIキーの保存とローテーションのインフラストラクチャ
OCR.space: APIキーはすべてのリクエストに注入する必要があります。チームは通常、IOptions<t>またはコンストラクター注入で注入し、露出時にローテーションします。 キーのローテーションには、すべての展開環境の更新とアプリケーションの再起動が必要です。
解決策: IronOCRのライセンスキーは起動時に一度設定され、実行中は二度と参照されません。 リクエストごとにキーを挿入するパターンを削除してください。 IOptions<OcrSpaceSettings>設定クラスを削除します。 キー初期化パターンは1行です。
// Startup.cs or Program.cs — once only
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
// Startup.cs or Program.cs — once only
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY");
Imports System
' Startup.vb or Program.vb — once only
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE_KEY")
リクエストごとの認証情報挿入、キーローテーション手順、リクエストトレースに誤ってキーが記録されるリスクはありません。
問題3:ファイルサイズ事前検証ロジック
OCR.space:無料プランでは、5MBを超えるファイルはエラー応答で拒否されます。 本番環境のコードでは、失敗する可能性のある呼び出しにレート制限スロットを無駄に消費しないように、すべてのリクエストの前にファイルサイズチェックを追加します。
// OCR.space: pre-validation to avoid wasting quota on large files
var fileInfo = new FileInfo(filePath);
if (fileInfo.Length > 5 * 1024 * 1024)
throw new InvalidOperationException("File exceeds 5MB free tier limit.");
// OCR.space: pre-validation to avoid wasting quota on large files
var fileInfo = new FileInfo(filePath);
if (fileInfo.Length > 5 * 1024 * 1024)
throw new InvalidOperationException("File exceeds 5MB free tier limit.");
Dim fileInfo As New FileInfo(filePath)
If fileInfo.Length > 5 * 1024 * 1024 Then
Throw New InvalidOperationException("File exceeds 5MB free tier limit.")
End If
解決策:このチェック項目を完全に削除してください。 IronOCRのOcrInput.LoadImage()には、利用可能なシステムメモリを超えるサイズ制限はありません。 人為的に設定された5MBの制限は、OCR.spaceの無料プランにおいてサーバー容量の制約から設けられたものです。 50MBのスキャン済みPDFファイルは、500KBのPDFファイルと同じように読み込まれます。
問題4:JSONレスポンスナビゲーションの脆弱性
OCR.space: 応答解析は、文字列キーのプロパティアクセスを伴うJsonDocumentをナビゲートすることに依存しています。 ParsedResultsをスローします。 どちらの場合も、全体を通してtry-catchガードまたはnullチェックが必要です。
解決策: IronOCRは型付きOcrResultオブジェクトを返します。 stringです — 決してnullではなく、決して欠落しません。 OCRが出力を生成しない場合(空白のページ、読めない画像)、result.Textは空の文字列です。 JSONをナビゲートする必要はなく、プロパティパスの脆弱性を防御する必要もありません。信頼度ベースのフィルタリングには、doubleを返し、これを直接比較します:
// IronOCR: typed result — no JSON path fragility
var result = new IronTesseract().Read("document.jpg");
if (result.Confidence < 50)
Console.WriteLine("Low confidence — consider preprocessing");
else
Console.WriteLine(result.Text);
// IronOCR: typed result — no JSON path fragility
var result = new IronTesseract().Read("document.jpg");
if (result.Confidence < 50)
Console.WriteLine("Low confidence — consider preprocessing");
else
Console.WriteLine(result.Text);
Imports IronOcr
Dim result = New IronTesseract().Read("document.jpg")
If result.Confidence < 50 Then
Console.WriteLine("Low confidence — consider preprocessing")
Else
Console.WriteLine(result.Text)
End If
信頼度スコアの算出方法については、単語ごとおよび文書ごとの信頼度閾値を解説しています。
問題5:CI/CDにおける共有IPレート制限の超過
OCR.space: OCR.spaceに対して統合テストを実行するCI/CDパイプラインは、開発オフィスネットワークと同じ送信元IPアドレスを使用します。 無料プランのアカウントでは、IPアドレスごとに1日あたり500件のリクエスト制限が設けられています。1回の実行で200件のテストドキュメントを処理するパイプラインでは、最初の開発者が手動テストを実行する前に、1日の制限に達してしまう可能性があります。チームはこの問題を回避するために、テスト内でOCR.spaceの応答をモック化していますが、これは統合テストの目的を損なうことになります。
解決策: IronOCRはローカルで処理を実行します。 テストスイートは、直接new IronTesseract().Read(testImagePath).Textを呼び出します — モックは必要なく、クォータが尽きることもなく、ネットワーク依存もありません。 統合テストは、レート制限管理やテスト分離パターンを使用せずに、本番環境と同じ実際のOCR結果を使用してCI/CDで実行されます。
問題6: HttpClient管理からのIDisposableパターン
OCR.space: IDisposableを実装し、HTTP接続プールを解放します。 OCRサービスのすべての消費者は、シングルトンを注入するか、usingブロックを使用するか、DIコンテナの廃棄ライフサイクルに登録する必要があります。 廃棄を忘れると、負荷がかかった状態でコンセントが消耗する原因となる。
解決策: IronTesseractはネットワーク接続を管理しません。 それはIDisposableを実装しません。 スレッドごとに1つのインスタンスを作成し(またはASP.NETでリクエストごとに)、.Read()を呼び出し、GCにそれを収集させます。 IronTesseractクラスはライフサイクル管理を必要としません。 OCRサービスラッパーからIDisposableの実装を削除し、DI登録をスコープ/一時的な廃棄からシンプルなファクトリーまたはシングルトンに簡素化します。
OCR.space移行チェックリスト
マイグレーション前のタスク
コードベースを監査して、OCR.spaceとの統合ポイントをすべて特定します。
# Find all OCR.space HTTP calls
grep -rn "ocr.space" --include="*.cs" .
# Find all base64 encoding related to OCR
grep -rn "base64Image\|Convert.ToBase64String" --include="*.cs" .
# Find rate limiter and retry logic
grep -rn "SemaphoreSlim\|TooManyRequests\|exponential" --include="*.cs" .
# Find JSON parsing for OCR responses
grep -rn "ParsedResults\|IsErroredOnProcessing\|FileParseExitCode" --include="*.cs" .
# Find custom OCR models and exception types
grep -rn "OcrSpaceException\|OcrResult\b" --include="*.cs" .
# Find API key configuration
grep -rn "apikey\|OcrSpaceApiKey\|ocr_space" --include="*.cs" --include="*.json" .
# Find all OCR.space HTTP calls
grep -rn "ocr.space" --include="*.cs" .
# Find all base64 encoding related to OCR
grep -rn "base64Image\|Convert.ToBase64String" --include="*.cs" .
# Find rate limiter and retry logic
grep -rn "SemaphoreSlim\|TooManyRequests\|exponential" --include="*.cs" .
# Find JSON parsing for OCR responses
grep -rn "ParsedResults\|IsErroredOnProcessing\|FileParseExitCode" --include="*.cs" .
# Find custom OCR models and exception types
grep -rn "OcrSpaceException\|OcrResult\b" --include="*.cs" .
# Find API key configuration
grep -rn "apikey\|OcrSpaceApiKey\|ocr_space" --include="*.cs" --include="*.json" .
OCR.spaceコードを含むファイルのリストを文書化する。 どのメソッドがOCR.spaceのHTTP依存性のためにasyncであるかに注意してください — 移行後は同期にできます。
コード更新タスク
IronOcrNuGetパッケージをインストール:dotnet add package IronOcr- アプリケーションの起動時に
IronOcr.License.LicenseKey = "..."を追加 OcrSpaceApiClientクラスとすべてのサポートインフラストラクチャを削除- カスタム
OcrResultPOCOを削除(IronOcr.OcrResultに置き換え) - カスタム
OcrSpaceExceptionクラスを削除(標準 for .NET例外で置き換え) SemaphoreSlimレートリミッターと関連するTask.Delayロジックを削除- OCR画像エンコーディングに使用される
Convert.ToBase64String()呼び出しをすべて削除 FormUrlEncodedContent/OcrInputに置き換えnew IronTesseract().Read(input)に置き換えresult.Textに置き換えTextOverlayJSON座標解析をresult.Pages[n].Wordsに置き換えOCREngineパラメータの切り替えを適切な前処理フィルタで置き換えOcrLanguage列挙型の値に置き換え- ファイルサイズ事前検証チェックを削除(5MBの制限は適用されなくなりました)
- HTTPだけが非同期の理由であった場合、
async Task<string>OCRメソッドを同期stringに変換 - 設定ファイルと環境変数設定からOCR.space APIキーを削除します。
移行後のテスト
- テキスト抽出が同じテスト文書で同等以上の精度で生成されることを検証する
- 5MBを超える大容量ファイルをエラーなく処理できることを確認します。
OcrLanguage.English + OcrLanguage.Frenchでマルチ言語のドキュメントをテストし、交互出力を検証- 実際のOCR呼び出しを使用してCI/CDパイプラインを実行し、どのドキュメント量でもレート制限エラーが発生しないことを確認します。 検索可能なPDF出力に透かしが入っていないことを検証する
- 同期化後も、以前は非同期だったコントローラーアクションが正しく応答することを確認します。
- エアギャップ環境またはネットワーク制限環境において、ドキュメントがエラーなく処理されることをテストする
- 以前に
result.Confidence値を確認 result.Pages[n].Words座標が構造化ドキュメントの期待フィールド位置と一致することを確認- 最初のOCR呼び出しの前に、アプリケーションの起動ライセンスの初期化が成功するかどうかをテストします。
IronOCRへの移行の主なメリット
80行を超えるインフラストラクチャの負担はなくなります。OCR.spaceのすべての統合機能には、HTTPクライアント、レートリミッター、JSONデシリアライザー、カスタム例外タイプ、およびカスタム結果モデルが付属しています。 そのコードはどれもアプリケーションが実際に必要とする機能を何も果たしていません。OCR.spaceにSDKがないことを補うために存在しているのです。 移行後、そのコードは削除されます。 コードベースのOCR表面積は、呼び出しサイトでnew IronTesseract().Read(path).Textと起動時のライセンス初期化行に縮小します。
文書処理速度は、ローカルハードウェアの性能に依存します。OCR.spaceでは、ネットワーク遅延、OCR.spaceサーバーのキュー深度、地理的な往復時間が、すべての処理操作に反映されます。 IronOCRはインプロセスで実行されます。 ローカルワークステーションは、バッチ処理を直列化する1分あたり60リクエストの制限がなく、あらゆるスループットレベルのクラウドAPIよりも高速にドキュメントを処理します。 複数のParallel.ForEachでの並列処理は、CPUコアと共に拡張します — マルチスレッドの例を参照してください。
機密文書は、お客様のインフラストラクチャ内に永久的に保存されます。移行後も、医療記録、財務文書、法的契約書、身分証明書類はアプリケーションサーバーから外部に持ち出されることはありません。 HIPAA、GDPR、SOC 2、および社内データ分類ポリシーに関するコンプライアンスレビューにおいて、OCR.spaceのデータ処理慣行をレビュー対象に含める必要はなくなりました。 監査対象は、自社のインフラストラクチャに限定されます。 DockerデプロイメントガイドとAzureデプロイメントガイドでは、データ所在地のコンプライアンスが求められるコンテナ環境およびクラウド環境へのIronOCRのデプロイについて説明しています。
構造化された出力は、ドキュメントインテリジェンス応用を可能にします。 OCR.spaceのParsedText文字列は、ドキュメント分析の行き止まりです。 IronOCRのresult.Linesでの座標および単語ごとの信頼スコアにより、特定のフィールドの位置特定、抽出品質の検証、表データの抽出、下流のドキュメントインテリジェンスパイプラインの構築が可能です。 OCR.spaceのプレーンテキスト出力の上に独自のレイアウト分析を構築する必要があった機能が、直接API呼び出しで実現可能になった。 表抽出ガイドとスキャン文書処理ガイドは、この構造化された基盤が何を可能にするかを示しています。
処理量に関わらず、コストは固定され、予測可能になります。OCR.spaceの無料プランでは、月間25,000件のリクエストに対応しています。 さらに、利用量に応じて購読料が変動します。 IronOCRの$999 Lite永続ライセンスは、任意のボリュームでのドキュメントごとの料金はかかりません。 月に10万件の文書を処理するチームも、月に1,000件の文書を処理するチームも、同じライセンス料を支払います。 文書処理アプリケーションの予算予測は、事業の成功に応じて変動する項目ではなく、固定の年間コストとなる。 IronOCRの製品ページには無料トライアルが用意されており、購入前にチームが特定の文書タイプにおける精度を検証できる。
よくある質問
なぜOCR.space APIからIronOCRに移行する必要があるのですか?
一般的な推進要因には、COM相互接続の複雑さの解消、ファイルベースのライセンス管理の置き換え、ページ単位の課金の回避、Docker/コンテナデプロイの有効化、標準的な.NETツールと統合するNuGetネイティブワークフローの採用などがあります。
OCR.spaceAPIからIronOCRに移行する際の主なコード変更は何ですか?
OCR.spaceの初期化シーケンスをIronTesseractのインスタンス化に置き換え、COMのライフサイクル管理(明示的なCreate/Load/Closeパターン)を削除し、結果のプロパティ名を更新します。その結果、定型的な行が大幅に減りました。
IronOCRをインストールして移行を開始するにはどうすればいいですか?
パッケージマネージャーコンソールで'Install-Package IronOcr'を実行するか、CLIで'dotnet add package IronOcr'を実行してください。言語パックは別のパッケージです:例えばフランス語の場合は'dotnet add package IronOcr.Languages.French'となります。
IronOCRは標準的なビジネス文書のOCR.space APIのOCR精度に匹敵しますか?
IronOCRは、請求書、契約書、領収書、入力フォームなどの標準的なビジネスコンテンツで高い精度を達成しています。画像前処理フィルター(傾き補正、ノイズ除去、コントラスト強調)により、劣化した入力の認識をさらに向上させます。
IronOCRはOCR.space APIが別途インストールする言語データをどのように扱うのですか?
IronOCRの言語データはNuGetパッケージとして配布されます。'dotnet add package IronOcr.Languages.German'でドイツ語のサポートがインストールされます。手動でのファイル配置やディレクトリパスは必要ありません。
OCR.spaceAPIからIronOCRに移行する場合、導入インフラの変更が必要ですか?
IronOCRはOCR.space APIよりもインフラの変更が少なくて済みます。SDKのバイナリパス、ライセンスファイルの配置、ライセンスサーバーの設定は必要ありません。NuGetパッケージには完全なOCRエンジンが含まれており、ライセンスキーはアプリケーションコードに設定された文字列です。
移行後のIronOCRライセンスの設定方法は?
アプリケーションの起動コードでIronOcr.License.LicenseKey = "YOUR-KEY "を割り当てます。DockerまたはKubernetesでは、キーを環境変数として保存し、スタートアップで読み込む。License.IsValidLicenseを使用して、トラフィックを受け入れる前にバリデーションを行う。
IronOCRはOCR.spaceと同じようにPDFを処理できますか?
IronOCRはネイティブPDFもスキャンしたPDFも読み取ります。IronTesseractをインスタンス化し、ocr.Read(input)を呼び出し、入力はPDFパスまたはOcrPdfInputであり、OcrResultページを反復します。別のPDFレンダリングパイプラインは必要ありません。
IronOCRはどのように大量処理のスレッディングを処理するのですか?
IronTesseractはスレッド毎にインスタンス化しても安全です。Parallel.ForEachまたはタスクプールでスレッドごとにインスタンスを1つスピンアップし、OCRを同時に実行し、完了したら各インスタンスを破棄します。グローバルステートやロックは必要ありません。
IronOCRはテキスト抽出後、どのような出力形式をサポートしていますか?
IronOCRはテキスト、単語座標、信頼度スコア、ページ構造を含む構造化された結果を返します。エクスポートオプションには、プレーンテキスト、検索可能なPDF、下流処理用の構造化結果オブジェクトが含まれます。
IronOCRの価格はOCR.space APIよりもワークロードのスケーリングが予測可能ですか?
IronOCRは、定額制の永久ライセンスを採用しており、ページ単位やボリューム単位の料金はかかりません。10,000ページでも1,000万ページでも、ライセンスコストは一定です。ボリュームライセンスとチームライセンスのオプションはIronOCRの価格ページをご覧ください。
OCR.spaceAPIからIronOCRに移行した後、既存のテストはどうなりますか?
抽出されたテキストコンテンツをアサートするテストは、移行後もパスし続ける必要があります。APIコールパターンやCOMオブジェクトのライフサイクルを検証するテストは、IronOCRのシンプルな初期化と結果モデルを反映するように更新する必要があります。

