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

时间:2016-10-16 04:24:01

标签: r crop

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

说明/背景:

我正在研究R中的一些代码,它会将Spotify艺术家图像显示为圆圈而不是默认的矩形/正方形。我找不到在R中裁剪图像的任何包或命令,特别是圆圈,所以我编写了自己的函数circ,它读取三维(或四维)RGB(A)数组和使用the parametric equation of a circle将它们裁剪成圆圈,以确定每个唯一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

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

有没有办法可以检查一半的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)

修改版(感谢@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)

使用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)

2 个答案:

答案 0 :(得分:5)

我不知道“效率”,但我不会在这里重新发明轮子。与@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))

现在

img

enter image description here

mask

enter image description here

image_composite(mask, img, "plus") 

enter image description here

image_composite(mask, img, "minus") 

enter image description here

其他一些composite operators

# 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()
}

答案 1 :(得分:2)

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

要执行此操作,请使用

替换功能的第二部分
  # 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)