Commit 180d7f3ae17a1a239a774f2106a3d79e7392d15f
1 parent
0ef519a4
added binary file framework
Showing
7 changed files
with
550 additions
and
394 deletions
Show diff stats
1 | +#ifndef RTS_BIL_H | |
2 | +#define RTS_BIL_H | |
3 | + | |
4 | +#include "../envi/envi.h" | |
5 | + | |
6 | +namespace rts{ | |
7 | + | |
8 | +//This class specifies a BIL binary file that can be streamed to and from storage media | |
9 | +template< typename T > | |
10 | +class bil{ | |
11 | + | |
12 | +protected: | |
13 | + | |
14 | + rts::envi header; | |
15 | + | |
16 | + | |
17 | +public: | |
18 | + | |
19 | + bil(){ | |
20 | + init(); | |
21 | + } | |
22 | + | |
23 | +}; | |
24 | + | |
25 | +} | |
26 | + | |
27 | +#endif | ... | ... |
1 | +#ifndef RTS_BINARY_H | |
2 | +#define RTS_BINARY_H | |
3 | + | |
4 | +#include "../envi/envi.h" | |
5 | + | |
6 | +#include <fstream> | |
7 | +#include <sys/stat.h> | |
8 | + | |
9 | +namespace rts{ | |
10 | + | |
11 | +//This class contains a bunch of functions useful for multidimensional binary file access | |
12 | +template< typename T, unsigned int D = 3 > | |
13 | +class binary{ | |
14 | + | |
15 | +protected: | |
16 | + | |
17 | + std::fstream file; //file stream used for reading and writing | |
18 | + std::string name; //file name | |
19 | + | |
20 | + unsigned int R[D]; //resolution | |
21 | + unsigned int header; //header size (in bytes) | |
22 | + | |
23 | + | |
24 | + //basic initialization | |
25 | + void init(){ | |
26 | + memset(R, 0, sizeof(unsigned int) * D); //initialize the resolution to zero | |
27 | + header = 0; //initialize the header size to zero | |
28 | + } | |
29 | + | |
30 | + //returns the file size | |
31 | + unsigned int get_file_size(){ | |
32 | + | |
33 | + struct stat results; | |
34 | + if(stat(name.c_str(), &results) == 0) | |
35 | + return results.st_size; | |
36 | + else return 0; | |
37 | + } | |
38 | + | |
39 | + bool test_file_size(){ | |
40 | + unsigned int sum = header; //initialize the sum (in bytes) to the header size | |
41 | + for(unsigned int i = 0; i<D; i++) //iterate over each dimension, summing the byte size | |
42 | + sum += R[i] * sizeof(T); | |
43 | + if(sum == get_file_size()) return true; //if the byte size matches the file size, we're golden | |
44 | + else return false; //otherwise return an error | |
45 | + | |
46 | + } | |
47 | + | |
48 | + bool open_file(std::string filename){ | |
49 | + //open the file as binary for reading and writing | |
50 | + file.open(filename.c_str(), std::ios::in | std::ios::out | std::ios::binary); | |
51 | + | |
52 | + //if the file is successful | |
53 | + if(file){ | |
54 | + name = filename; //set the name | |
55 | + if(test_file_size()) //test the file size | |
56 | + return true; | |
57 | + } | |
58 | + | |
59 | + return false; | |
60 | + } | |
61 | + | |
62 | +public: | |
63 | + | |
64 | + bool open(std::string filename, vec<unsigned int, D> r, unsigned int h = 0){ | |
65 | + if(!open_file(filename)) return false; | |
66 | + | |
67 | + for(unsigned int i = 0; i < D; i++) | |
68 | + R[i] = r[i]; | |
69 | + | |
70 | + header = h; | |
71 | + | |
72 | + return test_file_size(); | |
73 | + } | |
74 | + | |
75 | + bool open(std::string filename, unsigned int X, unsigned int h = 0){ | |
76 | + return open(filename, vec<unsigned int, 1>(X), h); | |
77 | + } | |
78 | + | |
79 | + bool open(std::string filename, unsigned int X, unsigned int Y, unsigned int h = 0){ | |
80 | + return open(filename, vec<unsigned int, 2>(X, Y), h); | |
81 | + | |
82 | + | |
83 | + } | |
84 | + | |
85 | + bool open(std::string filename, unsigned int X, unsigned int Y, unsigned int Z, unsigned int h = 0){ | |
86 | + return open(filename, vec<unsigned int, 3>(X, Y, Z), h); | |
87 | + } | |
88 | + | |
89 | +}; | |
90 | + | |
91 | +} | |
92 | + | |
93 | +#endif | ... | ... |
envi/envi.h
1 | -#ifndef RTS_ENVI_H | |
2 | -#define RTS_ENVI_H | |
1 | +#ifndef ENVI_HEADER_H | |
2 | +#define ENVI_HEADER_H | |
3 | 3 | |
4 | 4 | #include <string> |
5 | -#include <iostream> | |
5 | +#include <iostream> | |
6 | 6 | #include <fstream> |
7 | -#include <sstream> | |
8 | - | |
9 | -#include "rts/envi/envi_header.h" | |
10 | - | |
11 | -/* This is a class for reading and writing ENVI binary files. This class will be updated as needed. | |
12 | - | |
13 | -What this class CAN currently do: | |
14 | - *) Write band images to a BSQ file. | |
15 | - *) Append band images to a BSQ file. | |
16 | - *) Interpolate and query bands in a float32 BSQ file | |
17 | -*/ | |
18 | -class EnviFile | |
7 | +#include <sstream> | |
8 | +#include <vector> | |
9 | +#include <stdlib.h> | |
10 | + | |
11 | +//information from an ENVI header file | |
12 | +//A good resource can be found here: http://www.exelisvis.com/docs/enviheaderfiles.html | |
13 | + | |
14 | +namespace rts{ | |
15 | + | |
16 | +struct envi | |
19 | 17 | { |
20 | - EnviHeader header; | |
21 | - FILE* data; | |
22 | - std::string mode; | |
18 | + enum dataType {dummy0, int8, int16, int32, float32, float64, complex32, dummy7, dummy8, complex64, dummy10, dummy11, uint16, uint32, int64, uint64}; | |
19 | + enum interleaveType {BIP, BIL, BSQ}; //bip = Z,X,Y; bil = X,Z,Y; bsq = X,Y,Z | |
20 | + enum endianType {endianLittle, endianBig}; | |
23 | 21 | |
24 | - //a locked file has alread had data written..certain things can't be changed | |
25 | - // accessor functions deal with these issues | |
26 | - bool locked; | |
22 | + std::string name; | |
27 | 23 | |
28 | - void init() | |
29 | - { | |
30 | - locked = false; | |
31 | - data = NULL; | |
24 | + std::string description; | |
25 | + | |
26 | + unsigned int samples; //x-axis | |
27 | + unsigned int lines; //y-axis | |
28 | + unsigned int bands; //spectral axis | |
29 | + unsigned int header_offset; //header offset for binary file (in bytes) | |
30 | + std::string file_type; //should be "ENVI Standard" | |
31 | + | |
32 | + envi::dataType data_type; //data representation; common value is 4 (32-bit float) | |
33 | + | |
34 | + | |
35 | + envi::interleaveType interleave; | |
36 | + | |
37 | + std::string sensor_type; //not really used | |
38 | + | |
39 | + envi::endianType byte_order; //little = least significant bit first (most systems) | |
40 | + | |
41 | + double x_start, y_start; //coordinates of the upper-left corner of the image | |
42 | + std::string wavelength_units; //stored wavelength units | |
43 | + std::string z_plot_titles[2]; | |
44 | + | |
45 | + double pixel_size[2]; //pixel size along X and Y | |
46 | + | |
47 | + std::vector<std::string> band_names; //name for each band in the image | |
48 | + std::vector<double> wavelength; //wavelength for each band | |
49 | + | |
50 | + envi(){ | |
51 | + name = ""; | |
52 | + | |
53 | + //specify default values for a new or empty ENVI file | |
54 | + samples = 0; | |
55 | + lines = 0; | |
56 | + bands = 0; | |
57 | + header_offset = 0; | |
58 | + data_type = float32; | |
59 | + interleave = BSQ; | |
60 | + byte_order = endianLittle; | |
61 | + x_start = y_start = 0; | |
62 | + pixel_size[0] = pixel_size[1] = 1; | |
63 | + | |
64 | + //strings | |
65 | + file_type = "ENVI Standard"; | |
66 | + sensor_type = "Unknown"; | |
67 | + wavelength_units = "Unknown"; | |
68 | + z_plot_titles[0] = z_plot_titles[1] = "Unknown"; | |
32 | 69 | } |
33 | 70 | |
34 | - void readonly() | |
35 | - { | |
36 | - if(mode == "r") | |
71 | + std::string trim(std::string line){ | |
72 | + //trims whitespace from the beginning and end of line | |
73 | + unsigned int start_i, end_i; | |
74 | + for(start_i=0; start_i < line.length(); start_i++) | |
75 | + if(line[start_i] != 32) | |
76 | + { | |
77 | + break; | |
78 | + } | |
79 | + | |
80 | + for(end_i = line.length()-1; end_i >= start_i; end_i--) | |
81 | + if(line[end_i] != ' ') | |
82 | + { | |
83 | + break; | |
84 | + } | |
85 | + | |
86 | + return line.substr(start_i, end_i - start_i+1); | |
87 | + } | |
88 | + | |
89 | + | |
90 | + std::string get_token(std::string line){ | |
91 | + //returns a variable name; in this case we look for the '=' sign | |
92 | + size_t i = line.find_first_of('='); | |
93 | + | |
94 | + std::string result; | |
95 | + if(i != std::string::npos) | |
96 | + result = trim(line.substr(0, i-1)); | |
97 | + | |
98 | + return result; | |
99 | + } | |
100 | + | |
101 | + std::string get_data_str(std::string line){ | |
102 | + size_t i = line.find_first_of('='); | |
103 | + | |
104 | + std::string result; | |
105 | + if(i != std::string::npos) | |
106 | + result = trim(line.substr(i+1)); | |
107 | + else | |
37 | 108 | { |
38 | - std::cout<<"ENVI Error: changes cannot be made to a read-only file."<<std::endl; | |
109 | + std::cout<<"ENVI Header error - data not found for token: "<<get_token(line)<<std::endl; | |
39 | 110 | exit(1); |
40 | 111 | } |
112 | + return result; | |
41 | 113 | } |
42 | 114 | |
43 | - //this function determines if the current software is capable of the requested change | |
44 | - bool caps(EnviHeader new_header) | |
115 | + std::string get_brace_str(std::string token, std::string line, std::ifstream &file) | |
45 | 116 | { |
46 | - readonly(); //if the file is read-only, throw an exception | |
117 | + //this function assembles all of the characters between curly braces | |
118 | + //this is how strings are defined within an ENVI file | |
47 | 119 | |
48 | - //we currently only support BSQ | |
49 | - if(new_header.interleave == Envi::BIP || new_header.interleave == Envi::BIL) | |
120 | + std::string result; | |
121 | + | |
122 | + //first, find the starting brace | |
123 | + size_t i; | |
124 | + do | |
125 | + { | |
126 | + i = line.find_first_of('{'); | |
127 | + if(i != std::string::npos) | |
128 | + break; | |
129 | + }while(file); | |
130 | + | |
131 | + //if i is still npos, we have reached the end of the file without a brace...something is wrong | |
132 | + if(i == std::string::npos) | |
50 | 133 | { |
51 | - std::cout<<"This software only supports BSQ."; | |
52 | - return false; | |
134 | + std::cout<<"ENVI Header error - string token declared without being defined: "<<token<<std::endl; | |
135 | + exit(1); | |
53 | 136 | } |
137 | + line = line.substr(i+1); | |
54 | 138 | |
55 | - //if the number of bands has changed | |
56 | - if(header.bands != new_header.bands) | |
139 | + //copy character data into the result string until we find a closing brace | |
140 | + while(file) | |
57 | 141 | { |
58 | - //the new header must be a BSQ file | |
59 | - if(new_header.interleave != Envi::BSQ) | |
142 | + i = line.find_first_of('}'); | |
143 | + | |
144 | + | |
145 | + if(i != std::string::npos) | |
60 | 146 | { |
61 | - std::cout<<"ENVI Error: can only add bands to a BSQ file."<<std::endl; | |
62 | - return false; | |
147 | + result += line.substr(0, i); | |
148 | + break; | |
63 | 149 | } |
150 | + else | |
151 | + result += line; | |
152 | + | |
153 | + getline(file, line); | |
64 | 154 | } |
65 | 155 | |
66 | - //disallow anything that can't be done when the file is locked | |
67 | - if(locked) | |
156 | + if(i == std::string::npos) | |
68 | 157 | { |
69 | - if(header.lines != new_header.lines) | |
70 | - { | |
71 | - std::cout<<"ENVI Error: cannot change the number of lines from "<<header.lines<<" to "<<new_header.lines<<" in a locked BSQ file."<<std::endl; | |
72 | - return false; | |
73 | - } | |
74 | - if(header.samples != new_header.samples) | |
75 | - { | |
76 | - std::cout<<"ENVI Error: cannot change the number of samples in a locked BSQ file."<<std::endl; | |
77 | - return false; | |
78 | - } | |
79 | - if(header.data_type != new_header.data_type) | |
80 | - { | |
81 | - std::cout<<"ENVI Error: cannot change the data type of a locked file."<<std::endl; | |
82 | - return false; | |
83 | - } | |
84 | - if(header.byte_order != new_header.byte_order) | |
85 | - { | |
86 | - std::cout<<"ENVI Error: cannot change the byte order of a locked file."<<std::endl; | |
87 | - return false; | |
88 | - } | |
89 | - } | |
90 | - | |
91 | - return true; | |
158 | + std::cout<<"ENVI Header error - string token declared without a terminating '}': "<<token<<std::endl; | |
159 | + exit(1); | |
160 | + } | |
92 | 161 | |
162 | + return trim(result); | |
93 | 163 | } |
94 | 164 | |
95 | - EnviHeader load_header(std::string header_file) | |
165 | + std::vector<std::string> get_string_seq(std::string token, std::string sequence) | |
96 | 166 | { |
97 | - EnviHeader new_header; | |
98 | - new_header.load(header_file); | |
99 | - return new_header; | |
167 | + //this function returns a sequence of comma-delimited strings | |
168 | + std::vector<std::string> result; | |
169 | + | |
170 | + std::string entry; | |
171 | + size_t i; | |
172 | + do | |
173 | + { | |
174 | + i = sequence.find_first_of(','); | |
175 | + entry = sequence.substr(0, i); | |
176 | + sequence = sequence.substr(i+1); | |
177 | + result.push_back(trim(entry)); | |
178 | + }while(i != std::string::npos); | |
179 | + | |
180 | + return result; | |
100 | 181 | } |
101 | 182 | |
102 | - void set_header(EnviHeader new_header) | |
183 | + std::vector<double> get_double_seq(std::string token, std::string sequence) | |
103 | 184 | { |
104 | - if(caps(new_header)) | |
105 | - header = new_header; | |
106 | - else | |
107 | - exit(1); | |
108 | - } | |
109 | - | |
110 | - bool test_exist(std::string filename) | |
111 | - { | |
112 | - //tests for the existance of the specified binary file | |
113 | - FILE* f; | |
114 | - long sz = 0; | |
115 | - f = fopen(filename.c_str(), "rb"); | |
116 | - if(f == NULL) | |
117 | - { | |
118 | - //std::cout<<"ENVI Error: error testing file existance."<<std::endl; | |
119 | - //exit(1); | |
120 | - return false; | |
121 | - } | |
122 | - | |
123 | - fseek(f, 0, SEEK_END); | |
124 | - sz = ftell(f); | |
125 | - fclose(f); | |
126 | - | |
127 | - if(sz>0) | |
128 | - return true; | |
129 | - else | |
130 | - return false; | |
131 | - | |
132 | - } | |
133 | - | |
134 | - void get_surrounding_bands(double wavelength, unsigned int &low, unsigned int &high) | |
135 | - { | |
136 | - int i; | |
137 | - int nBands = header.wavelength.size(); | |
138 | - low = high = 0; | |
139 | - //std::cout<<"Wavelength to search: "<<wavelength<<" Number of Bands: "<<nBands<<std::endl; | |
140 | - for(i=0; i<nBands; i++) | |
141 | - { | |
142 | - if(header.wavelength[i] < wavelength) | |
143 | - { | |
144 | - if(header.wavelength[low] > wavelength) | |
145 | - low = i; | |
146 | - if(header.wavelength[i] > header.wavelength[low]) | |
147 | - low = i; | |
148 | - } | |
149 | - | |
150 | - if(header.wavelength[i] > wavelength) | |
151 | - { | |
152 | - if(header.wavelength[high] < wavelength) | |
153 | - high = i; | |
154 | - if(header.wavelength[i] < header.wavelength[high]) | |
155 | - high = i; | |
156 | - } | |
157 | - //std::cout<<"Low: "<<low<<" High: "<<high<<std::endl; | |
158 | - } | |
159 | - //exit(1); | |
160 | - } | |
161 | - | |
162 | - void interpolate_band(void* result, void* A, void* B, double a) | |
163 | - { | |
164 | - //interpolate between two bands independent of data type | |
165 | - // CURRENTLY ONLY HANDLES FLOAT | |
166 | - int X = header.samples; | |
167 | - int Y = header.lines; | |
168 | - int N = X * Y; | |
169 | - | |
170 | - if(header.data_type != Envi::float32) | |
171 | - { | |
172 | - std::cout<<"ENVI Error: this software only handles interpolation of 32-bit floats."<<std::endl; | |
173 | - exit(1); | |
174 | - } | |
175 | - | |
176 | - float v0, v1; | |
177 | - for(int n=0; n<N; n++) | |
178 | - { | |
179 | - v0 = ((float*)A)[n]; | |
180 | - v1 = ((float*)B)[n]; | |
181 | - float r = v0 * a + v1 * (1.0 - a); | |
182 | - //((float*)result)[n] = ((float*)A)[n] * a + ((float*)B)[n] * (1.0 - a); | |
183 | - ((float*)result)[n] = r; | |
184 | - } | |
185 | - | |
186 | - } | |
187 | - | |
188 | -public: | |
189 | - | |
190 | - EnviFile() | |
191 | - { | |
192 | - mode = "w"; | |
193 | - init(); | |
185 | + //this function returns a sequence of comma-delimited strings | |
186 | + std::vector<double> result; | |
187 | + std::string entry; | |
188 | + size_t i; | |
189 | + do | |
190 | + { | |
191 | + i = sequence.find_first_of(','); | |
192 | + entry = sequence.substr(0, i); | |
193 | + sequence = sequence.substr(i+1); | |
194 | + result.push_back(atof(entry.c_str())); | |
195 | + //std::cout<<entry<<" "; | |
196 | + }while(i != std::string::npos); | |
197 | + | |
198 | + return result; | |
194 | 199 | } |
195 | 200 | |
196 | - EnviFile(std::string filename, std::string file_mode = "r") | |
201 | + bool load(std::string filename) | |
197 | 202 | { |
198 | - init(); | |
199 | - open(filename, filename + ".hdr", file_mode); | |
200 | - } | |
203 | + //open the header file | |
204 | + std::ifstream file(filename.c_str()); | |
201 | 205 | |
202 | - void open(std::string file_name, std::string header_name, std::string file_mode = "r") | |
203 | - { | |
204 | - | |
205 | - //load the header file | |
206 | - // if the file doesn't exist, that is okay unless it is read-only | |
207 | - | |
208 | - //if the file is being written, create a new header | |
209 | - if(file_mode == "w") | |
210 | - header.name = header_name; | |
211 | - else if(!header.load(header_name)) | |
212 | - { | |
213 | - if(file_mode == "r") | |
214 | - { | |
215 | - std::cout<<"ENVI Error: header file not found: "<<header_name<<std::endl; | |
216 | - exit(1); | |
217 | - } | |
218 | - //otherwise use the default header and save the name | |
219 | - header.name = header_name; | |
220 | - } | |
221 | - | |
222 | - mode = file_mode; | |
223 | - | |
224 | - //lock the file if it is read-only | |
225 | - if(mode == "r") | |
226 | - locked = true; | |
227 | - else if(mode == "a" || mode == "r+" || mode == "a+") | |
228 | - { | |
229 | - //if the file can be edited, lock it if data has already been written | |
230 | - if(test_exist(file_name)) | |
231 | - locked = true; | |
206 | + if(!file) | |
207 | + { | |
208 | + return false; | |
232 | 209 | } |
233 | - | |
234 | - | |
235 | - //open the data file | |
236 | - std::string tmpmode = mode+"b"; | |
237 | - //std::cout<<"Mode: "<<tmpmode<<std::endl; | |
238 | - data = fopen(file_name.c_str(), tmpmode.c_str()); | |
239 | - if(data == NULL) | |
210 | + | |
211 | + //the first line should just be "ENVI" | |
212 | + std::string line; | |
213 | + file>>line; | |
214 | + if(line != "ENVI") | |
240 | 215 | { |
241 | - std::cout<<"ENVI Error: unable to open binary file: "<<file_name<<std::endl; | |
216 | + std::cout<<"ENVI Header Error: The header doesn't appear to be an ENVI file. The first line should be 'ENVI'."<<std::endl; | |
242 | 217 | exit(1); |
243 | 218 | } |
244 | 219 | |
220 | + //for each line in the file, get the token | |
221 | + std::string token; | |
245 | 222 | |
246 | - } | |
223 | + //get a line | |
224 | + getline(file, line); | |
225 | + while(file) | |
226 | + { | |
247 | 227 | |
248 | - void setDescription(std::string desc) | |
249 | - { | |
250 | - readonly(); //if the file is read-only, throw an exception | |
251 | - header.description = desc; | |
252 | - } | |
253 | 228 | |
254 | - void setZPlotTitles(std::string spectrum, std::string value) | |
255 | - { | |
256 | - readonly(); //if the file is read-only, throw an exception | |
257 | - header.z_plot_titles[0] = spectrum; | |
258 | - header.z_plot_titles[1] = value; | |
259 | - } | |
260 | 229 | |
261 | - void setPixelSize(double sx, double sy) | |
262 | - { | |
263 | - readonly(); //if the file is read-only, throw an exception | |
264 | - header.pixel_size[0] = sx; | |
265 | - header.pixel_size[1] = sy; | |
266 | - } | |
230 | + //get the token | |
231 | + token = get_token(line); | |
267 | 232 | |
268 | - void setWavelengthUnits(std::string units) | |
269 | - { | |
270 | - readonly(); //if the file is read-only, throw an exception | |
271 | - header.wavelength_units = units; | |
272 | - } | |
233 | + if(token == "description") | |
234 | + description = get_brace_str(token, line, file); | |
235 | + else if(token == "band names") | |
236 | + { | |
237 | + std::string string_sequence = get_brace_str(token, line, file); | |
238 | + band_names = get_string_seq(token, string_sequence); | |
239 | + } | |
240 | + else if(token == "wavelength") | |
241 | + { | |
242 | + std::string string_sequence = get_brace_str(token, line, file); | |
243 | + wavelength = get_double_seq(token, string_sequence); | |
244 | + } | |
245 | + else if(token == "pixel size") | |
246 | + { | |
247 | + std::string string_sequence = get_brace_str(token, line, file); | |
248 | + std::vector<double> pxsize = get_double_seq(token, string_sequence); | |
249 | + pixel_size[0] = pxsize[0]; | |
250 | + pixel_size[1] = pxsize[1]; | |
251 | + } | |
252 | + else if(token == "z plot titles") | |
253 | + { | |
254 | + std::string string_sequence = get_brace_str(token, line, file); | |
255 | + std::vector<std::string> titles = get_string_seq(token, string_sequence); | |
256 | + z_plot_titles[0] = titles[0]; | |
257 | + z_plot_titles[1] = titles[1]; | |
258 | + } | |
273 | 259 | |
274 | - void setCoordinates(double x, double y) | |
275 | - { | |
276 | - readonly(); //if the file is read-only, throw an exception | |
277 | - header.x_start = x; | |
278 | - header.y_start = y; | |
260 | + else if(token == "samples") | |
261 | + samples = atoi(get_data_str(line).c_str()); | |
262 | + else if(token == "lines") | |
263 | + lines = atoi(get_data_str(line).c_str()); | |
264 | + else if(token == "bands") | |
265 | + bands = atoi(get_data_str(line).c_str()); | |
266 | + else if(token == "header offset") | |
267 | + header_offset = atoi(get_data_str(line).c_str()); | |
268 | + else if(token == "file type") | |
269 | + file_type = get_data_str(line); | |
270 | + else if(token == "data type") | |
271 | + data_type = (dataType)atoi(get_data_str(line).c_str()); | |
272 | + else if(token == "interleave") | |
273 | + { | |
274 | + std::string interleave_str = get_data_str(line); | |
275 | + if(interleave_str == "bip") | |
276 | + interleave = BIP; | |
277 | + else if(interleave_str == "bil") | |
278 | + interleave = BIL; | |
279 | + else if(interleave_str == "bsq") | |
280 | + interleave = BSQ; | |
281 | + } | |
282 | + else if(token == "sensor type") | |
283 | + sensor_type = get_data_str(line); | |
284 | + else if(token == "byte order") | |
285 | + byte_order = (endianType)atoi(get_data_str(line).c_str()); | |
286 | + else if(token == "x start") | |
287 | + x_start = atof(get_data_str(line).c_str()); | |
288 | + else if(token == "y start") | |
289 | + y_start = atof(get_data_str(line).c_str()); | |
290 | + else if(token == "wavelength units") | |
291 | + wavelength_units = get_data_str(line); | |
292 | + | |
293 | + //get the next line | |
294 | + getline(file, line); | |
295 | + } | |
296 | + | |
297 | + //make sure the number of bands matches the number of wavelengths | |
298 | + unsigned int wavelengths = wavelength.size(); | |
299 | + if(bands != wavelengths) | |
300 | + { | |
301 | + std::cout<<"ENVI Header Error -- Number of wavelengths doesn't match the number of bands. Bands = "<<bands<<", Wavelengths = "<<wavelength.size()<<std::endl; | |
302 | + exit(1); | |
303 | + } | |
304 | + | |
305 | + //close the file | |
306 | + file.close(); | |
307 | + | |
308 | + //set the file name | |
309 | + name = filename; | |
310 | + | |
311 | + return true; | |
279 | 312 | } |
280 | 313 | |
281 | - //FUNCTIONS THAT MAY BE DISALLOWED IN A LOCKED FILE | |
282 | - void setSamples(unsigned int samples) | |
314 | + void save(std::string filename) | |
283 | 315 | { |
284 | - EnviHeader newHeader = header; | |
285 | - newHeader.samples = samples; | |
286 | - set_header(newHeader); | |
316 | + //open a file | |
317 | + std::ofstream outfile(filename.c_str()); | |
318 | + | |
319 | + //write the ENVI type identifier | |
320 | + outfile<<"ENVI"<<std::endl; | |
321 | + | |
322 | + //output all of the data | |
323 | + outfile<<"description = {"<<std::endl; | |
324 | + outfile<<" "<<description<<"}"<<std::endl; | |
325 | + | |
326 | + outfile<<"samples = "<<samples<<std::endl; | |
327 | + outfile<<"lines = "<<lines<<std::endl; | |
328 | + outfile<<"bands = "<<bands<<std::endl; | |
329 | + outfile<<"header offset = "<<header_offset<<std::endl; | |
330 | + outfile<<"file type = "<<file_type<<std::endl; | |
331 | + outfile<<"data type = "<<data_type<<std::endl; | |
332 | + outfile<<"interleave = "; | |
333 | + if(interleave == BIP) | |
334 | + outfile<<"bip"; | |
335 | + if(interleave == BIL) | |
336 | + outfile<<"bil"; | |
337 | + if(interleave == BSQ) | |
338 | + outfile<<"bsq"; | |
339 | + outfile<<std::endl; | |
340 | + outfile<<"sensor type = "<<sensor_type<<std::endl; | |
341 | + outfile<<"byte order = "<<byte_order<<std::endl; | |
342 | + outfile<<"x start = "<<x_start<<std::endl; | |
343 | + outfile<<"y start = "<<y_start<<std::endl; | |
344 | + outfile<<"wavelength units = "<<wavelength_units<<std::endl; | |
345 | + outfile<<"z plot titles = {"; | |
346 | + outfile<<z_plot_titles[0]<<", "<<z_plot_titles[1]<<"}"<<std::endl; | |
347 | + outfile<<"pixel size = {"<<pixel_size[0]<<", "<<pixel_size[1]<<", units=Meters}"<<std::endl; | |
348 | + if(band_names.size() > 0) | |
349 | + { | |
350 | + outfile<<"band names = {"<<std::endl; | |
351 | + for(unsigned int i=0; i<band_names.size(); i++) | |
352 | + { | |
353 | + outfile<<band_names[i]; | |
354 | + if(i < band_names.size() - 1) | |
355 | + outfile<<", "; | |
356 | + } | |
357 | + outfile<<"}"<<std::endl; | |
358 | + } | |
359 | + outfile<<"wavelength = {"<<std::endl; | |
360 | + for(unsigned int i=0; i<wavelength.size()-1; i++) | |
361 | + outfile<<wavelength[i]<<", "; | |
362 | + outfile<<wavelength.back()<<"}"<<std::endl; | |
363 | + | |
364 | + outfile.close(); | |
287 | 365 | } |
288 | 366 | |
289 | - void setLines(unsigned int lines) | |
290 | - { | |
291 | - EnviHeader newHeader = header; | |
292 | - newHeader.lines = lines; | |
293 | - set_header(newHeader); | |
294 | - } | |
295 | - | |
296 | - unsigned int lines() {return header.lines;} | |
297 | - unsigned int samples() {return header.samples;} | |
298 | - unsigned int bands() {return header.bands;} | |
299 | - double wavelength(unsigned int b) {return header.wavelength[b];} | |
300 | - | |
301 | - void setBands(unsigned int bands) | |
367 | + void save() | |
302 | 368 | { |
303 | - EnviHeader newHeader = header; | |
304 | - newHeader.bands = bands; | |
305 | - set_header(newHeader); | |
306 | - } | |
307 | - | |
308 | - unsigned int getElementSize() | |
309 | - { | |
310 | - //return the size of each element in bytes | |
311 | - return header.valsize(); | |
312 | - } | |
313 | - unsigned int getBandSize() | |
314 | - { | |
315 | - //returns the size of a band in bytes | |
316 | - return header.valsize() * header.lines * header.samples; | |
317 | - } | |
318 | - | |
319 | - Envi::dataType getDataType() | |
320 | - { | |
321 | - return header.data_type; | |
322 | - } | |
323 | - | |
324 | - //DATA MANIPULATION | |
325 | - void addBand(void* band, unsigned int samples, unsigned int lines, double wavelength, std::string band_name ="") | |
326 | - { | |
327 | - //add a band to the file | |
328 | - | |
329 | - EnviHeader newHeader = header; | |
330 | - newHeader.bands++; | |
331 | - newHeader.samples = samples; | |
332 | - newHeader.lines = lines; | |
333 | - newHeader.wavelength.push_back(wavelength); | |
334 | - if(band_name != "") | |
335 | - newHeader.band_names.push_back(band_name); | |
336 | - set_header(newHeader); | |
337 | - | |
338 | - //copy the data to the file | |
339 | - fwrite(band, header.valsize(), samples * lines, data); | |
340 | - | |
341 | - //since data was written, the file must be locked | |
342 | - locked = true; | |
343 | - } | |
344 | - | |
345 | - //DATA RETRIEVAL | |
346 | - void getBand(void* ptr, unsigned int n) | |
347 | - { | |
348 | - //reads the n'th band and writes it to the given pointer | |
349 | - // memory has been assumed to be allocated | |
350 | - | |
351 | - if(n > header.wavelength.size()) | |
352 | - { | |
353 | - std::cout<<"ENVI Error: Invalid band number."<<std::endl; | |
354 | - return; | |
355 | - } | |
356 | - | |
357 | - | |
358 | - if(header.interleave == Envi::BSQ) | |
359 | - { | |
360 | - | |
361 | - //compute the position of the desired band (relative to the beginning of the file) | |
362 | - int bandpos = header.header_offset + getBandSize() * n; | |
363 | - | |
364 | - //seek to that position | |
365 | - int seek_result = fseek(data, bandpos, SEEK_SET); | |
366 | - if(seek_result) | |
367 | - { | |
368 | - std::cout<<"ENVI Error: Error seeking through data file."<<std::endl; | |
369 | - return; | |
370 | - } | |
371 | - | |
372 | - //read the band data from the file | |
373 | - size_t n_read = fread(ptr, getElementSize(), header.samples * header.lines, data); | |
374 | - if(n_read != header.samples * header.lines) | |
375 | - { | |
376 | - std::cout<<"ENVI Error -- Error reading band data: "<<n_read<<"/"<<samples() * lines()<<" pixels read"<<std::endl; | |
377 | - exit(1); | |
378 | - } | |
379 | - | |
380 | - return; | |
381 | - } | |
382 | - else | |
383 | - { | |
384 | - std::cout<<"ENVI Error: Only BSQ operations are currently supported."<<std::endl; | |
385 | - return; | |
386 | - } | |
387 | - } | |
388 | - | |
389 | - void getWavelength(void* band, double wavelength) | |
390 | - { | |
391 | - //this function retrieves a band via interpolation of wavelength values | |
392 | - | |
393 | - //get the low and high band numbers | |
394 | - unsigned int low, high; | |
395 | - get_surrounding_bands(wavelength, low, high); | |
396 | - //std::cout<<"High: "<<high<<" Low: "<<low<<std::endl; | |
397 | - | |
398 | - //calculate the position between the two bands | |
399 | - double a; | |
400 | - if(low == high) | |
401 | - a = 1.0; | |
402 | - else | |
403 | - a = (wavelength - header.wavelength[low]) / (header.wavelength[high] - header.wavelength[low]); | |
404 | - | |
405 | - //read both bands | |
406 | - void* A; | |
407 | - A = malloc( getBandSize() ); | |
408 | - getBand(A, low); | |
409 | - | |
410 | - void* B; | |
411 | - B = malloc( getBandSize() ); | |
412 | - getBand(B, high); | |
413 | - | |
414 | - //interpolate between the bands | |
415 | - interpolate_band(band, A, B, a); | |
416 | - | |
417 | - //free the surrounding bands | |
418 | - free(A); | |
419 | - free(B); | |
420 | - } | |
421 | - | |
422 | - //close file | |
423 | - void close() | |
424 | - { | |
425 | - //std::cout<<"ENVI File Mode: "<<mode<<std::endl; | |
426 | - | |
427 | - //close the data stream | |
428 | - fclose(data); | |
429 | - | |
430 | - //close the header | |
431 | - if(mode != "r") | |
432 | - header.save(); | |
369 | + //std::cout<<"ENVI Header Name: "<<name<<std::endl; | |
370 | + save(name); | |
433 | 371 | } |
434 | 372 | |
435 | -}; //end EnviFile | |
436 | - | |
373 | + //returns the size of a single value (in bytes) | |
374 | + unsigned int valsize() | |
375 | + { | |
376 | + switch(data_type) | |
377 | + { | |
378 | + case int8: //1 = 8-bit byte | |
379 | + return 1; | |
380 | + case int16: //16-bit signed integer | |
381 | + case uint16: //16-bit unsigned integer | |
382 | + return 2; | |
383 | + case int32: //32-bit signed long integer | |
384 | + case float32: //32-bit floating point | |
385 | + case uint32: //32-bit unsigned long integer | |
386 | + return 4; | |
387 | + case float64: //64-bit double-precision floating point | |
388 | + case complex32: //32-bit complex value | |
389 | + case int64: //64-bit signed long integer | |
390 | + case uint64: //64-bit unsigned long integer | |
391 | + return 8; | |
392 | + case complex64: //64-bit complex value | |
393 | + return 16; | |
394 | + default: | |
395 | + return 0; | |
396 | + } | |
437 | 397 | |
438 | -#endif | |
398 | + } | |
399 | +}; //end EnviHeader | |
400 | +} | |
401 | +#endif | ... | ... |
math/complexfield.cuh
... | ... | @@ -55,7 +55,7 @@ public: |
55 | 55 | int index; //result of the max operation |
56 | 56 | rts::complex<T> result; |
57 | 57 | |
58 | - if(sizeof(T) == 4) | |
58 | + if(sizeof(T) == 8) | |
59 | 59 | stat = cublasIcamax(handle, L, (const cuComplex*)X[n], 1, &index); |
60 | 60 | else |
61 | 61 | stat = cublasIzamax(handle, L, (const cuDoubleComplex*)X[n], 1, &index); |
... | ... | @@ -114,7 +114,7 @@ public: |
114 | 114 | |
115 | 115 | void toImage(std::string filename, attribute type = magnitude, unsigned int n=0){ |
116 | 116 | |
117 | - realfield<T, 1, true> rf(R[0], R[1]); | |
117 | + field<T, 1> rf(R[0], R[1]); | |
118 | 118 | |
119 | 119 | //get cuda parameters |
120 | 120 | dim3 blocks, grids; |
... | ... | @@ -122,7 +122,7 @@ public: |
122 | 122 | |
123 | 123 | if(type == magnitude){ |
124 | 124 | gpu_complexfield_mag <<<grids, blocks>>> (rf.ptr(), X[n], R[0], R[1]); |
125 | - rf.toImages(filename, 0); | |
125 | + rf.toImage(filename, n, true); | |
126 | 126 | } |
127 | 127 | |
128 | 128 | } |
... | ... | @@ -134,4 +134,4 @@ public: |
134 | 134 | } //end namespace rts |
135 | 135 | |
136 | 136 | |
137 | -#endif | |
138 | 137 | \ No newline at end of file |
138 | +#endif | ... | ... |
math/field.cuh
... | ... | @@ -5,10 +5,14 @@ |
5 | 5 | #include <string> |
6 | 6 | #include <sstream> |
7 | 7 | |
8 | +#include "cublas_v2.h" | |
9 | +#include <cuda_runtime.h> | |
10 | + | |
8 | 11 | #include "../math/rect.h" |
9 | 12 | #include "../cuda/threads.h" |
10 | 13 | #include "../cuda/error.h" |
11 | 14 | #include "../cuda/devices.h" |
15 | +#include "../visualization/colormap.h" | |
12 | 16 | |
13 | 17 | |
14 | 18 | namespace rts{ |
... | ... | @@ -86,6 +90,41 @@ protected: |
86 | 90 | grids = dim3((R[0] + SQRT_BLOCK -1)/SQRT_BLOCK, (R[1] + SQRT_BLOCK - 1)/SQRT_BLOCK); |
87 | 91 | } |
88 | 92 | |
93 | + //find the maximum value of component n | |
94 | + T find_max(unsigned int n){ | |
95 | + cublasStatus_t stat; | |
96 | + cublasHandle_t handle; | |
97 | + | |
98 | + //create a CUBLAS handle | |
99 | + stat = cublasCreate(&handle); | |
100 | + if(stat != CUBLAS_STATUS_SUCCESS){ | |
101 | + std::cout<<"CUBLAS Error: initialization failed"<<std::endl; | |
102 | + exit(1); | |
103 | + } | |
104 | + | |
105 | + int L = R[0] * R[1]; //compute the number of discrete points in a slice | |
106 | + int index; //result of the max operation | |
107 | + T result; | |
108 | + | |
109 | + if(sizeof(T) == 4) | |
110 | + stat = cublasIsamax(handle, L, (const float*)X[n], 1, &index); | |
111 | + else | |
112 | + stat = cublasIdamax(handle, L, (const double*)X[n], 1, &index); | |
113 | + | |
114 | + index -= 1; //adjust for 1-based indexing | |
115 | + | |
116 | + //if there was a GPU error, terminate | |
117 | + if(stat != CUBLAS_STATUS_SUCCESS){ | |
118 | + std::cout<<"CUBLAS Error: failure finding maximum value."<<std::endl; | |
119 | + exit(1); | |
120 | + } | |
121 | + | |
122 | + //retrieve the maximum value for this slice and store it in the maxVal array | |
123 | + std::cout<<X[n]<<std::endl; | |
124 | + HANDLE_ERROR(cudaMemcpy(&result, X[n] + index, sizeof(T), cudaMemcpyDeviceToHost)); | |
125 | + return result; | |
126 | + } | |
127 | + | |
89 | 128 | public: |
90 | 129 | |
91 | 130 | //returns a list of file names given an input string with wild cards |
... | ... | @@ -286,10 +325,20 @@ public: |
286 | 325 | rts::gpu_field_crop <<<dimGrid, dimBlock>>> (result.X[n], X[n], R[0], R[1], width, height); |
287 | 326 | |
288 | 327 | return result; |
328 | + } | |
329 | + | |
330 | + //save an image representing component n | |
331 | + void toImage(std::string filename, unsigned int n = 0, | |
332 | + bool positive = false, rts::colormapType cmap = rts::cmBrewer){ | |
333 | + T max_val = find_max(n); //find the maximum value | |
289 | 334 | |
335 | + if(positive) //if the field is positive, use the range [0 max_val] | |
336 | + rts::gpu2image<T>(X[n], filename, R[0], R[1], 0, max_val, cmap); | |
337 | + else | |
338 | + rts::gpu2image<T>(X[n], filename, R[0], R[1], -max_val, max_val, cmap); | |
290 | 339 | } |
291 | 340 | |
292 | 341 | }; |
293 | 342 | |
294 | 343 | } //end namespace rts |
295 | -#endif | |
296 | 344 | \ No newline at end of file |
345 | +#endif | ... | ... |
math/function.h
... | ... | @@ -2,7 +2,6 @@ |
2 | 2 | #define RTS_FUNCTION_H |
3 | 3 | |
4 | 4 | #include <string> |
5 | -#include <cmath> | |
6 | 5 | |
7 | 6 | namespace rts{ |
8 | 7 | |
... | ... | @@ -59,7 +58,7 @@ protected: |
59 | 58 | |
60 | 59 | public: |
61 | 60 | function(){ |
62 | - range[0] = range[1] = NAN; | |
61 | + range[0] = range[1] = 0; | |
63 | 62 | bounding[0] = bounding[1] = 0; |
64 | 63 | } |
65 | 64 | |
... | ... | @@ -182,7 +181,8 @@ public: |
182 | 181 | function<Tx, Ty> & operator= (const Ty & rhs){ |
183 | 182 | X.clear(); |
184 | 183 | Y.clear(); |
185 | - if(rhs != 0) //if the RHS is zero, just clear, otherwise add one value of RHS | |
184 | + bounding[0] = bounding[1] = rhs; //set the boundary values to rhs | |
185 | + if(rhs != 0) //if the RHS is zero, just clear, otherwise add one value of RHS | |
186 | 186 | insert(0, rhs); |
187 | 187 | |
188 | 188 | return *this; | ... | ... |
optics/mirst-1d.cuh
1 | 1 | #include "../optics/material.h" |
2 | 2 | #include "../math/complexfield.cuh" |
3 | 3 | #include "../math/constants.h" |
4 | +#include "../envi/bil.h" | |
4 | 5 | |
5 | 6 | #include "cufft.h" |
6 | 7 | |
... | ... | @@ -163,6 +164,8 @@ private: |
163 | 164 | |
164 | 165 | T NA[2]; //numerical aperature (central obscuration and outer diameter) |
165 | 166 | |
167 | + function<T, T> source_profile; //profile (spectrum) of the source (expressed in inverse centimeters) | |
168 | + | |
166 | 169 | complexfield<T, 1> scratch; //scratch GPU memory used to build samples, transforms, etc. |
167 | 170 | |
168 | 171 | void fft(int direction = CUFFT_FORWARD){ |
... | ... | @@ -251,7 +254,7 @@ private: |
251 | 254 | HANDLE_ERROR(cudaMemcpy( gpuRi + inu, &ri, sizeof(complex<T>), cudaMemcpyHostToDevice )); |
252 | 255 | |
253 | 256 | //save the source profile to the GPU |
254 | - source = 1; | |
257 | + source = source_profile(10000 / lambdas[inu]); | |
255 | 258 | HANDLE_ERROR(cudaMemcpy( gpuSrc + inu, &source, sizeof(T), cudaMemcpyHostToDevice )); |
256 | 259 | |
257 | 260 | } |
... | ... | @@ -311,6 +314,11 @@ private: |
311 | 314 | scratch = scratch.crop(Z, S); |
312 | 315 | } |
313 | 316 | |
317 | + //save the scratch field as a binary file | |
318 | + void to_binary(std::string filename){ | |
319 | + | |
320 | + } | |
321 | + | |
314 | 322 | |
315 | 323 | public: |
316 | 324 | |
... | ... | @@ -322,6 +330,7 @@ public: |
322 | 330 | NA[0] = 0; |
323 | 331 | NA[1] = 0.8; |
324 | 332 | S = 0; |
333 | + source_profile = 1; | |
325 | 334 | } |
326 | 335 | |
327 | 336 | //add a layer, thickness = microns |
... | ... | @@ -334,6 +343,11 @@ public: |
334 | 343 | add_layer(material<T>(filename), thickness); |
335 | 344 | } |
336 | 345 | |
346 | + //adds a profile spectrum for the light source | |
347 | + void set_source(std::string filename){ | |
348 | + source_profile.load(filename); | |
349 | + } | |
350 | + | |
337 | 351 | //adds a block of wavenumbers (cm^-1) to the simulation parameters |
338 | 352 | void add_wavenumbers(unsigned int start, unsigned int stop, unsigned int step){ |
339 | 353 | unsigned int nu = start; |
... | ... | @@ -368,6 +382,10 @@ public: |
368 | 382 | na(0, out); |
369 | 383 | } |
370 | 384 | |
385 | + rts::function<T, T> get_source(){ | |
386 | + return source_profile; | |
387 | + } | |
388 | + | |
371 | 389 | void save_sample(std::string filename){ |
372 | 390 | //create a sample and save the magnitude as an image |
373 | 391 | build_sample(); |
... | ... | @@ -375,7 +393,7 @@ public: |
375 | 393 | scratch.toImage(filename, rts::complexfield<T, 1>::magnitude); |
376 | 394 | } |
377 | 395 | |
378 | - void save_mirst(std::string filename){ | |
396 | + void save_mirst(std::string filename, bool binary = true){ | |
379 | 397 | //apply the MIRST filter to a sample and save the image |
380 | 398 | |
381 | 399 | //build the sample in the Fourier domain |
... | ... | @@ -390,7 +408,10 @@ public: |
390 | 408 | crop(); |
391 | 409 | |
392 | 410 | //save the image |
393 | - scratch.toImage(filename, rts::complexfield<T, 1>::magnitude); | |
411 | + if(binary) | |
412 | + to_binary(filename); | |
413 | + else | |
414 | + scratch.toImage(filename, rts::complexfield<T, 1>::magnitude); | |
394 | 415 | } |
395 | 416 | |
396 | 417 | |
... | ... | @@ -411,6 +432,9 @@ public: |
411 | 432 | for(unsigned int l=0; l<layers.size(); l++) |
412 | 433 | ss<<"layer "<<l<<": "<<layers[l]<<" um"<<"---------"<<std::endl<<matlist[l].str()<<std::endl; |
413 | 434 | |
435 | + ss<<"source profile-----------"<<std::endl; | |
436 | + ss<<get_source().str()<<std::endl; | |
437 | + | |
414 | 438 | return ss.str(); |
415 | 439 | |
416 | 440 | ... | ... |