PythonでPDFの画像を抽出する(PyMuPDF)

PythonでPDFの画像を抽出する(PymuPDF)
スポンサーリンク
スポンサーリンク

業務効率化・自動化の事例として、PythonでPDFを読み込み画像を抽出する方法を解説していきます。

画像のマスク情報も取得して再構成する方法を解説しますので、背景が黒くなったりせず、完全な形で取得することができます。

スポンサーリンク

使用ライブラリ

PythonでPDFファイルを操作するライブラリはPyMuPDF,PyPDF2,PDFminerなどがあります。
それぞれのライブラリで得意とする操作が異なるため使い分けていきます。
ライブラリ使い分けをまとめると以下のようになります。

PDFライブラリ比較

・関連記事:PythonでPDFを読み込む(PyMuPDF, PyPDF2, PDFminer)

今回はPyMuPDFでPDFの画像を取得する方法を解説します。

ライブラリ :PyMuPDF(公式ドキュメント
インストール:必要

pipコマンドでPyMuPDFライブラリをインストールします。
・関連記事:Pythonライブラリのインストール(pipの使い方)

python -m pip install pymupdf

Successfully installed ・・・・と表示されればインストールは成功です。(依存関係のある他のライブラリも同時にインストールされます。)

PyMuPDFの基本的な使い方については以下の記事を参考にしてください。
・関連記事:PyMuPDFの基本的な使い方

PDFファイルから画像を抽出してpng/jpegで保存する

PDFファイルから画像を抽出する方法を解説していきます。

ここでは統計局の『高等学校における「情報II」のためのデータサイエンス・データ解析入門』の第1章テキストを利用させていただきました。

PDFサンプルファイル

以下がPDFファイルの画像データを取得して、png, jpegファイルで保存するプログラムです。対象のPDFファイル名は sample.pdf としています。

PDFファイル内の画像が保存されます。

画像取得結果

プログラム解説

プログラムのポイントを解説します。

PRG1:ライブラリ設定

PyMuPDFライブラリを使用するには、fitzをインポートします。(歴史的な理由によりこのように呼び出します)

また、画像を保存するパスを生成するためにosモジュールを呼び出します。
・関連記事:Pythonでファイル名の一括変更(os, globモジュール)

PRG2:画像の保存先フォルダを設定

ポイントを解説します。

filename = ‘sample.pdf’

今回はsample.pdfという名称のPDFを読み込みます。違うファイル名を読み込む場合は、ここを読み込みたいファイル名に変更する必要があります。

dir_name = filename.split(‘.’)[0]

ファイル名「sample.pdf」から拡張子抜きの文字列「sample」を取得し、保存するフォルダ名に設定します。
filename.split('.') で [‘sample’, ‘pdf’] のリストを取得し[0]で1番目の要素を取り出しています。

・関連記事:文字列処理ー文字列の分割(split)

img_dir = os.path.join(os.getcwd(),dir_name)

画像の保存フォルダパスを設定します。
ここではPythonスクリプトと同じフォルダに、PDFファイル名のフォルダを作成して保存します。

os.getcwd()で現在のフォルダを取得し、os.path.join()で前に設定したdir_nameと結合します。
これで、スクリプトフォルダ\sample というディレクトリ名を取得できます。

getcwd() については以下の記事を参考にしてください。
・参考記事:Pythonでカレントディレクトリを取得・変更

if os.path.isdir(img_dir) == False:
    os.mkdir(img_dir)

os.path.isdir()で、先程設定した画像保存先フォルダの有無を調べ、存在しない場合はos.mkdir()でフォルダを作成します。

if 文の使い方については以下の記事を参照してください。
・関連記事:条件分岐(if文)

PRG3:PDFファイルを読み込む

PyMuPDFのfitz.open()メソッドでPDFファイルを読み込みます。

PRG4:画像情報を格納するリストを作成

画像情報を格納する空のリストを作成します。

PRG5:1ページずつ画像データを取得

プログラムを解説します。

for page in range(len(doc)):

for文でPDFデータを1ページずつ呼び出します。

images.append(doc[page].get_images())

get_images()メソッドでページ内の画像データを取得する事ができます。
取得したデータは、各画像ごとのタプルが格納されたリストになっています。以下の場合は2つの画像がある場合の結果になります。
[(885, 884, 323, 280, 8, ‘DeviceRGB’, ”, ‘Im0’, ‘FlateDecode’),
(887, 886, 1368, 820, 8, ‘DeviceRGB’, ”, ‘Im1’, ‘FlateDecode’)]

タプルの内容は次のようになっています。

xref画像オブジェクト番号
smaskソフトマスクイメージのオブジェクト番号
width画像の幅
height画像の高さ
bpcコンポーネントあたりのビット数(通常は8)
colorspace色空間
alt. colorspacecolorspaceの値に応じた代替色空間
name画像が参照される記号名
filter画像のデコードフィルター

ページに画像が含まれていない場合は、空のリストを返します。

images.append()で取得したリストを、imagesリストに格納しています。
imagesは以下のような2次元配列になります。
[
[(885, 884, 323, 280, 8, ‘DeviceRGB’, ”, ‘Im0’, ‘FlateDecode’),
(887, 886, 1368, 820, 8, ‘DeviceRGB’, ”, ‘Im1’, ‘FlateDecode’)],
[(4, 3, 1125, 484, 8, ‘DeviceRGB’, ”, ‘Im0’, ‘DCTDecode’)],
[(11, 10, 1047, 482, 8, ‘DeviceRGB’, ”, ‘Im0’, ‘FlateDecode’)],

[(114, 113, 609, 321, 8, ‘DeviceRGB’, ”, ‘Im0’, ‘FlateDecode’)]
]

関連記事:リスト(配列)ー リストに要素を追加する

PRG6:ページ内の画像情報を順番に処理

enumerateでリストimagesの要素数をpageNoとして取得し、要素内容imageを順番に処理していきます。

for 文の使い方については以下の記事を参照してください。
・関連記事:繰り返し処理(for文)

PRG7:ページ内の画像情報を処理する

プログラムを解説します。

if image != []:

ページ内に画像が含まれていない場合、空のリストが格納されていますので、!=[]で空以外の場合のみ処理します。

for i in range(len(image)):

要素imageはタプルのリストになっているため、要素を一つずつ処理していきます。

PRG8:画像情報の取得

1行ずつ解説します。

xref = image[i][0]

画像情報のタプルから、画像オブジェクト番号xrefを取得します。

smask = image[i][1]

画像情報のタプルから、ソフトマスクイメージのオブジェクト番号smaskを取得します。
このマスクイメージが画像を完全に復元するために必要な情報となります。

PDFの一部の画像にはマスクが付いているため、元の状態に再構築するにはマスクを補完する必要があります。
・参考情報:PyMuPDF documentation – How to Handle Image Masks

ImageMask

マスクイメージを結合しないと、以下のように背景が黒くなってしまい、完全な画像を取得する事ができない場合があります。

マスク無し
if image[i][8] == ‘FlateDecode’:
    ext = ‘png’
elif image[i][8] == ‘DCTDecode’:
    ext = ‘jpeg’

デコードフィルター情報を取得して、画像の拡張子を設定しています。
「FlateDecode」の場合はpng形式、「DCTDecode」の場合はjpeg形式になります。

PRG9:マスク情報の取得と画像の再構築

1行ずつ解説します。

pix = fitz.Pixmap(doc.extract_image(xref)[“image”])

基本画像のPixmapを作成します。

if smask > 0:
    mask = fitz.Pixmap(doc.extract_image(smask)[“image”])

PRG8で取得したマスク情報が1以上の場合、マスク画像のPixmapを作成します。

pix = fitz.Pixmap(pix, 0)

マスク画像を合成するために、基本画像のαチャンネルを0(透明)に設定します。

pix = fitz.Pixmap(pix, mask)

基本画像とマスク画像を合成して、画像を再構築します。

PRG10:画像を保存

1行ずつ解説していきます。

img_name = os.path.join(img_dir, f’image{pageNo+1}_{i}.{ext}’)

2で設定したimg_dirとPDFのページ番号、ページ内の画像インデックス番号と拡張子をos.path.join()で結合して画像のパスを設定しています。

pix.save(img_name)

設定したパスで画像を保存します。

PDFファイルから画像データを取得する方法を解説しました。

・関連記事:PythonでPDFを読み込む(PyMuPDF, PyPDF2, PDFminer)
・関連記事:PythonでPDFのテキストを抽出する