I'm doing quantitative image analysis, and visualizing the results with ggplot2. The output contains one datapoint for each pixel in the original image.
geom_raster()
nicely visualizes my data in R. But it would be nice to output a raster image corresponding to the results. That way, I could flip through several derived images using a lightweight image viewer (e.g., feh
), and the pixels would line up perfectly.
Is there an easy way to output the pixels, and only the pixels, to an image file? No legend, no axes, nothing but the pixels. Assume my data.frame
has columns for row
and col
, and the desired output resolution is also known.
Here's one way:
library(ggplot2)
library(reshape2) # for melt(...)
n <- 100
set.seed(1) # for reproducible example
img <- matrix(rnorm(n^2,30,3),nc=n)
gg <- melt(data.frame(x=1:n,img),id="x")
ggplot(gg) + geom_raster(aes(x=x,y=variable,fill=value))+
scale_x_continuous(expand=c(0,0))+ # get rid of extra space on x-axis
guides(fill=FALSE)+ # turn off color legend
theme(axis.text=element_blank(), # turn off the axis annotations
axis.ticks=element_blank(),
axis.title=element_blank())
Thanks to jlhoward for pointing me in the right direction. There are a few more missing ingredients -- for instance, without labs(x=NULL, y=NULL)
, the output PNG will have white borders on the bottom and left.
I decided my solution should have two parts:
- Craft a ggplot object to visualize my data. (This step is the same as usual.)
- Call a general-purpose function to take care of all the annoying details which are necessary to output that plot as a pixel-perfect PNG.
Here is one such function.
BorderlessPlotPng <- function(plot, ...) {
# Write a ggplot2 plot to an image file with no borders.
#
# Args:
# plot: A ggplot2 plot object.
# ...: Arguments passed to the png() function.
require(grid)
png(type='cairo', antialias=NULL, units='px', ...)
print(plot
+ theme(plot.margin=unit(c(0, 0, -0.5, -0.5), 'line'),
axis.text=element_blank(),
axis.ticks=element_blank(),
axis.title=element_blank(),
legend.position='none')
+ scale_x_continuous(expand=c(0, 0))
+ scale_y_continuous(expand=c(0, 0))
+ labs(x=NULL, y=NULL)
)
dev.off()
}
To see it in action, here's a plot of some synthetic data. (I made each output pixel 10 pixels wide for demonstration purposes.)
# Synthetic data.
width <- 64
height <- 48
d <- data.frame(row=rep(1:height, each=width),
col=rep(1:width, height),
x=rnorm(n=width * height))
# Construct and print the plot.
library(ggplot2)
plot <- (ggplot(data=d, aes(x=col, y=height + 1 - row, fill=x))
+ geom_raster()
+ scale_fill_gradient2()
)
pixel_size <- 10
BorderlessPlotPng(plot,
filename='test.png',
width=width * pixel_size,
height=height * pixel_size)
Output:
Of course, running with pixel_size <- 1
would give you a 1:1 image, which you could compare to the original image by flipping back and forth.