views:

95

answers:

2

I am trying to use R to create a raster image from a matrix of data. However, I am getting some weird artifacts on the edge of my image.

The code I am using is as follows:

# From the example for rasterImage(). A 3 pixel by 5 pixel b/w checkerboard.
testImage <- as.raster(0:1, nrow=3, ncol=5)

testImage
     [,1]      [,2]      [,3]      [,4]      [,5]     
[1,] "#000000" "#FFFFFF" "#000000" "#FFFFFF" "#000000"
[2,] "#FFFFFF" "#000000" "#FFFFFF" "#000000" "#FFFFFF"
[3,] "#000000" "#FFFFFF" "#000000" "#FFFFFF" "#000000"

png('test.png', width=5, height=3, units='px')

# Just want the image, no margins, boarders or other fancy stuff.
par(mar = c(0,0,0,0) )
plot.new()
plotArea = par('fig')

rasterImage(testImage, plotArea[1], plotArea[3],
  plotArea[2], plotArea[4], interpolate = FALSE )

dev.off()

This was executed in R 2.12.0 on OS X but I get the same output from R 2.11.0.


The output I am getting is the following (scaled from 5x3 to 150x90)

R output

The pixels in the corners should be black which suggests some form of interpolation is occurring.


The output I am expecting to see is:

Expected output

Any suggestions on why my code fails to faithfully produce a raster image from a matrix?


Intended Use

This is for a package I am working on so I would like to stay within the R base packages if possible so as not to introduce additional dependencies. The package implements a graphics device, so if anyone has a C level solution picks up from the info passed by GERaster() in src/main/engine.c and creates a PNG using only the R libraries, I would be willing to give that a shot as well.


Solutions for OS X

As pointed out by nico, the errant behavior is the result of antialiasing. The plot behaves as expected if png() is told to use an output method for which antialiasing can be disabled, such as Cairo graphics:

png('test.png', width=5, height=3, units='px', type='cairo', antialias=NULL)

On OS X the default backend for png() is Quartz, however png(..., type='quartz') currently ignores directives set by quartz.options(). Faithful output can be produced natively on OS X if the device is started by calling quartz() directly instead of using png():

quartz(file='test.png', type='png', width=5, height=3, dpi=1, antialias=FALSE)

Windows Thinks Differently

The following output is generated on Windows:

Windows output

According to an answer given by Paul Murrell (the benevolent dictator of R graphics devices) on the R-help mailing list:

This is a rounding (truncation) problem. Working on a fix.

This behavior on Windows should not be noticeable unless the raster image contains a very small number of pixels.

+1  A: 

Your code works as intended for me... (R 2.11.1 running under Fedora Core 13). It appears to be an antialias problem

This code does the trick

png('test.png', width=5, height=3, units='px', type='cairo', antialias=NULL)

The default antialias options can be set in X11.options

From ?X11.options

antialias: for cairo types, the type of anti-aliasing (if any) to be
      used.  One of ‘c("default", "none", "gray", "subpixel")’.
nico
A: 

Have you heard of the raster package? Perhaps it may be of some service.

library(raster)
test.image <- matrix(c(1, 0, 1, 0, 0, 1, 0, 1), ncol = 4, byrow = TRUE)
plot(raster(test.image))

png("test.png")
image(test.image, axes = FALSE)
dev.off()
Roman Luštrik
I have heard of `raster`. However, I am reluctant to add to the dependency list of my package unless it is absolutely necessary. Given that `raster` is heavily focused on spatial data and looks like it requires GDAL to be installed in order to write files (and does not mention PNG as a supported filetype) it is overkill for my particular application. Thanks for the suggestion though!
Sharpie
As far as I know, you can use raster::image, who's arguments are passed to graphics::image. I've amended my answer on how to write to png.
Roman Luštrik