0

I need to read a pgm file and store the array of values contained in it in a 2D array. In PGM format, each pixel is specified by a gray value between 0 and MaxVal. The first three lines give information related to the image: magic number, height, width and maxVal. The file also includes whitespaces. Lines starting with # are comments. This is what I had written till now.

public class PGM{

public static void main(String args[]) throws Exception {
    FileInputStream f = new FileInputStream("C:\\......\\brain_001.pgm");
    DataInputStream d = new DataInputStream(f);
    d.readLine();//first line contains P5
    String line = d.readLine();//second line contains height and width
    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();
    byte[][] im = new byte[height][width];
    for (int i = 0; i < 258; i++) {
        for (int j = 0; j < 258; j++) {
            im[i][j] = -1;
        }
    }
    int count = 0;
    byte b;
    try {
        while (true) {
            b = (byte) (d.readUnsignedByte());
            if (b == '\n') { //do nothing if new line encountered
            } else if (b == '#') {
                d.readLine();
            } else if (Character.isWhitespace(b)) { // do nothing if whitespace encountered
            } else {
                im[count / width][count % width] = b;
                count++;
            }
        }
    } catch (EOFException e) {
    }
    System.out.println("Height=" + height);
    System.out.println("Width=" + height);
    System.out.println("Required elemnts=" + (height * width));
    System.out.println("Obtained elemnets=" + count);

}
}

When the program is run, I get the following output:

Height=258
Width=258
Required elemnts=66564
Obtained elemnets=43513

The number of elements (each corresponding to a gray value) are less than the required ones. When I open the file with a PGM viewer, everything shows up correctly. Also, when I print the contents of the array, I see a lot of negative values. But all of them have to be greater than or equal to zero. Where have I gone wrong?

Ranjith
  • 1,623
  • 3
  • 21
  • 34

2 Answers2

1

Most likely its because of deprecated method readLine() from DataInputStream. As mentioned in its annotation

*This method does not properly convert bytes to characters. As of JDK 1.1, the preferred way to read lines of text is via the BufferedReader.readLine() method. Programs that use the DataInputStream class to read lines can be converted to use the BufferedReader class by replacing code of the form: DataInputStream d = new DataInputStream(in);

with: BufferedReader d = new BufferedReader(new InputStreamReader(in));*

When I changed your program according to this advice, it worked for me (I made a couple of other changes as well:

(Updated to handle P2 and P5 flavors of PGM)

    public static void main(String args[]) throws Exception {
        try {
            InputStream f = ClassLoader.getSystemClassLoader().getResourceAsStream("lena.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("#")) {
                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();
            byte[][] im = new byte[height][width];

            int count = 0;
            int b = 0;
            try {
                while (count < height*width) {
                    b = d.read() ;
                    if ( b < 0 ) 
                        break ;

                    if (b == '\n') { // do nothing if new line encountered
                    } 
//                  else if (b == '#') {
//                      d.readLine();
//                  } 
//                  else if (Character.isWhitespace(b)) { // do nothing if whitespace encountered
//                  } 
                    else {
                        if ( "P5".equals(magic) ) { // Binary format
                            im[count / width][count % width] = (byte)((b >> 8) & 0xFF);
                            count++;
                            im[count / width][count % width] = (byte)(b & 0xFF);
                            count++;
                        }
                        else {  // ASCII format
                            im[count / width][count % width] = (byte)b ;
                            count++;
                        }
                    }
                }
            } catch (EOFException eof) {
                eof.printStackTrace(System.out) ;
            }
            System.out.println("Height=" + height);
            System.out.println("Width=" + height);
            System.out.println("Required elements=" + (height * width));
            System.out.println("Obtained elements=" + count);
        }
        catch(Throwable t) {
            t.printStackTrace(System.err) ;
            return ;
        }

    }
mazaneicha
  • 8,794
  • 4
  • 33
  • 52
  • There is still something wrong with the program. The values read at the end through the method d.read() are -1 which indicates that the program reaches the end of the stream before the input is obtained completely. Can you please suggest a solution to this problem? – Ranjith Aug 14 '12 at 06:41
  • I just tested this code on a couple of pgm files I have and it works fine, so I am pretty sure that if in your case 'Required elements' matches 'Obtained elements' then you get what you have in your file. I updated the source to print out a stack trace if the EOF is hit (which would mean, in turn, that the height or width in your header is wrong). Please try it and let me know what you see. – mazaneicha Aug 14 '12 at 15:29
  • d.read() reads a single character from the stream. If the end of the stream is reached, -1 is returned. EOFException does not occur. That is what is happening in this case. The end of the file is being reached and there are no more elements to be read. 4So, a great number of the final input read is -1. You can check it out by adding the following line after b = (byte) (d.read()) ::::: if(b==-1) System.out.println(count); ....................... – Ranjith Aug 14 '12 at 15:43
  • .............. The problem here I think is that the image data is stored as bytes and not as chars. One byte needs to be read at a time and not a char at a time. I tried doing it using InputStreamReader but I am still getting some diffidence in the number of bytes read. I am getting all the required elements much before the end of the stream is reached. – Ranjith Aug 14 '12 at 15:45
  • You're right! and now I see why it worked for me - apparently there are two PGM flavors, P5 is binary (bytes, as in your file) and P2 (ASCII characters as were my files). So we have to decode them differently. Ppheeew... Please see the updated source, let me know if it works. – mazaneicha Aug 14 '12 at 17:24
  • Change the condition of while to ----- while (count < height*width+152000) ----- Since, we have a break for b<0, the while loop terminates automatically when the end of the file is reached.----- Comment out the following two statemnets ----- – Ranjith Aug 15 '12 at 03:44
  • im[count / width][count % width] = (byte)((b >> 8) & 0xFF); ----- im[count / width][count % width] = (byte)(b & 0xFF); ----- Run the program. You will see that the obtained elemnts are greater than those required. The probable reason is that you have comment out the conditions for if b=# and b=whitespace ----- Including them in the program and running again ----- The obtained elemnts are still greater than the required elements. ----- – Ranjith Aug 15 '12 at 03:45
  • Also, many of the values being stored in the im array are turning out to be negative. They all should be positive. ( Off topic: How do I insert a line break in comments) – Ranjith Aug 15 '12 at 03:49
  • I obviously didnt see that happening in my test cases, otherwise I wouldnt have posted the code. Is there any chance you can send me your pgm file? mazaneicha@gmail.com – mazaneicha Aug 18 '12 at 19:47
0

The following code works for me:

FileInputStream f;
    try {
        f = new FileInputStream(fileLocation);
        BufferedReader br = new BufferedReader(new InputStreamReader(f));
        String magic = br.readLine();    // first line contains P2 or P5
        String line = br.readLine();     // second line contains height and width

        //-scan width and height
        Scanner s = new Scanner(line);
        int width = s.nextInt();
        int height = s.nextInt();
        s.close();

        //-scan max value
        line = br.readLine();// third line contains maxVal
        s = new Scanner(line);
        //          int maxVal = s.nextInt();
        //-close scanner
        s.close();
        //-close buffer
        br.close();
        f.close();

        xvec=new DenseMatrix64F(height*width, 1);

        int b=0;
        int counter=0;
        f = new FileInputStream(fileLocation);
        DataInputStream dis = new DataInputStream(f);
        //-move across unused lines
        dis.readLine();
        dis.readLine();
        dis.readLine();
        while ((b=dis.read()) >= 0) {
            xvec.set(counter, 0, b);
            counter++;
        }
        dis.close();
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
lenhhoxung
  • 2,530
  • 2
  • 30
  • 61