見出し画像

JPEG ファイルを綺麗に小さく作成する(Windows 環境)

Lightroom から書き出した JPEG ファイルって、そこそこ大きいですよね。リサイズなどをしてファイルサイズを小さくすることが解決策の一つですが、リサイズもせず可能な限り元の画像のまま小さくしたい、というニーズもあるかと思います。

MozJPEG は JPEG を綺麗なままできる限り小さく作成することができます。この記事では MozJPEG を便利に使う方法を紹介します。


MozJPEG を使用する

MozJPEG は Mozilla が開発した JPEG ファイルを扱うソフトウェアです。その中に、JPEG ファイルへの変換を行うツールが含まれており、今回はそちらを使います。

MozJPEG reduces file sizes of JPEG images while retaining quality and compatibility with the vast majority of the world's deployed decoders.

と言っているだけあり、クオリティを維持しつつ、よく使われる JPEG ファイルの変換よりファイルサイズを減らすことができるのが特徴です。
なお、バイナリは Mozilla 公式から配布されておらず、有志の方がビルドしたものが配布されております。この記事では配布先は紹介しませんが(野良ビルドの責任は負えませんので……)、Google で検索するといくつか見つかると思います。

実際に試してみましょう。cjpeg というバイナリが、JPEG を作成するツールです。ファイルは Lightroom から PNG で書き出したものを、BMP ファイルに変換しております。コマンドラインオプションは下記の通りです。

cjpeg -quality 90 -outfile sample_mozjpeg_q100.jpg sample.bmp

なお、今回の画像は https://raw.pixls.us/ でダウンロードした CC0 1.0 Universal (CC0 1.0) の RAW ファイルを使っています。具体的には「Sony - ILCE-7M3 - 14bit compressed (3 2).ARW」というファイルです。作成したファイルを公開するわけでもないのですが、参考までに。

PS E:\Lightroom\Output> Get-ChildItem | Where-Object {$_.Extension -eq ".jpg"} | Select-Object Name, @{Name="KByte";Expression={ "{0:N0}" -f ($_.Length / 1KB) }}
Name                    KByte
----                    -----
sample_mozjpeg_q70.jpg  4,365
sample_mozjpeg_q80.jpg  6,091
sample_mozjpeg_q90.jpg  10,121
sample_mozjpeg_q95.jpg  13,941

6000 x 4000 px の画像が画質 90 で 10MB 強となりました。画質 80 でも 6MB くらいです。 Irfan View で画質 80 の JPEG ファイルを作成しましたが、6.6 MB ほどとなりました。確かに、MozJPEG で作成するとファイルサイズが小さい JPEG ファイルができました。


Windows バッチ

さて、MozJPEG はコマンドラインのツールです。便利に使うには、スクリプトを作ってあげるとよろしいです。以下は Windows でのバッチスクリプトです。

なお、cjpeg は通常 BMP ファイルしか読み込めないため、対象のファイルの事前処理として ImageMagick を使って BMP にする処理を行っております。
その結果、BMP に変換された際に EXIF などのメタデータ(画像データに入っている色々なテキスト情報など。撮影日時とか、何のレンズを使って撮影したかなど、諸々の情報が埋め込まれています)が消えてしまいます。

そのため、exiftool を使って一旦エクスポートし JPEG に変換したあと、再度 EXIF を埋め込む処理を行っております。ImageMagick 含めて、Chocolatey でインストールできます。

(アップデートがあると、パスが毎回変わるのが難点です……)


変換バッチ

バッチファイルに渡された引数のファイルを、MozJPEG の cjpeg を使って JPEG に変換するバッチファイルです。デフォルトは、変換元のファイルと同じディレクトリに書き出します。元のファイルは削除されます。引数が複数あるとき以外のエラーハンドリングはしておりません。
VARS の箇所の値は、ご自身の環境に合わせてください。

> pictfile_to_jpg.bat [変換したい画像ファイルのパス] [JPEG ファイルの保存先(指定しなければ元ファイルと同じ場所)]

@echo on
rem ===========
rem:
rem  Picture file to jpeg with cjpeg (mozjpeg)
rem:
rem ===========

rem ===== VARS =====
set jpeg_quarity=95
set cjpeg_bin=[cjpeg.exe へのパス]
set imagemagick_bin=[ImageMagick の magick.exe へのパス]
set exiftool_bin=[exiftool.exe へのパス]
set cjpeg_ext_options=
rem ================

set target_file=%1

rem path exist check
call :pathcheck %cjpeg_bin%
call :pathcheck %imagemagick_bin%
call :pathcheck %exiftool_bin%
call :pathcheck %target_file%

set target_file_ext=%~x1

if "%2" == "" (
 set dest_dir=%~dp1
) else (
 setlocal ENABLEDELAYEDEXPANSION
 dir /ad "%2" 1> NUL 2> NUL
 if !ERRORLEVEL! EQU 0 (
   set dest_dir=%2
   if not !dest_dir:~-1!==\ SET dest_dir=%2\
   endlocal
 ) else (
   echo ERROR: Invalid path.
   exit /b 1
 )
)

set save_filename=%~n1
set exif_xml=%dest_dir%%save_filename%.xml
set save_dir_filename_ext=%dest_dir%%save_filename%.jpg
set invalid_target=%3

rem If there is too many args, exit this bat program.
if defined invalid_target (
 echo ERROR: Too many args.
 exit /b 1
)

if /i %target_file_ext% == .bmp (
 "%cjpeg_bin%" -outfile "%save_dir_filename_ext%" -quality %jpeg_quarity% %cjpeg_ext_options% "%target_file%"
) else (
 "%imagemagick_bin%" convert -quiet "%target_file%" bmp3:- ^
   | "%cjpeg_bin%" -outfile "%save_dir_filename_ext%" -quality %jpeg_quarity% %cjpeg_ext_options%
 "%exiftool_bin%" -X "%target_file%" > "%exif_xml%"
 "%exiftool_bin%" -overwrite_original -tagsfromfile "%exif_xml%" "%save_dir_filename_ext%"
 del /f "%exif_xml%" 
)

del /f "%target_file%"
exit /b

:pathcheck
if not exist "%1" (
 echo ERROR: Path is not exist.
 exit /b 1
)


Windows の「送る」メニューに追加して使うのが便利かと思います。


私の環境ですと、Lightroom との連携をするとき、以下のようにこのバッチファイルを使用しております。この結果、全自動で MozJPEG で変換まで完了します。

・Lightroom の書き出し設定で、特定のディレクトリに PNG で書き出し。
・Windows のバッチ処理用ユーザーが、そのディレクトリの PNG ファイルを上のバッチファイルを使って変換。この処理は、Windows のタスクスケジューラを使用し、1 分に一回処理を行う。
・変換された JPEG ファイルを、指定のディレクトリにコピー。

通常使うユーザーのタスクスケジューラに登録すると、実行時にウインドウが表示されたりするので鬱陶しくなります……。そのため、バッチを実行する専用ユーザーを作っておくと、普段の作業を邪魔されず、完全にバックグラウンドで処理してくれるので便利です。

そのバッチがこちらです。

@echo on
set TargetPath=[Lightroom で PNG ファイルを書き出しするディレクトリ]
set DestPath=[最終的に JPEG を保存したいディレクトリ]
set ExecuteFile=[先ほどの JPEG を作成するバッチのパス]
set LockFile=[適当なフォルダ + ファイル名(lockfile.lock など)]

rem if lock file is exist already, skip this bat on this time.
if exist %LockFile% exit /b 1

rem make lock file
echo %date% > %LockFile%
echo %time% >> %LockFile%

for /R "%TargetPath%" %%i in (*.png) do ( call "%ExecuteFile%" "%%i" "%DestPath%" )

rem delete lock file
del /f %LockFile%
exit /b


ロックファイルがある場合、何かしらでバッチが異常終了した場合です。その場合、次回の実行は自動的に失敗となり、何も処理が行われません。ロックファイルを削除すれば、再度実行します。
また、これをタスクスケジューラでバッチ専用ユーザーで毎分実行すれば、自動的に目的のフォルダへ JPEG ファイルが作成されます。


最後に

ところで、今回 Lightroom で作成された JPEG ファイルと MozJPEG のファイルとで、画質に差があるかの比較をしようとしておりました。具体的には、以下のサイトを参考に ImageMagick の compare を使用し、元画像との差分を確認するというものでした。

……ですが、以下の観点からその詳細は、当記事には記載しません。

・Lightroom で作成した JPEG ファイルは、画質のパラメータを変更してもファイルサイズが変わらない場合がある。そのため、MozJPEG で作成したファイルと比較しても、本当にパラメータ値から期待される画質が同じなのかが不明。


# 品質が「100」でも「95」でもファイルサイズが同じですね……。

PS E:\Lightroom\Output> Get-ChildItem | Where-Object {$_.Extension -eq ".jpg"} | Select-Object Name, @{Name="KByte";Expression={ "{0:N0}" -f ($_.Length / 1KB) }}
Name            KByte
----            -----
sample_q100.jpg 23,045
sample_q95.jpg  23,045

・実際、MozJPEG の画質 90 と Lightroom の画質 90 では、Lightroom が MozJPEG の 1.5 倍以上のファイルサイズの違いがある。かつ、やはり MozJPEG で作成したファイルの方が大きく元画像と差が出てしまっている。

と、Lightroom の画質パラメータに大きく疑問が出てしまったので、比較が難しいという結論にいたりました。
このサイトでも、特定の画質パラメータの場合、ファイルサイズが同じになるという考察を出されております。

Photoshop で画像を JPEG にする際に、1 から 100 までの画質の指定ではなく、1 から 12 までの指定です。もしかしたら、Lightroom も Photoshop と同じく 12 つの値の指定しか内部で処理できず、近い値が選択されるという動きなのかもしれません。

ということで、疑問が残る終わり方となりましたが、MozJPEG を活用頂ければと思います。

老後、奥さんと世界一周のための費用にします。