Fortes


Letterbox Blur with ImageMagick

Putting round pegs into square image holes

Snowy airport gate

Snowy airport gate Denver, Colorado

Web designers (and pre-2015 Instagram users) have all experienced tyrannical enforcement of square crops for images. If you’ve got an image with a non-square aspect ratio, you have two reasonable choices:

  1. Crop the image to a 1:1 aspect ratio: Fairly easy to do manually. When doing this automatically, the naïve technique is to crop from the center, but there are some clever heuristics out there to get better results.
  2. Letterbox/Pillarbox: Keep the entire image visible, filling the excess space by surrounding the image with (typically) black bars.

(Of course, you could stretch the image to force a new aspect ratio, but it’s pretty much guaranteed to look bad)

The terms “Letterbox” and “Pillarbox” come from the film world, as they’ve been dealing with the issue forever. With the increased frequency of vertical video, a recent technique has been to replace the traditional black bars with a stretched and blurred version of the video, which creates a neat effect. There doesn’t seem to be a standard name for the blurred letterbox effect, though some people call it “blanking fill” since that’s the name used in DaVinci Resolve.

It’s possible to do this effect purely with CSS (an exercise left to the reader), but due to reasons beyond my control, I recently had a situation where using CSS wasn’t an option so once again ImageMagick comes to the rescue. Here’s the command I used:

convert image.jpg \
  \( \
    -clone 0 \
    -blur 0x20 \
    -resize 800x800\! \
    -modulate 80,50,100 \
  \)
  +swap \
  -gravity center -compose over -composite

There’s no good way to put comments into a multi-line bash command, so here’s what the command does:

  • Make a copy of the original image
  • Blur by 20 pixels
  • Stretch to force a square aspect ratio
  • 80% brightness, 50% saturation
  • Center the original image on top of the blurred copy

Here’s the output for a landscape and a portrait image

Pillarbox blur on a portrait image
Letterbox blur on a landscape image

To convert a bunch of images in bulk, feel free to use this bash script which is, as always, of questionable quality:

#!/usr/bin/env bash
#
# Create a square image, letter/pillarbox blur effect if aspect ratio not already square

get_largest_dimension() {
  local dimensions
  dimensions=$(identify -format "%w\n%h" "$@" | sort --numeric-sort --reverse --unique)

  if [[ $(echo "${dimensions}" | wc -l) == "1" ]]; then
    echo 'square'
  else
    echo "$dimensions" | head -n 1
  fi
}

main() {
  for filepath in "$@"; do
    if [[ ! -r "${filepath}" ]]; then
      >&2 echo "Can't read file ${filepath}, skipping"
      continue
    fi

    local dim
    dim=$(get_largest_dimension "${filepath}")

    local dest
    local ext
    local filename
    filename=$(basename "${filepath}")
    ext=${filename##*.}

    dest="$(dirname "${filepath}")/$(basename -s ".${ext}" "${filename}")-square.${ext}"

    if [[ "$dim" == "square" ]]; then
      echo "$(basename "${filepath}") already square, linking to $(basename "${dest}")"
      # Link file so we don't take up excess disk space
      ln "${filepath}" "${dest}"
      continue
    fi

    echo "Converting $filepath into ${dim}x${dim} square"

    convert "${filepath}" \
      \( -clone 0 -blur 0x20 -resize "${dim}x${dim}"\! -modulate 80,50,100 \) \
      +swap -gravity center -compose over -composite "$dest"
    echo "Saved $dest"
  done
}

main "$@"