はじめに
pythonでpdfを読み取りしようとしたときに、AzureDocumentIntelligenceを用いればかなりの精度が出ます。
ただ図や表をうまく読み取れなかったり、読み取り結果が長い文字列になり情報が埋もれてしまうことが起こりえます。(上手く後処理すれば解決できそうですが、当方のその知見がありませんでした。)
そこでpdfを一度画像として保存し、LLMに説明させることで図や表の情報を読み取ることを検討してみます。
インストール
この記事では、pdfの画像化にPyMuPDFとPillow、画像の読み取りにAzure OpenAIを用います。
pip install pymupdf
pip install Pillow
pip install openai
コード
pdfの画像化
所定のフォルダに複数のpdfが保存されている状況を想定します。
import fitz # PyMuPDF
import os
from PIL import Image
def convert_pdfs_to_images(pdf_folder, output_folder):
if not os.path.exists(output_folder):
os.makedirs(output_folder)
for pdf_file in os.listdir(pdf_folder):
if pdf_file.endswith(".pdf"):
pdf_path = os.path.join(pdf_folder, pdf_file)
pdf_document = fitz.open(pdf_path)
for page_num in range(len(pdf_document)):
page = pdf_document.load_page(page_num)
pix = page.get_pixmap()
# Convert to PIL Image
img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
# Resize the image to half its size
img_resized = img.resize((pix.width // 2, pix.height // 2))
# Save the resized image
output_path = f"{image_folder}/{os.path.splitext(pdf_file)}_page_{page_num + 1}.png"
img_resized.save(output_path)
# フォルダ指定
# PDFファイルが保存されているフォルダ
pdf_folder = "PATH/TO/PDF"
# 画像を保存するフォルダ
image_folder = "PATH/TO/IMAGE_FOLDER"
# PDFを画像に変換
convert_pdfs_to_images(pdf_folder, output_folder)
画像をLLMに説明させる
import os
import base64
from openai import AzureOpenAI
# 上で画像を保存したフォルダのパス
image_folder_path = "PATH/TO/IMAGE_FOLDER"
# 画像の変換設定
def encode_image(image_path):
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode("utf-8")
endpoint = os.getenv("ENDPOINT_URL", "your_endpoint")
deployment = os.getenv("DEPLOYMENT_NAME", "your_model_name")
subscription_key = os.getenv("AZURE_OPENAI_API_KEY", "your_azureopenai_api_key")
# キーベースの認証を使用して Azure OpenAI Service クライアントを初期化する
client = AzureOpenAI(
azure_endpoint=endpoint,
api_key=subscription_key,
api_version="your_model_version",
)
for filename in os.listdir(image_folder_path):
base64_image = encode_image(os.path.join(image_folder_path, filename))
image_url = "data:image/png;base64," + base64_image
#チャット プロンプトを準備する
chat_prompt = [
{
"role": "system",
"content": "以下の画像について数値などを用いて具体的に説明してください。",
},
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": image_url
}
}
]
}
]
messages = chat_prompt
# 入力候補を生成する
explanation = client.chat.completions.create(
model=deployment,
messages=messages,
max_tokens=1000,
temperature=0.7,
top_p=0.95,
frequency_penalty=0,
presence_penalty=0,
stop=None,
stream=False
).choices[0].message.content
# 結果を保存
explanation_path = "PATH/TO/EXPLANATION"
with open(f"{explanation_path}/{os.path.splitext(filename)[0]}.txt", "w", encoding="utf-8") as f:
f.write(explanation)
検証
ここではサンプルとして東京大学大気海洋研究所50年史のリサーチハイライトを読み取ってみます。
例として3ページ目の結果が↓です。
結果
この画像は、北太平洋の元素の垂直プロファイルに関するデータと、関連する図表を示しています。
-
元素の垂直プロファイル:
- 上部の表には、北太平洋における様々な元素(例:酸素、窒素、リン、鉄など)の深さに応じた濃度の変化が示されています。
- 各行は異なる深さ(メートル)を示し、それに対する元素の濃度が示されています。例えば、表の上部では表層の濃度が高く、深海に向かうにつれて減少する傾向が見られます。
-
大陸棚と大陸斜面:
- 下部の図は、大陸棚と大陸斜面の断面を示しており、地形の変化や水深(メートル単位)の情報が視覚的に表現されています。
- 色分けされた領域があり、各色が異なる水深や地質の特性を示しています。たとえば、浅い水域は明るい色で示され、深海は暗い色で示されることが一般的です。
-
データの出典:
- 画像の下部にある引用文献は、データの信頼性を裏付けるものであり、研究の背景や使用された方法論に関する情報を提供しています。
このようなデータは、海洋学や環境科学の研究において非常に重要であり、海洋の生態系、栄養循環、そして気候変動に対する理解を深めるための基盤となります。
おわりに
上手くサマリーして読み取ってくれています。
一方で図の細かい部分が抜け落ちています。
プロンプトを調整すればもっと使いやすくなるかもしれません。