【Python】PyMuPDFでPDFから画像を賢く抽出!基本からOCR連携まで徹底解説
はじめに
PythonでPDFを操作する際、多くのライブラリが存在しますが、PyMuPDF (fitz) はその中でも特に高速かつ多機能で、強力な選択肢の一つです。テキスト抽出だけでなく、画像の取り扱いに関しても非常に優れた機能を持っています。
[cite_start]今回は、具体的なサンプルとしてこちらのラッコの画像が含まれるPDF を使って、実践的な画像抽出方法を解説していきます。
この記事で学べること
- PDFに表示されている「見たまま」の画像を保存する方法
- PDFに埋め込まれている「元の画像データ」を非破壊で抽出する方法
- 画像領域にテキストが含まれているかをOCRで効率的に判定する方法
準備:テスト用PDF
この記事のコードを試すには、まずサンプルとなるPDFファイルが必要です。
今回のサンプルコードでは、以下のURLで公開されているPDFファイルを使用します。コード内で自動的にダウンロードする処理を加えています。
【重要】著作権に関するご注意
※上記のPDFファイルは、本コードの動作確認を目的としてサンプルとして使用しています。著作権はファイルの発行元に帰属しますので、取り扱いには十分ご注意いただき、本来の目的以外での利用は避けてください。
[cite_start]このPDFの1ページ目には、大きなラッコの画像が含まれています 。この画像をターゲットに、様々な抽出方法を試していきましょう。
PyMuPDFにおける2つの画像抽出アプローチ
PyMuPDFで画像を扱う上で、まず理解すべき最も重要な概念は、**「レンダリング画像」と「元の画像データ」**の違いです。
-
レンダリング画像 (
page.get_pixmap
): これは、ページ上の表示をそのままピクセルデータにしたものです。いわば「スクリーンショット」のようなもので、回転やクリッピング(切り抜き)が適用された後の、ユーザーが見ている通りの見た目を画像として取得します。 -
元の画像データ (
doc.extract_image
): これは、PDFファイル内部に保存されているオリジナルの画像バイトデータを直接抽出する方法です。JPEGやPNGなどの元ファイルそのものを取り出すため、解像度の劣化などがありません。
どちらを使うべきかは、あなたの目的次第です。
- 「PDFのラッコの部分を、見えているまま画像にしたい」→
get_pixmap
- 「PDFに含まれるラッコの画像を、元のファイルの形で抜き出したい」→
extract_image
では、それぞれの具体的な方法を見ていきましょう。
方法1: 見たままの画像(レンダリング画像)を抽出する
page.get_pixmap()
を使うと、ページの特定領域を簡単に画像化できます。今回は、pdf_test.pdf
の1ページ目にあるラッコの画像部分を狙って切り出してみましょう。
サンプルコード1
先にPDFの保存
import fitz # PyMuPDF
import requests
import os
# --- テスト用PDFの準備 ---
url = "https://nihon-ped-surg.jp/wp-content/uploads/2021/11/pdf_test.pdf"
pdf_filename = "pdf_test.pdf"
print(f"テスト用PDFをダウンロードします: {url}")
response = requests.get(url)
with open(pdf_filename, "wb") as f:
f.write(response.content)
print(f"'{pdf_filename}' をダウンロードしました。")
以下のコードは、pdf_test.pdf
の1ページ目全体を保存し、さらにラッコの画像が含まれる領域だけをクリッピングして保存します。
import fitz # PyMuPDFをインポート
# PDFファイルを開く
try:
doc = fitz.open("pdf_test.pdf")
page = doc[0] # 最初のページを取得
# --- ページ全体を画像として保存 ---
pix_full = page.get_pixmap()
pix_full.save("output_full_page.png")
print("✅ ページ全体の画像を 'output_full_page.png' として保存しました。")
# --- 特定の画像領域だけを画像として保存 ---
# 画像のおおよその位置をBBox(x0, y0, x1, y1)で指定
image_rect = fitz.Rect(50, 50, 550, 400)
pix_clipped = page.get_pixmap(clip=image_rect)
pix_clipped.save("output_sea_otter.png")
print(f"✅ 画像を 'output_sea_otter.png' として保存しました。")
doc.close()
except FileNotFoundError:
print("エラー: 'pdf_test.pdf' が見つかりません。")
except Exception as e:
print(f"予期せぬエラーが発生しました: {e}")
実行結果の output_sea_otter.png を開くと、おおざっぱに指定した範囲での画像だけが切り出されているのが確認できるはずです。
方法2: 埋め込まれた元の画像データを抽出する
次に、PDFに埋め込まれているラッコの画像を、元の画質を保ったままファイルとして抽出します。
page.get_images()
: ページ内の画像リストと、それぞれの画像を識別するための相互参照番号 (xref) を取得します。
doc.extract_image()
: xref
を使って、実際の画像データ(バイト列)と拡張子を取得します。
サンプルコード2
このコードは、pdf_test.pdf
の1ページ目にあるラッコの画像を検出し、元のファイル形式で保存します。
import fitz
import os
# 保存用ディレクトリを作成
output_dir = "extracted_images"
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# PDFファイルを開く
try:
doc = fitz.open("pdf_test.pdf")
page = doc[0] # 最初のページ
# ページ内の画像リストを取得
image_list = page.get_images(full=True)
if not image_list:
print("このページに画像は見つかりませんでした。")
else:
print(f"このページに {len(image_list)} 個の画像が見つかりました。")
# 各画像を抽出して保存
for img_index, img in enumerate(image_list):
# 画像のxrefを取得
xref = img[0]
# xrefを使って元の画像データを抽出
base_image = doc.extract_image(xref)
image_bytes = base_image["image"]
image_ext = base_image["ext"]
# ファイルに保存
image_filename = f"image_{page.number+1}_{xref}.{image_ext}"
image_path = os.path.join(output_dir, image_filename)
with open(image_path, "wb") as img_file:
img_file.write(image_bytes)
print(f"✅ 画像を '{image_path}' として保存しました。")
doc.close()
except FileNotFoundError:
print("エラー: 'pdf_test.pdf' が見つかりません。")
except Exception as e:
print(f"予期せぬエラーが発生しました: {e}")
まとめ
今回は、PyMuPDFを使ってPDFから画像を抽出するための3つの強力な方法を、具体的なサンプルPDFと共に解説しました。
目的 | 使用するメソッド | ポイント |
---|---|---|
見たままの画像が欲しい | page.get_pixmap(clip=...) | 表示されている領域をそのまま画像化する。 |
元の画像ファイルが欲しい | page.get_images() + doc.extract_image() | PDFに埋め込まれた画像を、元の画質のまま抽出する。 |
これらの手法を使い分けることで、あなたのPDF処理はより柔軟でパワフルなものになります。PyMuPDFはここで紹介した以外にも多くの機能を秘めているので、ぜひ公式ドキュメントなども参考に、さらなる活用法を探求してみてください。
Happy coding! 🚀