Java Using the Java 2D LookupOp Filter Class to Process Images

Using the Java 2D LookupOp Filter Class to Process Images

Java Programming Notes # 452


Preface

One lesson in a series

In the earlier lesson entitled
A Framework for Experimenting with Java
2D Image-Processing Filters
, I taught you a little about the image-filtering
classes of the Java 2D API.  I also taught you how to write a framework
program that makes it easy to use those image-filtering classes to modify the
pixels in an image and to display the modified image.

I told you that future lessons would teach you how to use the following
image-filtering classes from the Java 2D API:

  • LookupOp
  • AffineTransformOp
  • BandCombineOp
  • ConvolveOp
  • RescaleOp
  • ColorConvertOp

In this lesson, I will keep that promise and teach you how to use the
LookupOp
class for a variety of purposes.  I will teach you how to use
the other classes from the above list in future lessons.

Viewing tip

You may find it useful to open another copy of this lesson in a
separate browser window.  That will make it easier for you to
scroll back
and forth among the different listings and figures while you are
reading
about them.

Supplementary material

I recommend that you also study the other lessons in my extensive
collection of online Java tutorials.  You will find those lessons
published
at Gamelan.com
However, as of the date of this writing, Gamelan doesn’t maintain a
consolidated index of my Java tutorial lessons, and sometimes they are
difficult to locate there.  You will find a consolidated index at www.DickBaldwin.com.

I also recommend that you pay particular attention to the lessons listed in
the References section of this document.

General Background Information

Constructing images

Before getting into the programming details, it may be useful for you to
review the concept of how images are constructed, stored, transported, and
rendered in Java (and in most modern computers for that matter).

I provided a great deal of information on those topics in the earlier lesson
entitled

Processing Image Pixels using Java, Getting Started
.  Therefore, I won’t
repeat that information here.  Rather, I will simply refer you back to the

earlier lesson
.

The framework program named ImgMod05

It will also be useful for you to understand the behavior of the framework
program named ImgMod05.  Therefore, I strongly recommend that you
study the earlier lesson entitled
A Framework for Experimenting with Java
2D Image-Processing Filters
.

However, if you don’t have the time to do that, I can summarize that
framework program as follows:

Purpose of ImgMod05

The purpose of ImgMod05 is to make it easy for you to experiment with
the modification of images using the image-filtering classes of the Java 2D API
and to display the modified version of the image along with the original image.

The Replot button

The ImgMod05 program GUI contains a Replot button (as shown
in Figure 1)
.  At the
beginning of the run, and each time thereafter that the Replot button is
clicked:

  • The image-processing method belonging to an object of specified
    image-processing class is invoked.
  • The original image is passed to the image-processing method, which
    returns a reference to a modified image.
  • The resulting modified image is displayed along with the original image.
  • The modified image is written into an output JPEG file named junk.jpg.

Display of the images

When the ImgMod05 program is started, the original image and the
processed version of the image are displayed in a frame with the original image
above the processed image (as shown in Figure 1).  The program attempts
to set the size of the display so as to accommodate both images.  If both images
are not totally visible, the user can manually resize the display frame.

Input and output file format

The ImgMod05 program will read gif and jpg input files and possibly
some other input file types as well.  The output file is always a JPEG file.

Typical usage

Enter the following at the command-line to run the ImgMod05 program:

java ImgMod05 ProcessingProgramName ImageFileName

Preview

In this lesson, I will present and explain two different image-processing
programs that are compatible with the framework program named ImgMod05.

The program named ImgMod38

The first program, named ImgMod38, is designed to show the
essential requirements
of writing a program that runs under control of the
framework program named ImgMod05 and uses an image-filtering class of the
Java 2D API to modify an image.

The program named ImgMod39

The second program, named ImgMod39, is designed to show how you can
expand on those essential requirements to create a program that allows for user
input and supports a great deal of experimentation.

Discussion
and Sample Code


I will discuss the two programs in this lesson in fragments.  A complete
listing of ImgMod38 is provided in Listing 37 near the end of the lesson. 
A complete listing of ImgMod39 is provided in Listing 38 near the end of
the lesson.

Complete listings of the framework program named ImgMod05
and its required interface named ImgIntfc05 were provided at the end of
the earlier lesson entitled
A Framework for Experimenting with Java 2D
Image-Processing Filters
.

The Program Named ImgMod38

Purpose

Secondary purpose
A secondary purpose of this program is to provide program comments
that will be referred to by future programs to avoid repetition of those
comments.

The purpose of this program is to provide a simple example of an
image-processing class that is compatible with the use of the framework program
named ImgMod05, and which illustrates a single usage of the LookupOp
image-filtering class from the Java 2D API.

Must implement the interface named ImgIntfc05

A class that is compatible with the framework program named ImgMod05 is required to implement the
interface named ImgIntfc05.  This, in turn, requires the class to
define the method named processImg, which receives one parameter of type
BufferedImage and returns a reference of type BufferedImage.

The required signature for the processImg method is:

public BufferedImage processImg(BufferedImage input);

The processImg method receives a reference to a BufferedImage object
containing the image that is to be processed.  The processImg method must return a
reference to a BufferedImage object containing the processed image.

A color-inverter method

In this
example, the method named processImg is a color inverter method. 
Figure 1 shows a sample of the output produced by this program when it is run
under control of the framework program named ImgMod05.


Figure 1

Inverting the colors

The method named
processImg as defined in this class receives an incoming reference to an image
as a parameter of type BufferedImage.  The method returns a reference to an image
as type BufferedImage where all of the color values in the pixels have been
inverted by subtracting the color values from 255.

The type of the image

The method has been
demonstrated to work properly only for the case where the incoming BufferedImage
object was constructed for image type BufferedImage.TYPE_INT_RGB.  However, it may
work properly for other image types as well.

No constructor
parameters are allowed

Note that this class does not define
a constructor.  However, if it did define a constructor, that constructor would
not be allowed to receive parameters.  This is because the class named ImgMod05
instantiates an object of this class by invoking the newInstance method of the
class named
Class using the name of this class as a String.  That process does not allow for constructor parameters for the class
being instantiated.

Program output

The framework program named ImgMod05 displays the original and the
modified images in the format shown in Figure 1, with the original image at the
top and the modified image at the bottom.

The program named ImgMod05 also writes the modified image into an output file in
JPEG format.  The name of the output file is junk.jpg and it is written into the
current directory.

The Replot button

As shown in Figure 1, the GUI for the framework program named ImgMod05 contains
a Replot button.  At the beginning of the run, and each time thereafter that the
Replot button is clicked:

  • The image processing method belonging to an object of the specified image
    processing class is invoked.
  • The resulting modified image is displayed along
    with the original image.
  • The modified image is written into the output JPEG file named
    junk.jpg
    .

An image-processing program GUI

ImgMod39
The use of an image-processing program GUI will be illustrated by the
program named ImgMod39.

Image processing programs such as this one may provide a
GUI for data input making it possible for the user to modify the behavior of the
image processing method each time the Replot button is clicked.  However, no such
GUI is provided by this program and clicking the Replot button is of no
consequence.

File formats

The framework program named ImgMod05 reads gif and jpg input files and
possibly some other input file types as well.  The output file is always a JPEG
file.

If the
program is unable to load the image file within ten seconds, it will abort with
an error message.

Usage

Enter the following at the command line to run this program:

java ImgMod05 ImgMod38 ImageFileName

The image file must be provided by the
user.  However, it doesn’t have to be in the current directory if a path to the
file is included along with the file name on the command line.

The size of the display frame

When the program
is started, the original image and the processed version of the image are
displayed in a frame with the original image above the processed image as shown
in Figure 1.

The framework
program named ImgMod05 attempts to adjust the size of the display frame to
accommodate both images.  However, if the processed image doesn’t fit in the display, the
user can manually resize the display frame in order to view both images.

Program testing

This program was tested using J2SE5.0 under WinXP.

The class named ImgMod38

This program consists of the single class named ImgMod38.  That
class begins in Listing 1.

class ImgMod38 implements ImgIntfc05{

  public BufferedImage processImg(BufferedImage theImage){

Listing 1

As shown in Listing 1, the class implements the required interface named
ImgIntfc05
.  This in turn requires the class to define the method named
processImg.

The beginning of the required method definition also begins in Listing 1.

A lookup table is required

This class uses the LookupOp class from the Java 2D API to invert all of the color values in the pixels. 
(The alpha value belonging to the pixel is not modified.)

The filter method that is later invoked on an object of the
LookupOp
class to modify the image uses a color value from a pixel as an
ordinal index into a lookup table.  It replaces the color value in the pixel
with the value stored in the lookup table at that index.  Thus, you can modify
the color values in the pixels using just about any substitution algorithm that
you can devise.

Constructing the lookup table

The lookup table can be constructed as an object of either of the following
classes, both of which are subclasses of the abstract LookupTable class:

  • ByteLookupTable
  • ShortLookupTable

The examples in this lesson will use lookup tables constructed from the
ShortLookupTable
class.

One or more substitution arrays can be used

The LookupTable object can be constructed from one or more data arrays
containing substitution values.  If a single substitution array is used to
construct the lookup table, that substitution array is applied to each of the
red, green, and blue color bands.  If three different substitution arrays
are used to construct the lookup table, different substitution arrays are
applied to each of the three color bands.  This makes it possible to process
the three color bands in different ways.

This program uses a single array to construct the lookup table.  The use
of three different arrays to construct the lookup table will be illustrated
later in the program named ImgMod39.

Prepare the data for the lookup table

Posterizing

The page shown in Figure 3 makes it possible for the user to apply a process
to the image commonly known as posterizing the image.  I will
explain what is meant by posterizing in conjunction with the explanation
of the program later in this lesson.


Figure 3

Custom transforms

The page shown in Figure 4 makes it possible for the user to apply either of
two different custom transforms to the image.  I will explain the custom transforms
in conjunction with the explanation of the program later.


Figure 4

Three substitution arrays are used

In each of the three cases illustrated by Figure 2
through Figure 4, the program uses three different substitution arrays to process the red, green,
and blue color bands individually or in combination (as opposed to the use of
a single substitution array in the case of the earlier program named
ImgMod38
)
.

The procedure for running the program

The general comments provided for the program named ImgMod38 apply to
this program also.  Enter the following at the command line to run
this program:

java ImgMod05 ImgMod39 ImageFileName

If the program is unable to load
the image file within ten seconds, it will abort with an error message.

A JTabbedPane object

As mentioned earlier, this program creates a GUI consisting of a
JTabbedPane
object containing three pages.  As shown in Figure 2, the tabs
on the pages are labeled:

  • Color Inversion
  • Posterizing
  • Custom Transforms

Each page
in the tabbed pane contains a set of controls that make it possible to process
an image in a way
that illustrates the image-processing concepts indicated by the labels on the tabs.  Processing
details for each page will be provided in the discussion of the code later.

A complete program listing

A complete listing of the program is provided in Listing 38 near the end of
the lesson.  The program was tested using J2SE 5.0 under
WinXP.

The ImgMod39 class

The class definition for the ImgMod39 class begins in Listing 6
The class extends the Frame class, making an object of the class eligible
to serve as
its own GUI.  The class also implements the ImgIntfc05 interface
making it eligible for being executed under control of the program named
ImgMod05.

class ImgMod39 extends Frame implements ImgIntfc05{

  JTabbedPane tabbedPane = new JTabbedPane();

Listing 6

The colors are inverted

If you compare Figure 5 with Figure 1, you will see that selection of
the image in Figure 1 caused all of the colors to be inverted.  As a
result, the top image in Figure 1 looks like the bottom image in Figure 5, and
the top image in Figure 5 looks like the bottom image in Figure 1.

Enough talk, let’s see some code

The method named processPage00 begins in Listing 15.  As you can
see, the method receives an incoming parameter referring to the input image
object and returns a reference to the processed
image.

  BufferedImage processPage00(BufferedImage theImage){

    short[] red = null;
    short[] green = null;
    short[] blue = null;

Listing 15

Posterizing results with a color wheel

The left panel of Figure 8 shows the result of eliminating all the colors from the color wheel
except red and then posterizing the resulting image into only three shades
of red.

Figure 8

The right panel of Figure 8 shows the result of eliminating all the colors from the color wheel
except green and then posterizing the resulting image into three shades
of green.

Similarly, the left panel of Figure 9 shows the result of eliminating all the colors from the
color wheel except blue and then posterizing the resulting image into three shades of
blue.

Figure 9

Finally, the right panel of Figure 9 shows the result of putting the
posterized versions of red,
green, and blue back together to produce a posterized version of the original
color wheel.

Should have 27 colors

According to my calculations, when each of the three colors is allowed to
have three shades, then the maximum number of possible colors in the posterized
version of the color wheel should be three cubed or 27.  However, the
differences between some of those colors must be very subtle because I am only
able to count about twenty different colors in the bottom image of Figure 9.

Now for some code

That is probably enough posterizing examples to give you an idea of the
behavior of posterizing.  Now let’s look at the code that accomplishes
posterizing.

The method named constructPage01

The method named constructPage01 is shown in its entirety in
Listing
24
.  This method constructs the Posterizing page shown in Figure 3
The method is called from the primary constructor in Listing 10.

  void constructPage01(){
    page01.setName("Posterizing");//Label on the tab.
    page01.setLayout(new BorderLayout());
    
    //Create and add the instructional text to the page.
    String text = "POSTERIZINGn"
      + "The effect of posterizing is to cause the image "
      + "to look similar to a painting that was painted "
      + "using a "paint by numbers" set where the smooth"
      + "transitions from one color to the next have been "
      + "eliminated.  This is accomplished by limiting "
      + "the number of different colors that appear in "
      + "the image.nn"
      + "To posterize an image, decide how many levels "
      + "you want to see for each of the three colors, "
      + "red, green, and blue.  Enter that number in the "
      + "text field below.  Select the colors that you "
      + "want to mix by checking the matching check boxes "
      + "and click the Replot button.";
      
    //Note:  The number of columns specified for the
    // following TextArea is immaterial because the
    // TextArea object is placed in the NORTH location of
    // a BorderLayout.
    TextArea textArea = new TextArea(text,9,1,
                                 TextArea.SCROLLBARS_NONE);
    page01.add(textArea,BorderLayout.NORTH);
    textArea.setEnabled(false);
    
    //Construct the control panel and add it to the page.
    Panel page01ControlPanel = new Panel();
    page01ControlPanel.add(page01TextField);
    page01ControlPanel.add(page01RedCkBx);
    page01ControlPanel.add(page01GreenCkBx);
    page01ControlPanel.add(page01BlueCkBx);
    page01.add(page01ControlPanel,BorderLayout.CENTER);
    
  }//end constructPage01

Listing 24

You should have no difficulty understanding the code in Listing 24 if you
compare the code with the image in Figure 3.  Therefore, further explanation of the code in
Listing 24 should not be necessary.

The method named processPage01

The method named processPage01 begins in Listing 25.  This method
applies a posterizing algorithm to the image using the controls located on the
Posterizing page in Figure 3.  The method is called from within the
switch statement in the method named processImg in Listing 14.  Note that this method processes the image using three
separate
arrays.

  BufferedImage processPage01(BufferedImage theImage){

    int numberLevels = 1;
    try{//Get input value from the text field.
      numberLevels = 
               Integer.parseInt(page01TextField.getText());
    }catch(java.lang.NumberFormatException e){
      page01TextField.setText("Bad Input");
      numberLevels = 1;//Override bad user input.
    }//end catch

    //Guarantee that the number of levels falls within the
    // allowable range.  Don't allow divison by 0.
    if((numberLevels <= 0) || (numberLevels >' 256)){
      page01TextField.setText("Bad Input");
      numberLevels = 1;//Override bad user input.
    }//end if

Listing 25

Get the number of levels

The processPage01 method in Listing 25 begins by getting user input
specifying the number of levels or shades that will be allowed for each of the
primary colors, red, green, and blue.  The code in Listing 25 is
straightforward.

Compute the bin size

Limiting the number of allowable shades for each primary color will cause the
number of different values contained in the substitution arrays to be less than
256.  The values in the substitution arrays will represent a stair-step
arrangement of values with several adjacent elements containing the same value
at each level.  Listing 26 computes the number of adjacent elements that
will contain the same value on the basis of the number of levels.

    int binSize = 256/numberLevels;

Listing 26

Create the substitution array objects

As you can see, the output image
in the left panel is somewhat lighter than the input image.  Setting the
Slope
to 1.0 would cause the output image to be an unmodified copy of the
input image.  Setting the Slope to lower values would cause the
output image to be progressively lighter, with a value of 0.1 causing the output
image to be almost completely white.

Log Transform

The right panel in Figure 10 shows the result of filtering an image by
checking all three checkboxes and selecting the Log Transform radio
button in the Custom Transforms page shown in Figure 4.

Similar to decibel conversion
For those who may be interested, this is somewhat analogous to
converting a 3D surface to decibels through a log base 10 conversion of the
elevation values.  See the earlier lesson entitled
Plotting
3D Surfaces using Java
.

This option doesn’t allow any user input parameters (such as the Slope in
the Linear Transform)
.  As you can see, the output image is very light
in the bottom-right panel of Figure 10.  Thus, the log transform is a
rather severe lightening filter.

The method named constructPage02

The method named constructPage02 is shown in its entirety in Listing 31.  This method
is used to construct the page shown in Figure 4.  The method is called from the primary constructor
in Listing 10.

  void constructPage02(){
    page02.setName("Custom Transforms");//Tab label.
    page02.setLayout(new BorderLayout());
    
    //Create and add the instructional text to the page.
    String text = "CUSTOM TRANSFORMSn"
      + "This page illustrates the use of two different "
      + "custom transforms, one based on the log to the "
      + "base 10, and the other based on the equation of "
      + "a straight line.  Both transforms are designed "
      + "to boost the intensity of pixels with low color "
      + "values.nn"
      + "Specify the color bands that you want to process "
      + "by checking the checkboxes.nn"
      + "Select a radio button to select a transform.  "
      + "For the linear transform, enter a positive slope "
      + "that is <= 1.0.  Try a slope of 0.85 for "
      + "examplenn"
      + "Then click the Replot button.";
      
    //Note:  The number of columns specified for the
    // following TextArea is immaterial because the
    // TextArea object is placed in the NORTH location of
    // a BorderLayout.
    TextArea textArea = new TextArea(text,9,1,
                                 TextArea.SCROLLBARS_NONE);
    page02.add(textArea,BorderLayout.NORTH);
    textArea.setEnabled(false);
    
    //Construct the control panel and add it to the page.
    Panel page02ControlPanel = new Panel();
    page02ControlPanel.setLayout(new GridLayout(3,1));
    
    Panel subControlPanel00 = new Panel();
    subControlPanel00.add(page02RedCkBx);
    subControlPanel00.add(page02GreenCkBx);
    subControlPanel00.add(page02BlueCkBx);
    page02ControlPanel.add(subControlPanel00);
    
    Panel subControlPanel01 = new Panel();
    subControlPanel01.setLayout(
                          new FlowLayout(FlowLayout.LEFT));
    subControlPanel01.add(page02LogRa
    page02ControlPanel.add(subControlPanel01);
    
    Panel subControlPanel02 = new Panel();
    subControlPanel02.setLayout(
                          new FlowLayout(FlowLayout.LEFT));
    subControlPanel02.add(page02LinearRadioButton);
    subControlPanel02.add(new Label("  Slope ="));
    subControlPanel02.add(page02TextField);
    subControlPanel02.add(
                   new Label("must be >'= 0.0 and <= 1.0"));
    page02ControlPanel.add(subControlPanel02);
    
    page02.add(page02ControlPanel,BorderLayout.CENTER);
    
  }//end constructPage02

Listing 31

Tedious but straightforward

Although somewhat tedious, the code in Listing 31 is straightforward. 
If you use Figure 4 as a guide, you should have no trouble understanding the
code in Listing 31.

The method named processPage02

The method named processPage02 begins in Listing 32.  This method processes the image according to the controls
shown on the Custom Transforms page in Figure 4.  This method is called from within a
switch statement in Listing 14.  Note that as with the pages in
Figure 2 and Figure 3, this method processes the image using three different
substitution arrays, one each for red, green, and blue.

The method named processPage02 transforms the values in the color bands according to either a
log transform, or a linear transform. 
The choice of which transform algorithm to use is determined by the radio button
selected by the user in Figure 4.

The net effect of both transforms is to emphasize or boost the intensity of colors having low values, thus causing the image to become brighter. 
However, the two transforms achieve this effect in different ways.

Create substitution array objects

Listing 32 creates array objects that will be populated with data that is
later used to populate the lookup table.  By default these arrays are populated with all zero
values.

  BufferedImage processPage02(BufferedImage theImage){
    
    short[] red = new short[256];
    short[] green = new short[256];
    short[] blue = new short[256];

Listing 32

Create and populate the master data array object

Listing 33 shows the beginning of the algorithm that will create and populate an array object with master data that will be used to populate the specific arrays for the colors that are selected to be
processed.

    short[] masterData = new short[256];
    for(int cnt = 0;cnt < 256;cnt++){
      short value = 0;

Listing 33

Populate according to linear or log selection

Listing 34 shows the if portion of an if-else statement that is
used to select between a log or linear transform based on the state of the two radio
buttons in Figure 4.  The code in Listing 34 creates the substitution data
values for a log conversion of the color values.

      if(page02LogRadioButton.getState() == true){
        //Perform a log conversion
        if(cnt == 0){
          //Avoid computing the log of 0.  Substitute the
          // log of 1 instead.  (Note that with J2SE 5.0,
          // I could have used a static import directive
          // in order to eliminate the explicit reference
          // to the Math class in the following
          // expressions.)
          value =
              (short)(Math.log10(1.0)*255/Math.log10(255));
        }else{
          value = 
              (short)(Math.log10(cnt)*255/Math.log10(255));
        }//end else

Listing 34

Purpose is not to explain logarithms

Since my purpose here is not to teach you about logarithms, I won’t attempt
to explain the rationale behind the code in Listing 34.  If you already
understand logarithms, you should have no trouble understanding the code in
Listing 34.  If not, just accept the code in Listing 34 as one of many
possible transform algorithms that can be used to convert the color values in
the input image into a different set of color values in the output image through
substitution.

The else clause

Listing 35 shows the else portion of the if-else statement that
began in Listing 34.

      }else{//Linear conversion must have been selected
        //Perform a linear conversion
        double slope = 0;
        try{//Get the slope from the text field.
          slope = 
             Double.parseDouble(page02TextField.getText());
        }catch(java.lang.NumberFormatException e){
          page02TextField.setText("Bad Input");
          slope = 0.0;//Override user input on bad input.
        }//end catch

        //Guarantee that the slope is positive and <= 1.0.
        if((slope < 0.0) || (slope >' 1.0)){
          page02TextField.setText("Bad Input");
          slope = 0.0;//Override user input on bad input.
        }//end if
        
        //Compute the intercept of a straight line with the
        // y-axis using the slope provided by the user. 
        // Cause the line to go through a y-value of 255
        // at an x-value of 255.
        int yIntercept = (int)(255.0 - 255.0*slope);
        
        //Compute the value of y for each value of x(cnt)
        // using the equation of a straight line, which
        // is, y = slope*x + yIntercept
        value = (short)(slope*cnt + yIntercept);
        
        //Guard against roundoff errors that might cause
        // the color values to go slightly outside their
        // allowed range of 0 through 255.
        if(value < 0) value = 0;
        if(value >' 255) value = 255;
      }//end else

      masterData[cnt] = value;
    }//end for loop

Listing 35

The code in Listing 35 creates a set of substitution values based on the
slope and the y-intercept using the equation for a straight line.

Purpose is not to explain analytical geometry

Once again, since my purpose here is not to teach you analytical geometry, I
won’t attempt to explain the rationale behind the code in Listing 35.  If
you already understand such things as the equation of a straight line, you
should have no trouble understanding the code in Listing 35.  If not, just
accept the code in Listing 35 as another one of many possible transform
algorithms that can be used to convert the color values in the input image into
a different set of color values in the output image through substitution.

Many different custom transforms are possible

There are many ways to come up with the substitution values for the custom
transforms and the purpose of this section of the lesson is simply to
illustrate two of them.  You may find other ways to develop substitution
values that better serve your image-processing needs.

Wrapping it up

The remainder of the processPage02 method is shown in Listing 36
This code is very similar to the code in the previously-explained methods, and
therefore shouldn’t require further explanation.

    //Examine the check boxes.  If any checkbox has been
    // checked, reset the corresponding array to point it
    // to the array containing the master data.  Otherwise,
    // it will contain all zero values by default.
    if(page02RedCkBx.getState() == true){
      red = masterData;
    }//end if
    if(page02GreenCkBx.getState() == true){
      green = masterData;
    }//end if
    if(page02BlueCkBx.getState() == true){
      blue = masterData;
    }//end if
    
    //Process the image and return the processed result.
    return processImageForThePage(theImage,red,green,blue);

  }//end processPage02

Listing 36

Listing 36 signals the end of the explanation for the program named
ImgMod39
.

Run the Programs

I encourage you to copy the code from
Listing 37 and Listing 38 into your text
editor, compile it, and execute it.  Experiment with it, making
changes, and observing the results of your changes.

Remember, you will also need to compile the code for the framework program
named ImgMod05 and the interface named ImgIntfc05.  You will
find that source code in the earlier lesson entitled
A Framework for
Experimenting with Java 2D Image-Processing Filters
.

You will also need one or more JPEG image files to experiment with.  You
should have no difficulty finding such files at a variety of locations on the
web.  I recommend that you stick with relatively small images so that both
the original image and the processed image will fit in the vertical space on
your screen.

Summary

In this
lesson, I provided and explained two different image-processing programs that
are compatible with the framework program named ImgMod05.  The
purpose of these two programs is to show you how to write such programs, and
also to illustrate a variety of different uses for the LookupOp class of
the Java 2D API.  Three specific uses of the LookupOp class were
illustrated, and you should be able to devise many more.

Along the way, I also showed you how to construct and use a JTabbedPane
object as a program GUI.  (If my memory serves me correctly, this is the
first time that I have used a JTabbedPane in a lesson that I have
published.)

What’s Next?

Future lessons in this series will teach you how to use the following image-filtering
classes from the Java 2D API:

  • AffineTransformOp
  • BandCombineOp
  • ConvolveOp
  • RescaleOp
  • ColorConvertOp

References

  • 400
    Processing Image Pixels using Java, Getting Started
  • 402
    Processing Image Pixels using Java, Creating a Spotlight
  • 404
    Processing Image Pixels Using Java: Controlling Contrast and Brightness
  • 406
    Processing Image Pixels, Color Intensity, Color Filtering, and Color
    Inversion
  • 408
    Processing Image Pixels, Performing Convolution on Images
  • 410
    Processing Image Pixels, Understanding Image Convolution in Java
  • 412
    Processing Image Pixels, Applying Image Convolution in Java, Part 1
  • 414
    Processing Image Pixels, Applying Image Convolution in Java, Part 2
  • 416 Processing Image Pixels, An Improved Image-Processing Framework in
    Java
  • 450 A Framework for Experimenting with Java 2D Image-Processing
    Filters

Complete Program Listings


Complete listings of the programs discussed in this lesson are shown in
Listing
37
and Listing 38 below.
 

/*File ImgMod38.java
Copyright 2006, R.G.Baldwin

The purpose of this class is to provide a simple example of
an image processing class that is compatible with the use
of the program named ImgMod05, and which illustrates a
single usage of the LookupOp class from the image
processing portion of the Java 2D API.  (Future programs
will illustrate other uses of the LookupOp class.)

A class that is compatible with ImgMod05 is required to
implement the interface named ImgIntfc05. This, in turn,
requires the class to define the method named processImg,
which receives one parameter of type BufferedImage and
returns a reference of type BufferedImage.

The required signature for the processImg method is:

public BufferedImage processImg(BufferedImage input);

The processImg method receives a reference to a
BufferedImage object containing the image that is to be
processed

The processImg method must return a reference to a
BufferedImage object containing the processed image.

In this example, the method named processImg is a color
inverter method.

The method named processImg as defined in this class
receives an incoming reference to an image as a parameter
of type BufferedImage.  The method returns a reference to
an image as type BufferedImage where all of the color
values in the pixels have been inverted by subtracting the
color values from 255.

The method has been demonstrated to work properly only for
the case where the incoming BufferedImage object was
constructed for image type BufferedImage.TYPE_INT_RGB. 
However, it may work properly for other image types as
well.

Note that this class does not define a constructor.
However, if it did define a constructor, that constructor
would not be allowed to receive parameters. This is because
the class named ImgMod05 instantiates an object of this
class by invoking the newInstance method of the Class
class passing the name of this class to the newInstance
method as a String parameter.  That process does not allow
for constructor parameters for the class being
instantiated.

The driver program named ImgMod05 displays the original and
the modified images.  It also writes the modified image
into an output file in JPEG format.  The name of the output
file is junk.jpg and it is written into the current
directory.

The output GUI for the driver program named ImgMod05
contains a Replot button.  At the beginning of the run, and
each time thereafter that the Replot button is clicked:
-The image processing method belonging to the image
 processing object is invoked,
-The resulting modified image is displayed along with the
 original image.

Image processing programs such as this one may provide a
GUI for data input making it possible for the user to
modify the behavior of the image processing method each
time the Replot button is clicked.  However, no such GUI
is provided by this program and clicking the Replot
button is of no consequence.

The driver program named ImgMod05 reads gif and jpg input
files and possibly some other input file types as well. 
The output file is always a JPEG file.

Usage:

Enter the following at the command line to run this
program:

java ImgMod05 ImgMod38 ImageFileName

The image file must be provided by the user.  However, it
doesn't have to be in the current directory if a path to
the file is included along with the file name on the
command line.

When the program is started, the original image and the
processed version of the image are displayed in a frame
with the original image above the processed image.

The driver program named ImgMod05 attempts to adjust the
size of the display frame to accommodate both images.  If
the processed image doesn't fit in the display, the user
can manually resize the display frame in order to view both
images.

If the program is unable to load the image file within ten
seconds, it will abort with an error message.

Tested using J2SE5.0 under WinXP.
**********************************************************/

import java.awt.image.*;

class ImgMod38 implements ImgIntfc05{

  //The following method must be defined to implement the
  // ImgIntfc05 interface.
  public BufferedImage processImg(BufferedImage theImage){

    //Use the LookupOp class from the Java 2D API to
    // invert all of the color values in the pixels.  The
    // alpha value is not modified.

    //Create the data for the lookup table.
    short[] lookupData = new short[256];
    for (int cnt = 0; cnt < 256; cnt++){
      lookupData[cnt] = (short)(255-cnt);
    }//end for loop
   
    //Create the lookup table
    ShortLookupTable lookupTable =
                        new ShortLookupTable(0,lookupData);

    //Create the filter object.
    BufferedImageOp thresholdOp =
                            new LookupOp(lookupTable,null);

    //Apply the filter to the incoming image and return
    // a reference to the resulting BufferedImage object.
    return thresholdOp.filter(theImage, null);
  }//end processImg

}//end class ImgMod38


Listing 37

Listing 38

/*File ImgMod39.java
Copyright 2006, R.G.Baldwin

The purpose of this class is to illustrate a variety of
different uses for the LookupOp class of the Java 2D API.
In each case, the program uses three data arrays to
process the red, green, and blue color bands individually
or in combination.

See general comments in the class named ImgMod038.

This class is compatible with the use of the driver program
named ImgMod05.

The driver program named ImgMod05 displays the original and
the modified images.  It also writes the modified image
into an output file in JPEG format.  The name of the output
file is junk.jpg and it is written into the current
directory.

Image-processing programs such as this one may provide a
GUI for user data input making it possible for the user to
modify the behavior of the image-processing method each
time the Replot button is clicked.  Such a GUI is provided
for this program.

Enter the following at the command line to run this
program:

java ImgMod05 ImgMod39 ImageFileName

If the program is unable to load the image file within ten
seconds, it will abort with an error message.

This program creates a GUI consisting of a tabbed pane
containing three pages.  The tabs on the pages are labeled:

Color Inversion
Posterizing
Custom Transforms

Each page contains a set of controls that make it possible
to process the image in a way that illustrates the
processing concept indicated by the labels on the tabs.
Processing details for each page are provided in the
comments in the code used to construct and process the
individual pages.

Tested using J2SE 5.0 under WinXP.
**********************************************************/

import java.awt.image.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class ImgMod39 extends Frame implements ImgIntfc05{
  //Primary container used to construct the GUI.
  JTabbedPane tabbedPane = new JTabbedPane();
 
  //Components used to construct the page in the
  // JTabbedPane that shows Color Inversion on the tab.
  // Components that require local access only are defined
  // locally.  Others are defined here as instance
  // variables.
  Panel page00 = new Panel();
  Checkbox page00RedCkBx = new Checkbox("Red");
  Checkbox page00GreenCkBx = new Checkbox("Green");
  Checkbox page00BlueCkBx = new Checkbox("Blue");

  //Components used to construct the Posterizing page in
  // the JTabbedPane. Components that require local access
  // only are defined locally.  Others are defined here as
  // instance variables.
  Panel page01 = new Panel();
  TextField page01TextField = new TextField("128",6);
  Checkbox page01RedCkBx = new Checkbox("Red");
  Checkbox page01GreenCkBx = new Checkbox("Green");
  Checkbox page01BlueCkBx = new Checkbox("Blue");
 
  //Components used to construct the Custom Transforms
  // page in the JTabbedPane.  Components that require
  // local access only are defined locally.  Others are
  // defined here as instance variables.
  Panel page02 = new Panel();
  TextField page02TextField = new TextField("1.0",6);
  Checkbox page02RedCkBx = new Checkbox("Red");
  Checkbox page02GreenCkBx = new Checkbox("Green");
  Checkbox page02BlueCkBx = new Checkbox("Blue");
  CheckboxGroup group = new CheckboxGroup();
  Checkbox page02LogRadioButton =
                  new Checkbox("Log Transform",group,true);
  Checkbox page02LinearRadioButton =
              new Checkbox("Linear Transform",group,false);
 
  //-----------------------------------------------------//
 
  //This is the primary constructor.  It calls other
  // methods to separate the construction of the GUI into
  // easily understandable units.  Each method that it
  // calls constructs one page in the tabbed pane.
  ImgMod39(){//constructor
 
    constructPage00();
    tabbedPane.add(page00);//Add page to the tabbedPane.
   
    constructPage01();
    tabbedPane.add(page01);//Add page to the tabbedPane.
   
    constructPage02();
    tabbedPane.add(page02);//Add page to the tabbedPane.
   
    add(tabbedPane);//Add tabbedPane to the Frame.

    setTitle("Copyright 2006, R.G.Baldwin");
    setBounds(555,0,470,300);
    setVisible(true);

    //Define a WindowListener to terminate the program.
    addWindowListener(
      new WindowAdapter(){
        public void windowClosing(WindowEvent e){
          System.exit(1);
        }//end windowClosing
      }//end windowAdapter
    );//end addWindowListener
  }//end constructor
  //-----------------------------------------------------//
 
  //This method constructs the page in the tabbed pane that
  // shows Color Inversion on the tab.  This method is
  // called from the primary constructor.  It illustrates
  // color inversion using three arrays, one for each of
  // the red, green, and blue color bands.
  void constructPage00(){
    page00.setName("Color Inversion");//Label on the tab.
    page00.setLayout(new BorderLayout());
   
    //Create and add the instructional text to the page.
    // This text appears in a disabled text area at the
    // top of the page in the tabbed pane.
    String text ="COLOR INVERSION USING MULTIPLE ARRAYSn"
      + "This page illustrates the use of the LookupOp "
      + "filter class for multiple lookup arrays.  By "
      + "checking the appropriate checkboxes below, you "
      + "can cause the program to invert the colors on "
      + "none, or on any combination of color bands.";
     
    //Note:  The number of columns specified for the
    // following TextArea is immaterial because the
    // TextArea object is placed in the NORTH location of
    // a BorderLayout.
    TextArea textArea = new TextArea(text,4,1,
                                 TextArea.SCROLLBARS_NONE);
    page00.add(textArea,BorderLayout.NORTH);
    textArea.setEnabled(false);

    //Construct the control panel and add it to the page.
    Panel page00ControlPanel = new Panel();
    page00ControlPanel.add(page00RedCkBx);
    page00ControlPanel.add(page00GreenCkBx);
    page00ControlPanel.add(page00BlueCkBx);
    page00.add(page00ControlPanel,BorderLayout.CENTER);
  }//end constructPage00
  //-----------------------------------------------------//

  //This method processes the image according to the
  // controls located on the page in the tabbed pane that
  // shows Color Inversion on the tab.
  //This method uses the LookupOp image-filtering class to
  // process the image using lookup data from three
  // separate arrays, one each for the red, green, and blue
  // color bands.
  //This method is called from within a switch statement in
  // the method named processImg, which is the primary
  // image-processing method in this program.
  //This method illustrates color inversion.  The effect of
  // color inversion is to produce an output in which the
  // image is similar to the negative of a color
  // photograph.
  BufferedImage processPage00(BufferedImage theImage){
    //Create array objects that will be populated with data
    // that is used later to populate the lookup table.
    short[] red = null;
    short[] green = null;
    short[] blue = null;
   
    //Create and populate arrays for straight (noInvert)
    // lookup data and inverted lookup data.
    short[] noInvert = new short[256];
    short[] invert = new short[256];
    for(int cnt = 0;cnt < 256;cnt++){
      invert[cnt] = (short)(255 - cnt);//inverted data
      noInvert[cnt] = (short)cnt;//straight lookup data
    }//end for loop
   
    //Point the three color arrays to the array containing
    // the data that doesn't invert the colors.  This is
    // the default case if no checkboxes are checked.
    red = noInvert;
    green = noInvert;
    blue = noInvert;
   
    //Examine the check boxes.  If any checkbox has been
    // checked, point the corresponding color array to the
    // array containing the inversion data.
    if(page00RedCkBx.getState() == true){
      red = invert;
    }//end if
    if(page00GreenCkBx.getState() == true){
      green = invert;
    }//end if
    if(page00BlueCkBx.getState() == true){
      blue = invert;
    }//end if

    //Use the LookupOp class from the Java 2D API along
    // with three separate data arrays to process the
    // color values in the selected color bands.  The
    // alpha value is not modified.
    return processImageForThePage(theImage,red,green,blue);

  }//end processPage00
  //-----------------------------------------------------//
 
  //This method constructs the page in the tabbed pane that
  // shows Posterizing on the tab.  This method is called
  // from the primary constructor.  It illustrates
  // posterizing.
  //Posterizing is a process of reducing the number of
  // colors in an image to a relatively small number.  The
  // effect is to cause the image to look similar to a
  // painting that was painted using a "paint by numbers"
  // set where the smooth transitions from one color to the
  // next have been eliminated.  This is accomplished by
  // limiting the number of different colors that appear
  // in the image.
  void constructPage01(){
    page01.setName("Posterizing");//Label on the tab.
    page01.setLayout(new BorderLayout());
   
    //Create and add the instructional text to the page.
    String text = "POSTERIZINGn"
      + "The effect of posterizing is to cause the image "
      + "to look similar to a painting that was painted "
      + "using a "paint by numbers" set where the smooth"
      + "transitions from one color to the next have been "
      + "eliminated.  This is accomplished by limiting "
      + "the number of different colors that appear in "
      + "the image.nn"
      + "To posterize an image, decide how many levels "
      + "you want to see for each of the three colors, "
      + "red, green, and blue.  Enter that number in the "
      + "text field below.  Select the colors that you "
      + "want to mix by checking the matching check boxes "
      + "and click the Replot button.";
     
    //Note:  The number of columns specified for the
    // following TextArea is immaterial because the
    // TextArea object is placed in the NORTH location of
    // a BorderLayout.
    TextArea textArea = new TextArea(text,9,1,
                                 TextArea.SCROLLBARS_NONE);
    page01.add(textArea,BorderLayout.NORTH);
    textArea.setEnabled(false);
   
    //Construct the control panel and add it to the page.
    Panel page01ControlPanel = new Panel();
    page01ControlPanel.add(page01TextField);
    page01ControlPanel.add(page01RedCkBx);
    page01ControlPanel.add(page01GreenCkBx);
    page01ControlPanel.add(page01BlueCkBx);
    page01.add(page01ControlPanel,BorderLayout.CENTER);
   
  }//end constructPage01
  //-----------------------------------------------------//

  //This method processes the image according to the
  // controls located on the page in the tabbed pane that
  // shows Posterizing on the tab.  This method is called
  // from within a switch statement in the method named
  // processImg.  Note that this method processes the image
  // using three arrays.
  BufferedImage processPage01(BufferedImage theImage){

    int numberLevels = 1;
    try{//Get input value from the text field.
      numberLevels =
               Integer.parseInt(page01TextField.getText());
    }catch(java.lang.NumberFormatException e){
      page01TextField.setText("Bad Input");
      numberLevels = 1;//Override bad user input.
    }//end catch
   
    //Guarantee that the number of levels falls within the
    // allowable range.  Don't allow divison by 0.
    if((numberLevels <= 0) || (numberLevels > 256)){
      page01TextField.setText("Bad Input");
      numberLevels = 1;//Override bad user input.
    }//end if
   
    //Compute the number of adjacent elements that will
    // specify the same color value.
    int binSize = 256/numberLevels;
   
    //Create array objects that will be populated with
    // data that is used to populate the lookup table.
    //Note that by default these arrays are populated with
    // all zero values.
    short[] red = new short[256];
    short[] green = new short[256];
    short[] blue = new short[256];
   
    //Create and populate an array object with master data
    // that will be used to populate the specific arrays
    // for the colors that are to be processed.
    short[] masterData = new short[256];
    for(int cnt = 0;cnt < 256;cnt++){
      short value =
              (short)((cnt/binSize)*binSize + binSize - 1);
      //Clip the values at 0 and 255.
      if(value >= 256) value = 255;
      if(value < 0) value = 0;//Probably not possible.
      masterData[cnt] = value;
    }//end for loop

    //Examine the check boxes.  If any checkbox has been
    // checked, reset the corresponding array to point it
    // to the array containing the master data.  Otherwise,
    // it will contain all zero values by default.
    if(page01RedCkBx.getState() == true){
      red = masterData;
    }//end if
    if(page01GreenCkBx.getState() == true){
      green = masterData;
    }//end if
    if(page01BlueCkBx.getState() == true){
      blue = masterData;
    }//end if
   
    //Process the image and return the result.
    return processImageForThePage(theImage,red,green,blue);

  }//end processPage01
  //-----------------------------------------------------//
 
  //This method constructs the page in the tabbed pane that
  // shows Custom Transforms on the tab.  This method is
  // called from the primary constructor.  This page
  // illustrates the use of custom transformations of the
  // values in the color bands.
  void constructPage02(){
    page02.setName("Custom Transforms");//Tab label.
    page02.setLayout(new BorderLayout());
   
    //Create and add the instructional text to the page.
    String text = "CUSTOM TRANSFORMSn"
      + "This page illustrates the use of two different "
      + "custom transforms, one based on the log to the "
      + "base 10, and the other based on the equation of "
      + "a straight line.  Both transforms are designed "
      + "to boost the intensity of pixels with low color "
      + "values.nn"
      + "Specify the color bands that you want to process "
      + "by checking the checkboxes.nn"
      + "Select a radio button to select a transform.  "
      + "For the linear transform, enter a positive slope "
      + "that is <= 1.0.  Try a slope of 0.85 for "
      + "examplenn"
      + "Then click the Replot button.";
     
    //Note:  The number of columns specified for the
    // following TextArea is immaterial because the
    // TextArea object is placed in the NORTH location of
    // a BorderLayout.
    TextArea textArea = new TextArea(text,9,1,
                                 TextArea.SCROLLBARS_NONE);
    page02.add(textArea,BorderLayout.NORTH);
    textArea.setEnabled(false);
   
    //Construct the control panel and add it to the page.
    Panel page02ControlPanel = new Panel();
    page02ControlPanel.setLayout(new GridLayout(3,1));
   
    Panel subControlPanel00 = new Panel();
    subControlPanel00.add(page02RedCkBx);
    subControlPanel00.add(page02GreenCkBx);
    subControlPanel00.add(page02BlueCkBx);
    page02ControlPanel.add(subControlPanel00);
   
    Panel subControlPanel01 = new Panel();
    subControlPanel01.setLayout(
                          new FlowLayout(FlowLayout.LEFT));
    subControlPanel01.add(page02LogRadioButton);
    page02ControlPanel.add(subControlPanel01);
   
    Panel subControlPanel02 = new Panel();
    subControlPanel02.setLayout(
                          new FlowLayout(FlowLayout.LEFT));
    subControlPanel02.add(page02LinearRadioButton);
    subControlPanel02.add(new Label("  Slope ="));
    subControlPanel02.add(page02TextField);
    subControlPanel02.add(
                   new Label("must be >= 0.0 and <= 1.0"));
    page02ControlPanel.add(subControlPanel02);
   
    page02.add(page02ControlPanel,BorderLayout.CENTER);
   
  }//end constructPage02
  //-----------------------------------------------------//

  //This method processes the image according to the
  // controls located on the page in the tabbed pane that
  // shows Custom Transforms on the tab.  This method is
  // called from within a switch statement in the method
  // named processImg.  Note that this method processes the
  // image using three arrays.  It transforms the values
  // in the color bands according to either a log
  // transform, or a linear transform, with the choice
  // being made by the user through the selection of a
  // radio button.  The net effect of both transforms is to
  // emphasize or boost the intensity of colors having low
  // values, thus causing the image to become brighter.
  // The two transforms achieve this effect in different
  // ways, however.
  BufferedImage processPage02(BufferedImage theImage){
   
    //Create array objects that will be populated with
    // data that is used to populate the lookup table.
    // Note that by default these arrays are populated with
    // all zero values.
    short[] red = new short[256];
    short[] green = new short[256];
    short[] blue = new short[256];
   
    //Create and populate an array object with master data
    // that will be used to populate the specific arrays
    // for the colors that are selected to be processed.
    short[] masterData = new short[256];
    for(int cnt = 0;cnt < 256;cnt++){
      short value = 0;
      //Select between log or linear transformation based
      // on the state of two radio buttons.     
      if(page02LogRadioButton.getState() == true){
        //Perform a log conversion
        if(cnt == 0){
          //Avoid computing the log of 0.  Substitute the
          // log of 1 instead.  (Note that with J2SE 5.0,
          // I could have used a static import directive
          // in order to eliminate the explicit reference
          // to the Math class in the following
          // expressions.)
          value =
              (short)(Math.log10(1.0)*255/Math.log10(255));
        }else{
          value =
              (short)(Math.log10(cnt)*255/Math.log10(255));
        }//end else
      }else{//Linear conversion must have been selected
        //Perform a linear conversion
        double slope = 0;
        try{//Get the slope from the text field.
          slope =
             Double.parseDouble(page02TextField.getText());
        }catch(java.lang.NumberFormatException e){
          page02TextField.setText("Bad Input");
          slope = 0.0;//Override user input on bad input.
        }//end catch

        //Guarantee that the slope is positive and <= 1.0.
        if((slope < 0.0) || (slope > 1.0)){
          page02TextField.setText("Bad Input");
          slope = 0.0;//Override user input on bad input.
        }//end if
       
        //Compute the intercept of a straight line with the
        // y-axis using the slope provided by the user.
        // Cause the line to go through a y-value of 255
        // at an x-value of 255.
        int yIntercept = (int)(255.0 - 255.0*slope);
       
        //Compute the value of y for each value of x(cnt)
        // using the equation of a straight line, which
        // is, y = slope*x + yIntercept
        value = (short)(slope*cnt + yIntercept);
       
        //Guard against roundoff errors that might cause
        // the color values to go slightly outside their
        // allowed range of 0 through 255.
        if(value < 0) value = 0;
        if(value > 255) value = 255;
      }//end else

      masterData[cnt] = value;
    }//end for loop

    //Examine the check boxes.  If any checkbox has been
    // checked, reset the corresponding array to point it
    // to the array containing the master data.  Otherwise,
    // it will contain all zero values by default.
    if(page02RedCkBx.getState() == true){
      red = masterData;
    }//end if
    if(page02GreenCkBx.getState() == true){
      green = masterData;
    }//end if
    if(page02BlueCkBx.getState() == true){
      blue = masterData;
    }//end if
   
    //Process the image and return the processed result.
    return processImageForThePage(theImage,red,green,blue);

  }//end processPage02
  //-----------------------------------------------------//
 
  //Use the LookupOp class from the Java 2D API along
  // with three separate data arrays to process the
  // color values in the selected color bands.  The
  // alpha value is not modified.  This is a common method
  // that is called by the code that processes each
  // individual page in the tabbed pane.
  BufferedImage processImageForThePage(
                                    BufferedImage theImage,
                                    short[] red,
                                    short[] green,
                                    short[] blue){
    //Create and populate a 2D array with data for the
    // lookup table.  Note that this is a 2D array, rather
    // than a 1D array, which is the case when a single
    // data array is used to process all three color bands.
    short[][] lookupData = new short[][]{red,green,blue};

    //Create the lookup table.  The first parameter is an
    // offset for extracting data from the array object.
    //In this case, all of the data is extracted from the
    // array object beginning at an index of 0.
    ShortLookupTable lookupTable =
                        new ShortLookupTable(0,lookupData);

    //Create the filter object. The second parameter
    // provides the opportunity to use RenderingHints.
    BufferedImageOp filterObject =
                            new LookupOp(lookupTable,null);
                           
    //For illustration purposes only, work backwards from
    // the filterObject to get and display some data
    // from the lookup table.  Note that this is not an
    // image-processing requirement.
    displayTableData(filterObject);

    //Apply the filter to the incoming image and return
    // a reference to the resulting BufferedImage object.
    // The second parameter can optionally specify an
    // existing BufferedImage object to serve as a
    // destination for the processed image.
    return filterObject.filter(theImage, null);
  }//end processImageForThePage
  //-----------------------------------------------------//

  //Print some column headers followed by data from the
  // lookup table.  Print every 32nd row beginning with
  // row 0.  Then print the data for row 255.
  void displayTableData(BufferedImageOp filterObject){
   
    //First, get a 2D array containing data from the lookup
    // table.
    ShortLookupTable theTable = ((ShortLookupTable)(
                       (LookupOp)filterObject).getTable());
    short[][] tableData = theTable.getTable();
   
    System.out.println("RowtRedtGreentBlue");
    for(int row = 0;
        row<tableData[0].length;
        row += 32){
        System.out.print((row) + "t");
      for(int col = 0;
          col<tableData.length;
          col++){
        System.out.print(tableData[col][row] + "t");
      }//end inner loop
      System.out.println();
    }//end outer loop
    System.out.println(255 + "t" + tableData[0][255]
                       + "t" + tableData[1][255] + "t"
                       + tableData[2][255]);
   
  }//end displayTableData
 
  //-----------------------------------------------------//

  //The following method must be defined to implement the
  // ImgIntfc05 interface.  It is called by the framework
  // program named ImgMod05.
  public BufferedImage processImg(BufferedImage theImage){
   
    BufferedImage outputImage = null;
   
    //Process the page in the tabbed pane that has been
    // selected by the user.
    switch(tabbedPane.getSelectedIndex()){
      case 0:outputImage = processPage00(theImage);
             break;
      case 1:outputImage = processPage01(theImage);
             break;
      case 2:outputImage = processPage02(theImage);
             break;
    }//end switch

    return outputImage;
  }//end processImg

}//end class ImgMod39

Listing 38

Copyright 2006, Richard G. Baldwin.  Reproduction in whole or in part in any
form or medium without express written permission from Richard Baldwin is
prohibited.

About the author

Richard Baldwin is a
college professor (at Austin Community College in Austin, TX) and private
consultant whose primary focus is a combination of Java, C#, and XML. In
addition to the many platform and/or language independent benefits of Java and
C# applications, he believes that a combination of Java, C#, and XML will become
the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he
frequently provides onsite training at the high-tech companies located in and
around Austin, Texas.  He is the author of Baldwin’s Programming
Tutorials, which have gained a
worldwide following among experienced and aspiring programmers. He has also
published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of
practical experience in Digital Signal Processing (DSP).  His first job after he
earned his Bachelor’s degree was doing DSP in the Seismic Research Department of
Texas Instruments.  (TI is still a world leader in DSP.)  In the following
years, he applied his programming and DSP expertise to other interesting areas
including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has
many years of experience in the application of computer technology to real-world
problems.

[email protected]

Latest Posts

Related Stories