#ifndef ENVI_HEADER_H #define ENVI_HEADER_H #include #include #include #include #include #include #include #include //information from an ENVI header file //A good resource can be found here: http://www.exelisvis.com/docs/enviheaderfiles.html namespace stim{ struct envi_header { enum dataType {dummy0, int8, int16, int32, float32, float64, complex32, dummy7, dummy8, complex64, dummy10, dummy11, uint16, uint32, int64, uint64}; enum interleaveType {BIP, BIL, BSQ}; //bip = Z,X,Y; bil = X,Z,Y; bsq = X,Y,Z enum endianType {endianLittle, endianBig}; std::string name; std::string description; size_t samples; //x-axis size_t lines; //y-axis size_t bands; //spectral axis size_t header_offset; //header offset for binary file (in bytes) std::string file_type; //should be "ENVI Standard" envi_header::dataType data_type; //data representation; common value is 4 (32-bit float) envi_header::interleaveType interleave; std::string sensor_type; //not really used envi_header::endianType byte_order; //little = least significant bit first (most systems) double x_start, y_start; //coordinates of the upper-left corner of the image std::string wavelength_units; //stored wavelength units std::string z_plot_titles[2]; double pixel_size[2]; //pixel size along X and Y std::vector band_names; //name for each band in the image std::vector wavelength; //wavelength for each band void init(){ name = ""; //specify default values for a new or empty ENVI file samples = 0; lines = 0; bands = 0; header_offset = 0; data_type = float32; interleave = BSQ; byte_order = endianLittle; x_start = y_start = 0; pixel_size[0] = pixel_size[1] = 1; //strings file_type = "ENVI Standard"; sensor_type = "Unknown"; wavelength_units = "Unknown"; z_plot_titles[0] = z_plot_titles[1] = "Unknown"; } envi_header(){ init(); } envi_header(std::string name){ init(); load(name); } //sets the wavelength vector given a starting value and uniform step size void set_wavelengths(double start, double step){ size_t B = bands; //get the number of bands wavelength.resize(B); for(size_t b = 0; b < B; b++) wavelength[b] = start + b * step; } std::string trim(std::string line){ if(line.length() == 0) return line; //trims whitespace from the beginning and end of line size_t start_i, end_i; for(start_i=0; start_i < line.length(); start_i++) if(line[start_i] != 32) { break; } for(end_i = line.length()-1; end_i >= start_i; end_i--) if(line[end_i] != ' ' && line[end_i] != '\r') { break; } return line.substr(start_i, end_i - start_i+1); } std::string get_token(std::string line){ //returns a variable name; in this case we look for the '=' sign size_t i = line.find_first_of('='); std::string result; if(i != std::string::npos) result = trim(line.substr(0, i-1)); return result; } std::string get_data_str(std::string line){ size_t i = line.find_first_of('='); std::string result; if(i != std::string::npos) result = trim(line.substr(i+1)); else { std::cout<<"ENVI Header error - data not found for token: "< get_string_seq(std::string token, std::string sequence) { //this function returns a sequence of comma-delimited strings std::vector result; std::string entry; size_t i; do { i = sequence.find_first_of(','); entry = sequence.substr(0, i); sequence = sequence.substr(i+1); result.push_back(trim(entry)); }while(i != std::string::npos); return result; } std::vector get_double_seq(std::string token, std::string sequence) { //this function returns a sequence of comma-delimited strings std::vector result; std::string entry; size_t i; do { i = sequence.find_first_of(','); entry = sequence.substr(0, i); sequence = sequence.substr(i+1); result.push_back(atof(entry.c_str())); //std::cout<>line; if(line != "ENVI") { std::cout<<"ENVI Header Error: The header doesn't appear to be an ENVI file. The first line should be 'ENVI'."< pxsize = get_double_seq(token, string_sequence); pixel_size[0] = pxsize[0]; pixel_size[1] = pxsize[1]; } else if(token == "z plot titles") { std::string string_sequence = get_brace_str(token, line, file); std::vector titles = get_string_seq(token, string_sequence); z_plot_titles[0] = titles[0]; z_plot_titles[1] = titles[1]; } else if(token == "samples") samples = atoi(get_data_str(line).c_str()); else if(token == "lines") lines = atoi(get_data_str(line).c_str()); else if(token == "bands") bands = atoi(get_data_str(line).c_str()); else if(token == "header offset") header_offset = atoi(get_data_str(line).c_str()); else if(token == "file type") file_type = get_data_str(line); else if(token == "data type") data_type = (dataType)atoi(get_data_str(line).c_str()); else if(token == "interleave") { std::string interleave_str = get_data_str(line); if(interleave_str == "bip") interleave = BIP; else if(interleave_str == "bil") interleave = BIL; else if(interleave_str == "bsq") interleave = BSQ; } else if(token == "sensor type") sensor_type = get_data_str(line); else if(token == "byte order") byte_order = (endianType)atoi(get_data_str(line).c_str()); else if(token == "x start") x_start = atof(get_data_str(line).c_str()); else if(token == "y start") y_start = atof(get_data_str(line).c_str()); else if(token == "wavelength units") wavelength_units = get_data_str(line); //get the next line getline(file, line); } //make sure the number of bands matches the number of wavelengths size_t wavelengths = wavelength.size(); if(wavelengths && bands != wavelengths) { std::cout<<"ENVI Header Error -- Number of wavelengths doesn't match the number of bands. Bands = "< 0) { outfile<<"band names = {"< band_index(double w){ std::vector idx; //create an empty array of indices if(wavelength.size() == 0){ //if a wavelength vector doesn't exist, assume the passed value is a band if(w < 0 || w > bands-1) return idx; //if the band is outside the given band range, return an empty vector size_t low, high; //allocate space for the floor and ceiling low = (size_t)std::floor(w); //calculate the floor high = (size_t)std::ceil(w); //calculate the ceiling if(low == high) //if the floor and ceiling are the same idx.push_back(low); //return a vector with one element (the given w matches a band exactly) else{ idx.resize(2); //otherwise return the floor and ceiling idx[0] = low; idx[1] = high; } return idx; } else if(w < wavelength[0] || w > wavelength[bands-1]) return idx; //if the wavelength range is outside of the file, return an empty array for(size_t b = 0; b < bands; b++){ //for each band in the wavelength vector if(wavelength[b] == w){ //if an exact match is found idx.push_back(b); //add the band to the array and return it return idx; } if(wavelength[b] >= w){ //if the current wavelength exceeds w idx.resize(2); idx[0] = b-1; //push both the previous and current band index idx[1] = b; return idx; //return the pair of indices } } return idx; } /// Convert a wavelength range to a list of bands std::vector band_indices(double w0, double w1){ size_t idx0 = 0; size_t idx1 = 0; //declare the interval indices for the band range //get the indices for the first wavelength std::vector r; if(w0 > wavelength[bands-1] || w1 < wavelength[0]){ std::cout<<"ERROR in envi_header::band_indices - wavelengths are completely out of range: ["< w0) } //get the indices for the second wavelength if(w1 > wavelength[bands-1]) idx1 = bands-1; else{ r = band_index(w1); if(r.size() == 0) idx1 = bands - 1; else idx1 = r[0]; //take the lowest band index (this is the last band < w1) } size_t n = idx1 - idx0 + 1; //calculate the number of bands in this interval r.resize(n); //resize the band index array for(size_t b = 0; b < n; b++){ //for each band in the interval r[b] = idx0 + b; // insert the band in the array } return r; //return the index array } }; //end EnviHeader } #endif