Optimizing images using ImageMagick, pngcrush (and ChatGPT)
Created:
Original problem - automate resizing and optimzing images
The idea was to convert screenshots into not wider than 600px and compress them into a quality that is “decent” and not too big. Maybe even monochrome if it reduces the size considerably in the manner of low<-tech magazine like Doug Belshaw has done here – HOWTO: Create radically smaller images for your minimalist blog | Open Thinkering, but his method uses GIMP and desktop tools. My intention is to use automate this command line tools and scripting. Here i’m only doing the first part, scripting for later (maybe I can use ChatGPT for that!) update: I did, see the bottom of the page.
Install dependencies on MacOS:
brew install imagemagick
brew install pngcrush
Let’s take one file and walk through various options:
$ls -l lipika-settings.png
-rw-r--r--@ 1 pradeep staff 224860 Mar 29 11:41 lipika-settings.png
$ mogrify -size 600x600 lipika-settings.png
$ls -l lipika-settings.png
-rw-r--r--@ 1 pradeep staff 191401 Mar 29 12:50 lipika-settings.png
224 to 191.
Lets use pngcrush
:
$ pngcrush -brute lipika-settings.png
Recompressing IDAT chunks in lipika-settings.png to pngout.png
Total length of data found in critical chunks = 183405
Best pngcrush method = 7 (ws 15 fm 0 zl 9 zs 0) = 134607
CPU time decode 2.916270, encode 8.311988, other 0.012039, total 11.253587 sec
$ ls -l pngout.png
-rw-r--r--@ 1 pradeep staff 142483 Mar 29 12:51 pngout.png
191 to 142
We will use -ow
to overwrite the original file
$ pngcrush -ow lipika-settings.png
We brought down the size from 224 to 142, a 36% reduction.
Using ChatGPT-3.5 to write a script to automate
The prompt
… given to OpenAI’s ChatGPT-3.5.
“write a bash script that resizes all png images in a given directory to 600x600 using imagemagick convert tool and compresses them to the maximum extent using pngcrush program crush option.”
The output
Here’s an example bash script that resizes all PNG images in a given
directory to 600x600
using ImageMagick’s
convert
tool and compresses them to the maximum extent
using pngcrush
program’s crush
option:
#!/bin/bash
# Set the maximum compression level for pngcrush
compression_level="-brute -reduce -rem alla -ow -crush"
# Loop through all PNG files in the directory and resize them
for file in *.png; do
echo "Resizing ${file}..."
convert "${file}" -resize 600x600 "${file}_resized.png"
echo "Compressing ${file}_resized.png..."
pngcrush ${compression_level} "${file}_resized.png" "${file}"
rm "${file}_resized.png"
done
echo "Done!"
In this script, we first set the maximum compression level for pngcrush by defining the compression_level variable.
Then, we use a for loop to iterate through all PNG files in the
directory. For each file, we use ImageMagick’s convert tool to resize it
to 600x600 and save the resized image as a new file with the
_resized
suffix.
Next, we use pngcrush with the maximum compression level defined by
the compression_level
variable to compress the resized
image to the maximum extent. Finally, we delete the resized image and
move on to the next file.
Once all PNG files have been resized and compressed, the script outputs a “Done!” message to indicate completion.
Update 1: Make the script friendly
Prompt
“modify the above script to take the directory as a command line input with help text”
Output
Here’s the modified script that takes the directory as a command-line input along with help text:
#!/bin/bash
# Set the maximum compression level for pngcrush
compression_level="-brute -reduce -rem alla -ow -crush"
# Define help function
help_function () {
echo "Usage: $0 [-h] [-d directory]"
echo "Resizes all PNG images in the given directory to 600x600 and compresses them to the maximum extent using pngcrush program crush option."
echo ""
echo "Options:"
echo "-h, --help Show help information"
echo "-d, --directory DIRECTORY Path to the directory containing PNG images"
echo ""
exit 1
}
# Parse command line arguments
while [ $# -gt 0 ](/-$#--gt-0-.html){.wikilink}; do
key="$1"
case $key in
-h|--help)
help_function
;;
-d|--directory)
directory="$2"
shift
shift
;;
*)
echo "Invalid argument: $1"
help_function
;;
esac
done
# Check if directory is provided
if [ -z "$directory" ](/--z-"$directory"-.html){.wikilink}; then
echo "Directory is not provided."
help_function
fi
# Check if directory exists
if [ ! -d "$directory" ](/-!--d-"$directory"-.html){.wikilink}; then
echo "Directory does not exist."
help_function
fi
# Loop through all PNG files in the directory and resize them
for file in "$directory"/*.png; do
echo "Resizing ${file}..."
convert "${file}" -resize 600x600 "${file}_resized.png"
echo "Compressing ${file}_resized.png..."
pngcrush ${compression_level} "${file}_resized.png" "${file}"
rm "${file}_resized.png"
done
echo "Done!"
In this script, we added a help_function
to print the
help text, which shows the usage of the script and the available
options. We also added a while
loop to parse the
command-line arguments using the case
statement. If the
-h
or --help
option is provided, it calls the
help_function
. If the -d
or
--directory
option is provided, it sets the directory
variable to the corresponding value.
After parsing the command-line arguments, we check if the directory
is provided and exists. If either condition is not satisfied, we call
the help_function
to print the help text.
Finally, we loop through all PNG files in the directory using
"${directory}"/*.png
as the file path. The rest of the
script is the same as the previous version.
Funnily, I had this two line script (bin/crush
) to do
the same on single files. I had forgotten about it because I don’t use
it enough.. because it is not ergonamic to “fire and forget” on a
directory full of images.
#!/bin/bash
pngcrush -q -ow $1
convert -resize 900x $1 $1
I’m naming the GPT generated script as bin/crushdir
:)