R tips and tricks: Producing smooth bitmap plots

Posted by Tom Moertel Sun, 26 Aug 2007 01:56:00 GMT

The R statistics system can produce first-class data visualizations, commonly known as plots. Internally, plots are represented in an abstract graphics format that can be rendered on any of R’s wide range of graphics “devices” to produce concrete output – windows, bitmap files, PostScript files, PDF files, and others.

The bitmap formats, such as PNG, are preferred for posting plots online because of their widespread support by web browsers. The default bitmap-rendering devices in R, unfortunately, produce graphics that look a little too “bitmapped” for modern web tastes. Here, for example, is a plot rendered by R’s “png” device:

Plot rendered via R's PNG device

There’s nothing technically wrong with the plot, but it looks out of place on a web page. That’s because modern web browsers use font-smoothing and anti-aliasing techniques to render just about everything else on the page. Against this clean, un-jagged backdrop, the oh-so-bitmapped plot looks like a throwback to a previous era.

Happily, we can produce clean, anti-aliased R plots with a little help. Here’s the earlier plot, anti-aliased:

Plot rendered via R's PDF device, then post-processed

To produce the anti-aliased plot, I used R to produce a PDF file. Then I rendered the PDF file into a PNG image at 300 dpi using Ghostscript. Finally, I scaled the 300-dpi image down to screen resolution, producing a high-quality, anti-aliased result.

Here’s the recipe in detail.

First, I define an R function called pdfit that takes an abstract graphics object and makes a PDF-file rendering of it, using my preferred graphics-device settings:

require("lattice")

pdfit <- function(f, ...) {
  trellis.device(dev=pdf, theme="col.whitebg", ...);
  print(f);
  dev.off()
}

Then, when I create a plot I want to publish, I use pdfit to render it into a PDF file:

P.img <- xyplot( subs.low + subs.high ~ date, ... )

pdfit(P.img, file="image-downloads.pdf")  # render plot into PDF file

Finally, I use Ghostscript and ImageMagick to convert the PDF file into a high-quality, anti-aliased PNG file. (I keep both formats: the PDF file is best for publishing in printed papers, and the PNG file is best for posting online.) I use a simple Makefile to automate the process of converting the PDF files into PNG files:

# Makefile (GNU make)

pdfs := $(wildcard *.pdf)
pngs := $(pdfs:.pdf=.png)

all: $(pngs)
.PHONY: all

%.png: %.pdf
    gs -dSAFTER -dBATCH -dNOPAUSE -sDEVICE=png16m \
       -dGraphicsAlphaBits=4 -dTextAlphaBits=4 -r300 \
       -dBackgroundColor='16#ffffff' \
       -sOutputFile=$@ > /dev/null \
       $< && \
    mogrify -resize 500 $@

With this Makefile in my graphics directory, just a single “make” command is all it takes to convert my PDF images into anti-aliased PNG files, ready to post online.

And that’s it.

Do you have any tips or tricks for making good-looking graphics with R? If so, please do share.

Update: There is one downside to the sexy, anti-aliased plots: they are not as compressible as the old-style jagged plots. For the images above, for instance, the anti-aliased PNG file weighs in at 45 KB, but the original PNG file is a feathery 4.7 KB. So, if bandwidth is precious to you – or you’re planning on getting Slashdotted – you might want to stick with the jaggies.

Posted in
Tags , , , , ,
10 comments
no trackbacks
Reddit Delicious

Comments

  1. Vineet Kumar said about 6 hours later:

    You can probably cut the size of that new PNG by half if you convert it to an 8-bit colormap instead of 24-bit rgb. Still, you’ll probably never get it as small as the jaggy one.

  2. Thibault said about 8 hours later:

    Thanks for this tip Tom. But can we not produce SVG file from R? Or a simple Flash file? Also can we not display the PDF inline directly? After all those pictures are originally vectors. Why converting them into bitmaps? Maybe I’m missing a point there, just a thought.

  3. Neil Mitchell said about 11 hours later:

    Optipng will take 20% of size off the final one.

  4. Tom Moertel said about 14 hours later:

    Neil, thanks for the tip on OptiPNG. Thanks to you, my images are now about 20% slimmer.

    Cheers!
    —Tom

  5. Peter Hosey said about 22 hours later:

    Even better: Use pngnq and pngout.

    pngnq converts the image (lossily, if necessary) to 8-bit color while preserving the quality of the image, and nine times out of ten, it does an awesome job. pngout then compresses it, almost always better than pngcrush or optipng do. Together, they make a mighty combo.

    In your case, plot2.png (the anti-aliased version) went from 45,716 bytes to 17,704 bytes (pngnq only) to 15,549 bytes (pngnq+pngout). That’s a savings of 66%.

    The downside to pngout over pngcrush and optipng is that pngout is closed-source, so if that’s a deal-breaker for you, you’ll have to stick with optipng.

  6. Howard B. Golden said 1 day later:

    My thoughts echo Thibault. Is there a vector format acceptable to almost all browsers? If so, I’d rather use that.

  7. Tom Moertel said 2 days later:

    Thibault and Howard, I’m completely in agreement that vector formats are the best way to represent plots. The problem is that on the web there is no ubiquitously supported vector format.

    Flash has fairly widespread support, but it is far from universal and additionally requires the use of a proprietary plug-in, making it unacceptable for people who prefer an open pipeline to the reader’s eyeballs. SVG is more open and has the hope of being integrated into future web browsers, and the RSvgDevice CRAN package lets R produce SVG output, but browser support for SVG is poor.

    In theory, HTTP content negotiation could allow us to offer both SVG and PNG formats for an image, with the client’s browser selecting the preferred format, but here, too, the practice has not caught up with the theory.

    In sum, if you care about communicating on the web, you are stuck with raster image formats for the present.

    Cheers,
    Tom

  8. Ana Nelson said 7 days later:

    This is also a useful approach if you are plotting a very large number of data points, for example data generated from a simulation, in which case a raster image file may actually be smaller and load much faster than a vector image which has to render each plotted data point individually.

  9. Ryan J. Parker said 429 days later:

    Wow I’m glad I finally found this. I’ve had an issue with blocky PNGs from R for some time. This works great on my Linux box.

    My image is only 16k, so not too bad. If it gets out of control I’ll try out some of the suggestions above.

    Thanks again.

  10. Wayne said 478 days later:

    Not sure if this issue is fixed on every platform at this point, but an option to consider is the Cairo library, which generates anti-aliased graphics (including transparencies) and can output to PNG.

Trackbacks

Use the following link to trackback from your own site:
http://blog.moertel.com/articles/trackback/550

(leave url/email »)

   Comment Markup Help Preview comment