Having different values between Java and Matlab when Reading a .pgm image

554 views Asked by At

I have to perpare a Trainging set for my Machine Learning Course, in which for a given face image it gives you an answer representing the side of the head ( straight , left , right , up )

For this purpose i need to read a .pgm image file in java and store its pixels in one row of matrix X, and then store the appropriate right answer of this image in a y vector. finally i will save these two arrays in a .mat file.

The problem is when trying to read the pixel values from a (P2 .pgm) image and printing them to console , they don't give identical values with the matlab matrix viewer. what would be the problem?

This is my code:

try{
    InputStream f = Main.class.getResourceAsStream("an2i_left_angry_open.pgm");
    BufferedReader d = new BufferedReader(new InputStreamReader(f));
    String magic = d.readLine();    // first line contains P2 or P5
    String line = d.readLine();     // second line contains height and width
    while (line.startsWith("#")) {  // ignoring comment lines
            line = d.readLine();
        }
    Scanner s = new Scanner(line);
        int width = s.nextInt();
        int height = s.nextInt();
        line = d.readLine();// third line contains maxVal
        s = new Scanner(line);
        int maxVal = s.nextInt();
        for(int i=0;i<30;i++) /* printing first 30 values from the image including spaces*/
            System.out.println((byte)d.read());

        } catch (EOFException eof) {
            eof.printStackTrace(System.out) ;
        }

these are the values i get: 50 49 32 50 32 49 32 48 32 50 32 49 56 32 53 57

while this photo is what is indeed in the image from MATLAB Viewer: (sorry i can't post images because of lack of reputationS)

and this is what you find when you open the .pgm file via notepad++

2

There are 2 answers

2
rayryeng On BEST ANSWER

Take a look at this post in particular. I've experienced similar issues with imread and with Java's ImageIO class and for the longest time, I could not find this link as proof that other people have experienced the same thing... until now. Similarly, someone experienced related issues in this post but it isn't quite the same at what you're experiencing.

Essentially, the reason why images loaded in both Java and MATLAB are different is due to enhancement purposes. MATLAB scales the intensities so the image isn't mostly black. Essentially, the maximum intensity in your PGM gets scaled to 255 while the other intensities are linearly scaled to suit the dynamic range of [0,255]. So for example, if your image had a dynamic range from [0-100] in your PGM file before loading it in with imread, this would get scaled to [0-255] and not be the original scale of [0-100]. As such, you would have to know the maximum intensity value of the image before you loaded it in (by scanning through the file yourself). That is very easily done by reading the third line of the file. In your case, this would be 156. Once you find this, you would need to scale every value in your image so that it is rescaled to what it originally was before you read it in.

To confirm that this is the case, take a look at the first pixel in your image, which has intensity 21 in the original PGM file. MATLAB would thus scale the intensities such that:

scaled = round(val*(255/156));

val would be the input intensity and scaled is the output intensity. As such, if val = 21, then scaled would be:

scaled = round(21*(255/156)) = 34

This matches up with the first pixel when reading it out in MATLAB. Similarly, the sixth pixel in the first row, the original value is 18. MATLAB would scale it such that:

scaled = round(18*(255/156)) = 29

This again matches up with what you see in MATLAB. Starting to see the pattern now? Basically, to undo the scaling, you would need to multiply by the reciprocal of the scaling factor. As such, given that A is the image you loaded in, you need to do:

A_scaled = uint8(double(A)*(max_value/255));

A_scaled is the output image and max_value is the maximum intensity found in your PGM file before you loaded it in with imread. This undoes the scaling, as MATLAB scales the images from [0-255]. Note that I need to cast the image to double first, do the multiplication with the scaling factor as this will most likely produce floating point values, then re-cast back to uint8. Therefore, to bring it back to [0-max_value], you would have to scale in the opposite way.

Specifically in your case, you would need to do:

A_scaled = uint8(double(A)*(156/255));

The disadvantage here is that you need to know what the maximum value is prior to working with your image, which can get annoying. One possibility is to use MATLAB and actually open up the file with file pointers and get the value of the third line yourself. This is also an annoying step, but I have an alternative for you.

Alternative... probably better for you

Alternatively, here are two links to functions written in MATLAB that read and write PGM files without doing that unnecessary scaling, and it'll provide the results that you are expecting (unscaled).

How the read function works is that it opens up the image using file pointers and manually parses in the data and stores the values into a matrix. You probably want to use this function instead of relying on imread. To save the images, file pointers are again used and the values are written such that the PGM standard is maintained and again, your intensities are unscaled.

0
Peter On

Your java implementation is printing the ASCII values of the text bytes "21 2 1" etc.

50->2
51->1
32->SPACE
50->2
32->SPACE
51->1 
etc.

Some PGM files use a text header, but binary representation for the pixels themselves. These are marked with a different magic string at the beginning. It looks like the java code is reading the file as if it had binary pixels.

Instead, your PGM file has ASCII-coded pixels, where you want to scan a whitespace-separated value for each pixel. You do this the same way you read the width and height.

The debug code might look like this:

line = d.readLine(); // first image line
s = new Scanner(line);
for(int i=0;i<30;i++) /* printing first 30 values from the image including spaces*/
    System.out.println((byte)s.nextInt());