pktools  2.6.3
Processing Kernel for geospatial data
pkfsann.cc
1 /**********************************************************************
2 pkfsann.cc: feature selection for artificial neural network classifier pkann
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 "imageclasses/ImgReaderOgr.h"
27 #include "algorithms/ConfusionMatrix.h"
28 #include "algorithms/CostFactory.h"
29 #include "algorithms/FeatureSelector.h"
30 #include "floatfann.h"
31 #include "algorithms/myfann_cpp.h"
32 #include "pkfsann.h"
33 
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37 
38 using namespace std;
39 
40 #define Malloc(type,n) (type *)malloc((n)*sizeof(type))
41 
42 CostFactoryANN::CostFactoryANN(const vector<unsigned int>& nneuron, float connection, const std::vector<float> weights, float learning, unsigned int maxit, unsigned short cv, bool verbose)
43  : CostFactory(cv,verbose), m_nneuron(nneuron), m_connection(connection), m_weights(weights), m_learning(learning), m_maxit(maxit){};
44 
45 CostFactoryANN::~CostFactoryANN(){
46 }
47 
48 double CostFactoryANN::getCost(const vector<Vector2d<float> > &trainingFeatures)
49 {
50  unsigned short nclass=trainingFeatures.size();
51  unsigned int ntraining=0;
52  unsigned int ntest=0;
53  for(int iclass=0;iclass<nclass;++iclass){
54  ntraining+=m_nctraining[iclass];
55  ntest+=m_nctest[iclass];
56  }
57  if(ntest)
58  assert(!m_cv);
59  if(!m_cv)
60  assert(ntest);
61  unsigned short nFeatures=trainingFeatures[0][0].size();
62 
63  FANN::neural_net net;//the neural network
64  const unsigned int num_layers = m_nneuron.size()+2;
65  const float desired_error = 0.0003;
66  const unsigned int iterations_between_reports = (m_verbose) ? m_maxit+1:0;
67  if(m_verbose>1){
68  cout << "creating artificial neural network with " << m_nneuron.size() << " hidden layer, having " << endl;
69  for(int ilayer=0;ilayer<m_nneuron.size();++ilayer)
70  cout << m_nneuron[ilayer] << " ";
71  cout << "neurons" << endl;
72  }
73  switch(num_layers){
74  case(3):{
75  unsigned int layers[3];
76  layers[0]=nFeatures;
77  layers[1]=m_nneuron[0];
78  layers[2]=nclass;
79  net.create_sparse_array(m_connection,num_layers,layers);
80  break;
81  }
82  case(4):{
83  unsigned int layers[4];
84  layers[0]=nFeatures;
85  layers[1]=m_nneuron[0];
86  layers[2]=m_nneuron[1];
87  layers[3]=nclass;
88  net.create_sparse_array(m_connection,num_layers,layers);
89  break;
90  }
91  default:
92  cerr << "Only 1 or 2 hidden layers are supported!" << endl;
93  exit(1);
94  break;
95  }
96 
97  net.set_learning_rate(m_learning);
98 
99  net.set_activation_function_hidden(FANN::SIGMOID_SYMMETRIC_STEPWISE);
100  net.set_activation_function_output(FANN::SIGMOID_SYMMETRIC_STEPWISE);
101 
102  vector<unsigned short> referenceVector;
103  vector<unsigned short> outputVector;
104  float rmse=0;
105  vector<Vector2d<float> > tmpFeatures(nclass);
106  for(int iclass=0;iclass<nclass;++iclass){
107  tmpFeatures[iclass].resize(trainingFeatures[iclass].size(),nFeatures);
108  for(unsigned int isample=0;isample<m_nctraining[iclass];++isample){
109  for(int ifeature=0;ifeature<nFeatures;++ifeature){
110  tmpFeatures[iclass][isample][ifeature]=trainingFeatures[iclass][isample][ifeature];
111  }
112  }
113  }
114  m_cm.clearResults();
115  if(m_cv>0){
116  rmse=net.cross_validation(tmpFeatures,
117  ntraining,
118  m_cv,
119  m_maxit,
120  desired_error,
121  referenceVector,
122  outputVector,
123  m_verbose);
124  for(int isample=0;isample<referenceVector.size();++isample){
125  string refClassName=m_nameVector[referenceVector[isample]];
126  string className=m_nameVector[outputVector[isample]];
127  if(m_classValueMap.size())
128  m_cm.incrementResult(type2string<short>(m_classValueMap[refClassName]),type2string<short>(m_classValueMap[className]),1.0);
129  else
130  m_cm.incrementResult(m_cm.getClass(referenceVector[isample]),m_cm.getClass(outputVector[isample]),1.0);
131  }
132  }
133  else{//not working yet. please repair...
134  assert(m_cv>0);
135  bool initWeights=true;
136  net.train_on_data(tmpFeatures,ntraining,initWeights, m_maxit,
137  iterations_between_reports, desired_error);
138  vector<Vector2d<float> > testFeatures(nclass);
139  vector<float> result(nclass);
140  int maxClass=-1;
141  for(int iclass=0;iclass<nclass;++iclass){
142  testFeatures.resize(m_nctest[iclass],nFeatures);
143  for(unsigned int isample=0;isample<m_nctraining[iclass];++isample){
144  for(int ifeature=0;ifeature<nFeatures;++ifeature){
145  testFeatures[iclass][isample][ifeature]=trainingFeatures[iclass][m_nctraining[iclass]+isample][ifeature];
146  }
147  result=net.run(testFeatures[iclass][isample]);
148  string refClassName=m_nameVector[iclass];
149  float maxP=-1;
150  for(int ic=0;ic<nclass;++ic){
151  float pv=(result[ic]+1.0)/2.0;//bring back to scale [0,1]
152  if(pv>maxP){
153  maxP=pv;
154  maxClass=ic;
155  }
156  }
157  string className=m_nameVector[maxClass];
158  if(m_classValueMap.size())
159  m_cm.incrementResult(type2string<short>(m_classValueMap[refClassName]),type2string<short>(m_classValueMap[className]),1.0);
160  else
161  m_cm.incrementResult(m_cm.getClass(referenceVector[isample]),m_cm.getClass(outputVector[isample]),1.0);
162  }
163  }
164  }
165  assert(m_cm.nReference());
166  return(m_cm.kappa());
167 }
168 
169 int main(int argc, char *argv[])
170 {
171  // vector<double> priors;
172 
173  //--------------------------- command line options ------------------------------------
174  Optionpk<string> input_opt("i", "input", "input test set (leave empty to perform a cross validation based on training only)");
175  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). Use multiple training files for bootstrap aggregation (alternative to the bag and bsize options, where a random subset is taken from a single training file)");
176  Optionpk<string> tlayer_opt("tln", "tln", "training layer name(s)");
177  Optionpk<string> label_opt("label", "label", "identifier for class label in training vector file.","label");
178  Optionpk<unsigned short> maxFeatures_opt("n", "nf", "number of features to select (0 to select optimal number, see also ecost option)", 0);
179  Optionpk<unsigned int> balance_opt("\0", "balance", "balance the input data to this number of samples for each class", 0);
180  Optionpk<bool> random_opt("random","random", "in case of balance, randomize input data", true);
181  Optionpk<int> minSize_opt("min", "min", "if number of training pixels is less then min, do not take this class into account", 0);
182  Optionpk<short> band_opt("b", "band", "band index (starting from 0, either use band option or use start to end)");
183  Optionpk<double> bstart_opt("s", "start", "start band sequence number",0);
184  Optionpk<double> bend_opt("e", "end", "end band sequence number (set to 0 to include all bands)", 0);
185  Optionpk<double> offset_opt("\0", "offset", "offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
186  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);
187  Optionpk<unsigned short> aggreg_opt("a", "aggreg", "how to combine aggregated classifiers, see also rc option (0: no aggregation, 1: sum rule, 2: max rule).",0);
188  // Optionpk<double> priors_opt("p", "prior", "prior probabilities for each class (e.g., -p 0.3 -p 0.3 -p 0.2 )", 0.0);
189  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");
190  Optionpk<float> epsilon_cost_opt("ecost", "ecost", "epsilon for stopping criterion in cost function to determine optimal number of features",0.001);
191  Optionpk<unsigned short> cv_opt("cv", "cv", "n-fold cross validation mode",2);
192  Optionpk<string> classname_opt("c", "class", "list of class names.");
193  Optionpk<short> classvalue_opt("r", "reclass", "list of class values (use same order as in classname opt.");
194  Optionpk<unsigned int> nneuron_opt("n", "nneuron", "number of neurons in hidden layers in neural network (multiple hidden layers are set by defining multiple number of neurons: -n 15 -n 1, default is one hidden layer with 5 neurons)", 5);
195  Optionpk<float> connection_opt("\0", "connection", "connection reate (default: 1.0 for a fully connected network)", 1.0);
196  Optionpk<float> weights_opt("w", "weights", "weights for neural network. Apply to fully connected network only, starting from first input neuron to last output neuron, including the bias neurons (last neuron in each but last layer)", 0.0);
197  Optionpk<float> learning_opt("l", "learning", "learning rate (default: 0.7)", 0.7);
198  Optionpk<unsigned int> maxit_opt("\0", "maxit", "number of maximum iterations (epoch) (default: 500)", 500);
199  Optionpk<short> verbose_opt("v", "verbose", "set to: 0 (results only), 1 (confusion matrix), 2 (debug)",0,2);
200 
201  tlayer_opt.setHide(1);
202  label_opt.setHide(1);
203  balance_opt.setHide(1);
204  random_opt.setHide(1);
205  minSize_opt.setHide(1);
206  band_opt.setHide(1);
207  bstart_opt.setHide(1);
208  bend_opt.setHide(1);
209  offset_opt.setHide(1);
210  scale_opt.setHide(1);
211  aggreg_opt.setHide(1);
212  // priors_opt.setHide(1);
213  selector_opt.setHide(1);
214  epsilon_cost_opt.setHide(1);
215  cv_opt.setHide(1);
216  classname_opt.setHide(1);
217  classvalue_opt.setHide(1);
218  nneuron_opt.setHide(1);
219  connection_opt.setHide(1);
220  weights_opt.setHide(1);
221  learning_opt.setHide(1);
222  maxit_opt.setHide(1);
223 
224  bool doProcess;//stop process when program was invoked with help option (-h --help)
225  try{
226  doProcess=input_opt.retrieveOption(argc,argv);
227  training_opt.retrieveOption(argc,argv);
228  maxFeatures_opt.retrieveOption(argc,argv);
229  tlayer_opt.retrieveOption(argc,argv);
230  label_opt.retrieveOption(argc,argv);
231  balance_opt.retrieveOption(argc,argv);
232  random_opt.retrieveOption(argc,argv);
233  minSize_opt.retrieveOption(argc,argv);
234  band_opt.retrieveOption(argc,argv);
235  bstart_opt.retrieveOption(argc,argv);
236  bend_opt.retrieveOption(argc,argv);
237  offset_opt.retrieveOption(argc,argv);
238  scale_opt.retrieveOption(argc,argv);
239  aggreg_opt.retrieveOption(argc,argv);
240  // priors_opt.retrieveOption(argc,argv);
241  selector_opt.retrieveOption(argc,argv);
242  epsilon_cost_opt.retrieveOption(argc,argv);
243  cv_opt.retrieveOption(argc,argv);
244  classname_opt.retrieveOption(argc,argv);
245  classvalue_opt.retrieveOption(argc,argv);
246  nneuron_opt.retrieveOption(argc,argv);
247  connection_opt.retrieveOption(argc,argv);
248  weights_opt.retrieveOption(argc,argv);
249  learning_opt.retrieveOption(argc,argv);
250  maxit_opt.retrieveOption(argc,argv);
251  verbose_opt.retrieveOption(argc,argv);
252  }
253  catch(string predefinedString){
254  std::cout << predefinedString << std::endl;
255  exit(0);
256  }
257  if(!doProcess){
258  cout << endl;
259  cout << "Usage: pkfsann -t training -n number" << endl;
260  cout << endl;
261  std::cout << "short option -h shows basic options only, use long option --help to show all options" << std::endl;
262  exit(0);//help was invoked, stop processing
263  }
264 
265  CostFactoryANN costfactory(nneuron_opt, connection_opt[0], weights_opt, learning_opt[0], maxit_opt[0], cv_opt[0], verbose_opt[0]);
266 
267  assert(training_opt.size());
268  if(input_opt.size())
269  costfactory.setCv(0);
270  if(verbose_opt[0]>=1){
271  if(input_opt.size())
272  std::cout << "input filename: " << input_opt[0] << std::endl;
273  std::cout << "training vector file: " << std::endl;
274  for(int ifile=0;ifile<training_opt.size();++ifile)
275  std::cout << training_opt[ifile] << std::endl;
276  std::cout << "verbose: " << verbose_opt[0] << std::endl;
277  }
278 
279  static std::map<std::string, SelectorValue> selMap;
280  //initialize selMap
281  selMap["sffs"]=SFFS;
282  selMap["sfs"]=SFS;
283  selMap["sbs"]=SBS;
284  selMap["bfs"]=BFS;
285 
286  assert(training_opt.size());
287  if(input_opt.size())
288  cv_opt[0]=0;
289  if(verbose_opt[0]>=1)
290  std::cout << "training vector file: " << training_opt[0] << std::endl;
291 
292  unsigned int totalSamples=0;
293  unsigned int totalTestSamples=0;
294 
295  unsigned short nclass=0;
296  int nband=0;
297  int startBand=2;//first two bands represent X and Y pos
298 
299  // if(priors_opt.size()>1){//priors from argument list
300  // priors.resize(priors_opt.size());
301  // double normPrior=0;
302  // for(int iclass=0;iclass<priors_opt.size();++iclass){
303  // priors[iclass]=priors_opt[iclass];
304  // normPrior+=priors[iclass];
305  // }
306  // //normalize
307  // for(int iclass=0;iclass<priors_opt.size();++iclass)
308  // priors[iclass]/=normPrior;
309  // }
310 
311  //sort bands
312  if(band_opt.size())
313  std::sort(band_opt.begin(),band_opt.end());
314 
315  // map<string,short> classValueMap;//global variable for now (due to getCost)
316  if(classname_opt.size()){
317  assert(classname_opt.size()==classvalue_opt.size());
318  for(int iclass=0;iclass<classname_opt.size();++iclass)
319  costfactory.setClassValueMap(classname_opt[iclass],classvalue_opt[iclass]);
320  }
321  //----------------------------------- Training -------------------------------
322  vector<double> offset;
323  vector<double> scale;
324  vector< Vector2d<float> > trainingPixels;//[class][sample][band]
325  vector< Vector2d<float> > testPixels;//[class][sample][band]
326  map<string,Vector2d<float> > trainingMap;
327  map<string,Vector2d<float> > testMap;
328  vector<string> fields;
329 
330  //organize training data
331  trainingPixels.clear();
332  if(verbose_opt[0]>=1)
333  std::cout << "reading imageVector file " << training_opt[0] << std::endl;
334  try{
335  ImgReaderOgr trainingReader(training_opt[0]);
336  if(band_opt.size()){
337  totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
338  if(input_opt.size()){
339  ImgReaderOgr inputReader(input_opt[0]);
340  totalTestSamples=trainingReader.readDataImageOgr(testMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
341  inputReader.close();
342  }
343  }
344  else{
345  totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
346  if(input_opt.size()){
347  ImgReaderOgr inputReader(input_opt[0]);
348  totalTestSamples=trainingReader.readDataImageOgr(testMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
349  inputReader.close();
350  }
351  }
352  if(trainingMap.size()<2){
353  string errorstring="Error: could not read at least two classes from training file";
354  throw(errorstring);
355  }
356  if(input_opt.size()&&testMap.size()<2){
357  string errorstring="Error: could not read at least two classes from test input file";
358  throw(errorstring);
359  }
360  trainingReader.close();
361  }
362  catch(string error){
363  cerr << error << std::endl;
364  exit(1);
365  }
366  catch(std::exception& e){
367  std::cerr << "Error: ";
368  std::cerr << e.what() << std::endl;
369  std::cerr << CPLGetLastErrorMsg() << std::endl;
370  exit(1);
371  }
372  catch(...){
373  cerr << "error catched" << std::endl;
374  exit(1);
375  }
376  //delete class 0 ?
377  // if(verbose_opt[0]>=1)
378  // std::cout << "erasing class 0 from training set (" << trainingMap[0].size() << " from " << totalSamples << ") samples" << std::endl;
379  // totalSamples-=trainingMap[0].size();
380  // trainingMap.erase(0);
381  //convert map to vector
382 
383  if(verbose_opt[0]>1)
384  std::cout << "training pixels: " << std::endl;
385  map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
386  while(mapit!=trainingMap.end()){
387  // if(classValueMap.size()){
388  // //check if name in training is covered by classname_opt (values can not be 0)
389  // if(classValueMap[mapit->first]>0){
390  // if(verbose_opt[0])
391  // std::cout << mapit->first << " -> " << classValueMap[mapit->first] << std::endl;
392  // }
393  // else{
394  // std::cerr << "Error: names in classname option are not complete, please check names in training vector and make sure classvalue is > 0" << std::endl;
395  // exit(1);
396  // }
397  // }
398  //delete small classes
399  if((mapit->second).size()<minSize_opt[0]){
400  trainingMap.erase(mapit);
401  continue;
402  }
403  trainingPixels.push_back(mapit->second);
404  if(verbose_opt[0]>1)
405  std::cout << mapit->first << ": " << (mapit->second).size() << " samples" << std::endl;
406  ++mapit;
407  }
408  nclass=trainingPixels.size();
409  if(classname_opt.size())
410  assert(nclass==classname_opt.size());
411  nband=trainingPixels[0][0].size()-2;//X and Y//trainingPixels[0][0].size();
412 
413  mapit=testMap.begin();
414  while(mapit!=testMap.end()){
415  //no need to delete small classes for test sample
416  testPixels.push_back(mapit->second);
417  if(verbose_opt[0]>1)
418  std::cout << mapit->first << ": " << (mapit->second).size() << " samples" << std::endl;
419  ++mapit;
420  }
421  if(input_opt.size()){
422  assert(nclass==testPixels.size());
423  assert(nband=testPixels[0][0].size()-2);//X and Y//testPixels[0][0].size();
424  assert(!cv_opt[0]);
425  }
426 
427  //do not remove outliers here: could easily be obtained through ogr2ogr -where 'B2<110' output.shp input.shp
428  //balance training data
429  if(balance_opt[0]>0){
430  if(random_opt[0])
431  srand(time(NULL));
432  totalSamples=0;
433  for(int iclass=0;iclass<nclass;++iclass){
434  if(trainingPixels[iclass].size()>balance_opt[0]){
435  while(trainingPixels[iclass].size()>balance_opt[0]){
436  int index=rand()%trainingPixels[iclass].size();
437  trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
438  }
439  }
440  else{
441  int oldsize=trainingPixels[iclass].size();
442  for(int isample=trainingPixels[iclass].size();isample<balance_opt[0];++isample){
443  int index = rand()%oldsize;
444  trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
445  }
446  }
447  totalSamples+=trainingPixels[iclass].size();
448  }
449  assert(totalSamples==nclass*balance_opt[0]);
450  }
451 
452  //set scale and offset
453  offset.resize(nband);
454  scale.resize(nband);
455  if(offset_opt.size()>1)
456  assert(offset_opt.size()==nband);
457  if(scale_opt.size()>1)
458  assert(scale_opt.size()==nband);
459  for(int iband=0;iband<nband;++iband){
460  if(verbose_opt[0]>1)
461  std::cout << "scaling for band" << iband << std::endl;
462  offset[iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
463  scale[iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
464  //search for min and maximum
465  if(scale[iband]<=0){
466  float theMin=trainingPixels[0][0][iband+startBand];
467  float theMax=trainingPixels[0][0][iband+startBand];
468  for(int iclass=0;iclass<nclass;++iclass){
469  for(int isample=0;isample<trainingPixels[iclass].size();++isample){
470  if(theMin>trainingPixels[iclass][isample][iband+startBand])
471  theMin=trainingPixels[iclass][isample][iband+startBand];
472  if(theMax<trainingPixels[iclass][isample][iband+startBand])
473  theMax=trainingPixels[iclass][isample][iband+startBand];
474  }
475  }
476  offset[iband]=theMin+(theMax-theMin)/2.0;
477  scale[iband]=(theMax-theMin)/2.0;
478  if(verbose_opt[0]>1){
479  std::cout << "Extreme image values for band " << iband << ": [" << theMin << "," << theMax << "]" << std::endl;
480  std::cout << "Using offset, scale: " << offset[iband] << ", " << scale[iband] << std::endl;
481  std::cout << "scaled values for band " << iband << ": [" << (theMin-offset[iband])/scale[iband] << "," << (theMax-offset[iband])/scale[iband] << "]" << std::endl;
482  }
483  }
484  }
485 
486  // if(priors_opt.size()==1){//default: equal priors for each class
487  // priors.resize(nclass);
488  // for(int iclass=0;iclass<nclass;++iclass)
489  // priors[iclass]=1.0/nclass;
490  // }
491  // assert(priors_opt.size()==1||priors_opt.size()==nclass);
492 
493  if(verbose_opt[0]>=1){
494  std::cout << "number of bands: " << nband << std::endl;
495  std::cout << "number of classes: " << nclass << std::endl;
496  // std::cout << "priors:";
497  // for(int iclass=0;iclass<nclass;++iclass)
498  // std::cout << " " << priors[iclass];
499  // std::cout << std::endl;
500  }
501 
502  //set names in confusion matrix using nameVector
503  vector<string> nameVector=costfactory.getNameVector();
504  for(int iname=0;iname<nameVector.size();++iname){
505  if(costfactory.getClassValueMap().empty())
506  costfactory.pushBackClassName(nameVector[iname]);
507  // cm.pushBackClassName(nameVector[iname]);
508  else if(costfactory.getClassIndex(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]))<0)
509  costfactory.pushBackClassName(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]));
510  }
511 
512  // if(classname_opt.empty()){
513  // for(int iclass=0;iclass<nclass;++iclass){
514  // if(verbose_opt[0])
515  // std::cout << iclass << " " << cm.getClass(iclass) << " -> " << string2type<short>(cm.getClass(iclass)) << std::endl;
516  // classValueMap[cm.getClass(iclass)]=string2type<short>(cm.getClass(iclass));
517  // }
518  // }
519 
520  //Calculate features of trainig set
521 
522  vector<unsigned int> nctraining;
523  vector<unsigned int> nctest;
524  nctraining.resize(nclass);
525  nctest.resize(nclass);
526  vector< Vector2d<float> > trainingFeatures(nclass);
527  for(int iclass=0;iclass<nclass;++iclass){
528  if(verbose_opt[0]>=1)
529  std::cout << "calculating features for class " << iclass << std::endl;
530  nctraining[iclass]=trainingPixels[iclass].size();
531  if(verbose_opt[0]>=1)
532  std::cout << "nctraining[" << iclass << "]: " << nctraining[iclass] << std::endl;
533  if(testPixels.size()>iclass){
534  nctest[iclass]=testPixels[iclass].size();
535  if(verbose_opt[0]>=1){
536  std::cout << "nctest[" << iclass << "]: " << nctest[iclass] << std::endl;
537  }
538  }
539  else
540  nctest[iclass]=0;
541 
542  trainingFeatures[iclass].resize(nctraining[iclass]+nctest[iclass]);
543  for(int isample=0;isample<nctraining[iclass];++isample){
544  //scale pixel values according to scale and offset!!!
545  for(int iband=0;iband<nband;++iband){
546  assert(trainingPixels[iclass].size()>isample);
547  assert(trainingPixels[iclass][isample].size()>iband+startBand);
548  assert(offset.size()>iband);
549  assert(scale.size()>iband);
550  float value=trainingPixels[iclass][isample][iband+startBand];
551  trainingFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
552  }
553  }
554  for(int isample=0;isample<nctest[iclass];++isample){
555  //scale pixel values according to scale and offset!!!
556  for(int iband=0;iband<nband;++iband){
557  assert(testPixels[iclass].size()>isample);
558  assert(testPixels[iclass][isample].size()>iband+startBand);
559  assert(offset.size()>iband);
560  assert(scale.size()>iband);
561  float value=testPixels[iclass][isample][iband+startBand];
562  // testFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
563  trainingFeatures[iclass][nctraining[iclass]+isample].push_back((value-offset[iband])/scale[iband]);
564  }
565  }
566  assert(trainingFeatures[iclass].size()==nctraining[iclass]+nctest[iclass]);
567  }
568 
569  costfactory.setNcTraining(nctraining);
570  costfactory.setNcTest(nctest);
571  int nFeatures=trainingFeatures[0][0].size();
572  int maxFeatures=(maxFeatures_opt[0])? maxFeatures_opt[0] : 1;
573  double previousCost=-1;
574  double cost=0;
575  list<int> subset;//set of selected features (levels) for each class combination
576  FeatureSelector selector;
577  try{
578  if(maxFeatures>=nFeatures){
579  subset.clear();
580  for(int ifeature=0;ifeature<nFeatures;++ifeature)
581  subset.push_back(ifeature);
582  cost=costfactory.getCost(trainingFeatures);
583  }
584  else{
585  while(fabs(cost-previousCost)>=epsilon_cost_opt[0]){
586  previousCost=cost;
587  switch(selMap[selector_opt[0]]){
588  case(SFFS):
589  subset.clear();//needed to clear in case of floating and brute force search
590  cost=selector.floating(trainingFeatures,costfactory,subset,maxFeatures,epsilon_cost_opt[0],verbose_opt[0]);
591  break;
592  case(SFS):
593  cost=selector.forward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
594  break;
595  case(SBS):
596  cost=selector.backward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
597  break;
598  case(BFS):
599  subset.clear();//needed to clear in case of floating and brute force search
600  cost=selector.bruteForce(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
601  break;
602  default:
603  std::cout << "Error: selector not supported, please use sffs, sfs, sbs or bfs" << std::endl;
604  exit(1);
605  break;
606  }
607  if(verbose_opt[0]>1){
608  std::cout << "cost: " << cost << std::endl;
609  std::cout << "previousCost: " << previousCost << std::endl;
610  std::cout << std::setprecision(12) << "cost-previousCost: " << cost - previousCost << " ( " << epsilon_cost_opt[0] << ")" << std::endl;
611  }
612  if(!maxFeatures_opt[0])
613  ++maxFeatures;
614  else
615  break;
616  }
617  }
618  }
619  catch(...){
620  std::cout << "catched feature selection" << std::endl;
621  exit(1);
622  }
623 
624  if(verbose_opt[0])
625  cout <<"cost: " << cost << endl;
626  subset.sort();
627  for(list<int>::const_iterator lit=subset.begin();lit!=subset.end();++lit)
628  std::cout << " -b " << *lit;
629  std::cout << std::endl;
630  // if((*(lit))!=subset.back())
631  // else
632  // cout << endl;
633 
634  // *NOTE* Because svm_model contains pointers to svm_problem, you can
635  // not free the memory used by svm_problem if you are still using the
636  // svm_model produced by svm_train().
637 
638  // free(prob.y);
639  // free(prob.x);
640  // free(x_space);
641  // svm_destroy_param(&param);
642  return 0;
643 }
644