pktools  2.6.3
Processing Kernel for geospatial data
pkfssvm.cc
1 /**********************************************************************
2 pkfssvm.cc: feature selection for support vector machine classifier pksvm
3 Copyright (C) 2008-2014 Pieter Kempeneers
4 
5 This file is part of pktools
6 
7 pktools is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 pktools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with pktools. If not, see <http://www.gnu.org/licenses/>.
19 ***********************************************************************/
20 #include <stdlib.h>
21 #include <vector>
22 #include <string>
23 #include <map>
24 #include <algorithm>
25 #include "base/Optionpk.h"
26 #include "algorithms/ConfusionMatrix.h"
27 #include "algorithms/CostFactorySVM.h"
28 #include "algorithms/FeatureSelector.h"
29 #include "algorithms/svm.h"
30 #include "imageclasses/ImgReaderOgr.h"
31 
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
35 
36 /******************************************************************************/
98 using namespace std;
99 
100 enum SelectorValue { NA=0, SFFS=1, SFS=2, SBS=3, BFS=4};
101 
102 // CostFactorySVM::CostFactorySVM()
103 // : CostFactory(2,0), m_svm_type("C_SVC"), m_kernel_type("radial"), m_kernel_degree(3), m_gamma(1.0), m_coef0(0), m_ccost(1000), m_nu(0.5), m_epsilon_loss(100), m_cache(100), m_epsilon_tol(0.001), m_shrinking(false), m_prob_est(true){
104 // }
105 
106 // CostFactorySVM::~CostFactorySVM(){
107 // }
108 
109 // CostFactorySVM::CostFactorySVM(std::string svm_type, std::string kernel_type, unsigned short kernel_degree, float gamma, float coef0, float ccost, float nu, float epsilon_loss, int cache, float epsilon_tol, bool shrinking, bool prob_est, unsigned short cv, bool verbose)
110 // : CostFactory(cv,verbose), m_svm_type(svm_type), m_kernel_type(kernel_type), m_kernel_degree(kernel_degree), m_gamma(gamma), m_coef0(coef0), m_ccost(ccost), m_nu(nu), m_epsilon_loss(epsilon_loss), m_cache(cache), m_epsilon_tol(epsilon_tol), m_shrinking(shrinking), m_prob_est(prob_est){};
111 
112 // double CostFactorySVM::getCost(const vector<Vector2d<float> > &trainingFeatures){
113 // std::map<std::string, svm::SVM_TYPE> svmMap;
114 
115 // svmMap["C_SVC"]=svm::C_SVC;
116 // svmMap["nu_SVC"]=svm::nu_SVC;
117 // svmMap["one_class"]=svm::one_class;
118 // svmMap["epsilon_SVR"]=svm::epsilon_SVR;
119 // svmMap["nu_SVR"]=svm::nu_SVR;
120 
121 // std::map<std::string, svm::KERNEL_TYPE> kernelMap;
122 
123 // kernelMap["linear"]=svm::linear;
124 // kernelMap["polynomial"]=svm::polynomial;
125 // kernelMap["radial"]=svm::radial;
126 // kernelMap["sigmoid;"]=svm::sigmoid;
127 
128 // unsigned short nclass=trainingFeatures.size();
129 // unsigned int ntraining=0;
130 // unsigned int ntest=0;
131 // for(int iclass=0;iclass<nclass;++iclass){
132 // ntraining+=m_nctraining[iclass];
133 // ntest+=m_nctest[iclass];
134 // }
135 // if(ntest)
136 // assert(!m_cv);
137 // if(!m_cv)
138 // assert(ntest);
139 // unsigned short nFeatures=trainingFeatures[0][0].size();
140 
141 // struct svm_parameter param;
142 // param.svm_type = svmMap[m_svm_type];
143 // param.kernel_type = kernelMap[m_kernel_type];
144 // param.degree = m_kernel_degree;
145 // param.gamma = (m_gamma>0)? m_gamma : 1.0/nFeatures;
146 // param.coef0 = m_coef0;
147 // param.nu = m_nu;
148 // param.cache_size = m_cache;
149 // param.C = m_ccost;
150 // param.eps = m_epsilon_tol;
151 // param.p = m_epsilon_loss;
152 // param.shrinking = (m_shrinking)? 1 : 0;
153 // param.probability = (m_prob_est)? 1 : 0;
154 // param.nr_weight = 0;//not used: I use priors and balancing
155 // param.weight_label = NULL;
156 // param.weight = NULL;
157 // param.verbose=(m_verbose>1)? true:false;
158 // struct svm_model* svm;
159 // struct svm_problem prob;
160 // struct svm_node* x_space;
161 
162 // prob.l=ntraining;
163 // prob.y = Malloc(double,prob.l);
164 // prob.x = Malloc(struct svm_node *,prob.l);
165 // x_space = Malloc(struct svm_node,(nFeatures+1)*ntraining);
166 // unsigned long int spaceIndex=0;
167 // int lIndex=0;
168 // for(int iclass=0;iclass<nclass;++iclass){
169 // // for(int isample=0;isample<trainingFeatures[iclass].size();++isample){
170 // for(int isample=0;isample<m_nctraining[iclass];++isample){
171 // prob.x[lIndex]=&(x_space[spaceIndex]);
172 // for(int ifeature=0;ifeature<nFeatures;++ifeature){
173 // x_space[spaceIndex].index=ifeature+1;
174 // x_space[spaceIndex].value=trainingFeatures[iclass][isample][ifeature];
175 // ++spaceIndex;
176 // }
177 // x_space[spaceIndex++].index=-1;
178 // prob.y[lIndex]=iclass;
179 // ++lIndex;
180 // }
181 // }
182 
183 // assert(lIndex==prob.l);
184 // if(m_verbose>2)
185 // std::cout << "checking parameters" << std::endl;
186 // svm_check_parameter(&prob,&param);
187 // if(m_verbose>2)
188 // std::cout << "parameters ok, training" << std::endl;
189 // svm=svm_train(&prob,&param);
190 // if(m_verbose>2)
191 // std::cout << "SVM is now trained" << std::endl;
192 
193 // m_cm.clearResults();
194 // if(m_cv>1){
195 // double *target = Malloc(double,prob.l);
196 // svm_cross_validation(&prob,&param,m_cv,target);
197 // assert(param.svm_type != EPSILON_SVR&&param.svm_type != NU_SVR);//only for regression
198 // for(int i=0;i<prob.l;i++){
199 // string refClassName=m_nameVector[prob.y[i]];
200 // string className=m_nameVector[target[i]];
201 // if(m_classValueMap.size())
202 // m_cm.incrementResult(type2string<short>(m_classValueMap[refClassName]),type2string<short>(m_classValueMap[className]),1.0);
203 // else
204 // m_cm.incrementResult(m_cm.getClass(prob.y[i]),m_cm.getClass(target[i]),1.0);
205 // }
206 // free(target);
207 // }
208 // else{
209 // struct svm_node *x_test;
210 // vector<double> result(nclass);
211 // x_test = Malloc(struct svm_node,(nFeatures+1));
212 // for(int iclass=0;iclass<nclass;++iclass){
213 // for(int isample=0;isample<m_nctest[iclass];++isample){
214 // for(int ifeature=0;ifeature<nFeatures;++ifeature){
215 // x_test[ifeature].index=ifeature+1;
216 // x_test[ifeature].value=trainingFeatures[iclass][m_nctraining[iclass]+isample][ifeature];
217 // }
218 // x_test[nFeatures].index=-1;
219 // double predict_label=0;
220 // assert(svm_check_probability_model(svm));
221 // predict_label = svm_predict_probability(svm,x_test,&(result[0]));
222 // // predict_label = svm_predict(svm,x_test);
223 // string refClassName=m_nameVector[iclass];
224 // string className=m_nameVector[static_cast<short>(predict_label)];
225 // if(m_classValueMap.size())
226 // m_cm.incrementResult(type2string<short>(m_classValueMap[refClassName]),type2string<short>(m_classValueMap[className]),1.0);
227 // else
228 // m_cm.incrementResult(refClassName,className,1.0);
229 // }
230 // }
231 // free(x_test);
232 // }
233 // if(m_verbose>1)
234 // std::cout << m_cm << std::endl;
235 // assert(m_cm.nReference());
236 // // if(m_verbose)
237 
238 // // std::cout << m_cm << std::endl;
239 // // std::cout << "Kappa: " << m_cm.kappa() << std::endl;
240 // // double se95_oa=0;
241 // // double doa=0;
242 // // doa=m_cm.oa_pct(&se95_oa);
243 // // std::cout << "Overall Accuracy: " << doa << " (" << se95_oa << ")" << std::endl;
244 
245 // // *NOTE* Because svm_model contains pointers to svm_problem, you can
246 // // not free the memory used by svm_problem if you are still using the
247 // // svm_model produced by svm_train().
248 // // however, we will re-train the svm later on after the feature selection
249 // free(prob.y);
250 // free(prob.x);
251 // free(x_space);
252 // svm_free_and_destroy_model(&(svm));
253 
254 // return(m_cm.kappa());
255 // }
256 
257 int main(int argc, char *argv[])
258 {
259  // vector<double> priors;
260 
261  //--------------------------- command line options ------------------------------------
262  Optionpk<string> input_opt("i", "input", "input test set (leave empty to perform a cross validation based on training only)");
263  Optionpk<string> training_opt("t", "training", "training vector file. A single vector file contains all training features (must be set as: B0, B1, B2,...) for all classes (class numbers identified by label option).");
264  Optionpk<string> tlayer_opt("tln", "tln", "training layer name(s)");
265  Optionpk<string> label_opt("label", "label", "identifier for class label in training vector file.","label");
266  Optionpk<unsigned short> maxFeatures_opt("n", "nf", "number of features to select (0 to select optimal number, see also ecost option)", 0);
267  Optionpk<unsigned int> balance_opt("bal", "balance", "balance the input data to this number of samples for each class", 0);
268  Optionpk<bool> random_opt("random","random", "in case of balance, randomize input data", true);
269  Optionpk<int> minSize_opt("min", "min", "if number of training pixels is less then min, do not take this class into account", 0);
270  Optionpk<short> band_opt("b", "band", "band index (starting from 0, either use band option or use start to end)");
271  Optionpk<double> bstart_opt("s", "start", "start band sequence number",0);
272  Optionpk<double> bend_opt("e", "end", "end band sequence number (set to 0 to include all bands)", 0);
273  Optionpk<double> offset_opt("\0", "offset", "offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
274  Optionpk<double> scale_opt("\0", "scale", "scale value for each spectral band input features: refl=(DN[band]-offset[band])/scale[band] (use 0 if scale min and max in each band to -1.0 and 1.0)", 0.0);
275  Optionpk<string> selector_opt("sm", "sm", "feature selection method (sffs=sequential floating forward search,sfs=sequential forward search, sbs, sequential backward search ,bfs=brute force search)","sffs");
276  Optionpk<float> epsilon_cost_opt("ecost", "ecost", "epsilon for stopping criterion in cost function to determine optimal number of features",0.001);
277 
278  Optionpk<std::string> svm_type_opt("svmt", "svmtype", "type of SVM (C_SVC, nu_SVC,one_class, epsilon_SVR, nu_SVR)","C_SVC");
279  Optionpk<std::string> kernel_type_opt("kt", "kerneltype", "type of kernel function (linear,polynomial,radial,sigmoid) ","radial");
280  Optionpk<unsigned short> kernel_degree_opt("kd", "kd", "degree in kernel function",3);
281  Optionpk<float> gamma_opt("g", "gamma", "gamma in kernel function",1.0);
282  Optionpk<float> coef0_opt("c0", "coef0", "coef0 in kernel function",0);
283  Optionpk<float> ccost_opt("cc", "ccost", "the parameter C of C-SVC, epsilon-SVR, and nu-SVR",1000);
284  Optionpk<float> nu_opt("nu", "nu", "the parameter nu of nu-SVC, one-class SVM, and nu-SVR",0.5);
285  Optionpk<float> epsilon_loss_opt("eloss", "eloss", "the epsilon in loss function of epsilon-SVR",0.1);
286  Optionpk<int> cache_opt("cache", "cache", "cache memory size in MB",100);
287  Optionpk<float> epsilon_tol_opt("etol", "etol", "the tolerance of termination criterion",0.001);
288  Optionpk<bool> shrinking_opt("shrink", "shrink", "whether to use the shrinking heuristics",false);
289  Optionpk<bool> prob_est_opt("pe", "probest", "whether to train a SVC or SVR model for probability estimates",true,2);
290  Optionpk<unsigned short> cv_opt("cv", "cv", "n-fold cross validation mode",2);
291  Optionpk<string> classname_opt("c", "class", "list of class names.");
292  Optionpk<short> classvalue_opt("r", "reclass", "list of class values (use same order as in classname opt.");
293  Optionpk<short> verbose_opt("v", "verbose", "set to: 0 (results only), 1 (confusion matrix), 2 (debug)",0,2);
294 
295  tlayer_opt.setHide(1);
296  label_opt.setHide(1);
297  balance_opt.setHide(1);
298  random_opt.setHide(1);
299  minSize_opt.setHide(1);
300  band_opt.setHide(1);
301  bstart_opt.setHide(1);
302  bend_opt.setHide(1);
303  offset_opt.setHide(1);
304  scale_opt.setHide(1);
305  svm_type_opt.setHide(1);
306  kernel_type_opt.setHide(1);
307  kernel_degree_opt.setHide(1);
308  gamma_opt.setHide(1);
309  coef0_opt.setHide(1);
310  ccost_opt.setHide(1);
311  nu_opt.setHide(1);
312  epsilon_loss_opt.setHide(1);
313  cache_opt.setHide(1);
314  epsilon_tol_opt.setHide(1);
315  shrinking_opt.setHide(1);
316  prob_est_opt.setHide(1);
317  selector_opt.setHide(1);
318  epsilon_cost_opt.setHide(1);
319  cv_opt.setHide(1);
320  classname_opt.setHide(1);
321  classvalue_opt.setHide(1);
322 
323  bool doProcess;//stop process when program was invoked with help option (-h --help)
324  try{
325  doProcess=input_opt.retrieveOption(argc,argv);
326  training_opt.retrieveOption(argc,argv);
327  maxFeatures_opt.retrieveOption(argc,argv);
328  tlayer_opt.retrieveOption(argc,argv);
329  label_opt.retrieveOption(argc,argv);
330  balance_opt.retrieveOption(argc,argv);
331  random_opt.retrieveOption(argc,argv);
332  minSize_opt.retrieveOption(argc,argv);
333  band_opt.retrieveOption(argc,argv);
334  bstart_opt.retrieveOption(argc,argv);
335  bend_opt.retrieveOption(argc,argv);
336  offset_opt.retrieveOption(argc,argv);
337  scale_opt.retrieveOption(argc,argv);
338  svm_type_opt.retrieveOption(argc,argv);
339  kernel_type_opt.retrieveOption(argc,argv);
340  kernel_degree_opt.retrieveOption(argc,argv);
341  gamma_opt.retrieveOption(argc,argv);
342  coef0_opt.retrieveOption(argc,argv);
343  ccost_opt.retrieveOption(argc,argv);
344  nu_opt.retrieveOption(argc,argv);
345  epsilon_loss_opt.retrieveOption(argc,argv);
346  cache_opt.retrieveOption(argc,argv);
347  epsilon_tol_opt.retrieveOption(argc,argv);
348  shrinking_opt.retrieveOption(argc,argv);
349  prob_est_opt.retrieveOption(argc,argv);
350  selector_opt.retrieveOption(argc,argv);
351  epsilon_cost_opt.retrieveOption(argc,argv);
352  cv_opt.retrieveOption(argc,argv);
353  classname_opt.retrieveOption(argc,argv);
354  classvalue_opt.retrieveOption(argc,argv);
355  verbose_opt.retrieveOption(argc,argv);
356  }
357  catch(string predefinedString){
358  std::cout << predefinedString << std::endl;
359  exit(0);
360  }
361  if(!doProcess){
362  cout << endl;
363  cout << "Usage: pkfssvm -t training -n number" << endl;
364  cout << endl;
365  std::cout << "short option -h shows basic options only, use long option --help to show all options" << std::endl;
366  exit(0);//help was invoked, stop processing
367  }
368 
369  CostFactorySVM costfactory(svm_type_opt[0], kernel_type_opt[0], kernel_degree_opt[0], gamma_opt[0], coef0_opt[0], ccost_opt[0], nu_opt[0], epsilon_loss_opt[0], cache_opt[0], epsilon_tol_opt[0], shrinking_opt[0], prob_est_opt[0], cv_opt[0], verbose_opt[0]);
370 
371  assert(training_opt.size());
372  if(input_opt.size())
373  costfactory.setCv(0);
374  if(verbose_opt[0]>=1){
375  if(input_opt.size())
376  std::cout << "input filename: " << input_opt[0] << std::endl;
377  std::cout << "training vector file: " << std::endl;
378  for(int ifile=0;ifile<training_opt.size();++ifile)
379  std::cout << training_opt[ifile] << std::endl;
380  std::cout << "verbose: " << verbose_opt[0] << std::endl;
381  }
382 
383  static std::map<std::string, SelectorValue> selMap;
384  //initialize selMap
385  selMap["sffs"]=SFFS;
386  selMap["sfs"]=SFS;
387  selMap["sbs"]=SBS;
388  selMap["bfs"]=BFS;
389 
390  unsigned int totalSamples=0;
391  unsigned int totalTestSamples=0;
392 
393  unsigned short nclass=0;
394  int nband=0;
395  int startBand=2;//first two bands represent X and Y pos
396 
397  // if(priors_opt.size()>1){//priors from argument list
398  // priors.resize(priors_opt.size());
399  // double normPrior=0;
400  // for(int iclass=0;iclass<priors_opt.size();++iclass){
401  // priors[iclass]=priors_opt[iclass];
402  // normPrior+=priors[iclass];
403  // }
404  // //normalize
405  // for(int iclass=0;iclass<priors_opt.size();++iclass)
406  // priors[iclass]/=normPrior;
407  // }
408 
409  //sort bands
410  if(band_opt.size())
411  std::sort(band_opt.begin(),band_opt.end());
412 
413  if(classname_opt.size()){
414  assert(classname_opt.size()==classvalue_opt.size());
415  for(int iclass=0;iclass<classname_opt.size();++iclass)
416  costfactory.setClassValueMap(classname_opt[iclass],classvalue_opt[iclass]);
417  }
418 
419  //----------------------------------- Training -------------------------------
420  vector<double> offset;
421  vector<double> scale;
422  vector< Vector2d<float> > trainingPixels;//[class][sample][band]
423  vector< Vector2d<float> > testPixels;//[class][sample][band]
424  map<string,Vector2d<float> > trainingMap;
425  map<string,Vector2d<float> > testMap;
426  vector<string> fields;
427 
428  struct svm_problem prob;
429  //organize training data
430  trainingPixels.clear();
431  testPixels.clear();
432  if(verbose_opt[0]>=1)
433  std::cout << "reading training file " << training_opt[0] << std::endl;
434  try{
435  ImgReaderOgr trainingReader(training_opt[0]);
436  if(band_opt.size()){
437  totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
438  if(input_opt.size()){
439  ImgReaderOgr inputReader(input_opt[0]);
440  totalTestSamples=inputReader.readDataImageOgr(testMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
441  inputReader.close();
442  }
443  }
444  else{
445  totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
446  if(input_opt.size()){
447  ImgReaderOgr inputReader(input_opt[0]);
448  totalTestSamples=inputReader.readDataImageOgr(testMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
449  inputReader.close();
450  }
451  }
452  if(trainingMap.size()<2){
453  string errorstring="Error: could not read at least two classes from training input file";
454  throw(errorstring);
455  }
456  if(input_opt.size()&&testMap.size()<2){
457  string errorstring="Error: could not read at least two classes from test input file";
458  throw(errorstring);
459  }
460  trainingReader.close();
461  }
462  catch(string error){
463  cerr << error << std::endl;
464  exit(1);
465  }
466  catch(std::exception& e){
467  std::cerr << "Error: ";
468  std::cerr << e.what() << std::endl;
469  std::cerr << CPLGetLastErrorMsg() << std::endl;
470  exit(1);
471  }
472  catch(...){
473  cerr << "error catched" << std::endl;
474  exit(1);
475  }
476  //todo: delete class 0 ?
477  // if(verbose_opt[0]>=1)
478  // std::cout << "erasing class 0 from training set (" << trainingMap[0].size() << " from " << totalSamples << ") samples" << std::endl;
479  // totalSamples-=trainingMap[0].size();
480  // trainingMap.erase(0);
481 
482  if(verbose_opt[0]>1)
483  std::cout << "training pixels: " << std::endl;
484  map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
485  while(mapit!=trainingMap.end()){
486  //delete small classes
487  if((mapit->second).size()<minSize_opt[0]){
488  trainingMap.erase(mapit);
489  continue;
490  }
491  costfactory.pushBackName(mapit->first);
492  trainingPixels.push_back(mapit->second);
493  if(verbose_opt[0]>1)
494  std::cout << mapit->first << ": " << (mapit->second).size() << " samples" << std::endl;
495  ++mapit;
496  }
497  nclass=trainingPixels.size();
498  if(classname_opt.size())
499  assert(nclass==classname_opt.size());
500  nband=trainingPixels[0][0].size()-2;//X and Y//trainingPixels[0][0].size();
501 
502  mapit=testMap.begin();
503  while(mapit!=testMap.end()){
504  if(costfactory.getClassValueMap().size()){
505  // if(classValueMap.size()){
506  //check if name in test is covered by classname_opt (values can not be 0)
507  if((costfactory.getClassValueMap())[mapit->first]>0){
508  ;//ok, no need to print to std::cout
509  }
510  else{
511  std::cerr << "Error: names in classname option are not complete, please check names in test vector and make sure classvalue is > 0" << std::endl;
512  exit(1);
513  }
514  }
515  //no need to delete small classes for test sample
516  testPixels.push_back(mapit->second);
517  if(verbose_opt[0]>1)
518  std::cout << mapit->first << ": " << (mapit->second).size() << " samples" << std::endl;
519  ++mapit;
520  }
521  if(input_opt.size()){
522  assert(nclass==testPixels.size());
523  assert(nband=testPixels[0][0].size()-2);//X and Y//testPixels[0][0].size();
524  assert(!cv_opt[0]);
525  }
526 
527  //do not remove outliers here: could easily be obtained through ogr2ogr -where 'B2<110' output.shp input.shp
528  //balance training data
529  //todo: do I mean to use random_opt?
530  if(balance_opt[0]>0){
531  if(random_opt[0])
532  srand(time(NULL));
533  totalSamples=0;
534  for(int iclass=0;iclass<nclass;++iclass){
535  if(trainingPixels[iclass].size()>balance_opt[0]){
536  while(trainingPixels[iclass].size()>balance_opt[0]){
537  int index=rand()%trainingPixels[iclass].size();
538  trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
539  }
540  }
541  else{
542  int oldsize=trainingPixels[iclass].size();
543  for(int isample=trainingPixels[iclass].size();isample<balance_opt[0];++isample){
544  int index = rand()%oldsize;
545  trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
546  }
547  }
548  totalSamples+=trainingPixels[iclass].size();
549  }
550  assert(totalSamples==nclass*balance_opt[0]);
551  }
552 
553  //set scale and offset
554  offset.resize(nband);
555  scale.resize(nband);
556  if(offset_opt.size()>1)
557  assert(offset_opt.size()==nband);
558  if(scale_opt.size()>1)
559  assert(scale_opt.size()==nband);
560  for(int iband=0;iband<nband;++iband){
561  if(verbose_opt[0]>1)
562  std::cout << "scaling for band" << iband << std::endl;
563  offset[iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
564  scale[iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
565  //search for min and maximum
566  if(scale[iband]<=0){
567  float theMin=trainingPixels[0][0][iband+startBand];
568  float theMax=trainingPixels[0][0][iband+startBand];
569  for(int iclass=0;iclass<nclass;++iclass){
570  for(int isample=0;isample<trainingPixels[iclass].size();++isample){
571  if(theMin>trainingPixels[iclass][isample][iband+startBand])
572  theMin=trainingPixels[iclass][isample][iband+startBand];
573  if(theMax<trainingPixels[iclass][isample][iband+startBand])
574  theMax=trainingPixels[iclass][isample][iband+startBand];
575  }
576  }
577  offset[iband]=theMin+(theMax-theMin)/2.0;
578  scale[iband]=(theMax-theMin)/2.0;
579  if(verbose_opt[0]>1){
580  std::cout << "Extreme image values for band " << iband << ": [" << theMin << "," << theMax << "]" << std::endl;
581  std::cout << "Using offset, scale: " << offset[iband] << ", " << scale[iband] << std::endl;
582  std::cout << "scaled values for band " << iband << ": [" << (theMin-offset[iband])/scale[iband] << "," << (theMax-offset[iband])/scale[iband] << "]" << std::endl;
583  }
584  }
585  }
586 
587  // if(priors_opt.size()==1){//default: equal priors for each class
588  // priors.resize(nclass);
589  // for(int iclass=0;iclass<nclass;++iclass)
590  // priors[iclass]=1.0/nclass;
591  // }
592  // assert(priors_opt.size()==1||priors_opt.size()==nclass);
593 
594  if(verbose_opt[0]>=1){
595  std::cout << "number of bands: " << nband << std::endl;
596  std::cout << "number of classes: " << nclass << std::endl;
597  // std::cout << "priors:";
598  // for(int iclass=0;iclass<nclass;++iclass)
599  // std::cout << " " << priors[iclass];
600  // std::cout << std::endl;
601  }
602 
603  //set names in confusion matrix using nameVector
604  vector<string> nameVector=costfactory.getNameVector();
605  for(int iname=0;iname<nameVector.size();++iname){
606  if(costfactory.getClassValueMap().empty())
607  costfactory.pushBackClassName(nameVector[iname]);
608  // cm.pushBackClassName(nameVector[iname]);
609  else if(costfactory.getClassIndex(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]))<0)
610  costfactory.pushBackClassName(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]));
611  }
612 
613 
614  //Calculate features of training (and test) set
615 
616  vector<unsigned int> nctraining;
617  vector<unsigned int> nctest;
618  nctraining.resize(nclass);
619  nctest.resize(nclass);
620  vector< Vector2d<float> > trainingFeatures(nclass);
621  for(int iclass=0;iclass<nclass;++iclass){
622  if(verbose_opt[0]>=1)
623  std::cout << "calculating features for class " << iclass << std::endl;
624  nctraining[iclass]=trainingPixels[iclass].size();
625  if(verbose_opt[0]>=1)
626  std::cout << "nctraining[" << iclass << "]: " << nctraining[iclass] << std::endl;
627  if(testPixels.size()>iclass){
628  nctest[iclass]=testPixels[iclass].size();
629  if(verbose_opt[0]>=1){
630  std::cout << "nctest[" << iclass << "]: " << nctest[iclass] << std::endl;
631  }
632  }
633  else
634  nctest[iclass]=0;
635 
636  trainingFeatures[iclass].resize(nctraining[iclass]+nctest[iclass]);
637  for(int isample=0;isample<nctraining[iclass];++isample){
638  //scale pixel values according to scale and offset!!!
639  for(int iband=0;iband<nband;++iband){
640  assert(trainingPixels[iclass].size()>isample);
641  assert(trainingPixels[iclass][isample].size()>iband+startBand);
642  assert(offset.size()>iband);
643  assert(scale.size()>iband);
644  float value=trainingPixels[iclass][isample][iband+startBand];
645  trainingFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
646  }
647  }
648  for(int isample=0;isample<nctest[iclass];++isample){
649  //scale pixel values according to scale and offset!!!
650  for(int iband=0;iband<nband;++iband){
651  assert(testPixels[iclass].size()>isample);
652  assert(testPixels[iclass][isample].size()>iband+startBand);
653  assert(offset.size()>iband);
654  assert(scale.size()>iband);
655  float value=testPixels[iclass][isample][iband+startBand];
656  // testFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
657  trainingFeatures[iclass][nctraining[iclass]+isample].push_back((value-offset[iband])/scale[iband]);
658  }
659  }
660  assert(trainingFeatures[iclass].size()==nctraining[iclass]+nctest[iclass]);
661  }
662 
663  costfactory.setNcTraining(nctraining);
664  costfactory.setNcTest(nctest);
665  int nFeatures=trainingFeatures[0][0].size();
666  int maxFeatures=(maxFeatures_opt[0])? maxFeatures_opt[0] : 1;
667  double previousCost=-1;
668  double cost=0;
669  list<int> subset;//set of selected features (levels) for each class combination
670  FeatureSelector selector;
671  try{
672  if(maxFeatures>=nFeatures){
673  subset.clear();
674  for(int ifeature=0;ifeature<nFeatures;++ifeature)
675  subset.push_back(ifeature);
676  cost=costfactory.getCost(trainingFeatures);
677  }
678  else{
679  while(fabs(cost-previousCost)>=epsilon_cost_opt[0]){
680  previousCost=cost;
681  switch(selMap[selector_opt[0]]){
682  case(SFFS):
683  subset.clear();//needed to clear in case of floating and brute force search
684  cost=selector.floating(trainingFeatures,costfactory,subset,maxFeatures,epsilon_cost_opt[0],verbose_opt[0]);
685  break;
686  case(SFS):
687  cost=selector.forward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
688  break;
689  case(SBS):
690  cost=selector.backward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
691  break;
692  case(BFS):
693  subset.clear();//needed to clear in case of floating and brute force search
694  cost=selector.bruteForce(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
695  break;
696  default:
697  std::cout << "Error: selector not supported, please use sffs, sfs, sbs or bfs" << std::endl;
698  exit(1);
699  break;
700  }
701  if(verbose_opt[0]>1){
702  std::cout << "cost: " << cost << std::endl;
703  std::cout << "previousCost: " << previousCost << std::endl;
704  std::cout << std::setprecision(12) << "cost-previousCost: " << cost - previousCost << " ( " << epsilon_cost_opt[0] << ")" << std::endl;
705  }
706  if(!maxFeatures_opt[0])
707  ++maxFeatures;
708  else
709  break;
710  }
711  }
712  }
713  catch(...){
714  std::cout << "catched feature selection" << std::endl;
715  exit(1);
716  }
717 
718  if(verbose_opt[0])
719  cout <<"cost: " << cost << endl;
720  subset.sort();
721  for(list<int>::const_iterator lit=subset.begin();lit!=subset.end();++lit)
722  std::cout << " -b " << *lit;
723  std::cout << std::endl;
724  // if((*(lit))!=subset.back())
725  // else
726  // cout << endl;
727 
728  // *NOTE* Because svm_model contains pointers to svm_problem, you can
729  // not free the memory used by svm_problem if you are still using the
730  // svm_model produced by svm_train().
731 
732  // free(prob.y);
733  // free(prob.x);
734  // free(x_space);
735  // svm_destroy_param(&param);
736  return 0;
737 }
738