Commit 27b826a8113f756e01a72ac5c643119f954e321a

Authored by David Mayerich
1 parent 6288483c

added a spherical harmonics visualization class, modified files to support it

stim/envi/binary.h
... ... @@ -225,6 +225,10 @@ public:
225 225 return true;
226 226 }
227 227  
  228 + /// Reads a plane given a coordinate along the 0-axis (YZ plane)
  229 +
  230 + /// @param p is a pointer to pre-allocated memory of size R[1] * R[2] * sizeof(T)
  231 + /// @param n is the 0-axis coordinate used to retrieve the plane
228 232 bool read_plane_0(T* p, unsigned int n){
229 233  
230 234 if (n >= R[0]){ //make sure the number is within the possible range
... ... @@ -247,6 +251,10 @@ public:
247 251  
248 252 }
249 253  
  254 + /// Reads a plane given a coordinate along the 1-axis (XZ plane)
  255 +
  256 + /// @param p is a pointer to pre-allocated memory of size R[0] * R[2] * sizeof(T)
  257 + /// @param n is the 1-axis coordinate used to retrieve the plane
250 258 bool read_plane_1(T* p, unsigned int n){
251 259  
252 260 unsigned int L = R[0] * sizeof(T); //caculate the number of bytes in a sample line
... ... @@ -268,10 +276,18 @@ public:
268 276 return true;
269 277 }
270 278  
  279 + /// Reads a plane given a coordinate along the 2-axis (XY plane)
  280 +
  281 + /// @param p is a pointer to pre-allocated memory of size R[0] * R[1] * sizeof(T)
  282 + /// @param n is the 2-axis coordinate used to retrieve the plane
271 283 bool read_plane_2(T* p, unsigned int n){
272 284 return read_page(p, n);
273 285 }
274 286  
  287 + /// Reads a single pixel, treating the entire data set as a linear array
  288 +
  289 + /// @param p is a pointer to pre-allocated memory of size sizeof(T)
  290 + /// @param i is the index to the pixel using linear indexing
275 291 bool read_pixel(T* p, unsigned int i){
276 292 if(i >= R[0] * R[1] * R[2]){
277 293 std::cout<<"ERROR read_pixel: n is out of range"<<std::endl;
... ... @@ -283,6 +299,12 @@ public:
283 299  
284 300 }
285 301  
  302 + /// Reads a single pixel, given an x, y, z coordinate
  303 +
  304 + /// @param p is a pointer to pre-allocated memory of size sizeof(T)
  305 + /// @param x is the x (0) axis coordinate
  306 + /// @param y is the y (1) axis coordinate
  307 + /// @param z is the z (2) axis coordinate
286 308 bool read_pixel(T* p, unsigned int x, unsigned int y, unsigned int z){
287 309  
288 310 if(x < 0 || x >= R[0] || y < 0 || y >= R[1] || z < 0 || z > R[2]){
... ... @@ -294,54 +316,6 @@ public:
294 316 return read_pixel(p, i);
295 317 }
296 318  
297   - //saves a hyperplane orthogonal to dimension d at intersection n
298   - /*bool read_plane(T * dest, unsigned int d, unsigned int n){
299   -
300   - //reset the file pointer back to the beginning of the file
301   - file.seekg(0, std::ios::beg);
302   -
303   - //compute the contiguous size C for each readable block
304   - unsigned int C = 1;
305   - for(unsigned int i = 0; i < d; i++) //for each dimension less than d
306   - C *= R[i]; //compute the product
307   -
308   - //compute the non-contiguous size NC for each readable block
309   - unsigned int NC = 1;
310   - for(unsigned int i = d + 1; i < D; i++)
311   - NC *= R[i];
312   -
313   - //for all noncontiguous blocks, read each contiguous block that makes up the hyper-plane
314   - for(unsigned int nc = 0; nc < NC; nc++){
315   - file.seekg(n * C * sizeof(T), std::ios::cur); //skip n contiguous blocks
316   - file.read( (char*)&dest[nc * C], C * sizeof(T)); //read one contiguous block
317   - file.seekg( (R[d] - n - 1) * C * sizeof(T), std::ios::cur); //skip R[d] - n contiguous blocks
318   - }
319   -
320   - return true;
321   -
322   - }*/
323   -
324   - //save one pixel of the file into the memory, and return the pointer
325   - /*bool read_spectrum(T * p, unsigned x, unsigned y){
326   -
327   - unsigned int i;
328   -
329   - if ( x >= R[0] || y >= R[1]){ //make sure the sample and line number is right
330   - std::cout<<"ERROR: sample or line out of range"<<std::endl;
331   - return false;
332   - }
333   -
334   - file.seekg((x + y * R[0]) * sizeof(T), std::ios::beg); //point to the certain sample and line
335   - for (i = 0; i < R[2]; i++)
336   - {
337   - file.read((char *)(p + i), sizeof(T));
338   - file.seekg((R[1] * R[0] - 1) * sizeof(T), std::ios::cur); //go to the next band
339   - }
340   -
341   - return true;
342   - }*/
343   -
344   -
345 319 };
346 320  
347 321 }
... ...
stim/envi/envi.h
... ... @@ -710,6 +710,11 @@ public:
710 710 return false;
711 711 }
712 712  
  713 + /// Retrieve a spectrum from the specified location
  714 +
  715 + /// @param ptr is a pointer to pre-allocated memory of size B*sizeof(T)
  716 + /// @param x is the x-coordinate of the spectrum
  717 + /// @param y is the y-coordinate of the spectrum
713 718 bool spectrum(void* ptr, unsigned int x, unsigned int y){
714 719  
715 720 if(header.interleave == envi_header::BSQ){ //if the infile is bsq file
... ...
stim/parser/arguments.h
... ... @@ -256,14 +256,16 @@ namespace stim{
256 256 args.add("foo", "foo takes a single integer value", "", "[intval]");
257 257 args.add("bar", "bar takes two floating point values", "", "[value1], [value2]");
258 258  
259   - 4) You generally want to immediately test for help and output available arguments:
  259 + 4) Parse the command line:
  260 +
  261 + args.parse(argc, argv);
  262 +
  263 + 5) You generally want to immediately test for help and output available arguments:
260 264  
261 265 if(args["help"].is_set())
262 266 std::cout<<args.str();
263 267  
264   - 5) Parse the command line:
265   -
266   - args.parse(argc, argv);
  268 +
267 269  
268 270 6) Retrieve values:
269 271  
... ... @@ -436,7 +438,8 @@ namespace stim{
436 438 }
437 439  
438 440 //set the last option
439   - set(name, params);
  441 + if(name != "")
  442 + set(name, params);
440 443 }
441 444  
442 445 ///Determines of a parameter has been set and returns true if it has
... ...
stim/visualization/camera.h
... ... @@ -167,14 +167,14 @@ public:
167 167 //output the camera settings
168 168 const void print(std::ostream& output)
169 169 {
170   - output<<"Position: "<<p<<std::endl;
  170 + output<<"Position: "<<p.str()<<std::endl;
171 171  
172 172 }
173 173 friend std::ostream& operator<<(std::ostream& out, const camera& c)
174 174 {
175   - out<<"Position: "<<c.p<<std::endl;
176   - out<<"Direction: "<<c.d<<std::endl;
177   - out<<"Up: "<<c.up<<std::endl;
  175 + out<<"Position: "<<c.p.str()<<std::endl;
  176 + out<<"Direction: "<<c.d.str()<<std::endl;
  177 + out<<"Up: "<<c.up.str()<<std::endl;
178 178 out<<"Focal Distance: "<<c.focus<<std::endl;
179 179 return out;
180 180 }
... ...
stim/visualization/colormap.h
... ... @@ -221,7 +221,11 @@ static void cpuApplyBrewer(T* cpuSource, unsigned char* cpuDest, unsigned int N,
221 221 for(int i=0; i<N; i++)
222 222 {
223 223 //compute the normalized value on [minVal maxVal]
224   - float a = (cpuSource[i] - minVal) / (maxVal - minVal);
  224 + float a;
  225 + if(minVal != maxVal)
  226 + a = (cpuSource[i] - minVal) / (maxVal - minVal);
  227 + else
  228 + a = 0.5;
225 229 if(a < 0) a = 0;
226 230 if(a > 1) a = 1;
227 231  
... ... @@ -263,11 +267,15 @@ static void cpu2cpu(T* cpuSource, unsigned char* cpuDest, unsigned int nVals, T
263 267 int i;
264 268 float a;
265 269 float range = valMax - valMin;
  270 +
266 271 for(i = 0; i<nVals; i++)
267 272 {
268 273 //normalize to the range [valMin valMax]
269   - a = (cpuSource[i] - valMin) / range;
270   -
  274 + if(range != 0)
  275 + a = (cpuSource[i] - valMin) / range;
  276 + else
  277 + a = 0.5;
  278 +
271 279 if(a < 0) a = 0;
272 280 if(a > 1) a = 1;
273 281  
... ... @@ -279,22 +287,26 @@ static void cpu2cpu(T* cpuSource, unsigned char* cpuDest, unsigned int nVals, T
279 287 }
280 288  
281 289 template<class T>
282   -static void cpu2cpu(T* cpuSource, unsigned char* cpuDest, unsigned int nVals, colormapType cm = cmGrayscale, bool positive = false)
  290 +static void cpu2cpu(T* cpuSource, unsigned char* cpuDest, unsigned int nVals, colormapType cm = cmGrayscale)//, bool positive = false)
283 291 {
284 292 //computes the max and min range automatically
285 293  
286 294 //find the largest magnitude value
287   - T maxVal = fabs(cpuSource[0]);
288   - for(int i=0; i<nVals; i++)
  295 + T maxVal = cpuSource[0];
  296 + T minVal = cpuSource[0];
  297 + for(int i=1; i<nVals; i++)
289 298 {
290   - if(fabs(cpuSource[i]) > maxVal)
291   - maxVal = fabs(cpuSource[i]);
  299 + if(cpuSource[i] > maxVal)
  300 + maxVal = cpuSource[i];
  301 + if(cpuSource[i] < minVal)
  302 + minVal = cpuSource[i];
292 303 }
293 304  
294   - if(positive)
295   - cpu2cpu(cpuSource, cpuDest, nVals, (T)0, maxVal, cm);
296   - else
297   - cpu2cpu(cpuSource, cpuDest, nVals, -maxVal, maxVal, cm);
  305 + //if(positive)
  306 + // cpu2cpu(cpuSource, cpuDest, nVals, (T)0, maxVal, cm);
  307 + //else
  308 + // cpu2cpu(cpuSource, cpuDest, nVals, -maxVal, maxVal, cm);
  309 + cpu2cpu(cpuSource, cpuDest, nVals, minVal, maxVal, cm);
298 310  
299 311 }
300 312  
... ...
stim/visualization/sph_harmonics.h 0 → 100644
  1 +#ifndef STIM_SPH_HARMONICS
  2 +#define STIM_SPH_HARMONICS
  3 +
  4 +#include <GL/gl.h>
  5 +
  6 +#include <stim/gl/error.h>
  7 +#include <stim/visualization/colormap.h>
  8 +#include <vector>
  9 +
  10 +#define PI 3.14159
  11 +#define WIRE_SCALE 1.001
  12 +namespace stim{
  13 +
  14 + class sph_harmonics{
  15 +
  16 + private:
  17 +
  18 + double* func; //stores the raw function data (samples at each point)
  19 +
  20 + GLuint color_tex; //texture map that acts as a colormap for the spherical function
  21 +
  22 + unsigned int N; //resolution of the spherical grid
  23 +
  24 + std::vector<double> C; //list of SH coefficients
  25 +
  26 +
  27 + //evaluates an associated Legendre polynomial (-l <= m <= l)
  28 + double P(int l,int m,double x)
  29 + {
  30 + // evaluate an Associated Legendre Polynomial P(l,m,x) at x
  31 + double pmm = 1.0;
  32 + if(m>0) {
  33 + double somx2 = sqrt((1.0-x)*(1.0+x));
  34 + double fact = 1.0;
  35 + for(int i=1; i<=m; i++) {
  36 + pmm *= (-fact) * somx2;
  37 + fact += 2.0;
  38 + }
  39 + }
  40 + if(l==m) return pmm;
  41 + double pmmp1 = x * (2.0*m+1.0) * pmm;
  42 + if(l==m+1) return pmmp1;
  43 + double pll = 0.0;
  44 + for(int ll=m+2; ll<=l; ++ll) {
  45 + pll = ( (2.0*ll-1.0)*x*pmmp1-(ll+m-1.0)*pmm ) / (ll-m);
  46 + pmm = pmmp1;
  47 + pmmp1 = pll;
  48 + }
  49 + return pll;
  50 + }
  51 +
  52 + //recursively calculate a factorial given a positive integer n
  53 + unsigned int factorial(unsigned int n) {
  54 + if (n == 0)
  55 + return 1;
  56 + return n * factorial(n - 1);
  57 + }
  58 +
  59 + //calculate the SH scaling constant
  60 + double K(int l, int m){
  61 +
  62 + // renormalisation constant for SH function
  63 + double temp = ((2.0*l+1.0)*factorial(l-m)) / (4.0*PI*factorial(l+m));
  64 + return sqrt(temp);
  65 + }
  66 +
  67 + //calculate the value of the SH basis function (l, m) at (theta, phi)
  68 + //here, theta = [0, PI], phi = [0, 2*PI]
  69 + double SH(int l, int m, double theta, double phi){
  70 + // return a point sample of a Spherical Harmonic basis function
  71 + // l is the band, range [0..N]
  72 + // m in the range [-l..l]
  73 + // theta in the range [0..Pi]
  74 + // phi in the range [0..2*Pi]
  75 + const double sqrt2 = sqrt(2.0);
  76 + if(m==0) return K(l,0)*P(l,m,cos(theta));
  77 + else if(m>0) return sqrt2*K(l,m)*cos(m*phi)*P(l,m,cos(theta));
  78 + else return sqrt2*K(l,-m)*sin(-m*phi)*P(l,-m,cos(theta));
  79 + }
  80 +
  81 + void gen_function(){
  82 +
  83 + //initialize the function to zero
  84 + memset(func, 0, sizeof(double) * N * N);
  85 +
  86 + double theta, phi;
  87 + double result;
  88 + int l, m;
  89 +
  90 + l = m = 0;
  91 + for(unsigned int c = 0; c < C.size(); c++){
  92 +
  93 +
  94 + for(unsigned int xi = 0; xi < N; xi++)
  95 + for(unsigned int yi = 0; yi < N; yi++){
  96 +
  97 + theta = (2 * PI) * ((double)xi / (N-1));
  98 + phi = PI * ((double)yi / (N-1));
  99 + result = C[c] * SH(l, m, phi, theta); //phi and theta are reversed here (damn physicists)
  100 + func[yi * N + xi] += result;
  101 + }
  102 +
  103 + m++; //increment m
  104 +
  105 + //if we're in a new tier, increment l and set m = -l
  106 + if(m > l){
  107 + l++;
  108 + m = -l;
  109 + }
  110 + }
  111 + }
  112 +
  113 + void gl_prep_draw(){
  114 +
  115 + //enable depth testing
  116 + //this has to be used instead of culling because the sphere can have negative values
  117 + glEnable(GL_DEPTH_TEST);
  118 + glDepthMask(GL_TRUE);
  119 + glEnable(GL_TEXTURE_2D); //enable 2D texture mapping
  120 + }
  121 +
  122 + //draw a texture mapped sphere representing the function surface
  123 + void gl_draw_sphere() {
  124 +
  125 + //PI is used to convert from spherical to cartesian coordinates
  126 + //const double PI = 3.14159;
  127 +
  128 + //bind the 2D texture representing the color map
  129 + glBindTexture(GL_TEXTURE_2D, color_tex);
  130 +
  131 + //Draw the Sphere
  132 + int i, j;
  133 +
  134 + for(i = 1; i <= N-1; i++) {
  135 + double phi0 = PI * ((double) (i - 1) / (N-1));
  136 + double phi1 = PI * ((double) i / (N-1));
  137 +
  138 + glBegin(GL_QUAD_STRIP);
  139 + for(j = 0; j <= N; j++) {
  140 +
  141 + //calculate the indices into the function array
  142 + int phi0_i = i-1;
  143 + int phi1_i = i;
  144 + int theta_i = j;
  145 + if(theta_i == N)
  146 + theta_i = 0;
  147 +
  148 + double v0 = func[phi0_i * N + theta_i];
  149 + double v1 = func[phi1_i * N + theta_i];
  150 +
  151 + v0 = fabs(v0);
  152 + v1 = fabs(v1);
  153 +
  154 +
  155 + double theta = 2 * PI * (double) (j - 1) / N;
  156 + double x0 = v0 * cos(theta) * sin(phi0);
  157 + double y0 = v0 * sin(theta) * sin(phi0);
  158 + double z0 = v0 * cos(phi0);
  159 +
  160 + double x1 = v1 * cos(theta) * sin(phi1);
  161 + double y1 = v1 * sin(theta) * sin(phi1);
  162 + double z1 = v1 * cos(phi1);
  163 +
  164 + glTexCoord2f(theta / (2 * PI), phi0 / PI);
  165 + glVertex3f(x0, y0, z0);
  166 +
  167 + glTexCoord2f(theta / (2 * PI), phi1 / PI);
  168 + glVertex3f(x1, y1, z1);
  169 + }
  170 + glEnd();
  171 + }
  172 + }
  173 +
  174 + //draw a wire frame sphere representing the function surface
  175 + void gl_draw_wireframe() {
  176 +
  177 + //PI is used to convert from spherical to cartesian coordinates
  178 + //const double PI = 3.14159;
  179 +
  180 + //bind the 2D texture representing the color map
  181 + glDisable(GL_TEXTURE_2D);
  182 + glColor3f(0.0f, 0.0f, 0.0f);
  183 +
  184 + //Draw the Sphere
  185 + int i, j;
  186 +
  187 + for(i = 1; i <= N-1; i++) {
  188 + double phi0 = PI * ((double) (i - 1) / (N-1));
  189 + double phi1 = PI * ((double) i / (N-1));
  190 +
  191 + glBegin(GL_LINE_STRIP);
  192 + for(j = 0; j <= N; j++) {
  193 +
  194 + //calculate the indices into the function array
  195 + int phi0_i = i-1;
  196 + int phi1_i = i;
  197 + int theta_i = j;
  198 + if(theta_i == N)
  199 + theta_i = 0;
  200 +
  201 + double v0 = func[phi0_i * N + theta_i];
  202 + double v1 = func[phi1_i * N + theta_i];
  203 +
  204 + v0 = fabs(v0);
  205 + v1 = fabs(v1);
  206 +
  207 +
  208 + double theta = 2 * PI * (double) (j - 1) / N;
  209 + double x0 = WIRE_SCALE * v0 * cos(theta) * sin(phi0);
  210 + double y0 = WIRE_SCALE * v0 * sin(theta) * sin(phi0);
  211 + double z0 = WIRE_SCALE * v0 * cos(phi0);
  212 +
  213 + double x1 = WIRE_SCALE * v1 * cos(theta) * sin(phi1);
  214 + double y1 = WIRE_SCALE * v1 * sin(theta) * sin(phi1);
  215 + double z1 = WIRE_SCALE * v1 * cos(phi1);
  216 +
  217 + glTexCoord2f(theta / (2 * PI), phi0 / PI);
  218 + glVertex3f(x0, y0, z0);
  219 +
  220 + glTexCoord2f(theta / (2 * PI), phi1 / PI);
  221 + glVertex3f(x1, y1, z1);
  222 + }
  223 + glEnd();
  224 + }
  225 + }
  226 +
  227 + void init(unsigned int n){
  228 +
  229 + //set the sphere resolution
  230 + N = n;
  231 +
  232 + //allocate space for the color map
  233 + unsigned int bytes = N * N * sizeof(unsigned char) * 3;
  234 + unsigned char* color_image;
  235 + color_image = (unsigned char*) malloc(bytes);
  236 +
  237 + //allocate space for the function
  238 + func = (double*) malloc(N * N * sizeof(double));
  239 +
  240 + //generate a function (temporary)
  241 + gen_function();
  242 +
  243 + //generate a colormap from the function
  244 + stim::cpu2cpu<double>(func, color_image, N*N, stim::cmBrewer);
  245 +
  246 + //prep everything for drawing
  247 + gl_prep_draw();
  248 +
  249 + //generate an OpenGL texture map in the current context
  250 + glGenTextures(1, &color_tex);
  251 + //bind the texture
  252 + glBindTexture(GL_TEXTURE_2D, color_tex);
  253 +
  254 + //copy the color data from the buffer to the GPU
  255 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, N, N, 0, GL_RGB, GL_UNSIGNED_BYTE, color_image);
  256 +
  257 + //initialize all of the texture parameters
  258 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  259 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  260 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  261 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  262 + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  263 +
  264 + //free the buffer
  265 + free(color_image);
  266 + }
  267 +
  268 +
  269 + public:
  270 +
  271 + void glRender(){
  272 + //set all OpenGL parameters required for drawing
  273 + gl_prep_draw();
  274 +
  275 + //draw the sphere
  276 + gl_draw_sphere();
  277 + //gl_draw_wireframe();
  278 +
  279 + }
  280 +
  281 + void glInit(unsigned int n){
  282 + init(n);
  283 + }
  284 +
  285 + void push(double c){
  286 + C.push_back(c);
  287 + }
  288 +
  289 +
  290 +
  291 +
  292 +
  293 + }; //end class sph_harmonics
  294 +
  295 +
  296 +
  297 +
  298 +}
  299 +
  300 +
  301 +#endif
... ...