saving rotate images in R - avoiding background and maintaining size -
i have below code based upon earlier question. when save image savethisplot.png, creates unwanted background , image size changes (it reduces). how can save image such rotated part of same size earlier (77 rows , 101 columns) , there no background?
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()
you can use transparent background, rotating plot require re-sizing.
when save plot can set png("savethisplot.png", bg="transparent")
prevent background showing. try:
png("savethisplot.png", width=101 height=77, units="px", pointsize=1, bg="transparent")
notice if try rotated version of logo, logo re-sized fit within 101 x 77 pixel png. if want rotate , maintain logo size, you'll have resize png, ass @romanluštrik mentioned in comment. re-sizing, if option you, matter of trigonometry find appropriate height , width parameters.
you can try setting new heights , widths save png using trigonometry. here example 35 degrees of rotation:
# 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()
the overall image not 77 x 101 pixels, logo should same size, though bit rugged. smoother image, see rgl
answer below, note transparent background isn't available rgl
alternative approach using rgl package:
a different approach producing image might better if end goal maintain size , smoothness of image. can try rgl
(r-to-opengl) package , persp3d
function, can rotate plot within window without re-sizing window or altering proportions of logo. can use you've got, color matrix needs modified work persp3d
:
col.mat2 <- cbind(rbind(1, col.mat), 1)
then, set scene, call persp3d
, rotate it, , save.
open3d() rgl.pop("lights") light3d(specular="black") # reduce reflection persp3d(x1,y1,z, col=col.mat2, ylab="", xlab="", zlab="", axes=false, smooth=false, aspect="iso") # rotate defining usermatrix, save par3d(usermatrix = rotationmatrix(0*pi/180, 0, 0, 1)) rgl.snapshot("rlogo_0.png") # 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")
to force image smaller window, can manipulate size of rgl display window , amount of background using combination of windowrect
, zoom
parameters (see question , answer, example). however, transparent background isn't option rgl
currently.
open3d() bg3d(color="#ff00ff") # pink background (some color not in 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")
i wasn't able make window less 124 pixels wide. seems there minimum width of 124 pixels rgl window on computer, can't make window smaller (i.e. can't collapse minimize, restore, , close buttons on top of window).
to transparent background, import these saved pngs, , assign alpha values of 1 background color ("#ff69b4"):
library(png) add.alpha <- readpng("rlogo_77h124w.png") #try rotate version, 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")
it turns out method isn't perfect, , may end slight border on rotated images color isn't "#ff00ff". can address border 1 way or another, if problem you, may not make difference depending on end goals. try using add.alpha[,,4][which(add.alpha[,,1] > .9 & add.alpha[,,3] > .9 & add.alpha[,,2] < .85 )] <- 0
before writepng
if don't want image corners cut off above, use trigonometry earlier size window within 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 , height trigonometry above rgl.snapshot("rlogo45_77h124w.png") #library(png) add.alpha <- readpng("rlogo45_77h124w.png") # try "rlogo_77h124w.png", #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 perfect solution writepng(add.alpha, "rlogo45_77h124w_alpha.png")
Comments
Post a Comment