Search code examples
rimageclip

Crop image from cowplot lines as mask


I would like to clip an image by an irregular mask, but do not know which libraries to use or if is possible using magick with cowplot.

Let's say I have an image downloaded into R via the magick library:

library(magick)
#> Linking to ImageMagick 6.9.12.3
#> Enabled features: cairo, fontconfig, freetype, heic, lcms, pango, raw, rsvg, webp
#> Disabled features: fftw, ghostscript, x11
library(cowplot)

tdir=tempdir()
url = "https://upload.wikimedia.org/wikipedia/commons/b/b3/USA-NYC-Empire_State1.JPG"
download.file(url,destfile = file.path(tdir,"/ESB.jpg"))
#if(file.exists(paste(tdir,"/ESB.jpg",sep=""))==FALSE){
#  download.file(url, destfile = file.path(tdir,"ESB.jpg"))}

ESB = image_read(path = paste(tdir,"/ESB.jpg",sep=""))
ESB2 = image_resize(ESB, "500x500")

I can use ggdraw() from the cowplot package to draw_lines on top of this image, like so:

ggdraw()+
  draw_image(ESB2)+
  draw_line(x=c(.5,.285),y=c(.9,.1),col='red')+
  draw_line(x=c(.5,.715),y=c(.9,.1),col='red')+
  draw_line(x=c(.715,.285),y=c(.1,.1),col='red')

But there is no way to clip the image area outside of these lines. I have seen other Stack Exchange posts that clip on simpler polygons, like circles. Is there a way to clip an image in such a way? I am open to other libraries to solve the problem.


Solution

  • As of R 4.1, we can define clipping masks in grid graphics. Convert your magick image to a rasterGrob and draw it in a viewport that has a mask made from your triangle co-ordinates. The following is a full reprex:

    library(magick)
    #> Linking to ImageMagick 6.9.12.3
    #> Enabled features: cairo, freetype, fftw, ghostscript, heic, lcms, pango, raw, rsvg, webp
    #> Disabled features: fontconfig, x11
    library(grid)
    
    url <- paste0("https://upload.wikimedia.org/wikipedia/commons/",
                  "b/b3/USA-NYC-Empire_State1.JPG")
    
    ESB <- image_read(url)
    ESB2 <- image_resize(ESB, "500x500")
    ESB_grob <- rasterGrob(ESB2)
    
    mask <- as.mask(polygonGrob(x = c(0.285, 0.5, 0.715, 0.285),
                                y = c(0.1, 0.9, 0.1, 0.1)))
    
    grid.newpage()
    pushViewport(viewport(mask = mask))
    grid.draw(ESB_grob)
    

    enter image description here

    Created on 2022-10-13 with reprex v2.0.2