📰
PDFのページを画像化するシェルスクリプト
概要
LVMを使っていると「PDFを分析させたいけど画像しか対応していない」、「そもそもPDFだけど元がパワポで構造が複雑」みたいなことがあると思います。(私はありました)
資料の数もページ数も多いとさすがにスクリプト書かないと厳しかったので、
- PDFのページをすべて画像化
- フォルダ内のすべてのPDFに対して実行
の2つのスクリプトを書きました。
環境
- WSL2 (Ubuntu 22.04.4)
準備
ImageMagick(画像変換全般のライブラリ・納品物に組み込むなどする場合ライセンスには注意してください)、poppler-utils(PDFのページ数カウントに使用)が必要になるのでインストールします。
sudo apt install imagemagick poppler-utils
Ubuntu環境がない人のためにWindowsでも使えるようにしようかと思ったのですが、ImageMagick内で使うGhostScriptも別でインストールする必要があるなど手間が多くて不便だったのでやめました。
Note
- 一括変換しようとするとメモリオーバー
-
convert -density 300 $input $output
で一気に変換しようとするとメモリオーバー - メモリ16GB環境で30ページくらいの資料でもメモリが足りなくなりました(WSLのメモリ使用量を増やすのは焼け石に水)
- 時間はかかりますがページ毎に変換する方針にしました
-
- 変換画質
- 資料によっては画質を落とせばいいのでdensity, qualityを下げればいいと思います
- 今回は資料の性質から png かつdpi 200くらいが許容範囲だったのでコードの内容になっています
- プログレスバー
- 待っている間の進捗状況可視化
コード
convert_pdf_to_images.sh <input file>
- 対象のファイルを画像化します。
- カレントディレクトリに
"images/<拡張子なしファイル名>"
のディレクトリを作成し、"0埋め3桁のページ番号.png"
で画像ファイルを出力します。- 親階層ディレクトリ"../"の文字列は消去するようにしていますが、inputのディレクトリ構造を保持するため絶対パスは使わない方がいいです。
convert_pdf_to_images.sh
start_time=$(date +%s)
show_progress() {
local current=$1
local total=$2
bar_length=50
# 進捗率に基づいたバーの長さを計算
local filled_length=$((current * bar_length / total))
# バーの表示部分を生成
local bar=$(printf '%-*s' "$filled_length" '' | tr ' ' '=')
local empty=$(printf '%*s' $((bar_length - filled_length)) '' | tr ' ' ' ')
# プログレスバーの更新
printf "\r[%s%s] %d%%" "$bar" "$empty" $((current * 100 / total))
}
input=$1
output_dir="images/${input%.*}"
output_dir="${output_dir//..\//}" # remove ../
# Check if the input file has a .pdf extension
if [[ ! $input =~ \.pdf$ ]]; then
echo "$1 is not pdf file. skipped converting."
elif [[ -d $output_dir ]]; then
echo "$output_dir already exists. Skipping converting."
else
echo "output directory: $output_dir"
mkdir -p $output_dir
# ボツ: ページ数が多いファイルを一気に変換しようとするとメモリが足りなくなるため、ページごとに変換する
# output="$output_dir/${input%.*}.png"
# convert -density 300 $input $output
pages=$(pdfinfo $input | grep Pages | awk '{print $2}')
printf "convert pages: $pages\n"
for i in $(seq 1 $pages); do
show_progress $i $pages
page_num=$(printf "%03d" $i)
convert -density 200 -quality 100 $input[$((i-1))] $output_dir/${page_num}.png
done
printf "\n finish converting \n"
fi
end_time=$(date +%s)
exec_time=$((end_time - start_time))
printf "time: $exec_time seconds \n\n"
convert_all_pdf_to_images.sh <target dir>
- 対象のディレクトリすべてを画像化します。
- 並列化は諦めてしまったので別ターミナルを開いて複数プロセスで実行していました
- そのため既に画像化されているファイルはスキップするようにしています
convert_all_pdf_to_images.sh
#!/bin/bash
# convert_pdf_to_png.shスクリプトの実行コマンドを設定
CONVERT_SCRIPT=". convert_pdf_to_images.sh"
# Get the list of PDF files in the current folder
if [ -z "$1" ]; then
echo "No directory specified."
elif [ ! -d "$1" ]; then
echo "Directory does not exist."
else
# ディレクトリ内のPDFファイルのリストを取得
PDF_FILES=$(find "$1" -type f -name "*.pdf")
# PDFファイルのリストを表示
echo "PDF files to convert:"
echo "$PDF_FILES"
# PDFファイルの数をカウント
PDF_COUNT=$(echo "$PDF_FILES" | wc -l)
# 変換を実行する前にユーザーに確認する
read -p "上記の ${PDF_COUNT} ファイル を変換しますがよろしいですか? (y/n): " answer
# レスポンスチェック
if [[ $answer == "n" || $answer == "N" ]]; then
echo "変換をキャンセルしました。"
else
echo "Converting PDF files..."
# convert_pdf_to_png.shスクリプトを使用してリストに含まれるPDFファイルを変換
for PDF_FILE in $PDF_FILES; do
$CONVERT_SCRIPT "$PDF_FILE"
done
fi
fi
Discussion