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

说明/背景:

我正在使用R中的一些代码,这些代码将Spotify艺术家图像显示为圆形,而不是默认的矩形/正方形。我找不到在R中裁切图像的任何程序包或命令,尤其是裁切成圆形,因此我编写了自己的函数circ,该函数读取3维(或4维)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值的一半而不是全部,并在整个圆上镜像?我可以使用实际的裁剪功能吗?任何帮助都将不胜感激!

编辑以添加一些复制粘贴运行的代码(感谢@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)

最佳答案

如果您对数组外的点使用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)

关于r - 裁剪图像以圆形的最有效方法(以R为单位)?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40066806/

10-12 17:35