Consolidate multiple figures into one

Written by Yifei Wang, December 2023

Besides using Jupyter notebooks on GitHub, we also recommend Overleaf for writing up your project results. Overleaf is an online LaTeX editor which is great for formal reports. It is better than PowerPoint for keeping track of daily updates to communicate with your colleagues, with a future manuscript in mind that you can shape this document to get there. However, adding lots of figures can be a bit more work than PowerPoint. A LaTeX document with many figures can also be long and takes lots of time to render.

To make it easier to add many figures to LaTeX document for informal documents, you can use ImageMagick to preprocess multiple figures into single PNG file, then insert this one file to LaTeX.

Step 1: Generate Figures in PDF Format and Save to Disk

From R, save figures in PDF format on your disk as separate files, for later processing.

Step 2: Combine PDFs into a single PNG Using ImageMagick

  • First, install ImageMagick using micromamba or conda.
conda install -c conda-forge imagemagick

or,

micromamba install -c conda-forge imagemagick
  • Once ImageMagick is installed, use the convert command to combine your PDF files into a single PNG.
convert file1.pdf file2.pdf file3.pdf +append output.png

You can specify the resolution of the resulting combined figure using -density switch for example:

convert -density 200 file1.pdf file2.pdf file3.pdf +append output.png

For simple concatenation in a single row or column, the append option of the convert tool is sufficient. Note that -append concatenates all images vertically, and +append concatenates horizontally.

  • To get finer control over the layout, we would need the montage tool which is also available from ImageMagick. For example if you want to create a $3\times2$ grid layout:
montage file1.pdf file2.pdf file3.pdf file4.pdf file5.pdf file6.pdf -tile 3x2 -geometry +0+0 miff:- | convert - -density 500 output.png

-tilecontrols the layout to be applied.tile follows the format columns$\times$rows, but either side may be missing and montage will figure out how to meet the constraints. The intermediate image is passed as a MIFF (Magick Image File Format) which preserves all detail and metadata and quality; convert will further process it to render it well.

Step 3: Add panel labels to subpanels (optional)

Here is a more sophisticated script and documentation written by ChatGPT4 to annotate and add panel labels to figures before combining,

#!/bin/bash

# Usage: ./montage_script.sh --layout <tile_layout> --spacing <spacing> --density <density> <output_image> <image1> <image2> ...

layout=""
spacing=""
density=""
output_image=""
declare -a input_images=()

while [[ "$#" -gt 0 ]]; do
    case "$1" in
        --layout) layout="$2"; shift 2;;
        --spacing) spacing="$2"; shift 2;;
        --density) density="$2"; shift 2;;
        *) 
          if [ -z "$output_image" ]; then
              output_image="$1"
          else
              input_images+=("$1")
          fi
          shift;;
    esac
done

# Check if required parameters are set
if [ -z "$layout" ] || [ -z "$spacing" ] || [ -z "$density" ] || [ -z "$output_image" ] || [ ${#input_images[@]} -eq 0 ]; then
    echo "Error: Missing required parameters."
    echo "Usage: $0 --layout <tile_layout> --spacing <spacing> --density <density> <output_image> <image1> <image2> ..."
    exit 1
fi

label="A"
annotated_images=()

# Function to increment a character
increment_char() {
    printf "\\$(printf '%03o' $(( $(printf '%d' "'$1") + 1 )))"
}

# Annotate each image
for img in "${input_images[@]}"; do
    annotated_img="annotated_${img}"
    convert "$img" -gravity NorthWest -pointsize 40 -weight Bold -annotate +10+10 "$label" "$annotated_img"
    annotated_images+=("$annotated_img")
    label=$(increment_char "$label")
done

# Combine the images using montage and convert
montage "${annotated_images[@]}" -tile "$layout" -geometry "$spacing" miff:- | convert - -density "$density" "$output_image"

# Clean up annotated images
for img in "${annotated_images[@]}"; do
    rm "$img"
done

How to Use This Script:

  1. Save the script as montage_script.sh.
  2. Make it executable: chmod +x montage_script.sh.
  3. Run the script: ./montage_script.sh --layout 3x2 --spacing +0+0 --density 200 output.png image1.jpg image2.jpg image3.jpg image4.jpg.

This script now accepts --layout, --spacing, and --density as parameters, followed by the output file name and the list of input images. The montage is created as per your provided template, and the script handles labeling and cleanup as before.

For example:

./montage_script.sh --layout 2x1 --spacing +1+1 --density 500 colocboost_overview.png colocboost_flowchart.jpg colocboost_hybrid_data.png

This is a very rough script with many hard-coded values. You should change it for your project as you see fit.

Step 4: Insert Figures on Overleaf

Use the LaTeX command \includegraphics to include the image in your Overleaf document, for example:

% Inserting the combined PNG image
\begin{figure}[ht]
    \centering
    \includegraphics[width=\textwidth]{output.png} 
    \caption{Combined image of the figures}
    \label{fig:combinedimage}
\end{figure}

If you use the document template from this repo (which is the case for some of our lab members) you can use these customized LaTeX syntax,

\generateFig[optionalRotationDegrees]{figCodeName}{figureNamePath}{size:propOfTextwidth}
    {\small Bold text main caption.}
    {Smaller normal text caption}
\figCodeName

You can (and maybe should) also upload the original PDF file into the Overleaf repository to keep a copy of high resolution figure that might be useful down the road.