pktools  2.6.3
Processing Kernel for geospatial data
pksvm.cc
1 /**********************************************************************
2 pksvm.cc: classify raster image using Support Vector Machine
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 <map>
23 #include <algorithm>
24 #include "imageclasses/ImgReaderGdal.h"
25 #include "imageclasses/ImgWriterGdal.h"
26 #include "imageclasses/ImgReaderOgr.h"
27 #include "imageclasses/ImgWriterOgr.h"
28 #include "base/Optionpk.h"
29 #include "base/PosValue.h"
30 #include "algorithms/ConfusionMatrix.h"
31 #include "algorithms/svm.h"
32 
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36 /******************************************************************************/
119 namespace svm{
120  enum SVM_TYPE {C_SVC=0, nu_SVC=1,one_class=2, epsilon_SVR=3, nu_SVR=4};
121  enum KERNEL_TYPE {linear=0,polynomial=1,radial=2,sigmoid=3};
122 }
123 
124 #define Malloc(type,n) (type *)malloc((n)*sizeof(type))
125 
126 using namespace std;
127 
128 int main(int argc, char *argv[])
129 {
130  vector<double> priors;
131 
132  //--------------------------- command line options ------------------------------------
133  Optionpk<string> input_opt("i", "input", "input image");
134  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)");
135  Optionpk<string> tlayer_opt("tln", "tln", "Training layer name(s)");
136  Optionpk<string> label_opt("label", "label", "Attribute name for class label in training vector file.","label");
137  Optionpk<unsigned int> balance_opt("bal", "balance", "Balance the input data to this number of samples for each class", 0);
138  Optionpk<bool> random_opt("random", "random", "Randomize training data for balancing and bagging", true, 2);
139  Optionpk<int> minSize_opt("min", "min", "If number of training pixels is less then min, do not take this class into account (0: consider all classes)", 0);
140  Optionpk<short> band_opt("b", "band", "Band index (starting from 0, either use band option or use start to end)");
141  Optionpk<double> bstart_opt("s", "start", "Start band sequence number",0);
142  Optionpk<double> bend_opt("e", "end", "End band sequence number (set to 0 to include all bands)", 0);
143  Optionpk<double> offset_opt("\0", "offset", "Offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
144  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);
145  Optionpk<double> priors_opt("prior", "prior", "Prior probabilities for each class (e.g., -p 0.3 -p 0.3 -p 0.2 ). Used for input only (ignored for cross validation)", 0.0);
146  Optionpk<string> priorimg_opt("pim", "priorimg", "Prior probability image (multi-band img with band for each class","",2);
147  Optionpk<unsigned short> cv_opt("cv", "cv", "N-fold cross validation mode",0);
148  Optionpk<string> cmformat_opt("cmf","cmf","Format for confusion matrix (ascii or latex)","ascii");
149  Optionpk<std::string> svm_type_opt("svmt", "svmtype", "Type of SVM (C_SVC, nu_SVC,one_class, epsilon_SVR, nu_SVR)","C_SVC");
150  Optionpk<std::string> kernel_type_opt("kt", "kerneltype", "Type of kernel function (linear,polynomial,radial,sigmoid) ","radial");
151  Optionpk<unsigned short> kernel_degree_opt("kd", "kd", "Degree in kernel function",3);
152  Optionpk<float> gamma_opt("g", "gamma", "Gamma in kernel function",1.0);
153  Optionpk<float> coef0_opt("c0", "coef0", "Coef0 in kernel function",0);
154  Optionpk<float> ccost_opt("cc", "ccost", "The parameter C of C_SVC, epsilon_SVR, and nu_SVR",1000);
155  Optionpk<float> nu_opt("nu", "nu", "The parameter nu of nu_SVC, one_class SVM, and nu_SVR",0.5);
156  Optionpk<float> epsilon_loss_opt("eloss", "eloss", "The epsilon in loss function of epsilon_SVR",0.1);
157  Optionpk<int> cache_opt("cache", "cache", "Cache memory size in MB",100);
158  Optionpk<float> epsilon_tol_opt("etol", "etol", "The tolerance of termination criterion",0.001);
159  Optionpk<bool> shrinking_opt("shrink", "shrink", "Whether to use the shrinking heuristics",false);
160  Optionpk<bool> prob_est_opt("pe", "probest", "Whether to train a SVC or SVR model for probability estimates",true,2);
161  // Optionpk<bool> weight_opt("wi", "wi", "Set the parameter C of class i to weight*C, for C_SVC",true);
162  Optionpk<unsigned short> comb_opt("comb", "comb", "How to combine bootstrap aggregation classifiers (0: sum rule, 1: product rule, 2: max rule). Also used to aggregate classes with rc option.",0);
163  Optionpk<unsigned short> bag_opt("bag", "bag", "Number of bootstrap aggregations", 1);
164  Optionpk<int> bagSize_opt("bagsize", "bagsize", "Percentage of features used from available training features for each bootstrap aggregation (one size for all classes, or a different size for each class respectively", 100);
165  Optionpk<string> classBag_opt("cb", "classbag", "Output for each individual bootstrap aggregation");
166  Optionpk<string> mask_opt("m", "mask", "Use the first band of the specified file as a validity mask. Nodata values can be set with the option msknodata.");
167  Optionpk<short> msknodata_opt("msknodata", "msknodata", "Mask value(s) not to consider for classification (use negative values if only these values should be taken into account). Values will be taken over in classification image.", 0);
168  Optionpk<unsigned short> nodata_opt("nodata", "nodata", "Nodata value to put where image is masked as nodata", 0);
169  Optionpk<string> output_opt("o", "output", "Output classification image");
170  Optionpk<string> oformat_opt("of", "oformat", "Output image format (see also gdal_translate). Empty string: inherit from input image");
171  Optionpk<string> option_opt("co", "co", "Creation option for output file. Multiple options can be specified.");
172  Optionpk<string> colorTable_opt("ct", "ct", "Color table in ASCII format having 5 columns: id R G B ALFA (0: transparent, 255: solid)");
173  Optionpk<string> prob_opt("prob", "prob", "Probability image.");
174  Optionpk<string> entropy_opt("entropy", "entropy", "Entropy image (measure for uncertainty of classifier output","",2);
175  Optionpk<string> active_opt("active", "active", "Ogr output for active training sample.","",2);
176  Optionpk<string> ogrformat_opt("f", "f", "Output ogr format for active training sample","SQLite");
177  Optionpk<unsigned int> nactive_opt("na", "nactive", "Number of active training points",1);
178  Optionpk<string> classname_opt("c", "class", "List of class names.");
179  Optionpk<short> classvalue_opt("r", "reclass", "List of class values (use same order as in class opt).");
180  Optionpk<string> extent_opt("extent", "extent", "get boundary to classify from extent from polygons in vector file");
181  Optionpk<short> verbose_opt("v", "verbose", "Verbose level",0,2);
182 
183  band_opt.setHide(1);
184  bstart_opt.setHide(1);
185  bend_opt.setHide(1);
186  balance_opt.setHide(1);
187  minSize_opt.setHide(1);
188  bag_opt.setHide(1);
189  bagSize_opt.setHide(1);
190  comb_opt.setHide(1);
191  classBag_opt.setHide(1);
192  prob_opt.setHide(1);
193  priorimg_opt.setHide(1);
194  offset_opt.setHide(1);
195  scale_opt.setHide(1);
196  svm_type_opt.setHide(1);
197  kernel_type_opt.setHide(1);
198  kernel_degree_opt.setHide(1);
199  coef0_opt.setHide(1);
200  nu_opt.setHide(1);
201  epsilon_loss_opt.setHide(1);
202  cache_opt.setHide(1);
203  epsilon_tol_opt.setHide(1);
204  shrinking_opt.setHide(1);
205  prob_est_opt.setHide(1);
206  entropy_opt.setHide(1);
207  active_opt.setHide(1);
208  nactive_opt.setHide(1);
209  random_opt.setHide(1);
210  extent_opt.setHide(1);
211 
212  verbose_opt.setHide(2);
213 
214  bool doProcess;//stop process when program was invoked with help option (-h --help)
215  try{
216  doProcess=training_opt.retrieveOption(argc,argv);
217  input_opt.retrieveOption(argc,argv);
218  output_opt.retrieveOption(argc,argv);
219  cv_opt.retrieveOption(argc,argv);
220  cmformat_opt.retrieveOption(argc,argv);
221  tlayer_opt.retrieveOption(argc,argv);
222  classname_opt.retrieveOption(argc,argv);
223  classvalue_opt.retrieveOption(argc,argv);
224  oformat_opt.retrieveOption(argc,argv);
225  ogrformat_opt.retrieveOption(argc,argv);
226  option_opt.retrieveOption(argc,argv);
227  colorTable_opt.retrieveOption(argc,argv);
228  label_opt.retrieveOption(argc,argv);
229  priors_opt.retrieveOption(argc,argv);
230  gamma_opt.retrieveOption(argc,argv);
231  ccost_opt.retrieveOption(argc,argv);
232  mask_opt.retrieveOption(argc,argv);
233  msknodata_opt.retrieveOption(argc,argv);
234  nodata_opt.retrieveOption(argc,argv);
235  // Advanced options
236  band_opt.retrieveOption(argc,argv);
237  bstart_opt.retrieveOption(argc,argv);
238  bend_opt.retrieveOption(argc,argv);
239  balance_opt.retrieveOption(argc,argv);
240  minSize_opt.retrieveOption(argc,argv);
241  bag_opt.retrieveOption(argc,argv);
242  bagSize_opt.retrieveOption(argc,argv);
243  comb_opt.retrieveOption(argc,argv);
244  classBag_opt.retrieveOption(argc,argv);
245  prob_opt.retrieveOption(argc,argv);
246  priorimg_opt.retrieveOption(argc,argv);
247  offset_opt.retrieveOption(argc,argv);
248  scale_opt.retrieveOption(argc,argv);
249  svm_type_opt.retrieveOption(argc,argv);
250  kernel_type_opt.retrieveOption(argc,argv);
251  kernel_degree_opt.retrieveOption(argc,argv);
252  coef0_opt.retrieveOption(argc,argv);
253  nu_opt.retrieveOption(argc,argv);
254  epsilon_loss_opt.retrieveOption(argc,argv);
255  cache_opt.retrieveOption(argc,argv);
256  epsilon_tol_opt.retrieveOption(argc,argv);
257  shrinking_opt.retrieveOption(argc,argv);
258  prob_est_opt.retrieveOption(argc,argv);
259  entropy_opt.retrieveOption(argc,argv);
260  active_opt.retrieveOption(argc,argv);
261  nactive_opt.retrieveOption(argc,argv);
262  verbose_opt.retrieveOption(argc,argv);
263  random_opt.retrieveOption(argc,argv);
264  extent_opt.retrieveOption(argc,argv);
265  }
266  catch(string predefinedString){
267  std::cout << predefinedString << std::endl;
268  exit(0);
269  }
270  if(!doProcess){
271  cout << endl;
272  cout << "Usage: pksvm -t training [-i input -o output] [-cv value]" << endl;
273  cout << endl;
274  std::cout << "short option -h shows basic options only, use long option --help to show all options" << std::endl;
275  exit(0);//help was invoked, stop processing
276  }
277 
278  if(entropy_opt[0]=="")
279  entropy_opt.clear();
280  if(active_opt[0]=="")
281  active_opt.clear();
282  if(priorimg_opt[0]=="")
283  priorimg_opt.clear();
284 
285 
286  std::map<std::string, svm::SVM_TYPE> svmMap;
287 
288  svmMap["C_SVC"]=svm::C_SVC;
289  svmMap["nu_SVC"]=svm::nu_SVC;
290  svmMap["one_class"]=svm::one_class;
291  svmMap["epsilon_SVR"]=svm::epsilon_SVR;
292  svmMap["nu_SVR"]=svm::nu_SVR;
293 
294  std::map<std::string, svm::KERNEL_TYPE> kernelMap;
295 
296  kernelMap["linear"]=svm::linear;
297  kernelMap["polynomial"]=svm::polynomial;
298  kernelMap["radial"]=svm::radial;
299  kernelMap["sigmoid;"]=svm::sigmoid;
300 
301  assert(training_opt.size());
302 
303  if(verbose_opt[0]>=1){
304  if(input_opt.size())
305  std::cout << "input filename: " << input_opt[0] << std::endl;
306  if(mask_opt.size())
307  std::cout << "mask filename: " << mask_opt[0] << std::endl;
308  std::cout << "training vector file: " << std::endl;
309  for(int ifile=0;ifile<training_opt.size();++ifile)
310  std::cout << training_opt[ifile] << std::endl;
311  std::cout << "verbose: " << verbose_opt[0] << std::endl;
312  }
313  unsigned short nbag=(training_opt.size()>1)?training_opt.size():bag_opt[0];
314  if(verbose_opt[0]>=1)
315  std::cout << "number of bootstrap aggregations: " << nbag << std::endl;
316 
317  ImgReaderOgr extentReader;
318  OGRLayer *readLayer;
319  OGRFeature *readFeature;
320  OGRPoint thePoint;
321 
322  double ulx=0;
323  double uly=0;
324  double lrx=0;
325  double lry=0;
326  if(extent_opt.size()){
327  extentReader.open(extent_opt[0]);
328  readLayer = extentReader.getDataSource()->GetLayer(0);
329  if(!(extentReader.getExtent(ulx,uly,lrx,lry))){
330  cerr << "Error: could not get extent from " << extent_opt[0] << endl;
331  exit(1);
332  }
333  }
334 
335  ImgWriterOgr activeWriter;
336  if(active_opt.size()){
337  prob_est_opt[0]=true;
338  ImgReaderOgr trainingReader(training_opt[0]);
339  activeWriter.open(active_opt[0],ogrformat_opt[0]);
340  activeWriter.createLayer(active_opt[0],trainingReader.getProjection(),wkbPoint,NULL);
341  activeWriter.copyFields(trainingReader);
342  }
343  vector<PosValue> activePoints(nactive_opt[0]);
344  for(int iactive=0;iactive<activePoints.size();++iactive){
345  activePoints[iactive].value=1.0;
346  activePoints[iactive].posx=0.0;
347  activePoints[iactive].posy=0.0;
348  }
349 
350  unsigned int totalSamples=0;
351  unsigned int nactive=0;
352  vector<struct svm_model*> svm(nbag);
353  vector<struct svm_parameter> param(nbag);
354 
355  short nclass=0;
356  int nband=0;
357  int startBand=2;//first two bands represent X and Y pos
358 
359  //normalize priors from command line
360  if(priors_opt.size()>1){//priors from argument list
361  priors.resize(priors_opt.size());
362  double normPrior=0;
363  for(short iclass=0;iclass<priors_opt.size();++iclass){
364  priors[iclass]=priors_opt[iclass];
365  normPrior+=priors[iclass];
366  }
367  //normalize
368  for(short iclass=0;iclass<priors_opt.size();++iclass)
369  priors[iclass]/=normPrior;
370  }
371 
372  //sort bands
373  if(band_opt.size())
374  std::sort(band_opt.begin(),band_opt.end());
375 
376  map<string,short> classValueMap;
377  vector<std::string> nameVector;
378  if(classname_opt.size()){
379  assert(classname_opt.size()==classvalue_opt.size());
380  for(int iclass=0;iclass<classname_opt.size();++iclass)
381  classValueMap[classname_opt[iclass]]=classvalue_opt[iclass];
382  }
383 
384  //----------------------------------- Training -------------------------------
386  vector< vector<double> > offset(nbag);
387  vector< vector<double> > scale(nbag);
388  map<string,Vector2d<float> > trainingMap;
389  vector< Vector2d<float> > trainingPixels;//[class][sample][band]
390  vector<string> fields;
391 
392  vector<struct svm_problem> prob(nbag);
393  vector<struct svm_node *> x_space(nbag);
394 
395  for(int ibag=0;ibag<nbag;++ibag){
396  //organize training data
397  if(ibag<training_opt.size()){//if bag contains new training pixels
398  trainingMap.clear();
399  trainingPixels.clear();
400  if(verbose_opt[0]>=1)
401  std::cout << "reading imageVector file " << training_opt[0] << std::endl;
402  try{
403  ImgReaderOgr trainingReaderBag(training_opt[ibag]);
404  if(band_opt.size())
405  totalSamples=trainingReaderBag.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
406  else
407  totalSamples=trainingReaderBag.readDataImageOgr(trainingMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
408  if(trainingMap.size()<2){
409  string errorstring="Error: could not read at least two classes from training file, did you provide class labels in training sample (see option label)?";
410  throw(errorstring);
411  }
412  trainingReaderBag.close();
413  }
414  catch(string error){
415  cerr << error << std::endl;
416  exit(1);
417  }
418  catch(std::exception& e){
419  std::cerr << "Error: ";
420  std::cerr << e.what() << std::endl;
421  std::cerr << CPLGetLastErrorMsg() << std::endl;
422  exit(1);
423  }
424  catch(...){
425  cerr << "error catched" << std::endl;
426  exit(1);
427  }
428 
429  //convert map to vector
430  // short iclass=0;
431  if(verbose_opt[0]>1)
432  std::cout << "training pixels: " << std::endl;
433  map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
434  while(mapit!=trainingMap.end()){
435  //delete small classes
436  if((mapit->second).size()<minSize_opt[0]){
437  trainingMap.erase(mapit);
438  continue;
439  }
440  trainingPixels.push_back(mapit->second);
441  if(verbose_opt[0]>1)
442  std::cout << mapit->first << ": " << (mapit->second).size() << " samples" << std::endl;
443  ++mapit;
444  }
445  if(!ibag){
446  nclass=trainingPixels.size();
447  if(classname_opt.size())
448  assert(nclass==classname_opt.size());
449  nband=trainingPixels[0][0].size()-2;//X and Y//trainingPixels[0][0].size();
450  }
451  else{
452  assert(nclass==trainingPixels.size());
453  assert(nband==trainingPixels[0][0].size()-2);
454  }
455 
456  //do not remove outliers here: could easily be obtained through ogr2ogr -where 'B2<110' output.shp input.shp
457  //balance training data
458  if(balance_opt[0]>0){
459  while(balance_opt.size()<nclass)
460  balance_opt.push_back(balance_opt.back());
461  if(random_opt[0])
462  srand(time(NULL));
463  totalSamples=0;
464  for(short iclass=0;iclass<nclass;++iclass){
465  if(trainingPixels[iclass].size()>balance_opt[iclass]){
466  while(trainingPixels[iclass].size()>balance_opt[iclass]){
467  int index=rand()%trainingPixels[iclass].size();
468  trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
469  }
470  }
471  else{
472  int oldsize=trainingPixels[iclass].size();
473  for(int isample=trainingPixels[iclass].size();isample<balance_opt[iclass];++isample){
474  int index = rand()%oldsize;
475  trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
476  }
477  }
478  totalSamples+=trainingPixels[iclass].size();
479  }
480  }
481 
482  //set scale and offset
483  offset[ibag].resize(nband);
484  scale[ibag].resize(nband);
485  if(offset_opt.size()>1)
486  assert(offset_opt.size()==nband);
487  if(scale_opt.size()>1)
488  assert(scale_opt.size()==nband);
489  for(int iband=0;iband<nband;++iband){
490  if(verbose_opt[0]>=1)
491  std::cout << "scaling for band" << iband << std::endl;
492  offset[ibag][iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
493  scale[ibag][iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
494  //search for min and maximum
495  if(scale[ibag][iband]<=0){
496  float theMin=trainingPixels[0][0][iband+startBand];
497  float theMax=trainingPixels[0][0][iband+startBand];
498  for(short iclass=0;iclass<nclass;++iclass){
499  for(int isample=0;isample<trainingPixels[iclass].size();++isample){
500  if(theMin>trainingPixels[iclass][isample][iband+startBand])
501  theMin=trainingPixels[iclass][isample][iband+startBand];
502  if(theMax<trainingPixels[iclass][isample][iband+startBand])
503  theMax=trainingPixels[iclass][isample][iband+startBand];
504  }
505  }
506  offset[ibag][iband]=theMin+(theMax-theMin)/2.0;
507  scale[ibag][iband]=(theMax-theMin)/2.0;
508  if(verbose_opt[0]>=1){
509  std::cout << "Extreme image values for band " << iband << ": [" << theMin << "," << theMax << "]" << std::endl;
510  std::cout << "Using offset, scale: " << offset[ibag][iband] << ", " << scale[ibag][iband] << std::endl;
511  std::cout << "scaled values for band " << iband << ": [" << (theMin-offset[ibag][iband])/scale[ibag][iband] << "," << (theMax-offset[ibag][iband])/scale[ibag][iband] << "]" << std::endl;
512  }
513  }
514  }
515  }
516  else{//use same offset and scale
517  offset[ibag].resize(nband);
518  scale[ibag].resize(nband);
519  for(int iband=0;iband<nband;++iband){
520  offset[ibag][iband]=offset[0][iband];
521  scale[ibag][iband]=scale[0][iband];
522  }
523  }
524 
525  if(!ibag){
526  if(priors_opt.size()==1){//default: equal priors for each class
527  priors.resize(nclass);
528  for(short iclass=0;iclass<nclass;++iclass)
529  priors[iclass]=1.0/nclass;
530  }
531  assert(priors_opt.size()==1||priors_opt.size()==nclass);
532 
533  //set bagsize for each class if not done already via command line
534  while(bagSize_opt.size()<nclass)
535  bagSize_opt.push_back(bagSize_opt.back());
536 
537  if(verbose_opt[0]>=1){
538  std::cout << "number of bands: " << nband << std::endl;
539  std::cout << "number of classes: " << nclass << std::endl;
540  if(priorimg_opt.empty()){
541  std::cout << "priors:";
542  for(short iclass=0;iclass<nclass;++iclass)
543  std::cout << " " << priors[iclass];
544  std::cout << std::endl;
545  }
546  }
547  map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
548  bool doSort=true;
549  try{
550  while(mapit!=trainingMap.end()){
551  nameVector.push_back(mapit->first);
552  if(classValueMap.size()){
553  //check if name in training is covered by classname_opt (values can not be 0)
554  if(classValueMap[mapit->first]>0){
555  if(cm.getClassIndex(type2string<short>(classValueMap[mapit->first]))<0){
556  cm.pushBackClassName(type2string<short>(classValueMap[mapit->first]),doSort);
557  }
558  }
559  else{
560  std::cerr << "Error: names in classname option are not complete, please check names in training vector and make sure classvalue is > 0" << std::endl;
561  exit(1);
562  }
563  }
564  else
565  cm.pushBackClassName(mapit->first,doSort);
566  ++mapit;
567  }
568  }
569  catch(BadConversion conversionString){
570  std::cerr << "Error: did you provide class pairs names (-c) and integer values (-r) for each class in training vector?" << std::endl;
571  exit(1);
572  }
573  if(classname_opt.empty()){
574  //std::cerr << "Warning: no class name and value pair provided for all " << nclass << " classes, using string2type<int> instead!" << std::endl;
575  for(int iclass=0;iclass<nclass;++iclass){
576  if(verbose_opt[0])
577  std::cout << iclass << " " << cm.getClass(iclass) << " -> " << string2type<short>(cm.getClass(iclass)) << std::endl;
578  classValueMap[cm.getClass(iclass)]=string2type<short>(cm.getClass(iclass));
579  }
580  }
581 
582  // if(priors_opt.size()==nameVector.size()){
583  // std::cerr << "Warning: please check if priors are provided in correct order!!!" << std::endl;
584  // for(int iclass=0;iclass<nameVector.size();++iclass)
585  // std::cerr << nameVector[iclass] << " " << priors_opt[iclass] << std::endl;
586  // }
587  }//if(!ibag)
588 
589  //Calculate features of training set
590  vector< Vector2d<float> > trainingFeatures(nclass);
591  for(short iclass=0;iclass<nclass;++iclass){
592  int nctraining=0;
593  if(verbose_opt[0]>=1)
594  std::cout << "calculating features for class " << iclass << std::endl;
595  if(random_opt[0])
596  srand(time(NULL));
597  nctraining=(bagSize_opt[iclass]<100)? trainingPixels[iclass].size()/100.0*bagSize_opt[iclass] : trainingPixels[iclass].size();//bagSize_opt[iclass] given in % of training size
598  if(nctraining<=0)
599  nctraining=1;
600  assert(nctraining<=trainingPixels[iclass].size());
601  int index=0;
602  if(bagSize_opt[iclass]<100)
603  random_shuffle(trainingPixels[iclass].begin(),trainingPixels[iclass].end());
604  if(verbose_opt[0]>1)
605  std::cout << "nctraining (class " << iclass << "): " << nctraining << std::endl;
606  trainingFeatures[iclass].resize(nctraining);
607  for(int isample=0;isample<nctraining;++isample){
608  //scale pixel values according to scale and offset!!!
609  for(int iband=0;iband<nband;++iband){
610  float value=trainingPixels[iclass][isample][iband+startBand];
611  trainingFeatures[iclass][isample].push_back((value-offset[ibag][iband])/scale[ibag][iband]);
612  }
613  }
614  assert(trainingFeatures[iclass].size()==nctraining);
615  }
616 
617  unsigned int nFeatures=trainingFeatures[0][0].size();
618  if(verbose_opt[0]>=1)
619  std::cout << "number of features: " << nFeatures << std::endl;
620  unsigned int ntraining=0;
621  for(short iclass=0;iclass<nclass;++iclass)
622  ntraining+=trainingFeatures[iclass].size();
623  if(verbose_opt[0]>=1)
624  std::cout << "training size over all classes: " << ntraining << std::endl;
625 
626  prob[ibag].l=ntraining;
627  prob[ibag].y = Malloc(double,prob[ibag].l);
628  prob[ibag].x = Malloc(struct svm_node *,prob[ibag].l);
629  x_space[ibag] = Malloc(struct svm_node,(nFeatures+1)*ntraining);
630  unsigned long int spaceIndex=0;
631  int lIndex=0;
632  for(short iclass=0;iclass<nclass;++iclass){
633  for(int isample=0;isample<trainingFeatures[iclass].size();++isample){
634  prob[ibag].x[lIndex]=&(x_space[ibag][spaceIndex]);
635  for(int ifeature=0;ifeature<nFeatures;++ifeature){
636  x_space[ibag][spaceIndex].index=ifeature+1;
637  x_space[ibag][spaceIndex].value=trainingFeatures[iclass][isample][ifeature];
638  ++spaceIndex;
639  }
640  x_space[ibag][spaceIndex++].index=-1;
641  prob[ibag].y[lIndex]=iclass;
642  ++lIndex;
643  }
644  }
645  assert(lIndex==prob[ibag].l);
646 
647  //set SVM parameters through command line options
648  param[ibag].svm_type = svmMap[svm_type_opt[0]];
649  param[ibag].kernel_type = kernelMap[kernel_type_opt[0]];
650  param[ibag].degree = kernel_degree_opt[0];
651  param[ibag].gamma = (gamma_opt[0]>0)? gamma_opt[0] : 1.0/nFeatures;
652  param[ibag].coef0 = coef0_opt[0];
653  param[ibag].nu = nu_opt[0];
654  param[ibag].cache_size = cache_opt[0];
655  param[ibag].C = ccost_opt[0];
656  param[ibag].eps = epsilon_tol_opt[0];
657  param[ibag].p = epsilon_loss_opt[0];
658  param[ibag].shrinking = (shrinking_opt[0])? 1 : 0;
659  param[ibag].probability = (prob_est_opt[0])? 1 : 0;
660  param[ibag].nr_weight = 0;//not used: I use priors and balancing
661  param[ibag].weight_label = NULL;
662  param[ibag].weight = NULL;
663  param[ibag].verbose=(verbose_opt[0]>1)? true:false;
664 
665  if(verbose_opt[0]>1)
666  std::cout << "checking parameters" << std::endl;
667  svm_check_parameter(&prob[ibag],&param[ibag]);
668  if(verbose_opt[0])
669  std::cout << "parameters ok, training" << std::endl;
670  svm[ibag]=svm_train(&prob[ibag],&param[ibag]);
671  if(verbose_opt[0]>1)
672  std::cout << "SVM is now trained" << std::endl;
673  if(cv_opt[0]>1){
674  if(verbose_opt[0]>1)
675  std::cout << "Cross validating" << std::endl;
676  double *target = Malloc(double,prob[ibag].l);
677  svm_cross_validation(&prob[ibag],&param[ibag],cv_opt[0],target);
678  assert(param[ibag].svm_type != EPSILON_SVR&&param[ibag].svm_type != NU_SVR);//only for regression
679 
680  for(int i=0;i<prob[ibag].l;i++){
681  string refClassName=nameVector[prob[ibag].y[i]];
682  string className=nameVector[target[i]];
683  if(classValueMap.size())
684  cm.incrementResult(type2string<short>(classValueMap[refClassName]),type2string<short>(classValueMap[className]),1.0/nbag);
685  else
686  cm.incrementResult(cm.getClass(prob[ibag].y[i]),cm.getClass(target[i]),1.0/nbag);
687  }
688  free(target);
689  }
690  // *NOTE* Because svm_model contains pointers to svm_problem, you can
691  // not free the memory used by svm_problem if you are still using the
692  // svm_model produced by svm_train().
693  }//for ibag
694  if(cv_opt[0]>1){
695  assert(cm.nReference());
696  cm.setFormat(cmformat_opt[0]);
697  cm.reportSE95(false);
698  std::cout << cm << std::endl;
699  // cout << "class #samples userAcc prodAcc" << endl;
700  // double se95_ua=0;
701  // double se95_pa=0;
702  // double se95_oa=0;
703  // double dua=0;
704  // double dpa=0;
705  // double doa=0;
706  // for(short iclass=0;iclass<cm.nClasses();++iclass){
707  // dua=cm.ua(cm.getClass(iclass),&se95_ua);
708  // dpa=cm.pa(cm.getClass(iclass),&se95_pa);
709  // cout << cm.getClass(iclass) << " " << cm.nReference(cm.getClass(iclass)) << " " << dua << " (" << se95_ua << ")" << " " << dpa << " (" << se95_pa << ")" << endl;
710  // }
711  // std::cout << "Kappa: " << cm.kappa() << std::endl;
712  // doa=cm.oa(&se95_oa);
713  // std::cout << "Overall Accuracy: " << 100*doa << " (" << 100*se95_oa << ")" << std::endl;
714  }
715 
716  //--------------------------------- end of training -----------------------------------
717  if(input_opt.empty())
718  exit(0);
719 
720  const char* pszMessage;
721  void* pProgressArg=NULL;
722  GDALProgressFunc pfnProgress=GDALTermProgress;
723  float progress=0;
724  if(!verbose_opt[0])
725  pfnProgress(progress,pszMessage,pProgressArg);
726  //-------------------------------- open image file ------------------------------------
727  bool inputIsRaster=false;
728  ImgReaderOgr imgReaderOgr;
729  try{
730  imgReaderOgr.open(input_opt[0]);
731  imgReaderOgr.close();
732  }
733  catch(string errorString){
734  inputIsRaster=true;
735  }
736  if(inputIsRaster){
737  ImgReaderGdal testImage;
738  try{
739  if(verbose_opt[0]>=1)
740  std::cout << "opening image " << input_opt[0] << std::endl;
741  testImage.open(input_opt[0]);
742  }
743  catch(string error){
744  cerr << error << std::endl;
745  exit(2);
746  }
747  ImgReaderGdal priorReader;
748  if(priorimg_opt.size()){
749  try{
750  if(verbose_opt[0]>=1)
751  std::cout << "opening prior image " << priorimg_opt[0] << std::endl;
752  priorReader.open(priorimg_opt[0]);
753  assert(priorReader.nrOfCol()==testImage.nrOfCol());
754  assert(priorReader.nrOfRow()==testImage.nrOfRow());
755  }
756  catch(string error){
757  cerr << error << std::endl;
758  exit(2);
759  }
760  catch(...){
761  cerr << "error catched" << std::endl;
762  exit(1);
763  }
764  }
765 
766  int nrow=testImage.nrOfRow();
767  int ncol=testImage.nrOfCol();
768  if(option_opt.findSubstring("INTERLEAVE=")==option_opt.end()){
769  string theInterleave="INTERLEAVE=";
770  theInterleave+=testImage.getInterleave();
771  option_opt.push_back(theInterleave);
772  }
773  vector<char> classOut(ncol);//classified line for writing to image file
774 
775  // assert(nband==testImage.nrOfBand());
776  ImgWriterGdal classImageBag;
777  ImgWriterGdal classImageOut;
778  ImgWriterGdal probImage;
779  ImgWriterGdal entropyImage;
780 
781  string imageType=testImage.getImageType();
782  if(oformat_opt.size())//default
783  imageType=oformat_opt[0];
784  try{
785  assert(output_opt.size());
786  if(verbose_opt[0]>=1)
787  std::cout << "opening class image for writing output " << output_opt[0] << std::endl;
788  if(classBag_opt.size()){
789  classImageBag.open(classBag_opt[0],ncol,nrow,nbag,GDT_Byte,imageType,option_opt);
790  classImageBag.GDALSetNoDataValue(nodata_opt[0]);
791  classImageBag.copyGeoTransform(testImage);
792  classImageBag.setProjection(testImage.getProjection());
793  }
794  classImageOut.open(output_opt[0],ncol,nrow,1,GDT_Byte,imageType,option_opt);
795  classImageOut.GDALSetNoDataValue(nodata_opt[0]);
796  classImageOut.copyGeoTransform(testImage);
797  classImageOut.setProjection(testImage.getProjection());
798  if(colorTable_opt.size())
799  classImageOut.setColorTable(colorTable_opt[0],0);
800  if(prob_opt.size()){
801  probImage.open(prob_opt[0],ncol,nrow,nclass,GDT_Byte,imageType,option_opt);
802  probImage.GDALSetNoDataValue(nodata_opt[0]);
803  probImage.copyGeoTransform(testImage);
804  probImage.setProjection(testImage.getProjection());
805  }
806  if(entropy_opt.size()){
807  entropyImage.open(entropy_opt[0],ncol,nrow,1,GDT_Byte,imageType,option_opt);
808  entropyImage.GDALSetNoDataValue(nodata_opt[0]);
809  entropyImage.copyGeoTransform(testImage);
810  entropyImage.setProjection(testImage.getProjection());
811  }
812  }
813  catch(string error){
814  cerr << error << std::endl;
815  }
816 
817  ImgWriterGdal maskWriter;
818  if(extent_opt.size()){
819  try{
820  maskWriter.open("/vsimem/mask.tif",ncol,nrow,1,GDT_Float32,imageType,option_opt);
821  maskWriter.GDALSetNoDataValue(nodata_opt[0]);
822  maskWriter.copyGeoTransform(testImage);
823  maskWriter.setProjection(testImage.getProjection());
824  vector<double> burnValues(1,1);//burn value is 1 (single band)
825  maskWriter.rasterizeOgr(extentReader,burnValues);
826  maskWriter.close();
827  }
828  catch(string error){
829  cerr << error << std::endl;
830  exit(2);
831  }
832  catch(...){
833  cerr << "error catched" << std::endl;
834  exit(1);
835  }
836  mask_opt.clear();
837  mask_opt.push_back("/vsimem/mask.tif");
838  }
839  ImgReaderGdal maskReader;
840  if(mask_opt.size()){
841  try{
842  if(verbose_opt[0]>=1)
843  std::cout << "opening mask image file " << mask_opt[0] << std::endl;
844  maskReader.open(mask_opt[0]);
845  }
846  catch(string error){
847  cerr << error << std::endl;
848  exit(2);
849  }
850  catch(...){
851  cerr << "error catched" << std::endl;
852  exit(1);
853  }
854  }
855 
856  for(int iline=0;iline<nrow;++iline){
857  vector<float> buffer(ncol);
858  vector<short> lineMask;
859  Vector2d<float> linePrior;
860  if(priorimg_opt.size())
861  linePrior.resize(nclass,ncol);//prior prob for each class
862  Vector2d<float> hpixel(ncol);
863  Vector2d<float> probOut(nclass,ncol);//posterior prob for each (internal) class
864  vector<float> entropy(ncol);
865  Vector2d<char> classBag;//classified line for writing to image file
866  if(classBag_opt.size())
867  classBag.resize(nbag,ncol);
868  try{
869  if(band_opt.size()){
870  for(int iband=0;iband<band_opt.size();++iband){
871  if(verbose_opt[0]==2)
872  std::cout << "reading band " << band_opt[iband] << std::endl;
873  assert(band_opt[iband]>=0);
874  assert(band_opt[iband]<testImage.nrOfBand());
875  testImage.readData(buffer,GDT_Float32,iline,band_opt[iband]);
876  for(int icol=0;icol<ncol;++icol)
877  hpixel[icol].push_back(buffer[icol]);
878  }
879  }
880  else{
881  for(int iband=bstart_opt[0];iband<bstart_opt[0]+nband;++iband){
882  if(verbose_opt[0]==2)
883  std::cout << "reading band " << iband << std::endl;
884  assert(iband>=0);
885  assert(iband<testImage.nrOfBand());
886  testImage.readData(buffer,GDT_Float32,iline,iband);
887  for(int icol=0;icol<ncol;++icol)
888  hpixel[icol].push_back(buffer[icol]);
889  }
890  }
891  }
892  catch(string theError){
893  cerr << "Error reading " << input_opt[0] << ": " << theError << std::endl;
894  exit(3);
895  }
896  catch(...){
897  cerr << "error catched" << std::endl;
898  exit(3);
899  }
900  assert(nband==hpixel[0].size());
901  if(verbose_opt[0]>1)
902  std::cout << "used bands: " << nband << std::endl;
903  //read prior
904  if(priorimg_opt.size()){
905  try{
906  for(short iclass=0;iclass<nclass;++iclass){
907  if(verbose_opt.size()>1)
908  std::cout << "Reading " << priorimg_opt[0] << " band " << iclass << " line " << iline << std::endl;
909  priorReader.readData(linePrior[iclass],GDT_Float32,iline,iclass);
910  }
911  }
912  catch(string theError){
913  std::cerr << "Error reading " << priorimg_opt[0] << ": " << theError << std::endl;
914  exit(3);
915  }
916  catch(...){
917  cerr << "error catched" << std::endl;
918  exit(3);
919  }
920  }
921  double oldRowMask=-1;//keep track of row mask to optimize number of line readings
922  //process per pixel
923  for(int icol=0;icol<ncol;++icol){
924  assert(hpixel[icol].size()==nband);
925  bool doClassify=true;
926  bool masked=false;
927  double geox=0;
928  double geoy=0;
929  if(extent_opt.size()){
930  doClassify=false;
931  testImage.image2geo(icol,iline,geox,geoy);
932  //check enveloppe first
933  if(uly>=geoy&&lry<=geoy&&ulx<=geox&&lrx>=geox){
934  doClassify=true;
935  }
936  // if(doClassify){
937  // thePoint.setX(geox);
938  // thePoint.setY(geoy);
939  // readLayer->ResetReading();
940  // while( (readFeature = readLayer->GetNextFeature()) != NULL ){
941  // OGRGeometry *poGeometry;
942  // poGeometry = readFeature->GetGeometryRef();
943  // assert(poGeometry!=NULL);
944  // //check if point is on surface
945  // if(thePoint.Within(poGeometry)){
946  // doClassify=true;
947  // break;
948  // }
949  // }
950  // }
951  }
952  if(mask_opt.size()){
953  //read mask
954  double colMask=0;
955  double rowMask=0;
956 
957  testImage.image2geo(icol,iline,geox,geoy);
958  maskReader.geo2image(geox,geoy,colMask,rowMask);
959  colMask=static_cast<int>(colMask);
960  rowMask=static_cast<int>(rowMask);
961  if(rowMask>=0&&rowMask<maskReader.nrOfRow()&&colMask>=0&&colMask<maskReader.nrOfCol()){
962  if(static_cast<int>(rowMask)!=static_cast<int>(oldRowMask)){
963  assert(rowMask>=0&&rowMask<maskReader.nrOfRow());
964  try{
965  // maskReader.readData(lineMask[imask],GDT_Int32,static_cast<int>(rowMask));
966  maskReader.readData(lineMask,GDT_Int16,static_cast<int>(rowMask));
967  }
968  catch(string errorstring){
969  cerr << errorstring << endl;
970  exit(1);
971  }
972  catch(...){
973  cerr << "error catched" << std::endl;
974  exit(3);
975  }
976  oldRowMask=rowMask;
977  }
978  short theMask=0;
979  for(short ivalue=0;ivalue<msknodata_opt.size();++ivalue){
980  if(msknodata_opt[ivalue]>=0){//values set in msknodata_opt are invalid
981  if(lineMask[colMask]==msknodata_opt[ivalue]){
982  theMask=lineMask[colMask];
983  masked=true;
984  break;
985  }
986  }
987  else{//only values set in msknodata_opt are valid
988  if(lineMask[colMask]!=-msknodata_opt[ivalue]){
989  theMask=lineMask[colMask];
990  masked=true;
991  }
992  else{
993  masked=false;
994  break;
995  }
996  }
997  }
998  if(masked){
999  if(classBag_opt.size())
1000  for(int ibag=0;ibag<nbag;++ibag)
1001  classBag[ibag][icol]=theMask;
1002  classOut[icol]=theMask;
1003  continue;
1004  }
1005  }
1006  bool valid=false;
1007  for(int iband=0;iband<hpixel[icol].size();++iband){
1008  if(hpixel[icol][iband]){
1009  valid=true;
1010  break;
1011  }
1012  }
1013  if(!valid)
1014  doClassify=false;
1015  }
1016  for(short iclass=0;iclass<nclass;++iclass)
1017  probOut[iclass][icol]=0;
1018  if(!doClassify){
1019  if(classBag_opt.size())
1020  for(int ibag=0;ibag<nbag;++ibag)
1021  classBag[ibag][icol]=nodata_opt[0];
1022  classOut[icol]=nodata_opt[0];
1023  continue;//next column
1024  }
1025  if(verbose_opt[0]>1)
1026  std::cout << "begin classification " << std::endl;
1027  //----------------------------------- classification -------------------
1028  for(int ibag=0;ibag<nbag;++ibag){
1029  vector<double> result(nclass);
1030  struct svm_node *x;
1031  x = (struct svm_node *) malloc((nband+1)*sizeof(struct svm_node));
1032  for(int iband=0;iband<nband;++iband){
1033  x[iband].index=iband+1;
1034  x[iband].value=(hpixel[icol][iband]-offset[ibag][iband])/scale[ibag][iband];
1035  }
1036  x[nband].index=-1;//to end svm feature vector
1037  double predict_label=0;
1038  vector<float> prValues(nclass);
1039  float maxP=0;
1040  if(!prob_est_opt[0]){
1041  predict_label = svm_predict(svm[ibag],x);
1042  for(short iclass=0;iclass<nclass;++iclass){
1043  if(iclass==static_cast<short>(predict_label))
1044  result[iclass]=1;
1045  else
1046  result[iclass]=0;
1047  }
1048  }
1049  else{
1050  assert(svm_check_probability_model(svm[ibag]));
1051  predict_label = svm_predict_probability(svm[ibag],x,&(result[0]));
1052  }
1053  //calculate posterior prob of bag
1054  if(classBag_opt.size()){
1055  //search for max prob within bag
1056  maxP=0;
1057  classBag[ibag][icol]=0;
1058  }
1059  double normPrior=0;
1060  if(priorimg_opt.size()){
1061  for(short iclass=0;iclass<nclass;++iclass)
1062  normPrior+=linePrior[iclass][icol];
1063  }
1064  for(short iclass=0;iclass<nclass;++iclass){
1065  if(priorimg_opt.size())
1066  priors[iclass]=linePrior[iclass][icol]/normPrior;//todo: check if correct for all cases... (automatic classValueMap and manual input for names and values)
1067  switch(comb_opt[0]){
1068  default:
1069  case(0)://sum rule
1070  probOut[iclass][icol]+=result[iclass]*priors[iclass];//add probabilities for each bag
1071  break;
1072  case(1)://product rule
1073  probOut[iclass][icol]*=pow(static_cast<float>(priors[iclass]),static_cast<float>(1.0-nbag)/nbag)*result[iclass];//multiply probabilities for each bag
1074  break;
1075  case(2)://max rule
1076  if(priors[iclass]*result[iclass]>probOut[iclass][icol])
1077  probOut[iclass][icol]=priors[iclass]*result[iclass];
1078  break;
1079  }
1080  if(classBag_opt.size()){
1081  //search for max prob within bag
1082  // if(prValues[iclass]>maxP){
1083  // maxP=prValues[iclass];
1084  // classBag[ibag][icol]=iclass;
1085  // }
1086  if(result[iclass]>maxP){
1087  maxP=result[iclass];
1088  classBag[ibag][icol]=iclass;
1089  }
1090  }
1091  }
1092  free(x);
1093  }//ibag
1094 
1095  //search for max class prob
1096  float maxBag1=0;//max probability
1097  float maxBag2=0;//second max probability
1098  float normBag=0;
1099  for(short iclass=0;iclass<nclass;++iclass){
1100  if(probOut[iclass][icol]>maxBag1){
1101  maxBag1=probOut[iclass][icol];
1102  classOut[icol]=classValueMap[nameVector[iclass]];
1103  }
1104  else if(probOut[iclass][icol]>maxBag2)
1105  maxBag2=probOut[iclass][icol];
1106  normBag+=probOut[iclass][icol];
1107  }
1108  //normalize probOut and convert to percentage
1109  entropy[icol]=0;
1110  for(short iclass=0;iclass<nclass;++iclass){
1111  float prv=probOut[iclass][icol];
1112  prv/=normBag;
1113  entropy[icol]-=prv*log(prv)/log(2.0);
1114  prv*=100.0;
1115 
1116  probOut[iclass][icol]=static_cast<short>(prv+0.5);
1117  // assert(classValueMap[nameVector[iclass]]<probOut.size());
1118  // assert(classValueMap[nameVector[iclass]]>=0);
1119  // probOut[classValueMap[nameVector[iclass]]][icol]=static_cast<short>(prv+0.5);
1120  }
1121  entropy[icol]/=log(static_cast<double>(nclass))/log(2.0);
1122  entropy[icol]=static_cast<short>(100*entropy[icol]+0.5);
1123  if(active_opt.size()){
1124  if(entropy[icol]>activePoints.back().value){
1125  activePoints.back().value=entropy[icol];//replace largest value (last)
1126  activePoints.back().posx=icol;
1127  activePoints.back().posy=iline;
1128  std::sort(activePoints.begin(),activePoints.end(),Decrease_PosValue());//sort in descending order (largest first, smallest last)
1129  if(verbose_opt[0])
1130  std::cout << activePoints.back().posx << " " << activePoints.back().posy << " " << activePoints.back().value << std::endl;
1131  }
1132  }
1133  }//icol
1134  //----------------------------------- write output ------------------------------------------
1135  if(classBag_opt.size())
1136  for(int ibag=0;ibag<nbag;++ibag)
1137  classImageBag.writeData(classBag[ibag],GDT_Byte,iline,ibag);
1138  if(prob_opt.size()){
1139  for(short iclass=0;iclass<nclass;++iclass)
1140  probImage.writeData(probOut[iclass],GDT_Float32,iline,iclass);
1141  }
1142  if(entropy_opt.size()){
1143  entropyImage.writeData(entropy,GDT_Float32,iline);
1144  }
1145  classImageOut.writeData(classOut,GDT_Byte,iline);
1146  if(!verbose_opt[0]){
1147  progress=static_cast<float>(iline+1.0)/classImageOut.nrOfRow();
1148  pfnProgress(progress,pszMessage,pProgressArg);
1149  }
1150  }
1151  //write active learning points
1152  if(active_opt.size()){
1153  for(int iactive=0;iactive<activePoints.size();++iactive){
1154  std::map<string,double> pointMap;
1155  for(int iband=0;iband<testImage.nrOfBand();++iband){
1156  double value;
1157  testImage.readData(value,GDT_Float64,static_cast<int>(activePoints[iactive].posx),static_cast<int>(activePoints[iactive].posy),iband);
1158  ostringstream fs;
1159  fs << "B" << iband;
1160  pointMap[fs.str()]=value;
1161  }
1162  pointMap[label_opt[0]]=0;
1163  double x, y;
1164  testImage.image2geo(activePoints[iactive].posx,activePoints[iactive].posy,x,y);
1165  std::string fieldname="id";//number of the point
1166  activeWriter.addPoint(x,y,pointMap,fieldname,++nactive);
1167  }
1168  }
1169 
1170  testImage.close();
1171  if(mask_opt.size())
1172  maskReader.close();
1173  if(priorimg_opt.size())
1174  priorReader.close();
1175  if(prob_opt.size())
1176  probImage.close();
1177  if(entropy_opt.size())
1178  entropyImage.close();
1179  if(classBag_opt.size())
1180  classImageBag.close();
1181  classImageOut.close();
1182  }
1183  else{//classify vector file
1184  cm.clearResults();
1185  //notice that fields have already been set by readDataImageOgr (taking into account appropriate bands)
1186  for(int ivalidation=0;ivalidation<input_opt.size();++ivalidation){
1187  if(output_opt.size())
1188  assert(output_opt.size()==input_opt.size());
1189  if(verbose_opt[0])
1190  std::cout << "opening img reader " << input_opt[ivalidation] << std::endl;
1191  imgReaderOgr.open(input_opt[ivalidation]);
1192  ImgWriterOgr imgWriterOgr;
1193 
1194  if(output_opt.size()){
1195  if(verbose_opt[0])
1196  std::cout << "opening img writer and copying fields from img reader" << output_opt[ivalidation] << std::endl;
1197  imgWriterOgr.open(output_opt[ivalidation],imgReaderOgr);
1198  }
1199  if(verbose_opt[0])
1200  cout << "number of layers in input ogr file: " << imgReaderOgr.getLayerCount() << endl;
1201  for(int ilayer=0;ilayer<imgReaderOgr.getLayerCount();++ilayer){
1202  if(verbose_opt[0])
1203  cout << "processing input layer " << ilayer << endl;
1204  if(output_opt.size()){
1205  if(verbose_opt[0])
1206  std::cout << "creating field class" << std::endl;
1207  if(classValueMap.size())
1208  imgWriterOgr.createField("class",OFTInteger,ilayer);
1209  else
1210  imgWriterOgr.createField("class",OFTString,ilayer);
1211  }
1212  unsigned int nFeatures=imgReaderOgr.getFeatureCount(ilayer);
1213  unsigned int ifeature=0;
1214  progress=0;
1215  pfnProgress(progress,pszMessage,pProgressArg);
1216  OGRFeature *poFeature;
1217  while( (poFeature = imgReaderOgr.getLayer(ilayer)->GetNextFeature()) != NULL ){
1218  if(verbose_opt[0]>1)
1219  std::cout << "feature " << ifeature << std::endl;
1220  if( poFeature == NULL ){
1221  cout << "Warning: could not read feature " << ifeature << " in layer " << imgReaderOgr.getLayerName(ilayer) << endl;
1222  continue;
1223  }
1224  OGRFeature *poDstFeature = NULL;
1225  if(output_opt.size()){
1226  poDstFeature=imgWriterOgr.createFeature(ilayer);
1227  if( poDstFeature->SetFrom( poFeature, TRUE ) != OGRERR_NONE ){
1228  CPLError( CE_Failure, CPLE_AppDefined,
1229  "Unable to translate feature %d from layer %s.\n",
1230  poFeature->GetFID(), imgWriterOgr.getLayerName(ilayer).c_str() );
1231  OGRFeature::DestroyFeature( poFeature );
1232  OGRFeature::DestroyFeature( poDstFeature );
1233  }
1234  }
1235  vector<float> validationPixel;
1236  vector<float> validationFeature;
1237 
1238  imgReaderOgr.readData(validationPixel,OFTReal,fields,poFeature,ilayer);
1239  assert(validationPixel.size()==nband);
1240  vector<float> probOut(nclass);//posterior prob for each class
1241  for(short iclass=0;iclass<nclass;++iclass)
1242  probOut[iclass]=0;
1243  for(int ibag=0;ibag<nbag;++ibag){
1244  for(int iband=0;iband<nband;++iband){
1245  validationFeature.push_back((validationPixel[iband]-offset[ibag][iband])/scale[ibag][iband]);
1246  if(verbose_opt[0]==2)
1247  std::cout << " " << validationFeature.back();
1248  }
1249  if(verbose_opt[0]==2)
1250  std::cout << std::endl;
1251  vector<double> result(nclass);
1252  struct svm_node *x;
1253  x = (struct svm_node *) malloc((validationFeature.size()+1)*sizeof(struct svm_node));
1254  for(int i=0;i<validationFeature.size();++i){
1255  x[i].index=i+1;
1256  x[i].value=validationFeature[i];
1257  }
1258 
1259  x[validationFeature.size()].index=-1;//to end svm feature vector
1260  double predict_label=0;
1261  if(!prob_est_opt[0]){
1262  predict_label = svm_predict(svm[ibag],x);
1263  for(short iclass=0;iclass<nclass;++iclass){
1264  if(iclass==static_cast<short>(predict_label))
1265  result[iclass]=1;
1266  else
1267  result[iclass]=0;
1268  }
1269  }
1270  else{
1271  assert(svm_check_probability_model(svm[ibag]));
1272  predict_label = svm_predict_probability(svm[ibag],x,&(result[0]));
1273  }
1274  if(verbose_opt[0]>1){
1275  std::cout << "predict_label: " << predict_label << std::endl;
1276  for(int iclass=0;iclass<result.size();++iclass)
1277  std::cout << result[iclass] << " ";
1278  std::cout << std::endl;
1279  }
1280 
1281  //calculate posterior prob of bag
1282  for(short iclass=0;iclass<nclass;++iclass){
1283  switch(comb_opt[0]){
1284  default:
1285  case(0)://sum rule
1286  probOut[iclass]+=result[iclass]*priors[iclass];//add probabilities for each bag
1287  break;
1288  case(1)://product rule
1289  probOut[iclass]*=pow(static_cast<float>(priors[iclass]),static_cast<float>(1.0-nbag)/nbag)*result[iclass];//multiply probabilities for each bag
1290  break;
1291  case(2)://max rule
1292  if(priors[iclass]*result[iclass]>probOut[iclass])
1293  probOut[iclass]=priors[iclass]*result[iclass];
1294  break;
1295  }
1296  }
1297  free(x);
1298  }//for ibag
1299 
1300  //search for max class prob
1301  float maxBag=0;
1302  float normBag=0;
1303  string classOut="Unclassified";
1304  for(short iclass=0;iclass<nclass;++iclass){
1305  if(verbose_opt[0]>1)
1306  std::cout << probOut[iclass] << " ";
1307  if(probOut[iclass]>maxBag){
1308  maxBag=probOut[iclass];
1309  classOut=nameVector[iclass];
1310  }
1311  }
1312  //look for class name
1313  if(verbose_opt[0]>1){
1314  if(classValueMap.size())
1315  std::cout << "->" << classValueMap[classOut] << std::endl;
1316  else
1317  std::cout << "->" << classOut << std::endl;
1318  }
1319  if(output_opt.size()){
1320  if(classValueMap.size())
1321  poDstFeature->SetField("class",classValueMap[classOut]);
1322  else
1323  poDstFeature->SetField("class",classOut.c_str());
1324  poDstFeature->SetFID( poFeature->GetFID() );
1325  }
1326  int labelIndex=poFeature->GetFieldIndex(label_opt[0].c_str());
1327  if(labelIndex>=0){
1328  string classRef=poFeature->GetFieldAsString(labelIndex);
1329  if(classRef!="0"){
1330  if(classValueMap.size())
1331  cm.incrementResult(type2string<short>(classValueMap[classRef]),type2string<short>(classValueMap[classOut]),1);
1332  else
1333  cm.incrementResult(classRef,classOut,1);
1334  }
1335  }
1336  CPLErrorReset();
1337  if(output_opt.size()){
1338  if(imgWriterOgr.createFeature(poDstFeature,ilayer) != OGRERR_NONE){
1339  CPLError( CE_Failure, CPLE_AppDefined,
1340  "Unable to translate feature %d from layer %s.\n",
1341  poFeature->GetFID(), imgWriterOgr.getLayerName(ilayer).c_str() );
1342  OGRFeature::DestroyFeature( poDstFeature );
1343  OGRFeature::DestroyFeature( poDstFeature );
1344  }
1345  }
1346  ++ifeature;
1347  if(!verbose_opt[0]){
1348  progress=static_cast<float>(ifeature+1.0)/nFeatures;
1349  pfnProgress(progress,pszMessage,pProgressArg);
1350  }
1351  OGRFeature::DestroyFeature( poFeature );
1352  OGRFeature::DestroyFeature( poDstFeature );
1353  }//get next feature
1354  }//next layer
1355  imgReaderOgr.close();
1356  if(output_opt.size())
1357  imgWriterOgr.close();
1358  }
1359  if(cm.nReference()){
1360  std::cout << cm << std::endl;
1361  cout << "class #samples userAcc prodAcc" << endl;
1362  double se95_ua=0;
1363  double se95_pa=0;
1364  double se95_oa=0;
1365  double dua=0;
1366  double dpa=0;
1367  double doa=0;
1368  for(short iclass=0;iclass<cm.nClasses();++iclass){
1369  dua=cm.ua_pct(cm.getClass(iclass),&se95_ua);
1370  dpa=cm.pa_pct(cm.getClass(iclass),&se95_pa);
1371  cout << cm.getClass(iclass) << " " << cm.nReference(cm.getClass(iclass)) << " " << dua << " (" << se95_ua << ")" << " " << dpa << " (" << se95_pa << ")" << endl;
1372  }
1373  std::cout << "Kappa: " << cm.kappa() << std::endl;
1374  doa=cm.oa(&se95_oa);
1375  std::cout << "Overall Accuracy: " << 100*doa << " (" << 100*se95_oa << ")" << std::endl;
1376  }
1377  }
1378  try{
1379  if(active_opt.size())
1380  activeWriter.close();
1381  if(extent_opt.size())
1382  extentReader.close();
1383  }
1384  catch(string errorString){
1385  std::cerr << "Error: errorString" << std::endl;
1386  }
1387 
1388  for(int ibag=0;ibag<nbag;++ibag){
1389  // svm_destroy_param[ibag](&param[ibag]);
1390  svm_destroy_param(&param[ibag]);
1391  free(prob[ibag].y);
1392  free(prob[ibag].x);
1393  free(x_space[ibag]);
1394  svm_free_and_destroy_model(&(svm[ibag]));
1395  }
1396  return 0;
1397 }
throw this class when syntax error in command line option
Definition: Optionpk.h:45
Definition: svm.h:12