💽

【jpegtran】 出来る!ベースラインJPEG ⇔ プログレッシブJPEGのロスレス変換 ~Droplet編~

2022/01/22に公開

さよなら、全てのプログレッシブJPEG

Walkmanがクソ雑魚だからしゃぁない(爆)

概要

以前Qiitaでこんな記事を書いた。

https://qiita.com/libraplanet/items/d0c3b102f27f034565a1

(いずれ、こっちに移動したい)

jpegtran を用いることで、ベースラインJPEGプログレッシブJPEG を (ほぼ) 欠損無く相互変換が行えることが分かった。しかしながら。 jpegtran のWindowsバイナリの国際文字の扱いに何があり、扱いが難しい。Windowsを捨てたくなるけどそれはさておき。

私はアルバム アートの一括変換用として使いたいのだが。画像ファイルはアルバムのフォルダ名と対にして管理をしており、どうしても管理上国際文字は避けられない。また、一括変換にしても、変換不要なものは特に弄りたくない。後に、音声データに設定する際に、更新日時を指標にしたいためである。

さらに、今回は一括変換ではあるが。今後のリッピング作業於いて画像収集した際には、逐次実行のし安さも必要になる。画像管理ツールを色々探したが、希望を叶えるものがなかった。

単ファイルの変換用バッチ と、ExplorerのSendtoから活用するための 変換Droplet の作成を行った。

jpegtran だけでなく、 batfileDropletsendto の活用サンプルにも若干なると思うので、興味がある人はご参考に。

jpegtran の Windowsバイナリ

jpegtranjpegclub版 のWindowsバイナリを利用する。

mozjpeg版 では、jpegtran で標準入出力が利用出来き、今回のファイル名問題の回避方法としては若干嬉しいが。標準入出力の同時利用でファイル上書きが出来ないため、結局今回は意味ない。加えて、Premature EOF in JPEG file というエラーが多発するため。実用には欠ける。。。

プログラミング

基本コマンド

JPEG process の確認

rdjpgcom はチェックだけなので、標準入力を利用することで、国際文字問題は回避可能。

cmd
@REM check JPEG process.
rdjpgcom.exe -verbos < "album.jpg"
output (Baselineの時)
JPEG process: Baseline
output (Progressiveの時)
JPEG process: Progressive

この JPEG process: Progressive findstr で捕まえる。

cmd
@REM check JPEG process.
(rdjpgcom.exe -verbos < "album.jpg" | findstr /C:"JPEG process: Progressive" > nul) && ECHO hoge

これで、プログレッシブJPEGの時だけ処理する ための土台 (トリガー) が出来た

② 安全なファイル名に退避し変換処理

jpegtranの実行の為に、安全なファイル名変換して変換を実行。移動で上書きをすることで、一時ファイルの残留も抑える。

batch
@REM copy to tempfile.
copy /B /Y "album.jpg" "tmp.jpg"

@REM convert.
jpegtran -outfile "tmp.jpg" "tmp.jpg"

@REM move (overwrite).
move /Y "tmp.jpg" "album.jpg"

これを、前処理が成功時のみに整形

batch
(
    @REM copy to tempfile.
    copy /B /Y "album.jpg" "tmp.jpg" > nul
) && (
    @REM convert.
    jpegtran -outfile "tmp.jpg" "tmp.jpg"
) && (
    @REM move (overwrite).
    move /Y "tmp.jpg" "album.jpg" > nul
)

③ より安全な一時ファイル名の生成

同時処理を加味した場合に、一時ファイル名が固定名のは望ましくない。そのため、一時フォルダを用いた安全な一時ファイル名を生成し、より安全に処理出来る様にする。

batch
:loop
    @SET TEMP_FILE_NAME=

    @SET _R=00000%RANDOM%
    @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
    @SET _R=00000%RANDOM%
    @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
    @SET _R=00000%RANDOM%
    @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
    @SET _R=00000%RANDOM%
    @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
    @SET _R=00000%RANDOM%
    @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%

    @SET TEMP_FILE_PATH=%TEMP%\%TEMP_FILE_NAME%
    @IF EXIST "%TEMP_FILE_PATH%" @(
        @GOTO :loop
    )

%RANDOM% の桁数が保証されていないが、1桁ずつ切り出すのも効率が悪いため。0埋めして下の桁から切り出す感じ。ここで生成されたパスを先程の一連の変換処理で使う。ソース画像も引数にすると、こんな感じ。

batch (変換処理)
(
    @REM copy to tempfile.
    copy /B /Y "%~1" "%TEMP_FILE_PATH%" > nul
) && (
    @REM convert.
    jpegtran -outfile "%TEMP_FILE_PATH%" "%TEMP_FILE_PATH%"
) && (
    @REM move (overwrite).
    move /Y "%TEMP_FILE_PATH%" "%~1" > nul
)

青果物

上記コマンドをベースに。利用形態別にbatfileを作成する。
Dropletは引数を全てファイルで使用するため、形式を指定出来ない。なので、 Baselineへ変換用Progressiveへ変換用 をそれぞれ設ける。

  1. jpegxproc.bat
  2. jpeg2baseline.bat
  3. jpeg2progressive.bat

以上3種。

① (簡易変換用) jpegxproc.bat

jpegxproc.bat
@SETLOCAL
@SET JPEGTRAN_HOME=jpegtran インストール先
@SET PATH=%JPEGTRAN_HOME%;%PATH%

@GOTO :l_main

:sub_conv
:l_sub_conv_init
  @SET TEMP_FILE_NAME=

  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%

  @SET TEMP_FILE_PATH=%TEMP%\%TEMP_FILE_NAME%
  @IF EXIST "%TEMP_FILE_PATH%" @(
    @GOTO :l_sub_conv_init
  )

  @REM proc.
  @(
    @REM check JPEG process.
    rdjpgcom.exe -verbos < "%~4" | findstr /C:"%~2" > nul
  ) && (
    @REM copy to tempfile.
    copy /B /Y "%~f4" "%TEMP_FILE_PATH%" > nul
  ) && (
    @REM convert.
    jpegtran %~3 -outfile "%TEMP_FILE_PATH%" "%TEMP_FILE_PATH%"
  ) && (
    @REM move (overwrite).
    move /Y "%TEMP_FILE_PATH%" "%~f5" > nul
  ) && (
    @REM message.
    ECHO %~1 "%~f4" =^> "%~f5"
  )
  @GOTO :EOF

:l_main
  @SET OPT="%~1"
  @SET SRC_FILE="%~2"
  @SET TGT_FILE="%~3"
  @SET WORKED=0

  @IF %TGT_FILE% == "" SET TGT_FILE="%~2"

  @IF %OPT% == "-baseline" (
    @CALL :sub_conv "converted to baseline." "JPEG process: Progressive" "" %SRC_FILE% %TGT_FILE%
    @SET WORKED=1
  )
  @IF %OPT% == "-progressive" (
    @CALL :sub_conv "converted to progressive." "JPEG process: Baseline" "-progressive" %SRC_FILE% %TGT_FILE%
    @SET WORKED=1
  )
  @IF %WORKED% EQU 0 (
    @ECHO not work....
    @ECHO  ^( arguments : %* ^)
  )
@ENDLOCAL

② (Droplet用) jpeg2baseline.bat

jpeg2baseline.bat
@SETLOCAL
@SET JPEGTRAN_HOME=jpegtran インストール先
@SET PATH=%JPEGTRAN_HOME%;%PATH%

@ECHO start. %DATE% %TIME%
@ECHO.
@GOTO :l_main

:sub_conv
:l_sub_conv_init
  @SET TEMP_FILE_NAME=

  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%

  @SET TEMP_FILE_PATH=%TEMP%\%TEMP_FILE_NAME%
  @IF EXIST "%TEMP_FILE_PATH%" @(
    @GOTO :l_sub_conv_init
  )

  @REM proc.
  @(
    @REM check JPEG process.
    rdjpgcom.exe -verbos < "%~4" | findstr /C:"%~2" > nul
  ) && (
    @REM copy to tempfile.
    copy /B /Y "%~f4" "%TEMP_FILE_PATH%" > nul
  ) && (
    @REM convert.
    jpegtran %~3 -outfile "%TEMP_FILE_PATH%" "%TEMP_FILE_PATH%"
  ) && (
    @REM move (overwrite).
    move /Y "%TEMP_FILE_PATH%" "%~f5" > nul
  ) && (
    @REM message.
    ECHO %~1 "%~f4" =^> "%~f5"
    ECHO.
  )
  @GOTO :EOF


:l_main
  @IF NOT "%~1" == "" (
    @CALL :sub_conv "converted to baseline." "JPEG process: Progressive" "" "%~1" "%~1"
    @SHIFT
    @GOTO :l_main
  )
  @ECHO end. %DATE% %TIME%
  @ECHO.

@ENDLOCAL
@pause

③ (Droplet用) jpeg2progressive.bat

jpeg2progressive.bat
@SETLOCAL
@SET JPEGTRAN_HOME=jpegtran インストール先
@SET PATH=%JPEGTRAN_HOME%;%PATH%

@ECHO start. %DATE% %TIME%
@ECHO.
@GOTO :l_main

:sub_conv
:l_sub_conv_init
  @SET TEMP_FILE_NAME=

  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%
  @SET _R=00000%RANDOM%
  @SET TEMP_FILE_NAME=%TEMP_FILE_NAME%%_R:~-5%

  @SET TEMP_FILE_PATH=%TEMP%\%TEMP_FILE_NAME%
  @IF EXIST "%TEMP_FILE_PATH%" @(
    @GOTO :l_sub_conv_init
  )

  @REM proc.
  @(
    @REM check JPEG process.
    rdjpgcom.exe -verbos < "%~4" | findstr /C:"%~2" > nul
  ) && (
    @REM copy to tempfile.
    copy /B /Y "%~f4" "%TEMP_FILE_PATH%" > nul
  ) && (
    @REM convert.
    jpegtran %~3 -outfile "%TEMP_FILE_PATH%" "%TEMP_FILE_PATH%"
  ) && (
    @REM move (overwrite).
    move /Y "%TEMP_FILE_PATH%" "%~f5" > nul
  ) && (
    @REM message.
    ECHO %~1 "%~f4" =^> "%~f5"
    ECHO.
  )
  @GOTO :EOF


:l_main
  @IF NOT "%~1" == "" (
    @CALL :sub_conv "converted to progressive." "JPEG process: Baseline" "-progressive" "%~1" "%~1"
    @SHIFT
    @GOTO :l_main
  )
  @ECHO end. %DATE% %TIME%
  @ECHO.

@ENDLOCAL
@pause

使い方

上記batfileはbatfileは jpegtran のインストール先に合わせて修正する。
あと、batfile の置き場もお好みなので、とりあえず言及なし。

① (簡易変換用) jpegxproc.bat

まぁ取り立てて特別なこともなく。

cmd.exe ベースラインJPEGに変換する場合
FOR %A IN (*.jpg) DO jpegxproc.bat -baseline "%~A" "%~A"
cmd.exe プログレッシブJPEGに変換する場合
FOR %A IN (*.jpg) DO jpegxproc.bat -progressive "%~A" "%~A"

② (Droplet用) jpeg2baseline.bat

以下の項目一緒にまとて記載。

③ (Droplet用) jpeg2progressive.bat

Droplet なので適当な場所にショートカット作るだけで良いのだけど。汎用的に sendtoDroplet を活用出来るので、利用頻度の高いものなどには結構オススメ。

shell:sendto にショートカット を作成する。

わかりやすく名前を買えると良い感じ。

ss.png

② ファイルを選択して 送る

ss.png
ss.png

Getty - FLVSH ØUT には S-JIS に収録されていない Ø が含まれていたが、正常に変換できる。

ss.png
漢字全角記号カッコ ()アンパサンド &アポストロフィ ' なども特にエラー無く変換できる。

検証

変換実行

ss.png
ss.png

確認

cmd.exe 確認用コマンド (Progressiveだけ抽出)
FOR %A IN (*.jpg) DO @(rdjpgcom -verbos < "%~A" | findstr /C:"JPEG process: Progressive" > nul) && (ECHO JPEG process: Progressive %~fA )

変換前

ss.png
ss.png

変換後

ss.png
ベースラインJPEGは1件も無くなってしまった...

ファイル比較

ss.png
ちゃんと歯抜けで更新されている。

ss.png
更新されたファイルには、追加で劣化した様なところは一応見受けられない。

Q&A

Q. batファイルに @ 多くね?
A. そういうもん。

Q. ラベルの l_ って?
A. GOTOジャンプ 用のラベルとしての目印

Q. ラベルの sub_ って?
A. CALL でで呼び出すサブルーチン用のラベルとしての目印

Q. なんか書き方変じゃない?
A. exit /b なんか使ってたらろくな部品にならないからな。。。。そういうのも加味して書くと、上部にサブルーチン、下部にエントリ ポイントを持ってくる感じにするのは、そこそこ扱いやすい。

あとがき

なんとか一括変換環境が出来たので、クソ雑魚WALKMAN NW-A30でのMP3運用に向けた、アルバム アートのBaseline化にようやく動き出せる。これのお陰で、Rippingも多少止まっていたほど。。。

まぁギャップレス再生を加味すると、ATLACが使えるWALKMAN NW-Aシリーズが結局最強にはなってしまうのだけれどもね。ギャップレス再生もできないデバイスでMIX音源をどう聴けとな。代替のフォーマットもなく、NW-Sシリーズが非対応になり廃れていくのは、本当に勘弁しやがれ。

とは言っても、ATLACの音質はMP3以上に宜しくなく、x-アプリも使い勝手が悪いので。ファイル システムとの併用になりそう。

と言うところで、ローカルのFLACをMP3に変換して入れたところ、アルバム アートが表示されない楽曲がちらほらで、愕然としたわけで。。。。

このご時世にBaselineしか対応していませんって、意味がわかんないんですけど。チェック ツールとか、他にProgressiveで起きる問題とかもなかなか無いのに。。。。

jpegtranでロスレス変換が出来ることが分かってから、技術的には可能というのが判明したので、そこから1年近く一括変換方法を考えていた。結局既存のいい感じのフロント エンドも無く、自作するにもライブラリ調査などで死ぬほど時間かかりそうで中々アクション起こせなかったけど。

今回ようやっと、当初から思案していたbatfileレベルの実装が出来た感じ。

まぁ、jpegtranのwindowsバイナリがちゃんとUNICODE対応していてくれれば、全然問題なかったのにね。。。

画像管理ソフトも自前で作りたい気持ちもあるので。いずれ、普通のウィンドウ アプリで baseline / progressive の確認・ロスレス変換が出来る様なGUIアプリを作りたい。

参考 (謝辞)

Discussion