[R] fidelity of generated raster images (R and perl)

Henrik Bengtsson hb at maths.lth.se
Thu Oct 14 19:20:25 CEST 2004

Hi. To add to Roger's pixmap format and Martin's image() replies, I played
with such problems a while ago and implemented a few metods and classes for
this. Install my R.classes bundle
(http://www.maths.lth.se/help/R/R.classes/) and try the MonochromeImage
class in the R.graphics package. R.classes is still not compatible with
2.0.0 (minor modification to DESCRIPTION etc are needed) so you have use R
v1.9.1 to try the below. Example:

bits <- matrix(c(
), nrow=8, byrow=TRUE)
img <- MonochromeImage(1-bits) # 1=white, 0=black
persp(img, phi=60, theta=150)
write(img, "foo.pbm")  # Write directly
write(img, "foo.png")  # Write via png() and image()
write(img, "foo.jpg")

The method for PBM (monochrome pixmap) will write to file directly without
using graphical devices. You can either write to binary or ASCII format.
Here is the code I use to write to PBM:

'this' is basically a list structure with the matrix element 'gray' (equals
'bits' in the above example).

writePBM.MonochromeImage <- function (this, filename, ascii = FALSE) {
    open <- if (ascii == TRUE) 
    else "wb"
    fout <- file(filename, open = open)
    magicNumber <- if (ascii == TRUE) 
    else "P4"
    cat(file = fout, magicNumber, "\n", sep = "")
    cat(file = fout, "# Creator: Class MonochromeImage in [R] package
R.graphics by Henrik Bengtsson. http://www.braju.com/R/\n", 
        sep = "")
    width <- ncol(this$gray)
    height <- nrow(this$gray)
    cat(file = fout, width, " ", height, "\n", sep = "")
    if (ascii == TRUE) {
        for (row in seq(length = height)) cat(file = fout, 
            1 - this$gray[row, ], sep = "\n")
    else {
        if (width%%8 == 0) 
            w <- width
        else w <- width + (8 - (width%%8))
        x <- matrix(0, nrow = height, ncol = w)
        x[1:height, 1:width] <- this$gray
        x <- as.vector(t(x))
        x <- as.integer((x == 0))
        x <- matrix(x, nrow = 8, byrow = FALSE)
        x <- (2^(7:0)) * x
        x <- colSums(x)
        x <- as.integer(x)
        writeBin(x, size = 1, con = fout)

The manipulation of x, e.g. t(x), can most likely be optimized by for
instance writing in blocks and so on. The bits-to-(0,255) convertion is
probably not optimal either.

The other write method utilizing png() and image() seems to be broken for
gray and monochrome images right now, but works for color images. I'll fix
this for the R v2.0.0 release. Workaround for now: write(as.RGBImage(img),
"foo.png"). However, I agree with Martin that image() might be too
inefficient for this so I would suggest to write to PBM and then use an
external converted, say, ImageMagick. For more info to use image() directly
see example at http://www.maths.lth.se/help/R/image/.

Hope this helps.


Henrik Bengtsson

> -----Original Message-----
> From: r-help-bounces at stat.math.ethz.ch 
> [mailto:r-help-bounces at stat.math.ethz.ch] On Behalf Of Martin Maechler
> Sent: Thursday, October 14, 2004 6:09 PM
> To: Scott Harrison
> Cc: r-help at stat.math.ethz.ch
> Subject: Re: [R] fidelity of generated raster images (R and perl)
> >>>>> "Scott" == Scott Harrison <harris41 at msu.edu>
> >>>>>     on Thu, 14 Oct 2004 11:13:16 -0400 writes:
>     Scott> Hi: Goal: use R to turn a matrix of 1's and 0's into
>     Scott> a corresponding image (e.g. png) of black and white
>     Scott> pixels.
>     Scott> Why R: Yes, I can do this more efficiently and
>     Scott> precisely with a perl module like Image::PBM.  Been
>     Scott> there, done that many times, etc.  (Just humor me.
>     Scott> I'm trying to do this with R for a number of
>     Scott> reasons.)
>     Scott> Problem: Difficult to get a perfect rasterization.
>     Scott> There can be appended or removed pixel columns or
>     Scott> pixel rows depending on plot region dimensions.  I
>     Scott> witness this with both R version 1.8.1 and R version
>     Scott> 2.0.
>  <....>
>     Scott> There are alternatives to rect (plot with type="p",
>     Scott> pch=".", etc) and I have also tried png() instead of
>     Scott> bitmap().  (I do prefer bitmap so this can run
>     Scott> without x11.)
> I think the "most typical" alternative is to use  image()
> which is probably the most efficient currently --- it is used 
> by the plot() method of "pixmap" objects in package 'pixmap' 
> {which you probably should really look at!}.
> Note however that image() is still very inefficient for 
> high-resolution (already 1000x1000) images since it really 
> fills a rectangular polygon for each pixel.  This e.g. leads 
> to horribly large postscript files when plotting such pixmaps.
> In some way, this is a known "missing feature" in R's 
> graphics engines.  However a nice implementation would 
> probably use properties of the graphics device:
> A smartImage() function would ``see'' that for a given output 
> device, it should only draw pixels instead of rects(); even 
> smarter for even larger pixmaps (where there are less 
> "pixels" on the output device than you have in your image) it 
> would even average
> ("color-smooth") neighboring pixmap pixels into device pixels.
>     Scott> I am guessing that R's internal region calculations
>     Scott> are vector based, which generally makes sense for
>     Scott> most statistical plots.  However, I do have some
>     Scott> ideas for R and the presentation of cellular automata
>     Scott> results.
>     Scott> Any tips out there?  (Is it just a matter of
>     Scott> height=50px to overcome the inches default,
>     Scott> etc?).....
>     Scott> Regards, Scott
> Regards,
> Martin Maechler
> ______________________________________________
> R-help at stat.math.ethz.ch mailing list 
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide! 
> http://www.R-project.org/posting-guide.html

More information about the R-help mailing list