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