Leave That Thing Alone Blog

Using JAI and ColdFusion to create image histograms

Here's another look at creating image histograms in ColdFusion, this time using JAI (Java Advanced Imaging). With CFImageHistogram I decided not to use JAI because I didn't want the CFC to require any external Java libraries not currently available to CF6/CF7.

Since speed is definitely an issue with CFImageHistogram I thought I would post some example of code that uses JAI. JAI is much fast at producing image histograms than my method of inspecting pixels in a bufferedimage; however, this method requires that JAI be in the class path of your CF server.

Another advantage of using JAI is that you can get the histograms of more file types (jpg,bmp,tif)

Here is a basic example of using JAI to create a histogram:


<cfscript>
//image file name

filename = expandPath('01.JPG');
//create JAI object

JAI = createObject("Java","javax.media.jai.JAI");
//create JAI PlanarImage

image = JAI.create("fileload", filename);
//create ParameterBlock

pb = createObject("Java","java.awt.image.renderable.ParameterBlock").init();
//add image source

pb.addSource(image);
//RenderedOp

op = JAI.create("histogram", pb);
//get histogram (javax.media.jai.Histogram)

histogram = op.getProperty("histogram");
</cfscript>

To get the histogram array[3][256]:


histogram.getBins()

To get the mean:


histogram.getMean()

Finally here is some code to create an color image histogram png file:


<cfscript>
//image file name

filename = expandPath('01.JPG');
//create JAI object

JAI = createObject("Java","javax.media.jai.JAI");
//create JAI PlanarImage

image = JAI.create("fileload", filename);
//create ParameterBlock

pb = createObject("Java","java.awt.image.renderable.ParameterBlock").init();
//add image source

pb.addSource(image);
//RenderedOp

op = JAI.create("histogram", pb);
//get histogram (javax.media.jai.Histogram)

histogram = op.getProperty("histogram");
//histogram bins - array[3][256]

bins = histogram.getBins();
//set image height

histogramImageHeight = 100;

//get PNG buffered image to draw on

pngBuffered = getPNGBufferedImage(histogramImageHeight);
//get java.awt.Graphics2D

Graphics2D = pngBuffered.getGraphics();
//set background color to white

setImageBackgroundColor(255,255,255);
Graphics2D.setBackground(variables.imageBackgroundColor);
Graphics2D.clearRect(0,0,256,histogramImageHeight);

//set colors with alpha of 33% (85/255)

red = createObject("java", "java.awt.Color").init(javaCast("int",255),javaCast("int",0),javaCast("int",0),javaCast("int",85));
green = createObject("java", "java.awt.Color").init(javaCast("int",0),javaCast("int",255),javaCast("int",0),javaCast("int",85));
blue = createObject("java", "java.awt.Color").init(javaCast("int",0),javaCast("int",0),javaCast("int",255),javaCast("int",85));

//loop thru color bins r-g-b

for(i=1;i LTE 3; i=i+1) {
    //set color based on bin
    
    if (i eq 1) {//red
        
        currentColor = red;
    } else if (i eq 2) {//green
        
        currentColor = green;
    } else if (i eq 3) {//blue
        
        currentColor = blue;
    }
    //set drawing color
    Graphics2D.setPaint(currentColor);
    //loop thru all bins in color
    
    for(ii=1;ii LTE 256; ii=ii+1) {
        //set as 0 so we get a line not a rectangle
        
        rectWidth = javaCast("int",0);
        //set max height based on the max size found in array
        
        rectHeight = javaCast("int",(bins[i][ii]/arrayMax(bins[1])) * histogramImageHeight);
        //X left to right 0-255
        
        rectX = javaCast("int",ii-1);
        
        //switch Y (vertical) so that lines sit on bottom
        
        rectY = javaCast("int",histogramImageHeight-rectHeight);
        //create rectangle that is actually a line becasue we gave it no width
        
        rect = createObject("java", "java.awt.Rectangle").init(rectX,rectY,rectWidth,rectHeight);
        //draw line
        
        Graphics2D.draw(rect);
    }
}
writePNGImage(expandpath("hist.png"),pngBuffered);
</cfscript>

<cffunction name="getPNGBufferedImage" access="private" output="false" hint="I draw a histogram image">
    <cfargument name="height" required="yes" type="numeric" hint="height of png to create" />
    <cfscript>
        var width = 256;
        //create bufferedimage object
        
        var bufferedImage = createObject("java", "java.awt.image.BufferedImage");
        //create PNG based on width height
        
        bufferedImage.init(JavaCast("int", width), JavaCast("int", arguments.height), BufferedImage.TYPE_4BYTE_ABGR);
        //return the buffered image
        
        return bufferedImage;
    
</cfscript>
</cffunction>

<cffunction name="writePNGImage" access="private" output="false" hint="I write a PNG file to disk">
    <cfargument name="outputHistogramImage" required="yes" type="string" hint="filename" />
    <cfargument name="pngBuffered" required="yes" hint="buffered PNG" />
    <cfscript>
        //write PNG file
        
        var outputStream = createObject("java", "java.io.File").init(arguments.outputHistogramImage);
        var ImageIO = createObject("java", "javax.imageio.ImageIO");
        ImageIO.write(arguments.pngBuffered, "png", outputStream);
    
</cfscript>
</cffunction>

<cffunction name="setImageBackgroundColor" access="public" returntype="void" output="false" hint="I set the the buffered image, i can be used if you want to set a buffered image instead of specifying a image file name and path">
    <cfargument name="red" required="yes" default="255" type="numeric" hint="red color value 0-255" />
    <cfargument name="green" required="yes" default="255" type="numeric" hint="green color value 0-255" />
    <cfargument name="blue" required="yes" default="255" type="numeric" hint="blue color value 0-255" />
    <cfargument name="alpha" required="yes" default="255" type="numeric" hint="alpha color value 0-255" />
    <cfset variables.imageBackgroundColor = createObject("java", "java.awt.Color").init(javaCast("int",arguments.red),javaCast("int",arguments.green),javaCast("int",arguments.blue),javaCast("int",arguments.alpha)) />
</cffunction>


<img src="hist.png" style="border:black 1px solid;margin:4px;" />

The above code may get mangled in my small code view so here's a text version

Example Input JPG:

Example Output histogram PNG:

Comments

robi's Gravatar Great stuff seth.
# Posted By robi | 9/27/07 6:36 PM
Chris's Gravatar Great work. Nice intuitive example for using JAI. Works perfectly for me.
# Posted By Chris | 6/1/09 8:41 AM