前提
大前提として大量のjpg/png/bmp/gifの入り混じった画像ファイルがあるのはそうなのだが、以下のソフトを利用する。
- Bash -- 確認してないけどBash固有の拡張を使ってないと思うからshで十分かも
- GNU Parallel(https://www.gnu.org/software/parallel/) -- 並列実行
- find/rm/mv等いわゆるcoreutils -- 多分GNU拡張使ってるからBSD系だと動かない
- libwebp(https://chromium.googlesource.com/webm/libwebp) -- examplesに入っているものを使う
- GraphicsMagick(http://www.GraphicsMagick.org) -- libwebpで直接扱えないものの一次変換
- zopfli(https://github.com/google/zopfli) -- pngの最適化
実装
シェルスクリプトで書く。
とりあえずお決まり
#!/bin/bash
set -e
libwebpに含まれるgif2webpを用いて、gifをWebPに変換する。
この時、元ファイルと変換後のファイルの小さいほうを残す。
function conv_gif(){
for arg; do
FN=`basename "${arg}"`
FDIR=`dirname "${arg}"`
OUT=${FN%.*}.webp
pushd "${FDIR}" >/dev/null 2>&1
if [ ! -f "${OUT}" ]; then
gif2webp -mixed -m 6 -v -o "${OUT}" "${FN}"
fi
if [ -f "${OUT}" -a -s "${OUT}" ]; then
P_SIZE=`stat -c %s -- "${FN}"`
A_SIZE=`stat -c %s -- "${OUT}"`
if [ ${P_SIZE} -gt ${A_SIZE} ]; then
rm -- "${FN}"
else
rm -- "${OUT}"
fi
fi
popd >/dev/null 2>&1
done
}
export -f conv_gif
find -O3 -type f -iname \*.gif -print0 | parallel -0 --eta -r --nice 10 -q conv_gif
bmpやtiffをとりあえずpngにする。ここで使うのはgm(GraphicsMagick)
function conv_png(){
for arg; do
FN=`basename "${arg}"`
FDIR=`dirname "${arg}"`
OUT=${FN%.*}.png
pushd "${FDIR}" >/dev/null 2>&1
if [ -f "${OUT}" ]; then
rm -- "${FN}"
else
gm convert "${FN}" "${OUT}"
if [ -f "${OUT}" -a -s "${OUT}" ]; then
rm -- "${FN}"
fi
fi
popd >/dev/null 2>&1
done
}
export -f conv_png
find -O3 -type f \( -iname \*.bmp -or -iname \*.tif -or -iname \*.tiff \) -print0 | parallel -0 --eta --nice 10 -r -q conv_png
find -empty -delete
jpgやpngをWebPにする。
ここでもやはり、元ファイルと変換後のファイルの小さいほうを残す。
また、一部のjpgがcwebpだと読み取りに失敗するので、失敗した場合は一旦pngに変換してリトライする。
function conv_webp(){
for arg; do
FN=`basename "${arg}"`
FDIR=`dirname "${arg}"`
OUT=${FN%.*}.webp
pushd "${FDIR}" >/dev/null 2>&1
if [ ! -f "${OUT}" ]; then
cwebp -m 6 -af -sharp_yuv -quiet -alpha_filter best -o "${OUT}" -- "${FN}"
fi
if [ ! -f "${OUT}" ]; then
gm convert "${FN}" "${FN%.*}.new.png"
P_SIZE=`stat -c %s -- "${FN}"`
A_SIZE=`stat -c %s -- "${FN%.*}.new.png"`
if [ ${P_SIZE} -gt ${A_SIZE} ]; then
rm -- "${FN}"
mv "${FN%.*}.new.png" "${FN%.*}.png"
else
cwebp -m 6 -af -sharp_yuv -quiet -alpha_filter best -o "${OUT}" -- "${FN%.*}.new.png"
rm -- "${FN%.*}.new.png"
fi
fi
if [ -f "${OUT}" -a -s "${OUT}" ]; then
P_SIZE=`stat -c %s -- "${FN}"`
A_SIZE=`stat -c %s -- "${OUT}"`
if [ ${P_SIZE} -gt ${A_SIZE} ]; then
rm -- "${FN}"
else
rm -- "${OUT}"
fi
fi
popd >/dev/null 2>&1
done
}
export -f conv_webp
find -O3 -type f \( -iname \*.jpg -or -iname \*.jpeg -or -iname \*.png \) -print0 | parallel -0 --eta --nice 10 -r -q conv_webp
find -empty -delete
pngを最適化する。
当然、元ファイルと変換後のファイルの小さいほうを残す。
function opt_png(){
for arg; do
FN=`basename "${arg}"`
FDIR=`dirname "${arg}"`
OUT=${FN%.*}.opt.png
pushd "${FDIR}" >/dev/null 2>&1
if [ ! -f "${OUT}" ]; then
zopflipng -m --filters=01234me --lossy_transparent --lossy_8bit "${FN}" "${OUT}"
fi
if [ -f "${OUT}" -a -s "${OUT}" ]; then
P_SIZE=`stat -c %s -- "${FN}"`
A_SIZE=`stat -c %s -- "${OUT}"`
if [ ${P_SIZE} -gt ${A_SIZE} ]; then
rm -- "${FN}"
mv "${OUT}" "${FN}"
else
rm -- "${OUT}"
fi
fi
popd >/dev/null 2>&1
done
}
export -f opt_png
find -O3 -type f -iname \*.png -print0 | parallel --nice 10 -0 --eta -r -q opt_png
find -empty -delete