I often post images on the web but I rarely think about optimizing the files properly before uploading. Here is a solution for how to do it programmatically in the terminal.
Asking ChatGPT for help
ChatGPT is great for some things, including suggesting program code for well-known problems. However, while it came up with something that worked right from the start, it took a few iterations to come up with a solution that does what I want. I wanted it to work on all image files in all the subfolders (using a recursive search), reduce the images to maxium 1920 pixels, and optimize them without recompression.
As it turned out, ChatGPT first suggested to remove all metadata (EXIF data) from the files. That saves a little extra, but only marginally so. The problem is that it then also effectively removes orientiation information, so that many of my portrait mode photos ended up being presented in landscape mode. Therefore, I added an automatic check for orientation in the begninning of the script.
Optimizing and overwriting existing files
Beware, the following script will replace your exisiting files. So please be sure that you have a backup somewhere else if you try this yourself.
The script is based on three libraries: imagemagick, jpegoptim and optipng. These can be installed easily on Ubuntu with the following command:
sudo apt-get install imagemagick jpegoptim
The script looks like this:
#!/bin/bash
# Directory containing image files
IMAGE_DIR="/path/to/your/images"
# Verify the path and replace with your actual image directory path
echo "Processing images in directory: $IMAGE_DIR"
# Check if the directory exists
if [ ! -d "$IMAGE_DIR" ]; then
echo "The directory '$IMAGE_DIR' does not exist. Please check the path."
exit 1
fi
# Find and process all jpg, jpeg, and png files
find "$IMAGE_DIR" -type f \( -iname '*.jpg' -o -iname '*.jpeg' \) -exec bash -c '
for image; do
echo "Processing JPEG $image"
# Automatically rotate images based on EXIF orientation
jhead -autorot "$image" 2> /dev/null # Suppress errors for non-jpeg files
# Resize the image so the longest side is 1920 pixels (implied overwrite by using the same filename)
mogrify -resize "1920x1920>" "$image"
# Optimize the resized image for screen use without stripping EXIF data
jpegoptim --all-progressive --max=80 "$image"
done
' bash {} +
find "$IMAGE_DIR" -type f -iname '*.png' -exec bash -c '
for image; do
echo "Processing PNG $image"
# Optimize PNG image
optipng -o7 "$image"
done
' bash {} +
To run the script you need to save it as something like compress_images.sh and make it executable with this command:
chmod u+x compress_images.sh
Then you can run it with:
./compress_images.sh
and watch the magic happen.
Optimize and save as new files
Sometimes it is better to not overwrite files but save with an extension instead. Here is a version that does exactly that:
#!/bin/bash
# Directory containing image files
IMAGE_DIR="/path/to/your/images"
# Verify the path and replace with your actual image directory path
echo "Processing images in directory: $IMAGE_DIR"
# Check if the directory exists
if [ ! -d "$IMAGE_DIR" ]; then
echo "The directory '$IMAGE_DIR' does not exist. Please check the path."
exit 1
fi
# Find and process all jpg, jpeg files
find "$IMAGE_DIR" -type f \( -iname '*.jpg' -o -iname '*.jpeg' \) -exec bash -c '
for image; do
echo "Processing JPEG $image"
# Extracting the filename without extension
filename="${image%.*}"
extension="${image##*.}"
# Create a new file name with a suffix to indicate it has been optimized
new_image="${filename}_optimized.${extension}"
# Automatically rotate images based on EXIF orientation and create a new file
jhead -autorot -ft "$image" "$new_image"
# Resize the new image file while preserving the aspect ratio
mogrify -resize "1920x1920>" "$new_image"
# Optimize the new resized image for screen use without stripping EXIF data
jpegoptim --all-progressive --max=80 "$new_image"
done
' bash {} +
# Find and process all png files
find "$IMAGE_DIR" -type f -iname '*.png' -exec bash -c '
for image; do
echo "Processing PNG $image"
# Extracting the filename without extension
filename="${image%.*}"
extension="${image##*.}"
# Create a new file name with a suffix to indicate it has been optimized
new_image="${filename}_optimized.${extension}"
# Copy the original PNG to a new file
cp "$image" "$new_image"
# Optimize the new PNG image file
optipng -o7 "$new_image"
done
' bash {} +
Again, you need to save and make executable before running.
Conclusion
I have tested the scripts above on several different folders containing thousands of images. The optimization rates varies; I have been able to optimize a folder as much as 70% but on average it is closer to 25%. In any case, it helps reduce server space and reduces network traffic (and load times). So all in all, well worth the effort.
Happy optimization!