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

C#でWindowsにTesseract OCRをインストールする方法

このガイドでは、 .NET開発者がGoogle Cloud VisionをIronOCRに置き換えるための手順を、オンプレミスのOCRエンジンとしてそのまま利用できる方法を説明します。認証情報の削除、Protobuf注釈解析の置き換え、バッチ注釈の簡素化、複数ページ文書の処理という、移行作業の大部分を占める4つの構造的変更について解説します。

Google Cloud Vision OCRから移行する理由

移行の決定は、ほぼ必ず次の2つの認識のいずれかから始まります。コンプライアンス監査によってクラウド上のドキュメント送信がブロックされるか、GCP認証情報、GCSバケット、非同期ポーリング、画像ごとの課金といった管理にかかる運用上のコストが、OCR自体のコストよりも高くなるかのどちらかです。

サービスアカウントJSONキーのライフサイクル。開発マシン、CI/CDパイプライン、ステージングサーバー、本番サーバー、Dockerコンテナ、Kubernetesポッドなど、アプリケーションのデプロイごとに、同じサービスアカウントJSONキーファイルが必要です。このファイルにはRSA秘密鍵が含まれています。 ソース管理には決して組み込んではならず、定期的にローテーションされ、ファイルシステムのアクセス許可によって保護され、ローテーションが行われる際にはすべての環境で同時に更新されなければならない。 侵害されたキーは、GCPコンソールで手動で取り消されるまで、APIへのアクセスを許可します。 IronOCRは、この操作インターフェース全体を、アプリケーション起動時に一度だけ設定される単一のライセンスキー文字列に置き換えます。

リクエストごとの課金が大規模に適用される。画像1,000枚あたり1.50ドル、PDFページ1ページあたり0.0015ドルという料金体系は、開発段階ではコストが目に見えないものの、生産段階では大きな負担となる。 月間20万ページを処理する文書処理パイプラインは、GCSストレージ料金やデータ送信料金を除いても、API料金だけで月額300ドルかかる。 その300ドルは毎月、無期限に支払われる。 IronOCRの永久ライセンスは、OCRを従量制の運用費用から、2年目や3年目には運用コストがかからない固定資本項目へと転換します。

GCS の PDF 用非同期パイプライン。Google Cloud Vision は、PDF を API への直接入力として受け付けません。 完全なパイプラインには、2番目のNuGetパッケージ(AsyncBatchAnnotateFilesAsync呼び出し、ポーリングループ、GCSからのJSON出力解析、およびクリーンアップステップが必要です。そのパイプラインは、テキストが抽出される前に50行以上のコードを必要とします。 IronOCRは、外部依存関係なしに、PDFを3行で同期的に読み取ります。

プロトバフシンボル結合。DOCUMENT_TEXT_DETECTION応答は、ページ、ブロック、段落、単語、シンボルのプロトバフ階層の内部にシンボルレベルでテキストを格納します。 段落テキストを読むには、5つのネストされたループを反復し、.SelectMany(w => w.Symbols).Select(s => s.Text)を呼び出す必要があります。 IronOCRは段落テキストをparagraph.Textとして返します - 型付き文字列プロパティです。

1,800リクエスト毎分デフォルトクォータ。デフォルトのクォータを超えるバッチワークロードは、超過ごとにパイプラインを60秒間停止させるStatusCode.ResourceExhausted応答を受け取ります。 割り当て量を増やすには、GCPコンソールからのリクエストとGoogleの承認が必要です。 IronOCRは利用可能なCPUコアの速度でローカルに処理を行うため、管理すべきクォータも、承認を得る必要も、レート制限のための再試行ロジックも不要です。

オフライン環境やエアギャップ環境ではサポートされていません。Google Cloud Visionは、Googleのエンドポイントへのインターネット接続が必要です。 エアギャップネットワーク、機密データセンター、および産業用制御システムでは、アーキテクチャの複雑さのレベルに関わらず、これを使用することはできません。 IronOCRは、最初のライセンス認証後、外部ネットワークへの接続を一切行わずに動作します。

基本的な問題

Google Cloud Visionでは、OCRコードの最初の行が実行される前に、ディスク上にJSONキーファイルが存在する必要があります。

// Google Cloud Vision: JSON key file deployed to every server before this line works
// GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
// Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create();
var image = Image.FromFile("document.jpg");
var response = _client.DetectText(image);   // document leaves your infrastructure
string text = response[0].Description;
// Google Cloud Vision: JSON key file deployed to every server before this line works
// GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
// Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create();
var image = Image.FromFile("document.jpg");
var response = _client.DetectText(image);   // document leaves your infrastructure
string text = response[0].Description;
' Google Cloud Vision: JSON key file deployed to every server before this line works
' GOOGLE_APPLICATION_CREDENTIALS="/etc/secrets/service-account.json" must be set
' Key contains RSA private key — rotate manually, revoke if compromised
_client = ImageAnnotatorClient.Create()
Dim image = Image.FromFile("document.jpg")
Dim response = _client.DetectText(image)   ' document leaves your infrastructure
Dim text As String = response(0).Description
$vbLabelText   $csharpLabel

IronOCRは1つの文字列から始まり、完全にローカルマシン上で動作します。

// IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
string text = new IronTesseract().Read("document.jpg").Text;  // local, no cloud
// IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
string text = new IronTesseract().Read("document.jpg").Text;  // local, no cloud
Imports IronOcr

' IronOCR: one string at startup, no key files, no environment variables
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
Dim text As String = New IronTesseract().Read("document.jpg").Text ' local, no cloud
$vbLabelText   $csharpLabel

IronOCRとGoogle Cloud Vision OCR:機能比較

以下の表は、移行のビジネスケースを作成するチームにとって役立つ機能を直接マッピングしたものです。

フィーチャー Google Cloud Vision OCR IronOCR
処理場所 Google Cloud(リモート) オンプレミス(ローカル)
認証 サービスアカウントのJSONキーと環境変数 ライセンスキー文字列
PDF入力 GCSへのアップロード + 非同期API input.LoadPdf() 直接
パスワードで保護されたPDF サポートされていません LoadPdf(path, Password: "...")
複数ページTIFF入力 制限的 input.LoadImageFrames()
検索可能なPDF出力 不可 result.SaveAsSearchablePdf()
構造化データへのアクセス Protobuf: ページ > ブロック > 段落 > 単語 > 記号 result.Words(型付き.NETオブジェクト)
段落テキストプロパティ いいえ — シンボル連結が必要です paragraph.Text 直接プロパティ
信頼度スコア シンボルごと(ループが必要) result.Confidence, word.Confidence
画像の自動前処理 なし(機械学習が処理します) 傾き補正、ノイズ除去、コントラスト調整、二値化、シャープ化
地域ベースのOCR 在来作物なし CropRectangle 上の OcrInput
バーコード読み取り 別個のAPI機能 ocr.Configuration.ReadBarCodes = true
レート制限 デフォルト:1,800リクエスト/分 なし(CPUバウンド)
オフライン/エアギャップ なし はい
文書ごとの費用 画像1,000枚あたり1.50ドル。 PDFページあたり0.0015ドル なし(永久ライセンス)
対応言語 約50 125+
FedRAMP認証 許可されていません 該当なし(オンプレミス)
HIPAA準拠への道筋 ビジネスアソシエイト契約が必要です 第三者によるデータ処理は行いません。
.NET Frameworkのサポート .NET Standard 2.0以降 .NET Framework 4.6.2以降および.NET 5/6/7/8/9
必要なNuGetパッケージ Google.Cloud.Vision.V1 + Google.Cloud.Storage.V1 IronOcr のみ
価格設定モデル リクエストごとの従量課金 永続的($999 Lite / $1,499 Plus / $2,999 Professional / $5,999 Unlimited)

クイックスタート:Google Cloud Vision OCR からIronOCRへの移行

ステップ 1: NuGet パッケージを置き換える

Google Cloud パッケージを削除します。

dotnet remove package Google.Cloud.Vision.V1
dotnet remove package Google.Cloud.Storage.V1
dotnet remove package Google.Cloud.Vision.V1
dotnet remove package Google.Cloud.Storage.V1
SHELL

NuGetパッケージページからIronOCRをインストールしてください。

dotnet add package IronOcr

ステップ 2: 名前空間の更新

Google Cloud ネームスペースを IronOCR ネームスペースに置き換えてください:

// Before (Google Cloud Vision)
using Google.Cloud.Vision.V1;
using Google.Cloud.Storage.V1;
using Google.Protobuf;
using Grpc.Core;

// After (IronOCR)
using IronOcr;
// Before (Google Cloud Vision)
using Google.Cloud.Vision.V1;
using Google.Cloud.Storage.V1;
using Google.Protobuf;
using Grpc.Core;

// After (IronOCR)
using IronOcr;
Imports IronOcr
$vbLabelText   $csharpLabel

ステップ 3: ライセンスの初期化

ライセンスの初期化をアプリケーションの起動時に1回だけ追加し、IronTesseractインスタンスが作成される前に実行します:

IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"
$vbLabelText   $csharpLabel

本番環境では、環境変数またはシークレットマネージャからキーを読み取ります。

IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
    ?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable not set.");
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
    ?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable not set.");
Imports System
Imports IronOcr

IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE"), Throw New InvalidOperationException("IRONOCR_LICENSE environment variable not set."))
$vbLabelText   $csharpLabel

コード移行の例

サービスアカウント認証情報の構成をなくす

Google Cloud Visionクライアントの初期化は一見1行のように見えますが、実際には広範な前提条件となるインフラストラクチャが必要です。 プロジェクトに参加するすべての開発者、すべてのデプロイ環境、およびすべての CI/CD パイプラインは、コンストラクタが例外をスローせずに完了する前に、完全な認証情報構成が設定されている必要があります。

Google Cloudのビジョンアプローチ:

using Google.Cloud.Vision.V1;

// Prerequisites before this class can be instantiated:
// 1. GCP project created and Vision API enabled in GCP Console
// 2. Service account created with roles/cloudvision.user IAM role
// 3. JSON key file downloaded to every server that runs this code
// 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
// 5. JSON file excluded from source control via .gitignore
// 6. Key rotation schedule established (recommended: 90 days)
// 7. Separate credentials per environment (dev/staging/prod)

public class DocumentOcrService
{
    private readonly ImageAnnotatorClient _client;
    private readonly string _projectId;

    public DocumentOcrService(string projectId)
    {
        _projectId = projectId;
        // Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
        _client = ImageAnnotatorClient.Create();
    }

    public string ReadDocument(string imagePath)
    {
        var image = Image.FromFile(imagePath);
        var response = _client.DetectText(image);
        return response.Count > 0 ? response[0].Description : string.Empty;
    }
}
using Google.Cloud.Vision.V1;

// Prerequisites before this class can be instantiated:
// 1. GCP project created and Vision API enabled in GCP Console
// 2. Service account created with roles/cloudvision.user IAM role
// 3. JSON key file downloaded to every server that runs this code
// 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
// 5. JSON file excluded from source control via .gitignore
// 6. Key rotation schedule established (recommended: 90 days)
// 7. Separate credentials per environment (dev/staging/prod)

public class DocumentOcrService
{
    private readonly ImageAnnotatorClient _client;
    private readonly string _projectId;

    public DocumentOcrService(string projectId)
    {
        _projectId = projectId;
        // Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
        _client = ImageAnnotatorClient.Create();
    }

    public string ReadDocument(string imagePath)
    {
        var image = Image.FromFile(imagePath);
        var response = _client.DetectText(image);
        return response.Count > 0 ? response[0].Description : string.Empty;
    }
}
Imports Google.Cloud.Vision.V1

' Prerequisites before this class can be instantiated:
' 1. GCP project created and Vision API enabled in GCP Console
' 2. Service account created with roles/cloudvision.user IAM role
' 3. JSON key file downloaded to every server that runs this code
' 4. GOOGLE_APPLICATION_CREDENTIALS env var pointing to the JSON file
' 5. JSON file excluded from source control via .gitignore
' 6. Key rotation schedule established (recommended: 90 days)
' 7. Separate credentials per environment (dev/staging/prod)

Public Class DocumentOcrService
    Private ReadOnly _client As ImageAnnotatorClient
    Private ReadOnly _projectId As String

    Public Sub New(projectId As String)
        _projectId = projectId
        ' Throws RpcException(StatusCode.PermissionDenied) if any prerequisite is missing
        _client = ImageAnnotatorClient.Create()
    End Sub

    Public Function ReadDocument(imagePath As String) As String
        Dim image = Image.FromFile(imagePath)
        Dim response = _client.DetectText(image)
        Return If(response.Count > 0, response(0).Description, String.Empty)
    End Function
End Class
$vbLabelText   $csharpLabel

IronOCRのアプローチ:

using IronOcr;

// Prerequisites: set the license key once at app startup
// No JSON files, no environment variables beyond the key, no GCP Console configuration
// No key rotation, no IAM roles, no per-environment credential sets

public class DocumentOcrService
{
    private readonly IronTesseract _ocr;

    public DocumentOcrService()
    {
        // IronTesseract is ready immediately — no external validation required
        _ocr = new IronTesseract();
    }

    public string ReadDocument(string imagePath)
    {
        return _ocr.Read(imagePath).Text;
    }
}
using IronOcr;

// Prerequisites: set the license key once at app startup
// No JSON files, no environment variables beyond the key, no GCP Console configuration
// No key rotation, no IAM roles, no per-environment credential sets

public class DocumentOcrService
{
    private readonly IronTesseract _ocr;

    public DocumentOcrService()
    {
        // IronTesseract is ready immediately — no external validation required
        _ocr = new IronTesseract();
    }

    public string ReadDocument(string imagePath)
    {
        return _ocr.Read(imagePath).Text;
    }
}
Imports IronOcr

' Prerequisites: set the license key once at app startup
' No JSON files, no environment variables beyond the key, no GCP Console configuration
' No key rotation, no IAM roles, no per-environment credential sets

Public Class DocumentOcrService
    Private ReadOnly _ocr As IronTesseract

    Public Sub New()
        ' IronTesseract is ready immediately — no external validation required
        _ocr = New IronTesseract()
    End Sub

    Public Function ReadDocument(imagePath As String) As String
        Return _ocr.Read(imagePath).Text
    End Function
End Class
$vbLabelText   $csharpLabel

運用上の違いは具体的です:Google Cloud Visionは実行時にRpcExceptionの5つのカテゴリを生成します - Unauthenticated - 各カテゴリは異なるインフラ障害モードを表します。 IronOCRの障害モードは、OcrException(処理失敗)です。 設定オプションについてはIronTesseractのセットアップガイドを、ライセンスの詳細についてはIronOCRの製品ページを参照してください。

バッチ注釈リクエストを複数のフィーチャタイプに置き換える

Google Cloud Visionは複数の画像を1つのDOCUMENT_TEXT_DETECTIONの結果を収集する必要がある場合や、多くの画像を送信してラウンドトリップオーバーヘッドを最小化したい場合に使用します。 プロトバフ応答では、各AnnotateImageResponseをインデックスで元のリクエストに戻す必要があります。

Google Cloudのビジョンアプローチ:

using Google.Cloud.Vision.V1;
using System.Collections.Generic;

public class BatchAnnotationService
{
    private readonly ImageAnnotatorClient _client;

    public BatchAnnotationService()
    {
        _client = ImageAnnotatorClient.Create();
    }

    public List<string> BatchAnnotateImages(string[] imagePaths)
    {
        // Build one request per image with TEXT_DETECTION feature
        var requests = imagePaths.Select(path => new AnnotateImageRequest
        {
            Image = Image.FromFile(path),
            Features =
            {
                new Feature { Type = Feature.Types.Type.TextDetection },
                new Feature { Type = Feature.Types.Type.DocumentTextDetection }
            }
        }).ToList();

        // Single round-trip for all images in the batch
        var batchResponse = _client.BatchAnnotateImages(requests);

        // Match responses back to requests by index
        var results = new List<string>();
        for (int i = 0; i < batchResponse.Responses.Count; i++)
        {
            var response = batchResponse.Responses[i];
            if (response.Error != null)
            {
                // Per-item error in batch — must handle individually
                results.Add($"Error on {imagePaths[i]}: {response.Error.Message}");
                continue;
            }

            // Prefer DOCUMENT_TEXT_DETECTION full text if available
            var fullText = response.FullTextAnnotation?.Text
                ?? response.TextAnnotations.FirstOrDefault()?.Description
                ?? string.Empty;
            results.Add(fullText);
        }

        return results;
    }
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;

public class BatchAnnotationService
{
    private readonly ImageAnnotatorClient _client;

    public BatchAnnotationService()
    {
        _client = ImageAnnotatorClient.Create();
    }

    public List<string> BatchAnnotateImages(string[] imagePaths)
    {
        // Build one request per image with TEXT_DETECTION feature
        var requests = imagePaths.Select(path => new AnnotateImageRequest
        {
            Image = Image.FromFile(path),
            Features =
            {
                new Feature { Type = Feature.Types.Type.TextDetection },
                new Feature { Type = Feature.Types.Type.DocumentTextDetection }
            }
        }).ToList();

        // Single round-trip for all images in the batch
        var batchResponse = _client.BatchAnnotateImages(requests);

        // Match responses back to requests by index
        var results = new List<string>();
        for (int i = 0; i < batchResponse.Responses.Count; i++)
        {
            var response = batchResponse.Responses[i];
            if (response.Error != null)
            {
                // Per-item error in batch — must handle individually
                results.Add($"Error on {imagePaths[i]}: {response.Error.Message}");
                continue;
            }

            // Prefer DOCUMENT_TEXT_DETECTION full text if available
            var fullText = response.FullTextAnnotation?.Text
                ?? response.TextAnnotations.FirstOrDefault()?.Description
                ?? string.Empty;
            results.Add(fullText);
        }

        return results;
    }
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Linq

Public Class BatchAnnotationService
    Private ReadOnly _client As ImageAnnotatorClient

    Public Sub New()
        _client = ImageAnnotatorClient.Create()
    End Sub

    Public Function BatchAnnotateImages(imagePaths As String()) As List(Of String)
        ' Build one request per image with TEXT_DETECTION feature
        Dim requests = imagePaths.Select(Function(path) New AnnotateImageRequest With {
            .Image = Image.FromFile(path),
            .Features = {
                New Feature With {.Type = Feature.Types.Type.TextDetection},
                New Feature With {.Type = Feature.Types.Type.DocumentTextDetection}
            }
        }).ToList()

        ' Single round-trip for all images in the batch
        Dim batchResponse = _client.BatchAnnotateImages(requests)

        ' Match responses back to requests by index
        Dim results = New List(Of String)()
        For i As Integer = 0 To batchResponse.Responses.Count - 1
            Dim response = batchResponse.Responses(i)
            If response.Error IsNot Nothing Then
                ' Per-item error in batch — must handle individually
                results.Add($"Error on {imagePaths(i)}: {response.Error.Message}")
                Continue For
            End If

            ' Prefer DOCUMENT_TEXT_DETECTION full text if available
            Dim fullText = If(response.FullTextAnnotation?.Text, response.TextAnnotations.FirstOrDefault()?.Description, String.Empty)
            results.Add(fullText)
        Next

        Return results
    End Function
End Class
$vbLabelText   $csharpLabel

IronOCRのアプローチ:

using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;

public class BatchAnnotationService
{
    public List<string> BatchAnnotateImages(string[] imagePaths)
    {
        var results = new ConcurrentDictionary<int, string>();

        // Parallel processing — no batch size limit, no network round-trips
        Parallel.For(0, imagePaths.Length, i =>
        {
            var ocr = new IronTesseract();   // thread-safe: one instance per thread
            results[i] = ocr.Read(imagePaths[i]).Text;
        });

        // Reconstruct in original order
        return Enumerable.Range(0, imagePaths.Length)
            .Select(i => results[i])
            .ToList();
    }

    public OcrResult BatchAsDocument(string[] imagePaths)
    {
        // Load all images into a single OcrInput for combined document output
        using var input = new OcrInput();
        foreach (var path in imagePaths)
            input.LoadImage(path);

        return new IronTesseract().Read(input);
    }
}
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;

public class BatchAnnotationService
{
    public List<string> BatchAnnotateImages(string[] imagePaths)
    {
        var results = new ConcurrentDictionary<int, string>();

        // Parallel processing — no batch size limit, no network round-trips
        Parallel.For(0, imagePaths.Length, i =>
        {
            var ocr = new IronTesseract();   // thread-safe: one instance per thread
            results[i] = ocr.Read(imagePaths[i]).Text;
        });

        // Reconstruct in original order
        return Enumerable.Range(0, imagePaths.Length)
            .Select(i => results[i])
            .ToList();
    }

    public OcrResult BatchAsDocument(string[] imagePaths)
    {
        // Load all images into a single OcrInput for combined document output
        using var input = new OcrInput();
        foreach (var path in imagePaths)
            input.LoadImage(path);

        return new IronTesseract().Read(input);
    }
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading.Tasks

Public Class BatchAnnotationService
    Public Function BatchAnnotateImages(imagePaths As String()) As List(Of String)
        Dim results = New ConcurrentDictionary(Of Integer, String)()

        ' Parallel processing — no batch size limit, no network round-trips
        Parallel.For(0, imagePaths.Length, Sub(i)
                                               Dim ocr = New IronTesseract() ' thread-safe: one instance per thread
                                               results(i) = ocr.Read(imagePaths(i)).Text
                                           End Sub)

        ' Reconstruct in original order
        Return Enumerable.Range(0, imagePaths.Length) _
            .Select(Function(i) results(i)) _
            .ToList()
    End Function

    Public Function BatchAsDocument(imagePaths As String()) As OcrResult
        ' Load all images into a single OcrInput for combined document output
        Using input As New OcrInput()
            For Each path In imagePaths
                input.LoadImage(path)
            Next
            Return New IronTesseract().Read(input)
        End Using
    End Function
End Class
$vbLabelText   $csharpLabel

IronOCRは、ネットワークオーバーヘッドを一切発生させることなく、CPUコア間でバッチ処理を並列実行します。 BatchSizeの上限も、応答インデックスの一致も、ネットワークまたは認証情報の失敗に対する項目ごとのエラーハンドリングもありません。 複数の画像を単一の論理ドキュメントに結合するワークロードの場合(例:個々のJPEGとして提出されたスキャンされた複数ページのフォーム)、OcrResultを返します。 マルチスレッドの例では、並列処理のパフォーマンスベンチマークを示しています。

Protobufワードレベル注釈抽出の移行

Google Cloud Visionの単語レベルの境界ボックスと信頼度データを取得するには、ページ、ブロック、段落、単語、シンボルというProtobufの階層全体をたどる必要があります。 単語テキストを抽出するには、各単語からシンボルを連結する必要があります - .Textプロパティがありません。 バウンディングボックスの座標は、個別のX、Y、幅、高さのフィールドではなく、BoundingPolyリストとして保存されます。

Google Cloudのビジョンアプローチ:

using Google.Cloud.Vision.V1;
using System.Collections.Generic;

public record WordAnnotation(string Text, int X, int Y, int Width, int Height, float Confidence);

public class WordLevelExtractor
{
    private readonly ImageAnnotatorClient _client;

    public WordLevelExtractor()
    {
        _client = ImageAnnotatorClient.Create();
    }

    public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
    {
        var image = Image.FromFile(imagePath);
        var annotation = _client.DetectDocumentText(image);

        var words = new List<WordAnnotation>();

        // Navigate: Pages -> Blocks -> Paragraphs -> Words
        foreach (var page in annotation.Pages)
        {
            foreach (var block in page.Blocks)
            {
                foreach (var paragraph in block.Paragraphs)
                {
                    foreach (var word in paragraph.Words)
                    {
                        // Word.Text does not exist — must concatenate Symbols
                        var text = string.Concat(word.Symbols.Select(s => s.Text));

                        // BoundingPoly has Vertices, not X/Y/Width/Height
                        var vertices = word.BoundingBox.Vertices;
                        int x = vertices[0].X;
                        int y = vertices[0].Y;
                        int width = vertices.Count > 1 ? vertices[1].X - vertices[0].X : 0;
                        int height = vertices.Count > 2 ? vertices[2].Y - vertices[0].Y : 0;

                        words.Add(new WordAnnotation(text, x, y, width, height, word.Confidence));
                    }
                }
            }
        }

        return words;
    }
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;

public record WordAnnotation(string Text, int X, int Y, int Width, int Height, float Confidence);

public class WordLevelExtractor
{
    private readonly ImageAnnotatorClient _client;

    public WordLevelExtractor()
    {
        _client = ImageAnnotatorClient.Create();
    }

    public List<WordAnnotation> ExtractWordAnnotations(string imagePath)
    {
        var image = Image.FromFile(imagePath);
        var annotation = _client.DetectDocumentText(image);

        var words = new List<WordAnnotation>();

        // Navigate: Pages -> Blocks -> Paragraphs -> Words
        foreach (var page in annotation.Pages)
        {
            foreach (var block in page.Blocks)
            {
                foreach (var paragraph in block.Paragraphs)
                {
                    foreach (var word in paragraph.Words)
                    {
                        // Word.Text does not exist — must concatenate Symbols
                        var text = string.Concat(word.Symbols.Select(s => s.Text));

                        // BoundingPoly has Vertices, not X/Y/Width/Height
                        var vertices = word.BoundingBox.Vertices;
                        int x = vertices[0].X;
                        int y = vertices[0].Y;
                        int width = vertices.Count > 1 ? vertices[1].X - vertices[0].X : 0;
                        int height = vertices.Count > 2 ? vertices[2].Y - vertices[0].Y : 0;

                        words.Add(new WordAnnotation(text, x, y, width, height, word.Confidence));
                    }
                }
            }
        }

        return words;
    }
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Linq

Public Class WordAnnotation
    Public Property Text As String
    Public Property X As Integer
    Public Property Y As Integer
    Public Property Width As Integer
    Public Property Height As Integer
    Public Property Confidence As Single

    Public Sub New(text As String, x As Integer, y As Integer, width As Integer, height As Integer, confidence As Single)
        Me.Text = text
        Me.X = x
        Me.Y = y
        Me.Width = width
        Me.Height = height
        Me.Confidence = confidence
    End Sub
End Class

Public Class WordLevelExtractor
    Private ReadOnly _client As ImageAnnotatorClient

    Public Sub New()
        _client = ImageAnnotatorClient.Create()
    End Sub

    Public Function ExtractWordAnnotations(imagePath As String) As List(Of WordAnnotation)
        Dim image = Image.FromFile(imagePath)
        Dim annotation = _client.DetectDocumentText(image)

        Dim words As New List(Of WordAnnotation)()

        ' Navigate: Pages -> Blocks -> Paragraphs -> Words
        For Each page In annotation.Pages
            For Each block In page.Blocks
                For Each paragraph In block.Paragraphs
                    For Each word In paragraph.Words
                        ' Word.Text does not exist — must concatenate Symbols
                        Dim text = String.Concat(word.Symbols.Select(Function(s) s.Text))

                        ' BoundingPoly has Vertices, not X/Y/Width/Height
                        Dim vertices = word.BoundingBox.Vertices
                        Dim x As Integer = vertices(0).X
                        Dim y As Integer = vertices(0).Y
                        Dim width As Integer = If(vertices.Count > 1, vertices(1).X - vertices(0).X, 0)
                        Dim height As Integer = If(vertices.Count > 2, vertices(2).Y - vertices(0).Y, 0)

                        words.Add(New WordAnnotation(text, x, y, width, height, word.Confidence))
                    Next
                Next
            Next
        Next

        Return words
    End Function
End Class
$vbLabelText   $csharpLabel

IronOCRのアプローチ:

using IronOcr;
using System.Collections.Generic;

public record WordAnnotation(string Text, int X, int Y, int Width, int Height, double Confidence);

public class WordLevelExtractor
{
    private readonly IronTesseract _ocr;

    public WordLevelExtractor()
    {
        _ocr = new IronTesseract();
    }

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

        // Words are a flat collection — no hierarchy traversal, no symbol concatenation
        return result.Words.Select(w => new WordAnnotation(
            Text:       w.Text,         // direct string property
            X:          w.X,
            Y:          w.Y,
            Width:      w.Width,
            Height:     w.Height,
            Confidence: w.Confidence    // double, no conversion needed
        )).ToList();
    }
}
using IronOcr;
using System.Collections.Generic;

public record WordAnnotation(string Text, int X, int Y, int Width, int Height, double Confidence);

public class WordLevelExtractor
{
    private readonly IronTesseract _ocr;

    public WordLevelExtractor()
    {
        _ocr = new IronTesseract();
    }

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

        // Words are a flat collection — no hierarchy traversal, no symbol concatenation
        return result.Words.Select(w => new WordAnnotation(
            Text:       w.Text,         // direct string property
            X:          w.X,
            Y:          w.Y,
            Width:      w.Width,
            Height:     w.Height,
            Confidence: w.Confidence    // double, no conversion needed
        )).ToList();
    }
}
Imports IronOcr
Imports System.Collections.Generic

Public Class WordAnnotation
    Public Property Text As String
    Public Property X As Integer
    Public Property Y As Integer
    Public Property Width As Integer
    Public Property Height As Integer
    Public Property Confidence As Double

    Public Sub New(text As String, x As Integer, y As Integer, width As Integer, height As Integer, confidence As Double)
        Me.Text = text
        Me.X = x
        Me.Y = y
        Me.Width = width
        Me.Height = height
        Me.Confidence = confidence
    End Sub
End Class

Public Class WordLevelExtractor
    Private ReadOnly _ocr As IronTesseract

    Public Sub New()
        _ocr = New IronTesseract()
    End Sub

    Public Function ExtractWordAnnotations(imagePath As String) As List(Of WordAnnotation)
        Dim result = _ocr.Read(imagePath)

        ' Words are a flat collection — no hierarchy traversal, no symbol concatenation
        Return result.Words.Select(Function(w) New WordAnnotation(
            Text:=w.Text,         ' direct string property
            X:=w.X,
            Y:=w.Y,
            Width:=w.Width,
            Height:=w.Height,
            Confidence:=w.Confidence    ' double, no conversion needed
        )).ToList()
    End Function
End Class
$vbLabelText   $csharpLabel

Google Cloud Vision版におけるシンボル連結ループは、設計上の選択ではなく、Protobufスキーマによって要求されるものです。 Word.Textは応答オブジェクトのプロパティではありません。 単語レベルAPIを使用するチームのすべてが同等のループを書きます。IronOCRのConfidenceを一級プロパティとして公開します。 読み取り結果ガイドには、あらゆる粒度レベルで利用可能なプロパティセット全体が記載されています。

複数ページTIFF処理から検索可能なPDF出力へ

Google Cloud Visionは、複数ページのTIFFファイルを連続した一連の画像として扱い、各画像に対して個別のAPI呼び出しが必要となります。 TIFFファイルを受け取り、すべてのフレームに対して構造化された出力を返す単一のAPI呼び出しは存在しません。 Google Cloud Visionの検索結果から検索可能なPDFを生成するには、別途PDF生成ライブラリが必要です。APIはテキストのみを返します。

Google Cloudのビジョンアプローチ:

using Google.Cloud.Vision.V1;
using System.Collections.Generic;
using System.Drawing;          // for TIFF frame extraction
using System.Drawing.Imaging;

public class TiffProcessingService
{
    private readonly ImageAnnotatorClient _client;

    public TiffProcessingService()
    {
        _client = ImageAnnotatorClient.Create();
    }

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

        // Load TIFF and extract frames manually using System.Drawing
        using var tiff = System.Drawing.Image.FromFile(tiffPath);
        var frameDimension = new FrameDimension(tiff.FrameDimensionsList[0]);
        int frameCount = tiff.GetFrameCount(frameDimension);

        for (int i = 0; i < frameCount; i++)
        {
            tiff.SelectActiveFrame(frameDimension, i);

            // Save each frame to a temp file — Vision API does not accept TIFF frames directly
            var tempPath = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg");
            tiff.Save(tempPath, ImageFormat.Jpeg);

            // One API call per frame — each call = one unit of quota
            var visionImage = Google.Cloud.Vision.V1.Image.FromFile(tempPath);
            var response = _client.DetectText(visionImage);
            pageTexts.Add(response.FirstOrDefault()?.Description ?? string.Empty);

            File.Delete(tempPath);
        }

        // Producing a searchable PDF requires a separate library (e.g., iTextSharp)
        // Google Cloud Vision has no PDF output capability
        return pageTexts;
    }
}
using Google.Cloud.Vision.V1;
using System.Collections.Generic;
using System.Drawing;          // for TIFF frame extraction
using System.Drawing.Imaging;

public class TiffProcessingService
{
    private readonly ImageAnnotatorClient _client;

    public TiffProcessingService()
    {
        _client = ImageAnnotatorClient.Create();
    }

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

        // Load TIFF and extract frames manually using System.Drawing
        using var tiff = System.Drawing.Image.FromFile(tiffPath);
        var frameDimension = new FrameDimension(tiff.FrameDimensionsList[0]);
        int frameCount = tiff.GetFrameCount(frameDimension);

        for (int i = 0; i < frameCount; i++)
        {
            tiff.SelectActiveFrame(frameDimension, i);

            // Save each frame to a temp file — Vision API does not accept TIFF frames directly
            var tempPath = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg");
            tiff.Save(tempPath, ImageFormat.Jpeg);

            // One API call per frame — each call = one unit of quota
            var visionImage = Google.Cloud.Vision.V1.Image.FromFile(tempPath);
            var response = _client.DetectText(visionImage);
            pageTexts.Add(response.FirstOrDefault()?.Description ?? string.Empty);

            File.Delete(tempPath);
        }

        // Producing a searchable PDF requires a separate library (e.g., iTextSharp)
        // Google Cloud Vision has no PDF output capability
        return pageTexts;
    }
}
Imports Google.Cloud.Vision.V1
Imports System.Collections.Generic
Imports System.Drawing          ' for TIFF frame extraction
Imports System.Drawing.Imaging
Imports System.IO

Public Class TiffProcessingService
    Private ReadOnly _client As ImageAnnotatorClient

    Public Sub New()
        _client = ImageAnnotatorClient.Create()
    End Sub

    Public Function ProcessMultiPageTiff(tiffPath As String) As List(Of String)
        Dim pageTexts As New List(Of String)()

        ' Load TIFF and extract frames manually using System.Drawing
        Using tiff As System.Drawing.Image = System.Drawing.Image.FromFile(tiffPath)
            Dim frameDimension As New FrameDimension(tiff.FrameDimensionsList(0))
            Dim frameCount As Integer = tiff.GetFrameCount(frameDimension)

            For i As Integer = 0 To frameCount - 1
                tiff.SelectActiveFrame(frameDimension, i)

                ' Save each frame to a temp file — Vision API does not accept TIFF frames directly
                Dim tempPath As String = Path.Combine(Path.GetTempPath(), $"tiff-frame-{i}.jpg")
                tiff.Save(tempPath, ImageFormat.Jpeg)

                ' One API call per frame — each call = one unit of quota
                Dim visionImage As Google.Cloud.Vision.V1.Image = Google.Cloud.Vision.V1.Image.FromFile(tempPath)
                Dim response = _client.DetectText(visionImage)
                pageTexts.Add(If(response.FirstOrDefault()?.Description, String.Empty))

                File.Delete(tempPath)
            Next
        End Using

        ' Producing a searchable PDF requires a separate library (e.g., iTextSharp)
        ' Google Cloud Vision has no PDF output capability
        Return pageTexts
    End Function
End Class
$vbLabelText   $csharpLabel

IronOCRのアプローチ:

using IronOcr;

public class TiffProcessingService
{
    private readonly IronTesseract _ocr;

    public TiffProcessingService()
    {
        _ocr = new IronTesseract();
    }

    public string ProcessMultiPageTiff(string tiffPath)
    {
        using var input = new OcrInput();
        // LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
        input.LoadImageFrames(tiffPath);

        var result = _ocr.Read(input);
        return result.Text;
    }

    public void ProcessMultiPageTiffToSearchablePdf(string tiffPath, string outputPdfPath)
    {
        using var input = new OcrInput();
        input.LoadImageFrames(tiffPath);

        var result = _ocr.Read(input);

        // Google Cloud Vision has no equivalent — this single call produces a searchable PDF
        result.SaveAsSearchablePdf(outputPdfPath);
    }

    public void ProcessLowQualityTiff(string tiffPath, string outputPdfPath)
    {
        using var input = new OcrInput();
        input.LoadImageFrames(tiffPath);

        // Preprocessing before OCR improves accuracy on degraded scans
        input.Deskew();
        input.DeNoise();
        input.Contrast();

        var result = _ocr.Read(input);
        result.SaveAsSearchablePdf(outputPdfPath);
    }
}
using IronOcr;

public class TiffProcessingService
{
    private readonly IronTesseract _ocr;

    public TiffProcessingService()
    {
        _ocr = new IronTesseract();
    }

    public string ProcessMultiPageTiff(string tiffPath)
    {
        using var input = new OcrInput();
        // LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
        input.LoadImageFrames(tiffPath);

        var result = _ocr.Read(input);
        return result.Text;
    }

    public void ProcessMultiPageTiffToSearchablePdf(string tiffPath, string outputPdfPath)
    {
        using var input = new OcrInput();
        input.LoadImageFrames(tiffPath);

        var result = _ocr.Read(input);

        // Google Cloud Vision has no equivalent — this single call produces a searchable PDF
        result.SaveAsSearchablePdf(outputPdfPath);
    }

    public void ProcessLowQualityTiff(string tiffPath, string outputPdfPath)
    {
        using var input = new OcrInput();
        input.LoadImageFrames(tiffPath);

        // Preprocessing before OCR improves accuracy on degraded scans
        input.Deskew();
        input.DeNoise();
        input.Contrast();

        var result = _ocr.Read(input);
        result.SaveAsSearchablePdf(outputPdfPath);
    }
}
Imports IronOcr

Public Class TiffProcessingService
    Private ReadOnly _ocr As IronTesseract

    Public Sub New()
        _ocr = New IronTesseract()
    End Sub

    Public Function ProcessMultiPageTiff(tiffPath As String) As String
        Using input As New OcrInput()
            ' LoadImageFrames handles all TIFF frames in one call — no temp files, no frame loop
            input.LoadImageFrames(tiffPath)

            Dim result = _ocr.Read(input)
            Return result.Text
        End Using
    End Function

    Public Sub ProcessMultiPageTiffToSearchablePdf(tiffPath As String, outputPdfPath As String)
        Using input As New OcrInput()
            input.LoadImageFrames(tiffPath)

            Dim result = _ocr.Read(input)

            ' Google Cloud Vision has no equivalent — this single call produces a searchable PDF
            result.SaveAsSearchablePdf(outputPdfPath)
        End Using
    End Sub

    Public Sub ProcessLowQualityTiff(tiffPath As String, outputPdfPath As String)
        Using input As New OcrInput()
            input.LoadImageFrames(tiffPath)

            ' Preprocessing before OCR improves accuracy on degraded scans
            input.Deskew()
            input.DeNoise()
            input.Contrast()

            Dim result = _ocr.Read(input)
            result.SaveAsSearchablePdf(outputPdfPath)
        End Using
    End Sub
End Class
$vbLabelText   $csharpLabel

Google Cloud Vision 版では、フレーム抽出に System.Drawing が必要となり、一時的な JPEG ファイルがディスクに書き込まれ、フレームごとに 1 つの API 呼び出し (フレーム数に比例してクォータを消費)、プレーン テキスト以外の出力を生成するために別途 PDF ライブラリが必要になります。 IronOCRは、SaveAsSearchablePdfを介して検索可能なPDF出力を生成します。 スキャンされたアーカイブTIFFドキュメントの場合、ProcessLowQualityTiffの前処理パイプラインは、一度の通過で最も一般的な品質問題に対処します。 完全なAPIについては、 TIFFおよびGIF入力検索可能なPDF出力に関するドキュメントを参照してください。

処理速度制限付きバッチ移行(進捗状況追跡機能付き)

本番環境規模では、Google Cloud Visionのデフォルトの1分あたり1,800リクエストというクォータでは、スロットリングを行うか、指数バックオフを用いた再試行ロジックを実装する必要があります。 一晩で5,000件の文書を処理すると、割り当て量を何度も超過することになります。 割り当て量を超過するたびに、パイプラインは強制的に60秒間停止します。 IronOCRには処理速度の制限はありません。パイプラインの制限はCPUコア数と利用可能なスレッド数のみです。

Google Cloudのビジョンアプローチ:

using Google.Cloud.Vision.V1;
using Grpc.Core;
using System.Collections.Generic;

public class ThrottledBatchProcessor
{
    private readonly ImageAnnotatorClient _client;
    private const int MaxRequestsPerMinute = 1800;
    private const int RetryDelayMs = 60_000;

    public ThrottledBatchProcessor()
    {
        _client = ImageAnnotatorClient.Create();
    }

    public async Task<Dictionary<string, string>> ProcessWithThrottlingAsync(
        string[] imagePaths,
        IProgress<(int completed, int total)> progress)
    {
        var results = new Dictionary<string, string>();
        int completed = 0;

        foreach (var path in imagePaths)
        {
            bool succeeded = false;
            while (!succeeded)
            {
                try
                {
                    var image = Google.Cloud.Vision.V1.Image.FromFile(path);
                    var response = _client.DetectText(image);
                    results[path] = response.FirstOrDefault()?.Description ?? string.Empty;
                    succeeded = true;
                }
                catch (RpcException ex) when (ex.StatusCode == StatusCode.ResourceExhausted)
                {
                    // Rate limit exceeded — wait and retry
                    await Task.Delay(RetryDelayMs);
                }
            }

            progress.Report((++completed, imagePaths.Length));
        }

        return results;
    }
}
using Google.Cloud.Vision.V1;
using Grpc.Core;
using System.Collections.Generic;

public class ThrottledBatchProcessor
{
    private readonly ImageAnnotatorClient _client;
    private const int MaxRequestsPerMinute = 1800;
    private const int RetryDelayMs = 60_000;

    public ThrottledBatchProcessor()
    {
        _client = ImageAnnotatorClient.Create();
    }

    public async Task<Dictionary<string, string>> ProcessWithThrottlingAsync(
        string[] imagePaths,
        IProgress<(int completed, int total)> progress)
    {
        var results = new Dictionary<string, string>();
        int completed = 0;

        foreach (var path in imagePaths)
        {
            bool succeeded = false;
            while (!succeeded)
            {
                try
                {
                    var image = Google.Cloud.Vision.V1.Image.FromFile(path);
                    var response = _client.DetectText(image);
                    results[path] = response.FirstOrDefault()?.Description ?? string.Empty;
                    succeeded = true;
                }
                catch (RpcException ex) when (ex.StatusCode == StatusCode.ResourceExhausted)
                {
                    // Rate limit exceeded — wait and retry
                    await Task.Delay(RetryDelayMs);
                }
            }

            progress.Report((++completed, imagePaths.Length));
        }

        return results;
    }
}
Imports Google.Cloud.Vision.V1
Imports Grpc.Core
Imports System.Collections.Generic
Imports System.Threading.Tasks

Public Class ThrottledBatchProcessor
    Private ReadOnly _client As ImageAnnotatorClient
    Private Const MaxRequestsPerMinute As Integer = 1800
    Private Const RetryDelayMs As Integer = 60000

    Public Sub New()
        _client = ImageAnnotatorClient.Create()
    End Sub

    Public Async Function ProcessWithThrottlingAsync(
        imagePaths As String(),
        progress As IProgress(Of (completed As Integer, total As Integer))) As Task(Of Dictionary(Of String, String))

        Dim results As New Dictionary(Of String, String)()
        Dim completed As Integer = 0

        For Each path In imagePaths
            Dim succeeded As Boolean = False
            While Not succeeded
                Try
                    Dim image = Google.Cloud.Vision.V1.Image.FromFile(path)
                    Dim response = _client.DetectText(image)
                    results(path) = If(response.FirstOrDefault()?.Description, String.Empty)
                    succeeded = True
                Catch ex As RpcException When ex.StatusCode = StatusCode.ResourceExhausted
                    ' Rate limit exceeded — wait and retry
                    Await Task.Delay(RetryDelayMs)
                End Try
            End While

            progress.Report((Threading.Interlocked.Increment(completed), imagePaths.Length))
        Next

        Return results
    End Function
End Class
$vbLabelText   $csharpLabel

IronOCRのアプローチ:

using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class BatchProcessor
{
    public Dictionary<string, string> ProcessBatch(
        string[] imagePaths,
        IProgress<(int completed, int total)> progress)
    {
        var results = new ConcurrentDictionary<string, string>();
        int completed = 0;

        // No rate limits — full parallelism, no retry logic needed
        Parallel.ForEach(imagePaths, imagePath =>
        {
            var ocr = new IronTesseract();
            results[imagePath] = ocr.Read(imagePath).Text;
            progress.Report((Interlocked.Increment(ref completed), imagePaths.Length));
        });

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

    public void ProcessBatchToSearchablePdfs(
        string[] imagePaths,
        string outputDirectory)
    {
        Parallel.ForEach(imagePaths, imagePath =>
        {
            var ocr = new IronTesseract();
            var result = ocr.Read(imagePath);

            var outputPath = Path.Combine(
                outputDirectory,
                Path.GetFileNameWithoutExtension(imagePath) + "-searchable.pdf");

            result.SaveAsSearchablePdf(outputPath);
        });
    }
}
using IronOcr;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class BatchProcessor
{
    public Dictionary<string, string> ProcessBatch(
        string[] imagePaths,
        IProgress<(int completed, int total)> progress)
    {
        var results = new ConcurrentDictionary<string, string>();
        int completed = 0;

        // No rate limits — full parallelism, no retry logic needed
        Parallel.ForEach(imagePaths, imagePath =>
        {
            var ocr = new IronTesseract();
            results[imagePath] = ocr.Read(imagePath).Text;
            progress.Report((Interlocked.Increment(ref completed), imagePaths.Length));
        });

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

    public void ProcessBatchToSearchablePdfs(
        string[] imagePaths,
        string outputDirectory)
    {
        Parallel.ForEach(imagePaths, imagePath =>
        {
            var ocr = new IronTesseract();
            var result = ocr.Read(imagePath);

            var outputPath = Path.Combine(
                outputDirectory,
                Path.GetFileNameWithoutExtension(imagePath) + "-searchable.pdf");

            result.SaveAsSearchablePdf(outputPath);
        });
    }
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
Imports System.IO

Public Class BatchProcessor
    Public Function ProcessBatch(imagePaths As String(), progress As IProgress(Of (completed As Integer, total As Integer))) As Dictionary(Of String, String)
        Dim results = New ConcurrentDictionary(Of String, String)()
        Dim completed As Integer = 0

        ' No rate limits — full parallelism, no retry logic needed
        Parallel.ForEach(imagePaths, Sub(imagePath)
                                         Dim ocr = New IronTesseract()
                                         results(imagePath) = ocr.Read(imagePath).Text
                                         progress.Report((Interlocked.Increment(completed), imagePaths.Length))
                                     End Sub)

        Return New Dictionary(Of String, String)(results)
    End Function

    Public Sub ProcessBatchToSearchablePdfs(imagePaths As String(), outputDirectory As String)
        Parallel.ForEach(imagePaths, Sub(imagePath)
                                         Dim ocr = New IronTesseract()
                                         Dim result = ocr.Read(imagePath)

                                         Dim outputPath = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(imagePath) & "-searchable.pdf")

                                         result.SaveAsSearchablePdf(outputPath)
                                     End Sub)
    End Sub
End Class
$vbLabelText   $csharpLabel

Google Cloud Vision版はシーケンシャル処理を採用しています。これは、並列リクエストを行うとレート制限のリスクが増大するためです。 各ResourceExhausted例外は、丸々60秒の停止を追加します。 5,000件の文書が処理上限に10回達すると、10分間の待機時間が発生する。 IronOCRのバージョンでは、待機時間なしで利用可能なすべてのコアで並列処理が行われます。 長時間実行バッチの場合、進捗追跡APIは、手動のInterlocked.Increment配線を必要とせずに組み込みの進捗コールバックを提供します。 バッチスキャンでの画像品質問題に対しては、画像品質修正ガイドが、各Read呼び出しの前に追加できる前処理パイプラインをカバーします。

Google Cloud Vision OCR API からIronOCRへのマッピングリファレンス

Google Cloud Vision IronOCR ノート
ImageAnnotatorClient.Create() new IronTesseract() クライアントの初期化; 認証ファイル不要
Image.FromFile(path) _ocr.Read(path) または input.LoadImage(path) IronTesseractでの直接パス読み取りが利用可能
_client.DetectText(image) _ocr.Read(path).Text TEXT_DETECTION 同等
_client.DetectDocumentText(image) _ocr.Read(path) DOCUMENT_TEXT_DETECTION 同等; モードは自動です
response[0].Description result.Text 文書全文
TextAnnotation OcrResult 最上位の結果コンテナ
annotation.Text result.Text 全文文字列
annotation.Pages[i] result.Pages[i] ページごとのアクセス
page.Blocks[i].Paragraphs[j] result.Paragraphs[i] IronOCRは段落をフラットなコレクションとして表示します
paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text) paragraph.Text 直接文字列プロパティ; 記号の反復なし
word.BoundingBox.Vertices word.X, word.Y, word.Width, word.Height 頂点リストの代わりに離散的な整数プロパティを使用する
word.Confidence word.Confidence 単語ごとの信頼度スコア
page.Confidence result.Confidence 全体的な結果に対する信頼
Feature.Types.Type.DocumentTextDetection 自動翻訳 IronOCRは処理モードを自動選択します
BatchAnnotateImagesRequest Parallel.ForEach + new IronTesseract() スレッドごと 並列ローカル処理 バッチサイズの上限なし
_client.BatchAnnotateImages(requests) new IronTesseract().Read(input) マルチイメージ OcrInput 複数画像入力のための単一呼び出し
AsyncBatchAnnotateFilesAsync() input.LoadPdf(); _ocr.Read(input) PDF処理は同期的に行われます。 GCSは不要
StorageClient.Create() 不要 GCSへの依存なし
storageClient.UploadObjectAsync() 不要 PDFファイルはローカルパスまたはストリームから直接読み込まれます。
operation.PollUntilCompletedAsync() 不要 処理は同期的に行われます
RpcException (StatusCode.ResourceExhausted) 該当なし レート制限なし
RpcException (StatusCode.PermissionDenied) 該当なし 実行時認証なし
GOOGLE_APPLICATION_CREDENTIALS 環境変数 IronOcr.License.LicenseKey ファイルパスではなく、文字列の代入です。

一般的な移行の問題と解決策

問題1:コンストラクタがGOOGLE_APPLICATION_CREDENTIALSなしで例外をスローする

Google Cloud Vision: 環境変数が設定されていないか無効なファイルを指す場合、StatusCode.PermissionDeniedでスローします。この失敗はスタートアップ時に発生し、最初のAPI呼び出し時ではなく、どれか1つの環境に資格情報が欠けているとアプリケーション全体が初期化に失敗します。

解決策: Google Cloud Visionパッケージを削除した後、すべての環境設定、CI/CDパイプラインシークレット、Kubernetesシークレット、およびDocker ComposeファイルからGOOGLE_APPLICATION_CREDENTIALSのすべての参照を削除します。 単一のIRONOCR_LICENSE環境変数で置き換えます:

// Remove this from every deployment environment:
// GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json

// Add this once at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
    ?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable is required.");
// Remove this from every deployment environment:
// GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json

// Add this once at application startup:
IronOcr.License.LicenseKey = Environment.GetEnvironmentVariable("IRONOCR_LICENSE")
    ?? throw new InvalidOperationException("IRONOCR_LICENSE environment variable is required.");
' Remove this from every deployment environment:
' GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json

' Add this once at application startup:
IronOcr.License.LicenseKey = If(Environment.GetEnvironmentVariable("IRONOCR_LICENSE"), Throw New InvalidOperationException("IRONOCR_LICENSE environment variable is required."))
$vbLabelText   $csharpLabel

問題2:名前空間削除後にProtobufシンボル連結コードが壊れる

Google Cloud Vision: パラグラフまたは単語のテキストをGoogle.Cloud.Vision.V1名前空間を削除した後にコンパイルエラーを生成します。 これらの呼び出しは、APIレスポンスを受け取ったヘルパークラスやサービスクラス全体に分散されます。

解決策: コードベース内のすべてのw.Symbolsパターンを検索し、IronOCR結果オブジェクトの直接プロパティアクセスで置き換えます。 結果の読み取りハウツーガイドでは、OcrResult.Wordのすべての利用可能なプロパティをカバーしています:

# Find all Protobuf symbol concatenation patterns
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
grep -rn "w\.Symbols\.Select" --include="*.cs" .
# Find all Protobuf symbol concatenation patterns
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .
grep -rn "w\.Symbols\.Select" --include="*.cs" .
SHELL

各出現箇所を置換します。

// Before: symbol concatenation required by Protobuf schema
var text = string.Join("", paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text));

// After: direct property on OcrResult.Paragraph
var text = paragraph.Text;
// Before: symbol concatenation required by Protobuf schema
var text = string.Join("", paragraph.Words.SelectMany(w => w.Symbols).Select(s => s.Text));

// After: direct property on OcrResult.Paragraph
var text = paragraph.Text;
' Before: symbol concatenation required by Protobuf schema
Dim text = String.Join("", paragraph.Words.SelectMany(Function(w) w.Symbols).Select(Function(s) s.Text))

' After: direct property on OcrResult.Paragraph
Dim text = paragraph.Text
$vbLabelText   $csharpLabel

問題3:Storage.V1削除後、PDF処理コードがコンパイルされない

Google Cloud Vision: PollUntilCompletedAsyncを参照しているすべてのコードはコンパイルに失敗します。このコードは複数のサービスクラスにまたがる可能性があり、通常、単一の大きな変更ブロックを表しています。

解決策: GCSパイプライン全体を削除します。50Plus非同期メソッドを、 IronOCRの3行の同等のメソッドに置き換えます。 呼び出し元互換性のために非同期シグネチャを維持していたコードの場合、Task.Runでラップします:

// Delete: StorageClient, GCS upload, AsyncBatchAnnotateFilesAsync,
//         PollUntilCompletedAsync, output download, DeleteObjectAsync

// Replace with:
public async Task<string> ProcessPdfAsync(string pdfPath)
{
    return await Task.Run(() =>
    {
        using var input = new OcrInput();
        input.LoadPdf(pdfPath);
        return new IronTesseract().Read(input).Text;
    });
}
// Delete: StorageClient, GCS upload, AsyncBatchAnnotateFilesAsync,
//         PollUntilCompletedAsync, output download, DeleteObjectAsync

// Replace with:
public async Task<string> ProcessPdfAsync(string pdfPath)
{
    return await Task.Run(() =>
    {
        using var input = new OcrInput();
        input.LoadPdf(pdfPath);
        return new IronTesseract().Read(input).Text;
    });
}
Imports System.Threading.Tasks

Public Async Function ProcessPdfAsync(pdfPath As String) As Task(Of String)
    Return Await Task.Run(Function()
                              Using input As New OcrInput()
                                  input.LoadPdf(pdfPath)
                                  Return New IronTesseract().Read(input).Text
                              End Using
                          End Function)
End Function
$vbLabelText   $csharpLabel

新しいコードの場合は、ネイティブ非同期OCRサポートTask.Runラッパーの代わりに使用します。 PDF入力ガイドでは、ページ範囲の選択方法とパスワードで保護されたPDFの読み込み方法について説明しています。

問題4:レート制限再試行ロジックは不要になりました

Google Cloud Vision: 1,800リクエスト毎分クォータを処理するためにStatusCode.ResourceExhaustedでキャッチし、待って再試行パターンを実装するコードが書かれていました。 この再試行ロジックは、ミドルウェア、パイプラインのステップ、またはバッチ処理ループに組み込まれる場合があります。

解決策:クォータエラーに関連するすべての再試行ロジックを削除します。 IronOCRは外部クォータなしでローカル処理を実行します。 エラーハンドラ契約は、RpcExceptionの5つのケースから2つに変わります:

// Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
//         Unavailable, DeadlineExceeded, Unauthenticated

// IronOCR error surface:
try
{
    var result = new IronTesseract().Read(imagePath);
    if (result.Confidence < 50)
        input.DeNoise(); // add preprocessing for low-confidence results
    return result.Text;
}
catch (IOException ex)
{
    // File not found or locked
    throw new InvalidOperationException($"Cannot read: {imagePath}", ex);
}
catch (IronOcr.Exceptions.OcrException ex)
{
    // Processing failure — not a transient network error
    throw new InvalidOperationException($"OCR failed: {ex.Message}", ex);
}
// Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
//         Unavailable, DeadlineExceeded, Unauthenticated

// IronOCR error surface:
try
{
    var result = new IronTesseract().Read(imagePath);
    if (result.Confidence < 50)
        input.DeNoise(); // add preprocessing for low-confidence results
    return result.Text;
}
catch (IOException ex)
{
    // File not found or locked
    throw new InvalidOperationException($"Cannot read: {imagePath}", ex);
}
catch (IronOcr.Exceptions.OcrException ex)
{
    // Processing failure — not a transient network error
    throw new InvalidOperationException($"OCR failed: {ex.Message}", ex);
}
Imports IronOcr
Imports System.IO

' Remove: all RpcException handlers for ResourceExhausted, PermissionDenied,
'         Unavailable, DeadlineExceeded, Unauthenticated

' IronOCR error surface:
Try
    Dim result = New IronTesseract().Read(imagePath)
    If result.Confidence < 50 Then
        input.DeNoise() ' add preprocessing for low-confidence results
    End If
    Return result.Text
Catch ex As IOException
    ' File not found or locked
    Throw New InvalidOperationException($"Cannot read: {imagePath}", ex)
Catch ex As IronOcr.Exceptions.OcrException
    ' Processing failure — not a transient network error
    Throw New InvalidOperationException($"OCR failed: {ex.Message}", ex)
End Try
$vbLabelText   $csharpLabel

問題5:複数ページTIFFにはフレーム抽出ループが必要

Google Cloud Vision: 既存のTIFF処理コードは、通常、System.Drawing.Imageを使用してフレームを抽出し、各フレームを一時ディレクトリにJPEGとして保存し、各JPEGを個別のAPI呼び出しとして提出し、その後一時ファイルを削除します。 このパターンはフレームごとに1つのクォータユニットを消費し、クラッシュ時に孤立した一時ファイルを残す可能性があります。

解決策: フレームの抽出ループと一時ファイル管理をinput.LoadImageFrames()で置き換えます。 System.Drawing フレームループ全体が削除されます。

// Remove: System.Drawing frame extraction, temp file writes, per-frame API calls

// Replace with:
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);   // all frames, no temp files
var result = new IronTesseract().Read(input);
// Remove: System.Drawing frame extraction, temp file writes, per-frame API calls

// Replace with:
using var input = new OcrInput();
input.LoadImageFrames(tiffPath);   // all frames, no temp files
var result = new IronTesseract().Read(input);
Imports IronOcr

Dim input As New OcrInput()
input.LoadImageFrames(tiffPath) ' all frames, no temp files
Dim result = (New IronTesseract()).Read(input)
$vbLabelText   $csharpLabel

フレーム範囲の選択を含む複数フレーム処理オプションについては、 TIFFおよびGIF入力ガイドを参照してください。

問題6:BoundingPolyの頂点計算の中断

Google Cloud Vision: vertices[2].Y - vertices[0].Y。 移行後、これらの式はIronOCRでは同等のものがありませんVerticesが存在しないためです。

解決策:頂点演算を直接整数プロパティに置き換える。 計算は不要です。

// Before: vertex index arithmetic
int x = word.BoundingBox.Vertices[0].X;
int y = word.BoundingBox.Vertices[0].Y;
int width  = word.BoundingBox.Vertices[1].X - word.BoundingBox.Vertices[0].X;
int height = word.BoundingBox.Vertices[2].Y - word.BoundingBox.Vertices[0].Y;

// After: direct properties
int x      = word.X;
int y      = word.Y;
int width  = word.Width;
int height = word.Height;
// Before: vertex index arithmetic
int x = word.BoundingBox.Vertices[0].X;
int y = word.BoundingBox.Vertices[0].Y;
int width  = word.BoundingBox.Vertices[1].X - word.BoundingBox.Vertices[0].X;
int height = word.BoundingBox.Vertices[2].Y - word.BoundingBox.Vertices[0].Y;

// After: direct properties
int x      = word.X;
int y      = word.Y;
int width  = word.Width;
int height = word.Height;
' Before: vertex index arithmetic
Dim x As Integer = word.BoundingBox.Vertices(0).X
Dim y As Integer = word.BoundingBox.Vertices(0).Y
Dim width As Integer = word.BoundingBox.Vertices(1).X - word.BoundingBox.Vertices(0).X
Dim height As Integer = word.BoundingBox.Vertices(2).Y - word.BoundingBox.Vertices(0).Y

' After: direct properties
Dim x As Integer = word.X
Dim y As Integer = word.Y
Dim width As Integer = word.Width
Dim height As Integer = word.Height
$vbLabelText   $csharpLabel

Google Cloud Vision OCR移行チェックリスト

移行前

変更を加える前に、コードベースを監査して、Google Cloud Visionへの依存関係をすべて特定してください。

# Find all Google Cloud Vision namespace imports
grep -rn "using Google.Cloud.Vision" --include="*.cs" .
grep -rn "using Google.Cloud.Storage" --include="*.cs" .
grep -rn "using Grpc.Core" --include="*.cs" .

# Find ImageAnnotatorClient usage
grep -rn "ImageAnnotatorClient" --include="*.cs" .

# Find GCS pipeline code
grep -rn "StorageClient\|UploadObjectAsync\|DeleteObjectAsync" --include="*.cs" .
grep -rn "AsyncBatchAnnotateFilesAsync\|PollUntilCompleted" --include="*.cs" .

# Find Protobuf symbol concatenation
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .

# Find BoundingPoly vertex calculations
grep -rn "BoundingPoly\|BoundingBox\.Vertices" --include="*.cs" .

# Find rate limit retry handlers
grep -rn "ResourceExhausted\|StatusCode\." --include="*.cs" .

# Find environment variable references
grep -rn "GOOGLE_APPLICATION_CREDENTIALS" .
# Find all Google Cloud Vision namespace imports
grep -rn "using Google.Cloud.Vision" --include="*.cs" .
grep -rn "using Google.Cloud.Storage" --include="*.cs" .
grep -rn "using Grpc.Core" --include="*.cs" .

# Find ImageAnnotatorClient usage
grep -rn "ImageAnnotatorClient" --include="*.cs" .

# Find GCS pipeline code
grep -rn "StorageClient\|UploadObjectAsync\|DeleteObjectAsync" --include="*.cs" .
grep -rn "AsyncBatchAnnotateFilesAsync\|PollUntilCompleted" --include="*.cs" .

# Find Protobuf symbol concatenation
grep -rn "\.Symbols\." --include="*.cs" .
grep -rn "SelectMany.*Symbols" --include="*.cs" .

# Find BoundingPoly vertex calculations
grep -rn "BoundingPoly\|BoundingBox\.Vertices" --include="*.cs" .

# Find rate limit retry handlers
grep -rn "ResourceExhausted\|StatusCode\." --include="*.cs" .

# Find environment variable references
grep -rn "GOOGLE_APPLICATION_CREDENTIALS" .
SHELL

開始前に記入する在庫確認メモ:

  • OCR入出力用に作成されたすべてのGCSバケットを一覧表示し、移行後にクリーンアップをスケジュールする サービスアカウントのメールアドレスを記録しておき、移行後にGCPコンソールで無効化できるようにします。
  • GOOGLE_APPLICATION_CREDENTIALSが設定されているすべての環境を特定する
  • 設定からGCPプロジェクトIDまたはバケット名を読み取るコードがあれば、移行後に削除してください。

コードの移行

  1. すべてのプロジェクトからGoogle.Cloud.Vision.V1 NuGetパッケージを削除する
  2. すべてのプロジェクトからGoogle.Cloud.Storage.V1 NuGetパッケージを削除する
  3. OCRを実行するすべてのプロジェクトにIronOcr NuGetパッケージをインストールする
  4. アプリケーション起動時にIronOcr.License.LicenseKey初期化を追加する
  5. すべてのusing IronOcrに置き換える
  6. すべてのusing Grpc.Coreインポートを置き換える
  7. new IronTesseract()に置き換える
  8. すべてのGCSパイプラインメソッド(UploadObjectAsync、非同期アノテーション、ポーリング、ダウンロード、削除)を削除する
  9. すべてのPDF処理パス用のinput.LoadPdf()を置き換える(非同期GCSオーケストレーションを削除し)
  10. すべてのプロトバフシンボル結合ループをOcrResultオブジェクト上で直接置き換える
  11. word.Heightに置き換える
  12. Unauthenticated
  13. フレームごとのTIFFループをinput.LoadImageFrames()で置き換える
  14. 順次バッチループをスレッドごとにParallel.ForEachに変換する
  15. GOOGLE_APPLICATION_CREDENTIALSをすべての環境設定、CI/CDパイプライン、Docker Composeファイル、およびKubernetesシークレットから削除する

移行後

  • エラーハンドリングコードにGoogleApiExceptionのタイプが参照されていないことを確認する
  • すべてのデプロイメント環境設定からGOOGLE_APPLICATION_CREDENTIALSが存在しないことを確認する
  • 本番環境で使用されているものと同じサンプル文書セットでOCRパイプラインを実行し、テキスト出力の品質を比較する。
  • GCS非同期パイプラインで以前に処理されたドキュメントでPDF処理をテストし、テキスト出力が同一であることを確認します。
  • @input.LoadPdf(path, Password: "...")でパスワード保護されたPDFをテストする - 以前はサポートされていませんでした
  • input.LoadImageFrames()を使用し、すべてのフレームが処理されていることを確認して、マルチページTIFF処理をテストする バッチプロセッサを代表的なサンプルで実行し、出力品質が以前の結果と一致することを確認します。
  • あなたのドキュメントコーパスに対して、result.Confidenceの値が許容範囲内にあることを確認する
  • 以前は別のPDFライブラリが必要だったドキュメントに対して、result.SaveAsSearchablePdf()を使用して検索可能なPDF出力を検証する
  • 外部インターネット接続のない環境でアプリケーションを実行し、OCRが正しく動作することを確認してください。

IronOCRへの移行の主なメリット

認証情報の表面がゼロファイルに縮小されました。 構造移行後、インフラにはJSONキーファイル、GCSバケット設定、IAMロール、サービスアカウント、およびGOOGLE_APPLICATION_CREDENTIALS環境変数がありません。 認証情報全体は、ライセンスキー文字列を含む1つの環境変数によってのみ管理されます。 Google Cloud Visionでは必須の定期操作であったキーローテーションは、もはや適用されない概念となりました。 複数の地域やクラウドプロバイダーで事業を展開するチームにとって、導入構成の複雑さの軽減は即座に実感できる。

外部依存関係なしでPDFおよびTIFFを処理。GCS非同期パイプラインとSystem.Drawing TIFFフレームループは完全に削除されています。 input.LoadImageFrames()は置換です - 両方とも同期的で、両方ともローカルで、結果までの呼び出しから3行です。 Google Cloud Visionでは不可能だったパスワード保護付きPDFも、たった1つの追加パラメータで実現可能になりました。 PDF OCRガイドTIFF入力ガイドには、入力APIの全内容が記載されています。

CPU速度でのバッチ処理。1分あたり1,800件のリクエスト制限と、必須の60秒間の再試行待機が撤廃されたことで、これまで処理速度に制約されていたバッチジョブが、利用可能なプロセッサコアの速度で実行されるようになりました。 16コアを搭載したマシンは、外部承認を一切必要とせずに、16個の文書を同時に処理できる。 IronTesseractインスタンスを持つものがスロットルされた順次ループの直接的な置き換えです。速度最適化ガイドでは、特定の文書タイプのスループットを調整するエンジン設定オプションをカバーしています。

Protobufを使用しない構造化データ。 すべてのCharactersを公開し、Protobuf名前空間の依存関係はなく、シンボル結合もなく、バウンディングボックスに対する頂点計算もありません。 段落テキストを抽出するために以前20行のネストされたループを必要としたコードはresult.Paragraphs.Select(p => p.Text)に減少します。 ドキュメントのレイアウト分析にワードレベルのポジショニングが必要なユースケースには、word.Heightが直接利用可能です。 OCR結果機能ページには、結果モデルに含まれるすべてのプロパティが記載されています。

検索可能なPDF出力機能が組み込まれています。Google Cloud Visionはテキストのみを返すため、検索可能なPDFを生成するには、別のPDF生成ライブラリが必要となり、 NuGet依存関係の追加、学習すべきAPIの追加、評価すべきライセンスの追加が必要でした。 IronOCRのresult.SaveAsSearchablePdf(outputPath)は、どんなOCR結果からも完全に検索可能なPDFを1行で生成します。ドキュメントのアーカイブワークフローや法的発見のパイプラインでは、これにより依存の一部が完全に排除されます。 検索可能なPDFサンプルは、パターンを最初から最後まで示しています。

規制対象業界におけるデータ主権。IronOCRで処理された文書は、サーバーから外部に送信されることはありません。 HIPAAの対象となる医療記録、ITARで規制される技術データ、CMMCの対象となる防衛請負業者の資料、弁護士と依頼人の間の秘匿特権のある法的文書、およびPCI-DSSの対象となる財務記録については、オンプレミスアーキテクチャでは、サードパーティデータ処理業者というカテゴリがコンプライアンスの範囲から完全に除外されます。 交渉すべきビジネスアソシエイト契約も、締結すべきデータ処理契約も、確認すべきGoogleのデータ保持ポリシーもありません。 IronOCRのドキュメントハブでは、データ所在地の要件が適用されるDocker、Linux、Azure、およびAWS環境におけるデプロイ構成について解説しています。

ご注意Google Cloud Vision, Tesseract、およびiTextは、それぞれの所有者の登録商標です。 このサイトは、GoogleやiText Groupとの提携、承認、またはスポンサーシップを受けていません。すべての製品名、ロゴ、およびブランドは、それぞれの所有者の財産です。 比較は情報提供のみを目的としており、執筆時点で公開されている情報を反映しています。

よくある質問

なぜGoogle Cloud Vision APIからIronOCRに移行する必要があるのですか?

一般的な推進要因には、COM相互接続の複雑さの解消、ファイルベースのライセンス管理の置き換え、ページ単位の課金の回避、Docker/コンテナデプロイの有効化、標準的な.NETツールと統合するNuGetネイティブワークフローの採用などがあります。

Google Cloud Vision APIからIronOCRに移行する際の主なコード変更は何ですか?

Google Cloud Visionの初期化シーケンスをIronTesseractのインスタンス化に置き換え、COMのライフサイクル管理(明示的なCreate/Load/Closeパターン)を削除し、結果のプロパティ名を更新します。その結果、定型的な行が大幅に減りました。

IronOCRをインストールして移行を開始するにはどうすればいいですか?

パッケージマネージャーコンソールで'Install-Package IronOcr'を実行するか、CLIで'dotnet add package IronOcr'を実行してください。言語パックは別のパッケージです:例えばフランス語の場合は'dotnet add package IronOcr.Languages.French'となります。

IronOCRは標準的なビジネス文書のGoogle Cloud Vision APIのOCR精度に匹敵しますか?

IronOCRは、請求書、契約書、領収書、入力フォームなどの標準的なビジネスコンテンツで高い精度を達成しています。画像前処理フィルター(傾き補正、ノイズ除去、コントラスト強調)により、劣化した入力の認識をさらに向上させます。

IronOCRはGoogle Cloud Vision APIが別途インストールする言語データをどのように扱うのですか?

IronOCRの言語データはNuGetパッケージとして配布されます。'dotnet add package IronOcr.Languages.German'でドイツ語のサポートがインストールされます。手動でのファイル配置やディレクトリパスは必要ありません。

Google Cloud Vision APIからIronOCRへの移行には、デプロイメント・インフラの変更が必要ですか?

IronOCRはGoogle Cloud Vision APIよりもインフラの変更が少なくて済みます。SDKのバイナリパス、ライセンスファイルの配置、ライセンスサーバーの設定は必要ありません。NuGetパッケージには完全なOCRエンジンが含まれており、ライセンスキーはアプリケーションコードに設定された文字列です。

移行後のIronOCRライセンスの設定方法は?

アプリケーションの起動コードでIronOcr.License.LicenseKey = "YOUR-KEY "を割り当てます。DockerまたはKubernetesでは、キーを環境変数として保存し、スタートアップで読み込む。License.IsValidLicenseを使用して、トラフィックを受け入れる前にバリデーションを行う。

IronOCRはGoogle Cloud Visionと同じようにPDFを処理できますか?

IronOCRはネイティブPDFもスキャンしたPDFも読み取ります。IronTesseractをインスタンス化し、ocr.Read(input)を呼び出し、入力はPDFパスまたはOcrPdfInputであり、OcrResultページを反復します。別のPDFレンダリングパイプラインは必要ありません。

IronOCRはどのように大量処理のスレッディングを処理するのですか?

IronTesseractはスレッド毎にインスタンス化しても安全です。Parallel.ForEachまたはタスクプールでスレッドごとにインスタンスを1つスピンアップし、OCRを同時に実行し、完了したら各インスタンスを破棄します。グローバルステートやロックは必要ありません。

IronOCRはテキスト抽出後、どのような出力形式をサポートしていますか?

IronOCRはテキスト、単語座標、信頼度スコア、ページ構造を含む構造化された結果を返します。エクスポートオプションには、プレーンテキスト、検索可能なPDF、下流処理用の構造化結果オブジェクトが含まれます。

IronOCRの価格は、ワークロードのスケーリングにおいてGoogle Cloud Vision APIよりも予測可能ですか?

IronOCRは、定額制の永久ライセンスを採用しており、ページ単位やボリューム単位の料金はかかりません。10,000ページでも1,000万ページでも、ライセンスコストは一定です。ボリュームライセンスとチームライセンスのオプションはIronOCRの価格ページをご覧ください。

Google Cloud Vision APIからIronOCRに移行した後、既存のテストはどうなりますか?

抽出されたテキストコンテンツをアサートするテストは、移行後もパスし続ける必要があります。APIコールパターンやCOMオブジェクトのライフサイクルを検証するテストは、IronOCRのシンプルな初期化と結果モデルを反映するように更新する必要があります。

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

アイアンサポートチーム

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