cgxytf
cgxytf

Reputation: 431

Create multi-panel figure using PNG/JPEG images

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.

Upvotes: 5

Views: 5678

Answers (4)

Evan M
Evan M

Reputation: 350

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".

Upvotes: 2

user5249203
user5249203

Reputation: 4648

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
      )

Upvotes: 2

Billy34
Billy34

Reputation: 2174

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")

Upvotes: 2

davidnortes
davidnortes

Reputation: 922

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.

Upvotes: 5

Related Questions