Create multi-panel figure using PNG/JPEG images

Viewed 2675

Problem:

I want to make a multi-panel figure using PNG or JPEG images. The images were not created in R but I want to patch them together in R to form one figure. All the images are the same size/dimensions.

What I've tried:

library(png)

img1 <- readPNG("filepath/img1.png")
img2 <- readPNG("filepath/img2.png")

library(patchwork)

patch <- img1 + img2
patch

When I run this, I get the following error:

[ reached getOption("max.print") -- omitted 3 matrix slice(s) ]

I increased the max print multiple times (to ridiculously high numbers):

options(maxprint = 1000000000000)

But still get the same error.

I then tried making each image into a ggplot (without the points) using:

library(ggplot2)

img1plot <- ggplot() + 
background_image(img1) +
theme(plot.margin = margin(t=1, l=1, r=1, b=1, unit = "cm"))

Which returns the following error:

Error in background_image(d311) : 
  could not find function "background_image"

Is there another way to patch images together in R to make a figure?

Edit:

Based on the comment from @davidnortes, I tried the following:

p1 <- ggplot2::annotation_custom(grid::rasterGrob(img1,
                                            width=ggplot2::unit(1,"npc"),
                                            height=ggplot2::unit(1,"npc")),
                           -Inf, Inf, -Inf, Inf)

p2 <- ggplot2::annotation_custom(grid::rasterGrob(img2,
                                                  width=ggplot2::unit(1,"npc"),
                                                  height=ggplot2::unit(1,"npc")),
                               -Inf, Inf, -Inf, Inf)


library(cowplot)

plots <- plot_grid(
  p1, p2,
  labels = c('A', 'B'),
  align="hv"
)
plots

I get the following warning messages and the plot doesn't form:

Warning messages:
1: In as_grob.default(plot) :
  Cannot convert object of class LayerInstanceLayerggprotogg into a grob.
2: In as_grob.default(plot) :
  Cannot convert object of class LayerInstanceLayerggprotogg into a grob.
4 Answers

I'm giving you the couple of alternatives that I Use the most:

Alternative 1: combination of ggplot2, grid and cowplot.

Each of your PNG image can be embedded in a ggplot object using:

ggplot2::ggplot() + ggplot2::annotation_custom(grid::rasterGrob(YourPNGimage,
                                                width=ggplot2::unit(1,"npc"),
                                                height=ggplot2::unit(1,"npc")),
                               -Inf, Inf, -Inf, Inf)

Then you can use cowplot::plot_grid() to arrange your plots.

Alternative 2: using magick package.

The package counts on its own functions to read images, thus we need to tweak your code a bit:

img1 <- magick::image_read("filepath/img1.png")
img2 <- magick::image_read("filepath/img2.png")

Then using functions like magick::image_append(c(img1, img2)) you can combine them. See the magick package documentation for more info.

You can also rbind image arrays. But as they are 3D (x,y,RGB) you must use abind function from abind package. along=1 to bind vertically, 2 horizontally.

Works because image have same size.

img1 <- readPNG("filepath/img1.png")
img2 <- readPNG("filepath/img2.png")
img12 <- abind::abind(img1,img2,along=1)
png::writePNG(img12,"filepath/img12.png")

You can use magick package in R to do a collage.

# read the the png files into a list
  pngfiles <-
  list.files(
    path = here::here("png_ouput_folder"),
    recursive = TRUE,
    pattern = "\\.png$",
    full.names = T
  )
 
 # read images and then create a montage
 # tile =2 , means arrange the images in 2 columns
 # geometry controls the pixel sixe, spacing between each image in the collage output. 

 magick::image_read(pngfiles) %>%
      magick::image_montage(tile = "2", geometry = "x500+10+5") %>%
      magick::image_convert("jpg") %>%
      magick::image_write(
        format = ".jpg", path = here::here(paste(name,"_collage.jpg",sep="")),
        quality = 100
      )

As others have suggested the magick package is much simpler than a low-level solutions using grobs and related. magick is powerful but IMHO the documentation is poor and very circular.

However, there is a simple solution to your question in the page for image_montage(). The most important argument is the geometry specification, which governs the spacing between the "tiles."

library(magick)
input <- rep(logo, 12)
image_montage(input, geometry = 'x100+10+10', tile = '4x3', bg = 'pink', shadow = TRUE)

To get no spacing at all, use geometry = '0x100+0+0', shadow = FALSE".

Related