File: PGM_Reader.java

package info (click to toggle)
imagej 1.52j-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 5,604 kB
  • sloc: java: 120,017; sh: 279; xml: 161; makefile: 6
file content (320 lines) | stat: -rw-r--r-- 11,707 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
package ij.plugin;

import ij.*;
import ij.io.*;
import ij.process.*;
import java.io.*;

/**
 * This plugin opens PxM format images.
 * <p/>
 * The portable graymap format is a lowest common denominator
 * grayscale file format. The definition is as follows:
 * <p/>
 * - A "magic number" for identifying the  file  type.   A  pgm
 * file's magic number is the two characters "P2".
 * - Whitespace (blanks, TABs, CRs, LFs).
 * - A width, formatted as ASCII characters in decimal.
 * - Whitespace.
 * - A height, again in ASCII decimal.
 * - Whitespace.
 * - The maximum gray value, again in ASCII decimal.
 * - Whitespace.
 * - Width * height gray values, each in ASCII decimal, between
 * 0 and the specified maximum value, separated by whi-
 * tespace, starting at the top-left corner of the graymap,
 * proceeding in normal English reading order. A value of 0
 * means black, and the maximum value means white.
 * - Characters from a "#" to the next end-of-line are ignored (comments).
 * - No line should be longer than 70 characters.
 * <p/>
 * Here is an example of a small graymap in this format:
 * P2
 * # feep.pgm
 * 24 7
 * 15
 * 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 * 0  3  3  3  3  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15 15 15 15  0
 * 0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0 15  0
 * 0  3  3  3  0  0  0  7  7  7  0  0  0 11 11 11  0  0  0 15 15 15 15  0
 * 0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0  0  0
 * 0  3  0  0  0  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15  0  0  0  0
 * 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 * <p/>
 * There is a  PGM variant that stores the pixel data as raw bytes:
 * <p/>
 * -The "magic number" is "P5" instead of "P2".
 * -The gray values are stored as plain bytes, instead of ASCII decimal.
 * -No whitespace is allowed in the grays section, and only a single
 * character of whitespace (typically a newline) is allowed after the maxval.
 * -The files are smaller and many times faster to read and write.
 * <p/>
 * Kai Barthel Nov 16 2004:
 * Extended to support PPM (portable pixmap) format images (24 bits only).
 * -The "magic numbers" are "P6" (raw) "P3" (ASCII).
 * <p/>
 * Ulf Dittmer April 2005:
 * Extended to support PBM (bitmap) images (P1 and P4)
 * <p/>
 * Jarek Sacha (jarek.at.ieee.org) December 2005:
 * Extended PPM support to 48 bit color images.
 */

public class PGM_Reader extends ImagePlus implements PlugIn {

    private int width, height;
    private boolean rawBits;
    private boolean sixteenBits;
    private boolean isColor;
    private boolean isBlackWhite;
    private int maxValue;

    public void run(String arg) {
        OpenDialog od = new OpenDialog("PBM/PGM/PPM Reader...", arg);
        String directory = od.getDirectory();
        String name = od.getFileName();
        if (name == null)
            return;
        String path = directory + name;

        IJ.showStatus("Opening: " + path);
        ImageStack stack;
        try {
            stack = openFile(path);
        }
        catch (IOException e) {
            String msg = e.getMessage();
            IJ.showMessage("PBM/PGM/PPM Reader", msg.equals("") ? "" + e : msg);
            return;
        }
        setStack(name, stack);
        FileInfo fi = new FileInfo();
        fi.fileFormat = FileInfo.PGM;
        fi.directory = directory;
        fi.fileName = name;
        setFileInfo(fi);
        if (arg.equals("")) show();
    }

    public ImageStack openFile(String path) throws IOException {
        InputStream is = new BufferedInputStream(new FileInputStream(path));
        try {
			StreamTokenizer tok = new StreamTokenizer(is); //deprecated, but it works
			//Reader r = new BufferedReader(new InputStreamReader(is));
			//StreamTokenizer tok = new StreamTokenizer(r);  // doesn't work
			tok.resetSyntax();
			tok.wordChars(33, 255);
			tok.whitespaceChars(0, ' ');
			tok.parseNumbers();
			tok.eolIsSignificant(true);
			tok.commentChar('#');
			openHeader(tok);
			//IJ.log("PGM_Reader: w="+width+",h="+height+",raw="+rawBits+",16bits="+sixteenBits+",color="+isColor+",b&w="+isBlackWhite+",max="+maxValue);
				
			if (!isColor && sixteenBits) { // 16-bit grayscale
				if (rawBits) {
					ImageProcessor ip = open16bitRawImage(is, width, height);
					ImageStack stack = new ImageStack(width, height);
					stack.addSlice("", ip);
					return stack;
				} else {
					ImageProcessor ip = open16bitAsciiImage(tok, width, height);
					ImageStack stack = new ImageStack(width, height);
					stack.addSlice("", ip);
					return stack;
				}
			}
			
			if (!isColor) { // 8-bit grayscale
				byte[] pixels = new byte[width * height];
				ImageProcessor ip = new ByteProcessor(width, height, pixels, null);
				if (rawBits)
					openRawImage(is, width * height, pixels);
				else
					openAsciiImage(tok, width * height, pixels);
				for (int i = pixels.length - 1; i >= 0; i--) {
					if (isBlackWhite) {
						if (rawBits) {
							if (i < (pixels.length / 8)) {
								for (int bit = 7; bit >= 0; bit--) {
									pixels[8 * i + 7 - bit] = (byte) ((pixels[i] & ((int) Math.pow(2, bit))) == 0 ? 255 : 0);
								}
							}
						} else
							pixels[i] = (byte) (pixels[i] == 0 ? 255 : 0);
					} else
						pixels[i] = (byte) (0xff & (255 * (int) (0xff & pixels[i]) / maxValue));
				}
				ImageStack stack = new ImageStack(width, height);
				stack.addSlice("", ip);
				return stack;
			}
			
			if (!sixteenBits) { // 8-bit color
				int[] pixels = new int[width * height];
				byte[] bytePixels = new byte[3 * width * height];
				ImageProcessor ip = new ColorProcessor(width, height, pixels);
				if (rawBits)
					openRawImage(is, 3 * width * height, bytePixels);
				else
					openAsciiImage(tok, 3 * width * height, bytePixels);
				for (int i = 0; i < width * height; i++) {
					int r = (int) (0xff & bytePixels[i * 3]);
					int g = (int) (0xff & bytePixels[i * 3 + 1]);
					int b = (int) (0xff & bytePixels[i * 3 + 2]);
					r = (r * 255 / maxValue) << 16;
					g = (g * 255 / maxValue) << 8;
					b = (b * 255 / maxValue);
					pixels[i] = 0xFF000000 | r | g | b;
				}
				ImageStack stack = new ImageStack(width, height);
				stack.addSlice("", ip);
				return stack;
			}
			
			// 16-bit raw color
			short[] red = new short[width*height];
			short[] green = new short[width*height];
			short[] blue = new short[width*height];
			if (rawBits) {
				byte[] bytePixels = new byte[6*width*height];
				openRawImage(is, 6*width*height, bytePixels);
				for (int i=0; i<width*height; i++) {
					int r1 = 0xff & bytePixels[i*6];
					int r2 = 0xff & bytePixels[i*6+1];
					int g1 = 0xff & bytePixels[i*6+2];
					int g2 = 0xff & bytePixels[i*6+3];
					int b1 = 0xff & bytePixels[i*6+ 4];
					int b2 = 0xff & bytePixels[i*6+5];
					red[i] = (short) (0xffff & (r1*256+r2));
					green[i] = (short) (0xffff & (g1*256+g2));
					blue[i] = (short) (0xffff & (b1*256+b2));
				}
			} else {
				ImageProcessor ip = open16bitAsciiImage(tok, 3*width, height);
				short[] pixels = (short[])ip.getPixels();
				for (int i=0; i<width*height; i++) {
					red[i] = (short)(pixels[i*3]&0xffffff);
					green[i] = (short)(pixels[i*3+1]&0xffffff);
					blue[i] = (short)(pixels[i*3+2]&0xffffff);
				}
			}
			ImageStack stack = new ImageStack(width, height);
			stack.addSlice("red", new ShortProcessor(width, height, red, null));
			stack.addSlice("green", new ShortProcessor(width, height, green, null));
			stack.addSlice("blue", new ShortProcessor(width, height, blue, null));
			return stack;
        } finally {
            if (is!=null) is.close();
        }
   }

    public void openHeader(StreamTokenizer tok) throws IOException {
        String magicNumber = getWord(tok);
        if (magicNumber.equals("P1")) {
            rawBits = false;
            isColor = false;
            isBlackWhite = true;
        } else if (magicNumber.equals("P4")) {
            rawBits = true;
            isColor = false;
            isBlackWhite = true;
        } else if (magicNumber.equals("P2")) {
            rawBits = false;
            isColor = false;
            isBlackWhite = false;
        } else if (magicNumber.equals("P5")) {
            rawBits = true;
            isColor = false;
            isBlackWhite = false;
        } else if (magicNumber.equals("P3")) {
            rawBits = false;
            isColor = true;
            isBlackWhite = false;
        } else if (magicNumber.equals("P6")) {
            rawBits = true;
            isColor = true;
            isBlackWhite = false;
        } else
            throw new IOException("PxM files must start with \"P1\" or \"P2\" or \"P3\" or \"P4\" or \"P5\" or \"P6\"");
        width = getInt(tok);
        height = getInt(tok);
        if (width == -1 || height == -1)
            throw new IOException("Error opening PxM header..");
        if (! isBlackWhite) {
            maxValue = getInt(tok);
            if (maxValue == -1)
                throw new IOException("Error opening PxM header..");
            sixteenBits = maxValue > 255;
        } else
            maxValue = 255;
        if (sixteenBits && maxValue > 65535)
            throw new IOException("The maximum gray value is larger than 65535.");
    }

    public void openAsciiImage(StreamTokenizer tok, int size, byte[] pixels) throws IOException {
        int i = 0;
        int inc = size/20;
        if (inc==0) inc = 1;
        while (tok.nextToken() != tok.TT_EOF) {
            if (tok.ttype == tok.TT_NUMBER) {
                pixels[i++] = (byte) (((int) tok.nval) & 255);
                if (i%inc==0)
                    IJ.showProgress(0.5 + ((double) i / size) / 2.0);
            }
        }
        IJ.showProgress(1.0);
    }

    public void openRawImage(InputStream is, int size, byte[] pixels) throws IOException {
        int count = 0;
        while (count < size && count >= 0)
            count = is.read(pixels, count, size - count);
    }

    public ImageProcessor open16bitRawImage(InputStream is, int width, int height) throws IOException {
        int size = width * height * 2;
        byte[] bytes = new byte[size];
        int count = 0;
        while (count < size && count >= 0)
            count = is.read(bytes, count, size - count);
        short[] pixels = new short[size / 2];
        for (int i = 0, j = 0; i < size / 2; i++, j += 2)
            pixels[i] = (short) (((bytes[j] & 0xff) << 8) | (bytes[j + 1] & 0xff)); //big endian
        return new ShortProcessor(width, height, pixels, null);
    }

    public ImageProcessor open16bitAsciiImage(StreamTokenizer tok, int width, int height) throws IOException {
        int i = 0;
        int size = width * height;
        int inc = size/20; // Progress update interval
        if (inc==0) inc = 1;
        short[] pixels = new short[size];
        while (tok.nextToken() != tok.TT_EOF) {
            if (tok.ttype == tok.TT_NUMBER) {
                pixels[i++] = (short) (((int) tok.nval) & 65535);
                if (i%inc==0)
                    IJ.showProgress(0.5 + ((double) i / size) / 2.0);
            }
        }
        IJ.showProgress(1.0);
        return new ShortProcessor(width, height, pixels, null);
    }

    String getWord(StreamTokenizer tok) throws IOException {
        while (tok.nextToken() != tok.TT_EOF) {
            if (tok.ttype == tok.TT_WORD)
                return tok.sval;
        }
        return null;
    }

    int getInt(StreamTokenizer tok) throws IOException {
        while (tok.nextToken() != tok.TT_EOF) {
            if (tok.ttype == tok.TT_NUMBER)
                return (int) tok.nval;
        }
        return -1;
    }

}