Skip to content

Instantly share code, notes, and snippets.

@caiguanhao
Last active May 13, 2022 00:16
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save caiguanhao/4528926 to your computer and use it in GitHub Desktop.
Save caiguanhao/4528926 to your computer and use it in GitHub Desktop.
Recursively crush/shrink/optimize/losslessly compress PNGs, JPEGs and GIFs.
#!/bin/bash
# This is an improved script of pngfix.sh (https://gist.github.com/404909)
# which can also crush/shrink/optimize/losslessly compress JPEGs and GIFs.
# It is recommended you backup your image files before executing this script.
# Operation will be skipped if output file is bigger in size.
#
# use chmod +x crushimg.sh to make it executable and then ./crushimg.sh to run, or
# bash ./crushimg.sh to run it directly
#
# ./crushimg.sh [FILE] - (default to current directory)
# ./crushimg.sh example/ ./crushimg.sh example/*.jpg
#
# Install these software first:
# Arch Linux: pacman -S bc optipng libjpeg gifsicle
# and pngcrush: http://sourceforge.net/projects/pmt/files/pngcrush/
# Debian/Ubuntu: sudo apt-get install pngcrush optipng libjpeg-progs gifsicle
# Fedora: sudo yum -y install pngcrush optipng libjpeg-turbo-utils gifsicle
# FreeBSD: pkg_add -r pngcrush optipng gifsicle
# Mac OS X: brew install pngcrush optipng jpeg
# and gifsicle: http://dept.psych.columbia.edu/~peter/gifsicle/gifsicle-1.64.dmg
# exit if software not found
set -e
output() { # text_left, text_right, text_right_color
NORMAL=$(tput sgr0)
case $3 in
0) COLOR=$(tput setaf 7);; #grey
1) COLOR=$(tput setaf 2);; #green
2) COLOR=$(tput setaf 1);; #red
*) COLOR=${NORMAL}
esac
MSG="$1"
let COL=$(tput cols)-${#MSG}+${#COLOR}+${#NORMAL}
printf "%s%${COL}s\n" "$MSG" "$COLOR[$2]$NORMAL"
}
if [[ $(date +%N) == "N" ]]; then
TIME_CMD="+%s"
else
TIME_CMD="+%s.%N"
fi
START=$(date $TIME_CMD)
CRUSH=$(which pngcrush)
OPTS1="-rem cHRM -rem gAMA -rem iCCP -rem sRGB -q"
OPTI=$(which optipng)
OPTS2="-quiet -fix -o4"
TRAN=$(which jpegtran)
OPTS3="-copy none -optimize -progressive -perfect"
GIFS=$(which gifsicle)
OPTS4="-O3 --careful -w"
BC=$(which bc)
TEMP="/tmp/tempimgfile"
# pngcrush may still output warnings/errors even with -q argument
# for example: Warning: versions are different between png.h and png.c
# therefore, restore default and then redirect all pngcrush output to /dev/null
set +e
# check jpegtran's -perfect option
if test "${OPTS3/-perfect/}" != "$OPTS3"
then
TRANHELP=$($TRAN -h >/dev/stdout 2>/dev/stdout)
if test "${TRANHELP/-perfect/}" == "$TRANHELP"
then
echo "Notice: jpegtran's -perfect option does not exist."
OPTS3=${OPTS3/-perfect/}
fi
fi
stat -f%z . >/dev/null 2>/dev/null
if [[ $? -eq 0 ]]; then
STAT_CMD="stat -f%z"
else
STAT_CMD="stat -c%s"
fi
BEFORE=0
AFTER=0
MAXDIFF=0
FILES=("$@")
if [[ $# -lt 2 ]] && [ -d $FILES ]; then
if [[ $# -eq 0 ]]; then FILES="."; fi
OLDIFS=$IFS
IFS=$'\n'
FILES=(
$(find $FILES -iregex '.*\.jpg$' -type f)
$(find $FILES -iregex '.*\.jpeg$' -type f)
$(find $FILES -iregex '.*\.png$' -type f)
$(find $FILES -iregex '.*\.gif$' -type f)
)
IFS=$OLDIFS
fi
LEN=${#FILES[@]}
if [[ $LEN == 0 ]]; then
echo "Nothing to do."
exit
fi
for (( i=0; i<${LEN}; i++ )); do
FILESIZE=$($STAT_CMD "${FILES[$i]}")
BEFORE=$(( $BEFORE + $FILESIZE ))
done
echo "Found ${LEN} files. Total size is" $(printf "%'d" $BEFORE) "bytes."
printf "%*s\n" "${COLUMNS:-$(tput cols)}" "" | tr " " -
for (( i=0; i<${LEN}; i++ )); do
FILEBEFORE=$($STAT_CMD "${FILES[$i]}")
FN=$(echo "${FILES[$i]}" | sed -e "s/^\.\///")
if [[ $FILEBEFORE -eq 0 ]]; then
output "$FN" "EMPTY INPUT" 2
continue
else
output "$FN" "PROCESSING..." 0
fi
if [[ ${FILES[$i]: -4} == ".png" ]]; then
$CRUSH $OPTS1 -- "${FILES[$i]}" $TEMP >/dev/null 2>/dev/null
$OPTI $OPTS2 -- $TEMP
elif [[ ${FILES[$i]: -4} == ".gif" ]]; then
$GIFS < "${FILES[$i]}" $OPTS4 > $TEMP
else
$TRAN $OPTS3 "${FILES[$i]}" > $TEMP
fi
FILESIZE=$($STAT_CMD "$TEMP")
DIFF=$(( $FILEBEFORE - $FILESIZE ))
if [[ $DIFF -gt 0 ]]; then
AFTER=$(( $AFTER + $FILESIZE ))
else
AFTER=$(( $AFTER + $FILEBEFORE ))
fi
PERCENT=$(echo "scale=2; ${DIFF} * 100 / ${FILEBEFORE}" | $BC)
DIFF_FORMAT=$(printf "%'d" $DIFF)
printf "\e[1A"
if [[ $FILESIZE -eq 0 ]]; then
output "$FN" "EMPTY OUTPUT, SKIPPED" 2
AFTER=$(( $AFTER + $FILEBEFORE ))
else
if [[ $DIFF -gt 0 ]]; then
cp $TEMP "${FILES[$i]}"
output "$FN" "${PERCENT}% (${DIFF_FORMAT}B) OK" 1
elif [[ $DIFF -lt 0 ]]; then
output "$FN" "SKIPPED" 2
else
output "$FN" "SKIPPED" 0
fi
fi
done
rm -f $TEMP
END=$(date $TIME_CMD)
DIFFSIZE=$(( $BEFORE - $AFTER ))
printf "%*s\n" "${COLUMNS:-$(tput cols)}" "" | tr " " -
echo "Total size now is" $(printf "%'d" $AFTER) "bytes."
if [[ $BEFORE -eq 0 || $DIFFSIZE -eq 0 ]]; then
echo "No reduction."
else
echo "Reduction:" $(printf "%'d" $DIFFSIZE) "bytes ("$(echo "scale=2; $DIFFSIZE * 100 / $BEFORE" | $BC)"%)."
fi
echo "Time used:" $(echo "$END - $START" | $BC) "seconds."
# pngcrush : http://pwet.fr/man/linux/commandes/pngcrush
# Remove all the color-correction data (gamma, white balance, ICC color profile,
# standard RGB color profile)
# -q : quiet, the opposite of verbose.
#
# OptiPNG : http://linux.die.net/man/1/optipng
# -quiet : Run in quiet mode
# -fix : Enable error recovery. This option has no effect on valid input files.
# -o level : The optimization levels 2 and higher enable multiple IDAT
# compression trials; the higher the level, the more trials.
#
# jpegtran : http://www.gsp.com/cgi-bin/man.cgi?section=1&topic=jpegtran
# -copy none : Copy no extra markers from source file. This setting suppresses
# all comments and other excess baggage present in the source file.
# -optimize : Perform optimization of entropy encoding parameters.
# -progressive : Create progressive JPEG file.
# -perfect : Fails with an error if the transformation is not perfect.
#
# Gifsicle : http://www.lcdf.org/gifsicle/man.html
# -O1 : Stores only the changed portion of each image. This is the default.
# -O2 : Also uses transparency to shrink the file further.
# -O3 : Try several optimization methods (usually slower, sometimes better results).
# --careful : Write slightly larger GIFs that avoid bugs in some other GIF
# implementations. Some Java and Internet Explorer versions cannot display
# the correct, minimal GIFs that Gifsicle produces.
# -w : Suppress all warning messages.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment