Logicool G ワイヤレス ゲーミングマウス G703h LIGHTSPEED HERO 25Kセンサー エルゴノミクス LIGHTSYNC RGB POWERPLAY 無線 充電 対応 ゲーミング マウス 充電式 無線 PC windows mac ブラック G703 国内正規品 【 ファイナルファンタジー XIV 推奨モデル 】
¥9,900 (2025年4月29日 13:11 GMT +09:00 時点 - 詳細はこちら価格および発送可能時期は表示された日付/時刻の時点のものであり、変更される場合があります。本商品の購入においては、購入の時点で当該の Amazon サイトに表示されている価格および発送可能時期の情報が適用されます。)【Amazon.co.jp限定】 バッファロー WiFi ルーター 無線 LAN Wi-Fi5 11ac ac1200 866+300Mbps IPv6 WPA3 デュアルバンド 日本メーカー 【 iPhone 16e / 16 / 15 / 14 / Nintendo Switch / PS5 動作確認済み 】 エコパッケージ WCR-1166DHPL/N
¥3,380 (2025年4月29日 13:11 GMT +09:00 時点 - 詳細はこちら価格および発送可能時期は表示された日付/時刻の時点のものであり、変更される場合があります。本商品の購入においては、購入の時点で当該の Amazon サイトに表示されている価格および発送可能時期の情報が適用されます。)ソニー ゲーミングイヤホン INZONE Buds:WF-G700N Fnatic監修 / 完全ワイヤレス / 低遅延2.4GHzワイヤレス接続 USBType-Cトランシーバー同梱 / LE Audio対応 / アクティブノイズキャンセリング / 立体音響 / 最大約24時間バッテリー / 急速充電 / マイク付き / PS5 スマホ PC Switch ホワイト
¥27,000 (2025年4月29日 13:12 GMT +09:00 時点 - 詳細はこちら価格および発送可能時期は表示された日付/時刻の時点のものであり、変更される場合があります。本商品の購入においては、購入の時点で当該の Amazon サイトに表示されている価格および発送可能時期の情報が適用されます。)
この記事について
RAGの精度向上には、元データの品質改善が欠かせません。本記事では、画像PDFのようにそのままでは検索できないドキュメントに対して、検索可能なPDFへ変換する検証を行った内容を紹介します。
問題意識
画像PDFは、単純にアップロードするだけではKendraやBedrock Knowledge Baseなどの検索エンジンに同期できません。
そのため、これまではAmazon Bedrockなどの基盤モデルを利用してOCRをかけ、テキストファイルに変換したうえで登録する、というプロセスを踏んでいました。
ただこの方法だと、以下のような課題があると感じるようになりました。
- ①元々あったPDFのページ数が失われてしまう
- ②構造的なOCRがなかなか難しい
- ③どこをどう読み取ったのか、という詳細の推論過程はブラックボックスになっている
そこで今回は、次の2点を意識して検証を進めることにしました。
①「画像PDF」→「検索可能なPDF」に蘇らせる(テキスト情報を埋め込む)
② より高精度なOCRエンジンを使用する
検証スタート
前提条件
RAGの仕組みの構築は、Amazon BedrockのKnowledge Base(以下KBと記載)を使用し、
ベクトルDBには、Pineconeを採用しました。
※ 検証を通して気づいたのですが、PineconeのServerless indexを用いれば、かなり費用を抑えられると思いました。(私はAWS Marketplace 経由で利用していたので Standardプラン)
検証を行う中で、まあまあ使っていましたが、0.003ドルくらいにしか費用がかさまなかったです。従量課金なのがメリットですね。
検証で確かめること
- ページ数が正しく保持されること
- OCR→PDFの過程で、正しくページ数を保持できているのか
- 検索を行った際に、「何ページ目から抽出できたのか」という情報が正しく抽出できているかで確認
- OCR→PDFの過程で、正しくページ数を保持できているのか
- OCR精度はいかほどか(期待する情報がある程度取れてくるか?)
検証に使用するドキュメント
今回は、OCRを実現するアプローチとして2つの策を考え、検証してみました。
検証に共通して、適当な画像をスキャンし、PDFにしたものをS3に配置し、データソースとして指定します。(test-input.pdf
と名付けます)
当然、このPDFは、検索ができる状況ではありません。
試しに、Amazon BedrockのKnowledge Baseを作成し、S3に配置したtest-input.pdf
と同期してみると、以下のように同期に失敗する旨のエラーが表示されています。
検証において使用したpythonスクリプト
今回の検証で使用した全体のpythonスクリプトを以下に添付します。
GitHubにも公開したので、自己責任の上ご利用ください。
各手順の部分で、かいつまんで説明します。
なお、処理の全体像の図を記事最下部(処理イメージ)の項に記載しましたので、気になる方は参考になれば幸いです。
検証で使用したスクリプト
import os
import sys
from pathlib import Path
from typing import List
import fitz
from google.cloud import documentai_v1 as documentai
from google.cloud.documentai_toolbox import document
from ocrmypdf import exceptions, hocrtransform, ocr
# サービスアカウントキーのパスを環境変数に設定
# NOTE 事前にスクリプトを実行するファイルと同じ階層に配置
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "XXXXXXX.json"
# プロジェクトID・ロケーション・プロセッサIDを設定
project_id = "your-project-id"
location = "us" #またはeu(プロセッサを作成したリージョン)
processor_id = "your-processor-id" #(作成したプロセッサID)
def process_pdf_by_ocrmypdf(input_pdf: str, output_pdf: str) -> None:
"""
OCRmyPDFを使ってPDFに透明テキストレイヤーを付与する
Args:
input_pdf (str): 入力PDFファイルパス
output_pdf (str): 出力PDFファイルパス
"""
try:
ocr(
input_file=input_pdf,
output_file=output_pdf,
language="jpn",
deskew=True,
clean=True,
progress_bar=True,
)
print(f"[INFO] OCRが正常に完了: {output_pdf}")
except exceptions.ExitStatusException as e:
print(f"[ERROR] OCRmyPDFエラー: {e}")
def split_pdf_page_by_page(input_path: str) -> List[str]:
"""
PDFファイルを1ページずつ分割し、個別ファイルとして保存する
Args:
input_path (str): 入力PDFファイルパス
Returns:
List[str]: 分割後の各ページPDFファイルパスリスト
"""
pdf = fitz.open(input_path)
chunk_files = []
base_name = os.path.splitext(input_path)[0]
for i in range(len(pdf)):
chunk_path = f"{base_name}_page{i + 1}.pdf"
chunk_pdf = fitz.open()
chunk_pdf.insert_pdf(pdf, from_page=i, to_page=i)
chunk_pdf.save(chunk_path)
chunk_pdf.close()
chunk_files.append(chunk_path)
pdf.close()
return chunk_files
def convert_hocr_to_pdf(
hocr_path: str, background_pdf_path: str, output_pdf_path: str, dpi: int = 300
) -> None:
"""
hOCRファイルから透明テキストレイヤーを作成し、背景PDFと合成する
Args:
hocr_path (str): hOCRファイルパス
background_pdf_path (str): 背景PDFファイルパス
output_pdf_path (str): 出力PDFファイルパス
dpi (int, optional): 解像度(デフォルト300)
"""
ocr_only_pdf_path = str(Path(output_pdf_path).with_suffix(".ocr_only.pdf"))
transformer = hocrtransform.HocrTransform(hocr_filename=Path(hocr_path), dpi=dpi)
transformer.to_pdf(out_filename=Path(ocr_only_pdf_path))
print(f"[INFO] 透明テキストPDF生成完了: {ocr_only_pdf_path}")
merge_background_and_ocr(background_pdf_path, ocr_only_pdf_path, output_pdf_path)
print(f"[INFO] 背景と透明テキストを合成完了: {output_pdf_path}")
Path(ocr_only_pdf_path).unlink(missing_ok=True)
def merge_background_and_ocr(
background_pdf_path: str, ocr_text_pdf_path: str, output_pdf_path: str
) -> None:
"""
背景PDFと透明テキストレイヤーPDFを合成する
Args:
background_pdf_path (str): 背景PDFパス
ocr_text_pdf_path (str): 透明テキストPDFパス
output_pdf_path (str): 出力PDFパス
"""
bg_doc = fitz.open(background_pdf_path)
ocr_doc = fitz.open(ocr_text_pdf_path)
for page_num in range(len(bg_doc)):
bg_page = bg_doc[page_num]
bg_page.show_pdf_page(bg_page.rect, ocr_doc, page_num)
bg_doc.save(output_pdf_path, garbage=4, deflate=True)
bg_doc.close()
ocr_doc.close()
def process_document_with_docai(file_path: str) -> documentai.Document:
"""
Document AIでPDFをOCR処理する
Args:
file_path (str): 入力PDFパス
Returns:
documentai.Document: OCR処理結果
"""
client = documentai.DocumentProcessorServiceClient()
name = f"projects/{project_id}/locations/{location}/processors/{processor_id}"
with open(file_path, "rb") as f:
content = f.read()
request = documentai.ProcessRequest(
name=name,
raw_document=documentai.RawDocument(
content=content, mime_type="application/pdf"
),
)
result = client.process_document(request=request)
return result.document
def save_docai_response_to_json(
document_obj: documentai.Document, output_path: str
) -> None:
"""
Document AIレスポンスをJSONファイルとして保存する
Args:
document_obj (Document): Documentオブジェクト
output_path (str): 出力JSONファイルパス
"""
json_obj = documentai.Document.to_json(document_obj)
with open(output_path, "w", encoding="utf-8") as f:
f.write(json_obj)
def convert_docai_response_to_hocr(
docai_document: documentai.Document, title: str, document_path: str
) -> str:
"""
Document AI JSONからhOCR形式に変換する
Args:
docai_document (Document): OCRドキュメント
title (str): hOCRファイルタイトル
document_path (str): Document AI JSONパス
Returns:
str: hOCRテキスト
"""
wrapped_doc = document.Document.from_document_path(document_path=document_path)
return wrapped_doc.export_hocr_str(title=title)
def merge_pdfs_with_pymupdf(pdf_files: List[str], output_path: str) -> None:
"""
複数PDFファイルを結合する
Args:
pdf_files (List[str]): PDFファイルリスト
output_path (str): 出力ファイルパス
"""
merger = fitz.open()
for pdf_file in pdf_files:
merger.insert_pdf(fitz.open(pdf_file))
merger.save(output_path)
merger.close()
def make_searchable_pdf(input_path: str, output_path: str, use_docai: bool = True) -> None:
"""
PDFをOCR処理して検索可能なPDFに変換する
Args:
input_path (str): 入力PDFパス
output_path (str): 出力PDFパス
use_docai (bool, optional): Document AIを使用するか(デフォルトTrue)
"""
print(f"[INFO] 分割処理中: {input_path}")
chunk_files = split_pdf_page_by_page(input_path)
processed_chunks = []
temp_files = []
for i, chunk_file in enumerate(chunk_files):
print(f"[INFO] ページ {i + 1}/{len(chunk_files)} 処理中...")
base = os.path.splitext(chunk_file)[0]
hocr_path = f"{base}.hocr.xml"
json_path = f"{base}.json"
output_chunk = f"{base}_processed.pdf"
try:
if use_docai:
docai_document = process_document_with_docai(chunk_file)
save_docai_response_to_json(docai_document, json_path)
hocr_content = convert_docai_response_to_hocr(
docai_document, f"Chunk {i + 1}", json_path
)
with open(hocr_path, "w", encoding="utf-8") as f:
f.write(hocr_content)
convert_hocr_to_pdf(hocr_path, chunk_file, output_chunk)
else:
process_pdf_by_ocrmypdf(chunk_file, output_chunk)
processed_chunks.append(output_chunk)
temp_files += [chunk_file]
if use_docai:
temp_files += [hocr_path, json_path]
except Exception as e:
print(f"[ERROR] ページ {i + 1} 処理失敗: {e}")
print("[INFO] 各ページを結合中...")
merge_pdfs_with_pymupdf(processed_chunks, output_path)
for path in temp_files + processed_chunks:
try:
os.remove(path)
except Exception as e:
print(f"[WARN] 一時ファイル削除失敗: {path} -> {e}")
print(f"[DONE] 完了: {output_path}")
# 実行部分
if __name__ == "__main__":
if len(sys.argv) 3:
print("使用方法: python script.py 入力PDF 出力PDF [use_docai]")
sys.exit(1)
input_pdf = sys.argv[1]
output_pdf = sys.argv[2]
use_docai = sys.argv[3].lower() == "true" if len(sys.argv) > 3 else True
if use_docai:
if not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS"):
print("[WARN] 認証情報が設定されていません。")
make_searchable_pdf(input_pdf, output_pdf, use_docai)
① pythonライブラリ「OCRmyPDF」を使ってOCR
OCRmyPDFとは
OCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched or copy-pasted.(GitHubリポジトリ説明から引用)
OCRmyPDFは、
スキャンされたPDFファイルにテキストを埋め込むことができる役割を担っています。
オープンソースのOCRエンジンである、「Tesseract OCR」が内部的に使われています。
Tesseract OCRは、日本語を含めた多言語にも対応しています。
手順1 ocrmypdfライブラリをインストール
uvを使って検証を行いました。
検証時点でのバージョンはuv 0.6.12
です。
$ uv version
uv 0.6.12 (e4e03833f 2025-04-02)
uv init
で任意のプロジェクトを作成したのちに、下記を実行します。これでプログラムからocrmypdfライブラリを呼び出す準備が整いました。
手順2 プログラムから呼び出す
OCR処理はocrmypdf.ocrが担っているので、これを使います。input_file
にはOCRをかけたいファイルのパスを、output_file
にはOCR結果の出力先のパスを指定します。
--language
オプションをつけることで、もとドキュメントの言語を指定することができます。(多くの場合はjpn
)を指定することになるかと思います。
from ocrmypdf import ocr
def process_pdf_by_ocrmypdf(input_pdf: str, output_pdf: str) -> None:
"""
OCRmyPDFを使ってPDFに透明テキストレイヤーを付与する
Args:
input_pdf (str): 入力PDFファイルのパス
output_pdf (str): 出力PDFファイルのパス
Returns:
None
"""
try:
ocr(
input_file=input_pdf,
output_file=output_pdf,
language="jpn",
deskew=True, # 傾きを補正するオプション
clean=True, # ノイズ除去をするオプション
progress_bar=True, # 処理状況をターミナルで確認可能にする
)
print(f"[INFO] OCRが正常に完了: {output_pdf}")
except ocrmypdf.exceptions.ExitStatusException as e:
print(f"[ERROR] OCR実行中にエラー: {e}")
progress_bar=True
をつけると、OCR処理の進捗状況をターミナル上で確認することができます。
出力結果は以下のようになります。
プログレスバー出力イメージ
$ uv run convert_to_searchable_pdf_v2.py test-input.pdf test-output.pdf false
[INFO] 分割処理中: test-input.pdf
[INFO] チャンク 1/3 処理中...
Scanning contents ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
[tesseract] lots of diacritics - possibly poor OCR
OCR ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
PDF/A conversion ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
Linearizing ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 100/100 0:00:00
Recompressing JPEGs ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0% 0/0 -:--:--
Deflating JPEGs ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
JBIG2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0% 0/0 -:--:--
[INFO] OCRが正常に完了: test-input_page1_processed.pdf
[INFO] チャンク 2/3 処理中...
Scanning contents ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
OCR ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
PDF/A conversion ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
Linearizing ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 100/100 0:00:00
Recompressing JPEGs ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0% 0/0 -:--:--
Deflating JPEGs ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
JBIG2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0% 0/0 -:--:--
[INFO] OCRが正常に完了: test-input_page2_processed.pdf
[INFO] チャンク 3/3 処理中...
Scanning contents ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
OCR ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
PDF/A conversion ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
Linearizing ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 100/100 0:00:00
Recompressing JPEGs ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0% 0/0 -:--:--
Deflating JPEGs ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 1/1 0:00:00
JBIG2 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0% 0/0 -:--:--
[INFO] OCRが正常に完了: test-input_page3_processed.pdf
[INFO] チャンク結合中...
[DONE] 完了: test-output.pdf
② Google CloudのOCRサービス「Document AI」を使ってOCRを行い、JSONファイルに保存
Document AIとは
Google CloudのOCRサービスです。
料金体系は以下のようになっています。
Enterprise Document OCR プロセッサを使う場合では、1000ページごとに1.5$ということで、少量のデータであれば比較的安価にOCRをすることができます。
料金体系は以下のページに記載がありますので、詳細はこちらをご覧ください。
上記のようにGoogle Cloud コンソール上から簡単にテストすることができますが、
今回はGoogle Cloud SDK経由から呼び出すことを考えます。
利用に際しては
サービスアカウントの認証情報作成、プロセッサの作成など事前準備が必要となります。
こちらのチュートリアル(1-5)が参考になります。
手順1.Document AI ToolBoxを用いて、画像PDFをhocr形式に変換する
Document AIには、Toolboxと呼ばれる、OCR結果を2次利用しやすい形式に変換するためのクライアントライブラリが用意されています。
def convert_docai_response_to_hocr(
docai_document: documentai.Document, title: str, document_path: str
) -> str:
"""
JSON(Document AIのレスポンスが入っている)をhOCR形式に変換する
Args:
docai_document (Document): OCRドキュメント
title (str): hOCRファイルタイトル
document_path (str): Document AI JSONパス
Returns:
str: hOCRテキスト
"""
wrapped_doc = document.Document.from_document_path(document_path=document_path)
return wrapped_doc.export_hocr_str(title=title)
その中に、Document AIによるOCR結果のレスポンス(JSON形式)をhocr形式に変換するツールがあったため、今回はこれを使用します。
変換された結果が以下です。(掲載しているのは一部です)bbox
という記述が多くありますが、これはOCRによって検出された物体(画像やテキストなど)の領域のことを指すようです。
つまり「どこに、何が書いてあるかを詳細にまとめたXML形式のファイル」と言えそうです。
xmlns="http://www.w3.org/1999/xhtml" xml:lang="unknown" lang="unknown">
Chunk 2
http-equiv="Content-Type" content="text/html;charset=utf-8" />
name="ocr-system" content="Document AI OCR" />
name="ocr-langs" content="unknown" />
name="ocr-scripts" content="unknown" />
name="ocr-number-of-pages" content="1" />
name="ocr-capabilities" content="ocrp_lang ocr_page ocr_carea ocr_par ocr_line ocrx_word" />
class='ocr_page' lang='unknown' title='bbox 0 0 1626 2459'> class='ocr_carea' id='block_1_0' title='bbox 400 54 797 61'> class='ocr_par' id='par_1_0_0' title='bbox 400 54 797 61'> class='ocr_line' id='line_1_0_0_0' title='bbox 400 54 797 61'>かわさきの上下水道 No.59 令和7年3月
class='ocrx_word' id='word_1_0_0_0_0' title='bbox 400 54 475 71'>かわさき class='ocrx_word' id='word_1_0_0_0_1' title='bbox 479 52 498 69'>の class='ocrx_word' id='word_1_0_0_0_2' title='bbox 498 51 580 68'>上下水道 class='ocrx_word' id='word_1_0_0_0_3' title='bbox 600 48 654 65'>No.59 class='ocrx_word' id='word_1_0_0_0_4' title='bbox 676 46 715 64'>令和 class='ocrx_word' id='word_1_0_0_0_5' title='bbox 720 45 732 63'>7 class='ocrx_word' id='word_1_0_0_0_6' title='bbox 738 44 757 61'>年 class='ocrx_word' id='word_1_0_0_0_7' title='bbox 763 44 774 62'>3 class='ocrx_word' id='word_1_0_0_0_8' title='bbox 781 43 798 61'>月
....中略
手順2.hocr形式から、PDFに変換する
OCRmyPDFのライブラリで、hocr形式からPDFに変換を行うことのできるhocrtransform
メソッドがあるため、これを利用します。
def convert_hocr_to_pdf(hocr_path, background_pdf_path, output_pdf_path, dpi=300):
"""hOCRファイルから透明テキストレイヤーを作成するサンプルコード"""
ocr_only_pdf_path = str(Path(output_pdf_path).with_suffix(".ocr_only.pdf"))
transformer = hocrtransform.HocrTransform(hocr_filename=Path(hocr_path), dpi=dpi)
transformer.to_pdf(out_filename=Path(ocr_only_pdf_path))
print(f"[INFO] 透明テキストPDF生成完了: {ocr_only_pdf_path}")
変換結果は以下のようになりした。
コード内にも記載していますが、あくまでもここでは検出されたテキストがPDF内に埋め込まれるだけです。なので文字列検索には引っかかりますが、こちら側からは確認ができません。
この問題を手順3で解決します。
手順3.透明テキストが含まれるPDFと元PDFを重ね合わせる
手順2で生じた問題を解決するため、元の画像PDFに、テキストを重ね合わせる処理を追加します。
def convert_hocr_to_pdf(hocr_path, background_pdf_path, output_pdf_path, dpi=300):
+ """hOCRファイルから透明テキストレイヤーを作成し、背景PDFと合成する"""
ocr_only_pdf_path = str(Path(output_pdf_path).with_suffix(".ocr_only.pdf"))
transformer = hocrtransform.HocrTransform(hocr_filename=Path(hocr_path), dpi=dpi)
transformer.to_pdf(out_filename=Path(ocr_only_pdf_path))
print(f"[INFO] 透明テキストPDF生成完了: {ocr_only_pdf_path}")
+ merge_background_and_ocr(background_pdf_path, ocr_only_pdf_path, output_pdf_path)
+ print(f"[INFO] 背景と透明テキストを合成完了: {output_pdf_path}")
新たに、merge_background_and_ocr
を作成します。
def merge_background_and_ocr(background_pdf_path, ocr_text_pdf_path, output_pdf_path):
"""背景PDFと透明テキストレイヤーPDFを合成"""
bg_doc = fitz.open(background_pdf_path)
ocr_doc = fitz.open(ocr_text_pdf_path)
for page_num in range(len(bg_doc)):
bg_page = bg_doc[page_num]
bg_page.show_pdf_page(bg_page.rect, ocr_doc, page_num)
bg_doc.save(output_pdf_path, garbage=4, deflate=True)
bg_doc.close()
ocr_doc.close()
今回はpymupdf
ライブラリのshow_pdf_page
メソッドを使用しました。
このメソッドはあるPDFの特定のページの内容を、別のPDFに描画できるメソッドで、
処理内では、画像PDFの上に、透明テキストが含まれるOCR済みのPDFを重ね合わせています。
# 補足
# bg.rect→配置場所(「どこに重ね合わせるか」)
# ocr_doc→OCR済みの、テキスト情報だけが存在するPDF(「何を重ね合わせるか」)
# page_num→重ね合わせる対象のページ数
bg_page.show_pdf_page(bg_page.rect, ocr_doc, page_num)
# saveメソッドについては以下を参照
# https://pymupdf.readthedocs.io/ja/latest/document.html#Document.save
## garbage=4にすることで不要なコンテンツを削除してくれる。
## deflate=Trueにすることでファイルを圧縮してくれる。
RAGの結果を見てみる
簡単な質問をしてみる
KBを使って、RAGをしてみます。
① OCRmypdfでOCRを行った場合
元々のPDFの2ページ目にあった、「工事をしないとどうなるの?」の項に関する質問をしてみます。
710kmと返してくれるのが期待値ですが、元ドキュメントの通り、710kmと返却してくれました。OCRの読み取り精度は悪くなさそうです。
また、x-amz-bedrock-kb-document-page-number
で、抽出元のページも相違なく取れてきています。
② Document AIでOCRを行った場合
こちらについても、同様問題なく結果が取れてきそうでした。①とはそこまで差異がないですが、ソースチャンクを見る限り、こちらの方が文字をより正確に読み取ってくれているような気がしました。
まとめ
ここまで読んでいただき、ありがとうございました。
今回は、画像PDFに対してOCRの処理を行い、検索可能なPDFに変換して、RAGのデータソースとして機能するかの検証を行いました。
比較的読み取りのしやすいドキュメントでテストを行ったのもありますが、問題なくデータの前処理を行うことができました。
冒頭の問題意識の項で記載した、ページ数の抽出の課題は、
ページごとにOCRをかける→PDFに書き戻す、というアプローチでクリアできそうな気がしました。
また、Document AIを使えば、どこをどう読み取っているのかの情報が、レスポンスからある程度把握できるので、こちらも便利な気がしました。
今後は、
-
表組みが多いPDF(帳票、レポート)
-
手書き文字が混在するPDF
-
フォントが特殊な文書
など、より難易度の高いケースでも本手法が使えるか検証していきたいと思います。
おまけ
処理イメージ
処理の全体像をmermaid記法で書いてみました。
Views: 0