Question

I would like to compute the Normalized Cross Correlation between two images using Java.

My program works fine but when I tried to verify my results in MATLAB, I unfortunately did not get the same results as I did with my Java implementation.

This is the code that I performed in MATLAB:

Img1 = rgb2gray(imread('image1.png'));
Img2 = rgb2gray(imread('image2.png'));
corr2(Img1, Img2);

This is part of my Java implementation. Some of the classes have been removed for better understanding:

I am not sure what is wrong with my Java implementation.

I also have one other question. In MATLAB, I had to convert the image to grayscale before using corr2. Do I need to do the same in Java?

Was it helpful?

Solution

The reason why it isn't the same is because you didn't account for the headers in the PNG file.

If you take a look at your Java code, you are reading in the image as a byte stream in your readImage method. For PNG, there are headers involved such as the size of the image and how many bits of colour per pixel there are. Not only are you grabbing image data (which by the way is compressed using a version of LZW so you're not even reading in raw image data), but you are also grabbing in extra information which is being collected in your correlation code.

What's confusing is that you are reading in the image fine using the BufferedImage type at the beginning of your correlation code to obtain the rows and columns. Why did you switch to using a byte stream in your readImage method?

As such, you need to change your readImage method to take in the BufferedImage object, or reread the data like you did in the correlation method in your readImage method. Once you do that, use the BufferedImage methods to access the RGB pixels. FWIW, if you are reading in the image as grayscale, then every channel should give you the same intensity so you can operate on one channel alone. Doesn't matter which.... but make sure that you're doing correlation on grayscale images. It is ambiguous when you go to colour, as there is currently no set standard on how to do this.

Using BufferedImage, you can use the getRGB method to obtain the pixel you want at column x and row y. x traverses from left to right, while y traverses from top to bottom. When you call getRGB it returns a single 32-bit integer in ARGB format. Each channel is 8 bits. As such, the first 8 bits (MSB) are the alpha value, the second 8 bits are for red, the third 8 bits are for green, and the final 8 are for blue. Depending on what channel you want, you need to bitshift and mask out the bits you don't need to get the value you want.

As an example:

int rgb = img.getRGB(x, y);
int alpha = rgb >> 24 & 0xFF;
int red = rgb >> 16 & 0xFF;
int green = rgb >> 8 & 0xFF;
int blue = rgb & 0xFF;    

For the alpha value, you need to shift to the right by 24 bits to get it down to the LSB positions, then mask with 0xFF to obtain only the 8 bits that represent the alpha value. Similarly you would have do the same for the red, green and blue channels. Because correlation is rather ill-posed for colour images, let's convert the image to grayscale within your readImage method. As such, there is no need to convert the image before you run this method. We will do that within the method itself to save you some hassle.

If you take a look at how MATLAB performs rgb2gray, it performs a weighted sum, weighting the channels differently. The weights are defined by the SMPTE Rec. 601 standard (for those of you that want to figure out how I know this, you can take a look at the source of rgb2gray and read off the first row of their transformation matrix. These coefficients are essentially the definition of the 601 standard).

Previous versions of MATLAB simply added up all of the channels, divided by 3 and took the floor. I don't know which version of MATLAB you are using, but to be safe I'm going to use the most up to date conversion.

public static void readImage(BufferedImage img, int array[][], int nrows, int ncols) {
      for (int i = 0; i < nrows; i++)
           for (int j = 0; j < ncols; j++) {
                int rgb = img.getRGB(j, i);
                int red = rgb >> 16 & 0xFF;
                int green = rgb >> 8 & 0xFF;
                int blue = rgb & 0xFF;
                array[i][j] = (int) (0.299*((double)red) + 0.587*((double)green) + 
                               0.114*((double)blue) );
           }
}

This should hopefully give you what you want!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top