Wednesday, June 29, 2016

Creating Images of Forward Ordered 2D Haar Wavelet Transforms




Problem
  
This note describes how to create images of forward ordered 2D Haar Wavelet Transform (HWT).



Auxiliary Methods
  
The methods below assume that the project has the jar archive for OpenCV 2.4.9. First of all, we define a method that takes a path with an image and convert it to grayscale. 
 
 public static double[][] getGrayscalePixMat(String infile) {
        Mat orig = Highgui.imread(IMAGE_SOURCE_DIR + infile);
        if (orig.rows() == 0 || orig.cols() == 0) {
            throw new IllegalArgumentException("Failed to read " + IMAGE_SOURCE_DIR + infile);
        }
       
        Mat grayscale = new Mat(orig.rows(), orig.cols(), CvType.CV_8UC1);
        Imgproc.cvtColor(orig, grayscale, Imgproc.COLOR_RGB2GRAY);
       
        double[][] pix_mat = get1CPixMat(grayscale);
        orig.release();
        grayscale.release();
        return pix_mat;

}

Second, we need to define methods to scale values of specific wavelets in a 2D transform in the third argument to be in the range [0, 255]. The scaled values are placed into the mat passed as the first argument. The methods scaleSetHorMatValsInImage(), scaleSetVerMatValsInImage(), scaleSetDigValsInImage() scale the values of the horizontal, vertical, and diagonal coefficients.

public static double findMaxVal(double[][] cmat) {
        double max = Double.MIN_VALUE;
        for(int row = 0; row < cmat.length; row++) {
            for(int col = 0; col < cmat[row].length; col++) {
                if ( cmat[row][col] > max ) {
                    max = cmat[row][col];
                }
            }
        }
        return max;
    }
   
    public static double findMinVal(double[][] cmat) {
        double min = Double.MAX_VALUE;
        for(int row = 0; row < cmat.length; row++) {
            for(int col = 0; col < cmat[row].length; col++) {
                if ( cmat[row][col] < min ) {
                    min = cmat[row][col];
                }
            }
        }
        return min;
    }

 
public static void scaleSetHorMatValsInImage(Mat grayscale, int mat_size, double[][] cmat) {
        System.out.println("setHorMatValsInImage " + mat_size);
        double max    = findMaxVal(cmat);
        double min    = findMinVal(cmat);
        double crange = max - min;
        double scaler = 255/crange;
        int half = mat_size/2;
        Mat temp = new Mat(cmat.length, cmat[0].length, CvType.CV_8UC1);
        for(int img_row = 0, cmat_row = 0; img_row < half; img_row++, cmat_row++) {
            for(int img_col = half, cmat_col = 0; img_col < mat_size; img_col++, cmat_col++) {
                double cmat_val = (cmat[cmat_row][cmat_col] - min)*scaler;
                double[] data = { cmat_val, cmat_val, cmat_val };
                grayscale.put(img_row, img_col, data);
                temp.put(cmat_row, cmat_col, data);
            }
        }
        Highgui.imwrite(IMAGE_SOURCE_DIR + "temp_hor_scale_" + half + ".jpg", temp);
        temp.release();
 }


public static void scaleVerMatValsInImage(Mat grayscale, int mat_size, double[][] cmat) {
        System.out.println("setVerMatValsInImage " + mat_size);
        double max    = findMaxVal(cmat);
        double min    = findMinVal(cmat);
        double crange = max - min;
        double scaler = 255/crange;
        int half = mat_size/2;
        Mat temp = new Mat(cmat.length, cmat[0].length, CvType.CV_8UC1);
        for(int img_row = half, cmat_row = 0; img_row < mat_size; img_row++, cmat_row++) {
            for(int img_col = 0, cmat_col = 0; img_col < half; img_col++, cmat_col++) {
                double cmat_val = (cmat[cmat_row][cmat_col] - min)*scaler;
                double[] data = { cmat_val, cmat_val, cmat_val };
                grayscale.put(img_row, img_col, data);
                temp.put(cmat_row, cmat_col, data);
            }
        }
        Highgui.imwrite(IMAGE_SOURCE_DIR + "temp_ver_scale_" + half + ".jpg", temp);
        temp.release();
    }


public static void scaleDigMatValsInImage(Mat grayscale, int mat_size, double[][] cmat) {
        System.out.println("scaleDigMatValsInImage " + mat_size);
        double max    = findMaxVal(cmat);
        double min    = findMinVal(cmat);
        double crange = max - min;
        double scaler = 255/crange;
        int half = mat_size/2;
        Mat temp = new Mat(cmat.length, cmat[0].length, CvType.CV_8UC1);
        for(int img_row = half, cmat_row = 0; img_row < mat_size; img_row++, cmat_row++) {
            for(int img_col = half, cmat_col = 0; img_col < mat_size; img_col++, cmat_col++) {
                double cmat_val = (cmat[cmat_row][cmat_col] - min)*scaler;
                double[] data = { cmat_val, cmat_val, cmat_val };
                grayscale.put(img_row, img_col, data);
                temp.put(cmat_row, cmat_col, data);
            }
        }
        Highgui.imwrite(IMAGE_SOURCE_DIR + "temp_dig_scale_" + half + ".jpg", temp);
        temp.release();
    }


The method below scales the averages.
 
public static void setAvrgMatValsInImage(Mat grayscale, int mat_size, double[][] cmat) {
        System.out.println("setAvrgMatValsInImage " + mat_size);
        double max    = findMaxVal(cmat);
        double min    = findMinVal(cmat);
        double crange = max - min;
        double scaler = 255/crange;
        int half = mat_size/2;
        Mat temp = new Mat(cmat.length, cmat[0].length, CvType.CV_8UC1);
        for(int img_row = 0, cmat_row = 0; img_row < half; img_row++, cmat_row++) {
            for(int img_col = 0, cmat_col = 0; img_col < half; img_col++, cmat_col++) {
                double cmat_val = cmat[cmat_row][cmat_col];
                double[] data = { cmat_val, cmat_val, cmat_val };
                grayscale.put(img_row, img_col, data);
                temp.put(cmat_row, cmat_col, data);
            }
        }
        Highgui.imwrite(IMAGE_SOURCE_DIR + "temp_avrg_" + half + ".jpg", temp);
        temp.release();
    }


The method createScaledImageOf2DHWT() creates an image representation of applying the ordered forward 2D HWT for a given specific number of iterations.

public static void createScaledImageOf2DHWT(String infile, String outfile, int num_iters) {
        double[][] mat = getGrayscalePixMat(infile);
        int n = (int)(Math.log(mat.length)/Math.log(2));
        System.out.println("n == " + n);
       
        ArrayList<double[][]> transform = TwoDHaar.orderedForwardDWTForNumIters(mat, n, num_iters);
       
        double[][] lastAvrgMat = transform.remove(transform.size()-1);
        Mat grayscale = new Mat(mat.length, mat[0].length, CvType.CV_8UC1);

        int avrg_size = mat.length;
        for(int i = 0, mat_size = mat.length; i < transform.size(); i += 3, mat_size /= 2) {
            scaleSetHorMatValsInImage(grayscale, mat_size, transform.get(i));
            scaleVerMatValsInImage(grayscale, mat_size, transform.get(i+1));
            scaleDigMatValsInImage(grayscale, mat_size, transform.get(i+2));
            avrg_size /= 2;
        }
       
        setAvrgMatValsInImage(grayscale, 2*avrg_size, lastAvrgMat);
       
        Highgui.imwrite(IMAGE_SOURCE_DIR + outfile, grayscale);
        grayscale.release();
    }





Tests
  
Let us generate four images of applying 1, 2, 3, and 4 scales of the ordered forward 2D HWT to the image shown in Fig. 1 with the following calls.

createScaledImageOf2DHWT("ornament_02.jpg", "ornament_02_1_scale.jpg",  1);    // output shown in Fig. 2
createScaledImageOf2DHWT("ornament_02.jpg", "ornament_02_2_scales.jpg", 2);   // output shown in Fig. 3
createScaledImageOf2DHWT("ornament_02.jpg", "ornament_02_3_scales.jpg", 3);   //output shown in Fig. 4
createScaledImageOf2DHWT("ornament_02.jpg", "ornament_02_4_scales.jpg", 4);   //output shown in Fig. 5



Figure 1. ornament_02.jpg



Figure 2. ornament_02_1_scale.jpg
Figure 3. ornament_02_2_scales.jpg



Figure 4. ornament_02_3_scales.jpg
Figure 5. ornament_02_4_scales.jpg





 

Interpretation of Forward Ordered 2D Haar Wavelet Transform




Problem
  
This note describes how to interpret the results of the forward ordered 2D Haar Wavelet Transform (HWT).



Forward Ordered 2D HWT
  
Let us define several 2D signals to which we will apply the forward ordered 2D HWT. 

public static double[][] signal_2x2_01 = {
        {9, 7},
        {5, 3}

};

public static double[][] signal_4x4_01 = {
        {9,    7,    6,    2},
        {5,    3,    4,    4},
        {8,    2,    4,    0},
        {6,    0,    2,    2}

};

public static double[][] signal_8x8_01 = {
        {8.0,     5.0,      4.0,      8.0,      6.0,      8.0,    10.0,   8.0},   
        {8.0,     10.0,    10.0,    4.0,      10.0,    4.0,    8.0,     2.0},   
        {6.0,     10.0,    2.0,      4.0,      2.0,      6.0,    1.0,     6.0},   
        {0.0,     8.0,      0.0,      10.0,    10.0,    6.0,    6.0,     10.0},
        {8.0,     8.0,      8.0,      0.0,      4.0,      8.0,    6.0,     2.0},   
        {10.0,   6.0,      2.0,      2.0,      6.0,      6.0,    6.0,     8.0},   
        {2.0,     4.0,     10.0,    10.0,     10.0,    4.0,    6.0,     10.0},   
        {10.0,   6.0,    10.0,     6.0,       6.0,      4.0,    4.0,     4.0}

};


Let us define several methods that will be helpful in interpreting the results. The first method, computeMean(), will be placed in Utils.java. The other three methods, i.e., computeHorChangeMagn() computeVerChangeMagn(), computeDiagChangeMagn(), are placed in TwoDWT.java.  The method computeMean() computes the mean of a 2D signal. The method computeHorChangeMagn() computes the change from the average of the left half of the signal and the average of the right half of the signal. The method computeVerChangeMagn() computes the change from the average of the top half of the signal and the average of the bottom half of the signal. The method computeDiagChangeMagn() comptues the diagonal change magnitude, i.e., the change from the average of the top left and bottom right quadrants and the average of the top right and bottom left quadrants. The second argument, ttn, stands for "two to the n."

public static double computeMean(double[][] signal) {
        double mean = 0;
        int num_rows = signal.length;
        int num_cols = signal[0].length;
        for(int r = 0; r < num_rows; r++) {
            for(int c = 0; c < num_cols; c++) {
                mean += signal[r][c];
            }
        }
        return mean/(num_rows*num_cols);

}
  
public static double computeHorChangeMagn(double[][] sig, int ttn) {
        double left_mean  = Utils.computeMean(sig, 0, ttn-1, 0, ttn/2 - 1);
        double right_mean = Utils.computeMean(sig, 0, ttn-1, ttn/2, ttn-1);
        return (left_mean - right_mean)/2;

}
    

public static double computeVerChangeMagn(double[][] sig, int ttn) {
        double upper_mean = Utils.computeMean(sig, 0, ttn/2 - 1, 0, ttn-1);
        double lower_mean = Utils.computeMean(sig, ttn/2, ttn-1, 0, ttn-1);
        return (upper_mean - lower_mean)/2.0;

}
    

public static double computeDiagChangeMagn(double[][] sig, int ttn) {
        double top_left_quad_mean = Utils.computeMean(sig, 0, ttn/2 - 1, 0, ttn/2 - 1);
        double bot_right_quad_mean = Utils.computeMean(sig, ttn/2, ttn-1, ttn/2, ttn-1);
        double top_right_quad_mean = Utils.computeMean(sig, 0, ttn/2 - 1, ttn/2, ttn-1);
        double bot_left_quad_mean = Utils.computeMean(sig, ttn/2, ttn-1, 0, ttn/2 - 1);
        return ((top_left_quad_mean+bot_right_quad_mean)/2 -
                    (top_right_quad_mean+bot_left_quad_mean)/2)/2;

}

Here is a test method that computes the statistics of the 2D signal. Then it computes the ordered forward 2D HWT, displays the results, and then inverses the transform.

static void test_ordFwdInvHWT(double[][] sig, int num_iters, boolean dbg_flag) {
        final int ttn = sig.length;
        System.out.println("AVR="+Utils.computeMean(sig));
        System.out.println("HOR="+TwoDHWT.computeHorChangeMagn(sig, ttn));
        System.out.println("VER="+TwoDHWT.computeVerChangeMagn(sig, ttn));
        System.out.println("DIG="+TwoDHWT.computeDiagChangeMagn(sig, ttn));
       
        TwoDHWT.ordFwdDWTForNumIters(sig, num_iters, dbg_flag);


        if ( dbg_flag ) {
            System.out.println();
            System.out.println("Forward Transform");
            Utils.display2DArray(sig, sig.length, sig[0].length);
            System.out.println("================");
            System.out.println("Iverse Transform");
        }
       
        TwoDHWT.ordInvDWTForNumIters(sig, num_iters, num_iters, dbg_flag);


        if ( dbg_flag ) {
            System.out.println("Inversed Matrix");
            Utils.display2DArray(sig, sig.length, sig[0].length);
        }
 }






Interpretation of Results
  
Let us define several 2D signals to which we will apply the forward ordered 2D HWT. 

test_ordFwdInvHWT(signal_2x2_01, 1, true):

The statistics computed by the above method call correspond to the resultant transform.

AVR=6.0
HOR=1.0
VER=2.0
DIG=0.0


Forward Transform
6.0 1.0
2.0 0.0


Let us call the test method on a 4x4 signal: test_ordFwdInvHWT(signal_4x4_01, 2, true):

AVR=4.0
HOR=1.0
VER=1.0
DIG=0.0


Forward Transform
4.0 1.0 1.0 1.0
1.0 0.0 3.0 1.0
2.0 0.0 0.0 1.0
1.0 0.0 0.0 1.0


The top 2x2 corner of the above forward transform contains the average (4.0), the horizontal change magnitude (1.0), the vertical change magnitude (1.0), and the diagonal change magnitude (0.0).  

Let us call the test method on an 8x8 signal: test_ordFwdInvHWT(signal_8x8_01, 2, true):

AVR=6.1875
HOR=0.03125
VER=0.0625
DIG=-0.21875


Forward Transform
6.1875 0.03125 0.8125 0.0625 0.25 0.5 1.0 2.0
0.0625 -0.21875 0.375 0.125 -3.0 -3.0 0.0 -2.25
1.0625 0.5625 -0.1875 -0.0625 1.0 2.0 -1.0 0.5
-0.875 -0.125 2.125 0.125 0.5 1.0 2.0 -1.0
-1.25 -0.5 0.0 2.0 1.25 -2.5 -2.0 -1.0
2.0 -1.0 -2.0 -2.25 1.0 2.0 -2.0 -0.25
0.0 1.0 0.0 -1.5 -1.0 2.0 -1.0 1.5
-2.5 1.0 1.0 2.0 -1.5 -1.0 1.0 -1.0 


The top 2x2 corner of the above transform contains the average (6.1875), the horizontal change magnitude (0.03125), the vertical change magnitude (0.0625), and the diagonal change (-0.21875).  Figures 1, 2, and 3 give the components of the forward transform after the 1st, 2nd, and 3rd scales, respectively.

Figure 1. Horizontal, vertical, diagonal wavelet coefficients after 1st scale

Figure 2. Horizontal, vertical, diagonal wavelets after 2nd scale


Figure 3. Averages, horizontal, vertical, diagonal wavelets after 3rd scale