2024/11/16(土)exiftool(CLI付Perlライブラリ)の存在とインストール,使い方(編集中)

ImageMagickよりも高速なEXIF情報読み書きツールの存在を知ったので,
そのインストール方法とコマンドとしての使い方,スクリプトの例を紹介しようと思います.
元の情報は『Ubuntu Weekly Recipe 第553回Ubuntuで写真のメタデータを扱う方法』というgihyo.jpの記事です.
これを参考にして,先日作った写真画像ファイルにフィルム写真風の日付を挿入するシェルスクリプト改3にさらに手を加え,
カメラ本体やレンズの情報を写真に挿入するスクリプトを作成します.

exiftoolのインストール

リンク先はUbuntu向けの記事ですが,Debian 12でも同じパッケージをインストールできるのでそうします*1
$ su -
# apt install libimage-exiftool-perl

*1 : ただし,流儀の違いでsudoは使わず,“su -”でrootにスイッチして作業しています.

使い方(コマンド単発)

写真ファイル名が“IMGP55○○.JPG”として,次のようにオプションなしで実行すると,読み出し可能なEXIF情報が整形されて表示されます.
カメラ本体やレンズのモデルまでしっかり表示されます.
ImageMagickの“ magick identify -verbose”コマンドはレンズのモデル名が表示されず,表示に時間も掛かるので,exiftoolコマンドは優秀です.
$ exiftool IMGP55○○.JPG
このオプションなしのコマンドは,シェルスクリプト等で利用する際に少し不便です.
対策として-sオプションを付けると,項目名が短縮されたものに変わるため,使いやすくなります.
$ exiftool -s IMGP55○○.JPG

スクリプトの例

ImageMagick6系用のシェルスクリプトです.
#!/bin/bash
# 写真ファイルを整理し,画像中に各種撮影情報を追記するシェルスクリプトです.
#
# 引数の説明
# $1: 操作対象パス(ファイル)リストが記載されたファイル.
#
# 使い方: 例えば,
# A. Linux端末上で処理したい場所に移動し,このスクリプトファイル(例では“magick-010.sh”です)を実行可能にして置いてください.
# B. “ls | grep JPG > list.txt”等で処理リストを作成します.
# C. “./magick-010.sh list.txt”のようにして実行してください.
#
# 実行に必要なソフト: ImageMagickとexiftoolコマンド,bcコマンド,および使用するフォント.
# 他は標準でインストールされていると思います.
#
# SEIREKI_BACKQUOTE="\`" # "`"はグレイヴアクセント.西暦の略記はアポストロフィ“'”.
SEIREKI_BACKQUOTE=`echo -e "\U0027"` # グレイヴアクセントや文字コード指定がsed中で上手くエスケープできないので変数に分けた.
while read IMAGENAME # $1の各行に記載された画像ファイルのパス.
 do
                echo "\"${IMAGENAME}\"の撮影情報の取得……"
                DateTimeOriginal=`exiftool -s -lang ja -DateTimeOriginal ${IMAGENAME} | #表示結果例: 『DateTimeOriginal                : 2024:08:17 09:50:58』
                       sed -e "s/^DateTimeOriginal.*: //g" ` #処理結果例: 『2024:08:17 09:50:58』
                FILENAME_DATETIME=`echo ${DateTimeOriginal} |
                        sed -e "s/ /-/g" | 	#処理結果例: 『2024:08:17-09:50:58』
                        sed -e "s/^/p/g" | 	#処理結果例: 『p2024:08:17-09:50:58』
                        sed -e "s/://g" ` 	#処理結果例: 『p20240817-095058』
                DateOriginal=`echo ${DateTimeOriginal} | 
                        sed -e "s/ .*$//g" |					#処理結果例: 『2024:08:17』
                        sed -e "s/20\(..\):/${SEIREKI_BACKQUOTE}\1:/g" |	#処理結果例: 『'24:08:17』
                        sed -e "s/:/     /g" `					#処理結果例: 『'24     08     17』(これくらいスペースを空けないと体裁整わない.)
                Model=`exiftool -s -lang ja -Model ${IMAGENAME} | #表示結果例: 『Model                           : PENTAX K-3 Mark III』
                       sed -e "s/^Model.*: //g" ` #処理結果例: 『PENTAX K-3 Mark III』
                LensType=`exiftool -s -lang ja -LensType ${IMAGENAME} | #表示結果例: 『LensID                          : HD PENTAX-DA 16-85mm F3.5-5.6 ED DC WR』
                       sed -e "s/^LensType.*: //g" ` #処理結果例: 『HD PENTAX-DA 16-85mm F3.5-5.6 ED DC WR』
                FocalLength=`exiftool -s -lang ja -FocalLength ${IMAGENAME} | #表示結果例: 『FocalLength                     : 16.0 mm』
                       sed -e "s/^FocalLength.*: //g" ` #処理結果例: 『16.0 mm』
                FNumber=`exiftool -s -lang ja -FNumber ${IMAGENAME} | #表示結果例: 『FNumber                         : 8.0』
                       sed -e "s/^FNumber.*: //g" ` #処理結果例: 『8.0』
                ExposureTime=`exiftool -s -lang ja -ExposureTime ${IMAGENAME} | #表示結果例: 『ExposureTime                    : 1/40』
                       sed -e "s/^ExposureTime.*: //g" ` #処理結果例: 『1/40』
                ISO=`exiftool -s -lang ja -ISO ${IMAGENAME} | #表示結果例: 『ISO                             : 6400』
                       sed -e "s/^ISO.*: //g" ` #処理結果例: 『6400』
                ExposureCompensation=`exiftool -s -lang ja -ExposureCompensation ${IMAGENAME} | #表示結果例: 『ExposureCompensation            : 0』
                       sed -e "s/^ExposureCompensation.*: //g" ` #処理結果例: 『0』
		FOV=`exiftool -s -lang ja -FOV ${IMAGENAME} | #表示結果例: 『FOV                             : 73.7 deg』
                       sed -e "s/^FOV.*: //g" ` #処理結果例: 『73.7 deg』
                FocalLength35efl=`exiftool -s -lang ja -FocalLength35efl ${IMAGENAME} | #表示結果例: 『FocalLength35efl                : 17.0 mm (35 mm equivalent: 17.0 mm)』
                       sed -e "s/^FocalLength35efl                : //g" ` #処理結果例: 『17.0 mm (35 mm equivalent: 17.0 mm)』この項目だけコロンが2つあるので,置換パターンを決め打ちで入力.
                Time=`exiftool -s -lang ja -Time ${IMAGENAME} | #表示結果例: 『Time                            : 09:50:58』
                       sed -e "s/^Time.*: //g" ` #処理結果例: 『09:50:58』
                echo "撮影情報読み込みOK! ${DateOriginal}, ${FILENAME_DATETIME}, ${Model}, ${LensType}, ${FocalLength35efl}, F${FNumber}, ${ExposureTime}, ISO${ISO}, EV ${ExposureCompensation}"

		IMAGE_WIDTH=`exiftool -s -b -ImageWidth ${IMAGENAME}`
		IMAGE_HEIGHT=`exiftool -s -b -ImageHeight ${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"`
		DATE_SHADOW_POSITION_X=`echo "${DATE_POSITION_X} - ${DATE_STROKEWIDTH}" | bc -l | xargs printf "%.0f"`
		DATE_POSITION_Y=`echo "${IMAGE_HEIGHT} * 0.08" | bc -l | xargs printf "%.0f"`
		DATE_SHADOW_POSITION_Y=`echo "${DATE_POSITION_Y} - ${DATE_STROKEWIDTH}" | bc -l | xargs printf "%.0f"`
		#時刻のみタイムスタンプ
		TIME_POINTSIZE=`echo "${IMAGE_WIDTH} * 0.02" | bc -l | xargs printf "%.0f"`
		TIME_STROKEWIDTH=`echo "${IMAGE_WIDTH} * 0.00065" | bc -l | xargs printf "%.0f"`
		TIME_POSITION_X=`echo "${IMAGE_WIDTH} * 0.02" | bc -l | xargs printf "%.0f"`
		TIME_SHADOW_POSITION_X=`echo "${DATE_POSITION_X} - ${DATE_STROKEWIDTH}" | bc -l | xargs printf "%.0f"`
		TIME_POSITION_Y=`echo "${IMAGE_HEIGHT} * 0.07" | bc -l | xargs printf "%.0f"`
		TIME_SHADOW_POSITION_Y=`echo "${DATE_POSITION_Y} - ${DATE_STROKEWIDTH}" | bc -l | xargs printf "%.0f"`
		
		#撮影情報のフォント・位置設定
		#上段
		INFO_POINTSIZE=`echo "${IMAGE_WIDTH} * 0.02" | bc -l | xargs printf "%.0f"`
		INFO_STROKEWIDTH=`echo "${IMAGE_WIDTH} * 0.00065" | bc -l | xargs printf "%.0f"`
		INFO_POSITION_X=`echo "${IMAGE_WIDTH} * 0.04" | bc -l | xargs printf "%.0f"`
		INFO_SHADOW_POSITION_X=`echo "${INFO_POSITION_X} - ${INFO_STROKEWIDTH}" | bc -l | xargs printf "%.0f"`
		INFO_POSITION_Y=`echo "${IMAGE_HEIGHT} * 0.032" | bc -l | xargs printf "%.0f"`
		INFO_SHADOW_POSITION_Y=`echo "${INFO_POSITION_Y} - ${INFO_STROKEWIDTH}" | bc -l | xargs printf "%.0f"`
		#下段
		INFO2_POSITION_X=`echo "${IMAGE_WIDTH} * 0.04" | bc -l | xargs printf "%.0f"`
		INFO2_SHADOW_POSITION_X=`echo "${INFO_POSITION_X} - ${INFO_STROKEWIDTH}" | bc -l | xargs printf "%.0f"`
		INFO2_POSITION_Y=`echo "${IMAGE_HEIGHT} * 0.004" | bc -l | xargs printf "%.0f"`
		INFO2_SHADOW_POSITION_Y=`echo "${INFO_POSITION_Y} - ${INFO_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+${DATE_SHADOW_POSITION_X}+${DATE_SHADOW_POSITION_Y} "${DateOriginal}" ${IMAGENAME} ${OUTPUT_IMAGENAME}
                #↓日付を生成する.
		convert -font DSEG14-Classic-Bold -fill "#fd7e0070" -stroke "#00000070" -strokewidth ${DATE_STROKEWIDTH} -pointsize ${DATE_POINTSIZE} \
			-gravity southeast -annotate 0x0+${DATE_POSITION_X}+${DATE_POSITION_Y} "${DateOriginal}" ${OUTPUT_IMAGENAME} ${OUTPUT_IMAGENAME}
		#↓時刻を生成する.
	        convert -font Nimbus-Sans-Narrow-Bold -fill "#fd7e0070" -stroke "#00000070" -strokewidth ${TIME_STROKEWIDTH} -pointsize ${TIME_POINTSIZE} \
			-gravity southeast -annotate 0x0+${TIME_POSITION_X}+${TIME_POSITION_Y} \
			"${Time}" ${OUTPUT_IMAGENAME} ${OUTPUT_IMAGENAME}
		#↓撮影情報を追記する.
		#上段
	        convert -font Nimbus-Sans-Narrow-Bold -fill "#fd7e0070" -stroke "#00000070" -strokewidth ${INFO_STROKEWIDTH} -pointsize ${INFO_POINTSIZE} \
			-gravity southwest -annotate 0x0+${INFO_POSITION_X}+${INFO_POSITION_Y} \
			"${Model},    ${LensType}" ${OUTPUT_IMAGENAME} ${OUTPUT_IMAGENAME}
		#下段
	        convert -font Nimbus-Sans-Narrow-Bold -fill "#fd7e0070" -stroke "#00000070" -strokewidth ${INFO_STROKEWIDTH} -pointsize ${INFO_POINTSIZE} \
			-gravity southeast -annotate 0x0+${INFO2_POSITION_X}+${INFO2_POSITION_Y} \
			"${FocalLength35efl},  F${FNumber},  ${ExposureTime},  ISO${ISO},  EV ${ExposureCompensation}" ${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

2024/09/30(月)LaTeXで空白記号を出力する

半年以上前,LaTeXでプログラミングとPC操作の説明文を書く際に,
半角スペースを明示する必要があったので空白記号を使おうとして,探したが見つからず…….
『LaTeX 空白記号』とかでGoogle先生に尋ねるとLaTeXにおける空白の調整を行う方法が列挙され,欲しい情報が出てきませんでした*1

そして,また空白記号を使う必要があったので探し直していたのですが,漸く見つけました.
『LaTeX2e 美文書作成入門 改訂第8版』*2の付録E,385ページに記載.
次のコマンドが空白記号に対応します.そのまんまなコマンド名ですね.
\textvisiblespace
インターネット(検索エンジン)は万能ではないと改めて思った次第です.

*1 : 仕方が無いので,このときは空白の箱を定義してごまかしていました.“\setlength{\fboxsep}{0pt}\fbox{\rule[0.8ex]{0pt}{0.8ex}\setlength{\fboxsep}{3pt}\hspace{0.1em}}”

*2 : LaTeXの解説本として有名な本です.今は9版が出ています.

2024/09/06(金)写真画像ファイルにフィルム写真風の日付を挿入するシェルスクリプト改3

3月に思いつきで作成したシェルスクリプトを改良したので公開します.
前回の記事は,説明がわかりづらく,不十分だったため,もう少し解説を充実させました.
簡単な内容ですが,誰かのお役に立てれば嬉しいです.

p20180813__IMP0221_TS.jpg
2018:08:13 14:34:15 PENTAX PENTAX Q7 / 3.8mm F5.0 0.001秒 ISO100



1 できること

本バージョン“改3”では,次の機能が実装されています.
  1. 一般的なデジタルカメラで撮影した写真ファイルに,後付けでフィルム写真風の日付を追加します.
    • 実際のフィルムカメラのデート機能による日付よりも大きく,ハッキリと表示されるように設定してあります.
    • これは,プロジェクター等で投影したときに判読しやすいことが必要と考えたからです*1.用途に応じて調整してください.
  2. 出力される文字は,オレンジ色に塗りつぶされ,指定幅でグレー(透過の薄い黒)にて縁取られた文字です.
    • 塗りつぶしは単純なものではなく,画像がわずかに透けて見えるように調整してあります(透過率設定がしてあります).
      これは,デート機能による日付写し込みの挙動をちょっとだけ再現する意図です*2
    • 縁取ってあるのは可読性を向上するためです.ただし,完全な黒で縁取ると雰囲気を損ねるので薄く縁取っています.
      濃淡は好みで変更してください.
    • 右下方向に影も出ます*3.デート機能の写し込みの挙動の滲みっぽいものを表現できればと思ったのですが,イマイチです.
  3. 写真ファイルのピクセル幅と高さを数えて,画面と日付の大きさの比率を維持して日付を挿入します.
    • 機材によって写真のピクセルサイズが異なるのは当然なので,この調整機能は必須と考えました.
    • 写真のピクセルサイズが小さすぎると破綻すると思います.撮って出しの大きなJPEGファイルを処理対象にするのが推奨です.
  4. 処理後の写真ファイルは先頭に年月日が追加されます.
    • 元ファイルの名前が“_IMP0221.JPG”だった場合,“p20180813__IMP0221_TS.jpg”のようになります.
    • 元ファイルの名前は恐らくどのような文字列・長さでも対応しています.ただし半角英数の短めのファイル名が推奨です.
    • 万が一画像処理に失敗しても元のファイルが消失することは多分ありません.
  5. 処理後の写真ファイルのEXIF情報は維持されます.写真のピクセルサイズも変わりません.画質は少し劣化します(JPEGファイルをそのまま複数回処理しているため.).
市販ソフトでも似たような機能のものはあると思いますが,それらに対する優位性は以下のようなことだと思います.
  1. 無料(ただし無保証.(一応ですが)GPLライセンスとします)
  2. 好きなフォントに差し替え可能.
  3. 日付の位置や大きさ等を,コードを改変して自由に設定できる.
  4. コマンドライン(bashシェル)からお手軽に操作できる.
  5. 写真ファイルのピクセルサイズがバラバラでも,配置やサイズの比率を維持して大量に処理できる.
今の時点での問題点は
  1. 日付を挿入するだけなのに割と時間が掛かる(ImageMagickを何度も実行しているため).
  2. パイプ入力に対応していない.
  3. 1999年以前の日付に対応していない*4
  4. 縦構図の写真を処理する際,処理前に画像を回転していると,日付の挿入位置が縦横反転してしまう*5
    挿入位置を揃えたい場合は,本シェルスクリプトの処理前に回転操作をしないようにするのが手っ取り早いですね.
私が使う分にはあまり問題ではないので,改良版は出ない可能性が高いです.

続きを読む

2024/03/18(月)写真画像ファイルにフィルム写真風の日付を挿入するシェルスクリプト

写真の画像ファイルに,フィルム写真風の日付を追加できると良いと思い立ち,シェルスクリプトを作成しました.
これは,仕事の写真の説明や保存管理にも使えるでしょうし,
手抜きのフォトアルバムを作るのにも使えそうです.

改良版の“改3”を作成し,内容を整理しました.以下よりはわかりやすいと思いますので,適宜参照してください.

処理結果は次のような感じになります*1
_IMP0222.JPG.jpg
[2018:08:13 14:34:22, PENTAX Q7 , / 3.8mm F5.0 0.001秒 ISO100]


日付情報は,通常写真ファイルに埋め込んであるので,それを読み出して写真画面上に描き込みます.
7セグ・14セグフォント 「DSEG」から,14セグメントなフォントをいただいて*2,それっぽく見せます.

以下のスクリプトはWSL上のDebian 12で動作確認しました.
Debian 12のImageMagickは6.9系なのでmagickコマンドではなく,昔ながらのidentifyやconvertコマンドです.
ImageMagick7系の場合は書き換える必要があります.
引数の処理対象のリストファイルはUNIX系のパス記述方法で記載します.lsの出力結果を整形するのが良さそうです.
#!/bin/bash
# 引数の説明
# $1: 操作対象パス(ファイル)リストが記載されたファイル.
# SEIREKI_BACKQUOTE="\`" # "`"はグレイヴアクセント.西暦の略記はアポストロフィ“'”.
SEIREKI_BACKQUOTE=`echo -e "\U0027"` # グレイヴアクセントや文字コード指定がsed中で上手くエスケープできないので変数に分けた.
while read IMAGENAME # $1の各行に記載された画像ファイルのパス.
 do
                 echo "\"${IMAGENAME}\"の撮影日時の取得……"
                 DateTimeOriginal=`identify -verbose ${IMAGENAME} |
                         grep "DateTimeOriginal" |
                         sed -e "s/^.*exif:DateTimeOriginal: //g" |
                         sed -e "s/ .*$//g" |
                         sed -e "s/:/     /g" |
                         sed -e "s/20/${SEIREKI_BACKQUOTE}/g" `
                 echo "${DateTimeOriginal}"
                 OUTPUT_IMAGENAME=`echo "${IMAGENAME}.jpg"`
                 echo "出力ファイル名: ${OUTPUT_IMAGENAME}"
                 convert -resize 3096x2064 -font DSEG14-Classic-Bold -fill orange -pointsize 100 -gravity southeast -annotate 0x0+300+150 "${DateTimeOriginal}" ${IMAGENAME} ${OUTPUT_IMAGENAME}
done < $1

続きを読む

2024/03/04(月)Let's Encryptの証明書で古いドメインを削除する

Let's Encryptから古い方のドメインの証明書を更新するよう案内メールが届いた.
このドメインは二度と使うことはないので,削除する.
Rocky Linux 8.9での作業である.

端折っているが,ウェブサーバ等の設定ファイルで“ac206223.ppp.asahi-net.or.jp”が記述されていないかをチェックした方がいいかもしれない.
少なくとも,下の案内文にはその旨書いてある.
[root@server ~]# certbot delete
Saving debug log to /var/log/letsencrypt/letsencrypt.log

Which certificate(s) would you like to delete?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: ac206223.ppp.asahi-net.or.jp
2: cshs-small-worlds.aa0.netvolante.jp
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter 'c' to cancel): 1

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The following certificate(s) are selected for deletion:

  * ac206223.ppp.asahi-net.or.jp

WARNING: Before continuing, ensure that the listed certificates are not being
used by any installed server software (e.g. Apache, nginx, mail servers).
Deleting a certificate that is still being used will cause the server software
to stop working. See https://certbot.org/deleting-certs for information on
deleting certificates safely.

Are you sure you want to delete the above certificate(s)?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
Deleted all files relating to certificate ac206223.ppp.asahi-net.or.jp.
[root@server ~]# systemctl restart httpd
[root@server ~]#