画像ファイルをAVIF形式に一括変換するバッチファイル
このブログを含め、Web上の画像データは容量削減のためにも徐々にWebP形式に移行しています。JPEGに比べて容量が3割以上削減できるし、主要ブラウザでWebP形式が見られないということもないためです。
WebPの変換作業にはGoogleが公開しているcwebpコマンド※が使えるため便利なのですが、ローカルにしか保存しない画像データは更に圧縮率の高いAVIF形式にしようと考えています。
※https://developers.google.com/speed/webp/download?hl=ja
以前、cavif-rsというオープンソースのアプリ(コマンド)を試したこともあるのですが、これはCPUで変換するため、なかなかエグい時間がかかり、実用的ではないと感じました。たしかWebP変換の10倍くらいだったかな。
しかし、この前の記事「15万円ほどでコンパクトなRTX4070ゲーミングPCを組んでみた」で書いたとおり、GPUをRTX40シリーズにしたため、AV1のハードウェアエンコードが使えるのではないか、とffmpeg※でAVIF形式への変換を試したところ、cwebpよりも早い速度で画像変換できたため、ローカル保存用の画像形式として採用することにしました。
※ffmpegは動画専用ではなく、画像の変換にも使用できる
毎日差分バックアップ、週に1回フルバックアップを取るので、容量が半分になるのはとても魅力的なんですよね。
というわけで備忘録を兼ねてffmpegを使い、画像をAVIF形式へ一括変換するバッチファイルを置いておきます。
※ハードウェアエンコードを前提に書いていますが、オプションの -c:v av1_nvenc を外せばGPUなしでも使えます
ffmpegでAVIF形式へ変換する基本
ffmpeg -i sample.jpg sample.avif
-iオプションの後に変換したい画像ファイル名を指定し、その次に拡張子をavifにしたファイル名を付けるだけ。
基本はこれだけなんですが、CPUエンコードになるのでクソ遅い。そのため、nVidia製のグラボRTX40シリーズを搭載しているなら、下記のようにハードウェアエンコードを指定します。
ffmpeg -i sample.jpg -c:v av1_nvenc sample.avif
「-c:v av1_nvenc」の部分がnVidiaのAV1ハードウェアエンコードを使えよ、という指定。
ただ、こんなエラーメッセージが出ることがあります。
[av1_nvenc @ 000001c0e6041580] YUV444P not supported [av1_nvenc @ 000001c0e6041580] No capable devices found
メッセージのとおり、YUV444Pには対応しとらんぞ、という意味。
これはピクセルフォーマットとやらのことらしいのですが、ぼくは専門外なのでよくわかりません。AV1だとYUV444Pがデフォルトなんだけどハードウェアエンコードではサポートしてねーよってこと、なのかな?
いずれにせよ、YUV444Pをサポートしてないなら別のフォーマット指定すれば良いだろ、ってことでYUV420Pを指定したら動いたのでそうしています。
例)
ffmpeg -i sample.jpg -c:v av1_nvenc -pix_fmt yuv420p sample.avif
ということで、これがハードウェアエンコードでAVIFファイルを作るコマンドの基本形。
こんな1枚だけの変換なら、GoogleのSquooshでも使ったほうがマシなので、ここから複数ファイル対応に書き換えます。
ZIPファイル内の画像を一括でAVIFへ変換するバッチファイル
ffmpeg / zip / unzip の3つのコマンドにPATHが通っていることが前提で書かれていますが、それが嫌な場合はバッチファイル上部の環境変数を書き換えて対応してください。
@echo off
set TEMP_DIR=.\temp
set CONVERT_COMMAND=ffmpeg -hide_banner -loglevel error
set CONVERT_OPTION=-c:v av1_nvenc -preset p7 -cq 40 -pix_fmt yuv420p -vf "scale=-1:'if(gt(ih,2160),2160,ih)'" -y
set ZIP_COMMAND=zip
set UNZIP_COMMAND=unzip
cd /d %~dp1
if /i "%~x1"==".zip" ( goto :zip )
goto :file
:file
%CONVERT_COMMAND% -i "%1" %CONVERT_OPTION% "%~n1.avif"
goto :exit
:zip
mkdir %TEMP_DIR%
%UNZIP_COMMAND% -j %1 -d %TEMP_DIR%
for %%A in (%TEMP_DIR%\*.*) do (
%CONVERT_COMMAND% -i "%%A" %CONVERT_OPTION% "%TEMP_DIR%\%%~nA.avif"
)
%ZIP_COMMAND% -q -j "%~n1.avif.zip" "%TEMP_DIR%\*.avif"
rmdir /S /Q %TEMP_DIR%
goto :exit
:exit
set TEMP_DIR=
set CONVERT_COMMAND=
set CONVERT_OPTION=
set ZIP_COMMAND=
set UNZIP_COMMAND=
pause
バッチファイルに渡されたのが画像ファイル単体ならそのまま変換。ZIPファイルなら解凍して中身の画像ファイルを全てAVIFへ変換して再圧縮。その際、拡張子はavif.zipにしています。
つまり、sample.zipをこのバッチファイルへドラック&ドロップしたら、同じフォルダに sample.avif.zip が作成される、ということ。
以前投稿した「動画ファイルを右クリックメニューからH.265エンコードできるようにする」と同じようにshell:sendtoへ登録しておけば、ZIPファイルを右クリック→送る→バッチファイルで即座にAVIF変換が可能です。らくちん!
AVIFの品質指定について
ぼくは画質をぎりぎりまで我慢できる程度まで下げて、なるべくファイルサイズを小さくしたいので、 -preset p7 -cq 40とか指定していますが、気になる人は-cqの数値を低くすると良いです。 (大きくすると品質が低く、ファイルサイズは小さくなります)
AV1形式の動画などは -cq 45 まで下げて保存していますが、静止画だとちょっとぼやけ具合が気になったので -cq 40 にしました。画質の劣化を気にする人は -cq 30 以下じゃないと耐えられないかも。ぼくの場合はスキャンした経理資料の書類データとかが多く、質より量が大事なので上述のような指定です。
scaleオプションについて
最近のスマホって画素数がとんでもないですよね。余裕で4k超えてる。
なので、画像のピクセルサイズを見て、縦2160pxを超えた場合は2160pxに収まるようにリサイズしています。それがこの部分
-vf "scale=-1:'if(gt(ih,2160),2160,ih)'"
ffmpegってコマンドラインツールなのにIF指定まで出来ちゃうんですよね。たぶん、ihがImage Height(画像の高さ)の略で、gtはGrater Thanの略かな。
ffmpegのドキュメント読むとこのif文の例が出てくるんだけど、ihの意味とかgtの意味とか細かい解説が見当たらないのよね。見ればわかるだろ?という雰囲気を感じます。まぁわかるけどさ。
画像の縦pxは1080以下で良いっスわ、という人は下記のように書き換えればOK。
-vf "scale=-1:'if(gt(ih,1080),1080,ih)'"
コマンド類のダウンロード先 (Windows)
ffmpeg
https://ffmpeg.org/download.html
Zip for Windows
https://gnuwin32.sourceforge.net/packages/zip.htm
UnZip for Windows
https://gnuwin32.sourceforge.net/packages/unzip.htm
AV1 Video Extension
https://apps.microsoft.com/detail/9MVZQVXJBQ9V?hl=ja-jp
※Windows標準のフォトでAVIFが開けるようになります
まとめ
やはり一括処理はGUIツールより、CUIツールが便利。
……………なんだけど、パソコン苦手な人のためにもうちょいお手軽なソフトあっても良い気はする。以前だったらRalphaなんだけど、あれはWebPにもAVIFにも対応してないしなー。