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