フッターコンテンツにスキップ
ビデオ

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
$vbLabelText   $csharpLabel

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)
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

ステップ 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"
$vbLabelText   $csharpLabel

コード移行の例

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

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
$vbLabelText   $csharpLabel

課題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")
$vbLabelText   $csharpLabel

リクエストごとの認証情報挿入、キーローテーション手順、リクエストトレースに誤ってキーが記録されるリスクはありません。

問題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
$vbLabelText   $csharpLabel

解決策:このチェック項目を完全に削除してください。 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
$vbLabelText   $csharpLabel

信頼度スコアの算出方法については、単語ごとおよび文書ごとの信頼度閾値を解説しています。

問題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" .
SHELL

OCR.spaceコードを含むファイルのリストを文書化する。 どのメソッドがOCR.spaceのHTTP依存性のためにasyncであるかに注意してください — 移行後は同期にできます。

コード更新タスク

  1. IronOcr NuGetパッケージをインストール: dotnet add package IronOcr
  2. アプリケーションの起動時にIronOcr.License.LicenseKey = "..."を追加
  3. OcrSpaceApiClientクラスとすべてのサポートインフラストラクチャを削除
  4. カスタムOcrResult POCOを削除(IronOcr.OcrResultに置き換え)
  5. カスタムOcrSpaceExceptionクラスを削除(標準 for .NET例外で置き換え)
  6. SemaphoreSlimレートリミッターと関連するTask.Delayロジックを削除
  7. OCR画像エンコーディングに使用されるConvert.ToBase64String()呼び出しをすべて削除
  8. FormUrlEncodedContent / OcrInputに置き換え
  9. new IronTesseract().Read(input)に置き換え
  10. result.Textに置き換え
  11. TextOverlay JSON座標解析をresult.Pages[n].Wordsに置き換え
  12. OCREngineパラメータの切り替えを適切な前処理フィルタで置き換え
  13. OcrLanguage列挙型の値に置き換え
  14. ファイルサイズ事前検証チェックを削除(5MBの制限は適用されなくなりました)
  15. HTTPだけが非同期の理由であった場合、async Task<string> OCRメソッドを同期stringに変換
  16. 設定ファイルと環境変数設定から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およびTesseractはそれぞれの所有者の登録商標です。 このサイトはGoogleおよびOCR.spaceと提携せず、承認もサポートもされていません。 すべての製品名、ロゴ、およびブランドは各所有者の所有物です。 比較は情報提供のみを目的としており、執筆時点で公開されている情報を反映しています。

よくある質問

なぜ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のシンプルな初期化と結果モデルを反映するように更新する必要があります。

Kannaopat Udonpant
ソフトウェアエンジニア
ソフトウェアエンジニアになる前に、Kannapatは北海道大学で環境資源の博士号を修了しました。博士号を追求する間に、彼はバイオプロダクションエンジニアリング学科の一部である車両ロボティクスラボラトリーのメンバーになりました。2022年には、C#のスキルを活用してIron Softwareのエンジニアリングチームに参加し、IronPDFに注力しています。Kannapatは、IronPDFの多くのコードを執筆している開発者から直接学んでいるため、この仕事を大切にしています。同僚から学びながら、Iron Softwareでの働く社会的側面も楽しんでいます。コードやドキュメントを書いていない時は、KannapatはPS5でゲームをしたり、『The Last of Us』を再視聴したりしていることが多いです。

アイアンサポートチーム

私たちは週5日、24時間オンラインで対応しています。
チャット
メール
電話してね