Сохранение вращающихся изображений в R - избегая фона и поддерживая размер
У меня есть код ниже, который основан на моем предыдущем вопросе. Когда я сохраняю изображение SaveThisPlot.png, он создает нежелательный фон и размер изображения также изменяется (он уменьшается). Как я могу сохранить изображение таким образом, чтобы повернутая часть была точно такого же размера, как и раньше (77 строк и 101 столбец), а фон не был?
library(raster)
r1 <- brick(system.file("external/rlogo.grd", package="raster"))
r1
x <- crop(r1, extent(0,ncol(r1),0,nrow(r1)))
plotRGB(x)
x1 <- 0:ncol(x)
y1 <- 0:nrow(x)
z <- matrix(1, nrow=length(x1), ncol=length(y1))
col.mat <- t(apply(matrix(rgb(getValues(x)/255), nrow=nrow(x), byrow=TRUE), 2, rev))
# Rotate 45 degrees
persp(x1, y1, z, zlim=c(0,2), theta = 20, phi = 90,
col = col.mat, scale=FALSE, border=NA, box=FALSE)
png("SaveThisPlot.png")
persp(x1, y1, z, zlim=c(0,2), theta = 20, phi = 90,
col = col.mat, scale=FALSE, border=NA, box=FALSE)
dev.off()
1 ответ
Вы можете использовать прозрачный фон, но поворот графика потребует изменения размера.
Когда вы сохраняете свой график, вы можете установить png("SaveThisPlot.png", bg="transparent")
чтобы фон не показывался. Пытаться:
png("SaveThisPlot.png", width=101 height=77, units="px", pointsize=1, bg="transparent")
Обратите внимание, что если вы попытаетесь сделать это с повернутой версией логотипа, логотип будет изменен в соответствии с размером пикселя 101 x 77 пикселей. Если вы хотите повернуть его и сохранить размер логотипа, вам придется изменить размер png, ass @RomanLuštrik, упомянутый в его комментарии. Изменение размера, если это вариант для вас, это просто вопрос тригонометрии, чтобы найти соответствующие параметры высоты и ширины.
Вы можете попробовать установить новые высоты и ширины, чтобы сохранить png, используя некоторую тригонометрию. Вот пример с 35 градусами вращения:
# hypotenuse
hypot <- sqrt((nrow(x)/2)^2 + (ncol(x)/2)^2)
# angle
angle <- asin((nrow(x)/2)/ hypot)/(pi/180)
width <- 2 * max(abs(hypot * cos((angle + 35) * pi/180)),
abs(hypot * cos((- angle + 35) * pi/180)))
height <- 2 * max(abs(hypot * sin((angle + 35) * pi/180)),
abs(hypot * sin((- angle + 35) * pi/180)))
png("SaveThisPlot.png", height=height, width=width, units="px", pointsize=1,
bg="transparent")
par(mar=c(0,0,0,0), xaxs = "i", yaxs = "i")
persp(x1, y1, z, zlim=c(0,2), theta = 35, phi = 90,
col = col.mat, scale=FALSE, border=NA, box=FALSE)
dev.off()
Общее изображение не будет 77 х 101 пикселей, но сам логотип должен быть такого же размера, хотя и немного грубым. Для более плавного изображения см. rgl
ответьте ниже, но учтите, что прозрачный фон недоступен для rgl
Альтернативный подход с использованием пакета rgl:
Другой подход к получению изображения может быть лучше, если конечной целью является сохранение размера и гладкости изображения. Вы можете попробовать rgl
(R-to-OpenGL) и persp3d
функция, в которой вы можете вращать график внутри его окна, не меняя размеры окна и не изменяя пропорции логотипа. В основном вы можете использовать то, что у вас уже есть, но цветовую матрицу необходимо изменить, чтобы работать с persp3d
:
col.mat2 <- cbind(rbind(1, col.mat), 1)
Затем установите сцену, позвоните persp3d
, поверните его и сохраните.
open3d()
rgl.pop("lights")
light3d(specular="black") # to reduce reflection
persp3d(x1,y1,z, col=col.mat2, ylab="", xlab="", zlab="",
axes=FALSE, smooth=FALSE, aspect="iso")
# Rotate it by defining the userMatrix, then save
par3d(userMatrix = rotationMatrix(0*pi/180, 0, 0, 1))
rgl.snapshot("Rlogo_0.png") # to save
par3d(userMatrix = rotationMatrix(45*pi/180, 0, 0, 1))
rgl.snapshot("Rlogo_45.png")
par3d(userMatrix = rotationMatrix(135*pi/180, 0, 0, 1))
rgl.snapshot("Rlogo_135.png")
Чтобы заставить это изображение в меньшее окно, вы можете манипулировать размером окна дисплея rgl и количеством фона, используя комбинацию windowRect
а также zoom
параметры ( см. этот вопрос и ответ, например). Тем не менее, прозрачный фон не вариант с rgl
В настоящее время.
open3d()
bg3d(color="#FF00FF") # pink background (some color not in the actual image)
rgl.pop("lights")
light3d(specular="black")
persp3d(x1,y1,z, col=col.mat2, axes=FALSE,
ylab="", xlab="", zlab="", smooth=FALSE, aspect="iso")
rgl.viewpoint(zoom=0.52)
par3d(userMatrix = rotationMatrix(0*pi/180, 0, 0, 1), windowRect=c(0,0, 101, 77))
rgl.snapshot("Rlogo_77h124w.png")
par3d(userMatrix = rotationMatrix(45*pi/180, 0, 0, 1), windowRect=c(0,0, 101, 77))
rgl.snapshot("Rlogo45_77h124w.png")
Я не смог сделать окно шириной менее 124 пикселей. Похоже, что минимальная ширина 124 пикселя для окна rgl на моем компьютере, так как я не могу сделать окно немного меньше (то есть не могу свернуть кнопки свертывания, восстановления и закрытия в верхней части окна).
Чтобы получить прозрачный фон, вы можете импортировать эти сохраненные png-файлы и присвоить альфа-значения 1 цвету фона ("#FF69B4"):
library(png)
add.alpha <- readPNG("Rlogo_77h124w.png") #Try a rotate version, too
library(abind)
add.alpha <- abind(add.alpha, matrix(1, nrow=nrow(add.alpha), ncol=ncol(add.alpha)))
add.alpha[,,4][which(rgb(add.alpha[,,1],add.alpha[,,2],add.alpha[,,3]) == "#FF00FF")] <- 0
writePNG(add.alpha, "Rlogo_77h124w_alpha.png")
Оказывается, этот метод не идеален, и у вас может получиться небольшая граница на повернутых изображениях, где цвет не совсем "#FF00FF". Вы можете обратиться к границе так или иначе, если это для вас проблема, но это может не иметь значения в зависимости от конечных целей. Попробуйте использовать add.alpha[,,4][which(add.alpha[,,1] > .9 & add.alpha[,,3] > .9 & add.alpha[,,2] < .85 )] <- 0
до writePNG
Если вы не хотите, чтобы углы изображения обрезались, как описано выше, используйте тригонометрию, указанную ранее, чтобы правильно настроить размер окна в пределах par3d
:
open3d(); bg3d(color="#FF69B4"); rgl.pop("lights"); light3d(specular="black")
persp3d(x1,y1,z, col=col.mat2, axes=FALSE,
ylab="", xlab="", zlab="", smooth=FALSE, aspect="iso")
view3d(zoom=.862)
par3d(userMatrix = rotationMatrix(-45*pi/180, 0, 0, 1), windowRect=c(0,0, width, height)) # width and height from trigonometry above
rgl.snapshot("Rlogo45_77h124w.png")
#library(png)
add.alpha <- readPNG("Rlogo45_77h124w.png") # Try "Rlogo_77h124w.png", too
#library(abind)
add.alpha <- abind(add.alpha, matrix(1, nrow=nrow(add.alpha), ncol=ncol(add.alpha)))
add.alpha[,,4][which(add.alpha[,,1] > .9 & add.alpha[,,3] > .9 & add.alpha[,,2] < .85 )] <- 0 # not a perfect solution
writePNG(add.alpha, "Rlogo45_77h124w_alpha.png")