#include //stim libraries #include #include #include #include #include #include //input arguments stim::arglist args; #include #include #include #include #include #include #define NOMINMAX //GA #include "ga_gpu.h" #include "enviload.h" //envi input file and associated parameters stim::envi E; //ENVI binary file object unsigned int B; //shortcuts storing the spatial and spectral size of the ENVI image //mask and class information used for training //std::vector< stim::image > C; //2D array used to access each mask C[m][p], where m = mask# and p = pixel# std::vector nP; //array holds the number of pixels in each mask: nP[m] is the number of pixels in mask m size_t nC = 0; //number of classes size_t tP = 0; //total number of pixels in all masks: tP = nP[0] + nP[1] + ... + nP[nC] float* fea; //ga_gpu class object ga_gpu ga; bool debug; bool binaryClass; int binClassOne; //creating struct to pass to thread functions as it limits number of arguments to 3 typedef struct { float* S; float* Sb; float* Sw; float* lda; }gnome; gnome gnom; void gpuComputeEignS( size_t g, size_t fea){ //eigen value computation will return r = (nC-1) eigen vectors so new projected data will have dimension of r rather than f // std::thread::id this_id = std::this_thread::get_id(); // std::cout<<"thread id is "<< this_id< features = ga.getGnome(g); std::vector featuresunique; int flag = 0; std::sort(features.begin(), features.end()); // 1 1 2 2 3 3 3 4 4 5 5 6 7 std::unique_copy(features.begin(), features.end(), std::back_inserter(featuresunique)); if(featuresunique.size()< features.size()){ f = featuresunique.size(); } size_t r = nC-1; //LDA projected dimension (limited to number of classes - 1 by rank) if(r > f){ r = f; } int info; float* EigenvaluesI_a = (float*)malloc(f * sizeof(float)); float* Eigenvalues_a = (float*)malloc(f * sizeof(float)); int *IPIV = (int*) malloc(sizeof(int) * f); //computing inverse of matrix Sw memset(IPIV, 0, f * sizeof(int)); LAPACKE_sgetrf(LAPACK_COL_MAJOR, (int)f, (int)f, gSw_a, (int)f, IPIV); // DGETRI computes the inverse of a matrix using the LU factorization computed by DGETRF. LAPACKE_sgetri(LAPACK_COL_MAJOR, (int)f, gSw_a, (int)f, IPIV); float* gSbSw_a = (float*)calloc(f * f, sizeof(float)); //mtxMul(gSbSw_a, gSw_a, &gnom.Sb[g * f * f * sizeof(float)], f, f, f,f); mtxMul(gSbSw_a, gSw_a, &gnom.Sb[g * f * f], f, f, f,f); if(debug){ std::cout<<"From Eigen function: inverse of sw and ratio of sb and sw (Sb/Sw)"; displayS(gSw_a, f); //display inverse of Sw (1/Sw) displayS(gSbSw_a, f); //display ratio of Sb and Sw (Sb/Sw) } //compute left eigenvectors for current gnome from ratio of between class scatter and within class scatter: Sb/Sw info = LAPACKE_sgeev(LAPACK_COL_MAJOR, 'V', 'N', (int)f, gSbSw_a, (int)f, Eigenvalues_a, EigenvaluesI_a, LeftEigVectors_a, (int)f, 0, (int)f); //sort eignevalue indices in descending order size_t* sortedindx = sortIndx(Eigenvalues_a, f); //displayS(LeftEigVectors_a, f); //display Eignevectors (Note these are -1 * matlab eigenvectors does not change fitness score results but keep in mind while projecting data on it) //sorting left eigenvectors (building forward transformation matrix As) for (size_t rowE = 0; rowE < r; rowE++){ for (size_t colE = 0; colE < f; colE++){ size_t ind1 = g * r * f + rowE * f + colE; //size_t ind1 = rowE * f + colE; size_t ind2 = sortedindx[rowE] * f + colE; //eigenvector as row vector gnom.lda[ind1] = LeftEigVectors_a[ind2]; } } if(debug){ std::cout<<"Eigenvalues are"< threads; //for (size_t g = 0; g ga.gnrtn - 2){ std::cout << "gpu_eigen time "<(elapsed1).count() << "us" << std::endl; profilefile<< "gpu_eigen time "<(elapsed1).count() << "us" << std::endl; } //ga.S = gnom.score; //size_t bestGnomeIdx = ga.sortSIndx()[0]; }//end of fitness function void binaryclassifier(int classnum){ unsigned int* target = (unsigned int*) calloc(tP, sizeof(unsigned int)); memcpy(target, ga.T, tP * sizeof(unsigned int)); for(int i = 0 ; i < tP; i++){ if(target[i]==classnum){ ga.T[i] = 1; }else ga.T[i] = 0; } } void advertisement() { std::cout << std::endl; std::cout << "=========================================================================" << std::endl; std::cout << "Thank you for using the GA-GPU features selection for spectroscopic image!" << std::endl; std::cout << "=========================================================================" << std::endl << std::endl; // std::cout << args.str(); } int main(int argc, char* argv[]){ //Add the argument options and set some of the default parameters args.add("help", "print this help"); args.section("Genetic Algorithm"); args.add("features", "select features selection algorithm parameters","10", "number of features to be selected"); args.add("classes", "image masks used to specify classes", "", "class1.bmp class2.bmp class3.bmp"); args.add("population", "total number of feature subsets in puplation matrix", "1000"); args.add("generations", "number of generationsr", "50"); args.add("initial_guess", "initial guess of featues", ""); args.add("debug", "display intermediate data for debugging"); args.add("binary", "Select features for binary classes", ""); args.add("trim", "this gives wavenumber to use in trim option of siproc which trims all bands from envi file except gagpu selected bands"); args.parse(argc,argv); //parse the command line arguments //Print the help text if set if(args["help"].is_set()){ //display the help text if requested advertisement(); std::cout< wavelengths = E.header.wavelength; //wavelengths of each band if(E.header.interleave != stim::envi_header::BIP){ //this code can only load bip files and hence check that in header file std::cout<<"this code works for only bip files. please convert file to bip file"< i_guess(ga.f); for (size_t b = 0; b < ga.f; b++) //generate default initial guess i_guess[b] = rand() % B + 0; if (args["initial_guess"].is_set()) {//if the user specifies the --initialguess option & provides feature indices as initial guess size_t nf = args["initial_guess"].nargs(); //get the number of arguments after initial_guess if (nf == 1 || nf == ga.f) { //check if file with initial guessed indices is given or direct indices are given as argument if (nf == 1) { //if initial guessed feature indices are given in file std::ifstream in; //open the file containing the baseline points in.open(args["initial_guess"].as_string().c_str()); if (in.is_open()){ //if file is present and can be opened then read it unsigned int b_ind; while (in >> b_ind) //get each band index and push it into the vector i_guess.push_back(b_ind); } else std::cout << "cannot open file of initial_guess indices" << std::endl; } else if (nf == ga.f) { //if direct indices are given as argument for (size_t b = 0; b < nf; b++) //for each band given by the user i_guess[b] = args["initial_guess"].as_int(b); //store that band in the i_guess array } } } ga.initialize_population(i_guess, debug); //initialize first population set for first generation, user can pass manually preferred features from command line //display_gnome(0); //------------------Calculate class means and total mean of features---------------------------- float* M = (float*)calloc( B , sizeof(float)); //total mean of entire feature martix for all features (bands B) ga.ttlMean(M, tP, B); //calculate total mean, ga.F is entire feature matrix, M is mean for all bands B(features) if(debug) ga.dispalymean(M); //if option --debug is used display all bands mean //display band index of bands with mean zero, this indicates that band has all zero values std::cout<<"Display features indices with zero mean "< bestgnome; //holds best gnome after each generation evaluation size_t bestG_Indx; //This gives index of best gnome in the current population to get best gnome and its fitness value unsigned int* newPop = (unsigned int*) calloc(ga.p * ga.f, sizeof(unsigned int)); //temprory storage of new population double* best_S = (double*) calloc (ga.gnrtn, sizeof(double)); //stores fitness value of best gnome at each iteration float* lda = (float*) calloc (ga.p * (nC-1) * ga.f, sizeof(float)); //stores LDA basis for each gnome so that we can have best gnome's LDA basis float* sb = (float*) calloc( ga.p * ga.f * ga.f , sizeof(float)) ; //3d matrix for between class scatter (each 2d matrix between class scatter for one gnome) float* sw = (float*) calloc( ga.p * ga.f * ga.f , sizeof(float)) ; //3d matrix for within class scatter (each 2d matrix within class scatter for one gnome) ga.zerobandcheck(M, true); //checking bands with all zeros and duplicated bands in a gnome replacing them with other bands avoiding duplication and zero mean ga.zerobandcheck(M, true); //Repeating zeroband cheack as some of these bands are not replaced in previous run and gave random results time_t gpu_timestart = time(NULL); //start a timer for total evoluation for (size_t gen = 0; gen < ga.gnrtn; gen++){ //for each generation find fitness value of all gnomes in population matrix and generate population for next generation //std::cout<<"Generation: "<ga.gnrtn -2){ std::cout << "population evolution time "<(pop_generation).count() << "us" << std::endl; profilefile<<"population evolution time "<(pop_generation).count() << "us" < trimindex(ga.f); //create a vector to store temprory trim index bounds std::vector finaltrim_ind; //create a vector to store final trim index bounds std::vector trim_wv; //create a vector to store final trim wavelength bounds //trim index trimindex.push_back(1); //1st trimming band index is 1 for (size_t i = 0; i < ga.f; i++) { // for each feature find its bound indexes trimindex[i * 2] = bestgnome.at(i) - 1; trimindex[i * 2 + 1] = bestgnome.at(i) + 1; } trimindex.push_back(B); //last bound index is B //organize trim index int k = 0; for (size_t i = 0; i < ga.f + 1; i++) { // find valid pair of trim indices bound excluding adjacent trim indices if (trimindex[2 * i] < trimindex[2 * i + 1]) { finaltrim_ind.push_back(trimindex[2 * i]); //this is left bound finaltrim_ind.push_back(trimindex[2 * i + 1]); k = k + 2; } } //add duplicated trim indices as single index to final trim index for (size_t i = 0; i < ga.f + 1; i++) { //check each pair of trim indices for duplications if (trimindex[2 * i] == trimindex[2 * i + 1]) { // (duplication caused due to adjacent features) finaltrim_ind.push_back(trimindex[2 * i]); // remove duplicated trim indices replace by one k = k + 1; } } ////output actual wavelenths corresponding to those trim indices ////these wavenumber are grouped in pairs, check each pair, if duplicated numbers are there in pair delete one and keep other as band to trim, if 2nd wavnumber is smaller than 1st in a pair ignore that pair ////e.g [1, 228, 230, 230, 232, 350,352, 351, 353, 1200] pairas [1(start)-228,230-230, 232-350, 352-351, 353-1200(end)], trimming wavenumbers are [1-228, 230, 233-350, 353-1200] csv << (wavelengths[finaltrim_ind.at(0)]); for (size_t i = 1; i < ga.f * + 2 ; i++) csv << "," << (wavelengths[finaltrim_ind.at(i)]); csv << std::endl; } //end trim option //free gpu pointers ga.gpu_Destroy(); //free pointers std::free(sb); std::free(sw); std::free(M); std::free(cM); std::free(best_S); std::free(lda); std::free(newPop); }//end main