diff --git a/README.md b/README.md index 9dfe6a4..bcc01dc 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,11 @@ kill every wine process including processes hanging ### webptogif.sh convert animated .webp at $1 (path) to a gif +### image_compress.sh +Usage: `./image_compress.sh ` +Compress an image until it's below a specified target size. Output format is JPEG, regardless of the input format. +(requires ImageMagick) + ### convert_md_to_pdf.sh (need to be used with listings-setup.tex in the same folder, xelatex and pandoc are required) convert infile.md in the current folder to a pdf file (outfile.pdf) with code blocks in red boxes. diff --git a/image_compress.sh b/image_compress.sh new file mode 100755 index 0000000..938242d --- /dev/null +++ b/image_compress.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +input="$1" +output="$2" +target_size=$3 + +if [[ -z "$input" || -z "$output" || -z "$target_size" ]]; then + echo "Usage: $0 input output.jpg " + exit 1 +fi + +compress_and_get_size() { + local q=$1 + magick "$work" -strip -interlace Plane -quality "$q" "$output" + stat -c%s "$output" +} + + +work=$(mktemp --suffix=.jpg) +cp "$input" "$work" + +magick "$input" \ + -auto-orient \ + -colorspace sRGB \ + -strip \ + -background white -alpha remove -alpha off \ + "$work" + +echo "Trying to fit below $target_size bytes" + +while true; do + + # Binary search + low=1 + high=95 + best_q=1 + + while (( low <= high )); do + mid=$(((low + high) / 2)) + size=$(compress_and_get_size "$mid") + + echo " Quality $mid -> $size bytes" + + best_size=$size + + if (( size <= target_size )); then + best_q=$mid + low=$((mid + 1)) + else + high=$((mid - 1)) + fi + done + + echo "Best quality this round: $best_q ($best_size bytes)" + + if (( best_size <= target_size )); then + echo "DONE: $output is $best_size bytes" + break + fi + + echo " Reducing resolution" + tmp=$(mktemp --suffix=.jpg) + + magick "$work" -resize 75% -quality 85 "$tmp" + mv "$tmp" "$work" +done + +rm -f "$work"