Last active
May 9, 2023 15:30
-
-
Save wittman/ab90f0eca5124233fa5163a32f890220 to your computer and use it in GitHub Desktop.
imgopt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -e | |
set -o nounset | |
set -o errexit | |
RESTORE=$(echo -en '\001\033[0m\002') | |
RED=$(echo -en '\001\033[00;31m\002') | |
GREEN=$(echo -en '\001\033[00;32m\002') | |
YELLOW=$(echo -en '\001\033[00;33m\002') | |
timestamp() { | |
date "+%Y-%m-%d-%H-%M-%S" | |
} | |
validParams() { | |
cat <<-EOF | |
${YELLOW}--wh 0${RESTORE} # w(idth)h(eight) - max height and width in pixels | |
${YELLOW}--w 0${RESTORE} # w(idth) max in pixels | |
${YELLOW}--h 0${RESTORE} # h(ight) max in pixels | |
${YELLOW}--pngQualityQ 20-50${RESTORE} # PNG Pngquant quality min-max 0to100-0to100 | |
${YELLOW}--pngQualityX 3${RESTORE} # PNG Oxipng optimize factor (1-6) | |
${YELLOW}--webpQ 60${RESTORE} # webP quality (0to100, default 75) | |
${YELLOW}--webpAlphaQ 80${RESTORE} # webP alpha channel quality (0to100, default 100) | |
${RED}Note:${RESTORE} ${YELLOW}Customize quality settings for JPG and GIF files in desktop app ImageOptim Preferences.${RESTORE} | |
${YELLOW}--name${RESTORE} # Filter: Only select file names containing --name value | |
${YELLOW}--saveversion Y${RESTORE} # Save optimize run in a timestamped directory 'version' (_imgopt-YYYY-mm-dd-H-M-S) | |
(--saveversion with any string param value is save version 'On'. Do not include --saveversion param at all for 'Off') | |
${YELLOW}--heictojpg Y${RESTORE} # Convert .HEIC to .JPG format | |
(--heictojpg with any string param value is save version 'On'. Do not include --heictojpg param at all for 'Off') | |
${YELLOW}--pngtojpg Y${RESTORE} # Convert .PNG to .JPG format | |
(--pngtojpg with any string param value is 'On'. Do not include --pngtojpg param at all for 'Off') | |
${YELLOW}--jpgtopng Y${RESTORE} # Convert .JPG to .PNG format | |
(--jpgtopng with any string param value is 'On'. Do not include --jpgtopng param at all for 'Off') | |
${YELLOW}--towebp Y${RESTORE} # Convert (.PNG, .JPG) to .WebP format | |
(--towebp with any string param value is 'On'. Do not include --towebp param at all for 'Off') | |
EOF | |
} | |
if [ $# -eq 0 ]; then | |
cat <<-EOF | |
${YELLOW}=== Prerequisites ===${RESTORE} | |
OS X | |
ImageOptim (desktop app) <https://imageoptim.com/mac> | |
pngquant <https://github.com/kornelski/pngquant> | |
brew install pngquant | |
oxipng <https://github.com/shssoichiro/oxipng> | |
brew install oxipng | |
${YELLOW}=== Usage ===${RESTORE} | |
${YELLOW}imgopt.sh <dir> <opt> <opt> ...${RESTORE} | |
${YELLOW}<dir>${RESTORE}: directory of images to optimize | |
${YELLOW}<opt>${RESTORE}: optional parameter | |
Output: ${YELLOW}<dir>/_imgopt${RESTORE} (subdir '_imgopt' will be created by this script). | |
Original image files will remain unchanged. | |
${YELLOW}=== Available parameters (shown with default value [if non-boolean type]) ===${RESTORE} | |
EOF | |
validParams | |
cat <<-EOF | |
${YELLOW}=== Examples ===${RESTORE} | |
# Resize to max width: 600px | |
${YELLOW}./imgopt.sh ~/project/original-images --w 600${RESTORE} | |
# Processed files will be created in ~/project/original-images/_imgopt subdirectory. | |
# Resize to max height: 450px | |
${YELLOW}./imgopt.sh ~/project/original-images --h 450${RESTORE} | |
# Resize to max width and height: 500px | |
${YELLOW}./imgopt.sh ~/project/original-images --wh 500${RESTORE} | |
# Use specific pngQualityQ (min-max default shown) | |
${YELLOW}./imgopt.sh ~/project/original-images --pngQualityQ 20-50${RESTORE} | |
# Example pngquant min (20) and max (50) are numbers in range 0 (worst) to 100 (perfect) | |
# Use specific pngQualityX (default shown) | |
${YELLOW}./imgopt.sh ~/project/original-images --pngQualityX 3${RESTORE} | |
# Example oxipng 3 is an integer between 1 and 6 (lower is faster, higher is better compression - higher than 3 is unlikely to give any extra compression gains) | |
# Use specific webP quality (0..100) | |
${YELLOW}./imgopt.sh ~/project/original-images --webpQ 60${RESTORE} | |
# Example --webpQ 60 would run cwebp -q 60 | |
# Use specific webP alpha channel quality (0..100) | |
${YELLOW}./imgopt.sh ~/project/original-images --webpAlphaQ 80${RESTORE} | |
# Example --webpAlphaQ 80 would run cwebp -alpha_q 80 | |
# Filter: select only filenames to optimize that contain substring | |
${YELLOW}./imgopt.sh ~/project/original-images --name background${RESTORE} | |
# Convert HEIC image format to JPG | |
${YELLOW}./imgopt.sh ~/project/original-images --heictojpg Y${RESTORE} | |
# Convert PNG image format to JPG | |
${YELLOW}./imgopt.sh ~/project/original-images --pngtojpg Y${RESTORE} | |
# Convert JPG image format to PNG | |
${YELLOW}./imgopt.sh ~/project/original-images --jpgtopng Y${RESTORE} | |
# Convert (PNG, JPG) image format to WebP | |
${YELLOW}./imgopt.sh ~/project/original-images --towebp Y${RESTORE} | |
EOF | |
exit 0 | |
fi | |
param_dir_src=$1 | |
w=${w:-0} | |
h=${h:-0} | |
wh=${wh:-0} | |
pngQualityQ=${pngQualityQ:-20-50} | |
pngQualityX=${pngQualityX:-3} | |
name=${name:-} | |
saveversion=${saveversion:-} | |
heictojpg=${heictojpg:-} | |
pngtojpg=${pngtojpg:-} | |
jpgtopng=${jpgtopng:-} | |
towebp=${towebp:-} | |
webpQ=${webpQ:-75} | |
webpAlphaQ=${webpAlphaQ:-100} | |
containsElement() { | |
local e match="$1" | |
shift | |
for e; do [[ "$e" == "$match" ]] && return 0; done | |
return 1 | |
} | |
okparams=("--w" "--h" "--wh" "--pngQualityQ" "--pngQualityX" "--webpQ" "--webpAlphaQ" "--name" "--saveversion" "--heictojpg" "--pngtojpg" "--jpgtopng" "--towebp") | |
while [ $# -gt 0 ]; do | |
if [[ $1 == *"--"* ]]; then | |
param="${1/--/}" | |
declare "$param"="$2" | |
if ! containsElement "--${param}" "${okparams[@]}"; then | |
echo "Valid Parameters:" | |
validParams | |
cat <<-EOF | |
${RED}'--${param}' is not a valid parameter.${RESTORE} | |
(${RED}Script Exit [2]${RESTORE}) | |
EOF | |
exit | |
fi | |
fi | |
shift | |
done | |
# MAIN PROCEDURES | |
echo "Directory parameter value: $param_dir_src" | |
echo "cd $param_dir_src" | |
cd "$param_dir_src" | |
dir_src=$(pwd -P) | |
dir_out="_imgopt" | |
if [ -n "$saveversion" ]; then | |
dir_out="_imgopt-$(timestamp)" | |
fi | |
[[ -d "$dir_out" ]] || mkdir "$dir_out" | |
echo "Output dir: ${YELLOW}$dir_out${RESTORE}" | |
if [ -n "$name" ]; then | |
# Only copy/process images with filename containing substring --name param | |
while IFS= read -r -d '' file; do | |
if [[ "$file" =~ $name ]]; then | |
echo "${file} contains $name" | |
cp "$file" "$dir_out" | |
fi | |
done < <(find "$dir_src" -type f -maxdepth 1 -print0 -iname '*.png' -or -iname '*.jpeg' -or -iname '*.jpg' -or -iname '*.gif' -or -iname '*.heic') | |
else | |
# Copy/process all images | |
set +e # disables set -o errexit | |
cp ./*.{png,jpeg,jpg,gif,heic,HEIC} "$dir_out" 2>/dev/null | |
set -e # reenables set -o errexit | |
fi | |
cd "$dir_out" | |
if [[ (($w -gt 0)) && ((${h} -gt 0)) ]]; then | |
cat <<-EOF | |
(${RED}Params --w and --h cannot be used together. Use --wh with one pixel value to have both height and width have a max size.${RESTORE}) | |
Example: ./imgopt.sh --wh 500 | |
(${RED}Script Exit [3]${RESTORE}) | |
EOF | |
exit | |
fi | |
if [ -n "$heictojpg" ]; then | |
echo "" | |
echo "${YELLOW}Convert HEIC files to JPG:${RESTORE}" | |
# filename=$("$1" | sed 's/.HEIC//') | |
find "$dir_src/$dir_out" -iname '*.HEIC' -exec sh -c ' | |
filename=${1//".HEIC"/""} | |
filename=${filename//".heic"/""} | |
echo "sips --setProperty format jpeg $1 --out ${filename}.jpg" | |
sips --setProperty format jpeg "$1" --out "${filename}.jpg" | |
rm "$1" | |
' sh {} \; | |
fi | |
if [ -n "$pngtojpg" ]; then | |
echo "" | |
echo "${YELLOW}Convert PNG files to JPG:${RESTORE}" | |
find "$dir_src/$dir_out" -iname '*.png' -exec sh -c ' | |
filename=${1//".png"/""} | |
filename=${filename//".png"/""} | |
echo "sips --setProperty format jpeg $1 --out ${filename}.jpg" | |
sips --setProperty format jpeg "$1" --out "${filename}.jpg" | |
rm "$1" | |
' sh {} \; | |
fi | |
if [ -n "$jpgtopng" ]; then | |
echo "" | |
echo "${YELLOW}Convert JPG files to PNG:${RESTORE}" | |
find "$dir_src/$dir_out" -iname '*.jpg' -exec sh -c ' | |
filename=${1//".jpg"/""} | |
filename=${filename//".jpg"/""} | |
echo "sips --setProperty format png $1 --out ${filename}.png" | |
sips --setProperty format png "$1" --out "${filename}.png" | |
rm "$1" | |
' sh {} \; | |
find "$dir_src/$dir_out" -iname '*.jpeg' -exec sh -c ' | |
filename=${1//".jpeg"/""} | |
filename=${filename//".jpeg"/""} | |
echo "sips --setProperty format png $1 --out ${filename}.png" | |
sips --setProperty format png "$1" --out "${filename}.png" | |
rm "$1" | |
' sh {} \; | |
fi | |
if [ -n "$towebp" ]; then | |
echo "" | |
echo "${YELLOW}Convert image files to WebP:${RESTORE}" | |
find "$dir_src/$dir_out" -iname '*.jpg' -exec sh -c ' | |
filename=${1//".jpg"/""} | |
filename=${filename//".jpg"/""} | |
echo "cwebp $1 -q $2 -alpha_q $3 -o $filename.webp" | |
cwebp "$1" -q $2 -alpha_q $3 -o "${filename}.webp" | |
rm "$1" | |
' sh {} $webpQ $webpAlphaQ \; | |
find "$dir_src/$dir_out" -iname '*.jpeg' -exec sh -c ' | |
filename=${1//".jpeg"/""} | |
filename=${filename//".jpeg"/""} | |
echo "cwebp $1 -q $2 -alpha_q $3 -alpha_q $3 -o $filename.webp" | |
cwebp "$1" -q $2 -alpha_q $3 -o "${filename}.webp" | |
rm "$1" | |
' sh {} $webpQ $webpAlphaQ \; | |
find "$dir_src/$dir_out" -iname '*.png' -exec sh -c ' | |
filename=${1//".png"/""} | |
filename=${filename//".png"/""} | |
echo "cwebp $1 -q $2 -alpha_q $3 -o $filename.webp" | |
cwebp "$1" -q $2 -alpha_q $3 -o "${filename}.webp" | |
rm "$1" | |
' sh {} $webpQ $webpAlphaQ \; | |
fi | |
if [[ $wh -gt 0 ]]; then | |
cat <<-EOF | |
${YELLOW}Resize Files:${RESTORE} | |
${YELLOW}sips -Z ${wh} ./*${RESTORE} | |
EOF | |
sips -Z "${wh}" ./* | |
elif [[ $w -gt 0 ]]; then | |
cat <<-EOF | |
${YELLOW}Resize Files:${RESTORE} | |
${YELLOW}sips --resampleWidth ${w} ./*${RESTORE} | |
EOF | |
sips --resampleWidth "${w}" ./* | |
elif [[ $h -gt 0 ]]; then | |
cat <<-EOF | |
${YELLOW}Resize Files:${RESTORE} | |
${YELLOW}sips --resampleHeight ${h} ./*${RESTORE} | |
EOF | |
sips --resampleHeight "${h}" ./* | |
else | |
echo "" | |
echo "${GREEN}No size change.${RESTORE}" | |
fi | |
echo "" | |
echo "${YELLOW}Process with ImageOptim: JPEGs${RESTORE}" | |
find "$dir_src/$dir_out" -name '*.jpeg' -exec sh -c ' | |
echo "${1}" | |
open -a ImageOptim.app "$1" | |
' sh {} \; | |
find "$dir_src/$dir_out" -name '*.jpg' -exec sh -c ' | |
echo "${1}" | |
open -a ImageOptim.app "$1" | |
' sh {} \; | |
echo "" | |
echo "${YELLOW}Process with ImageOptim: GIFs${RESTORE}" | |
find "$dir_src/$dir_out" -name '*.gif' -exec sh -c ' | |
echo "${1}" | |
open -a ImageOptim.app "$1" | |
' sh {} \; | |
echo "" | |
echo "${YELLOW}Process with pngquant & oxipng: PNGs${RESTORE}" | |
count=0 | |
while IFS= read -r -d '' file; do | |
((count++)) | |
echo "${file}" | |
pngquant --ext=.png --force --quality "$pngQualityQ" "$file" | |
oxipng -o "$pngQualityX" -i 0 --strip safe "$file" | |
done < <(find "$dir_src/$dir_out" -name '*.png' -print0) | |
echo "Processed PNG Count: (${count})" | |
# Open output dir in Finder | |
open . | |
# Return source dir | |
cd .. | |
echo "${GREEN}Done.${RESTORE}" |
Add feature: --saveversion Y (optional parameter):
Save optimize run in a timestamped directory 'version' (_imgopt-YYYY-mm-dd-H-M-S)
Add feature: ----heictojpg Y (optional parameter):
Convert HEIC images to JPEG
Add feature: ----pngtojpg Y (optional parameter):
Convert PNG images to JPEG
Add feature: ----jpgtopng Y (optional parameter):
Convert JPEG images to PNG
Add feature: --towebp Y (optional parameter):
Convert (JPEG, PNG) images to webP
Related Optional parameter: Quality
--webpQ 90
Related Optional parameter: Alpha Channel Quality
--webpAlphaQ 65
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add feature: --name (optional parameter):
A filter to select only filenames to optimize that contain substring.