Batch Resizing Using Command Line and ImageMagick

About The Author

Vlad Gerasimov is a digital artist from Russia, most known by his wallpaper project, Vladstudio. In 1998 Vlad started to design user interfaces for web sites … More about Vlad ↬

Email Newsletter

Weekly tips on front-end & UX.
Trusted by 200,000+ folks.

Vlad Gerasimov used Photoshop actions to save multiple sizes from a source file, but it quickly became a nightmare to maintain. He found a solution: a command-line image manipulation program. Dive into this post to learn more!

If you deal with images, sooner or later you will want to automate the repeating process of saving different sizes from one source image.

If you own Adobe Photoshop and do not save too many output sizes, Photoshop actions are probably quite enough for your needs. However, keeping a Photoshop action up-to-date is quite painful — change a source folder, and you’re screwed.

As you probably noticed in Smashing Magazine’s monthly desktop wallpaper series, especially if you work on wallpapers, preparing them for a plethora of desktop resolutions is quite a task. On my own wallpapers website (Vladstudio), I generate more than 300 JPEG files for each wallpaper! I want my art to reach as many devices as possible, which means I need to publish my wallpapers in as many sizes as I can support. On the other hand, I do not want to spend the rest of my life resizing my artworks — I’d rather draw new ones!

Long ago, I used Photoshop actions to save multiple sizes from a source file, but it quickly became a nightmare to maintain. Photoshop provides a more powerful tool — scripting language (it’s not the same as “actions”, though the concept is similar). When writing a script, you use a programming language to tell Photoshop what to do (compared to actions, where Photoshop records what you do with mouse and keyboard). However, it’s not easy to learn at all, and I also wanted to completely remove Photoshop from process.

The solution I found is ImageMagick — a command-line image manipulation program, available for Windows, Mac and Linux. Unless you are a server administrator, you probably never thought of resizing images using command line. However, after switching to command line, I never looked back at Photoshop for batch resizing.

Using command line is:

  • Easy to expand — adding new sizes takes only one line of code.
  • Easy to maintain — changing folders is as easy as changing one variable.
  • Portable — I can save images on my server as well as my main computer.
Terminal in Python
The bash script in use in Terminal. Large view

If you have never worked with command line before, it all might look scary at first. However, with a bit of patience, you will find that it’s actually a very powerful tool.

Below is the bash script I wrote, simplified for more universal usage. It takes the following parameters (from either you or from default values):

  • Set of output sizes.
  • Source file.
  • Destination path (path must include the % symbol, which will be replaced by output size, i.e. “800x600”).
  • Optional signature image.
Batch Resize Script
The result after the script was applied. Large view.

Then it resizes source image into every output size, applying a signature. For a better understanding, please look through the comments in source code.

The script requires:

  • imagemagick — to install. Follow instructions for your OS.
  • python — I’m sure your PC already has it installed!

Tested in Ubuntu and Mac OS X. How to use:

  • Save resize.sh to any folder on your computer.
  • Edit the file and set default values for variables (optional).
  • Open Terminal, navigate to this folder, and execute the following command: bash resize.sh.
  • Follow instructions, sit back and enjoy!

The Bash Script

Here is the full bash script. Of course, you can also download it from GitHub.

#!/bin/bash

# Edit the settings below:

# Output sizes - 
# Please note the format: each size is wrapped with quotes, width and height are separated with space.
output=("800 600" "1024 768" "1152 864" "1280 960" "1280 1024" "1400 1050" "1440 960" "1600 1200" "1920 1440" "1024 600" "1366 768" "1440 900" "1600 900" "1680 1050" "1280 800" "1920 1080" "1920 1200" "2560 1440" "2560 1600" "2880 1800")

# If you frequently use the same source file (e.g. "~/Desktop/src.jpg"), 
# set it in "default_src"
default_src="src.jpg";

# If you frequently use the same destination
# (e.g. "~/Desktop/Some_folder/%.jpg"), set it in "default_dst"
# Destination must include "%", it will be replaced by output size, e.g. "800x600"
default_dst="%.jpg";

# Add signature? 
default_sign=’y’

# If you frequently use the same signature file (e.g. "~/Desktop/sig.png"), 
# set it in "default_sig"
default_sig="sig.png";

# Gravity is for cropping left/right edges for different proportions (center, east, west)
default_gravity="center"

# Output JPG quality: maximum is 100 (recommended)
quality=100

# ======
# Do not edit below.
# ======
# Welcome to Smashing resizer!
# Written by Vlad Gerasimov from https://www.vladstudio.com
# 
# This script takes one "source" image and saves it in different sizes.
# 
# Requires:
# * imagemagick - https://www.imagemagick.org/
# * python - I’m sure your PC already has it!

# Unfortunately, bash is awful at math operations.
# We’ll create a simple function that handles math for us.
# Example: $(math 2 * 2)

function math(){
  echo $(python -c "from __future__ import division; print $@")
}

# To make our script short and nice, here is the "save()" function.
# We’ll use it to save each size.

function save(){

  # read target width and height from function parameters
  local dst_w=${1}
  local dst_h=${2}

  # calculate ratio 
  local ratio=$(math $dst_w/$dst_h);

  # calculate "intermediate" width and height
  local inter_w=$(math "int(round($src_h*$ratio))")
  local inter_h=${src_h}

  # calculate best sharpness
  local sharp=$(math "round((1/$ratio)/4, 2)")

  # which size we’re saving now
  local size="${dst_w}x${dst_h}"
  echo "Saving ${size}..."

  #crop intermediate image (with target ratio)
  convert ${src} -gravity ${gravity} -crop ${inter_w}x${inter_h}+0+0 +repage temp.psd

  # apply signature
  if [ "${sign}" == "y" ]; then
  convert temp.psd ${sig} -gravity southeast -geometry ${sig_w}x${sig_h}+24+48 -composite temp.psd
  fi

  # final convert! resize, sharpen, save
  convert temp.psd -interpolate bicubic -filter Lagrange -resize ${dst_w}x${dst_h} -unsharp 0x${sharp} +repage -density 72x72 +repage -quality ${quality} ${dst/%/${size}}

}

# Ask for source image, or use default value
echo "Enter path to source image, or hit Enter to keep default value (${default_src}): "
read src
src=${src:-${default_src}}

# ask for destination path, or use default value
echo "Enter destination path, or hit Enter to keep default value (${default_dst})."
echo "must include % symbol, it will be replaced by output size, e.g. ’800x600’"
read dst
dst=${dst:-${default_dst}}

# Ask for signature image, or use default value
echo "Apply signature? (hit Enter for ’yes’ or type ’n’ for ’no’) "
read sign
sign=${sign:-${default_sign}}

# Ask for signature image, or use default value
echo "Enter path to signature image, or hit Enter to keep default value (${default_sig}): "
read sig
sig=${sig:-${default_sig}}

# ask for gravity, or use default value
echo "Enter gravity for cropping left/right edges (center, east, west), or hit Enter to keep default value (${default_gravity}): "
read gravity
gravity=${gravity:-${default_gravity}}

# detect source image width and height
src_w=$(identify -format "%w" "${src}")
src_h=$(identify -format "%h" "${src}")

# detect signature width and height
sig_w=$(identify -format "%w" "${sig}")
sig_h=$(identify -format "%h" "${sig}")

# loop throught output sizes and save each size
for i in "${output[@]}"
do
  save ${i}
done

# Delete temporary file
rm temp.psd

# Done!
echo "Done!"

Please let me know if you have any suggestions for improvements. Or perhaps you use a different technique to batch resize your images? Please share your thoughts, opinions and ideas in the comments section below!

Further Reading

Smashing Editorial (jvb, vf, mrn)