Blame view

stim/image/image.h 10.8 KB
d32a1854   David Mayerich   added framework f...
1
2
  #ifndef STIM_IMAGE_H
  #define STIM_IMAGE_H
fbf0ab02   David Mayerich   added support for...
3
  
c8c976a9   David Mayerich   replaced CImg wit...
4
5
  #include <opencv2/core/core.hpp>
  #include <opencv2/highgui/highgui.hpp>
912d9073   David Mayerich   added vector.h to...
6
  #include <vector>
d32a1854   David Mayerich   added framework f...
7
  #include <iostream>
a2bf1d08   David Mayerich   general bug fixes...
8
  #include <limits>
6d30a707   Tianshu Cheng   add cuda/array_ad...
9
  
7b3948ab   David Mayerich   added support for...
10
  namespace stim{
c8c976a9   David Mayerich   replaced CImg wit...
11
12
  /// This static class provides the STIM interface for loading, saving, and storing 2D images.
  /// Data is stored in an interleaved (BIP) format (default for saving and loading is RGB).
d32a1854   David Mayerich   added framework f...
13
14
15
16
17
18
  
  //currently this interface uses CImg
  //	T = data type (usually unsigned char)
  template <class T>
  class image{
  
c8c976a9   David Mayerich   replaced CImg wit...
19
20
21
22
  	//cimg_library::CImg<T> img;
  	T* img;										//pointer to the image data (assumes RGB for loading/saving)
  	size_t R[3];
  
a2bf1d08   David Mayerich   general bug fixes...
23
24
25
  	size_t X() const { return R[1]; }
  	size_t Y() const { return R[2]; }
  	size_t C() const { return R[0]; }
c8c976a9   David Mayerich   replaced CImg wit...
26
27
28
29
30
31
32
33
34
  
  	void init(){								//initializes all variables, assumes no memory is allocated
  		memset(R, 0, sizeof(size_t) * 3);		//set the resolution and number of channels to zero
  		img = NULL;
  	}
  
  	void unalloc(){								//frees any resources associated with the image
  		if(img)	free(img);						//if memory has been allocated, free it
  	}
1a224b6a   David Mayerich   fixed linux compa...
35
  
c8c976a9   David Mayerich   replaced CImg wit...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  
  	void clear(){								//clears all image data
  		unalloc();								//unallocate previous memory
  		init();									//re-initialize the variables
  	}
  
  	void allocate(){
  		img = (T*) malloc( sizeof(T) * R[0] * R[1] * R[2] );	//allocate memory
  	}
  
  	void allocate(size_t x, size_t y, size_t c){	//allocate memory based on the resolution
  		R[0] = c; R[1] = x; R[2] = y;				//set the resolution
  		allocate();									//allocate memory
  	}
  
  	size_t bytes(){ return size() * sizeof(T); }
  
  	size_t idx(size_t x, size_t y, size_t c = 0){
  		return y * C() * X() + x * C() + c;
  	}
1a224b6a   David Mayerich   fixed linux compa...
56
  
c8c976a9   David Mayerich   replaced CImg wit...
57
58
59
60
61
62
63
64
65
  
  	int cv_type(){
  		if(std::is_same<T, unsigned char>::value)	return CV_MAKETYPE(CV_8U, (int)C());
  		if(std::is_same<T, char>::value)			return CV_MAKETYPE(CV_8S, (int)C());
  		if(std::is_same<T, unsigned short>::value)	return CV_MAKETYPE(CV_16U, (int)C());
  		if(std::is_same<T, short>::value)			return CV_MAKETYPE(CV_16S, (int)C());
  		if(std::is_same<T, int>::value)				return CV_MAKETYPE(CV_32S, (int)C());
  		if(std::is_same<T, float>::value)			return CV_MAKETYPE(CV_32F, (int)C());
  		if(std::is_same<T, double>::value)			return CV_MAKETYPE(CV_64F, (int)C());
1a224b6a   David Mayerich   fixed linux compa...
66
  
c8c976a9   David Mayerich   replaced CImg wit...
67
68
69
70
  		std::cout<<"ERROR in stim::image::cv_type - no valid data type found"<<std::endl;
  		exit(1);
  	}
  
a2bf1d08   David Mayerich   general bug fixes...
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  	/// Returns the value for "white" based on the dynamic range (assumes white is 1.0 for floating point images)
  	T white(){
  		if(std::is_same<T, unsigned char>::value)		return UCHAR_MAX;
  		if(std::is_same<T, unsigned short>::value)		return SHRT_MAX;
  		if(std::is_same<T, unsigned>::value)			return UINT_MAX;
  		if(std::is_same<T, unsigned long>::value)		return ULONG_MAX;
  		if(std::is_same<T, unsigned long long>::value)	return ULLONG_MAX;
  		if(std::is_same<T, float>::value)				return 1.0f;
  		if(std::is_same<T, double>::value)				return 1.0;
  
  		std::cout<<"ERROR in stim::image::white - no white value known for this data type"<<std::endl;
  
  	}
  
d32a1854   David Mayerich   added framework f...
85
86
87
  
  public:
  
a2bf1d08   David Mayerich   general bug fixes...
88
  	/// Default constructor - creates an empty image object
c8c976a9   David Mayerich   replaced CImg wit...
89
  	image(){ init(); }							//initialize all variables to zero, don't allocate any memory
7b3948ab   David Mayerich   added support for...
90
  
a2bf1d08   David Mayerich   general bug fixes...
91
  	/// Constructor with a filename - loads the specified file
c8c976a9   David Mayerich   replaced CImg wit...
92
93
  	image(std::string filename){				//constructor initialize the image with an image file
  		load(filename);
7b3948ab   David Mayerich   added support for...
94
95
  	}
  
c8c976a9   David Mayerich   replaced CImg wit...
96
  	/// Create a new image from scratch given a number of samples and channels
1a224b6a   David Mayerich   fixed linux compa...
97
  	image(size_t x, size_t y = 1, size_t c = 1){
a2bf1d08   David Mayerich   general bug fixes...
98
  		allocate(x, y, c);
c8c976a9   David Mayerich   replaced CImg wit...
99
  	}
f186dbda   Tianshu Cheng   header file for b...
100
  
a2bf1d08   David Mayerich   general bug fixes...
101
  	/// Create a new image with the data given in 'data'
c8c976a9   David Mayerich   replaced CImg wit...
102
103
104
  	image(T* data, size_t x, size_t y, size_t c = 1){
  		allocate(x, y, c);
  		memcpy(img, data, bytes());
41acaf5d   David Mayerich   added a CUDA Gaus...
105
106
  	}
  
a2bf1d08   David Mayerich   general bug fixes...
107
108
109
110
111
112
113
114
  	/// Copy constructor - duplicates an image object
  	image(const stim::image<T>& I){
  		allocate(I.X(), I.Y(), I.C());
  		//allocate(I.R[1], I.R[2], I.R[0]);
  		memcpy(img, I.img, bytes());
  	}
  
  	/// Destructor - clear memory
ae05c3e4   Pavel Govyadinov   Found an error wi...
115
116
117
118
119
120
121
  //	~image(){
  //		free(img);
  //	}
  
  	void clear_exp(){								//clears all image data
  		unalloc();		
  
a2bf1d08   David Mayerich   general bug fixes...
122
123
124
125
126
127
128
129
130
131
  	}
  
  	stim::image<T> operator=(const stim::image<T>& I){
  		if(&I == this)									//handle self-assignment
  			return *this;
  		allocate(I.X(), I.Y(), I.C());
  		memcpy(img, I.img, bytes());
  		return *this;
  	}
  
c8c976a9   David Mayerich   replaced CImg wit...
132
  	/// Load an image from a file
d32a1854   David Mayerich   added framework f...
133
  	void load(std::string filename){
c8c976a9   David Mayerich   replaced CImg wit...
134
135
  
  		cv::Mat cvImage = cv::imread(filename, CV_LOAD_IMAGE_UNCHANGED);	//use OpenCV to open the image file
a2bf1d08   David Mayerich   general bug fixes...
136
137
138
139
  		if(!cvImage.data){
  			std::cout<<"ERROR stim::image::load() - unable to find image "<<filename<<std::endl;
  			exit(1);
  		}
c8c976a9   David Mayerich   replaced CImg wit...
140
  		allocate(cvImage.cols, cvImage.rows, cvImage.channels());			//allocate space for the image
ae05c3e4   Pavel Govyadinov   Found an error wi...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  		T* cv_ptr = (T*) cvImage.data;
  		if(C() == 1)
  		{
  	//if this is a single-color image, just copy the data
  /*
  			THIS DOES NOT WORK PROPERLY.
  			YOU CANNOT DO MEMCPY LIKE THIS BECAUSE IT CORRUPTS THE POINTREF MECHANIC IN OPENCV AND NEVER ACTUALLY CREATES A DEEP COPY BUT TAKES UP THE MEMORY.
  			TO TEST run and compile pseudocode
  			stim::image<unsigned char> I;
  			for(int i = 0; i < file_list.size(); i++)
  				I.load(file_list[i].srt())
  
  			
  			You will run out of memory.
  */			 
  			memcpy(img, cv_ptr, bytes());     
  //			img = cvImage.data;
  		}
a2bf1d08   David Mayerich   general bug fixes...
159
160
  		if(C() == 3)														//if this is a 3-color image, OpenCV uses BGR interleaving
  			set_interleaved_bgr(cv_ptr, X(), Y());
ae05c3e4   Pavel Govyadinov   Found an error wi...
161
162
  
  //		cvImage.deallocate();
d32a1854   David Mayerich   added framework f...
163
164
165
166
  	}
  
  	//save a file
  	void save(std::string filename){
c8c976a9   David Mayerich   replaced CImg wit...
167
168
  		//OpenCV uses an interleaved format, so convert first and then output
  		T* buffer = (T*) malloc(bytes());
a2bf1d08   David Mayerich   general bug fixes...
169
170
171
172
  
  		if(C() == 1)
  			memcpy(buffer, img, bytes());
  		else if(C() == 3)
9b766f1f   Pavel Govyadinov   completed merge f...
173
  			get_interleaved_bgr(buffer);
c8c976a9   David Mayerich   replaced CImg wit...
174
175
  		cv::Mat cvImage((int)Y(), (int)X(), cv_type(), buffer);
  		cv::imwrite(filename, cvImage);
ae05c3e4   Pavel Govyadinov   Found an error wi...
176
177
  		cvImage.release();
  		free(buffer);
d32a1854   David Mayerich   added framework f...
178
179
  	}
  
8b7be670   David Mayerich   implemented savin...
180
  	//create an image from an interleaved buffer
ae05c3e4   Pavel Govyadinov   Found an error wi...
181
182
  	void set_interleaved_rgb(T* buffer, size_t width, size_t height, size_t channels = 3){
  		allocate(width, height, channels);
c8c976a9   David Mayerich   replaced CImg wit...
183
184
  		memcpy(img, buffer, bytes());
  	}
fbf0ab02   David Mayerich   added support for...
185
  
ae05c3e4   Pavel Govyadinov   Found an error wi...
186
187
  	void set_interleaved_bgr(T* buffer, size_t width, size_t height, size_t channels = 3){
  		allocate(width, height, channels);
c8c976a9   David Mayerich   replaced CImg wit...
188
189
190
191
192
  		for(size_t c = 0; c < C(); c++){								//copy directly
  			for(size_t y = 0; y < Y(); y++){
  				for(size_t x = 0; x < X(); x++){
  					img[idx(x, y, c)] = buffer[y * X() * C() + x * C() + (2-c)];
  				}
fbf0ab02   David Mayerich   added support for...
193
194
  			}
  		}
8b7be670   David Mayerich   implemented savin...
195
196
  	}
  
9b766f1f   Pavel Govyadinov   completed merge f...
197
  	void get_interleaved_bgr(T* data){
8b7be670   David Mayerich   implemented savin...
198
199
  
  		//for each channel
c8c976a9   David Mayerich   replaced CImg wit...
200
201
202
203
204
205
206
  		for(size_t y = 0; y < Y(); y++){
  			for(size_t x = 0; x < X(); x++){
  				for(size_t c = 0; c < C(); c++){
  					data[y * X() * C() + x * C() + (2-c)] = img[idx(x, y, c)];
  				}
  			}
  		}
8b7be670   David Mayerich   implemented savin...
207
208
  	}
  
9b766f1f   Pavel Govyadinov   completed merge f...
209
210
211
212
213
  	void get_interleaved_rgb(T* data){
  		memcpy(data, img, bytes());
  	}
  
  
9d3ba0b1   David Mayerich   added stim::hsi a...
214
  	image<T> channel(size_t c){
41acaf5d   David Mayerich   added a CUDA Gaus...
215
216
  
  		//create a new image
a2bf1d08   David Mayerich   general bug fixes...
217
  		image<T> r(X(), Y(), 1);
41acaf5d   David Mayerich   added a CUDA Gaus...
218
  
c8c976a9   David Mayerich   replaced CImg wit...
219
220
221
222
223
  		for(size_t x = 0; x < X(); x++){
  			for(size_t y = 0; y < Y(); y++){
  				r.img[r.idx(x, y, c)] = img[idx(x, y, c)];
  			}
  		}
41acaf5d   David Mayerich   added a CUDA Gaus...
224
  
c8c976a9   David Mayerich   replaced CImg wit...
225
  		return r;
41acaf5d   David Mayerich   added a CUDA Gaus...
226
227
228
  
  	}
  
c8c976a9   David Mayerich   replaced CImg wit...
229
230
  	T& operator()(size_t x, size_t y, size_t c = 0){
  		return img[idx(x, y, c)];
3d0b6243   David Mayerich   fixed hsiproc bug...
231
232
233
234
235
236
237
  	}
  
  	/// Set all elements in the image to a given scalar value
  
  	/// @param v is the value used to set all values in the image
  	image<T> operator=(T v){
  
c8c976a9   David Mayerich   replaced CImg wit...
238
  		size_t N = size();
3d0b6243   David Mayerich   fixed hsiproc bug...
239
  
c8c976a9   David Mayerich   replaced CImg wit...
240
241
  		for(size_t n = 0; n < N; n++)
  			img[n] = v;
3d0b6243   David Mayerich   fixed hsiproc bug...
242
243
244
245
246
  
  		return *this;
  
  	}
  
f186dbda   Tianshu Cheng   header file for b...
247
248
249
250
251
  	/// Copy the given data to the specified channel
  
  	/// @param c is the channel number that the data will be copied to
  	/// @param buffer is a pointer to the image to be copied to channel c
  
c8c976a9   David Mayerich   replaced CImg wit...
252
  	void set_channel(T* buffer, size_t c){
6d30a707   Tianshu Cheng   add cuda/array_ad...
253
  
c8c976a9   David Mayerich   replaced CImg wit...
254
255
256
257
258
259
  		size_t x, y;
  		for(y = 0; y < Y(); y++){
  			for(x = 0; x < X(); x++){
  				img[idx(x, y, c)] = buffer[c];
  			}
  		}
6d30a707   Tianshu Cheng   add cuda/array_ad...
260
261
  	}
  
9d3ba0b1   David Mayerich   added stim::hsi a...
262
  	size_t channels(){
c8c976a9   David Mayerich   replaced CImg wit...
263
  		return C();
8b7be670   David Mayerich   implemented savin...
264
265
  	}
  
9d3ba0b1   David Mayerich   added stim::hsi a...
266
  	size_t width(){
c8c976a9   David Mayerich   replaced CImg wit...
267
  		return X();
8b7be670   David Mayerich   implemented savin...
268
269
  	}
  
9d3ba0b1   David Mayerich   added stim::hsi a...
270
  	size_t height(){
c8c976a9   David Mayerich   replaced CImg wit...
271
  		return Y();
3d0b6243   David Mayerich   fixed hsiproc bug...
272
273
  	}
  
6d30a707   Tianshu Cheng   add cuda/array_ad...
274
  	T* data(){
c8c976a9   David Mayerich   replaced CImg wit...
275
  		return img;
6d30a707   Tianshu Cheng   add cuda/array_ad...
276
277
  	}
  
7b3948ab   David Mayerich   added support for...
278
  	//returns the size (number of values) of the image
c8c976a9   David Mayerich   replaced CImg wit...
279
  	size_t size(){ return C() * X() * Y(); }
d32a1854   David Mayerich   added framework f...
280
  
faef7718   David Mayerich   updates to stim::...
281
  	/// Returns the number of nonzero values
9d3ba0b1   David Mayerich   added stim::hsi a...
282
  	size_t nnz(){
faef7718   David Mayerich   updates to stim::...
283
  
c8c976a9   David Mayerich   replaced CImg wit...
284
  		size_t N = X() * Y() * C();
faef7718   David Mayerich   updates to stim::...
285
  
c8c976a9   David Mayerich   replaced CImg wit...
286
287
288
  		size_t nz = 0;
  		for(size_t n = 0; n < N; n++)
  			if(img[n] != 0) nz++;
faef7718   David Mayerich   updates to stim::...
289
  
c8c976a9   David Mayerich   replaced CImg wit...
290
  		return nz;	//return the number of nonzero pixels
faef7718   David Mayerich   updates to stim::...
291
292
293
294
  
  	}
  
  	//this function returns indices of pixels that have nonzero values
9d3ba0b1   David Mayerich   added stim::hsi a...
295
  	std::vector<size_t> sparse_idx(){
faef7718   David Mayerich   updates to stim::...
296
  
c8c976a9   David Mayerich   replaced CImg wit...
297
  		std::vector<size_t> s;				//allocate an array
faef7718   David Mayerich   updates to stim::...
298
299
  		s.resize(nnz());					//allocate space in the array
  
c8c976a9   David Mayerich   replaced CImg wit...
300
301
  		size_t N = size();
  		//size_t C = channels();
faef7718   David Mayerich   updates to stim::...
302
  
c8c976a9   David Mayerich   replaced CImg wit...
303
  		//T* ptr = img.data();				//get a pointer to the image data
faef7718   David Mayerich   updates to stim::...
304
  
9d3ba0b1   David Mayerich   added stim::hsi a...
305
  		size_t i = 0;
c8c976a9   David Mayerich   replaced CImg wit...
306
307
308
309
  		for(size_t n = 0; n < N; n++){
  			if(img[n] != 0){
  				s[i] = n;
  				i++;
faef7718   David Mayerich   updates to stim::...
310
311
312
313
314
  			}
  		}
  
  		return s;			//return the index list
  	}
6d30a707   Tianshu Cheng   add cuda/array_ad...
315
  
1a224b6a   David Mayerich   fixed linux compa...
316
  
6d30a707   Tianshu Cheng   add cuda/array_ad...
317
  	/// Returns the maximum pixel value in the image
96f9b10f   Laila Saadatifard   change the header...
318
  	T maxv(){
c8c976a9   David Mayerich   replaced CImg wit...
319
320
  		T max_val = img[0];				//initialize the maximum value to the first one
  		size_t N = size();	//get the number of pixels
6d30a707   Tianshu Cheng   add cuda/array_ad...
321
  
c8c976a9   David Mayerich   replaced CImg wit...
322
  		for (size_t n=0; n<N; n++){		//for every value
6d30a707   Tianshu Cheng   add cuda/array_ad...
323
  
c8c976a9   David Mayerich   replaced CImg wit...
324
325
  			if (img[n] > max_val){			//if the value is higher than the current max
  				max_val = img[n];
1a224b6a   David Mayerich   fixed linux compa...
326
  			}
6d30a707   Tianshu Cheng   add cuda/array_ad...
327
328
  		}
  
1a224b6a   David Mayerich   fixed linux compa...
329
  		return max_val;
6d30a707   Tianshu Cheng   add cuda/array_ad...
330
  	}
d32a1854   David Mayerich   added framework f...
331
  
c8c976a9   David Mayerich   replaced CImg wit...
332
  	/// Returns the maximum pixel value in the image
96f9b10f   Laila Saadatifard   change the header...
333
  	T minv(){
c8c976a9   David Mayerich   replaced CImg wit...
334
335
  		T min_val = img[0];				//initialize the maximum value to the first one
  		size_t N = size();	//get the number of pixels
6d30a707   Tianshu Cheng   add cuda/array_ad...
336
  
c8c976a9   David Mayerich   replaced CImg wit...
337
338
339
  		for (size_t n=0; n<N; n++){		//for every value
  			if (img[n] < min_val){			//if the value is higher than the current max
  				min_val = img[n];
1a224b6a   David Mayerich   fixed linux compa...
340
  			}
6d30a707   Tianshu Cheng   add cuda/array_ad...
341
342
  		}
  
1a224b6a   David Mayerich   fixed linux compa...
343
  		return min_val;
6d30a707   Tianshu Cheng   add cuda/array_ad...
344
345
  	}
  
a2bf1d08   David Mayerich   general bug fixes...
346
347
348
349
350
351
  	/// Invert an image by calculating I1 = alpha - I0, where alpha is the maximum image value
  	image<T> invert(T white_val){
  		size_t N = size();						//calculate the total number of values in the image
  		image<T> r(X(), Y(), C());				//allocate space for the resulting image
  		for(size_t n = 0; n < N; n++)
  			r.img[n] = white_val - img[n];		//perform the inversion
1a224b6a   David Mayerich   fixed linux compa...
352
  
a2bf1d08   David Mayerich   general bug fixes...
353
354
355
  		return r;								//return the inverted image
  	}
  
ae05c3e4   Pavel Govyadinov   Found an error wi...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
  	///crops the image from x1 to x0 and y1 to y0 and returns a new (smaller) image.
  	///Untested
  	image<T> crop(int x0, int x1, int y0, int y1)
  	{
  		
  		image<T> ret(x1-x0, y1-y0, C());
  		int newWidth = x1-x0;
  		int destidx, srcidx;
  //		for(int i = 0; i < (y1-y0); i++)
  		{
  			destidx = i*newWidth*C(); ///destination index one per each row
  			srcidx = ((i+(y0))*X()+x0)*C(); ///source index, one per each row.
  			ret.set_interleaved_rgb(img[srcidx], newWidth, y1-y0, C());
  			memcpy(&ret.img[destidx], &buffer[srcidx], sizeof(T)*newWidth*C());
  		}		
  	
  //		for(int i = 0; i < (x1 -x0); i++)
  //		{
  //			for(int j = 0; j < (y1 - y0); j++)
  //			{
  //				destidx = j*newWidth*C() + i;
  //				srcidx = ((i+(y0))*X() +x0)*C() + i;
  //				ret.img[destidx] = img[srcidx];
  //			}
  //		}
  		return ret;
  	}
  
6d30a707   Tianshu Cheng   add cuda/array_ad...
384
  	image<T> srgb2lab(){
c8c976a9   David Mayerich   replaced CImg wit...
385
386
  		std::cout<<"ERROR stim::image::srgb2lab - function has been broken, re-implement."<<std::endl;
  		exit(1);
6d30a707   Tianshu Cheng   add cuda/array_ad...
387
388
389
  	}
  
  	image<T> convolve2(image<T> mask){
1a224b6a   David Mayerich   fixed linux compa...
390
  
c8c976a9   David Mayerich   replaced CImg wit...
391
392
  		std::cout<<"ERROR stim::image::convolve2 - function has been broken, and shouldn't really be in here."<<std::endl;
  		exit(1);
6d30a707   Tianshu Cheng   add cuda/array_ad...
393
  	}
d32a1854   David Mayerich   added framework f...
394
395
  
  
f186dbda   Tianshu Cheng   header file for b...
396
  	image<T> rotate(float angle, float cx, float cy){
c8c976a9   David Mayerich   replaced CImg wit...
397
398
  		std::cout<<"ERROR stim::image::rotate - function has been broken, and shouldn't really be in here."<<std::endl;
  		exit(1);
f186dbda   Tianshu Cheng   header file for b...
399
  	}
945ee13c   Laila Saadatifard   the get_list func...
400
  
ae05c3e4   Pavel Govyadinov   Found an error wi...
401
  
945ee13c   Laila Saadatifard   the get_list func...
402
403
  	// leila's code for non_interleaving data in 3D
  	//create an data set from an interleaved buffer
9d3ba0b1   David Mayerich   added stim::hsi a...
404
  	void set_interleaved3(T* buffer, size_t width, size_t height, size_t depth, size_t channels = 3){
c8c976a9   David Mayerich   replaced CImg wit...
405
406
  		std::cout<<"ERROR stim::image::set_interleaved3 - stim::image no longer supports 3D images."<<std::endl;
  		exit(1);
945ee13c   Laila Saadatifard   the get_list func...
407
  	}
1a224b6a   David Mayerich   fixed linux compa...
408
  
d32a1854   David Mayerich   added framework f...
409
410
  };
  
7b3948ab   David Mayerich   added support for...
411
412
  };		//end namespace stim
  
d32a1854   David Mayerich   added framework f...
413
414
  
  #endif