October 21, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Processing Image Pixels, Understanding Image Convolution in Java

  • January 24, 2006
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

References

In preparation for understanding the material in this lesson, I recommend that you study the material in the following previously-published lessons:

  • 100   Periodic Motion and Sinusoids
  • 104   Sampled Time Series
  • 108   Averaging Time Series
  • 1478 Fun with Java, How and Why Spectral Analysis Works
  • 1482 Spectrum Analysis using Java, Sampling Frequency, Folding Frequency, and the FFT Algorithm
  • 1483 Spectrum Analysis using Java, Frequency Resolution versus Data Length
  • 1484 Spectrum Analysis using Java, Complex Spectrum and Phase Angle
  • 1485 Spectrum Analysis using Java, Forward and Inverse Transforms, Filtering in the Frequency Domain
  • 1487 Convolution and Frequency Filtering in Java
  • 1488 Convolution and Matched Filtering in Java
  • 1489 Plotting 3D Surfaces using Java
  • 1490 2D Fourier Transforms using Java
  • 1491 2D Fourier Transforms using Java, Part 2
  • 1492 Plotting Large Quantities of Data using Java
  • 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

Complete Program Listing

A complete listing of the class discussed in this lesson is provided in Listing 20.

A disclaimer

The programs that I am providing and explaining in this series of lessons are not intended to be used for high-volume production work.  Numerous integrated image-processing programs are available for that purpose.  In addition, the Java Advanced Imaging API (JAI) has a number of built-in special effects if you prefer to write your own production image-processing programs using Java.

The programs that I am providing in this series are intended to make it easier for you to develop and experiment with image-processing algorithms and to gain a better understanding of how they work, and why they do what they do.

/*File ImgMod32.java
Copyright 2005, R.G.Baldwin

This class provides a general purpose 2D image convolution 
capability in the form of a static method named convolve.

The convolve method that is defined in this class receives 
an incoming 3D array of image pixel data of type int 
containing four planes. The format of this image data is 
consistent with the format for image data used in the 
program named ImgMod02a.

The planes are identified as follows:
0 - alpha or transparency data
1 - red color data
2 - green color data
3 - blue color data

The convolve method also receives an incoming 2D array of 
type double containing the weights that make up a 2D 
convolution filter.

The pixel values on each color plane are convolved 
separately with the same convolution filter.  

The results are normalized so as to cause the filtered 
output to fall within the range from 0 to 255.

The values on the alpha plane are not modified.

The method returns a filtered 3D pixel array in the same 
format as the incoming pixel array.  The returned array 
contains filtered values for each of the three color 
planes.

The method does not modify the contents of the incoming 
array of pixel data.

An unfiltered dead zone equal to half the filter length is
left around the perimeter of the filtered image to avoid
any attempt to perform convolution using data outside the
bounds of the image.

Although this class is intended to be used to implement 2D 
convolution in other programs, a main method is provided so
that the class can be tested in a stand-alone mode.  In 
addition, the main method illustrates the relationship 
between convolution in the image domain and the 
wave-number spectrum of the raw and filtered image.

When run as a stand-alone program, this class displays raw 
surfaces, filtered surfaces, and the Fourier Transform of 
both raw and filtered surfaces.  See the details in the 
comments in the main method.  The program also displays
some text on the command-line screen.

Execution of the main method in this class requires access
to the following classes, plus some inner classed defined
within these classes:

ImgMod29.class - Displays 3D surfaces
ImgMod30.class - Provides 2D Fourier Transform
ImgMod32.class - This class

Tested using J2SE 5.0 and WinXP
**********************************************************/

class ImgMod32{
  //The primary purpose of this main method is to test the
  // class in a stand-alone mode.  A secondary purpose is
  // to illustrate the relationship between convolution
  // filtering in the image domain and the spectrum of the
  // raw and filtered images in the wave-number domain.
  
  //The code in this method creates a nine-point
  // convolution filter and applies it to three  different
  // surfaces.  The convolution filter has a dc response of
  // zero with a high response at the folding wave numbers.
  // Hence, it tends to have the characteristic of a
  // sharpening or edge-detection filter.  

  //The three surfaces consist of:
  // 1. A single impulse
  // 2. A 3x3 square
  // 3. A 5x5 square
  
  //The three surfaces are constructed on what ordinarily
  // is considered to be the color planes in an image.
  // However, in this case, the surfaces have nothing in
  // particular to do with color.  They simply  represent
  // three surfaces on which it is convenient to
  // synthetically construct 3D shapes that are useful for
  // testing and illustrating the image convolution
  // concepts.  But, in order to be consistent with the
  // concept of color planes, the comments in the main
  // method frequently refer to the values as color values.
  
  //In addition to the display of some text material on the
  // command-line screen, the program displays twelve
  // different graphs.  They are described as follows:
  
  //The following surfaces are displayed:
  // 1. The impulse
  // 2. The raw 3x3 square
  // 3. The raw 5x5 square
  // 4. The filtered impulse
  // 5. The filtered 3x3 square
  // 6. The filtered 5x5 square
  
  // In addition, a 2D Fourier Transform is computed and
  // the results are displayed for the following surfaces:
  // 1. The impulse
  // 2. The 3x3 square input
  // 3. The 5x5 square input
  // 4. The filtered impulse
  // 5. The filtered 3x3 square
  // 6. The filtered 5x5 square
  public static void main(String[] args){
   
    //Create a 2D convolution filter having nine weights in
    // a square.
    double[][] filter = {
                         {-1,-1,-1},
                         {-1, 8,-1},
                         {-1,-1,-1}
                        };

    //Create synthetic image pixel data.  Use a surface
    // that is sufficiently large to produce good
    // resolution in the 2D Fourier Transform.  Zero-fill
    // those portions of the surface that don't describe
    // the shapes of interest.
    int rowLim = 31;
    int colLim = 31;
    int[][][] threeDPix = new int[rowLim][colLim][4];

    //Place a single impulse in the red plane 1
    threeDPix[3][3][1] = 255;
  
    //Place a 3x3 square in the green plane 2
    threeDPix[2][2][2] = 255;
    threeDPix[2][3][2] = 255;
    threeDPix[2][4][2] = 255;    
    
    threeDPix[3][2][2] = 255;
    threeDPix[3][3][2] = 255;
    threeDPix[3][4][2] = 255;
    
    threeDPix[4][2][2] = 255;
    threeDPix[4][3][2] = 255;
    threeDPix[4][4][2] = 255;   

    //Place a 5x5 square in the blue plane 3
    threeDPix[2][2][3] = 255;
    threeDPix[2][3][3] = 255;
    threeDPix[2][4][3] = 255;
    threeDPix[2][5][3] = 255;
    threeDPix[2][6][3] = 255;
        
    threeDPix[3][2][3] = 255;
    threeDPix[3][3][3] = 255;
    threeDPix[3][4][3] = 255;
    threeDPix[3][5][3] = 255;
    threeDPix[3][6][3] = 255;
    
    threeDPix[4][2][3] = 255;
    threeDPix[4][3][3] = 255;
    threeDPix[4][4][3] = 255;
    threeDPix[4][5][3] = 255;
    threeDPix[4][6][3] = 255;
    
    threeDPix[5][2][3] = 255;
    threeDPix[5][3][3] = 255;
    threeDPix[5][4][3] = 255;
    threeDPix[5][5][3] = 255;
    threeDPix[5][6][3] = 255;
    
    threeDPix[6][2][3] = 255;
    threeDPix[6][3][3] = 255;
    threeDPix[6][4][3] = 255;
    threeDPix[6][5][3] = 255;
    threeDPix[6][6][3] = 255;
    
    //Perform the convolution.
    int[][][] output = convolve(threeDPix,filter);
    
    //All of the remaining code in the main method is used
    // to display material that is used to test and to
    // illustrate the convolution process.
                               
    //Remove the mean values from the filtered color planes
    // before plotting and computing spectra.

    //First convert the color values from int to double.
    double[][][] outputDouble = intToDouble(output);
    //Now remove the mean color value from each plane.
    removeMean(outputDouble,1);
    removeMean(outputDouble,2);
    removeMean(outputDouble,3);

    //Convert the raw image data from int to double
    double[][][] rawDouble = intToDouble(threeDPix);

    //Get and plot the raw red plane 1.  This is an input
    // to the filter process.
    //Get the plane of interest.
    double[][] temp = getPlane(rawDouble,1);
    //Generate and display the graph by plotting the 3D
    // surface on the computer screen.
    new ImgMod29(temp,4,true,1);

    //Get and display the 2D Fourier Transform of plane 1.
    //Get the plane of interest.
    temp = getPlane(rawDouble,1);
    //Prepare arrays to receive the results of the Fourier
    // transform.
    double[][] real = new double[rowLim][colLim];
    double[][] imag = new double[rowLim][colLim];
    double[][] amp = new double[rowLim][colLim];
    //Perform the 2D Fourier transform.
    ImgMod30.xform2D(temp,real,imag,amp);
    //Ignore the real and imaginary results.  Prepare the
    // amplitude spectrum for more-effective plotting by
    // shifting the origin to the center in wave-number
    // space.
    double[][] shiftedAmplitudeSpect = 
                                 ImgMod30.shiftOrigin(amp);
    //Generate and display the graph by plotting the 3D
    // surface on the computer screen.
    new ImgMod29(shiftedAmplitudeSpect,4,true,1);
    

    //Get and plot the filtered plane 1.  This is the
    // impulse response of the convolution filter.
    temp = getPlane(outputDouble,1);
    new ImgMod29(temp,4,true,1);
                               
    //Get and display the transform of filtered plane 1.
    // This is the transform of the impulse response of
    // the convolution filter.
    temp = getPlane(outputDouble,1);
    real = new double[rowLim][colLim];
    imag = new double[rowLim][colLim];
    amp = new double[rowLim][colLim];
    ImgMod30.xform2D(temp,real,imag,amp);
    shiftedAmplitudeSpect = ImgMod30.shiftOrigin(amp);
    new ImgMod29(shiftedAmplitudeSpect,4,true,1);


    //Get and plot the raw green plane 2.  This is another
    // input to the filter process.
    temp = getPlane(rawDouble,2);
    new ImgMod29(temp,4,true,1);

    //Get and display the transform of plane 2.
    temp = getPlane(rawDouble,2);
    real = new double[rowLim][colLim];
    imag = new double[rowLim][colLim];
    amp = new double[rowLim][colLim];
    ImgMod30.xform2D(temp,real,imag,amp);
    shiftedAmplitudeSpect = ImgMod30.shiftOrigin(amp);
    new ImgMod29(shiftedAmplitudeSpect,4,true,1);
    
    
    //Get and plot the filtered plane 2.
    temp = getPlane(outputDouble,2);
    new ImgMod29(temp,4,true,1);
    
    //Get and display the transform of filtered plane 2.
    temp = getPlane(outputDouble,2);
    real = new double[rowLim][colLim];
    imag = new double[rowLim][colLim];
    amp = new double[rowLim][colLim];
    ImgMod30.xform2D(temp,real,imag,amp);
    shiftedAmplitudeSpect = ImgMod30.shiftOrigin(amp);
    new ImgMod29(shiftedAmplitudeSpect,4,true,1);


    //Get and plot the raw blue plane 3.  This is another
    // input to the filter process.
    temp = getPlane(rawDouble,3);
    new ImgMod29(temp,4,true,1);

    //Get and display the transform of plane 3.
    temp = getPlane(rawDouble,3);
    real = new double[rowLim][colLim];
    imag = new double[rowLim][colLim];
    amp = new double[rowLim][colLim];
    ImgMod30.xform2D(temp,real,imag,amp);
    shiftedAmplitudeSpect = ImgMod30.shiftOrigin(amp);
    new ImgMod29(shiftedAmplitudeSpect,4,true,1);
    

    //Get and plot the filtered plane 3.
    temp = getPlane(outputDouble,3);
    new ImgMod29(temp,4,true,1);
    
    //Get and display the transform of filtered plane 3
    temp = getPlane(outputDouble,3);
    real = new double[rowLim][colLim];
    imag = new double[rowLim][colLim];
    amp = new double[rowLim][colLim];
    ImgMod30.xform2D(temp,real,imag,amp);
    shiftedAmplitudeSpect = ImgMod30.shiftOrigin(amp);
    new ImgMod29(shiftedAmplitudeSpect,4,true,1);
                               
  }//end main
  //-----------------------------------------------------//
  
  //The purpose of this method is to extract a color plane
  // from the double version of an image and to return it
  // as a 2D array of type double.  This is useful, for
  // example, for performing Fourier transforms on the data
  // in a color plane.
  //This method is used only in support of the operations
  // in the main method.  It is not required for performing
  // the convolution.
  
  public static double[][] getPlane(
                   double[][][] threeDPixDouble,int plane){
    
    int numImgRows = threeDPixDouble.length;
    int numImgCols = threeDPixDouble[0].length;
    
    //Create an empty output array of the same
    // size as a single plane in the the incoming array of
    // pixels.
    double[][] output =new double[numImgRows][numImgCols];

    //Copy the values from the specified plane to the
    // double array converting them to type double in the
    // process.
    for(int row = 0;row < numImgRows;row++){
      for(int col = 0;col < numImgCols;col++){
        output[row][col] =
                          threeDPixDouble[row][col][plane];
      }//end loop on col
    }//end loop on row
    return output;
  }//end getPlane
  //-----------------------------------------------------//

  //The purpose of this method is to get and remove the
  // mean value from a specified color plane in the double
  // version of an image pixel array.  The method returns
  // the mean value that was removed so that it can be
  // saved by the calling method and restored later.
  static double removeMean(
                   double[][][] inputImageArray,int plane){
    int numImgRows = inputImageArray.length;
    int numImgCols = inputImageArray[0].length;
    
    //Compute the mean color value
    double sum = 0;
    for(int row = 0;row < numImgRows;row++){
      for(int col = 0;col < numImgCols;col++){
        sum += inputImageArray[row][col][plane];
      }//end inner loop
    }//end outer loop
    
    double mean = sum/(numImgRows*numImgCols);
    
    //Remove the mean value from each pixel value.
    for(int row = 0;row < numImgRows;row++){
      for(int col = 0;col < numImgCols;col++){
        inputImageArray[row][col][plane] -= mean;
      }//end inner loop
    }//end outer loop
    
    return mean;
  }//end removeMean
  //-----------------------------------------------------//
  
  //The purpose of this method is to add a constant to
  // every color value in a specified color plane in the
  // double version of an image pixel array.  For example,
  // this method can be used to restore the mean value to a
  // color plane that was removed earlier.
  static void addConstantToColor(
                              double[][][] inputImageArray,
                              int plane,
                              double constant){
    int numImgRows = inputImageArray.length;
    int numImgCols = inputImageArray[0].length;
    //Add the constant value to each color value
    for(int row = 0;row < numImgRows;row++){
      for(int col = 0;col < numImgCols;col++){
        inputImageArray[row][col][plane] = 
               inputImageArray[row][col][plane] + constant;
      }//end inner loop
    }//end outer loop
  }//end addConstantToColor
  //-----------------------------------------------------//
  
  //The purpose of this method is to scale every color
  // value in a specified color plane in the double version
  // of an image pixel array by a specified scale factor.
  static void scaleColorPlane(
      double[][][] inputImageArray,int plane,double scale){
    int numImgRows = inputImageArray.length;
    int numImgCols = inputImageArray[0].length;
    //Scale each color value
    for(int row = 0;row < numImgRows;row++){
      for(int col = 0;col < numImgCols;col++){
        inputImageArray[row][col][plane] = 
                  inputImageArray[row][col][plane] * scale;
      }//end inner loop
    }//end outer loop
  }//end scaleColorPlane
  //-----------------------------------------------------//

  //The purpose of this method is to get and to return the
  // algebraic maximum color value for a specified color
  // plane in the double version of an image pixel array.
  static double getMax(
                   double[][][] inputImageArray,int plane){
    int numImgRows = inputImageArray.length;
    int numImgCols = inputImageArray[0].length;
    
    double maxValue = -Double.MAX_VALUE;
    //Find the maximum value
    for(int row = 0;row < numImgRows;row++){
      for(int col = 0;col < numImgCols;col++){
        if(inputImageArray[row][col][plane] > maxValue){
          maxValue = inputImageArray[row][col][plane];
        }//end if
      }//end inner loop
    }//end outer loop
    return maxValue;
  }//end getMax
  //-----------------------------------------------------//
  
  //The purpose of this method is to get and return the
  // algebraic minimum color value for a specified color
  // plane in the double version of an image pixel array.
  static double getMin(
                   double[][][] inputImageArray,int plane){
    int numImgRows = inputImageArray.length;
    int numImgCols = inputImageArray[0].length;
    
    double minValue = Double.MAX_VALUE;
    //Find the minimum value
    for(int row = 0;row < numImgRows;row++){
      for(int col = 0;col < numImgCols;col++){
        if(inputImageArray[row][col][plane] < minValue){
          minValue = inputImageArray[row][col][plane];
        }//end if
      }//end inner loop
    }//end outer loop
    return minValue;
  }//end getMin
  //-----------------------------------------------------//
  
  //The purpose of this method is to convert an image pixel
  // array (where the pixel values are represented as type
  // int) to an image pixel array where the pixel values
  // are represented as type double.
  static double[][][] intToDouble(
                                int[][][] inputImageArray){
    
    int numImgRows = inputImageArray.length;
    int numImgCols = inputImageArray[0].length;
    
    double[][][] outputImageArray =
                     new double[numImgRows][numImgCols][4];
    for(int row = 0;row < numImgRows;row++){
      for(int col = 0;col < numImgCols;col++){
        outputImageArray[row][col][0] = 
                              inputImageArray[row][col][0];
        outputImageArray[row][col][1] = 
                              inputImageArray[row][col][1];
        outputImageArray[row][col][2] = 
                              inputImageArray[row][col][2];
        outputImageArray[row][col][3] = 
                              inputImageArray[row][col][3];
      }//end inner loop
    }//end outer loop
    return outputImageArray;
  }//end intToDouble
  //-----------------------------------------------------//

  //The purpose of this method is to convert an image pixel
  // array (where the pixel values are represented as type
  // double) to an image pixel array where the pixel values
  // are represented as type int.
  static int[][][] doubleToInt(
                             double[][][] inputImageArray){
    
    int numImgRows = inputImageArray.length;
    int numImgCols = inputImageArray[0].length;
    
    int[][][] outputImageArray =
                        new int[numImgRows][numImgCols][4];
    for(int row = 0;row < numImgRows;row++){
      for(int col = 0;col < numImgCols;col++){
        outputImageArray[row][col][0] = 
                         (int)inputImageArray[row][col][0];
        outputImageArray[row][col][1] = 
                         (int)inputImageArray[row][col][1];
        outputImageArray[row][col][2] = 
                         (int)inputImageArray[row][col][2];
        outputImageArray[row][col][3] = 
                         (int)inputImageArray[row][col][3];
      }//end inner loop
    }//end outer loop
    return outputImageArray;
  }//end doubleToInt
  //-----------------------------------------------------//
  
  //The purpose of this method is to get and return the
  // maximum value among three color values where the color
  // values are represented as type double.
  static double getMaxColor(
                      double red,double green,double blue){
    double max = -Double.MAX_VALUE;
    if(red > max){
      max = red;
    }//end if
    
    if(green > max){
      max = green;
    }//end if
    
    if(blue > max){
      max = blue;
    }//end if
    
    return max;
  }//end getMaxColor
  //-----------------------------------------------------//
  
  //The purpose of this method is to get and return the
  // minimum value among three color values where the
  // color values are represented as type double.
  static double getMinColor(
                      double red,double green,double blue){
    double min = Double.MAX_VALUE;
    if(red < min){
      min = red;
    }//end if
    
    if(green < min){
      min = green;
    }//end if
    
    if(blue < min){
      min = blue;
    }//end if
    
    return min;
  }//end getMinColor
  //-----------------------------------------------------//
  
  //This method applies an incoming 2D convolution filter
  // to each color plane in an incoming 3D array of pixel
  // data and returns a filtered 3D array of pixel data.
  //The convolution operator is applied separately to each
  // color plane.
  //The alpha plane is not modified.
  //The output is normalized so as to guarantee that the
  // output color values fall within the range from 0
  // to 255.
  //The convolution filter is passed to the method as a 2D
  // array of type double.  All convolution and
  // normalization arithmetic is performed as type double.
  //The normalized results are converted to type int before
  // returning them to the calling method.
  //This method does not modify the contents of the
  // incoming array of pixel data.
  //An unfiltered dead zone equal to half the filter length
  // is left around the perimeter of the filtered image to
  // avoid any attempt to perform convolution using data
  // outside the bounds of the image.
  public static int[][][] convolve(
                    int[][][] threeDPix,double[][] filter){
    //Get the dimensions of the image and filter arrays.
    int numImgRows = threeDPix.length;
    int numImgCols = threeDPix[0].length;
    int numFilRows = filter.length;
    int numFilCols = filter[0].length;

    //Display the dimensions of the image and filter
    // arrays.
    System.out.println("numImgRows = " + numImgRows);
    System.out.println("numImgCols = " + numImgCols);
    System.out.println("numFilRows = " + numFilRows);
    System.out.println("numFilCols = " + numFilCols);

    //Make a working copy of the incoming 3D pixel array to
    // avoid making permanent changes to the original image
    // data. Convert the pixel data to type double in the
    // process.  Will convert back to type int when
    // returning from this method.
    double[][][] work3D = intToDouble(threeDPix);
    
    //Remove the mean value from each color plane.  Save
    // the mean values for later restoration.
    double redMean = removeMean(work3D,1);
    double greenMean = removeMean(work3D,2);
    double blueMean = removeMean(work3D,3);
    
    //Create an empty output array of the same size as the
    // incoming array of pixels.
    double[][][] output = 
                     new double[numImgRows][numImgCols][4];
    
    //Copy the alpha values directly to the output array.
    // They will not be processed during the convolution
    // process.
    for(int row = 0;row < numImgRows;row++){
      for(int col = 0;col < numImgCols;col++){
        output[row][col][0] = work3D[row][col][0];
      }//end inner loop
    }//end outer loop

//Because of the length of the following statements, and
// the width of this publication format, this format
// sacrifices indentation style for clarity. Otherwise,it
// would be necessary to break the statements into so many
// short lines that it would be very difficult to read
// them.

//Use nested for loops to perform a 2D convolution of each
// color plane with the 2D convolution filter.

for(int yReg = numFilRows-1;yReg < numImgRows;yReg++){
  for(int xReg = numFilCols-1;xReg < numImgCols;xReg++){
    for(int filRow = 0;filRow < numFilRows;filRow++){
      for(int filCol = 0;filCol < numFilCols;filCol++){
        
        output[yReg-numFilRows/2][xReg-numFilCols/2][1] += 
                      work3D[yReg-filRow][xReg-filCol][1] *
                                    filter[filRow][filCol];

        output[yReg-numFilRows/2][xReg-numFilCols/2][2] += 
                      work3D[yReg-filRow][xReg-filCol][2] *
                                    filter[filRow][filCol];

        output[yReg-numFilRows/2][xReg-numFilCols/2][3] += 
                      work3D[yReg-filRow][xReg-filCol][3] *
                                    filter[filRow][filCol];

      }//End loop on filCol
    }//End loop on filRow
/*
    //Divide the result at each point in the output by the
    // number of filter coefficients.  Note that in some
    // cases, this is not helpful.  For example, it is not
    // helpful when a large number of the filter
    // coefficients have a value of zero.
    output[yReg-numFilRows/2][xReg-numFilCols/2][1] = 
           output[yReg-numFilRows/2][xReg-numFilCols/2][1]/
                                   (numFilRows*numFilCols);
    output[yReg-numFilRows/2][xReg-numFilCols/2][2] = 
           output[yReg-numFilRows/2][xReg-numFilCols/2][2]/
                                   (numFilRows*numFilCols);
    output[yReg-numFilRows/2][xReg-numFilCols/2][3] = 
           output[yReg-numFilRows/2][xReg-numFilCols/2][3]/
                                   (numFilRows*numFilCols);
*/
  }//End loop on xReg
}//End loop on yReg

    //Return to the normal indentation style.

    //Restore the original mean value to each color plane.
    addConstantToColor(output,1,redMean);
    addConstantToColor(output,2,greenMean);
    addConstantToColor(output,3,blueMean);
    
    //Normalize the color values, if necessary, to
    // guarantee that they fall within the range
    // from 0 to 255.
    
    //Begin by dealing with the minimum algebraic color
    // value. If any color plane contains a negative value,
    // add a constant to all color values in all planes to
    // guarantee that the minimum value in any color plane
    // is 0.
    
    //Get the minimum value of the filtered output across
    // all color planes.
    //Get the minimum value for each plane.
    double redOutMin = getMin(output,1);
    double greenOutMin = getMin(output,2);
    double blueOutMin = getMin(output,3);
    //Get and save the minimum color value among all three
    // color planes
    double minOut = getMinColor(
                         redOutMin,greenOutMin,blueOutMin);
    
    //Make the adjustment to every color value if the
    // minimum value is less than zero.  Otherwise, don't
    // make any adjustment on the basis of the minimum
    // value.
    if(minOut < 0){
      addConstantToColor(output,1,-minOut);
      addConstantToColor(output,2,-minOut);
      addConstantToColor(output,3,-minOut);
    }//end if
    
    //If any color value exceeds 255, scale all color
    // values to guarantee that the maximum color value in
    // any  color plane is 255.
    
    //Get the peak value of the filtered output across all
    // color planes.
    //Get the maximum value for each color plane.
    double redOutMax = getMax(output,1);
    double greenOutMax = getMax(output,2);
    double blueOutMax = getMax(output,3);
    //Get and save the maximum color value among all three
    // color planes
    double peakOut = getMaxColor(
                         redOutMax,greenOutMax,blueOutMax);
    
    //Make the adjustment if the maximum value is greater
    // than 255.  Otherwise, don't make any adjustment on
    // the basis of the maximum value.
    if(peakOut > 255){
      scaleColorPlane(output,1,255/peakOut);
      scaleColorPlane(output,2,255/peakOut);
      scaleColorPlane(output,3,255/peakOut);
    }//end if

    //Return a reference to the array containing the
    // normalized filtered pixels.  Convert the color
    // valuesto type int before returnng.
    return doubleToInt(output);

  }//end convolve method
  //-----------------------------------------------------//
}//end class ImgMod32

Listing 20


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 has 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.

Baldwin@DickBaldwin.com





Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel