spectrakit Package – Part 4: Creating a Composite Image Grid in R with makeComposite()
This is part 4 of a series on the spectrakit R package. Start
here for an introduction.
Overview
The
makeComposite() function from the spectrakit R
package creates a composite image by arranging multiple sub-images into a
structured grid with flexible control over layout, resizing, spacing and
multi-layer labeling. This function is useful when combining figures, such
as spectra, plots or image panels, into a single publication-ready output,
especially when consistent sizing and precise annotation are required. It
helps automate what would otherwise be a manual and error-prone process of
aligning images, adjusting dimensions and adding labels for reports,
presentations or scientific figures.
Syntax
makeComposite(
folder = ".",
custom_order,
rows,
cols,
spacing = 15,
resize_mode = c("none", "fit", "fill", "width", "height", "both"),
labels = list(),
label_settings = list(),
background_color = "white",
desired_width = 15,
width_unit = "cm",
ppi = 300,
output_format = "tiff",
output_folder = NULL
)
Arguments
| Argument name | Type | Description | Default value (if provided, argument is optional) |
|---|---|---|---|
| folder | Character | Path to the folder containing images | "." (working directory) |
| custom_order | Character vector | Ordered set of filenames (use NA for blank slots) | Argument is required |
| rows | Integer | Number of rows in the grid | Argument is required |
| cols | Integer | Number of columns in the grid | Argument is required |
| spacing | Integer |
Spacing (in pixels) between tiles
|
15 |
| resize_mode | Character |
Method to resize panels in the composite. One of:
|
"none" |
| labels | List of up to 4 character vectors |
Labels to apply to each panel. Each vector corresponds to one
label layer and must be the same length as the number of non-NA
images. Use empty strings "" or NULL entries to omit specific
labels
|
list() (no labels) |
| label_settings | List of named lists |
Each named list specifies styling options for a label layer.
Options include:
|
list() (default styling is used) |
| background_color | Character |
Background color used for blank tiles and borders. Use "none" for
transparency
|
"white" |
| desired_width | Numeric | Desired width of final image (in centimeters, inches or pixels) | 15 |
| width_unit | Character |
One of:
|
"cm" |
| ppi | Numeric |
Resolution (pixels per inch) for output file
|
300 |
| output_format | Character | File format for saving image. Examples: "tiff", "png", "pdf" | "tiff" |
| output_folder | Character | Path to folder where the composite image is saved. If NULL, the image is not saved and a magick image object is returned. If specified, the image is saved automatically; if ".", the image is saved in the working directory | NULL |
Return value
If
output_folder = NULL, the function returns a
magick image object. When output_folder is
specified, the composite image is written directly to disk and returned
invisibly. This function is therefore typically used in workflows where
the generated composite is saved for external use (e.g., inclusion in
reports, presentations or publications) rather than further manipulated
within R.
Practical examples
The function takes a set of specified image files and generates a composite
image grid. In this example, we generate 12 near-infrared (NIR) sample
spectra using the
NIRsoil dataset included in the prospectr package. The
code below randomly selects 12 spectra from the dataset and saves each as a
CSV file in a temporary folder on the Desktop, with named columns for
wavelength and reflectance:
# ---- Install & load package if needed ----
# install.packages("prospectr")
library(prospectr)
# ---- Load dataset ----
data(NIRsoil)
# Spectral matrix and wavelength axis
spectra_matrix <- NIRsoil$spc
wavelength <- as.numeric(colnames(spectra_matrix)) # wavelength in nm
# ---- Create TEMP folder on Desktop ----
desktop_path <- file.path(path.expand("~"), "Desktop")
temp_dir <- file.path(desktop_path, "TEMP")
if (!dir.exists(temp_dir)) {
dir.create(temp_dir, recursive = TRUE)
}
# ---- Randomly select 12 spectra ----
set.seed(260329) # for reproducibility
n_available <- nrow(spectra_matrix)
if (n_available < 12) stop("Dataset contains fewer than 12 spectra.")
selected_rows <- sample(seq_len(n_available), 12)
# ---- Save each spectrum as a CSV file ----
for (i in seq_along(selected_rows)) {
reflectance <- spectra_matrix[selected_rows[i], ]
output_df <- data.frame(
Wavelength = wavelength,
Reflectance = as.numeric(reflectance)
)
file_path <- file.path(
temp_dir,
paste0("NIRsoil_spectrum_", i, ".csv")
)
write.csv(
output_df,
file = file_path,
row.names = FALSE,
quote = FALSE
)
}
cat("12 random NIR spectra saved in:", temp_dir, "\n")
Now we can use the
plotSpectra() function (see
spectrakit Package – Part 1) to read spectral data from the 12
files, and generate and save the corresponding plots, which we will then
combine using makeComposite():
# ---- Install & load package if needed ----
# install.packages("spectrakit")
library(spectrakit)
setwd(temp_dir)
plotSpectra(
normalization = "min-max",
x_config = c(1200, 2400, 200),
x_label = "Wavelength (nm)",
y_label = "Reflectance (a.u.)",
plot_mode = "individual",
display_names = TRUE,
output_folder = "."
)
The minimal working example of the
makeComposite() function
requires specifying only the custom_order,
rows and cols arguments, along with the
output_folder argument (otherwise
the function returns the composite image), while all other
arguments use their default values:
makeComposite(custom_order = c("Spec_1.tiff", "Spec_2.tiff", "Spec_3.tiff", "Spec_4.tiff",
"Spec_5.tiff", "Spec_6.tiff", "Spec_7.tiff", "Spec_8.tiff",
"Spec_9.tiff", "Spec_10.tiff", "Spec_11.tiff", "Spec_12.tiff"),
rows = 4, cols = 3,
output_folder = ".")
This produces an image grid with 12 panels or tiles (4 rows × 3
columns), one for each plotted spectrum, and saves it as a TIFF file
with the date and time of export included in the filename:
By default, the spacing between tiles is set to 15 pixels, but it can
be adjusted using the
spacing argument.
In this example, all images have identical dimensions, so no resizing
is necessary (the default
resize_mode is
"none"). When images have different dimensions, the
resize_mode argument can be set to one of five different
methods to control how they are resized:
-
"fit"– scale panels to fit within the smallest width and height, preserving aspect ratio. -
"fill"– scale panels to fully cover the smallest width and height, preserving aspect ratio. -
"width"– resize panels to the minimum width, scaling height proportionally. -
"height"– resize panels to the minimum height, scaling width proportionally. -
"both"– resize panels to the exact minimum width and height (aspect ratio not preserved).
One of the most powerful features of this function is the ability to
add multiple labels to the tiles in the grid. For example, to add a
letter to each panel for easy referencing of sub-figures, we can use
the
labels and label_settings arguments to
control their appearance and placement:
makeComposite(custom_order = c("Spec_1.tiff", "Spec_2.tiff", "Spec_3.tiff", "Spec_4.tiff",
"Spec_5.tiff", "Spec_6.tiff", "Spec_7.tiff", "Spec_8.tiff",
"Spec_9.tiff", "Spec_10.tiff", "Spec_11.tiff", "Spec_12.tiff"),
rows = 4, cols = 3,
labels = list(c("A","B","C","D",
"E","F","G","H", # A cleaner way to do this
"I","J","K","L")), # would be: LETTERS[1:12]
label_settings = list(list(gravity = "northeast")),
output_folder = ".")
Here, we chose to apply a single label layer (although up to four
layers are possible) and leave all label settings at their default
values, except for the placement (
gravity), which we set
to the upper-right corner ("northeast"):
Finally, we can control the background color, width, resolution, and
file format of the saved image using the
background_color, desired_width,
width_unit, ppi, and
output_format arguments.
The
makeComposite() function can, of course, be used not
only for grids of spectra but for any type of images. For example, the
composite image below, created for comparing elemental maps, shows how
various arguments can be adjusted, how NA can be used for
blank slots, and how NULL entries can omit specific
labels:
makeComposite(
custom_order = c(
"VIS.png", "Pb L.png", "Pb M.png", "Ca K.png",
"P K.png", "Cu K.png", "Fe K.png", "Mn K.png",
"K K.png", NA, NA, "Hg L.png"
),
rows = 3,
cols = 4,
spacing = 12,
resize_mode = "fit", # All images resized to smallest dimensions
labels = list(
c(
"VIS", "Pb L", "Pb M", "Ca K",
"P K", "Cu K", "Fe K", "Mn K",
"K K", NULL, NULL, "Hg L"
),
c(
"a", "b", "c", "d", "e", "f",
"g", "h", "i", NULL, NULL, "j"
)
),
label_settings = list(
# Top labels
list(
size = 140,
font = "Arial",
color = "black",
boxcolor = "white",
gravity = "northwest",
location = "+15+15",
weight = 400
),
# Bottom labels
list(
size = 204,
font = "Arial",
color = "white",
gravity = "southeast",
location = "+15+15",
weight = 700
)
),
background_color = "gray",
desired_width = 6,
width_unit = "in",
ppi = 150,
output_format = "png",
output_folder = "."
)
![]() |
| Rembrandt van Rijn. Portrait of a 39-year-old Woman, 1632. The Nivaagaard Collection, Denmark. |
Quick recap
The
makeComposite() function generates a labeled image
grid from a set of images, allowing flexible control over layout,
spacing, resizing, labels, background, and output settings. Key
arguments include custom_order (the ordered image
files), rows and cols (grid dimensions),
resize_mode (to handle images of different sizes),
labels and label_settings (for adding and
customizing labels), and output_format/output_folder
(for saving the composite). A typical usage pattern involves
specifying the images and grid dimensions, adjusting spacing and
resizing, optionally adding labels, and saving the final image to a
desired format. In sum, makeComposite() streamlines the
creation of clean, publication-ready image grids with minimal code.
About the author
Gianluca Pastorelli is a Heritage Scientist (Senior Researcher) working at the National Gallery of Denmark (SMK).



Comments
Post a Comment