0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PDF前処理サービス3選(AWS/Azure/Mistral)を比較してみた

Last updated at Posted at 2025-10-02

はじめに

本記事では、AWS、Azure、Mistralが提供するPDFドキュメントのOCRサービスについて、機能を比較した結果をまとめます。

業務で RAG(Retrieval-Augmented Generation: 検索拡張生成)システムを構築する際、精度向上のためにPDFドキュメントの前処理が必要となりました。その中でPDFのレイアウトを認識し、要素を抽出するサービスをいくつか試した経験から得た情報を本記事で共有します。

検証の概要

実現したい前処理フロー

当初、図表を含むPDFを丸ごとLLMに読み込ませていましたが、特に表のMarkdown変換精度に課題がありました。そこで「図表を一度画像として切り出し、個別にLLMで処理する方が精度が向上する」という情報を基に、以下の前処理フローを設計しました。

  1. 要素の抽出: PDFドキュメントからテキスト、図、表を抽出する
  2. 変換: 抽出した図表をMarkdownや説明テキストに変換する
  3. 結合: 抽出・変換した各要素を、元のPDFドキュメントのレイアウト通りに結合する

上記フローのうち、要素の抽出にOCRサービスを活用します。

OCRサービスに求める要件

以下の要件を設定しました。

必須要件

  • PDFをバイナリ形式で送信し、分析結果を取得できる
  • PDF内の日本語テキストを抽出できる
  • PDF内の表を画像として抽出できる(または座標を取得できる)
  • PDF内の図を画像として抽出できる(または座標を取得できる)

任意要件

  • 抽出したテキストをMarkdownに変換できる
  • 抽出した表をMarkdownのテーブル形式に変換できる
  • 抽出した図を説明テキスト(図の概要など)に変換できる
  • 抽出した図表の元ドキュメントにおける位置情報を取得できる

比較対象サービス

  • Amazon Textract
  • Azure Document Intelligence
  • Mistral OCR

検証用PDF

デジタル庁が公開している資料「R6年度 生成AIの業務利用に関する技術検証、利用環境整備報告書」の16ページ目を使用しました。このページにはテキスト、図、表がすべて含まれており、今回の検証に最適だったためです。

検証用ソースコード

各サービスでPDFの分析を実行し、結果をJSONファイルとして保存するPythonスクリプトです。

実行環境:

  • OS: Windows11
  • Python: 3.12

実行する場合、各サービスの認証情報(APIキー、エンドポイントなど)を環境変数として設定してください。

ocr_services_comparison.py
import tkinter as tk
from tkinter import filedialog
import os
import boto3
from typing import Dict, List
from azure.core.credentials import AzureKeyCredential
from azure.ai.documentintelligence import DocumentIntelligenceClient
from azure.ai.documentintelligence.models import AnalyzeOutputOption
from mistralai import Mistral
import json

def select_pdf_file():
    """
    ファイル選択ダイアログを開き、ユーザーが選択したPDFファイルのパスを取得
    """
    # Tkinterのルートウィンドウを作成(表示はしない)
    root = tk.Tk()
    # メインウィンドウを表示しない
    root.withdraw()

    file_path = filedialog.askopenfilename(
        title="処理するPDFファイルを選択してください",
        filetypes=[("PDF files", "*.pdf"), ("All files", "*.*")],
        initialdir=os.path.expanduser("~") 
    )
    root.destroy()
    return file_path



def ocr_azure_di(pdf_path: str) -> Dict[str, List]:
    client = DocumentIntelligenceClient(
            api_version="2024-11-30",
            endpoint=os.environ.get("AZURE_DI_ENDPOINT"),
            credential=AzureKeyCredential(os.environ.get("AZURE_DI_API_KEY"))
        )
    with open(pdf_path, "rb") as f:
        poller = client.begin_analyze_document(
            "prebuilt-layout",
            body=f,
            output=[AnalyzeOutputOption.FIGURES],
            output_content_format="markdown",
            locale="ja"
        )
    result = poller.result()
    return result.as_dict()


def ocr_textract(pdf_path: str) -> Dict[str, List]:
    client = boto3.client(
        'textract',
        aws_access_key_id=os.environ.get("AWS_ACCESS_KEY"),
        aws_secret_access_key=os.environ.get("AWS_SECRET"),
        region_name="ap-northeast-2"
    )
    with open(pdf_path, 'rb') as document_file:
        document_bytes = document_file.read()
        response = client.analyze_document(
                Document={'Bytes': document_bytes},
                FeatureTypes=['LAYOUT']
            )
    return response

def ocr_mistral(pdf_path: str) -> Dict[str, List]:
    client = Mistral(api_key=os.environ.get("MISTRAL_API_KEY"))
    
    with open(pdf_path, "rb") as f:
        uploaded_pdf = client.files.upload(
            file={
                "file_name": os.path.basename(pdf_path),
                "content": f.read(),
            },
            purpose="ocr"
        )

    signed_url = client.files.get_signed_url(file_id=uploaded_pdf.id)

    response = client.ocr.process(
        model="mistral-ocr-latest",
        document={
            "type": "document_url",
            "document_url": signed_url.url,
        }
    )
    return response.model_dump()


def save_json_result(data, filename):
    with open(filename, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
 
   
if __name__ == "__main__":

    pdf_path = select_pdf_file()
    
    textract_response=ocr_textract(pdf_path)
    azure_di_response=ocr_azure_di(pdf_path)
    mistralocr_response=ocr_mistral(pdf_path)
    
    save_json_result(textract_response, "ocr_textract.json")
    save_json_result(azure_di_response, "ocr_azure_di.json")
    save_json_result(mistralocr_response, "ocr_mistral.json")

比較結果

比較表

要件 Amazon Textract Azure Document Intelligence Mistral OCR
【必須】PDFバイナリ送信
【必須】表の画像抽出 △¹ △¹ ×
【必須】図の画像抽出 △¹ 〇² 〇²
【必須】日本語テキスト抽出 ×
【任意】Markdown付与 ×
【任意】表のMarkdown変換 ×
【任意】図の説明テキスト変換 × × ×
【任意】要素の位置情報取得 × △³

注釈:
¹ 座標のみ取得可能。トリミング処理の自前実装が必要。
² APIで図のバイナリデータを直接取得可能。
³ 図のみ対応。Markdownテキスト内に [image] のようなプレースホルダーが挿入される。

各サービスの制約と評価

Amazon Textract

  • 2025年7月時点で、日本語のテキスト抽出に非対応です。
  • 2025年7月時点で、日本リージョンに非対応です。
  • 同期処理には1ページ以内かつ10MBまでの制限があります。これを超える場合は非同期処理が必須となります。非同期処理ではファイルアップロードはS3経由のみ、処理完了を通知するSNSトピックの連携が必須と、その他AWSリソースの構築が求められるのでやや扱いにくいかもしれません。

日本語のテキスト抽出に非対応な点から今回のケースには合致しませんでした。英語ドキュメントを前処理する場合や、すでにAWSで構築しているシステムと連携するケースには向いているかもしれません。

Azure Document Intelligence

  • 無料プラン、従量課金プランが選択でき、無料プランでは2ページ以内かつ4MBまでの制限があります。従量課金プランでは大幅に緩和され、2000ページ以内かつ500MBまでとなります。

今回の要件に最も合致したサービスです。特に、各要素にそれぞれ付与される位置情報(offset/length)が優秀です。これにより、抽出した要素を元のドキュメントの構成通りに再構築する実装が容易に行えます。
総じて高機能なので、様々なケースで活用できるサービスだと思います。

Mistral OCR

  • 1000ページ以内かつ50MBまでの制限があります。
  • 表は自動でMarkdownに変換されますが、その位置情報は保存されません。そのため、仮にMarkdownの変換精度が良くなかった場合、表を画像として切り出して再度LLMに渡すことができません。
    表の画像抽出に非対応な点から今回のケースには合致しませんでした。Markdown変換精度に関してもAzure Document Intelligenceと比較すると劣っている印象です(複雑な構成の表はMarkdownのレイアウトが崩れることも多かったため)。

結論

各社とも一見似たサービスに思えますが、詳しく調べたり実際に使ったりすると、それぞれかなり違いがありました。そのうえで、今回の要件においてはAzure Document Intelligenceが最も最適なサービスであるという結論に至りました。その他の様々なケースでも、総じて高機能なAzure Document Intelligenceが優良な選択肢となる可能性は高いと思います。
この情報が、データエンジニアリングでPDFの前処理に課題を持つ方の参考になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?