6

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.

Sharpie
  • 17,323
  • 4
  • 44
  • 47
  • Works as intended for me... (R 2.11.1 running under Fedora Core 13). What happens if you set `antialias=NULL`? – nico Oct 17 '10 at 20:38
  • Tried passing `rasterImage(..., antialias=NULL)` and got no change. Are you setting it somewhere else? `??"antialias"` did not have any suggestions for me. Also, I am using OS X so maybe it is a platform-dependent issue. – Sharpie Oct 17 '10 at 20:49
  • 1
    Thanks nico, I found it it. I get correct output if I start the device with `png('test.png', width=5, height=3, units='px', type='cairo', antialias=NULL)` instead of `png('test.png', width=5, height=3, units='px')`. If you put this in an answer, I will accept it if it turns out to be a cross-platform solution (firing up the windows VBox right now). – Sharpie Oct 17 '10 at 20:57
  • Apparently Windows has issues of it's own with this particular plot. There appears to be no antialiasing artifacts, but the output is shifted up by one row and to the left by one column-- possibly indicating an off-by-one bug. I may have to ask the grizzled ancients on `R-Devel` about this. – Sharpie Oct 17 '10 at 22:28
  • Glad it worked! Added as an answer with a bonus on how to set the default antialias :) – nico Oct 18 '10 at 06:44

2 Answers2

1

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
  • 50,859
  • 17
  • 87
  • 112
1

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
  • 69,533
  • 24
  • 154
  • 197
  • 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 Oct 18 '10 at 08:21
  • 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 Oct 18 '10 at 11:38