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

Migrating from Google Cloud Vision OCR to IronOCR

.NET開発者がPaddleSharp OCR (Sdcb.PaddleOCR)からIronOCRへの完全な移行を進めるためのガイドです。 これには、推論セッション管理の置き換え、OpenCVの前処理への依存関係の排除、CPU、GPU、およびOpenVINO向けのバックエンド選択ロジックの削除、ならびにテーブル認識ワークフローの移行が含まれます。 各セクションでは、一般的なOCR比較には見られない、PaddleSharp特有のパターンから抽出した"翻訳前"と"翻訳後"のコードを掲載しています。

PaddleSharp OCRからの移行理由

PaddleSharpは、アプリケーション層でディープラーニングの推論パイプラインを提供します。 このアーキテクチャにより、PaddlePaddleモデルのパフォーマンスを活用できますが、通常はインフラストラクチャの課題となる部分をアプリケーション側で管理する必要があります。 以下の課題が、多くの .NET チームに代替手段を探すきっかけとなっています。

推論バックエンドの設定はアプリケーションコードです。 PaddleSharpでのCPU、GPU、OpenVINOバックエンドの選択には、PaddleConfigオブジェクトの構築と設定、デプロイメント対象の適切なネイティブランタイムNuGetパッケージの選択、および実行時に利用可能なハードウェアに基づいて初期化コードを条件分岐させることが必要です。このロジックはライブラリではなくアプリケーションに存在し、ターゲット環境が変わるとブレークします。

画像入力には OpenCV が必須の依存関係となります。PaddleSharp はファイルパスやストリームを直接受け入れることはできません。 すべての画像はOCRエンジンに到達する前にOpenCVのOpenCvSharp4.runtime.*パッケージが含まれます。 一方のプラットフォームのランタイムのみを更新すると、環境間で再現が困難なランタイムエラーが発生します。

推論セッションのライフタイムは明示的な設計が必要です。 PaddleOcrAllは構築時にディスクから3つのモデルバイナリをロードします。このコストは数百ミリ秒単位で測定可能で、オブジェクトがリクエストごとにインスタンス化できないことを意味します。チームはライフサイクル戦略(シングルトン、プール、またはスコープ)を設計する必要があります。 ASP.NET Coreでは、通常PaddleOcrAllが基盤となるネイティブ状態を共有するため、スレッドセーフティの慎重な分析を伴う登録済みサービスを意味します。

表の認識には別途モデルのダウンロードが必要です。PaddleSharpでの構造化ドキュメントの抽出には、標準的な3段階の検出/分類/認識パイプラインに加え、専用の表認識モデルが必要です。そのモデルは、ダウンロード、バージョン管理、設定を行う必要がある4つ目のファイルとなります。 統一されたAPIインターフェースは存在しません。テーブル認識は、独自の結果型を持つ別のコードパスを使用します。

検索可能なPDF出力は行われません。PaddleSharpはテキスト文字列を生成します。 検索可能なPDFファイルを作成することはできません。 スキャンした文書をテキスト検索可能なPDFとしてアーカイブする必要があるチームは、別途PDFライブラリを統合し、その追加の依存関係を管理し、変換レイヤーを実装する必要があります。 出力形式のギャップは完全に解消されています:hOCRなし、構造化された検索可能なPDFなし、テキストレイヤーのオーバーレイなし。

上流の依存関係チェーンは、.NETコミュニティが所有するものではありません。PaddleSharpは、BaiduのPaddlePaddle推論フレームワークをラップしています。 PaddleOCRのバージョンアップに伴うモデル形式の変更により、過去に.NETバインディングレイヤーが破損したことがあります。問題の追跡、ドキュメント、およびリリースに関する議論のほとんどは中国語で行われています。 上流プロジェクトを監視する中国語話者がいない.NETチームにとって、互換性を損なう変更は予告なしに発生します。

基本的な問題

PaddleSharpでバックエンドを選択および初期化するには、OCRロジックではなくインフラストラクチャに属する設定コードが必要です:

// PaddleSharp: Backend selection sprawls into application startup
// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;

// CPU-only deployment
var config = PaddleConfig.FromModelDir("models/det");
config.SetCpuMathLibraryNumThreads(4);

// GPU deployment — different package, different init path
// var config = PaddleConfig.FromModelDir("models/det");
// config.EnableGpu(500, 0);  // memoryMB, deviceId

// OpenVINO deployment — third conditional branch
// config.EnableMkldnn();

// Application code now owns the hardware topology decision
// PaddleSharp: Backend selection sprawls into application startup
// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;

// CPU-only deployment
var config = PaddleConfig.FromModelDir("models/det");
config.SetCpuMathLibraryNumThreads(4);

// GPU deployment — different package, different init path
// var config = PaddleConfig.FromModelDir("models/det");
// config.EnableGpu(500, 0);  // memoryMB, deviceId

// OpenVINO deployment — third conditional branch
// config.EnableMkldnn();

// Application code now owns the hardware topology decision
Imports Sdcb.PaddleInference
Imports Sdcb.PaddleOCR

' PaddleSharp: Backend selection sprawls into application startup
' Simplified — see Sdcb.PaddleInference documentation for full API

' CPU-only deployment
Dim config = PaddleConfig.FromModelDir("models/det")
config.SetCpuMathLibraryNumThreads(4)

' GPU deployment — different package, different init path
' Dim config = PaddleConfig.FromModelDir("models/det")
' config.EnableGpu(500, 0)  ' memoryMB, deviceId

' OpenVINO deployment — third conditional branch
' config.EnableMkldnn()

' Application code now owns the hardware topology decision
$vbLabelText   $csharpLabel
// IronOCR: なし backend selection. なし config objects. Zero hardware decisions.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
// Runs on CPU, Linux, Docker, or ARM without a code change
// IronOCR: なし backend selection. なし config objects. Zero hardware decisions.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";

var result = new IronTesseract().Read("document.jpg");
Console.WriteLine(result.Text);
// Runs on CPU, Linux, Docker, or ARM without a code change
Imports IronOcr

' IronOCR: なし backend selection. なし config objects. Zero hardware decisions.
IronOcr.License.LicenseKey = "YOUR-LICENSE-KEY"

Dim result = New IronTesseract().Read("document.jpg")
Console.WriteLine(result.Text)
' Runs on CPU, Linux, Docker, or ARM without a code change
$vbLabelText   $csharpLabel

IronOCR 対 PaddleSharp OCR:機能比較

移行時に最も重要な要素について、各機能の直接的な比較は以下の通りです:

フィーチャー PaddleSharp OCR IronOCR
必要なNuGetパッケージ 最低3~4人 1
画像入力方法 OpenCV Cv2.ImRead() 直接パス、ストリーム、またはバイト配列
PDF入力(ネイティブ) なし はい
パスワードで保護されたPDF なし はい
複数ページTIFF OpenCV経由 ネイティブ
検索可能なPDF出力 なし はい (result.SaveAsSearchablePdf())
hOCRエクスポート なし はい
バックエンドの選択(CPU/GPU/OpenVINO) マニュアル PaddleConfig 自動翻訳
前処理パイプライン OpenCVの手動操作 内蔵 (Deskew, DeNoise, Contrast など)
推論セッションのライフサイクル管理 マニュアル(高コストな構成) 軽量 IronTesseract
表認識モデル ダウンロードとコードのパスを分離する input.LoadImage() + 構造化された結果
対応言語 約10~20 125+
言語のインストール モデルファイルのダウンロード NuGetパッケージ
多言語同時通訳 制限的 はい (OcrLanguage.French + OcrLanguage.German)
地域ベースのOCR 組み込みの CropRectangle
OCR中のバーコード読み取り なし はい (ocr.Configuration.ReadBarCodes = true)
信頼度スコア 地域別 単語単位、行単位、ページ単位
構造化された出力階層 フラットな地域リスト ページ → 段落 → 行 → WORD → 文字
クロスプラットフォーム展開 複雑(プラットフォームランタイムパッケージ) 単一のNuGetパッケージ、全プラットフォーム対応
Dockerデプロイメント 複数のレイヤー、ランタイムパッケージ 単層
商用サポート GitHubのイシュー(主に中国語) メールサポート
ライセンスモデル アパッチ2.0 永続 ($999 Lite, $1,499 Pro, $2,999 Enterprise)

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

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

PaddleSharp およびその OpenCV 依存関係を削除してください:

dotnet remove package Sdcb.PaddleOCR
dotnet remove package Sdcb.PaddleInference
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet remove package Sdcb.PaddleOCR
dotnet remove package Sdcb.PaddleInference
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
SHELL

NuGetからIronOCRをインストールしてください。

dotnet add package IronOcr

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

PaddleSharp ネームスペースを単一の IronOCR ネームスペースに置き換えてください:

// Before (PaddleSharp)
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using Sdcb.PaddleInference;
using OpenCvSharp;

// After (IronOCR)
using IronOcr;
// Before (PaddleSharp)
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using Sdcb.PaddleInference;
using OpenCvSharp;

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

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

アプリケーションの起動時に一度、Startup.cs、またはコンポジションルートでライセンスの初期化を追加してください。

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

コード移行の例

推論セッションのライフサイクル置換

PaddleSharpのPaddleOcrAllは、インスタンス化時に3つのモデルバイナリを同期的にロードするため、構築が高価です。 本番環境のアプリケーションでは、これを長寿命オブジェクトとして扱う必要があり、これが特定の依存性注入パターンを導きます。 また、基盤となるネイティブリソースを正しい順序で解放する必要があるため、破棄処理の順序にも注意が必要です。

PaddleSharp OCRのアプローチ:

// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;
using Microsoft.Extensions.DependencyInjection;

// Expensive: loads 3 model files from disk on construction (~300–800ms)
public class PaddleOcrEngine : IDisposable
{
    private readonly PaddleOcrAll _ocr;
    private bool _disposed;

    public PaddleOcrEngine()
    {
        var detModel = LocalFullModels.ChineseV3.DetectionModel;
        var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
        var recModel = LocalFullModels.ChineseV3.RecognitionModel;

        // Must be singleton — cannot afford per-request construction
        _ocr = new PaddleOcrAll(detModel, clsModel, recModel);
    }

    public string Read(string imagePath)
    {
        using var mat = Cv2.ImRead(imagePath); // OpenCV required even for a file path
        var result = _ocr.Run(mat);
        return string.Join(" ", result.Regions.Select(r => r.Text));
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            _ocr?.Dispose();
            _disposed = true;
        }
    }
}

// Startup.cs — forced singleton because of construction cost
services.AddSingleton<PaddleOcrEngine>();
// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;
using Microsoft.Extensions.DependencyInjection;

// Expensive: loads 3 model files from disk on construction (~300–800ms)
public class PaddleOcrEngine : IDisposable
{
    private readonly PaddleOcrAll _ocr;
    private bool _disposed;

    public PaddleOcrEngine()
    {
        var detModel = LocalFullModels.ChineseV3.DetectionModel;
        var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
        var recModel = LocalFullModels.ChineseV3.RecognitionModel;

        // Must be singleton — cannot afford per-request construction
        _ocr = new PaddleOcrAll(detModel, clsModel, recModel);
    }

    public string Read(string imagePath)
    {
        using var mat = Cv2.ImRead(imagePath); // OpenCV required even for a file path
        var result = _ocr.Run(mat);
        return string.Join(" ", result.Regions.Select(r => r.Text));
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            _ocr?.Dispose();
            _disposed = true;
        }
    }
}

// Startup.cs — forced singleton because of construction cost
services.AddSingleton<PaddleOcrEngine>();
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models
Imports OpenCvSharp
Imports Microsoft.Extensions.DependencyInjection

' Expensive: loads 3 model files from disk on construction (~300–800ms)
Public Class PaddleOcrEngine
    Implements IDisposable

    Private ReadOnly _ocr As PaddleOcrAll
    Private _disposed As Boolean

    Public Sub New()
        Dim detModel = LocalFullModels.ChineseV3.DetectionModel
        Dim clsModel = LocalFullModels.ChineseV3.ClassifierModel
        Dim recModel = LocalFullModels.ChineseV3.RecognitionModel

        ' Must be singleton — cannot afford per-request construction
        _ocr = New PaddleOcrAll(detModel, clsModel, recModel)
    End Sub

    Public Function Read(imagePath As String) As String
        Using mat = Cv2.ImRead(imagePath) ' OpenCV required even for a file path
            Dim result = _ocr.Run(mat)
            Return String.Join(" ", result.Regions.Select(Function(r) r.Text))
        End Using
    End Function

    Public Sub Dispose() Implements IDisposable.Dispose
        If Not _disposed Then
            _ocr?.Dispose()
            _disposed = True
        End If
    End Sub
End Class

' Startup.vb — forced singleton because of construction cost
services.AddSingleton(Of PaddleOcrEngine)()
$vbLabelText   $csharpLabel

IronOCRのアプローチ:

using IronOcr;
using Microsoft.Extensions.DependencyInjection;

// IronTesseract has lightweight initialization — no model loading on construction
public class OcrEngine
{
    public string Read(string imagePath)
    {
        return new IronTesseract().Read(imagePath).Text;
    }
}

// Flexible registration — singleton, scoped, or transient all work
services.AddTransient<OcrEngine>();

// Or skip the wrapper entirely and inject IronTesseract directly
services.AddTransient<IronTesseract>();
using IronOcr;
using Microsoft.Extensions.DependencyInjection;

// IronTesseract has lightweight initialization — no model loading on construction
public class OcrEngine
{
    public string Read(string imagePath)
    {
        return new IronTesseract().Read(imagePath).Text;
    }
}

// Flexible registration — singleton, scoped, or transient all work
services.AddTransient<OcrEngine>();

// Or skip the wrapper entirely and inject IronTesseract directly
services.AddTransient<IronTesseract>();
Imports IronOcr
Imports Microsoft.Extensions.DependencyInjection

' IronTesseract has lightweight initialization — no model loading on construction
Public Class OcrEngine
    Public Function Read(imagePath As String) As String
        Return New IronTesseract().Read(imagePath).Text
    End Function
End Class

' Flexible registration — singleton, scoped, or transient all work
services.AddTransient(Of OcrEngine)()

' Or skip the wrapper entirely and inject IronTesseract directly
services.AddTransient(Of IronTesseract)()
$vbLabelText   $csharpLabel

強制シングルトンから柔軟なライフタイムへの移行は、重要な変更点です。 PaddleSharpの構築コストは、サービスのライフサイクルにおける決定を固定します; IronOCR では、アプリケーションのスレッド処理やリクエストの分離に関する要件に基づいて選択することができます。 IronTesseractセットアップガイドでは、インスタンスレベルで適用される設定オプションについて解説しています。

OpenCV 前処理パイプラインの移行

PaddleSharpは、低品質なスキャン画像を扱う場合、通常、OCRエンジンを呼び出す前にOpenCVによる前処理パイプラインを構築します。このパイプラインにはOpenCVのAPIに関する知識が必要ですが、その範囲は、実際のOCR前処理タスクで必要とされるものよりもはるかに広範です。 一般的な操作(デスクュー、ノイズ除去、コントラストストレッチ)は、複数のusingブロックでの慎重なメモリ管理を必要とします。

PaddleSharp OCRのアプローチ:

// Simplified — see OpenCvSharp documentation for full API
using OpenCvSharp;
using Sdcb.PaddleOCR;

public string ReadWithPreprocessing(string imagePath, PaddleOcrAll ocr)
{
    using var original = Cv2.ImRead(imagePath);

    // Step 1: Grayscale conversion
    using var gray = new Mat();
    Cv2.CvtColor(original, gray, ColorConversionCodes.BGR2GRAY);

    // Step 2: Denoise (Gaussian blur to reduce noise)
    using var denoised = new Mat();
    Cv2.GaussianBlur(gray, denoised, new Size(3, 3), 0);

    // Step 3: Adaptive threshold for binarization
    using var binary = new Mat();
    Cv2.AdaptiveThreshold(denoised, binary, 255,
        AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2);

    // Step 4: Deskew — requires custom rotation detection logic (not shown)
    // Several dozen lines of custom Mat operations

    var result = ocr.Run(binary);
    return string.Join(" ", result.Regions.Select(r => r.Text));
    // Each Mat must be disposed; missing a using block leaks native memory
}
// Simplified — see OpenCvSharp documentation for full API
using OpenCvSharp;
using Sdcb.PaddleOCR;

public string ReadWithPreprocessing(string imagePath, PaddleOcrAll ocr)
{
    using var original = Cv2.ImRead(imagePath);

    // Step 1: Grayscale conversion
    using var gray = new Mat();
    Cv2.CvtColor(original, gray, ColorConversionCodes.BGR2GRAY);

    // Step 2: Denoise (Gaussian blur to reduce noise)
    using var denoised = new Mat();
    Cv2.GaussianBlur(gray, denoised, new Size(3, 3), 0);

    // Step 3: Adaptive threshold for binarization
    using var binary = new Mat();
    Cv2.AdaptiveThreshold(denoised, binary, 255,
        AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2);

    // Step 4: Deskew — requires custom rotation detection logic (not shown)
    // Several dozen lines of custom Mat operations

    var result = ocr.Run(binary);
    return string.Join(" ", result.Regions.Select(r => r.Text));
    // Each Mat must be disposed; missing a using block leaks native memory
}
Imports OpenCvSharp
Imports Sdcb.PaddleOCR

Public Function ReadWithPreprocessing(imagePath As String, ocr As PaddleOcrAll) As String
    Using original As Mat = Cv2.ImRead(imagePath)

        ' Step 1: Grayscale conversion
        Using gray As New Mat()
            Cv2.CvtColor(original, gray, ColorConversionCodes.BGR2GRAY)

            ' Step 2: Denoise (Gaussian blur to reduce noise)
            Using denoised As New Mat()
                Cv2.GaussianBlur(gray, denoised, New Size(3, 3), 0)

                ' Step 3: Adaptive threshold for binarization
                Using binary As New Mat()
                    Cv2.AdaptiveThreshold(denoised, binary, 255, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2)

                    ' Step 4: Deskew — requires custom rotation detection logic (not shown)
                    ' Several dozen lines of custom Mat operations

                    Dim result = ocr.Run(binary)
                    Return String.Join(" ", result.Regions.Select(Function(r) r.Text))
                End Using
            End Using
        End Using
    End Using
End Function
$vbLabelText   $csharpLabel

IronOCRのアプローチ:

using IronOcr;

public string ReadWithPreprocessing(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);

    // Named operations replace OpenCV knowledge requirements
    input.Deskew();
    input.DeNoise();
    input.Contrast();
    input.Binarize();

    var result = new IronTesseract().Read(input);
    return result.Text;
    // OcrInput implements IDisposable; using block handles cleanup
}
using IronOcr;

public string ReadWithPreprocessing(string imagePath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);

    // Named operations replace OpenCV knowledge requirements
    input.Deskew();
    input.DeNoise();
    input.Contrast();
    input.Binarize();

    var result = new IronTesseract().Read(input);
    return result.Text;
    // OcrInput implements IDisposable; using block handles cleanup
}
Imports IronOcr

Public Function ReadWithPreprocessing(imagePath As String) As String
    Using input As New OcrInput()
        input.LoadImage(imagePath)

        ' Named operations replace OpenCV knowledge requirements
        input.Deskew()
        input.DeNoise()
        input.Contrast()
        input.Binarize()

        Dim result = New IronTesseract().Read(input)
        Return result.Text
        ' OcrInput implements IDisposable; using block handles cleanup
    End Using
End Function
$vbLabelText   $csharpLabel

Matの割り当てなし。 適応しきい値パラメータに関する知識は不要です。 独自の傾き補正計算は行わないでください。 OpenCVのコードで30~50行必要だった前処理パイプラインが、4つのメソッド呼び出しで済むようになります。 画像品質補正ガイドでは、利用可能なすべてのフィルターについて、適用前後の例を交えて解説しています。 重い背景ノイズがある文書に対して、DeNoise()よりも遠くまで進みます。

前処理要件が標準的でないチーム向けに、フィルターウィザードは、コードを確定する前に特定のドキュメントタイプに対してフィルターの組み合わせを評価できる対話型ツールを提供します。

バックエンドの選定と排除

PaddleSharpは、推論バックエンドをアプリケーションレベルの機能として公開します。 CPUのみのクラウドVM上で実行する必要があるデプロイメントでは、GPUワークステーションやIntel OpenVINO対応のエッジデバイスをターゲットとする場合とは異なる初期化コードが使用されます。そのような条件分岐ロジックは、通常、アプリケーションの起動コード、環境変数のチェック、または機能フラグなどに組み込まれます。これらは、画像からテキストを読み取る作業とは無関係なインフラストラクチャ関連の作業です。

PaddleSharp OCRのアプローチ:

// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;

public PaddleOcrAll CreateOcrEngine(string backendMode)
{
    // Each backend requires a different NuGet runtime package installed
    switch (backendMode)
    {
        case "gpu":
            // Requires: Sdcb.PaddleInference.runtime.win64.cuda
            // Requires: CUDA toolkit + cuDNN installed on host
            var gpuConfig = PaddleConfig.FromModelDir("models/");
            gpuConfig.EnableGpu(500, deviceId: 0); // Simplified
            break;

        case "openvino":
            // Requires: Sdcb.PaddleInference.runtime.win64.mkl
            var oviConfig = PaddleConfig.FromModelDir("models/");
            oviConfig.EnableMkldnn(); // Simplified
            break;

        default:
            // CPU-only — still requires platform-specific runtime package
            var cpuConfig = PaddleConfig.FromModelDir("models/");
            cpuConfig.SetCpuMathLibraryNumThreads(Environment.ProcessorCount);
            break;
    }

    // Backend-specific config passed to model constructors — Simplified
    var detModel = LocalFullModels.ChineseV3.DetectionModel;
    var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
    var recModel = LocalFullModels.ChineseV3.RecognitionModel;
    return new PaddleOcrAll(detModel, clsModel, recModel);
}
// Simplified — see Sdcb.PaddleInference documentation for full API
using Sdcb.PaddleInference;
using Sdcb.PaddleOCR;

public PaddleOcrAll CreateOcrEngine(string backendMode)
{
    // Each backend requires a different NuGet runtime package installed
    switch (backendMode)
    {
        case "gpu":
            // Requires: Sdcb.PaddleInference.runtime.win64.cuda
            // Requires: CUDA toolkit + cuDNN installed on host
            var gpuConfig = PaddleConfig.FromModelDir("models/");
            gpuConfig.EnableGpu(500, deviceId: 0); // Simplified
            break;

        case "openvino":
            // Requires: Sdcb.PaddleInference.runtime.win64.mkl
            var oviConfig = PaddleConfig.FromModelDir("models/");
            oviConfig.EnableMkldnn(); // Simplified
            break;

        default:
            // CPU-only — still requires platform-specific runtime package
            var cpuConfig = PaddleConfig.FromModelDir("models/");
            cpuConfig.SetCpuMathLibraryNumThreads(Environment.ProcessorCount);
            break;
    }

    // Backend-specific config passed to model constructors — Simplified
    var detModel = LocalFullModels.ChineseV3.DetectionModel;
    var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
    var recModel = LocalFullModels.ChineseV3.RecognitionModel;
    return new PaddleOcrAll(detModel, clsModel, recModel);
}
Imports Sdcb.PaddleInference
Imports Sdcb.PaddleOCR

Public Function CreateOcrEngine(ByVal backendMode As String) As PaddleOcrAll
    ' Each backend requires a different NuGet runtime package installed
    Select Case backendMode
        Case "gpu"
            ' Requires: Sdcb.PaddleInference.runtime.win64.cuda
            ' Requires: CUDA toolkit + cuDNN installed on host
            Dim gpuConfig As PaddleConfig = PaddleConfig.FromModelDir("models/")
            gpuConfig.EnableGpu(500, deviceId:=0) ' Simplified

        Case "openvino"
            ' Requires: Sdcb.PaddleInference.runtime.win64.mkl
            Dim oviConfig As PaddleConfig = PaddleConfig.FromModelDir("models/")
            oviConfig.EnableMkldnn() ' Simplified

        Case Else
            ' CPU-only — still requires platform-specific runtime package
            Dim cpuConfig As PaddleConfig = PaddleConfig.FromModelDir("models/")
            cpuConfig.SetCpuMathLibraryNumThreads(Environment.ProcessorCount)
    End Select

    ' Backend-specific config passed to model constructors — Simplified
    Dim detModel = LocalFullModels.ChineseV3.DetectionModel
    Dim clsModel = LocalFullModels.ChineseV3.ClassifierModel
    Dim recModel = LocalFullModels.ChineseV3.RecognitionModel
    Return New PaddleOcrAll(detModel, clsModel, recModel)
End Function
$vbLabelText   $csharpLabel

IronOCRのアプローチ:

using IronOcr;

// なし backend selection. なし switch statement. なし environment variable check.
// The same code runs on CPU-only VMs, GPU workstations, and ARM devices.
public IronTesseract CreateOcrEngine()
{
    return new IronTesseract();
}

// Parallel processing across CPU cores — no GPU configuration required
public IEnumerable<string> ReadBatch(IEnumerable<string> imagePaths)
{
    var results = new System.Collections.Concurrent.ConcurrentBag<string>();
    Parallel.ForEach(imagePaths, path =>
    {
        var result = new IronTesseract().Read(path);
        results.Add(result.Text);
    });
    return results;
}
using IronOcr;

// なし backend selection. なし switch statement. なし environment variable check.
// The same code runs on CPU-only VMs, GPU workstations, and ARM devices.
public IronTesseract CreateOcrEngine()
{
    return new IronTesseract();
}

// Parallel processing across CPU cores — no GPU configuration required
public IEnumerable<string> ReadBatch(IEnumerable<string> imagePaths)
{
    var results = new System.Collections.Concurrent.ConcurrentBag<string>();
    Parallel.ForEach(imagePaths, path =>
    {
        var result = new IronTesseract().Read(path);
        results.Add(result.Text);
    });
    return results;
}
Imports IronOcr
Imports System.Collections.Concurrent
Imports System.Threading.Tasks

' なし backend selection. なし switch statement. なし environment variable check.
' The same code runs on CPU-only VMs, GPU workstations, and ARM devices.
Public Function CreateOcrEngine() As IronTesseract
    Return New IronTesseract()
End Function

' Parallel processing across CPU cores — no GPU configuration required
Public Function ReadBatch(imagePaths As IEnumerable(Of String)) As IEnumerable(Of String)
    Dim results As New ConcurrentBag(Of String)()
    Parallel.ForEach(imagePaths, Sub(path)
                                     Dim result = New IronTesseract().Read(path)
                                     results.Add(result.Text)
                                 End Sub)
    Return results
End Function
$vbLabelText   $csharpLabel

ここでのParallel.ForEachパターンは、標準でスレッドセーフです。 それぞれのIronTesseractインスタンスは独立しており、共有されるネイティブ状態はありません。 PaddleSharpのデプロイにおいてバックエンドの条件分岐の管理に時間を費やしているチームにとって、この簡素化はデプロイの信頼性向上にもつながります。ハードウェア検出コードが不要になるため、同じビルド成果物があらゆる環境で動作するようになるからです。 この速度最適化ガイドでは、スループットが重要なシナリオにおける設定オプションについて解説しています。

表認識の移行

PaddleSharpでのテーブル抽出には、専用のテーブル認識モデルが必要です。これは、標準的な検出、分類、認識のセットに加えて、4つ目のモデルファイルとなります。 このテーブルモデルは個別のAPI呼び出しを使用し、独自の結果構造を返します。 請求書、フォーム、またはスプレッドシートの処理パイプラインを構築するチームは、2つの並列初期化パスと2つの結果解析戦略を維持します。

PaddleSharp OCRのアプローチ:

// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;

public class TableRecognitionService
{
    // Standard OCR engine — 3 models
    private readonly PaddleOcrAll _textOcr;

    // Table engine — 4th model, separate initialization
    // private readonly PaddleOcrTable _tableOcr; // Simplified

    public TableRecognitionService()
    {
        var detModel = LocalFullModels.ChineseV3.DetectionModel;
        var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
        var recModel = LocalFullModels.ChineseV3.RecognitionModel;
        _textOcr = new PaddleOcrAll(detModel, clsModel, recModel);

        // Table model: separate download, separate version tracking
        // var tableModel = LocalFullModels.TableEnV2.Model; // Simplified
        // _tableOcr = new PaddleOcrTable(tableModel); // Simplified
    }

    public void ProcessDocument(string imagePath)
    {
        using var image = Cv2.ImRead(imagePath);

        // Text extraction path
        var textResult = _textOcr.Run(image);
        var text = string.Join(" ", textResult.Regions.Select(r => r.Text));

        // Table extraction path — different API, different result structure
        // var tableResult = _tableOcr.Run(image); // Simplified
        // foreach (var cell in tableResult.Cells) { ... } // Simplified
    }
}
// Simplified — see Sdcb.PaddleOCR documentation for full API
using Sdcb.PaddleOCR;
using Sdcb.PaddleOCR.Models;
using OpenCvSharp;

public class TableRecognitionService
{
    // Standard OCR engine — 3 models
    private readonly PaddleOcrAll _textOcr;

    // Table engine — 4th model, separate initialization
    // private readonly PaddleOcrTable _tableOcr; // Simplified

    public TableRecognitionService()
    {
        var detModel = LocalFullModels.ChineseV3.DetectionModel;
        var clsModel = LocalFullModels.ChineseV3.ClassifierModel;
        var recModel = LocalFullModels.ChineseV3.RecognitionModel;
        _textOcr = new PaddleOcrAll(detModel, clsModel, recModel);

        // Table model: separate download, separate version tracking
        // var tableModel = LocalFullModels.TableEnV2.Model; // Simplified
        // _tableOcr = new PaddleOcrTable(tableModel); // Simplified
    }

    public void ProcessDocument(string imagePath)
    {
        using var image = Cv2.ImRead(imagePath);

        // Text extraction path
        var textResult = _textOcr.Run(image);
        var text = string.Join(" ", textResult.Regions.Select(r => r.Text));

        // Table extraction path — different API, different result structure
        // var tableResult = _tableOcr.Run(image); // Simplified
        // foreach (var cell in tableResult.Cells) { ... } // Simplified
    }
}
Imports Sdcb.PaddleOCR
Imports Sdcb.PaddleOCR.Models
Imports OpenCvSharp

Public Class TableRecognitionService
    ' Standard OCR engine — 3 models
    Private ReadOnly _textOcr As PaddleOcrAll

    ' Table engine — 4th model, separate initialization
    ' Private ReadOnly _tableOcr As PaddleOcrTable ' Simplified

    Public Sub New()
        Dim detModel = LocalFullModels.ChineseV3.DetectionModel
        Dim clsModel = LocalFullModels.ChineseV3.ClassifierModel
        Dim recModel = LocalFullModels.ChineseV3.RecognitionModel
        _textOcr = New PaddleOcrAll(detModel, clsModel, recModel)

        ' Table model: separate download, separate version tracking
        ' Dim tableModel = LocalFullModels.TableEnV2.Model ' Simplified
        ' _tableOcr = New PaddleOcrTable(tableModel) ' Simplified
    End Sub

    Public Sub ProcessDocument(imagePath As String)
        Using image = Cv2.ImRead(imagePath)
            ' Text extraction path
            Dim textResult = _textOcr.Run(image)
            Dim text = String.Join(" ", textResult.Regions.Select(Function(r) r.Text))

            ' Table extraction path — different API, different result structure
            ' Dim tableResult = _tableOcr.Run(image) ' Simplified
            ' For Each cell In tableResult.Cells ' Simplified
            ' ... 
            ' Next
        End Using
    End Sub
End Class
$vbLabelText   $csharpLabel

IronOCRのアプローチ:

using IronOcr;

public class TableRecognitionService
{
    // One engine handles both text and table regions
    public void ProcessDocument(string imagePath)
    {
        var ocr = new IronTesseract();
        var result = ocr.Read(imagePath);

        // Structured hierarchy: pages → paragraphs → lines → words
        foreach (var page in result.Pages)
        {
            foreach (var paragraph in page.Paragraphs)
            {
                Console.WriteLine($"Block at ({paragraph.X},{paragraph.Y}): {paragraph.Text}");
            }
        }

        Console.WriteLine($"Full document text: {result.Text}");
    }
}
using IronOcr;

public class TableRecognitionService
{
    // One engine handles both text and table regions
    public void ProcessDocument(string imagePath)
    {
        var ocr = new IronTesseract();
        var result = ocr.Read(imagePath);

        // Structured hierarchy: pages → paragraphs → lines → words
        foreach (var page in result.Pages)
        {
            foreach (var paragraph in page.Paragraphs)
            {
                Console.WriteLine($"Block at ({paragraph.X},{paragraph.Y}): {paragraph.Text}");
            }
        }

        Console.WriteLine($"Full document text: {result.Text}");
    }
}
Imports IronOcr

Public Class TableRecognitionService
    ' One engine handles both text and table regions
    Public Sub ProcessDocument(imagePath As String)
        Dim ocr As New IronTesseract()
        Dim result = ocr.Read(imagePath)

        ' Structured hierarchy: pages → paragraphs → lines → words
        For Each page In result.Pages
            For Each paragraph In page.Paragraphs
                Console.WriteLine($"Block at ({paragraph.X},{paragraph.Y}): {paragraph.Text}")
            Next
        Next

        Console.WriteLine($"Full document text: {result.Text}")
    End Sub
End Class
$vbLabelText   $csharpLabel

表の構造そのものを行と列として抽出する必要があるドキュメントの場合、IronOCRは専用の表抽出機能を提供します:

using IronOcr;

var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("invoice-with-table.jpg");

var result = ocr.Read(input);

// Access structured page layout for table region extraction
foreach (var page in result.Pages)
{
    foreach (var line in page.Lines)
    {
        // Lines within a table region preserve spatial ordering
        Console.WriteLine($"Row text: {line.Text} | Y position: {line.Y}");
        foreach (var word in line.Words)
        {
            Console.WriteLine($"  Cell: '{word.Text}' at X={word.X}");
        }
    }
}
using IronOcr;

var ocr = new IronTesseract();
using var input = new OcrInput();
input.LoadImage("invoice-with-table.jpg");

var result = ocr.Read(input);

// Access structured page layout for table region extraction
foreach (var page in result.Pages)
{
    foreach (var line in page.Lines)
    {
        // Lines within a table region preserve spatial ordering
        Console.WriteLine($"Row text: {line.Text} | Y position: {line.Y}");
        foreach (var word in line.Words)
        {
            Console.WriteLine($"  Cell: '{word.Text}' at X={word.X}");
        }
    }
}
Imports IronOcr

Dim ocr As New IronTesseract()
Using input As New OcrInput()
    input.LoadImage("invoice-with-table.jpg")

    Dim result = ocr.Read(input)

    ' Access structured page layout for table region extraction
    For Each page In result.Pages
        For Each line In page.Lines
            ' Lines within a table region preserve spatial ordering
            Console.WriteLine($"Row text: {line.Text} | Y position: {line.Y}")
            For Each word In line.Words
                Console.WriteLine($"  Cell: '{word.Text}' at X={word.X}")
            Next
        Next
    Next
End Using
$vbLabelText   $csharpLabel

モデル1つのダウンロードが削除されました。 初期化パスが1つ削除されました。 IronOCRの構造化された結果階層(単語レベルのX/Y座標を含む)は、個別の認識モデルを使用せずに表の行と列を再構築するために必要な位置情報を提供します。 "テーブル読み取りガイド"および"読み取り結果ガイド"では、構造化出力APIの全範囲を網羅しています。

スキャンした文書からの検索可能なPDF出力

PaddleSharpはテキスト文字列のみを生成し、それ以外は何も生成しません。 スキャンしたPDFをテキスト検索可能にするドキュメントアーカイブを構築するには、別のPDFライブラリを統合し、テキストオーバーレイ層を実装し、2つのライブラリを連携させて管理する必要があります。 この制約を受け入れたチームは、それが移行のきっかけになることが多いと気づきます。つまり、2つのライブラリを統合する労力が、OCRプロバイダーを切り替える労力を上回るからです。

PaddleSharp OCRのアプローチ:

// Simplified — PaddleSharp has no PDF output. Requires a separate PDF library.
// Example of what teams typically build:

// using Sdcb.PaddleOCR;
// using SomePdfLibrary; // Third dependency to produce searchable PDF

public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
    // Step 1: OCR via PaddleSharp — produces text only
    // var text = _ocr.Run(Cv2.ImRead(imagePath));

    // Step 2: Build a PDF with text overlay using a separate PDF library
    // Requires: text positions mapped to PDF coordinate space
    // Requires: image embedded as background
    // Requires: invisible text layer positioned over image
    // ~50–100 lines of PDF construction code
    throw new NotImplementedException("Requires a separate PDF library");
}
// Simplified — PaddleSharp has no PDF output. Requires a separate PDF library.
// Example of what teams typically build:

// using Sdcb.PaddleOCR;
// using SomePdfLibrary; // Third dependency to produce searchable PDF

public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
    // Step 1: OCR via PaddleSharp — produces text only
    // var text = _ocr.Run(Cv2.ImRead(imagePath));

    // Step 2: Build a PDF with text overlay using a separate PDF library
    // Requires: text positions mapped to PDF coordinate space
    // Requires: image embedded as background
    // Requires: invisible text layer positioned over image
    // ~50–100 lines of PDF construction code
    throw new NotImplementedException("Requires a separate PDF library");
}
Public Sub ArchiveScannedDocument(imagePath As String, outputPdfPath As String)
    ' Step 1: OCR via PaddleSharp — produces text only
    ' Dim text = _ocr.Run(Cv2.ImRead(imagePath))

    ' Step 2: Build a PDF with text overlay using a separate PDF library
    ' Requires: text positions mapped to PDF coordinate space
    ' Requires: image embedded as background
    ' Requires: invisible text layer positioned over image
    ' ~50–100 lines of PDF construction code
    Throw New NotImplementedException("Requires a separate PDF library")
End Sub
$vbLabelText   $csharpLabel

IronOCRのアプローチ:

using IronOcr;

public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);
    input.Deskew();   // Straighten scan before archiving
    input.DeNoise();  // Clean up scan artifacts

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

    // One call: OCR + searchable PDF with text layer + image background
    result.SaveAsSearchablePdf(outputPdfPath);
}

// Multi-page document — same pattern
public void ArchiveMultiPageDocument(string[] imageFiles, string outputPdfPath)
{
    using var input = new OcrInput();
    foreach (var file in imageFiles)
        input.LoadImage(file);

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

public void ArchiveScannedDocument(string imagePath, string outputPdfPath)
{
    using var input = new OcrInput();
    input.LoadImage(imagePath);
    input.Deskew();   // Straighten scan before archiving
    input.DeNoise();  // Clean up scan artifacts

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

    // One call: OCR + searchable PDF with text layer + image background
    result.SaveAsSearchablePdf(outputPdfPath);
}

// Multi-page document — same pattern
public void ArchiveMultiPageDocument(string[] imageFiles, string outputPdfPath)
{
    using var input = new OcrInput();
    foreach (var file in imageFiles)
        input.LoadImage(file);

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

Public Sub ArchiveScannedDocument(imagePath As String, outputPdfPath As String)
    Using input As New OcrInput()
        input.LoadImage(imagePath)
        input.Deskew()   ' Straighten scan before archiving
        input.DeNoise()  ' Clean up scan artifacts

        Dim ocr As New IronTesseract()
        Dim result = ocr.Read(input)

        ' One call: OCR + searchable PDF with text layer + image background
        result.SaveAsSearchablePdf(outputPdfPath)
    End Using
End Sub

' Multi-page document — same pattern
Public Sub ArchiveMultiPageDocument(imageFiles As String(), outputPdfPath As String)
    Using input As New OcrInput()
        For Each file In imageFiles
            input.LoadImage(file)
        Next

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

PDFライブラリはありません。 座標マッピングは行わないでください。 テキストレイヤーの配置は不要です。 IronOCRの検索可能なPDF出力形式では、元の画像の上に目に見えないテキストレイヤーを埋め込むことで、スキャンした文書に視覚的に忠実でありながら、テキスト検索が完全に可能なファイルが生成されます。 検索可能なPDF形式ハウツーガイドでは、ページの選択、品質オプション、およびメタデータの制御について解説しています。

PaddleSharp OCRAPI から IronOCR へのマッピングリファレンス

PaddleSharp OCR IronOCR
Sdcb.PaddleOCR (namespace) IronOcr (namespace)
Sdcb.PaddleInference (namespace) 不要 — 自動設定済み
PaddleOcrAll IronTesseract
new PaddleOcrAll(det, cls, rec) new IronTesseract()
LocalFullModels.ChineseV3.DetectionModel 同等のものがない — モデル選択なし
LocalFullModels.ChineseV3.ClassifierModel 同等のものがない — モデル選択なし
LocalFullModels.ChineseV3.RecognitionModel 同等のものがない — モデル選択なし
PaddleConfig.FromModelDir() 対応する項目なし — config オブジェクトなし
config.EnableGpu(memMB, deviceId) 対応する項目なし — バックエンドは自動処理されます
config.EnableMkldnn() 対応する項目なし — バックエンドは自動処理されます
config.SetCpuMathLibraryNumThreads(n) 対応する項目なし — 内部管理
Cv2.ImRead(path) (OpenCV load) input.LoadImage(path)
ocr.Run(mat) ocr.Read(input) または ocr.Read("file.jpg")
result.Regions result.Pages[0].Words または result.Pages[0].Lines
region.Text word.Text, line.Text, paragraph.Text
region.Rect.Center.X/.Y word.X, word.Y
region.Score (confidence) word.Confidence, result.Confidence
モデルレベルの言語置換 ocr.Language = OcrLanguage.French
テーブルモデル(別途ダウンロード) 組み込みの構造化結果階層
Cv2.CvtColor(..., GRAY) input.Binarize() または input.Contrast()
Cv2.GaussianBlur(...) input.DeNoise()
検索可能なPDF出力はありません result.SaveAsSearchablePdf("output.pdf")

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

課題 1: OpenCV の依存関係がアンロードに失敗する

PaddleSharp OCR: OpenCvSharp4.runtime.winと類似のプラットフォーム固有のランタイムパッケージが、管理されていないネイティブDLLをインストールします。 これらの DLL は、一部のホスティング環境(特に IIS アプリケーションプールのリサイクル時)において適切なクリーンアップを妨げ、ビルド時に誤ったプラットフォームランタイムパッケージが参照された場合にアセンブリの読み込みエラーを引き起こす可能性があります。これらを削除するには、NuGet パッケージの削除に加え、出力ディレクトリ内のキャッシュされたネイティブバイナリをすべてクリアする必要があります。

ソリューション: OpenCvSharp4.runtime.*パッケージを削除した後にビルド出力ディレクトリをクリーンし、再ビルドしてください。

dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet clean
dotnet build
dotnet remove package OpenCvSharp4
dotnet remove package OpenCvSharp4.runtime.win
dotnet clean
dotnet build
SHELL

IronOCRは、ネイティブ依存関係を内部でバンドルし、アンマネージドのライフサイクルを処理します。 プラットフォーム固有のランタイムパッケージの選択は不要です。 IronTesseractセットアップガイドには、IronOCRが自動的に処理するプラットフォーム要件が記載されています。

課題 2: 移行後にディスクに残されたモデルファイル

PaddleSharp OCR: PaddleSharpによってダウンロードされたモデルファイル(検出、分類、認識、その他のテーブルモデル)は通常、アプリケーションに対して相対的なmodels/ディレクトリまたは設定されたパスに格納されます。 NuGet パッケージをアンインストールしても、これらのファイルは削除されません。 Docker イメージ内では、これらは不要なレイヤーサイズを増加させます。デプロイメントパイプラインでは、古いパスにある古いモデルファイルが、初期化コードの残骸によって参照されている場合、起動エラーの原因となる可能性があります。

解決策:移行の一環として、モデルディレクトリを明示的に削除してください。 スタートアップ構成にパス参照がないか確認してください:

# Locate model directory references in application code
grep -r "LocalFullModels\|ModelPath\|models/" --include="*.cs" .
grep -r "DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .
# Locate model directory references in application code
grep -r "LocalFullModels\|ModelPath\|models/" --include="*.cs" .
grep -r "DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .
SHELL

モデル参照が削除され、IronOCRが初期化されたら、リポジトリおよびDockerビルドコンテキストからモデルディレクトリを削除してください。

課題 3: 移行後にシングルトンのライフタイム仮定が破綻する

PaddleSharp OCR: PaddleOcrAllは構築コストがリクエストごとのインスタンス化を不可能にしたため、シングルトンとして登録されました。 IronOCRを同じシングルトン登録に組み込む移行コードは、リクエスト間で不要な状態の共有を引き起こします。 同時に使用するとIronTesseractはスレッドセーフですが、単一インスタンスを共有する必要はありません。すべてのインスタンスは独立しています。

解決策:シングルトンの登録が、パフォーマンス以外の目的にも役立つかどうかを評価してください。 ほとんどの ASP.NET Core アプリケーションにおいて、IronOCR を使用する場合、一時的な登録がより適切な選択肢となります:

// PaddleSharp — forced singleton due to construction cost
services.AddSingleton<PaddleOcrAll>(sp =>
{
    var det = LocalFullModels.ChineseV3.DetectionModel;  // Simplified
    var cls = LocalFullModels.ChineseV3.ClassifierModel; // Simplified
    var rec = LocalFullModels.ChineseV3.RecognitionModel; // Simplified
    return new PaddleOcrAll(det, cls, rec);
});

// IronOCR — transient works; no expensive construction
services.AddTransient<IronTesseract>();
// PaddleSharp — forced singleton due to construction cost
services.AddSingleton<PaddleOcrAll>(sp =>
{
    var det = LocalFullModels.ChineseV3.DetectionModel;  // Simplified
    var cls = LocalFullModels.ChineseV3.ClassifierModel; // Simplified
    var rec = LocalFullModels.ChineseV3.RecognitionModel; // Simplified
    return new PaddleOcrAll(det, cls, rec);
});

// IronOCR — transient works; no expensive construction
services.AddTransient<IronTesseract>();
Imports Microsoft.Extensions.DependencyInjection

' PaddleSharp — forced singleton due to construction cost
services.AddSingleton(Of PaddleOcrAll)(Function(sp)
    Dim det = LocalFullModels.ChineseV3.DetectionModel ' Simplified
    Dim cls = LocalFullModels.ChineseV3.ClassifierModel ' Simplified
    Dim rec = LocalFullModels.ChineseV3.RecognitionModel ' Simplified
    Return New PaddleOcrAll(det, cls, rec)
End Function)

' IronOCR — transient works; no expensive construction
services.AddTransient(Of IronTesseract)()
$vbLabelText   $csharpLabel

明示的なインスタンスの再利用が必要な高スループットのバッチ処理シナリオにおいても、シングルトンやプールパターンは有効です。ただし、これはパフォーマンス上の選択であり、正しさの要件ではありません。

課題 4: 結果領域の順序指定が不要になりました

PaddleSharp OCR: result.Regionsは検出順序で検出されたテキスト領域を返すため、必ずしも読み取る順序(左から右、上から下)とは一致しません。 チームは通常、領域テキストを結合する前に.Rect.Center.Xを適用します。このパターンは、ほとんどすべてのPaddleSharpテキスト抽出実装に見られます。 このパターンを文字通り IronOCR に移植すると、冗長なコードが生成されます。

解決策:IronOCRは、デフォルトで読み取り順に結果を返します。 ソートを取り除く:

// PaddleSharp — manual reading-order sort required
var text = string.Join("\n", result.Regions
    .OrderBy(r => r.Rect.Center.Y)
    .ThenBy(r => r.Rect.Center.X)
    .Select(r => r.Text));

// IronOCR — result.Text is already in reading order; no sort needed
var text = result.Text;

// For word-level access with position, use the structured hierarchy directly
foreach (var word in result.Pages[0].Words)
{
    Console.WriteLine($"{word.Text} at ({word.X},{word.Y})");
}
// PaddleSharp — manual reading-order sort required
var text = string.Join("\n", result.Regions
    .OrderBy(r => r.Rect.Center.Y)
    .ThenBy(r => r.Rect.Center.X)
    .Select(r => r.Text));

// IronOCR — result.Text is already in reading order; no sort needed
var text = result.Text;

// For word-level access with position, use the structured hierarchy directly
foreach (var word in result.Pages[0].Words)
{
    Console.WriteLine($"{word.Text} at ({word.X},{word.Y})");
}
Imports System
Imports System.Linq

' PaddleSharp — manual reading-order sort required
Dim text = String.Join(vbCrLf, result.Regions _
    .OrderBy(Function(r) r.Rect.Center.Y) _
    .ThenBy(Function(r) r.Rect.Center.X) _
    .Select(Function(r) r.Text))

' IronOCR — result.Text is already in reading order; no sort needed
text = result.Text

' For word-level access with position, use the structured hierarchy directly
For Each word In result.Pages(0).Words
    Console.WriteLine($"{word.Text} at ({word.X},{word.Y})")
Next
$vbLabelText   $csharpLabel

課題 5: バックエンド条件付きパッケージの復元が機能しない

PaddleSharp OCR: 一部のPaddleSharpセットアップは、ターゲット環境に基づいて異なるSdcb.PaddleInference.runtime.*パッケージを条件付きで参照します(GPU用のCUDA、OpenVINO用のMKL、CPU専用)。 これは.csproj条件として表示されることもあれば、デプロイメントターゲットごとに異なるプロジェクトファイルとして表示されることもあります。 結果として生成されたビルドマトリックスは、誤ったパッケージセットが復元された場合にCIパイプラインを中断させます。

ソリューション: PaddleSharpパッケージを削除した後、任意のPackageReferenceブロックを監査し、それらを完全に削除してください。

grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
grep -n "Sdcb\|OpenCvSharp\|PaddleInference" *.csproj
SHELL

IronOCRはプラットフォーム条件なしで単一のIronOcrパッケージ参照を使用します。 このパッケージは、Windows、Linux、macOSのいずれでも正しく復元されます。

課題 6: 表の結果構造に直接対応する表現がない

PaddleSharp OCR: PaddleOcrTableは、認識されたセルごとに行と列のインデックスを持つセルベースの構造を返します。 この構造を使用するコードは通常、(row, column)をインデックスとする2次元配列を構築します。 IronOCRは、セルインデックスの構造そのものを提供するわけではありません。セルグリッドを再構築するために空間的なグループ化が必要な、単語および行の座標を提供します。

解決策:IronOCRの単語座標から、行はY座標によるグループ化、列はX座標によるソートを用いて、表の構造を再構築します。 一般的な表形式については、表の読み方ガイドで空間的なグループ化の手法が紹介されています。 既知のフィールド位置を持つ構造化された請求書について、領域ベースのOCRCropRectangleは、全ページのテーブル抽出よりもクリーンなパターンです。

using IronOcr;

// Target specific table cells by region instead of full-page table detection
var totalAmountRegion = new CropRectangle(400, 600, 200, 30); // x, y, width, height
using var input = new OcrInput();
input.LoadImage("invoice.jpg", totalAmountRegion);

var result = new IronTesseract().Read(input);
Console.WriteLine($"Total: {result.Text}");
using IronOcr;

// Target specific table cells by region instead of full-page table detection
var totalAmountRegion = new CropRectangle(400, 600, 200, 30); // x, y, width, height
using var input = new OcrInput();
input.LoadImage("invoice.jpg", totalAmountRegion);

var result = new IronTesseract().Read(input);
Console.WriteLine($"Total: {result.Text}");
Imports IronOcr

' Target specific table cells by region instead of full-page table detection
Dim totalAmountRegion As New CropRectangle(400, 600, 200, 30) ' x, y, width, height
Using input As New OcrInput()
    input.LoadImage("invoice.jpg", totalAmountRegion)

    Dim result = New IronTesseract().Read(input)
    Console.WriteLine($"Total: {result.Text}")
End Using
$vbLabelText   $csharpLabel

PaddleSharp OCR移行チェックリスト

移行前

パッケージを削除する前に、コードベース内のすべての PaddleSharp の参照を監査してください:

# Find all PaddleSharp and PaddleInference usages
grep -rn "Sdcb\.PaddleOCR\|Sdcb\.PaddleInference" --include="*.cs" .

# Find OpenCV usages that will need replacement
grep -rn "OpenCvSharp\|Cv2\.\|using var.*Mat\b" --include="*.cs" .

# Find model path references and configuration
grep -rn "LocalFullModels\|ModelDir\|DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .

# Find backend selection logic
grep -rn "EnableGpu\|EnableMkldnn\|PaddleConfig\|SetCpuMath" --include="*.cs" .

# Find table recognition usages
grep -rn "PaddleOcrTable\|TableModel\|table.*ocr\|ocr.*table" --include="*.cs" .

# Find result region access patterns that need updating
grep -rn "\.Regions\b\|Rect\.Center\|region\.Text" --include="*.cs" .
# Find all PaddleSharp and PaddleInference usages
grep -rn "Sdcb\.PaddleOCR\|Sdcb\.PaddleInference" --include="*.cs" .

# Find OpenCV usages that will need replacement
grep -rn "OpenCvSharp\|Cv2\.\|using var.*Mat\b" --include="*.cs" .

# Find model path references and configuration
grep -rn "LocalFullModels\|ModelDir\|DetectionModel\|RecognitionModel\|ClassifierModel" --include="*.cs" .

# Find backend selection logic
grep -rn "EnableGpu\|EnableMkldnn\|PaddleConfig\|SetCpuMath" --include="*.cs" .

# Find table recognition usages
grep -rn "PaddleOcrTable\|TableModel\|table.*ocr\|ocr.*table" --include="*.cs" .

# Find result region access patterns that need updating
grep -rn "\.Regions\b\|Rect\.Center\|region\.Text" --include="*.cs" .
SHELL

ディスク上のモデルファイルを一覧表示し、そのパスを記録してください。 すべてのデプロイメントターゲットと、.csprojにGPUまたはOpenVINO固有のNuGet条件があるかどうかを確認してください。 PaddleSharpの構築コストによりシングルトンとして登録されているサービスがある場合は、その点に留意してください。

コードの移行

  1. すべてのプロジェクトファイルからすべてのOpenCvSharp4.runtime.* NuGetパッケージを削除してください。
  2. IronOcr NuGetパッケージをインストールします。
  3. 必要な言語の言語NuGetパッケージをインストールします(例:IronOcr.Languages.ChineseSimplified)。
  4. アプリケーションの起動時にIronOcr.License.LicenseKey = "YOUR-LICENSE-KEY";を追加してください。
  5. すべてのusing IronOcrで置き換えてください。
  6. new IronTesseract()で置き換えてください。
  7. すべてのPaddleConfigバックエンド選択ブロック(CPU、GPU、OpenVINO条件)を削除してください。
  8. input.LoadImage(path)に置き換えてください。
  9. OpenCVの前処理操作(Binarize())で置き換えてください。
  10. ocr.Read(input)で置き換えてください。
  11. result.Pages[n].Wordsで置き換えてください。
  12. .OrderBy(r => r.Rect.Center.Y).ThenBy(r => r.Rect.Center.X)ソートチェーンを削除してください — 読み取り順は自動です。
  13. OcrInput領域ベースのターゲティングまたは座標ベースの単語グルーピングで置き換えてください。
  14. 検索可能なPDFアーカイブが必要な場所にresult.SaveAsSearchablePdf(path)を追加してください。
  15. サービスのライフタイム登録を見直す:PaddleSharpのコンストラクタコストによって駆動されるシングルトン登録は、通常、一時的またはスコープ限定のものに変更可能です。
  16. ディスクからモデルファイルを削除し、Dockerビルドコンテキストからモデルディレクトリを削除します。
  17. プラットフォーム固有のPaddleまたはOpenCVランタイムパッケージ用のPackageReferenceブロックを削除してください。

移行後

  • パイプライン内の各ドキュメントタイプから抽出した代表的なサンプル(20~30件)について、テキスト抽出の出力が PaddleSharp の出力と同等か、それ以上であることを確認してください。
  • アプリケーションの起動ログにOpenCvSharp関連のアセンブリロード例外がないことを確認してください。
  • 同一のビルド成果物を使用して、各ターゲットプラットフォーム(Windows、Linux、Docker)でのデプロイをテストしてください。プラットフォーム固有のパッケージ選択は不要である必要があります。
  • 以前に手動の結果ソートが必要だった文書がresult.Textを介して正しく順序付けされたテキストを生成することを確認してください。
  • 検索可能なPDF出力ファイルが、Adobe Acrobat Readerまたは任意のPDFビューアでテキスト検索可能であることを確認してください。
  • アプリケーションを負荷状態で実行して、リクエストごとに作成されたPaddleOcrAll構築に匹敵するメモリ圧力を生じないことを確認してください。
  • NuGet パッケージとしてインストールされた言語パックが、追加のファイル展開手順なしに CI 環境で正しく復元されることを確認してください。
  • 領域ベースまたは座標グループ化のアプローチを用いて、期待される行/列構造に対してあらゆるテーブル抽出シナリオをテストしてください。
  • 起動パスからPaddleOcrAllシングルトン構築を削除した後、アプリケーションの起動時間が短縮されることを確認してください。

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

1つのパッケージが4つのパッケージスタックを置き換えます。 移行後、OCR依存関係フットプリントは単一のIronOcr NuGetリファレンスになります。 4つのパッケージスタック — OpenCvSharp4、およびプラットフォーム固有のランタイム — はプロジェクトファイルで1つのエントリになります。依存関係の監査、ライセンススキャン、および脆弱性の監視は、4つの表面ではなく1つの表面に集中します。

デプロイメントアーティファクトは環境を問わず統一されています。バックエンドの選択条件(CPU、GPU、OpenVINOの比較)はなくなりました。 単一のビルド成果物により、環境固有のパッケージ選択や初期化の分岐を行うことなく、開発者のノートPC、CIランナー、Linuxコンテナ、およびクラウドVMへのデプロイが可能です。 モデルファイルをCOPYする必要がなく、プラットフォームランタイムパッケージをインストールする必要がないため、Dockerイメージが縮小されます。

文書アーカイブパイプラインではもはや2番目のライブラリは必要ありません。 result.SaveAsSearchablePdf()は、以前PaddleSharpのチームが検索可能なアーカイブを生成するために追加していたPDFライブラリの依存関係を排除します。 OCR処理と検索可能なPDFへの書き込みは、1回のAPI呼び出しで実行されます。 1日に数千件のスキャン文書を処理するチームにとって、この簡素化により、ライブラリ間のバージョン競合という問題が一掃されます。 検索可能なPDFに関するブログ記事では、本番環境での運用に関する考慮事項について解説しています。

サービスライフタイムの決定がアプリケーションの要件を反映し、ライブラリの制約ではありません。 IronTesseractは軽量な構築です。 PaddleSharpの高コストなモデル読み込みによって引き起こされていた強制シングルトンパターンは、もはや必要ありません。 .NET Core では、リクエストごとにサービスのスコープを定義できるため、同時接続ユーザー間の分離が明確になり、共有状態によるスレッド関連の懸念が解消されます。 デプロイメントのオプションの詳細については、ASP.NET OCR ユースケースページをご覧ください。

言語の拡張は、研究プロジェクトではなく、パッケージのインストールです。125以上の言語カタログは、ヨーロッパ、アジア、中東の言語や特殊な文字体系をNuGetパッケージとして網羅しています。 中国語のみで始まったパイプラインにフランス語、ドイツ語、アラビア語、日本語を追加することはdotnet add package IronOcr.Languages.Frenchであり、設定項目は1行です。モデルファイルの調達は不要で、上流での利用可能性の調査も不要で、手動のファイルデプロイも必要ありません。

前処理はOCR APIの一部です。 PaddleSharpによる前処理が要求していたOpenCVの知識 — フィルタカーネルの理解、Matの廃棄管理、適応的しきい値パラメータの選択 — はOCR作業の前提条件ではなくなりました。 OcrInputは、適切なデフォルト値を持つ命名された操作を提供します。 OpenCVの専門家ではないものの、OpenCVの前処理コードを保守していたチームは、そのコードを置き換えることなく削除することができます。 前処理機能ページには、利用可能なすべてのフィルターと、それぞれを適用すべきタイミングに関するドキュメントが記載されています。

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

よくある質問

なぜPaddleSharp OCRからIronOCRに移行する必要があるのですか?

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

PaddleSharp OCRからIronOCRに移行する際の主なコード変更は何ですか?

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

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

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

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

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

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

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

PaddleSharp OCRからIronOCRに移行する場合、導入インフラの変更は必要ですか?

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

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

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

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

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

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

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

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

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

IronOCRの価格はPaddleSharp OCRよりもワークロードのスケーリングが予測できますか?

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

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

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

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

アイアンサポートチーム

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