C#でドキュメントのテーブルを読む方法

This article was translated from English: Does it need improvement?
Translated
View the article in English

IronOCRはC#の開発者が高度な機械学習モデルを使用してPDFや画像の表からデータを抽出することを可能にし、ReadDocumentAdvancedメソッドを使用して基本的なセルを持つ単純な表とセルがマージされた請求書のような複雑な構造の両方を扱うことができます。

Tesseractを使用したテーブルからのデータ抽出は、テキストがセルに存在し、ドキュメントにまばらに散らばっていることが多いため、困難な場合があります。 しかし、私たちのライブラリには、テーブルデータを正確に検出して抽出するために訓練され、微調整された機械学習モデルが含まれています。 財務報告書、在庫リスト、請求書データなど、IronOCRは構造化データを効率的に解析するツールを提供します。

単純なテーブルについては、標準的なOcrInputクラスを使用した簡単なテーブル検出に頼ってください。 より複雑な構造の場合、当社独自のReadDocumentAdvancedメソッドは、テーブルを効果的に解析してデータを配信し、堅牢な結果を提供します。 この高度な方法は、機械学習を活用し、従来のOCRが苦手とするテーブルレイアウト、結合セル、複雑な書式設定を理解します。

クイックスタート: 1 回の呼び出しで複雑な表のセルを抽出する

この例では、ReadDocumentAdvancedを使ってIronOCRを一回呼び出すだけで、複雑なドキュメントから詳細なテーブル・セル・データが得られることを示しています。 PDF を読み込み、高度なテーブル検出を適用し、セル情報のリストを直接返すことで、使いやすさを実証します。

Nuget Icon今すぐ NuGet で PDF を作成してみましょう:

  1. NuGet パッケージ マネージャーを使用して IronOCR をインストールします

    PM > Install-Package IronOcr

  2. このコード スニペットをコピーして実行します。

    var cells = new IronTesseract().ReadDocumentAdvanced(new OcrInput().LoadPdf("invoiceTable.pdf")).Tables.First().CellInfos;
  3. 実際の環境でテストするためにデプロイする

    今すぐ無料トライアルでプロジェクトに IronOCR を使い始めましょう
    arrow pointer

次の手順は、IronOCR を使用して表の読み取りを開始するためのガイドです。


単純なテーブルからデータを抽出するには?

ReadDataTablesプロパティを true に設定すると、Tesseract を使用したテーブル検出が有効になります。 このアプローチは、セルの境界が明確で、セルが結合されていない基本的な表に対して効果的です。 この機能をテストするために、簡単な表の PDF を作成しました。ここからダウンロードできます: ' simple-table.pdf '。 この方法を使用すると、結合されたセルのない単純な表を検出できます。 より複雑な表については、以下に説明する方法を参照してください。

標準的なテーブル検出方法は、特に次のような場合に効果的です:

  • スプレッドシートのエクスポート
  • 一貫した行/列構造を持つ基本的なデータテーブル
  • 表形式データのレポート
  • 簡単な在庫リスト

PDF OCRテキスト抽出を一般的に扱う場合、この方法はIronOCRの幅広い文書処理機能とシームレスに統合されます。

:path=/static-assets/ocr/content-code-examples/how-to/read-table-in-document-with-tesseract.cs
using IronOcr;
using System;
using System.Data;

// Instantiate OCR engine
var ocr = new IronTesseract();

// Enable table detection
ocr.Configuration.ReadDataTables = true;

using var input = new OcrPdfInput("simple-table.pdf");
var result = ocr.Read(input);

// Retrieve the data
var table = result.Tables[0].DataTable;

// Print out the table data
foreach (DataRow row in table.Rows)
{
    foreach (var item in row.ItemArray)
    {
        Console.Write(item + "\t");
    }
    Console.WriteLine();
}
$vbLabelText   $csharpLabel

複雑な請求書テーブルを読むにはどうすればよいですか?

ビジネス環境でよく見られる複雑な表の 1 つが請求書です。 請求書は、データの行と列を持つ複雑な表であり、多くの場合、セルの結合、さまざまな列幅、入れ子構造などが特徴です。 IronOCRでは、ReadDocumentAdvancedメソッドを利用して、これらを効果的に処理します。 このプロセスには、ドキュメントのスキャン、テーブル構造の識別、およびデータの抽出が含まれます。 この例では、'invoiceTable.pdf'ファイルを使用して、IronOCRが請求書からすべての情報を取得する方法を紹介します。

ReadDocumentAdvancedメソッドには、IronOcr.Extensions.AdvancedScanパッケージがIronOCRパッケージと一緒にインストールされている必要があります。 このエクステンションは、複雑なドキュメントレイアウト用に特別にトレーニングされた高度な機械学習機能を提供します。

ブラケットオープン .NET Framework で詳細スキャンを使用するには、プロジェクトを x64 アーキテクチャで実行する必要があります。 これを実現するには、プロジェクト構成に移動し、"32 ビットを優先"オプションのチェックを外します。 詳しくは、以下のトラブルシューティングガイドをご覧ください:"Advanced Scan on .NET Framework".
ブラケットを閉じる

:path=/static-assets/ocr/content-code-examples/how-to/read-table-in-document-with-ml.cs
using IronOcr;
using System.Linq;

// Instantiate OCR engine
var ocr = new IronTesseract();

using var input = new OcrInput();
input.LoadPdf("invoiceTable.pdf");

// Perform OCR
var result = ocr.ReadDocumentAdvanced(input);

var cellList = result.Tables.First().CellInfos;
$vbLabelText   $csharpLabel

この方法は、ドキュメントのテキスト データを、境界線で囲まれたカテゴリと境界線のないカテゴリの 2 つのカテゴリに分割します。 境界線で囲まれたコンテンツの場合、ライブラリはテーブルの構造に基づいてさらにサブセクションに分割します。 このメソッドが得意とする処理

  • さまざまな説明を含む請求書項目
  • 複数列の価格内訳
  • 配送先および請求先住所ブロック
  • 税金と合計の計算セクション
  • ヘッダーとフッター情報

結果は以下の通りです。 この方法では、境界線で囲まれた情報に重点を置いているため、複数の行にまたがるマージされたセルは1つのセルとして扱われます。

抽出されたデータはどのように見えますか?

IronSoftwareのOCRで、出荷時の送り状からテーブルデータを構造化された階層形式に抽出する

抽出したテーブルセルをどのように整理し、処理すればよいですか?

現在の実装では、抽出されたセルはまだ適切に整理されていません。 ただし、各セルには、X 座標、Y 座標、寸法などの貴重な情報が含まれています。 このデータを使用して、さまざまな目的のヘルパー クラスを作成できます。 セル情報には以下が含まれます:

  • 位置決めのための正確なX/Y座標
  • 幅と高さの寸法
  • テキスト内容
  • 信頼度スコア
  • セルの関係

この詳細な情報により、プログラムによってテーブル構造を再構築し、データ抽出のためのカスタムロジックを適用することができます。 また、これらの座標を使用して、特定の領域を定義し、その後の操作でターゲットを絞った OCR 処理を行うこともできます。

以下にいくつかの基本的なヘルパー メソッドを示します。

using System;
using System.Collections.Generic;
using System.Linq;

// A helper class to process table data by sorting cells based on coordinates
public static class TableProcessor
{
    // Method to organize cells by their coordinates (Y top to bottom, X left to right)
    public static List<CellInfo> OrganizeCellsByCoordinates(List<CellInfo> cells)
    {
        // Sort cells by Y (top to bottom), then by X (left to right)
        var sortedCells = cells
            .OrderBy(cell => cell.CellRect.Y)
            .ThenBy(cell => cell.CellRect.X)
            .ToList();

        return sortedCells;
    }

    // Example method demonstrating how to process multiple tables
    public static void ProcessTables(Tables tables)
    {
        foreach (var table in tables)
        {
            var sortedCells = OrganizeCellsByCoordinates(table.CellInfos);

            Console.WriteLine("Organized Table Cells:");

            // Initialize previous Y coordinate
            int previousY = sortedCells.Any() ? sortedCells.First().CellRect.Y : 0;

            foreach (var cell in sortedCells)
            {
                // Print a new line if the Y-coordinate changes, indicating a new row
                if (Math.Abs(cell.CellRect.Y - previousY) > cell.CellRect.Height * 0.8)
                {
                    Console.WriteLine();  // Start a new row
                    previousY = cell.CellRect.Y;
                }

                // Print the cell text followed by a tab
                Console.Write($"{cell.CellText}\t");
            }

            Console.WriteLine("\n--- End of Table ---");  // End of a table
        }
    }

    // Method to extract a specific row by the given index
    public static List<CellInfo> ExtractRowByIndex(TableInfo table, int rowIndex)
    {
        if (table == null || table.CellInfos == null || !table.CellInfos.Any())
        {
            throw new ArgumentException("Table is empty or invalid.");
        }

        var sortedCells = OrganizeCellsByCoordinates(table.CellInfos);
        List<List<CellInfo>> rows = new List<List<CellInfo>>();

        // Group cells into rows based on Y coordinates
        int previousY = sortedCells.First().CellRect.Y;
        List<CellInfo> currentRow = new List<CellInfo>();

        foreach (var cell in sortedCells)
        {
            if (Math.Abs(cell.CellRect.Y - previousY) > cell.CellRect.Height * 0.8)
            {
                // Store the completed row and start a new one
                rows.Add(new List<CellInfo>(currentRow));
                currentRow.Clear();

                previousY = cell.CellRect.Y;
            }

            currentRow.Add(cell);
        }

        // Add the last row if it wasn't added yet
        if (currentRow.Any())
        {
            rows.Add(currentRow);
        }

        // Retrieve the specified row
        if (rowIndex < 0 || rowIndex >= rows.Count)
        {
            throw new IndexOutOfRangeException($"Row index {rowIndex} is out of range.");
        }

        return rows[rowIndex];
    }
}
using System;
using System.Collections.Generic;
using System.Linq;

// A helper class to process table data by sorting cells based on coordinates
public static class TableProcessor
{
    // Method to organize cells by their coordinates (Y top to bottom, X left to right)
    public static List<CellInfo> OrganizeCellsByCoordinates(List<CellInfo> cells)
    {
        // Sort cells by Y (top to bottom), then by X (left to right)
        var sortedCells = cells
            .OrderBy(cell => cell.CellRect.Y)
            .ThenBy(cell => cell.CellRect.X)
            .ToList();

        return sortedCells;
    }

    // Example method demonstrating how to process multiple tables
    public static void ProcessTables(Tables tables)
    {
        foreach (var table in tables)
        {
            var sortedCells = OrganizeCellsByCoordinates(table.CellInfos);

            Console.WriteLine("Organized Table Cells:");

            // Initialize previous Y coordinate
            int previousY = sortedCells.Any() ? sortedCells.First().CellRect.Y : 0;

            foreach (var cell in sortedCells)
            {
                // Print a new line if the Y-coordinate changes, indicating a new row
                if (Math.Abs(cell.CellRect.Y - previousY) > cell.CellRect.Height * 0.8)
                {
                    Console.WriteLine();  // Start a new row
                    previousY = cell.CellRect.Y;
                }

                // Print the cell text followed by a tab
                Console.Write($"{cell.CellText}\t");
            }

            Console.WriteLine("\n--- End of Table ---");  // End of a table
        }
    }

    // Method to extract a specific row by the given index
    public static List<CellInfo> ExtractRowByIndex(TableInfo table, int rowIndex)
    {
        if (table == null || table.CellInfos == null || !table.CellInfos.Any())
        {
            throw new ArgumentException("Table is empty or invalid.");
        }

        var sortedCells = OrganizeCellsByCoordinates(table.CellInfos);
        List<List<CellInfo>> rows = new List<List<CellInfo>>();

        // Group cells into rows based on Y coordinates
        int previousY = sortedCells.First().CellRect.Y;
        List<CellInfo> currentRow = new List<CellInfo>();

        foreach (var cell in sortedCells)
        {
            if (Math.Abs(cell.CellRect.Y - previousY) > cell.CellRect.Height * 0.8)
            {
                // Store the completed row and start a new one
                rows.Add(new List<CellInfo>(currentRow));
                currentRow.Clear();

                previousY = cell.CellRect.Y;
            }

            currentRow.Add(cell);
        }

        // Add the last row if it wasn't added yet
        if (currentRow.Any())
        {
            rows.Add(currentRow);
        }

        // Retrieve the specified row
        if (rowIndex < 0 || rowIndex >= rows.Count)
        {
            throw new IndexOutOfRangeException($"Row index {rowIndex} is out of range.");
        }

        return rows[rowIndex];
    }
}
$vbLabelText   $csharpLabel

テーブル抽出のベストプラクティス

IronOCRでテーブル抽出を行う際には、以下のベストプラクティスを考慮してください:

1.ドキュメントの品質:解像度の高いドキュメントほど、良い結果が得られます。 スキャン文書の場合、最低300DPIを確保してください。

2.前処理:品質の悪い文書や傾いた表については、処理の前にIronOCRの画像補正機能の使用を検討してください。

3.パフォーマンス:複数のテーブルを持つ大きなドキュメントの場合、マルチスレッドと非同期サポートを使用してページを並列処理することを検討してください。

4.出力オプション:テーブルデータを抽出した後、さまざまな形式で結果をエクスポートできます。 データ出力オプションの詳細と、処理したドキュメントから検索可能なPDFを作成する方法について説明します。

5.ストリーム処理:ウェブ アプリケーションやインメモリ ドキュメントを扱うシナリオでは、ファイル システム操作を避けるために OCR for PDF streams の使用を検討してください。

まとめ

IronOCRは標準的なTesseractベースの検出と高度な機械学習による強力なテーブル抽出機能を提供します。 標準的なアプローチは単純なテーブルには効果的ですが、ReadDocumentAdvancedメソッドは請求書のような複雑なドキュメントを得意としています。 提供されているヘルパーメソッドを使用すると、特定のニーズに合わせて抽出されたデータを整理して処理できます。

IronOCRの機能をもっと調べて、文書処理ワークフローを強化し、.NETアプリケーションで光学式文字認識の可能性を最大限に活用してください。

よくある質問

C#でPDFや画像から表データを抽出するには?

C#の開発者は高度な機械学習モデルを使ってPDFや画像から表データを抽出することができます。単純な表については、ReadDataTablesプロパティをtrueに設定したOcrInputクラスを使用します。セルがマージされた複雑な表については、ReadDocumentAdvancedメソッドを使用するとより正確な結果が得られます。

単純なテーブル抽出と複雑なテーブル抽出の違いは何ですか?

IronOCRの単純な表抽出はTesseractのReadDataTablesプロパティを使用し、セル境界が明確な基本的な表であればうまくいきます。複雑な表抽出にはReadDocumentAdvancedメソッドが必要であり、これは機械学習を使用して結合セル、請求書、複雑な書式設定を処理する。

複雑なテーブルから素早くデータを抽出するには?

IronOCRのReadDocumentAdvancedメソッドを1回の呼び出しで使用する: var cells = new IronTesseract().ReadDocumentAdvanced(new OcrInput().LoadPdf('invoiceTable.pdf')).Tables.First().CellInfos; これはテーブルレイアウトと複雑なフォーマットを理解するために機械学習を活用する。

シンプルなテーブル検出が最も効果的なドキュメントのタイプは?

IronOCRのシンプルな表検出方法は、スプレッドシートのエクスポート、一貫した行/列構造を持つ基本的なデータテーブル、表形式のデータを含むレポート、セルがマージされていないシンプルな在庫リストに特に効果的です。

基本テーブルのテーブル検出を有効にするにはどうすればよいですか?

IronOCRで基本テーブルのテーブル検出を有効にするには、ReadDataTablesプロパティをtrueに設定します。これはTesseractのテーブル検出機能を使用し、セル境界が明確でセルが結合されていないテーブルに有効です。

ライブラリは、複雑なレイアウトの請求書や財務報告書を扱うことができますか?

IronOCRのReadDocumentAdvancedメソッドは、請求書や財務報告書のような複雑な文書を扱うために特別に設計されています。これは、セルが結合され、複雑な書式が設定された表を検出し、そこからデータを抽出するために訓練された機械学習モデルを使用します。

カーティス・チャウ
テクニカルライター

Curtis Chauは、カールトン大学でコンピュータサイエンスの学士号を取得し、Node.js、TypeScript、JavaScript、およびReactに精通したフロントエンド開発を専門としています。直感的で美しいユーザーインターフェースを作成することに情熱を持ち、Curtisは現代のフレームワークを用いた開発や、構造の良い視覚的に魅力的なマニュアルの作成を楽しんでいます。

開発以外にも、CurtisはIoT(Internet of Things)への強い関心を持ち、ハードウェアとソフトウェアの統合方法を模索しています。余暇には、ゲームをしたりDiscordボットを作成したりして、技術に対する愛情と創造性を組み合わせています。

レビュー済み
Jeff Fritz
Jeffrey T. Fritz
プリンシパルプログラムマネージャー - .NETコミュニティチーム
Jeffはまた、.NETとVisual Studioチームのプリンシパルプログラムマネージャーです。彼は.NET Conf仮想会議シリーズのエグゼクティブプロデューサーであり、週に二回放送される開発者向けライブストリーム『Fritz and Friends』のホストを務め、テクノロジーについて話すことや視聴者と一緒にコードを書くことをしています。Jeffはワークショップ、プレゼンテーション、およびMicrosoft Build、Microsoft Ignite、.NET Conf、Microsoft MVPサミットを含む最大のMicrosoft開発者イベントのコンテンツを企画しています。
準備はできましたか?
Nuget ダウンロード 5,299,091 | バージョン: 2025.12 リリース