2024/09/06(金)写真画像ファイルにフィルム写真風の日付を挿入するシェルスクリプト改3
前回の記事は,説明がわかりづらく,不十分だったため,もう少し解説を充実させました.
簡単な内容ですが,誰かのお役に立てれば嬉しいです.

1 できること
本バージョン“改3”では,次の機能が実装されています.- 一般的なデジタルカメラで撮影した写真ファイルに,後付けでフィルム写真風の日付を追加します.
- 実際のフィルムカメラのデート機能による日付よりも大きく,ハッキリと表示されるように設定してあります.
- これは,プロジェクター等で投影したときに判読しやすいことが必要と考えたからです*1.用途に応じて調整してください.
- 出力される文字は,オレンジ色に塗りつぶされ,指定幅でグレー(透過の薄い黒)にて縁取られた文字です.
- 写真ファイルのピクセル幅と高さを数えて,画面と日付の大きさの比率を維持して日付を挿入します.
- 機材によって写真のピクセルサイズが異なるのは当然なので,この調整機能は必須と考えました.
- 写真のピクセルサイズが小さすぎると破綻すると思います.撮って出しの大きなJPEGファイルを処理対象にするのが推奨です.
- 処理後の写真ファイルは先頭に年月日が追加されます.
- 元ファイルの名前が“_IMP0221.JPG”だった場合,“p20180813__IMP0221_TS.jpg”のようになります.
- 元ファイルの名前は恐らくどのような文字列・長さでも対応しています.ただし半角英数の短めのファイル名が推奨です.
- 万が一画像処理に失敗しても元のファイルが消失することは多分ありません.
- 処理後の写真ファイルのEXIF情報は維持されます.写真のピクセルサイズも変わりません.画質は少し劣化します(JPEGファイルをそのまま複数回処理しているため.).
- 無料(ただし無保証.(一応ですが)GPLライセンスとします)
- 好きなフォントに差し替え可能.
- 日付の位置や大きさ等を,コードを改変して自由に設定できる.
- コマンドライン(bashシェル)からお手軽に操作できる.
- 写真ファイルのピクセルサイズがバラバラでも,配置やサイズの比率を維持して大量に処理できる.
- 日付を挿入するだけなのに割と時間が掛かる(ImageMagickを何度も実行しているため).
- パイプ入力に対応していない.
- 1999年以前の日付に対応していない*4.
- 縦構図の写真を処理する際,処理前に画像を回転していると,日付の挿入位置が縦横反転してしまう*5.
挿入位置を揃えたい場合は,本シェルスクリプトの処理前に回転操作をしないようにするのが手っ取り早いですね.
2 使い方
次の2行で説明できますが,例を示して補足します.好みのやり方を見つけてください.私は例の通り,WindowsとWSL上のLinuxを使って処理するのが好みです.
- 最初に何らかの手段を使って,日付を入れたいファイルのリストをテキストファイルで作成します.
- 作成したテキストファイルのリストを,シェルスクリプトの引数に指定して実行します.
2.1 日付を入れたいファイルのリストをテキストファイルで作成する例
一般的なデジタルカメラで撮影した写真ファイルの拡張子は,大文字で“.JPG”となっています.この点に着目して,lsとgrepを使いリストファイルを作成します.
WSL(Windows Subsystem for Linux)ならば,写真ファイルが保存してあるフォルダへエクスプローラーで移動し,シフトキーを押しながら右クリックしてください.
表示されるコンテキストメニューで『Linux シェルをここに開く』を実行し,WSLを作業フォルダで開きます.
開かれたシェルで,例えば,“ls | grep JPG > list.txt”と入力してエンターキーを押します.
$ ls | grep JPG > list.txt $これは,カレントディレクトリ(作業フォルダ)内で“JPG”という文字列がファイル名に含まれたファイルを検索し,
“list.txt”というファイルへ書き込むコマンドです.
検索する文字列が“JPG”ではなく,他の文字列でも構いません.
各自のファイルの分類方法に従ってリストを作成します.
なお,この例では“list.txt”の中身は次のようなものとします.
IMGP4676.JPG IMGP4677.JPG _IMP0221.JPG
2.2 シェルスクリプトの実行例
本記事の下に記述してあるシェルスクリプトのコードをコピーし,作業フォルダ内に作成した空のテキストファイル(右クリックで新規作成できる『新しいテキスト ドキュメント.txt』)に貼り付け,上書き保存します.
保存後,拡張子を“sh”としたファイルに書き換えます.この場では“magick-ts.sh”とします*6
実行する準備ができたので,“./magick-ts.sh list.txt”を実行します.
$ ./magick-ts.sh list.txt "IMGP4676.JPG"の撮影日時の取得…… OK! '24 03 20 出力ファイル名: p20240320_IMGP4676_TS.jpg "IMGP4677.JPG"の撮影日時の取得…… OK! '24 03 20 出力ファイル名: p20240320_IMGP4677_TS.jpg "_IMP0221.JPG"の撮影日時の取得…… OK! '18 08 13 出力ファイル名: p20180813__IMP0221_TS.jpg $これで日付が挿入された写真ファイルが得られます.
3 実行環境について
3.1 オペレーティングシステム
解説はWSL上のDebian 12での作業を想定しています(もちろんネイティブなDebian 12でも動きます.).WSLでDebianをインストールしてください.
3.2 ImageMagick
画像処理を行うImageMagickはバージョンによってコマンド体系が異なります.バージョン6以前は,“convert”や“identify”というコマンドを使用します.
バージョン7以降のコマンド名は“magick”を最初に記述し,
その後にオプションのような扱いで“convert”や“identify”というコマンドを続ける仕様になっています.
それぞれのバージョンで動作するコードを用意したので,環境に合わせて選んで使ってください.
Debian 12デフォルトのImageMagickはバージョン6系なので,それをインストールします.
$ sudo su - [sudo] password for username: # apt install imagemagick ・ ・ ・ #7系はRocky Linux 8などのRHEL系OSではすでに採用されています.
ImageMagickのバージョンが“convert -version”で表示されたら6系以前,“magick -verson”で表示されれば7系以降です.
これらを確認してからシェルスクリプトを実行してください.
$ convert --version Version: ImageMagick 6.9.11-60 Q16 x86_64 2021-01-25 https://imagemagick.org Copyright: (C) 1999-2021 ImageMagick Studio LLC License: https://imagemagick.org/script/license.php Features: Cipher DPC Modules OpenMP(4.5) Delegates (built-in): bzlib djvu fftw fontconfig freetype heic jbig jng jp2 jpeg lcms lqr ltdl lzma openexr pangocairo png tiff webp wmf x xml zlib $
3.3 bc
本記事の“改3”バージョンのスクリプトでは,“bc”という任意精度計算コマンドを使用します.Debian 12では標準でインストールされていないようなのでインストールします.$ sudo su - # apt install bc ・ ・ ・ #
3.4 使用フォント
7セグ・14セグフォント 「DSEG」から,14セグメントなフォントをいただいて*7,使用させていただきます.Windows上でフォントのあるフォルダを確認し,フォントファイルを管理者権限で“/usr/local/share/fonts/”へコピーします.
これだけで使用可能になります.
sudo cp /mnt/c/Users/user/Downloads/fonts-DSEG_v046/DSEG14-Classic/*.ttf /usr/local/share/fonts/ネイティブなDebian 12でも,入手したフォントファイルを“/usr/local/share/fonts/”へ置けば使用できます.
4 ソースコード
コーディングの途中からコメント文による説明が適当になってしまいました.ご容赦願います.そのまんまな変数名を定義しているので,何をしているかはわかると思います.
4.1 ImageMagick 6以前版
#!/bin/bash # 引数の説明 # $1: 操作対象パス(ファイル)リストが記載されたファイル. # SEIREKI_BACKQUOTE="\`" # "`"はグレイヴアクセント.西暦の略記はアポストロフィ“'”. SEIREKI_BACKQUOTE=`echo -e "\U0027"` # グレイヴアクセントや文字コード指定がsed中で上手くエスケープできないので変数に分けた. while read IMAGENAME # $1の各行に記載された画像ファイルのパス. do echo "\"${IMAGENAME}\"の撮影日時の取得……" DateTimeOriginal=`identify -verbose ${IMAGENAME} | grep "DateTimeOriginal" | #表示結果例: 『 exif:DateTimeOriginal: 2024:03:20 08:00:23』 sed -e "s/^.*exif:DateTimeOriginal: //g" ` #処理結果例: 『2024:03:20 08:00:23』 FILENAME_DATETIME=`echo ${DateTimeOriginal} | sed -e "s/ /_/g" | #処理結果例: 『2024:03:20_08:00:23』 sed -e "s/^/p/g" | #処理結果例: 『p2024:03:20_08:00:23』 sed -e "s/_.*$//g" | #処理結果例: 『p2024:03:20』 sed -e "s/://g" ` #処理結果例: 『p20240320』 DateTimeOriginal=`echo ${DateTimeOriginal} | sed -e "s/ .*$//g" | #処理結果例: 『2024:03:20』 sed -e "s/20\(..\):/${SEIREKI_BACKQUOTE}\1:/g" | #処理結果例: 『'24:03:20』 sed -e "s/:/ /g" ` #処理結果例: 『'24 03 20』(これくらいスペースを空けないと体裁整わない.) echo "OK! ${DateTimeOriginal}" IMAGE_WIDTH=`identify -format "%[width]" ${IMAGENAME}` IMAGE_HEIGHT=`identify -format "%[height]" ${IMAGENAME}` DATE_POINTSIZE=`echo "${IMAGE_WIDTH} * 0.03" | bc -l | xargs printf "%.0f"` DATE_STROKEWIDTH=`echo "${IMAGE_WIDTH} * 0.00065" | bc -l | xargs printf "%.0f"` DATE_POSITION_X=`echo "${IMAGE_WIDTH} * 0.1" | bc -l | xargs printf "%.0f"` SHADOW_DATE_POSITION_X=`echo "${DATE_POSITION_X} - ${DATE_STROKEWIDTH}" | bc -l | xargs printf "%.0f"` DATE_POSITION_Y=`echo "${IMAGE_HEIGHT} * 0.075" | bc -l | xargs printf "%.0f"` SHADOW_DATE_POSITION_Y=`echo "${DATE_POSITION_Y} - ${DATE_STROKEWIDTH}" | bc -l | xargs printf "%.0f"` OUTPUT_IMAGENAME=`echo "${IMAGENAME}-tmp.jpg"` #一時ファイルの名前 OUTPUT_IMAGENAME2=`echo "${IMAGENAME}.jpg"` #一時ファイルの名前2 convert -font DSEG14-Classic-Bold -fill "#00000010" -pointsize ${DATE_POINTSIZE} -gravity southeast -annotate 0x0+${SHADOW_DATE_POSITION_X}+${SHADOW_DATE_POSITION_Y} "${DateTimeOriginal}" ${IMAGENAME} ${OUTPUT_IMAGENAME} #↑黒文字で日付の陰を先に生成する. convert -font DSEG14-Classic-Bold -fill "#fd7e0070" -stroke "#00000030" -strokewidth ${DATE_STROKEWIDTH} -pointsize ${DATE_POINTSIZE} -gravity southeast -annotate 0x0+${DATE_POSITION_X}+${DATE_POSITION_Y} "${DateTimeOriginal}" ${OUTPUT_IMAGENAME} ${OUTPUT_IMAGENAME2} rm ${OUTPUT_IMAGENAME} #一時ファイルを削除する. OUTPUT_IMAGENAME3=`echo ${OUTPUT_IMAGENAME2} | #OUTPUT_IMAGENAME2を元に出力ファイル名を決定 sed -e "s/\.JPG\.jpg/_TS.jpg/g" | # sed -e "s/^.*_TS.jpg/${FILENAME_DATETIME}_&/g"` #先頭に日付の文字列を追加. mv ${OUTPUT_IMAGENAME2} "${OUTPUT_IMAGENAME3}" echo "出力ファイル名: ${OUTPUT_IMAGENAME3}" done < $1
4.2 ImageMagick 7以降版
#!/bin/bash # 引数の説明 # $1: 操作対象パス(ファイル)リストが記載されたファイル. # SEIREKI_BACKQUOTE="\`" # "`"はグレイヴアクセント.西暦の略記はアポストロフィ“'”. SEIREKI_BACKQUOTE=`echo -e "\U0027"` # グレイヴアクセントや文字コード指定がsed中で上手くエスケープできないので変数に分けた. while read IMAGENAME # $1の各行に記載された画像ファイルのパス. do echo "\"${IMAGENAME}\"の撮影日時の取得……" DateTimeOriginal=`magick identify -verbose ${IMAGENAME} | grep "DateTimeOriginal" | #表示結果例: 『 exif:DateTimeOriginal: 2024:03:20 08:00:23』 sed -e "s/^.*exif:DateTimeOriginal: //g" ` #処理結果例: 『2024:03:20 08:00:23』 FILENAME_DATETIME=`echo ${DateTimeOriginal} | sed -e "s/ /_/g" | #処理結果例: 『2024:03:20_08:00:23』 sed -e "s/^/p/g" | #処理結果例: 『p2024:03:20_08:00:23』 sed -e "s/_.*$//g" | #処理結果例: 『p2024:03:20』 sed -e "s/://g" ` #処理結果例: 『p20240320』 DateTimeOriginal=`echo ${DateTimeOriginal} | sed -e "s/ .*$//g" | #処理結果例: 『2024:03:20』 sed -e "s/20\(..\):/${SEIREKI_BACKQUOTE}\1:/g" | #処理結果例: 『'24:03:20』 sed -e "s/:/ /g" ` #処理結果例: 『'24 03 20』(これくらいスペースを空けないと体裁整わない.) echo "OK! ${DateTimeOriginal}" IMAGE_WIDTH=`magick identify -format "%[width]" ${IMAGENAME}` IMAGE_HEIGHT=`magick identify -format "%[height]" ${IMAGENAME}` DATE_POINTSIZE=`echo "${IMAGE_WIDTH} * 0.03" | bc -l | xargs printf "%.0f"` DATE_STROKEWIDTH=`echo "${IMAGE_WIDTH} * 0.00065" | bc -l | xargs printf "%.0f"` DATE_POSITION_X=`echo "${IMAGE_WIDTH} * 0.1" | bc -l | xargs printf "%.0f"` SHADOW_DATE_POSITION_X=`echo "${DATE_POSITION_X} - ${DATE_STROKEWIDTH}" | bc -l | xargs printf "%.0f"` DATE_POSITION_Y=`echo "${IMAGE_HEIGHT} * 0.075" | bc -l | xargs printf "%.0f"` SHADOW_DATE_POSITION_Y=`echo "${DATE_POSITION_Y} - ${DATE_STROKEWIDTH}" | bc -l | xargs printf "%.0f"` OUTPUT_IMAGENAME=`echo "${IMAGENAME}-tmp.jpg"` #一時ファイルの名前 OUTPUT_IMAGENAME2=`echo "${IMAGENAME}.jpg"` #一時ファイルの名前2 magick convert -font DSEG14-Classic-Bold -fill "#00000010" -pointsize ${DATE_POINTSIZE} -gravity southeast -annotate 0x0+${SHADOW_DATE_POSITION_X}+${SHADOW_DATE_POSITION_Y} "${DateTimeOriginal}" ${IMAGENAME} ${OUTPUT_IMAGENAME} #↑黒文字で日付の陰を先に生成する. magick convert -font DSEG14-Classic-Bold -fill "#fd7e0070" -stroke "#00000030" -strokewidth ${DATE_STROKEWIDTH} -pointsize ${DATE_POINTSIZE} -gravity southeast -annotate 0x0+${DATE_POSITION_X}+${DATE_POSITION_Y} "${DateTimeOriginal}" ${OUTPUT_IMAGENAME} ${OUTPUT_IMAGENAME2} rm ${OUTPUT_IMAGENAME} #一時ファイルを削除する. OUTPUT_IMAGENAME3=`echo ${OUTPUT_IMAGENAME2} | #OUTPUT_IMAGENAME2を元に出力ファイル名を決定 sed -e "s/\.JPG\.jpg/_TS.jpg/g" | # sed -e "s/^.*_TS.jpg/${FILENAME_DATETIME}_&/g"` #先頭に日付の文字列を追加. mv ${OUTPUT_IMAGENAME2} "${OUTPUT_IMAGENAME3}" echo "出力ファイル名: ${OUTPUT_IMAGENAME3}" done < $1
5 撮影日時の挿入例
過去の写真に日付を挿入した例.どの写真も日付を判読でき,写真のピクセルサイズが異なっても,日付の大きさも対応して変更できているようです.






