///KNOWN BUGS /// *) The mouse pointer position gets messed up if any of the band windows are resized /// This is caused by not knowing which window is clicked when the mouse_band() callback is triggered. /// If it's possible to get the window, just change the if(n == 0) code to reference the correct window and pull it outside the loop /// *) Each spectrum should be displayed in a different color /// *) ENVI files with different numbers of bands aren't supported /// This can be fixed by changing everything in the HSIview code to deal with wavenumbers rather than bands #include #include #include #include #include #include #ifdef _WIN32 #include #endif #include #include #define NN 5 size_t N; //number of open ENVI files stim::arglist args; //program arguments structure stim::envi ENVI[NN]; //global ENVI file being visualized size_t SAMPLES; //dimensions of the loaded ENVI files size_t LINES; size_t BANDS[NN]; //number of bands in each spectrum stim::image band_image[NN]; //stores an image of the band float* spectrum[NN]; //array stores the current spectrum double* wavelengths[NN]; //array stores the independent variable for the spectrum (usually wavelength or frequency) float sMax, sMin; //maximum value in the current spectrum float wMin, wMax; //minimum and maximum independent spectral values float colorMin, colorMax; bool colorManual = false; //control the color mapping manually size_t B[NN] = {0, 0}; //current band being visualized double W = 0.0; //wavelength current visualized size_t X, Y; //current pixel position double vFactor = 0.2; //fraction of the spectral window used as space above the spectrum to display numbers GLuint tex_id[NN]; int winBand[NN]; //Window identifier for the band image int winSpectrum; //window identifier for the spectrum float colorMap[NN][3] = {{1, 1, 1}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {0, 1, 1}}; ///Output software advertisement and instructions void advertise(){ //output advertisement std::cout< sMax) sMax = spectrum[i][b]; if(spectrum[i][b] < sMin) sMin = spectrum[i][b]; } } } void update_spectrum(size_t n){ if(spectrum[n] == NULL) //if space hasn't been allocated for the spectrum spectrum[n] = (float*) malloc(BANDS[n] * sizeof(float)); //allocate the space if(wavelengths[n] == NULL) wavelengths[n] = (double*) malloc(BANDS[n] * sizeof(double)); if(ENVI[n].header.wavelength.size() == 0){ //if there are no wavelength values for the ENVI file for(size_t i = 0; i < BANDS[n]; i++) //for each band wavelengths[n][i] = (double)i; //save the band number as the wavelength } else memcpy(wavelengths[n], &ENVI[n].header.wavelength[0], sizeof(double) * BANDS[n]); //copy the wavelength values ENVI[n].spectrum(spectrum[n], X, Y); //load the spectrum from the ENVI file glutSetWindow(winSpectrum); glutPostRedisplay(); } void update_spectra(){ for(size_t n = 0; n < N; n++) update_spectrum(n); update_spectrum_minmax(); } /// Update the band image displayed for ENVI file n void update_band(size_t n){ unsigned long long sx = SAMPLES; //calculate the image size unsigned long long sy = LINES; float* buffer = NULL; //allocate a buffer buffer = (float*)malloc( sx * sy * sizeof(float)); //allocate space for the band std::vector b_idx = ENVI[n].header.band_index(W); //get the bands closest to the current wavelength if (b_idx.size() > 0) { //if a band exists B[n] = b_idx[0]; if (b_idx.size() > 1) { double w0 = ENVI[n].header.wavelength[b_idx[0]]; //get the lower wavelength double w1 = ENVI[n].header.wavelength[b_idx[1]]; //get the upper wavelength if (abs(w1 - W) < abs(W - w0)) B[n] = b_idx[1]; //if the current wavelength is closer to the higher band, display the higher one std::cout << "w0: " << w0 << std::endl; std::cout << "w1: " << w1 << std::endl; } std::cout << "Display band: " << B[n] << std::endl; } ENVI[n].band_index(buffer, B[n]); //retrieve the band image if(colorManual) stim::cpu2cpu(buffer, band_image[n].data(), sx * sy, colorMin, colorMax, stim::cmBrewer); else stim::cpu2cpu(buffer, band_image[n].data(), sx * sy, stim::cmBrewer); //convert the band image to a color image free(buffer); glutSetWindow(winBand[n]); //switch to the band window glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //enable linear interpolation glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); //clamp the values at the minimum and maximum glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); //test to see if the texture can be accomodated glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGB, (GLsizei)sx, (GLsizei)sy, 0, GL_RGB, GL_UNSIGNED_BYTE, band_image[n].data()); int success; glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &success); if(success) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, (GLsizei)sx, (GLsizei)sy, 0, GL_RGB, GL_UNSIGNED_BYTE, band_image[n].data()); //upload the texture map to the GPU else std::cout << "Texture format not supported (likely the image size won't work with OpenGL - which sucks)" << std::endl; CHECK_OPENGL_ERROR glutPostRedisplay(); //update the visualization } /// Updates all band images void update_bands(){ for(size_t n = 0; n < N; n++) update_band(n); } /// Refresh band windows (update overlays and pixel locator positions) void refresh_band(size_t n){ glutSetWindow(winBand[n]); glutPostRedisplay(); } void refresh_bands(){ for(size_t n = 0; n < N; n++) refresh_band(n); } /// Updates the spectrum when a new pixel position is selected in any band window void mouse_band(int button, int state, int x, int y){ for(size_t n = 0; n < N; n++){ //for each band window //translate from the window to the image glutSetWindow(winBand[n]); //set the current window to the band window if(n == 0){ //only evaluate the mouse position for the first window X = (unsigned long long)(SAMPLES * (double)x / (double)glutGet(GLUT_WINDOW_WIDTH)); //calculate the ENVI x coordinate Y = (unsigned long long)(LINES * (double)y / (double)glutGet(GLUT_WINDOW_HEIGHT)); //calculate the ENVI y coordinate std::cout<<"("< LINES-1) Y = LINES-1; break; case GLUT_KEY_DOWN: Y++; if(Y > LINES-1) Y = 0; break; case GLUT_KEY_LEFT: X--; if(X > SAMPLES-1) X = SAMPLES-1; break; case GLUT_KEY_RIGHT: X++; if(X > SAMPLES-1) X = 0; break; } refresh_bands(); update_spectra(); } /// Special keys for the band image void key_spectrum(unsigned char key, int x, int y){ std::ofstream outfile("spectrum.csv"); if(key == 's'){ for(size_t b = 0; b < BANDS[0]; b++){ if(b != 0) outfile<<","; outfile<(sx, sy, 3); //allocate space for the band visualization image glGenTextures(1, &tex_id[n]); //generate a texture map name glBindTexture(GL_TEXTURE_2D, tex_id[n]); //bind the texture map } void glut_initialize(std::string filename){ int myargc = 1; //GLUT requires arguments, so create some bogus ones char* myargv[1]; myargv[0] = (char*)malloc(1); myargv[0][0] = 'h'; glutInit(&myargc, myargv); //pass bogus arguments to glutInit() glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); //generate a color buffer, depth buffer, and enable double buffering for(size_t n = 0; n < N; n++){ glutInitWindowPosition(100,100); //set the initial window position glutInitWindowSize(320,320); //set the initial window size winBand[n] = glutCreateWindow((filename + " - HSIview - STIM Lab, UH").c_str()); //set the dialog box title glutDisplayFunc(render_bands); //function executed for rendering - renders networks glutMouseFunc(mouse_band); //executed on a mouse click - sets starting mouse positions for rotations glutMotionFunc(glut_motion); //executed when the mouse is moved while a button is pressed glutSpecialFunc(special_band); //special keys pressed when the band image is selected texture_initialize(n); //set up texture mapping (create texture maps, enable features) } glutInitWindowPosition(400,500); //set the initial window position glutInitWindowSize(960,320); //set the initial window size winSpectrum = glutCreateWindow("Spectrum"); glutMouseFunc(mouse_spectrum); glutDisplayFunc(render_spectra); glutKeyboardFunc(key_spectrum); #ifdef _WIN32 GLenum err = glewInit(); //initialize GLEW (necessary for Windows) if (GLEW_OK != err){ //eror with GLEW std::cout<<"Error with GLEW: "< NN){ std::cout<<"ERROR - HSIview cannot currently support more than "< max_tex || LINES > max_tex){ std::cout<<"ERROR: image too large for OpenGL texture support. Maximum texture size is: "<