TL; DR: Каков наиболее эффективный способ обрезать прямоугольное изображение на круг?Самый эффективный способ обрезать изображение до круга (в R)?
Объяснение/Общие сведения:
Я работаю на некоторый код в R, который будет отображать Spotify художника образы как круги вместо rectanges по умолчанию/квадратов. Я не мог найти никаких пакетов или команд, которые обрезают изображения в R, особенно в круге, поэтому я написал свою собственную функцию, circ
, которая читает 3-мерные (или 4-мерные) RGB (A) массивы и выдает их на circle, используя the parametric equation of a circle, чтобы определить значения x для каждого уникального y. Вот мой psuedocode:
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, а не всех, и зеркало по кругу? Есть ли фактическая функция обрезки, которую я мог бы использовать вместо этого? Любая помощь очень ценится!
Edited добавить код копипаст перспективе (благодаря @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)
Вы можете обрезать с помощью Imager :: IMSUB, но он принимает только прямоугольные аргументы. Вам придется придерживаться того, что вы делаете, но он вырезает один шаг вашей эвристики. – shayaa
Не следует ли использовать реальный инструмент обработки изображений, а не пытаться взломать R, как это? –
[i only read tl; dr] есть примеры с графикой сетки, чтобы сделать растровую маску – baptiste