ImageTools and densityplot

June 23 2012 acer 9716
Maple

10

This post in reply to the Post, colour and 2D point-plots

Many of us know that issuing plotting commands produces various kinds of plot data structure, the details of which are documented on the plot,structure help-page. That page covers most of the details, and a thorough read can reveal that the numeric data of a plot is often stored within such structures as either Array or Matrix.

But what about the result of a call to plots:-densityplot , a 2D plotting command? If you call the relatively new plottools:-getdata on the resulting structure then the only tabular part recognized is an mxn Matrix in the GRID portion of the structure. Inspection of that Matrix, however, shows that all the entries are equal -- it is actually inconsequential to the displayed density plot.

So, where is all the data which specifies the coloring in the densityplot? It turns out that it is stored in an m*n-by-3 Array with double-precision datatype=float[8], and that this Array is in the COLOR portion of the plot structure.

Those dimensions might remind you of the three m-by-n layers that ImageTools uses to represent an uncompressed raw image, inside m-by-n-by-3 Arrays. Two natural questions are: are these forms related, and can you convert from one to the other?

The answer to both those questions is yes.

First, let's go from density plot to image. The visual mismatch in sizes of what is displayed below is primarily on account of how Maple chooses by default to show them in a worksheet, and isn't so important. What is more important is that the two structures contain the same numeric data, and can be made to represent the same number of pixels in an image, and that they can be displayed with the right aspect ratio. The aspect ratio we can change at any time, easily enough, and the ranges passed to the densityplot command are useful for that.

This is as good a time as any to mention display of ImageTools "images" in a worksheet. The ImageTools:-Preview command converts the image to a 3D density plot, and inserts that with a convenient straight-on orientation which happens to look as if it were a 2D plot. That's why it shows as smaller, because that's just what Maple happens to do with 3D plots.

This particular example is just an illustration of the concept. Without too much effort someone might turn this conversion into a robustly behaving procedure. (No points awarded for coming up with an example that needs a little something extra, to get the aspect right, say.)

restart:

p := plots:-densityplot(x*(x^2-y^2)/(x^2+y^2)^2,
                        x=0..0.1, y=0..0.3, grid=[128,128],
                        style=patchnogrid, colour="Red",
                        gridlines=false, axes=none, labels=[``,``],
                        scaling=constrained):

p;

plottools:-getdata(p);

      [                                                     
      [                                                     
      ["grid", [-0.000393700787401575 .. 0.100393700787402, 
      [                                                     

        -0.00118110236220472 .. 0.301181102362205, 16384. .. 16384.], 

        [ 128 x 128 Matrix     ]]
        [ Data Type: float[8]  ]]
        [ Storage: rectangular ]]
        [ Order: C_order       ]]

indets(p,specfunc(anything,COLOR));

                   /     /     [ 16384 x 3 2-D  Array ]\\ 
                   |     |     [ Data Type: float[8]  ]|| 
                  < COLOR|RGB, [ Storage: rectangular ]| >
                   |     \     [ Order: C_order       ]/| 
                   \                                    / 

C:=indets(indets(p,specfunc(anything,COLOR)),Array)[1]:

m,n := map(rhs,[rtable_dims(plottools:-getdata(p)[-1])])[];

                              m, n := 128, 128

ArrayTools:-DataTranspose(C,m,n,1):
P:=ArrayTools:-Alias(C,[m,n,3]):
P:=ArrayTools:-FlipDimension(P,1):
P:=ImageTools:-Scale(P,(0.3-0.0)/(0.1-0.0),1.0);

                           [ 384 x 128 x 3 3-D  Array ]
                           [ Data Type: float[8]      ]
                      P := [ Storage: rectangular     ]
                           [ Order: C_order           ]

ImageTools:-Preview(P);

That doesn't look so good here only because I've shown the result of Preview(P) which is an intentionally coarse mechanism. A better look at the image is available using the command call ImageTools:-View(P) which I can't show here because it displays inside a pop-up Maplet.

Here is the result of writing the resulting image to a jpg file, using the ImageTools:-Write command.

One reason that I much prefer images over density plots is that an image can be programmatically displayed on either a Button Component or a Label Component, using the SetProperty command. This results in a very clear picture, and with which the Maple Standard GUI behaves well. In contrast a densityplot gives the GUI some grief -- it scrolls sluggishly, it takes several seconds to insert, and even longer to get its right-click context menu.

Next, let's go from image to densityplot. I'll use the tree.jpg file that comes with Maple, so that this example can be run directly.

restart:

fn:=cat(kernelopts(mapledir),"/data/tree.jpg"):
P:=ImageTools:-Read(fn):

m,n := ImageTools:-Width(P),ImageTools:-Height(P);

                            400, 266

C:=ArrayTools:-Alias(P,[m*n,3]):
ArrayTools:-DataTranspose(C,n,m,1):
img:=ArrayTools:-Alias(C,[m,n,3]):
img:=ArrayTools:-FlipDimension(img,2):

# Create a dummy density plot, whose data we'll replace with that from the image.
p:=plots:-densityplot(1,x=1..m,y=1..n,axes=none,scaling=constrained,
                      style=patchnogrid,gridlines=false,grid=[m,n]):

p:=subsindets(p,specfunc(anything,COLOR),z->COLOR(RGB,img)):
p;

I wanted to save the above density plot, displayed in my worksheet, using the right-click context menu. It took around 20 sec for the context menu to pop up, for the above 400x266 densityplot, another 15 sec for the file manager to show, and another 10 sec for the export to GIF, all on a fast Intel i7 on Windows 7. Working with images is sometimes faster than working with density plots, although more work to program.

Something that can be programmed easily, with a densityplot, is overlay with text or 2D Math.

plots:-display( p, plots:-textplot([100,230,typeset(sum(a[i],i=1..N)=Psi(N))]) );

With images, on the other hand, it is possible to accomplish the same overlay but doing so requires adjusting for whitespace boundaries.

acer

Please Wait...