package fr.unistra.pelican.util.remotesensing; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.StringTokenizer; import fr.unistra.pelican.Image; import fr.unistra.pelican.algorithms.histogram.ContrastStretchEachBands; import fr.unistra.pelican.algorithms.io.ImageLoader; import fr.unistra.pelican.algorithms.visualisation.Viewer2D; /** * Provides some fields and methods to read an image from its HDR file. * The HDR file is read, and then, a class extending the BinReader abstract class is called depending on the format. * For the moment, only ENVI format is supported (not ArcView). * Some pieces of code are taken from Mustic's RawImage and BinImage. * More information here : http://www.brockmann-consult.de/beam/doc/help/BeamDimapFormat.html * * @author Clément Hengy */ public final class HdrReader { /** String of the Image category. */ public static final String IMAGE_CATEG = "Image"; /** String of the Image Size category. */ public static final String IMAGE_SIZE_CATEG = "Image size"; /** String of the Data category. */ public static final String DATA_CATEG = "Data"; /** String of the Geographical information category. */ public static final String GEO_INFO_CATEG = "Geographical information"; /** Code of the BSQ format. */ public static final int FORMAT_BSQ = 1; /** Code of the BIL format. */ public static final int FORMAT_BIL = 2; /** Code of the BIP format. */ public static final int FORMAT_BIP = 3; /** Representes the error code. */ public static final int I_ERR = -1; /** Represents the width tag in header file. */ public static final String DESCRIPION_ENVI = "description"; /** Represents the code of description tag. */ public static final int I_DESC = 0; /** Represents the width tag in header file. */ public static final String SAMPLE_ENVI = "samples"; /** Represents the code of width tag. */ public static final int I_SAMP = 1; /** Represents the height tag in header file. */ public static final String LINES_ENVI = "lines"; /** Represents the code of height tag. */ public static final int I_LIN = 2; /** Represents the number of band tag in header file. */ public static final String BANDS_ENVI = "bands"; /** Represents the code of number of band tag. */ public static final int I_BAND = 3; /** Represents the format tag in header file = "bsq", "bil" or "bip". */ public static final String INTERLEAVE_ENVI = "interleave"; /** Represents the code of format tag. */ public static final int I_INTERL = 4; /** Represents the byte order tag in header file. */ public static final String BYTE_ORDER_ENVI = "byte order"; /** Represents the code of byte order tag. */ public static final int I_BYTE = 5; /** Represents the data type tag in header file. */ public static final String DATA_TYPE_ENVI = "data type"; /** Represents the code of data type tag. */ public static final int I_DATA = 6; /** Represents the (0,0) x position tag in header file. */ public static final String X_START_ENVI = "x-start"; public static final String X_START_ENVI2 = "x start"; /** Represents the code of (0,0) x position tag. */ public static final int I_X = 7; /** Represents the (0,0) y position tag in header file. */ public static final String Y_START_ENVI = "y-start"; public static final String Y_START_ENVI2 = "y start"; /** Represents the code of (0,0) y position tag. */ public static final int I_Y = 8; /** Represents the wavelength tag in header file. */ public static final String WAVELENGTH_ENVI = "wavelength"; /** Represents the code of wavelength tag. */ public static final int I_WAVE = 9; /** Represents the map info tag in the header file */ public static final String MAP_INFO_ENVI = "map info"; /** Represents the code of the map info tag */ public static final int I_MAP_INFO = 10; /** Represents the fwhm tag in the header file */ public static final String FWHM_ENVI = "fwhm"; /** Represents the code of the fwhm tag */ public static final int I_FWHM = 11; /** Represents the band names tag in header file.*/ public static final String BAND_NAMES_ENVI = "band names"; /**Represents the code of band names tag. */ public static final int I_BAND_NAMES = 12; /** String describing the image or processing performed. */ private String description = "No description"; /** Number of pixels per image line for each band. */ private int cols; /** Number of lines per image for each band. */ private int lines; /** Number of bands per image file. */ private int bands; /** * Depending on this number, the value of each pixel can be stored on 1, 2, 4 or 8 bytes (signed or not). * Here is the list of the supported values and there meaning : * 1 : 1 byte * 2 : 2 bytes (signed integer) * 3 : 4 bytes (signed long integer) * 4 : 4 bytes (floating point) * 5 : 8 bytes (double precision floating point) * 12 : 2 bytes (unsigned integer) */ private int dataType; /** This field can take the values HdrReader.FORMAT_BSQ, HdrReader.FORMAT_BIL or HdrReader.FORMAT_BSQ. */ private int fileType; /** True if the values are stored in big-endian (Most Significant Byte First), False otherwise. */ private boolean byteOrder = true; /** True if the values are signed, False otherwise. */ private boolean isSigned; /** Number of byte per pixel. */ public static final String BYTE_NUMBER = "byte number"; /** Number of bytes used to store each value. */ private int bytesNumber; /** Lists geographic coordinates information in the order of projection name (UTM), reference pixel x location in file coordinates, pixel y, pixel easting, pixel northing, x pixel size, y pixel size, Projection Zone, "North" or "South" for UTM only. */ private String mapinfo = "No map information"; /** Lists the center wavelength values of each band in an image. */ private ArrayList<Double> wavelength = new ArrayList<Double>(); /** Lists of the band names */ private ArrayList<String> bandNames = new ArrayList<String>(); /** Lists the full width at half maximum of each band in an image. */ private String fwhm = "No FWHM"; private double xstart = 0.0; private double ystart = 0.0; /** Absolute location of the header file*/ public static final String HEADER_PATH = "header path"; /** The absolute path of the header file. */ private String headerPath; /** Represents the x pixel size (in meters) */ public static final String RESOLUTIONX = "resolution x"; private double resolutionX = 1.0; /** Represents the y pixel size (in meters) */ public static final String RESOLUTIONY = "resolution y"; private double resolutionY = 1.0; /** Here is all the crap they don't know where to place it somewhere else. */ private String history = "No history"; /** A class implementing BinReader will convert the binary file into a fr.unistra.pelican.DoubleImage. */ private BinReader br; /** * * @param path path of the header file * @return the fr.unistra.pelican Image generated */ public Image getPelicanImage(String path){ this.readHeader(path); switch(this.fileType){ case HdrReader.FORMAT_BSQ: br = new BSQReader(this, path); break; case HdrReader.FORMAT_BIL: br = new BILReader(this, path); break; default: System.err.println("getPelicanImage(String) : the asked format is currently not supported"); return null; } headerPath = path; try{ return br.getPelicanImage(); }catch(Throwable t) { throw new RuntimeException(t); } } /** * * @param path path of the header file * @return the fr.unistra.pelican Image generated */ public Image getPelicanImage(String path, int sx, int sy, int ex, int ey){ this.readHeader(path); switch(this.fileType){ case HdrReader.FORMAT_BSQ: br = new BSQReader(this, path); break; case HdrReader.FORMAT_BIL: br = new BILReader(this, path); break; default: System.err.println("getPelicanImage(String) : the asked format is currently not supported"); return null; } headerPath = path; try{ return br.getPelicanImage(sx, sy, ex, ey); }catch(Throwable t) { throw new RuntimeException(t); } } /** * Determines the tag corresponding with the line of a header file. * @param line a line contained in the header file. * * @return the tag corresponding with the description or HdrReader.I_ERR if the description is not found. */ private static int getTypeInfo(String line) { int resVal = HdrReader.I_ERR; // find the correct code for each keyword if (line.startsWith(HdrReader.DESCRIPION_ENVI)) { // the description keyword resVal = HdrReader.I_DESC; } else if (line.startsWith(HdrReader.SAMPLE_ENVI)) { // the numberof columns keyword resVal = HdrReader.I_SAMP; } else if (line.startsWith(HdrReader.LINES_ENVI)) { // the number of lines keyword resVal = HdrReader.I_LIN; } else if (line.startsWith(HdrReader.BANDS_ENVI)) { // the number of bands keyword resVal = HdrReader.I_BAND; } else if (line.startsWith(HdrReader.INTERLEAVE_ENVI)) { // the type of bin file keyword resVal = HdrReader.I_INTERL; } else if (line.startsWith(HdrReader.BYTE_ORDER_ENVI)) { // the type of coding keyword resVal = HdrReader.I_BYTE; } else if (line.startsWith(HdrReader.DATA_TYPE_ENVI)) { // the position of the signed bit keyword resVal = HdrReader.I_DATA; } else if (line.startsWith(HdrReader.X_START_ENVI) || line.startsWith(HdrReader.X_START_ENVI2)) { // the x-start position keyword resVal = HdrReader.I_X; } else if (line.startsWith(HdrReader.Y_START_ENVI) || line.startsWith(HdrReader.Y_START_ENVI2)) { // the y-start position keyword resVal = HdrReader.I_Y; } else if (line.startsWith(HdrReader.WAVELENGTH_ENVI) && line.startsWith(HdrReader.WAVELENGTH_ENVI + " = {")) { // the list of the wavelenght keyword resVal = HdrReader.I_WAVE; } else if (line.startsWith(HdrReader.MAP_INFO_ENVI)) { resVal = HdrReader.I_MAP_INFO; } else if(line.startsWith(HdrReader.FWHM_ENVI)) { resVal = HdrReader.I_FWHM; } else if(line.startsWith(HdrReader.BAND_NAMES_ENVI+" = {")){ resVal = HdrReader.I_BAND_NAMES; } return resVal; } /** * Extracts the value from a line in the format : key = value. * @param line a String that contains the couple (key, value). * @return the value of the line, or null if the character = does not occur. */ private static String getInfo(String line) { String res = null; if (line.indexOf("=") != -1) res = line.substring(line.indexOf("= ") + 2); return res; } /** * Reads and extracts all informations in the header file of the binary image. * @param headerPath path of the header file */ public void readHeader(String headerPath){ FileReader fd = null; // reader to open the header file BufferedReader br = null; // buffered to store the file String line; // string to store one line of the bufferedReader String information; // the information extracted String fileName = headerPath; // opening the header file try { fd = new FileReader(fileName); br = new BufferedReader(fd); } catch (IOException ex) { System.err.println("readHeader() : File not found"); return; } catch (IllegalArgumentException ex) { System.err.println("readHeader() : The file cannot be opened"); return; } // recovery of all informations of the header try { line = br.readLine(); // reading information StringTokenizer stk; while (line != null) { information = getInfo(line); // extracts the field name // result of the reading information switch (getTypeInfo(line)) { case I_DESC: if (information.startsWith("{")) { while (!(information.endsWith("}"))) { information += "\n"; line = br.readLine(); if (line == null) { System.err.println("readHeader() : Error while reading Header file"); return; } information += line; } } this.description = information; // description line break; case I_BAND: this.bands = Integer.parseInt(information); // number of bands break; case I_SAMP: this.cols = Integer.parseInt(information); // number of columns break; case I_LIN: this.lines = Integer.parseInt(information); // number of lines break; case I_INTERL: if(information.equals("bsq") || information.equals("BSQ")) this.fileType = HdrReader.FORMAT_BSQ; else if(information.equals("bil") || information.equals("BIL")) this.fileType = HdrReader.FORMAT_BIL; else if(information.equals("bip") || information.equals("BIP")) this.fileType = HdrReader.FORMAT_BIP; break; case I_WAVE: line = line.substring(line.indexOf("{")+1); while(true){ stk = new StringTokenizer(line, ","); while(stk.hasMoreTokens()){ String str = stk.nextToken(); if(str.contains("}")) str = str.substring(0, str.length()-1); try{ wavelength.add(Double.parseDouble(str)); } catch(NumberFormatException ex){ System.err.println("Little error while reading wavelength, please, check your HDR."); } } if(line.contains("}")) break; line = br.readLine(); } break; case I_FWHM: line = br.readLine(); // list of fwhm this.fwhm= line; // the list is in the next line after the descriptor break; case I_DATA: this.dataType = Integer.parseInt(information); // coding type break; case I_BYTE: this.byteOrder = (Integer.parseInt(information) == 1); // order of the bytes in the bin file break; case I_MAP_INFO: this.mapinfo = information; // next section will extract x and y resolution stk = new StringTokenizer(information, ","); for(int i = 0; i < 5; ++i){stk.nextToken();} resolutionX = Double.parseDouble(stk.nextToken()); resolutionY = Double.parseDouble(stk.nextToken()); break; case I_X: this.xstart = Double.parseDouble(information); break; case I_Y: this.ystart = Double.parseDouble(information); break; case I_BAND_NAMES: line = line.substring(line.indexOf("{")+1); while(true){ stk = new StringTokenizer(line, ","); while(stk.hasMoreTokens()){ String str = stk.nextToken(); if(str.contains("}")) str = str.substring(0, str.length()-1); try{ if(str.length()!=1) bandNames.add(str); } catch(NumberFormatException ex){ System.err.println("Little error while reading wavelength, please, check your HDR."); } } if(line.contains("}")) break; line = br.readLine(); } break; default: break; } // read the next line of the header file line = br.readLine(); } // all the header file is read. Closes of the readers br.close(); fd.close(); switch (this.dataType) { case 1: this.bytesNumber = 1; this.isSigned = false; break; case 2: this.bytesNumber = 2; this.isSigned = true; break; case 3: case 4: this.bytesNumber = 4; this.isSigned = true; break; case 5: this.bytesNumber = 8; this.isSigned = true; break; case 12: this.bytesNumber = 2; this.isSigned = false; break; default: this.bytesNumber = 8; this.isSigned = true; System.out.println("readHeader() : no dataType or unsupported dataType. Assumed values are 8 bytes signed."); break; } } catch (IOException ex) { System.err.println("readHeader() : Error while Header file reading process -> " + ex.toString()); } catch (IllegalArgumentException ex) { System.err.println("readHeader() : Error while Header file reading process -> " + ex.toString()); } } public int getBands() { return bands; } public boolean getByteOrder() { return byteOrder; } public int getBytesNumber() { return bytesNumber; } public int getCols() { return cols; } public int getDataType() { return dataType; } public String getDescription() { return description; } public double getXStart() { return xstart; } public double getYStart() { return ystart; } public int getFileType() { return fileType; } public String getFwhm() { return fwhm; } public String getHistory() { return history; } public boolean isSigned() { return isSigned; } public int getLines() { return lines; } public String getMapinfo() { return mapinfo; } public Double[] getWavelength() { return this.wavelength.toArray(new Double[0]); } public String[] getBandNames(){ return this.bandNames.toArray(new String[0]); } public String getHeaderPath(){ return headerPath; } public double getResolutionX(){ return resolutionX; } public double getResolutionY(){ return resolutionY; } public static void main(String[] args) { //String path = "/home/hengy/Desktop/images/LVlandsat.hdr"; String path = "/home/hengy/double.hdr"; Image img = (Image) new ImageLoader().process(path); img = (Image) new ContrastStretchEachBands().process(img); //img = fr.unistra.pelican.algorithms.conversion.ColorImageFromMultiBandImage.process(img, 0, 3, 2); new Viewer2D().process(img, "toto"); /* HdrReader hr = new HdrReader(); hr.readHeader(path); System.out.println("******** Informations : ********"); System.out.println("Description : " + hr.description); System.out.println("Nombre de lignes : " + hr.lines); System.out.println("Nombre de colonnes : " + hr.cols); System.out.println("Nombre de bandes : " + hr.bands); System.out.println("Data type : " + hr.dataType); System.out.println("File type : " + hr.fileType); System.out.println("Ordre des octets : " + hr.byteOrder); System.out.println("Signé : " + hr.isSigned); System.out.println("Nombre d'octets : " + hr.bytesNumber); //for(int i = 0; i < hr.wavelength.length; ++i) //System.out.println("WaveLength : " + hr.wavelength[i]); System.out.println("********************************"); System.out.println("Image en préparation"); Image img = hr.getPelicanImage(path); //img = ((fr.unistra.pelican.DoubleImage) img).scaleToZeroOne(); img = ((fr.unistra.pelican.IntegerImage) img).scaleToVisibleRange(); img = fr.unistra.pelican.algorithms.conversion.ColorImageFromMultiBandImage.process(img,0,1,2); System.out.println("Affichage de l'image"); //img.setColor(true); fr.unistra.pelican.algorithms.visualisation.Viewer2D.exec(img, "toto"); */ } }