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