裁剪图像到圆圈的最有效方法(在R中)?

Dan*_*iel 7 r crop

TL; DR:将矩形图像裁剪成圆圈的最有效方法是什么?

说明/背景:

我正在研究R中的一些代码,它会将Spotify艺术家图像显示为圆圈而不是默认的矩形/正方形.我找不到在R中裁剪图像的任何包或命令,特别是圆圈,所以我编写了自己的函数circ,它读取三维(或四维)RGB(A)数组并将它们裁剪成一个圆圈使用圆的参数方程来确定每个唯一y的x值.这是我的伪代码:

Given an RGB(A) array:
    Find the center of the image, radius = min(x coord, y coord)
    Pre-crop the image to a square of dimensions 2r x 2r
    For every unique y value:
        Determine the x coordinates on the circle
        Make pixels outside of the circle transparent
    Return the cropped image as an RGBA array
Run Code Online (Sandbox Code Playgroud)

这个功能比我之前的功能有了很大的改进,它检查了每个像素的位置,看它是在圆圈的内部还是外部,但我仍然觉得它可以进一步加速.

有没有办法可以检查一半的y值而不是所有的y值,然后镜像在圆圈上?我可以使用实际的裁剪功能吗?任何和所有的帮助非常感谢!

编辑添加一些复制粘贴运行代码(感谢@lukeA):

我的原始裁剪方法:

circ = function(a){
  # First part of the function finds the radius of the circle and crops the image accordingly
  xc = floor(dim(a[,,1])[2]/2)  # X coordinate of the center
  yc = floor(dim(a[,,1])[1]/2)  # Y coordinate of the center
  r = min(xc, yc) - 1  # Radius is the smaller of the two -1 to avoid reading nonexistent data
  ma = array(data = c(a[,,1][(yc-r):(yc+r),(xc-r):(xc+r)],  # Read in the cropped image
                      a[,,2][(yc-r):(yc+r),(xc-r):(xc+r)],  # Of dimensions 2r x 2r, centered
                      a[,,3][(yc-r):(yc+r),(xc-r):(xc+r)],  # Around (xc, yc)
                      rep(1,length(a[,,1][(yc-r):(yc+r),(xc-r):(xc+r)]))),  # Add fourth alpha layer
             dim = c(length((yc-r):(yc+r)),length((xc-r):(xc+r)),4))

  if(yc > xc) yc = xc else if(xc > yc) xc = yc  # Re-evaluate your center for the cropped image
  xmax = dim(ma[,,1])[2]; ymax = dim(ma[,,1])[1]  # Find maximum x and y values

  # Second part of the function traces circle by the parametric eqn. and makes outside pixels transparent
  for(y in 1:ymax){  # For every y in the cropped image
    theta = asin((y - yc) / r)  # y = yc + r * sin(theta) by parametric equation for a circle
    x = xc + r * cos(theta)  # Then we can find the exact x coordinate using the same formula
    x = which.min(abs(1:xmax - x))  # Find which x in array is closest to exact coordinate
    if(!x - xc == 0 && !xmax - x == 0){  # If you're not at the "corners" of the circle
      ma[,,4][y,c(1:(xmax-x), (x+1):xmax)] = 0  # Make pixels on either side of the circle trans.
    } else if(!xmax - x == 0) ma[,,4][y,] = 0  # This line makes tops/bottoms transparent
  }
  return(ma)
} 

library(jpeg)
a = readJPEG("http://1.bp.blogspot.com/-KYvXCEvK9T4/Uyv8xyDQnTI/AAAAAAAAHFY/swaAHLS-ql0/s1600/pink-smiley-face-balls-laughing-HD-image-for-faacebook-sharing.jpg")
par(bg = "grey"); plot(1:2, type="n")  # Color background to check transparency
rasterImage(circ(a),1,1,2,2)
Run Code Online (Sandbox Code Playgroud)

修改版(感谢@dww):

dwwcirc = function(a){
  # First part of the function finds the radius of the circle and crops the image accordingly
  xc = floor(dim(a[,,1])[2]/2)  # X coordinate of the center
  yc = floor(dim(a[,,1])[1]/2)  # Y coordinate of the center
  r = min(xc, yc) - 1  # Radius is the smaller of the two -1 to avoid reading nonexistent data
  ma = array(data = c(a[,,1][(yc-r):(yc+r),(xc-r):(xc+r)],  # Read in the cropped image
                      a[,,2][(yc-r):(yc+r),(xc-r):(xc+r)],  # Of dimensions 2r x 2r, centered
                      a[,,3][(yc-r):(yc+r),(xc-r):(xc+r)],  # Around (xc, yc)
                      rep(1,length(a[,,1][(yc-r):(yc+r),(xc-r):(xc+r)]))),  # Add fourth alpha layer
             dim = c(length((yc-r):(yc+r)),length((xc-r):(xc+r)),4))

  if(yc > xc) yc = xc else if(xc > yc) xc = yc  # Re-evaluate your center for the cropped image
  xmax = dim(ma[,,1])[2]; ymax = dim(ma[,,1])[1]  # Find maximum x and y values

  x = rep(1:xmax, ymax)  # Vector containing all x values
  y = rep(1:ymax, each=xmax)  # Value containing all y values
  r2 = r^2
  ma[,,4][which(( (x-xc)^2 + (y-yc)^2 ) > r2)] = 0
  return(ma)
} 

library(jpeg)
a = readJPEG("http://1.bp.blogspot.com/-KYvXCEvK9T4/Uyv8xyDQnTI/AAAAAAAAHFY/swaAHLS-ql0/s1600/pink-smiley-face-balls-laughing-HD-image-for-faacebook-sharing.jpg")
par(bg = "grey"); plot(1:2, type="n")  # Color background to check transparency
rasterImage(dwwcirc(a),1,1,2,2)
Run Code Online (Sandbox Code Playgroud)

使用magick和plotrix的版本(感谢@lukeA和@hrbrmstr):

library(plotrix)
jpeg(tf <- tempfile(fileext = "jpeg"), 1000, 1000)
par(mar = rep(0,4), yaxs="i", xaxs = "i")
plot(0, type = "n", ylim = c(0, 1), xlim = c(0,1), axes=F, xlab=NA, ylab=NA)
draw.circle(.5,.5,.5,col="black")
dev.off()

library(magick)
img = image_read("http://1.bp.blogspot.com/-KYvXCEvK9T4/Uyv8xyDQnTI/AAAAAAAAHFY/swaAHLS-ql0/s1600/pink-smiley-face-balls-laughing-HD-image-for-faacebook-sharing.jpg")
mask = image_read(tf)
radius = min(c(image_info(img)$width, image_info(img)$height))
mask = image_scale(mask, as.character(radius))

par(bg = "grey"); plot(1:2, type="n")
rasterImage(as.raster(image_composite(image = mask, composite_image = img, operator = "plus")),1,1,2,2)
Run Code Online (Sandbox Code Playgroud)

luk*_*keA 6

我不知道"效率",但我不会在这里重新发明轮子.就像@hrbrmstr的评论中所建议的那样,您可以尝试一下magick,它可以为您提供所需的所有灵活性:

png(tf <- tempfile(fileext = ".png"), 1000, 1000)
par(mar = rep(0,4), yaxs="i", xaxs="i")
plot(0, type = "n", ylim = c(0,1), xlim=c(0,1), axes=F, xlab=NA, ylab=NA)
plotrix::draw.circle(.5,0.5,.5, col="black")
dev.off()

library(magick)
fn <- "https://www.gravatar.com/avatar/f57aba01c52e5c67696817eb87df84f2?s=328&d=identicon&r=PG&f=1"
img <- image_read(fn)
mask <- image_read(tf)
mask <- image_scale(mask, as.character(image_info(img)$width))
Run Code Online (Sandbox Code Playgroud)

现在

img
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

mask
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

image_composite(mask, img, "plus") 
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

image_composite(mask, img, "minus") 
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

其他一些复合运算符:

# https://www.imagemagick.org/Magick++/Enumerations.html#CompositeOperator
ops <- c("over", "in", "out", "atop", "xor", "plus", "minus", "add",  "difference", "multiply")
for (op in ops) {
  print(image_composite(img, mask, op))
  print(op)
  readline()
}
Run Code Online (Sandbox Code Playgroud)

  • 好的,我加入了我拥有的三种变体.运行完所有三个后,@ dww的建议运行时间最短(1.420秒),然后是你的变体(7.027秒)和我原来的(16.749秒).另外,`image_composite`版本会删除使用其他两个版本可见的灰色背景,我认为这意味着图像并没有真正裁剪成圆形,而是圆形被切成白色.也许我错误地实现了它? (2认同)

dww*_*dww 3

如果您使用圆外点的circ事实对数组执行向量化子集分配操作(而不是循环),则可以提高函数的性能。(x-xc)^2 +(y-yc)^2 > r^2

为此,请将函数的第二部分替换为

  # Second part of the function traces circle by...
  x = rep(1:xmax, ymax)
  y = rep(1:ymax, each=xmax)
  r2 = r^2
  ma[,,4][which(( (x-xc)^2 + (y-yc)^2 ) > r2)] <- 0
  return(ma)
Run Code Online (Sandbox Code Playgroud)