pktools  2.6.3
Processing Kernel for geospatial data
pkoptsvm.cc
1 /**********************************************************************
2 pkoptsvm.cc: program to optimize parameters 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 <iostream>
21 #include <sstream>
22 #include <fstream>
23 #include <vector>
24 #include <math.h>
25 #include <nlopt.hpp>
26 #include "base/Optionpk.h"
27 #include "base/Optionpk.h"
28 #include "algorithms/ConfusionMatrix.h"
29 #include "algorithms/FeatureSelector.h"
30 #include "algorithms/OptFactory.h"
31 #include "algorithms/CostFactorySVM.h"
32 #include "algorithms/svm.h"
33 #include "imageclasses/ImgReaderOgr.h"
34 
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38 
39 using namespace std;
40 
41 #define Malloc(type,n) (type *)malloc((n)*sizeof(type))
42  //declare objective function
43 double objFunction(const std::vector<double> &x, std::vector<double> &grad, void *my_func_data);
44 
45 //global parameters used in objective function
46 map<string,short> classValueMap;
47 vector<std::string> nameVector;
48 vector<unsigned int> nctraining;
49 vector<unsigned int> nctest;
50 Optionpk<std::string> svm_type_opt("svmt", "svmtype", "type of SVM (C_SVC, nu_SVC,one_class, epsilon_SVR, nu_SVR)","C_SVC");
51 Optionpk<std::string> kernel_type_opt("kt", "kerneltype", "type of kernel function (linear,polynomial,radial,sigmoid) ","radial");
52 Optionpk<unsigned short> kernel_degree_opt("kd", "kd", "degree in kernel function",3);
53 Optionpk<float> coef0_opt("c0", "coef0", "coef0 in kernel function",0);
54 Optionpk<float> nu_opt("nu", "nu", "the parameter nu of nu-SVC, one-class SVM, and nu-SVR",0.5);
55 Optionpk<float> epsilon_loss_opt("eloss", "eloss", "the epsilon in loss function of epsilon-SVR",0.1);
56 Optionpk<int> cache_opt("cache", "cache", "cache memory size in MB",100);
57 Optionpk<float> epsilon_tol_opt("etol", "etol", "the tolerance of termination criterion",0.001);
58 Optionpk<bool> shrinking_opt("shrink", "shrink", "whether to use the shrinking heuristics",false);
59 Optionpk<bool> prob_est_opt("pe", "probest", "whether to train a SVC or SVR model for probability estimates",true,2);
60 Optionpk<bool> costfunction_opt("cf", "cf", "use Overall Accuracy instead of kappa",false);
61 // Optionpk<bool> weight_opt("wi", "wi", "set the parameter C of class i to weight*C, for C-SVC",true);
62 Optionpk<unsigned short> cv_opt("cv", "cv", "n-fold cross validation mode",2);
63 Optionpk<string> classname_opt("c", "class", "list of class names.");
64 Optionpk<short> classvalue_opt("r", "reclass", "list of class values (use same order as in class opt).");
65 Optionpk<short> verbose_opt("v", "verbose", "use 1 to output intermediate results for plotting",0,2);
66 
67 double objFunction(const std::vector<double> &x, std::vector<double> &grad, void *my_func_data){
68 
69  assert(grad.empty());
70  vector<Vector2d<float> > *tf=reinterpret_cast<vector<Vector2d<float> >*> (my_func_data);
71  float ccost=x[0];
72  float gamma=x[1];
73  double error=1.0/epsilon_tol_opt[0];
74  double kappa=1.0;
75  double oa=1.0;
76 
77  CostFactorySVM costfactory(svm_type_opt[0], kernel_type_opt[0], kernel_degree_opt[0], gamma, coef0_opt[0], ccost, 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]);
78 
79  assert(tf->size());
80  // if(nctest>0)
81  // costfactory.setCv(0);
82 
83  costfactory.setCv(cv_opt[0]);
84 
85  if(classname_opt.size()){
86  assert(classname_opt.size()==classvalue_opt.size());
87  for(int iclass=0;iclass<classname_opt.size();++iclass)
88  costfactory.setClassValueMap(classname_opt[iclass],classvalue_opt[iclass]);
89  }
90  //set names in confusion matrix using nameVector
91  costfactory.setNameVector(nameVector);
92  // vector<string> nameVector=costfactory.getNameVector();
93  for(int iname=0;iname<nameVector.size();++iname){
94  if(costfactory.getClassValueMap().empty()){
95  costfactory.pushBackClassName(nameVector[iname]);
96  // cm.pushBackClassName(nameVector[iname]);
97  }
98  else if(costfactory.getClassIndex(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]))<0)
99  costfactory.pushBackClassName(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]));
100  }
101 
102  costfactory.setNcTraining(nctraining);
103  costfactory.setNcTest(nctest);
104 
105  kappa=costfactory.getCost(*tf);
106  return(kappa);
107 
108  // std::map<std::string, svm::SVM_TYPE> svmMap;
109 
110  // svmMap["C_SVC"]=svm::C_SVC;
111  // svmMap["nu_SVC"]=svm::nu_SVC;
112  // svmMap["one_class"]=svm::one_class;
113  // svmMap["epsilon_SVR"]=svm::epsilon_SVR;
114  // svmMap["nu_SVR"]=svm::nu_SVR;
115 
116  // std::map<std::string, svm::KERNEL_TYPE> kernelMap;
117 
118  // kernelMap["linear"]=svm::linear;
119  // kernelMap["polynomial"]=svm::polynomial;
120  // kernelMap["radial"]=svm::radial;
121  // kernelMap["sigmoid;"]=svm::sigmoid;
122 
123  // unsigned short nclass=tf->size();
124  // unsigned int ntraining=0;
125  // unsigned int ntest=0;
126  // for(int iclass=0;iclass<nclass;++iclass){
127  // ntraining+=nctraining[iclass];
128  // ntest+=nctest[iclass];
129  // }
130  // if(ntest)
131  // cv_opt[0]=0;
132  // if(!cv_opt[0])
133  // assert(ntest);
134 
135  // unsigned short nFeatures=(*tf)[0][0].size();
136  // struct svm_parameter param;
137  // param.svm_type = svmMap[svm_type_opt[0]];
138  // param.kernel_type = kernelMap[kernel_type_opt[0]];
139  // param.degree = kernel_degree_opt[0];
140  // param.gamma = gamma;
141  // param.coef0 = coef0_opt[0];
142  // param.nu = nu_opt[0];
143  // param.cache_size = cache_opt[0];
144  // param.C = ccost;
145  // param.eps = epsilon_tol_opt[0];
146  // param.p = epsilon_loss_opt[0];
147  // param.shrinking = (shrinking_opt[0])? 1 : 0;
148  // param.probability = (prob_est_opt[0])? 1 : 0;
149  // param.nr_weight = 0;//not used: I use priors and balancing
150  // param.weight_label = NULL;
151  // param.weight = NULL;
152  // param.verbose=(verbose_opt[0]>2)? true:false;
153  // struct svm_model* svm;
154  // struct svm_problem prob;
155  // struct svm_node* x_space;
156 
157  // prob.l=ntraining;
158  // prob.y = Malloc(double,prob.l);
159  // prob.x = Malloc(struct svm_node *,prob.l);
160  // x_space = Malloc(struct svm_node,(nFeatures+1)*ntraining);
161  // unsigned long int spaceIndex=0;
162  // int lIndex=0;
163  // for(int iclass=0;iclass<nclass;++iclass){
164  // // for(int isample=0;isample<(*tf)[iclass].size();++isample){
165  // for(int isample=0;isample<nctraining[iclass];++isample){
166  // prob.x[lIndex]=&(x_space[spaceIndex]);
167  // for(int ifeature=0;ifeature<nFeatures;++ifeature){
168  // x_space[spaceIndex].index=ifeature+1;
169  // x_space[spaceIndex].value=(*tf)[iclass][isample][ifeature];
170  // ++spaceIndex;
171  // }
172  // x_space[spaceIndex++].index=-1;
173  // prob.y[lIndex]=iclass;
174  // ++lIndex;
175  // }
176  // }
177 
178  // assert(lIndex==prob.l);
179  // if(verbose_opt[0]>2)
180  // std::cout << "checking parameters" << std::endl;
181  // svm_check_parameter(&prob,&param);
182  // if(verbose_opt[0]>2)
183  // std::cout << "parameters ok, training" << std::endl;
184  // svm=svm_train(&prob,&param);
185  // if(verbose_opt[0]>2)
186  // std::cout << "SVM is now trained" << std::endl;
187 
188  // ConfusionMatrix cm;
189  // //set names in confusion matrix using nameVector
190  // for(int iname=0;iname<nameVector.size();++iname){
191  // if(classValueMap.empty())
192  // cm.pushBackClassName(nameVector[iname]);
193  // else if(cm.getClassIndex(type2string<short>(classValueMap[nameVector[iname]]))<0)
194  // cm.pushBackClassName(type2string<short>(classValueMap[nameVector[iname]]));
195  // }
196  // if(cv_opt[0]>1){
197  // double *target = Malloc(double,prob.l);
198  // svm_cross_validation(&prob,&param,cv_opt[0],target);
199  // assert(param.svm_type != EPSILON_SVR&&param.svm_type != NU_SVR);//only for regression
200  // for(int i=0;i<prob.l;i++){
201  // string refClassName=nameVector[prob.y[i]];
202  // string className=nameVector[target[i]];
203  // if(classValueMap.size())
204  // cm.incrementResult(type2string<short>(classValueMap[refClassName]),type2string<short>(classValueMap[className]),1.0);
205  // else
206  // cm.incrementResult(cm.getClass(prob.y[i]),cm.getClass(target[i]),1.0);
207  // }
208  // free(target);
209  // }
210  // else{
211  // struct svm_node *x_test;
212  // x_test = Malloc(struct svm_node,(nFeatures+1));
213  // for(int iclass=0;iclass<nclass;++iclass){
214  // for(int isample=0;isample<nctest[iclass];++isample){
215  // for(int ifeature=0;ifeature<nFeatures;++ifeature){
216  // x_test[ifeature].index=ifeature+1;
217  // x_test[ifeature].value=(*tf)[iclass][nctraining[iclass]+isample][ifeature];
218  // }
219  // x_test[nFeatures].index=-1;
220  // double predict_label=0;
221  // //todo: make distinction between svm_predict and svm_predict_probability?
222  // predict_label = svm_predict(svm,x_test);
223  // string refClassName=nameVector[iclass];
224  // string className=nameVector[static_cast<short>(predict_label)];
225  // if(classValueMap.size())
226  // cm.incrementResult(type2string<short>(classValueMap[refClassName]),type2string<short>(classValueMap[className]),1.0);
227  // else
228  // cm.incrementResult(refClassName,className,1.0);
229  // }
230  // }
231  // free(x_test);
232  // }
233  // if(verbose_opt[0]>1)
234  // std::cout << cm << std::endl;
235  // assert(cm.nReference());
236  // free(prob.y);
237  // free(prob.x);
238  // free(x_space);
239  // svm_free_and_destroy_model(&(svm));
240  // if(verbose_opt[0]>2)
241  // std::cout << cm << std::endl;
242  // kappa=cm.kappa();
243  // oa=cm.oa();
244  // if(verbose_opt[0]>1){
245  // std::cout << " --ccost " << x[0];
246  // std::cout << " --gamma " << x[1];
247  // std::cout << std::endl;
248  // std::cout << "oa: " << oa << std::endl;
249  // std::cout << "kappa: " << kappa << std::endl;
250  // }
251  // double cost=(costfunction_opt[0])? oa : kappa;
252  // if(cost>0)
253  // error=1.0/cost;
254  // return(error);
255 }
256 
257 int main(int argc, char *argv[])
258 {
259  map<short,int> reclassMap;
260  vector<int> vreclass;
261  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).");
262  Optionpk<float> ccost_opt("cc", "ccost", "min and max boundaries the parameter C of C-SVC, epsilon-SVR, and nu-SVR (optional: initial value)",1);
263  Optionpk<float> gamma_opt("g", "gamma", "min max boundaries for gamma in kernel function (optional: initial value)",0);
264  Optionpk<double> stepcc_opt("stepcc","stepcc","multiplicative step for ccost in GRID search",2);
265  Optionpk<double> stepg_opt("stepg","stepg","multiplicative step for gamma in GRID search",2);
266  Optionpk<string> input_opt("i", "input", "input test vector file");
267  Optionpk<string> tlayer_opt("tln", "tln", "training layer name(s)");
268  Optionpk<string> label_opt("label", "label", "identifier for class label in training vector file.","label");
269  // Optionpk<unsigned short> reclass_opt("\0", "rc", "reclass code (e.g. --rc=12 --rc=23 to reclass first two classes to 12 and 23 resp.).", 0);
270  Optionpk<unsigned int> balance_opt("bal", "balance", "balance the input data to this number of samples for each class", 0);
271  Optionpk<bool> random_opt("random","random", "in case of balance, randomize input data", true);
272  Optionpk<int> minSize_opt("min", "min", "if number of training pixels is less then min, do not take this class into account", 0);
273  Optionpk<short> band_opt("b", "band", "band index (starting from 0, either use band option or use start to end)");
274  Optionpk<double> bstart_opt("s", "start", "start band sequence number",0);
275  Optionpk<double> bend_opt("e", "end", "end band sequence number (set to 0 to include all bands)", 0);
276  Optionpk<double> offset_opt("\0", "offset", "offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
277  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);
278  Optionpk<unsigned int> maxit_opt("maxit","maxit","maximum number of iterations",500);
279  Optionpk<string> algorithm_opt("a", "algorithm", "GRID, or any optimization algorithm from http://ab-initio.mit.edu/wiki/index.php/NLopt_Algorithms","GRID");
280  Optionpk<double> tolerance_opt("tol","tolerance","relative tolerance for stopping criterion",0.0001);
281 
282  input_opt.setHide(1);
283  tlayer_opt.setHide(1);
284  label_opt.setHide(1);
285  balance_opt.setHide(1);
286  random_opt.setHide(1);
287  minSize_opt.setHide(1);
288  band_opt.setHide(1);
289  bstart_opt.setHide(1);
290  bend_opt.setHide(1);
291  offset_opt.setHide(1);
292  scale_opt.setHide(1);
293  svm_type_opt.setHide(1);
294  kernel_type_opt.setHide(1);
295  kernel_degree_opt.setHide(1);
296  coef0_opt.setHide(1);
297  nu_opt.setHide(1);
298  epsilon_loss_opt.setHide(1);
299  cache_opt.setHide(1);
300  epsilon_tol_opt.setHide(1);
301  shrinking_opt.setHide(1);
302  prob_est_opt.setHide(1);
303  cv_opt.setHide(1);
304  costfunction_opt.setHide(1);
305  maxit_opt.setHide(1);
306  tolerance_opt.setHide(1);
307  algorithm_opt.setHide(1);
308  classname_opt.setHide(1);
309  classvalue_opt.setHide(1);
310 
311  bool doProcess;//stop process when program was invoked with help option (-h --help)
312  try{
313  doProcess=training_opt.retrieveOption(argc,argv);
314  ccost_opt.retrieveOption(argc,argv);
315  gamma_opt.retrieveOption(argc,argv);
316  stepcc_opt.retrieveOption(argc,argv);
317  stepg_opt.retrieveOption(argc,argv);
318  input_opt.retrieveOption(argc,argv);
319  tlayer_opt.retrieveOption(argc,argv);
320  label_opt.retrieveOption(argc,argv);
321  balance_opt.retrieveOption(argc,argv);
322  random_opt.retrieveOption(argc,argv);
323  minSize_opt.retrieveOption(argc,argv);
324  band_opt.retrieveOption(argc,argv);
325  bstart_opt.retrieveOption(argc,argv);
326  bend_opt.retrieveOption(argc,argv);
327  offset_opt.retrieveOption(argc,argv);
328  scale_opt.retrieveOption(argc,argv);
329  svm_type_opt.retrieveOption(argc,argv);
330  kernel_type_opt.retrieveOption(argc,argv);
331  kernel_degree_opt.retrieveOption(argc,argv);
332  coef0_opt.retrieveOption(argc,argv);
333  nu_opt.retrieveOption(argc,argv);
334  epsilon_loss_opt.retrieveOption(argc,argv);
335  cache_opt.retrieveOption(argc,argv);
336  epsilon_tol_opt.retrieveOption(argc,argv);
337  shrinking_opt.retrieveOption(argc,argv);
338  prob_est_opt.retrieveOption(argc,argv);
339  cv_opt.retrieveOption(argc,argv);
340  costfunction_opt.retrieveOption(argc,argv);
341  maxit_opt.retrieveOption(argc,argv);
342  tolerance_opt.retrieveOption(argc,argv);
343  algorithm_opt.retrieveOption(argc,argv);
344  classname_opt.retrieveOption(argc,argv);
345  classvalue_opt.retrieveOption(argc,argv);
346  verbose_opt.retrieveOption(argc,argv);
347  }
348  catch(string predefinedString){
349  std::cout << predefinedString << std::endl;
350  exit(0);
351  }
352  if(!doProcess){
353  cout << endl;
354  cout << "Usage: pkoptsvm -t training" << endl;
355  cout << endl;
356  std::cout << "short option -h shows basic options only, use long option --help to show all options" << std::endl;
357  exit(0);//help was invoked, stop processing
358  }
359 
360  assert(training_opt.size());
361  if(input_opt.size())
362  cv_opt[0]=0;
363 
364  if(verbose_opt[0]>=1){
365  if(input_opt.size())
366  std::cout << "input filename: " << input_opt[0] << std::endl;
367  std::cout << "training vector file: " << std::endl;
368  for(int ifile=0;ifile<training_opt.size();++ifile)
369  std::cout << training_opt[ifile] << std::endl;
370  std::cout << "verbose: " << verbose_opt[0] << std::endl;
371  }
372 
373  unsigned int totalSamples=0;
374  unsigned int totalTestSamples=0;
375 
376  unsigned short nclass=0;
377  int nband=0;
378  int startBand=2;//first two bands represent X and Y pos
379 
380  vector<double> offset;
381  vector<double> scale;
382  vector< Vector2d<float> > trainingPixels;//[class][sample][band]
383  vector< Vector2d<float> > testPixels;//[class][sample][band]
384 
385  // if(priors_opt.size()>1){//priors from argument list
386  // priors.resize(priors_opt.size());
387  // double normPrior=0;
388  // for(int iclass=0;iclass<priors_opt.size();++iclass){
389  // priors[iclass]=priors_opt[iclass];
390  // normPrior+=priors[iclass];
391  // }
392  // //normalize
393  // for(int iclass=0;iclass<priors_opt.size();++iclass)
394  // priors[iclass]/=normPrior;
395  // }
396 
397  //sort bands
398  if(band_opt.size())
399  std::sort(band_opt.begin(),band_opt.end());
400 
401  // map<string,short> classValueMap;//global variable for now (due to getCost)
402  if(classname_opt.size()){
403  assert(classname_opt.size()==classvalue_opt.size());
404  for(int iclass=0;iclass<classname_opt.size();++iclass)
405  classValueMap[classname_opt[iclass]]=classvalue_opt[iclass];
406  }
407 
408  //----------------------------------- Training -------------------------------
409  struct svm_problem prob;
410  vector<string> fields;
411  //organize training data
412  trainingPixels.clear();
413  testPixels.clear();
414  map<string,Vector2d<float> > trainingMap;
415  map<string,Vector2d<float> > testMap;
416  if(verbose_opt[0]>=1)
417  std::cout << "reading training file " << training_opt[0] << std::endl;
418  try{
419  ImgReaderOgr trainingReader(training_opt[0]);
420  if(band_opt.size()){
421  totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
422  if(input_opt.size()){
423  ImgReaderOgr inputReader(input_opt[0]);
424  totalTestSamples=inputReader.readDataImageOgr(testMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
425  inputReader.close();
426  }
427  }
428  else{
429  totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
430  if(input_opt.size()){
431  ImgReaderOgr inputReader(input_opt[0]);
432  totalTestSamples=inputReader.readDataImageOgr(testMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
433  inputReader.close();
434  }
435  trainingReader.close();
436  }
437  if(trainingMap.size()<2){
438  // map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
439  // while(mapit!=trainingMap.end())
440  // cerr << mapit->first << " -> " << classValueMap[mapit->first] << std::endl;
441  string errorstring="Error: could not read at least two classes from training input file";
442  throw(errorstring);
443  }
444  if(input_opt.size()&&testMap.size()<2){
445  string errorstring="Error: could not read at least two classes from test input file";
446  throw(errorstring);
447  }
448  }
449  catch(string error){
450  cerr << error << std::endl;
451  exit(1);
452  }
453  catch(...){
454  cerr << "error catched" << std::endl;
455  exit(1);
456  }
457  //todo delete class 0 ?
458  // if(verbose_opt[0]>=1)
459  // std::cout << "erasing class 0 from training set (" << trainingMap[0].size() << " from " << totalSamples << ") samples" << std::endl;
460  // totalSamples-=trainingMap[0].size();
461  // trainingMap.erase(0);
462 
463  if(verbose_opt[0]>1)
464  std::cout << "training pixels: " << std::endl;
465  map<string,Vector2d<float> >::iterator mapit;
466  mapit=trainingMap.begin();
467  while(mapit!=trainingMap.end()){
468  if(classValueMap.size()){
469  //check if name in training is covered by classname_opt (values can not be 0)
470  if(classValueMap[mapit->first]>0){
471  if(verbose_opt[0])
472  std::cout << mapit->first << " -> " << classValueMap[mapit->first] << std::endl;
473  }
474  else{
475  std::cerr << "Error: names in classname option are not complete, please check names in training vector and make sure classvalue is > 0" << std::endl;
476  exit(1);
477  }
478  }
479  //delete small classes
480  if((mapit->second).size()<minSize_opt[0]){
481  trainingMap.erase(mapit);
482  continue;
483  }
484  nameVector.push_back(mapit->first);
485  trainingPixels.push_back(mapit->second);
486  if(verbose_opt[0]>1)
487  std::cout << mapit->first << ": " << (mapit->second).size() << " samples" << std::endl;
488  // trainingPixels.push_back(mapit->second); ??
489  // ++iclass;
490  ++mapit;
491  }
492  nclass=trainingPixels.size();
493  if(classname_opt.size())
494  assert(nclass==classname_opt.size());
495  nband=trainingPixels[0][0].size()-2;//X and Y//trainingPixels[0][0].size();
496 
497  mapit=testMap.begin();
498  while(mapit!=testMap.end()){
499  if(classValueMap.size()){
500  //check if name in test is covered by classname_opt (values can not be 0)
501  if(classValueMap[mapit->first]>0){
502  ;//ok, no need to print to std::cout
503  }
504  else{
505  std::cerr << "Error: names in classname option are not complete, please check names in test vector and make sure classvalue is > 0" << std::endl;
506  exit(1);
507  }
508  }
509  //no need to delete small classes for test sample
510  testPixels.push_back(mapit->second);
511  if(verbose_opt[0]>1)
512  std::cout << mapit->first << ": " << (mapit->second).size() << " samples" << std::endl;
513  ++mapit;
514  }
515  if(input_opt.size()){
516  assert(nclass==testPixels.size());
517  assert(nband=testPixels[0][0].size()-2);//X and Y//testPixels[0][0].size();
518  assert(!cv_opt[0]);
519  }
520 
521  //do not remove outliers here: could easily be obtained through ogr2ogr -where 'B2<110' output.shp input.shp
522  //balance training data
523  if(balance_opt[0]>0){
524  if(random_opt[0])
525  srand(time(NULL));
526  totalSamples=0;
527  for(int iclass=0;iclass<nclass;++iclass){
528  if(trainingPixels[iclass].size()>balance_opt[0]){
529  while(trainingPixels[iclass].size()>balance_opt[0]){
530  int index=rand()%trainingPixels[iclass].size();
531  trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
532  }
533  }
534  else{
535  int oldsize=trainingPixels[iclass].size();
536  for(int isample=trainingPixels[iclass].size();isample<balance_opt[0];++isample){
537  int index = rand()%oldsize;
538  trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
539  }
540  }
541  totalSamples+=trainingPixels[iclass].size();
542  }
543  assert(totalSamples==nclass*balance_opt[0]);
544  }
545 
546  //no need to balance test sample
547  //set scale and offset
548  offset.resize(nband);
549  scale.resize(nband);
550  if(offset_opt.size()>1)
551  assert(offset_opt.size()==nband);
552  if(scale_opt.size()>1)
553  assert(scale_opt.size()==nband);
554  for(int iband=0;iband<nband;++iband){
555  if(verbose_opt[0]>1)
556  std::cout << "scaling for band" << iband << std::endl;
557  offset[iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
558  scale[iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
559  //search for min and maximum
560  if(scale[iband]<=0){
561  float theMin=trainingPixels[0][0][iband+startBand];
562  float theMax=trainingPixels[0][0][iband+startBand];
563  for(int iclass=0;iclass<nclass;++iclass){
564  for(int isample=0;isample<trainingPixels[iclass].size();++isample){
565  if(theMin>trainingPixels[iclass][isample][iband+startBand])
566  theMin=trainingPixels[iclass][isample][iband+startBand];
567  if(theMax<trainingPixels[iclass][isample][iband+startBand])
568  theMax=trainingPixels[iclass][isample][iband+startBand];
569  }
570  }
571  offset[iband]=theMin+(theMax-theMin)/2.0;
572  scale[iband]=(theMax-theMin)/2.0;
573  if(verbose_opt[0]>1){
574  std::cout << "Extreme image values for band " << iband << ": [" << theMin << "," << theMax << "]" << std::endl;
575  std::cout << "Using offset, scale: " << offset[iband] << ", " << scale[iband] << std::endl;
576  std::cout << "scaled values for band " << iband << ": [" << (theMin-offset[iband])/scale[iband] << "," << (theMax-offset[iband])/scale[iband] << "]" << std::endl;
577  }
578  }
579  }
580 
581  // if(priors_opt.size()==1){//default: equal priors for each class
582  // priors.resize(nclass);
583  // for(int iclass=0;iclass<nclass;++iclass)
584  // priors[iclass]=1.0/nclass;
585  // }
586  // assert(priors_opt.size()==1||priors_opt.size()==nclass);
587 
588  if(verbose_opt[0]>=1){
589  std::cout << "number of bands: " << nband << std::endl;
590  std::cout << "number of classes: " << nclass << std::endl;
591  // std::cout << "priors:";
592  // for(int iclass=0;iclass<nclass;++iclass)
593  // std::cout << " " << priors[iclass];
594  // std::cout << std::endl;
595  }
596 
597  //Calculate features of training (and test) set
598  nctraining.resize(nclass);
599  nctest.resize(nclass);
600  vector< Vector2d<float> > trainingFeatures(nclass);
601  for(int iclass=0;iclass<nclass;++iclass){
602  if(verbose_opt[0]>=1)
603  std::cout << "calculating features for class " << iclass << std::endl;
604  nctraining[iclass]=trainingPixels[iclass].size();
605  if(verbose_opt[0]>=1)
606  std::cout << "nctraining[" << iclass << "]: " << nctraining[iclass] << std::endl;
607  if(testPixels.size()>iclass){
608  nctest[iclass]=testPixels[iclass].size();
609  if(verbose_opt[0]>=1){
610  std::cout << "nctest[" << iclass << "]: " << nctest[iclass] << std::endl;
611  }
612  }
613  else
614  nctest[iclass]=0;
615  // trainingFeatures[iclass].resize(nctraining[iclass]);
616  trainingFeatures[iclass].resize(nctraining[iclass]+nctest[iclass]);
617  for(int isample=0;isample<nctraining[iclass];++isample){
618  //scale pixel values according to scale and offset!!!
619  for(int iband=0;iband<nband;++iband){
620  assert(trainingPixels[iclass].size()>isample);
621  assert(trainingPixels[iclass][isample].size()>iband+startBand);
622  assert(offset.size()>iband);
623  assert(scale.size()>iband);
624  float value=trainingPixels[iclass][isample][iband+startBand];
625  trainingFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
626  }
627  }
628  // assert(trainingFeatures[iclass].size()==nctraining[iclass]);
629  for(int isample=0;isample<nctest[iclass];++isample){
630  //scale pixel values according to scale and offset!!!
631  for(int iband=0;iband<nband;++iband){
632  assert(testPixels[iclass].size()>isample);
633  assert(testPixels[iclass][isample].size()>iband+startBand);
634  assert(offset.size()>iband);
635  assert(scale.size()>iband);
636  float value=testPixels[iclass][isample][iband+startBand];
637  // testFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
638  trainingFeatures[iclass][nctraining[iclass]+isample].push_back((value-offset[iband])/scale[iband]);
639  }
640  }
641  assert(trainingFeatures[iclass].size()==nctraining[iclass]+nctest[iclass]);
642  }
643 
644  assert(ccost_opt.size()>1);//must have boundaries at least (initial value is optional)
645  if(ccost_opt.size()<3)//create initial value
646  ccost_opt.push_back(sqrt(ccost_opt[0]*ccost_opt[1]));
647  assert(gamma_opt.size()>1);//must have boundaries at least (initial value is optional)
648  if(gamma_opt.size()<3)//create initial value
649  gamma_opt.push_back(sqrt(gamma_opt[0]*gamma_opt[1]));//will be translated to 1.0/nFeatures
650  assert(ccost_opt.size()==3);//min, init, max
651  assert(gamma_opt.size()==3);//min, init, max
652  assert(gamma_opt[0]<gamma_opt[1]);
653  assert(gamma_opt[0]<gamma_opt[2]);
654  assert(gamma_opt[2]<gamma_opt[1]);
655  assert(ccost_opt[0]<ccost_opt[1]);
656  assert(ccost_opt[0]<ccost_opt[2]);
657  assert(ccost_opt[2]<ccost_opt[1]);
658 
659  std::vector<double> x(2);
660  if(algorithm_opt[0]=="GRID"){
661  // double minError=1000;
662  // double minCost=0;
663  // double minGamma=0;
664  double maxKappa=0;
665  double maxCost=0;
666  double maxGamma=0;
667  const char* pszMessage;
668  void* pProgressArg=NULL;
669  GDALProgressFunc pfnProgress=GDALTermProgress;
670  double progress=0;
671  if(!verbose_opt[0])
672  pfnProgress(progress,pszMessage,pProgressArg);
673  double ncost=log(ccost_opt[1])/log(stepcc_opt[0])-log(ccost_opt[0])/log(stepcc_opt[0]);
674  double ngamma=log(gamma_opt[1])/log(stepg_opt[0])-log(gamma_opt[0])/log(stepg_opt[0]);
675  for(double ccost=ccost_opt[0];ccost<=ccost_opt[1];ccost*=stepcc_opt[0]){
676  for(double gamma=gamma_opt[0];gamma<=gamma_opt[1];gamma*=stepg_opt[0]){
677  x[0]=ccost;
678  x[1]=gamma;
679  std::vector<double> theGrad;
680  double kappa=0;
681  kappa=objFunction(x,theGrad,&trainingFeatures);
682  if(kappa>maxKappa){
683  maxKappa=kappa;
684  maxCost=ccost;
685  maxGamma=gamma;
686  }
687  if(verbose_opt[0])
688  std::cout << ccost << " " << gamma << " " << kappa<< std::endl;
689  progress+=1.0/ncost/ngamma;
690  if(!verbose_opt[0])
691  pfnProgress(progress,pszMessage,pProgressArg);
692  }
693  }
694  progress=1.0;
695  if(!verbose_opt[0])
696  pfnProgress(progress,pszMessage,pProgressArg);
697  x[0]=maxCost;
698  x[1]=maxGamma;
699  }
700  else{
701  nlopt::opt optimizer=OptFactory::getOptimizer(algorithm_opt[0],2);
702  if(verbose_opt[0]>1)
703  std::cout << "optimization algorithm: " << optimizer.get_algorithm_name() << "..." << std::endl;
704  std::vector<double> lb(2);
705  std::vector<double> init(2);
706  std::vector<double> ub(2);
707 
708  lb[0]=ccost_opt[0];
709  lb[1]=(gamma_opt[0]>0)? gamma_opt[0] : 1.0/trainingFeatures[0][0].size();
710  init[0]=ccost_opt[2];
711  init[1]=(gamma_opt[2]>0)? gamma_opt[1] : 1.0/trainingFeatures[0][0].size();
712  ub[0]=ccost_opt[1];
713  ub[1]=(gamma_opt[1]>0)? gamma_opt[1] : 1.0/trainingFeatures[0][0].size();
714  // optimizer.set_min_objective(objFunction, &trainingFeatures);
715  optimizer.set_max_objective(objFunction, &trainingFeatures);
716  optimizer.set_lower_bounds(lb);
717  optimizer.set_upper_bounds(ub);
718  if(verbose_opt[0]>1)
719  std::cout << "set stopping criteria" << std::endl;
720  //set stopping criteria
721  if(maxit_opt[0])
722  optimizer.set_maxeval(maxit_opt[0]);
723  else
724  optimizer.set_xtol_rel(tolerance_opt[0]);
725  double minf=0;
726  x=init;
727  try{
728  optimizer.optimize(x, minf);
729  }
730  catch(string error){
731  cerr << error << std::endl;
732  exit(1);
733  }
734  catch (exception& e){
735  cout << e.what() << endl;
736  }
737  catch(...){
738  cerr << "error catched" << std::endl;
739  exit(1);
740  }
741 
742  double ccost=x[0];
743  double gamma=x[1];
744  if(verbose_opt[0])
745  std::cout << "optimized with " << optimizer.get_algorithm_name() << "..." << std::endl;
746  }
747  std::cout << " --ccost " << x[0];
748  std::cout << " --gamma " << x[1];
749  std::cout << std::endl;
750 }