PythonでPDFファイルのサムネイル画像を作る
PythonでPDFファイルのサムネイル画像を作ってみました。
目次
動機
ローカルのNASにウェブサーバーを立ててPDFファイルをブラウザで読めるようにしているのですが、そのローカルのウェブページにPDFのサムネイルが貼ってあると便利だろうなと思い立ちまして、作ってみました。
環境
Python3
Poppler
pdf2imageモジュール
opencv-python(cv2)モジュール
Pillowモジュール
Windows10
手順
pdf2image(Poppler)でPDFの1ページ目を画像に変換する
画像をOpenCVでクロップする(余白を削除する)
クロップした画像をサムネイルサイズに縮小して出力する
コード
from pdf2image import convert_from_path
import numpy as np
import cv2
import os.path
THUMBNAIL_SIZE = 200
PDF_RESOLUTION = 300
PRE_CROP_AREA_UPPER = 400
PRE_CROP_AREA_LOWER = 400
PRE_CROP_AREA_LEFT = 200
PRE_CROP_AREA_RIGHT = 200
def read_pdf(file_path):
# PDFを画像に変換
pages = convert_from_path(file_path, dpi=PDF_RESOLUTION, last_page=1)
# 最初のページをPIL.Imageからcv2用のndarrayに変換
top_page = np.array(pages[0], dtype=np.uint8)
page_img = cv2.cvtColor(top_page, cv2.COLOR_RGB2BGR)
return page_img
def make_thumbnail(page_img):
# ヘッダーやフッターを削除するためのクロップ
precrop_img = page_img[PRE_CROP_AREA_UPPER:page_img.shape[0]-PRE_CROP_AREA_LOWER, PRE_CROP_AREA_LEFT:page_img.shape[1]-PRE_CROP_AREA_RIGHT]
# 画像の2値化
gray_img = cv2.cvtColor(precrop_img, cv2.COLOR_BGR2GRAY)
ret_, bw_img = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# 輪郭の抽出
conts, h_ = cv2.findContours(bw_img, cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_NONE)
# すべての輪郭を含むボックスの計算
x_min = precrop_img.shape[0]
y_min = precrop_img.shape[1]
x_max = 0
y_max = 0
for c in conts:
r = cv2.boundingRect(c)
if r[0] < y_min:
y_min = r[0]
if r[1] < x_min:
x_min = r[1]
if r[0]+r[2]-1 > y_max:
y_max = r[0]+r[2]-1
if r[1]+r[3]-1 > x_max:
x_max = r[1]+r[3]-1
# ボックスに従ってクロップ
crop_img = precrop_img[x_min:x_max, y_min:y_max]
# サムネイル画像の大きさの計算
max_len = max(crop_img.shape[0], crop_img.shape[1])
shrink_ratio = THUMBNAIL_SIZE / float(max_len)
width = round(crop_img.shape[1]*shrink_ratio)
height = round(crop_img.shape[0]*shrink_ratio)
# クロップした画像のリサイズ
shrink_img = cv2.resize(src=crop_img, dsize=(width,height), interpolation=cv2.INTER_AREA)
return shrink_img
def main():
fn = 'test.pdf'
# PDFファイルから最初のページを画像として取り出す
page = read_pdf(fn)
# 画像からサムネイルの生成
thumbnail = make_thumbnail(page)
# 出力
(fn_root, e_) = os.path.splitext(os.path.basename(fn))
cv2.imwrite(fn_root + '_thumbnail.jpg', thumbnail)
if __name__ == '__main__':
main()
コードの説明
こういうPDFファイルのサムネイル画像を生成してみます。
まず、pdf2image.convert_from_path()を使ってPDFファイルを画像に変換します。 pdf2image.convert_from_path()については、PDFを画像に変換する投稿を参照ください。
変換された画像は、PIL.Imageのリストになります。Pillowの画像形式ですね。
ここではサムネイル画像の生成にOpenCVを使いますので、Pillowの画像形式からcv2の画像形式に変換します。NumPyのarray()にImageを渡すと、ndarrayができます。このデータがRGB形式になっているので、cv2cvtColor()でBGR形式に変換します。(PDFファイルからの変換なので、ここでは透過は考慮していません。)
元のPDFファイルには、ページ番号が入っています。ページ番号はサムネイルには不要ですので、サムネイル作成前にヘッダーやフッター部分を削除します。
この際のクロップの量は、適当に決め打ちしました。
そして、画像をグレースケールに変換し、さらに白黒(2値)にします。画像の2値化については、2値化についての記事を参照してください。
画像の2値化をしたら、その画像を使って輪郭の抽出をします。輪郭の抽出の仕方については、輪郭の抽出についての記事を参照してください。
輪郭が各オブジェクト(文字や図)に対してそれぞれ抽出されますので、すべての輪郭を含むボックスを計算する必要があります。そこでcv2.boundingRect()ですべての輪郭の外接矩形を計算して、全ての矩形の中から最小と最大の座標を求めます。そうすると、全ての輪郭を含むボックスが得られます。cv2.boundingRect()については、外接矩形を求める記事を参照してください。
ボックスを計算できたら、その座標を使って画像をクロップします。
そして、クロップした画像を縮小します。
これなら、文字だけでリンクするよりは良さそうです。
公開日
広告
作ってみたカテゴリの投稿
- PythonでFizzBuzz問題をやってみた
- PythonでPDFファイルのサムネイル画像を作る
- Pythonでオブジェクトを選択してクロップするツールを作ってみた
- Pythonでデータロガーのログから瞬断を抽出してみる
- Pythonで写真の中の線を抽出してみた
- Pythonで動画から静止部分を抜き出してみた
- Pythonで測定データのピーク値を検出してみる
- Pythonで複数のCSVデータを1つのファイルにまとめてみた
- PythonとExcelでフォルダの使用量を調べてみた
- Sphinx(ablog)の後処理をする
- WordPressのブログを静的サイトに書き換えてみました
- シェルスクリプトでSphinxのビルドの前処理をする
- 数式を中置記法から後置記法(逆ポーランド記法)に変換してみた