IRONSOFTWAREHOME
他のコンポーネントと比較する

ABBYY FineReader対Tesseract:OCR比較

Kannaopat Udonpant
Kannapat Udonpant
Updated: 2026年6月28日

OCR.spaceにはNuGetパッケージがありません。 その単一の事実が、.NET開発者がこれを選ぶ際のすべての統合決定を定義します: 独自のhttps://api.ocr.space/parse/imageにPOSTすること、JSON応答を手動で解析すること、HTTP 429を取得すること、指数バックオフを実装すること、レート制限簿記を作成すること、独自の例外タイプを定義すること―これらの全ては、あなたのアプリケーションにテキストの単一の文字が到達する前に行わなければなりません。 この記事では、DIYによるオーバーヘッドがコード、時間、および本番環境の信頼性に実際にどれだけのコストがかかるかを検証し、単一のNuGetパッケージとして提供されるネイティブ.NETライブラリであるIronOCRと直接比較します。

OCR.spaceを理解する

OCR.spaceは、OCR.spaceが運営するリモートサーバー上で画像やPDFを処理する、フリーミアムのREST APIです。 このサービスは、無料プランを強みとしており、クレジットカード不要で月間25,000件のリクエストが可能となっている。これは、OCRを試用したり、個人的なプロジェクトを構築したりする開発者にとって魅力的な点だ。 NuGetパッケージも、公式 for .NET SDKも、どの言語においても厳密に型指定されたクライアントライブラリも存在しません。 すべて for .NETインテグレーションはHttpClientの上に手作業で構築されています。

.NET開発者にとってのアーキテクチャ上の現実:

  • NuGetパッケージなし: .NETのファーストクラスのサポートは皆無です。 IntelliSense機能なし、型付きモデルなし、統合エラー処理なし。 -**クラウドのみでの処理:**すべてのドキュメントはお客様のインフラストラクチャから外部に送信されます。 オンプレミスオプションはなく、セルフホスト型のデプロイメントパスもありません。
  • 手動によるREST統合: 開発者はファイルをbase64でエンコードし、MultipartFormDataContentを構築し、生のJSON応答を解析します。
  • **DIYによるレート制限:**無料プランでは、1分あたり60リクエスト、1日あたりIPアドレスごとに500リクエストという制限が適用されます。本番環境のアプリケーションでは、この制限ロジックを独自に構築する必要があります。 -**ファイルサイズ制限:**無料プランでは、5MBを超えるファイルは拒否されます。 複数ページのPDFファイルは、この制限を超えることが多い。 -**透かし入りの PDF 出力:**無料プランの検索可能な PDF 生成では、OCR.space の透かしが埋め込まれるため、出力はクライアントへの納品や文書のアーカイブには使用できません。
  • **SLAなし:**無料プランには稼働時間に関する保証はありません。 有料プランでは上限額は高くなりますが、アーキテクチャ上の制約は同じです。

REST統合開発者が必ず書くべきこと

OCR.spaceのドキュメントは開発者に対しRESTエンドポイントを指し示すだけで、それ以外のすべては開発者自身で構築するよう指示している。 本番環境におけるセキュリティ強化を考慮する前の、必要最低限​​ for .NETクライアントは以下のようになります。

// OCR.space: what you build before the first line of your actual application
public class OcrSpaceApiClient : IDisposable
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly SemaphoreSlim _rateLimiter;
    private const string ApiEndpoint = "https://api.ocr.space/parse/image";
    private const int MaxFileSizeFree = 5 * 1024 * 1024; // 5MB — hard limit on free tier
    private const int RateLimitPerMinute = 60;

    public OcrSpaceApiClient(string apiKey)
    {
        _apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
        _httpClient = new HttpClient();
        _httpClient.Timeout = TimeSpan.FromSeconds(120);
        _rateLimiter = new SemaphoreSlim(RateLimitPerMinute, RateLimitPerMinute);
    }

    public async Task<OcrResult> ExtractTextFromFileAsync(
        string filePath,
        string language = "eng",
        CancellationToken cancellationToken = default)
    {
        if (!File.Exists(filePath))
            throw new FileNotFoundException("Image file not found", filePath);

        var fileInfo = new FileInfo(filePath);
        if (fileInfo.Length > MaxFileSizeFree)
        {
            throw new InvalidOperationException(
                $"File size {fileInfo.Length / 1024 / 1024}MB exceeds free tier limit of 5MB.");
        }

        await _rateLimiter.WaitAsync(cancellationToken);

        try
        {
            // Document leaves your infrastructure here
            byte[] imageBytes = await File.ReadAllBytesAsync(filePath, cancellationToken);
            string base64Image = Convert.ToBase64String(imageBytes);
            string mimeType = GetMimeType(filePath);

            var formContent = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("apikey", _apiKey),
                new KeyValuePair<string, string>("base64Image", $"data:{mimeType};base64,{base64Image}"),
                new KeyValuePair<string, string>("language", language),
                new KeyValuePair<string, string>("isOverlayRequired", "false"),
                new KeyValuePair<string, string>("filetype", Path.GetExtension(filePath).TrimStart('.')),
                new KeyValuePair<string, string>("detectOrientation", "true"),
                new KeyValuePair<string, string>("scale", "true"),
                new KeyValuePair<string, string>("OCREngine", "2")
            });

            var response = await _httpClient.PostAsync(ApiEndpoint, formContent, cancellationToken);

            if (!response.IsSuccessStatusCode)
            {
                string errorBody = await response.Content.ReadAsStringAsync(cancellationToken);
                throw new OcrSpaceException(
                    $"OCR.space API returned {response.StatusCode}: {errorBody}",
                    (int)response.StatusCode);
            }

            string jsonResponse = await response.Content.ReadAsStringAsync(cancellationToken);
            return ParseOcrResponse(jsonResponse);
        }
        finally
        {
            _ = Task.Run(async () =>
            {
                await Task.Delay(60000 / RateLimitPerMinute);
                _rateLimiter.Release();
            });
        }
    }

    private OcrResult ParseOcrResponse(string jsonResponse)
    {
        using var doc = JsonDocument.Parse(jsonResponse);
        var root = doc.RootElement;

        if (root.TryGetProperty("IsErroredOnProcessing", out var isErrored) && isErrored.GetBoolean())
        {
            string errorMessage = "OCR processing failed";
            if (root.TryGetProperty("ErrorMessage", out var errorMessages))
            {
                // Parse the array of error strings manually
                var messages = new List<string>();
                if (errorMessages.ValueKind == JsonValueKind.Array)
                {
                    foreach (var msg in errorMessages.EnumerateArray())
                        messages.Add(msg.GetString() ?? "Unknown error");
                }
                errorMessage = string.Join("; ", messages);
            }
            throw new OcrSpaceException(errorMessage, 500);
        }

        var result = new OcrResult();

        if (root.TryGetProperty("ParsedResults", out var parsedResults))
        {
            foreach (var parsedResult in parsedResults.EnumerateArray())
            {
                if (parsedResult.TryGetProperty("ParsedText", out var parsedText))
                    result.ParsedText += parsedText.GetString();

                if (parsedResult.TryGetProperty("FileParseExitCode", out var exitCode))
                    result.ExitCodes.Add(exitCode.GetInt32());
            }
        }

        return result;
    }

    private string GetMimeType(string filePath) =>
        Path.GetExtension(filePath).ToLowerInvariant() switch
        {
            ".png"          => "image/png",
            ".jpg" or ".jpeg" => "image/jpeg",
            ".bmp"          => "image/bmp",
            ".tiff" or ".tif" => "image/tiff",
            ".pdf"          => "application/pdf",
            _               => "application/octet-stream"
        };

    public void Dispose()
    {
        _httpClient?.Dispose();
        _rateLimiter?.Dispose();
    }
}

// Custom models — no SDK means you define these yourself
public class OcrResult
{
    public string ParsedText { get; set; } = string.Empty;
    public List<string> Warnings { get; } = new();
    public List<int> ExitCodes { get; } = new();
}

public class OcrSpaceException : Exception
{
    public int StatusCode { get; }
    public OcrSpaceException(string message, int statusCode) : base(message)
        => StatusCode = statusCode;
}

これは、最初のビジネスロジックメソッドの前に存在する、90行以上のインフラストラクチャコードです。 OCR.spaceの統合にはすべてこの定型コード(またはそれに相当するもの)が同梱されており、それを作成するすべてのチームは、OCR.spaceが解決しなかった同じ問題を解決している。

IronOCRを理解する

IronOCR は .NET 用の商用OCRライブラリで、IronOcr NuGetパッケージを介してインストールされます。 最適化されたTesseract 5エンジンをベースに、自動前処理、ネイティブPDF入力、検索可能なPDF出力、125以上の言語パック、構造化された結果抽出機能を搭載。クラウドへの依存、APIキー、リクエストごとの料金は一切不要です。 ドキュメントは、 .NETアプリケーションを実行しているインフラストラクチャ上でローカルに処理されます。

主な特徴:

  • 単一のNuGetパッケージ: dotnet add package IronOcrが完全なインストールです。 管理すべきネイティブバイナリも、tessdataディレクトリも、環境変数もありません。
  • 強く型付けされたAPI: IronTesseract, OcrInput, OcrResult, CropRectangle はすべて、完全なIntelliSenseサポートを備えた一級 for .NETタイプです。 -**自動前処理:**デスクウ、ノイズ除去、コントラスト、二値化、解像度向上フィルターが、外部ライブラリを使用せずに自動的に、または必要に応じて適用されます。 -**ネイティブPDFサポート:**スキャンしたPDFの読み込みと、検索可能なPDFの生成の両方が組み込まれています。 利用可能なメモリ容量を超えるサイズ制限はありません。 -ローカル実行: OCR処理中にネットワーク呼び出しは発生しません。 このライブラリは完全にプロセス内で動作します。 エアギャップ環境は、設定変更なしで動作します。
  • スレッドセーフ: 複数のIronTesseractインスタンスが、ロックや競合管理なしに並行して実行されます。
  • 永続ライセンス: $999 Lite / $1,499 Plus / $2,399 Professional。 リクエスト量に関わらず、リクエストごとの料金は一切かかりません。

機能比較

フィーチャーOCR.spaceIronOCR
NuGetパッケージなし — REST APIのみIronOcr - 完全な.NETサポート
処理場所OCR.space クラウドサーバー地域密着型インフラ
無料プラン月間25,000件のリクエスト(限定)無料トライアル利用可能
有料料金OCR.spaceに現在の価格をお問い合わせください$2,399 一度限りの永続
律速段階60リクエスト/分、500リクエスト/日(無料)None
オフライン機能なしはい、完全にエアギャップされています
データプライバシー第三者に送付された文書ドキュメントはサーバー上に残ります

詳細な機能比較

フィーチャーOCR.spaceIronOCR
統合
NuGetパッケージNoneはい (IronOcr)
厳密な型付けモデルなし — 手動JSON解析はい (OcrResult, OcrInput)
IntelliSenseのサポートNoneフル
カスタム例外タイプ自分自身を定義しなければならない内蔵
再試行ロジック自分で築き上げなければならない内蔵
処理
処理場所クラウドサーバーローカル処理中
インターネット接続が必要ですはい、すべての電話なし
エアギャップサポートなしはい
レート制限はい、すべてのプランNone
ファイルサイズ制限5MB(無料枠)メモリのみ
OCR機能
画像OCRはいはい
PDF OCRはい(ただし制限あり)はい(ネイティブ、サイズ制限なし)
検索可能なPDF出力無料プランでは透かしが入ります出力はきれいで、透かしはありません。
自動前処理なし(サーバー側のみ)デスクウ、ノイズ除去、コントラスト調整、二値化、解像度向上
手動前処理制御NoneフルフィルターAPI
言語サポート約25言語NuGet言語パック経由で125種類以上
文書ごとに複数の言語制限的はい — 主要言語と副言語
構造化された出力(単語数/行数/ページ数)いいえ — プレーンテキストのみはい、ページ、段落、行、座標付きの単語です。
信頼度スコアなしはい、結果ごと、単語ごと
地域ベースのOCRなしはい — CropRectangle
OCR中のバーコード読み取りなしはい — ReadBarCodes = true
デプロイメント
Docker発信インターネット接続が必要ですすぐに使える
Linux発信インターネット接続が必要です完全サポート
HIPAA準拠リスク — 書類があなたの管理下から離れるはい、外部送信はありません
GDPRに準拠リスク — EUのデータ転送は不明確はい、第三者によるデータ処理は行いません。
価格について
価格設定モデル月額サブスクリプション1回限りの永久ライセンス
エントリー価格OCR.spaceに現在の価格をお問い合わせください$999 一度限り
ドキュメントごとのコストが大規模に適用されるはいNone
SLA無料(なし)、有料(金額は変動する)EnterpriseSLAが利用可能

SDKの深度と生のHTTP:統合コスト

OCR.spaceとネイティブSDKの間のギャップは、スタイルの好みの問題ではありません。 これは、開発時間、バグが発生する可能性のある領域、および各デプロイメントにおけるメンテナンスの負担という観点から測定されます。

OCR.space アプローチ

OCR.spaceを使用するすべて for .NET開発者は、独自のHTTPクライアントを作成します。 最もシンプルな実装(再試行ロジックなし、バッチ処理なし、前処理なしの基本的なテキスト抽出)でも、50行以上になります。

// OCR.space: minimum viable extraction — 50歳以上 lines before business logic
public class OcrSpaceBasicExtraction
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;

    public OcrSpaceBasicExtraction(string apiKey)
    {
        _apiKey = apiKey;
        _httpClient = new HttpClient();
    }

    public async Task<string> ExtractText(string imagePath)
    {
        // Read file and encode — document leaves your infrastructure
        byte[] imageBytes = File.ReadAllBytes(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", "eng")
        });

        // Send to cloud
        var response = await _httpClient.PostAsync(
            "https://api.ocr.space/parse/image", content);

        if (!response.IsSuccessStatusCode)
            throw new Exception($"API error: {response.StatusCode}");

        // Parse JSON manually — no typed models
        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);

        return doc.RootElement
            .GetProperty("ParsedResults")[0]
            .GetProperty("ParsedText")
            .GetString() ?? string.Empty;
    }
}
C#

バッチ処理を追加すると、行数は80行以上に増えます。なぜなら、すべてのバッチリクエストには、レート制限によるスロットリング、指数バックオフを使用したリクエストごとの再試行ロジック、およびHTTP 429レスポンスの明示的な処理が必要となるからです。 PDF処理を追加すると、すべての呼び出しの前に5MBのファイルサイズチェックが加わります。なぜなら、無料プランでは、警告ではなくエラーでより大きなファイルが拒否されるからです。

IronOCRのアプローチ

NuGetからIronOCRをインストールすると、既存のメソッド呼び出しでインフラストラクチャコードがすべて置き換えられます。

// IronOCR: complete implementation
var result = new IronTesseract().Read("invoice.png");
Console.WriteLine(result.Text);

IronTesseractクラスは、ファイルの読み取り、前処理、エンジンの実行、および結果の構築を処理します。 HTTPクライアントがありません。 Base64エンコードは使用していません。 JSON解析は行いません。 APIキーがありません。 レート制限なし。 画像からテキストを読み込むチュートリアルでは、ファイルパス、バイト配列、ストリーム、ビットマップなど、入力形式のさまざまなバリエーションを取り上げていますが、それらはすべて同じ1回の呼び出しパターンに従います。

ソースファイルから得られるコードの複雑度に関する数値は、仮説的なものではありません。

シナリオOCR.スペースラインIronOCRライン
基本的な抽出50歳以上3
PDF処理60歳以上12
バッチ処理80歳以上15
エラー処理70歳以上15
完全なクライアントインフラストラクチャ200以上0(NuGetが提供)

バッチ処理とレート制限

レート制限の問題は、OCR.spaceの無料プランが実際の運用環境下で最も顕著に破綻する点である。

OCR.space アプローチ

OCR.spaceの下でのバッチ処理では、毎分60リクエストの制約内に収まるようにSemaphoreSlimを構築および管理し、セマフォロジックが不正確な場合や共有インフラストラクチャがIPアドレスを共有しているときに発生するHTTP 429応答用の指数バックオフリトライロジックが必要です:

// OCR.space batch: 80歳以上 lines of rate-limit plumbing before actual work
public class OcrSpaceBatchProcessing
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly SemaphoreSlim _rateLimiter;

    public OcrSpaceBatchProcessing(string apiKey)
    {
        _apiKey = apiKey;
        _httpClient = new HttpClient();
        _rateLimiter = new SemaphoreSlim(60, 60); // Free tier: 60/minute
    }

    public async Task<Dictionary<string, string>> ProcessBatch(string[] imagePaths)
    {
        var results = new Dictionary<string, string>();

        foreach (var imagePath in imagePaths)
        {
            await _rateLimiter.WaitAsync();

            try
            {
                string text = await ProcessWithRetry(imagePath);
                results[imagePath] = text;
            }
            finally
            {
                _ = Task.Run(async () =>
                {
                    await Task.Delay(1000); // 1 second spacing
                    _rateLimiter.Release();
                });
            }
        }

        return results;
    }

    private async Task<string> ProcessWithRetry(string imagePath, int maxRetries = 3)
    {
        for (int attempt = 0; attempt < maxRetries; attempt++)
        {
            try
            {
                byte[] imageBytes = File.ReadAllBytes(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", "eng")
                });

                var response = await _httpClient.PostAsync(
                    "https://api.ocr.space/parse/image", content);

                if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
                {
                    // Rate limited — exponential backoff
                    await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
                    continue;
                }

                response.EnsureSuccessStatusCode();

                string json = await response.Content.ReadAsStringAsync();
                using var doc = JsonDocument.Parse(json);

                return doc.RootElement
                    .GetProperty("ParsedResults")[0]
                    .GetProperty("ParsedText")
                    .GetString() ?? string.Empty;
            }
            catch (HttpRequestException) when (attempt < maxRetries - 1)
            {
                await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
            }
        }

        throw new Exception($"Failed to process {imagePath} after {maxRetries} attempts");
    }
}
C#

無料プランの1日あたりのリクエスト数上限が500件であることも、この制限をさらに強めている。 1日に600件の文書を処理するアプリケーションは、処理の途中で失敗し、翌日まで自動的に処理が引き継がれません。

IronOCRのアプローチ

IronOCRにはレート制限がありません。 バッチ処理はループです。

// IronOCR batch: 8 lines, no rate limits, no retries needed
public Dictionary<string, string> ProcessBatch(string[] imagePaths)
{
    var results = new Dictionary<string, string>();
    var ocr = new IronTesseract();

    foreach (var imagePath in imagePaths)
    {
        // なし rate limits, no retries, no cloud dependency
        var result = ocr.Read(imagePath);
        results[imagePath] = result.Text;
    }

    return results;
}
C#

バッチ全体でIronTesseractインスタンスを再利用することで、エンジンの初期化オーバーヘッドを繰り返すことを避けられます。 処理速度は、リモートAPIのスロットリングポリシーではなく、ローカルCPUとディスクI/Oによって制限されます。 マルチスレッドの例では、スループットが重要な場合に、コア間で並列処理を行う方法を示しています。

PDF処理

PDFサポートは、2つ目の主要な構造的欠陥を示している。

OCR.space アプローチ

OCR.spaceは有料プランではPDFファイルのアップロードを受け付けており、無料プランでも一部対応していますが、無料アカウントのファイルサイズ上限が5MBであるため、ほとんどの複数ページ文書はアップロードできません。 無料プランの検索可能なPDF出力には透かしが埋め込まれます。 実装には、ページごとの結果抽出と、リクエストごとに明示的なファイルサイズチェックが必要です。

// OCR.space PDF: size check required, watermarks on free tier
public async Task<List<string>> ExtractTextFromPdf(string pdfPath)
{
    var pageTexts = new List<string>();

    // Reject before wasting a request quota entry
    var fileInfo = new FileInfo(pdfPath);
    if (fileInfo.Length > 5 * 1024 * 1024)
        throw new InvalidOperationException("File exceeds 5MB free tier limit. Upgrade or split PDF.");

    byte[] pdfBytes = File.ReadAllBytes(pdfPath);
    string base64 = Convert.ToBase64String(pdfBytes);

    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>("language", "eng"),
        new KeyValuePair<string, string>("filetype", "PDF"),
        new KeyValuePair<string, string>("isCreateSearchablePdf", "false") // Watermarked on free tier
    });

    var response = await _httpClient.PostAsync("https://api.ocr.space/parse/image", content);

    if (!response.IsSuccessStatusCode)
        throw new Exception($"API error: {response.StatusCode}");

    string json = await response.Content.ReadAsStringAsync();
    using var doc = JsonDocument.Parse(json);

    if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
    {
        foreach (var result in results.EnumerateArray())
        {
            if (result.TryGetProperty("ParsedText", out var text))
                pageTexts.Add(text.GetString() ?? string.Empty);
        }
    }

    return pageTexts;
}

IronOCRのアプローチ

IronOCRはPDFをネイティブに読み取ります。 制限となるのは利用可能なメモリ容量であり、任意のファイルサイズしきい値ではない。 検索可能なPDF出力は、どのライセンスレベルでも透かしの​​ないクリーンなファイルを生成します。

// IronOCR PDF: native support, no size limit, no watermarks
public List<string> ExtractTextFromPdf(string pdfPath)
{
    var pageTexts = new List<string>();

    using var input = new OcrInput();
    input.LoadPdf(pdfPath); // なし 5MB ceiling

    var result = new IronTesseract().Read(input);

    foreach (var page in result.Pages)
        pageTexts.Add(page.Text);

    return pageTexts;
}

// Generate searchable PDF — no watermarks
public void CreateSearchablePdf(string inputPath, string outputPath)
{
    var result = new IronTesseract().Read(inputPath);
    result.SaveAsSearchablePdf(outputPath);
}
C#

パスワード保護されたPDFは単一のパラメータ: input.LoadPdf("encrypted.pdf", Password: "secret")PDF OCRの操作ガイドでは、ページ範囲の選択方法とパスワードの処理方法について詳しく説明しています。 検索可能なPDF生成パターンについては、検索可能なPDFの作成方法を参照してください。

エラーハンドリング

OCR.spaceはHTTPステータスコードとJSONのIsErroredOnProcessingフラグの2つの別々のチャンネルを通じてエラーを提供します。 本番コードは両方をチェックし、FileParseExitCode値を確認する必要があります。 これらはすべて型付けされていません — すべてJsonDocumentからの文字列解析です:

// OCR.space: two error layers, all string-based
public async Task<string> SafeExtract(HttpClient client, string apiKey, string imagePath)
{
    try
    {
        byte[] imageBytes = File.ReadAllBytes(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}")
        });

        var response = await client.PostAsync("https://api.ocr.space/parse/image", content);

        if (!response.IsSuccessStatusCode)
        {
            switch (response.StatusCode)
            {
                case System.Net.HttpStatusCode.Unauthorized:
                    throw new Exception("Invalid API key");
                case System.Net.HttpStatusCode.TooManyRequests:
                    throw new Exception("Rate limit exceeded — wait and retry");
                case System.Net.HttpStatusCode.PaymentRequired:
                    throw new Exception("Quota exceeded — upgrade plan");
                default:
                    throw new Exception($"API error: {response.StatusCode}");
            }
        }

        // Second error layer: JSON-level failures
        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);

        if (doc.RootElement.TryGetProperty("IsErroredOnProcessing", out var isError)
            && isError.GetBoolean())
        {
            var messages = new List<string>();
            if (doc.RootElement.TryGetProperty("ErrorMessage", out var errors))
            {
                foreach (var e in errors.EnumerateArray())
                    messages.Add(e.GetString() ?? "Unknown error");
            }
            throw new Exception(string.Join("; ", messages));
        }

        // Third layer: per-page exit codes
        if (doc.RootElement.TryGetProperty("ParsedResults", out var results))
        {
            foreach (var result in results.EnumerateArray())
            {
                if (result.TryGetProperty("FileParseExitCode", out var exitCode)
                    && exitCode.GetInt32() != 1)
                    throw new Exception($"Page parse failed: exit code {exitCode.GetInt32()}");
            }
        }

        return doc.RootElement
            .GetProperty("ParsedResults")[0]
            .GetProperty("ParsedText")
            .GetString() ?? string.Empty;
    }
    catch (JsonException ex)
    {
        throw new Exception($"Invalid API response: {ex.Message}");
    }
    catch (HttpRequestException ex)
    {
        throw new Exception($"Network error: {ex.Message}");
    }
}

IronOCRは、明確なメッセージとともに標準的な.NET例外を発生させます。 JSON解析なし、HTTPステータスコード切り替えなし、階層型エラー抽出なし:

// IronOCR: standard .NET exceptions
public string SafeExtract(string imagePath)
{
    try
    {
        var result = new IronTesseract().Read(imagePath);
        return result.Text;
    }
    catch (FileNotFoundException)
    {
        throw; // File not found — straightforward
    }
    catch (Exception ex) when (ex.Message.Contains("corrupt"))
    {
        throw new Exception($"Image file is corrupt: {imagePath}");
    }
    // なし JSON errors. なし HTTP errors. なし rate limit errors.
}
C#

IronTesseract APIリファレンスOcrResult APIリファレンス は、抽出品質を評価するためのConfidenceプロパティを含む型付き結果構造を文書化しています。

APIマッピングリファレンス

OCR.space コンセプトIronOCR相当値
POST https://api.ocr.space/parse/imagenew IronTesseract().Read(path)
FormUrlEncodedContent / MultipartFormDataContentOcrInput
base64Imageパラメータinput.LoadImage(bytes)
languageパラメータocr.Language = OcrLanguage.English
OCREngineパラメータエンジンは内部で管理されています
isOverlayRequiredパラメータresult.Words / result.Lines (常に利用可能)
isCreateSearchablePdfパラメータresult.SaveAsSearchablePdf(outputPath)
filetype=PDFパラメータinput.LoadPdf(path)
ParsedResults[0].ParsedTextresult.Text
ParsedResults[n] (ページごとに)result.Pages[n].Text
IsErroredOnProcessing JSONフラグ標準 for .NET例外
FileParseExitCode ページごとのフラグ標準 for .NET例外
ProcessingTimeInMilliseconds暗黙的 - 追加の解析は不要
HTTP 429 / レート制限該当なし - レート制限なし
カスタムOcrResult POCO (ユーザ定義)IronOcr.OcrResult (NuGetが提供)
カスタムOcrSpaceException (ユーザ定義)標準 for .NET例外タイプ
SemaphoreSlimレートリミッター (ユーザーが構築)不要
すべてのリクエストにAPIキーを含めるIronOcr.License.LicenseKey (起動時一度だけ)

チームがOCR.spaceからIronOCRへの移行を検討する際

負荷テスト中に無料ティアに到達する

OCR.spaceの1日あたりIPアドレスごとのリクエスト数500件という上限は、緩やかな勧告ではなく、厳格な制限です。 チームは、複数の開発者がオフィスのIPアドレスを共有している負荷テストやステージング環境へのデプロイ中に、この問題に気づく。 OCR.spaceに対して統合テストを実行する共有CI/CDパイプラインは、営業日終了前に1日の割り当て量を使い果たしてしまうでしょう。 その時点でアプリケーションが失敗するのは、コードに誤りがあるからではなく、サードパーティ製のカウンターが任意のしきい値に達したためです。 ローカル処理に移行すれば、この種の障害は完全に解消されます。処理はプロセス内で実行されるため、使い果たすクォータは存在しないからです。

コンプライアンスおよびデータ分類レビュー

文書を個人情報、医療情報、または財務的に機密性の高い情報として分類するセキュリティレビューは、OCR.spaceにとって二者択一の問題を生み出します。つまり、このサービスにはオンプレミス展開のパスがないのです。 HIPAAのシナリオに対応するビジネスアソシエイト契約(BAA)に相当する文書は存在せず、GDPRの下でEUのデータ転送を明確に規定するデータ処理契約の構造も存在せず、契約上の管理措置が講じられている場合でも文書の送信を防止する技術的なメカニズムも存在しない。 クラウド経由で送信される文書処理に関してコンプライアンス違反の指摘を受けたチームは、ローカルなソリューションを必要としている。 IronOCRはこの要件を設計段階から満たしています。つまり、ドキュメントはアプリケーションサーバーから決して外部に送信されません。 ライセンスページには永続ライセンスの構造が記載されており、クラウドベンダーによるレビューを対象範囲から除外することで、コンプライアンス文書の作成も簡素化されます。

統合コードの保守負担

OCR.spaceの統合機能は、その機能要件に比例して技術的負債を蓄積する。 初期のHTTPクライアントは管理しやすい。 そしてチームは再試行ロジックを追加する。 複数のサービスが同じアドレスを共有しているため、IPアドレスごとのレート制限追跡機能が追加されます。 次に、ファイルサイズの事前検証ステップを追加します。その後、エンジン1とエンジン2のレスポンス間でJSONエラー構造が異なることを発見し、分岐を追加します。 6か月後、OCR.spaceとの統合は400行ものインフラストラクチャコードとなり、新しいチームメンバーはOCR関連の作業に携わる前に、そのコードを理解しなければならない。 そのチームがメンテナンスコストを一度きりの$999 IronOCRライセンスと比較すると、算術は簡単です。

構造化された出力要件

OCR.spaceはプレーンテキストのみを返して、それ以外の情報は返しません。 回答には、単語座標、行境界、段落分割、単語ごとの信頼度スコアは含まれていません。 請求書から特定のフィールド(既知の領域にあるベンダー名、右下隅にある合計金額など)を抽出する必要があるアプリケーションは、OCR.spaceの出力を基に構築するための構造化された基盤を持っていません。 IronOCRは、ページ、段落、行、個々の単語といったあらゆる粒度レベルで、座標と信頼度値を含む完全な文書階層構造を公開します。 領域ベースの処理により、ページ全体の出力を後処理することなく、特定の文書領域を対象とすることができます。 読み取り結果の操作方法領域ベースのOCRガイドでは、これらのパターンについて詳しく説明しています。

無料枠を超えたボリューム増加

無料プランは月間25,000件のリクエストまで対応しており、低負荷のシナリオでは十分機能します。 それ以外は有料の階層が適用されます — OCR.spaceに現在の価格をお問い合わせください。 それらのコストは時間と共に増加し、いつの日か、リクエストごとの料金が永続ライセンス$999と比較されるときまで続きます。 月間文書処理量が25,000件を超える見込みのチームにとって、現地処理のコスト削減は明確なメリットとなる。

一般的な移行の考慮事項

HTTPクライアントをメソッド呼び出しに置き換える

OCR.spaceからIronOCRへの移行は、どちらも文書からテキストを返すため、アーキテクチャ的には単純です。 HTTPクライアント、JSONパーサー、レートリミッター、およびカスタム例外タイプが消えます。 それらに代わるのは、単一のNuGetパッケージとメソッド呼び出しです。 サービス境界における変更前/変更後:

// Before: OCR.space service (simplified — actual implementation is 200以上 lines)
public class OcrSpaceService : IDisposable
{
    private readonly HttpClient _client = new();
    private readonly string _apiKey;

    public OcrSpaceService(string apiKey) => _apiKey = apiKey;

    public async Task<string> ProcessDocument(string path)
    {
        byte[] bytes = File.ReadAllBytes(path);
        string base64 = Convert.ToBase64String(bytes);

        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("apikey", _apiKey),
            new KeyValuePair<string, string>("base64Image", $"data:image/png;base64,{base64}")
        });

        var response = await _client.PostAsync("https://api.ocr.space/parse/image", content);
        response.EnsureSuccessStatusCode();

        string json = await response.Content.ReadAsStringAsync();
        using var doc = JsonDocument.Parse(json);
        return doc.RootElement.GetProperty("ParsedResults")[0].GetProperty("ParsedText").GetString()!;
    }

    public void Dispose() => _client.Dispose();
}

// After: IronOCR service — no HttpClient, no JSON, no API key
public class IronOcrService
{
    private readonly IronTesseract _ocr = new();

    public string ProcessDocument(string path)
    {
        return _ocr.Read(path).Text;
    }
    // なし Dispose — no external connections to close
}
C#

HttpClientがないからです。

APIキーの削除

OCR.spaceでは、すべてのHTTPリクエストにAPIキーが必要です。そのキーは安全に保管し、外部に公開する場合はローテーションを行い、ソースコード管理から除外する必要があります。 IronOCRは、アプリケーション起動時に一度だけ設定されるライセンスキーを使用します。このライセンスキーは通常、環境変数または設定システムから取得されます。

// At application startup — once, not per request
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE");

移行後は、APIキーのローテーションプロセス、シークレット管理の設定、およびリクエストごとのキー挿入ロジックはすべて不要になります。

移行後の前処理

OCR.spaceは結果を返す前にサーバー側で処理を行いますが、開発者はその処理内容を制御することはできません。 IronOCRは、明示的な前処理方法を公開しています。 文書の品質が懸念される場合(傾いたスキャン画像、コントラストの低いコピー、ノイズの多いファックス出力など)、前処理パイプラインは3~5つのメソッド呼び出しで構成されます。

using var input = new OcrInput();
input.LoadImage("low-quality-scan.jpg");
input.Deskew();
input.DeNoise();
input.Contrast();
input.Binarize();
input.EnhanceResolution(300);
var result = new IronTesseract().Read(input);

画像品質補正ガイド画像色補正ガイドには、利用可能なすべてのフィルターが記載されており、補正前後の精度差を示す例が添えられています。

非同期処理と同期処理

OCR.space の呼び出しは、HTTP の往復通信を伴うため、本質的に非同期です。 IronOCRの呼び出しはデフォルトでは同期的に行われるため、Web以外の環境ではコードが簡素化されます。 非同期実行を要求するASP.NETおよびサーバーのシナリオの場合、IronOCRはTask.Runラッピングまたは直接非同期APIを通じて非同期操作をサポートします。 非同期OCRガイドでは、パターンについて説明しています。 以前にクラウド呼び出しを待機していたサービスメソッドからawaitを削除することで、OCR.spaceがそれを必要としているコンテキストで非同期が採用された場合のコールスタックが簡素化されます。

IronOCRの追加機能

上記のセクションでは説明されていない機能で、それぞれOCR.spaceには同等の機能がないものです。

  • hOCRエクスポート: result.SaveAsHocrFile("output.hocr")は、下流の文書処理パイプラインのための標準準拠のhOCR出力を生成します。 -**進捗状況の追跡長時間実行される複数ページにわたるバッチ処理の進捗イベントを購読することで、UIアプリケーションで進捗バーや完了予定時刻の計算が可能になります。 -テーブル抽出:**構造化されたテーブルデータは結果モデルを介してアクセス可能であり、列の整列が必要な請求書明細項目の抽出などのユースケースをサポートします。
  • 専門的な文書読み取り: パスポートMRZゾーン、ナンバープレート、MICR小切手行、手書き認識は、同じIronTesseract APIを通じてアクセス可能な目的に合った機能です。

.NETの互換性と将来の準備

IronOCRは.NET 6、 .NET 7、 .NET 8、および.NET 9を対象としており、各LTSおよびSTSリリースを積極的にサポートしています。 このライブラリは、Windows x64、Windows x86、Linux x64、macOS、Docker、AWS Lambda、Azure App Serviceで動作します。しかも、すべて同じNuGetパッケージから入手でき、プラットフォーム固有の設定は不要です。 OCR.spaceは、すべての展開環境において発信HTTPS接続を必須としています。そのため、エアギャップネットワーク、制限的な企業プロキシ、およびサードパーティAPIへの発信トラフィックがポリシーによってブロックされているインフラストラクチャは対象外となります。 IronOCRには実行時のネットワーク要件はありません。 完全にプロセス内で実行されます。 .NET 10が2026年後半のリリースに向けて進む中、IronOCRは.NETのメジャーバージョン間で互換性を維持してきた実績があり、統合の変更なしに継続性を提供します。

結論

OCR.spaceは、週末にOCR機能のプロトタイプを作成する必要がある開発者、テキスト抽出の概念を研究している学生、あるいはコンプライアンス要件のない月間25,000件以下の小規模な個人用ツールなど、真に役立つニッチな市場を占めています。 その層にとって、無料プランは真の価値を提供する。

問題は、 .NET開発者は通常、週末にプロトタイプを作成しているわけではないということだ。彼らは請求書システム、医療記録処理システム、文書アーカイブパイプライン、そしてコンプライアンスに敏感なビジネスアプリケーションなどを構築しているのだ。 そういった状況においては、OCR.spaceにNuGetパッケージが存在しないことは、単なる不便ではなく、構造的な非互換性である。 HTTPクライアント、レートリミッター、JSONパーサー、およびリトライインフラストラクチャの構築に費やす1時間は、アプリケーションの実際の要件に費やせない1時間となる。 その費用は初期費用として発生しますが、メンテナンス費用はシステム統合のライフサイクル全体にわたって発生します。

IronOCRは、OCR.spaceのあらゆる統合において共通する、真のSDKの欠如という特定の失敗モードに対処します。 NuGetパッケージ1つ、メソッド呼び出し1つ、HTTP関連の処理なし、レート制限なし、外部サーバーへのドキュメント送信なし。 $999の初期費用は1回限りの費用です。継続的なOCR.spaceサブスクリプションコストは、特にボリュームが増加すると、これを超えることになります。 コンプライアンス要件のあるチームにとっては、コストに関係なくローカル処理が唯一の選択肢です。

最終的に、この比較は一つの疑問に集約されます。アプリケーションはOCRを外部REST依存関係として必要とするのか、それともライブラリ関数として必要とするのか? 本番環境で使用される.NETアプリケーションの場合、答えはほぼ常に後者です。

ご注意: OCR.spaceおよびTesseractはそれぞれの所有者の登録商標です。 このサイトはGoogleおよびOCR.spaceと提携せず、承認もサポートもされていません。 すべての製品名、ロゴ、およびブランドは各所有者の所有物です。 比較は情報提供のみを目的としており、執筆時点で公開されている情報を反映しています。

関連する記事

Key in blue circle

無料の30日間トライアルキーをすぐに入手してください。

Your trial license will be sent to your email address

制限なし。100% ロック解除済み。クレジットカード不要。

bullet_checkedクレジットカードやアカウントの作成は不要です。制限なし。100% ロック解除済み。クレジットカード不要。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
義務のない相談を受ける
下記のフォームを記入するか、sales@ironsoftware.comにメールしてください。
あなたの詳細は常に守秘されます。
世界中の数百万人のエンジニアから信頼されています。
ライセンスはより安く
あなたの無料30日間の試用キーをすぐに入手。
クレジットカードやアカウントの作成は不要です。