pktools  2.6.3
Processing Kernel for geospatial data
pkann.cc
1 /**********************************************************************
2 pkann.cc: classify raster image using Artificial Neural Network
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 "floatfann.h"
32 #include "algorithms/myfann_cpp.h"
33 
34 /******************************************************************************/
108 using namespace std;
109 
110 int main(int argc, char *argv[])
111 {
112  vector<double> priors;
113 
114  //--------------------------- command line options ------------------------------------
115  Optionpk<string> input_opt("i", "input", "input image");
116  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)");
117  Optionpk<string> tlayer_opt("tln", "tln", "training layer name(s)");
118  Optionpk<string> label_opt("label", "label", "identifier for class label in training vector file.","label");
119  Optionpk<unsigned int> balance_opt("bal", "balance", "balance the input data to this number of samples for each class", 0);
120  Optionpk<bool> random_opt("random", "random", "in case of balance, randomize input data", true,2);
121  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);
122  Optionpk<short> band_opt("b", "band", "band index (starting from 0, either use band option or use start to end)");
123  Optionpk<double> bstart_opt("s", "start", "start band sequence number",0);
124  Optionpk<double> bend_opt("e", "end", "end band sequence number (set to 0 to include bands)", 0);
125  Optionpk<double> offset_opt("\0", "offset", "offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
126  Optionpk<double> scale_opt("\0", "scale", "scale value for each spectral band input features: refl=(DN[band]-offset[band])/scale[band] (use 0 if scale min and max in each band to -1.0 and 1.0)", 0.0);
127  Optionpk<unsigned short> aggreg_opt("a", "aggreg", "how to combine aggregated classifiers, see also rc option (1: sum rule, 2: max rule).",1);
128  Optionpk<double> priors_opt("prior", "prior", "prior probabilities for each class (e.g., -p 0.3 -p 0.3 -p 0.2 )", 0.0);
129  Optionpk<string> priorimg_opt("pim", "priorimg", "prior probability image (multi-band img with band for each class","",2);
130  Optionpk<unsigned short> cv_opt("cv", "cv", "n-fold cross validation mode",0);
131  Optionpk<string> cmformat_opt("cmf","cmf","Format for confusion matrix (ascii or latex)","ascii");
132  Optionpk<unsigned int> nneuron_opt("nn", "nneuron", "number of neurons in hidden layers in neural network (multiple hidden layers are set by defining multiple number of neurons: -n 15 -n 1, default is one hidden layer with 5 neurons)", 5);
133  Optionpk<float> connection_opt("\0", "connection", "connection reate (default: 1.0 for a fully connected network)", 1.0);
134  Optionpk<float> learning_opt("l", "learning", "learning rate (default: 0.7)", 0.7);
135  Optionpk<float> weights_opt("w", "weights", "weights for neural network. Apply to fully connected network only, starting from first input neuron to last output neuron, including the bias neurons (last neuron in each but last layer)", 0.0);
136  Optionpk<unsigned int> maxit_opt("\0", "maxit", "number of maximum iterations (epoch) (default: 500)", 500);
137  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. Default is sum rule (0)",0);
138  Optionpk<unsigned short> bag_opt("bag", "bag", "Number of bootstrap aggregations (default is no bagging: 1)", 1);
139  Optionpk<int> bagSize_opt("bs", "bsize", "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);
140  Optionpk<string> classBag_opt("cb", "classbag", "output for each individual bootstrap aggregation (default is blank)");
141  Optionpk<string> mask_opt("m", "mask", "Use the first band of the specified file as a validity mask. Nodata values can be set with the option msknodata.");
142  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. Default is 0", 0);
143  Optionpk<unsigned short> nodata_opt("nodata", "nodata", "nodata value to put where image is masked as nodata", 0);
144  Optionpk<string> output_opt("o", "output", "output classification image");
145  Optionpk<string> otype_opt("ot", "otype", "Data type for output image ({Byte/Int16/UInt16/UInt32/Int32/Float32/Float64/CInt16/CInt32/CFloat32/CFloat64}). Empty string: inherit type from input image");
146  Optionpk<string> oformat_opt("of", "oformat", "Output image format (see also gdal_translate). Empty string: inherit from input image");
147  Optionpk<string> option_opt("co", "co", "Creation option for output file. Multiple options can be specified.");
148  Optionpk<string> colorTable_opt("ct", "ct", "colour table in ASCII format having 5 columns: id R G B ALFA (0: transparent, 255: solid)");
149  Optionpk<string> prob_opt("\0", "prob", "probability image. Default is no probability image");
150  Optionpk<string> entropy_opt("entropy", "entropy", "entropy image (measure for uncertainty of classifier output","",2);
151  Optionpk<string> active_opt("active", "active", "ogr output for active training sample.","",2);
152  Optionpk<string> ogrformat_opt("f", "f", "Output ogr format for active training sample","SQLite");
153  Optionpk<unsigned int> nactive_opt("na", "nactive", "number of active training points",1);
154  Optionpk<string> classname_opt("c", "class", "list of class names.");
155  Optionpk<short> classvalue_opt("r", "reclass", "list of class values (use same order as in class opt).");
156  Optionpk<short> verbose_opt("v", "verbose", "set to: 0 (results only), 1 (confusion matrix), 2 (debug)",0,2);
157 
158  band_opt.setHide(1);
159  bstart_opt.setHide(1);
160  bend_opt.setHide(1);
161  balance_opt.setHide(1);
162  minSize_opt.setHide(1);
163  bag_opt.setHide(1);
164  bagSize_opt.setHide(1);
165  comb_opt.setHide(1);
166  classBag_opt.setHide(1);
167  minSize_opt.setHide(1);
168  prob_opt.setHide(1);
169  priorimg_opt.setHide(1);
170  minSize_opt.setHide(1);
171  offset_opt.setHide(1);
172  scale_opt.setHide(1);
173  connection_opt.setHide(1);
174  weights_opt.setHide(1);
175  maxit_opt.setHide(1);
176  learning_opt.setHide(1);
177 
178  bool doProcess;//stop process when program was invoked with help option (-h --help)
179  try{
180  doProcess=input_opt.retrieveOption(argc,argv);
181  training_opt.retrieveOption(argc,argv);
182  tlayer_opt.retrieveOption(argc,argv);
183  label_opt.retrieveOption(argc,argv);
184  balance_opt.retrieveOption(argc,argv);
185  random_opt.retrieveOption(argc,argv);
186  minSize_opt.retrieveOption(argc,argv);
187  band_opt.retrieveOption(argc,argv);
188  bstart_opt.retrieveOption(argc,argv);
189  bend_opt.retrieveOption(argc,argv);
190  offset_opt.retrieveOption(argc,argv);
191  scale_opt.retrieveOption(argc,argv);
192  aggreg_opt.retrieveOption(argc,argv);
193  priors_opt.retrieveOption(argc,argv);
194  priorimg_opt.retrieveOption(argc,argv);
195  cv_opt.retrieveOption(argc,argv);
196  cmformat_opt.retrieveOption(argc,argv);
197  nneuron_opt.retrieveOption(argc,argv);
198  connection_opt.retrieveOption(argc,argv);
199  weights_opt.retrieveOption(argc,argv);
200  learning_opt.retrieveOption(argc,argv);
201  maxit_opt.retrieveOption(argc,argv);
202  comb_opt.retrieveOption(argc,argv);
203  bag_opt.retrieveOption(argc,argv);
204  bagSize_opt.retrieveOption(argc,argv);
205  classBag_opt.retrieveOption(argc,argv);
206  mask_opt.retrieveOption(argc,argv);
207  msknodata_opt.retrieveOption(argc,argv);
208  nodata_opt.retrieveOption(argc,argv);
209  output_opt.retrieveOption(argc,argv);
210  otype_opt.retrieveOption(argc,argv);
211  oformat_opt.retrieveOption(argc,argv);
212  colorTable_opt.retrieveOption(argc,argv);
213  option_opt.retrieveOption(argc,argv);
214  prob_opt.retrieveOption(argc,argv);
215  entropy_opt.retrieveOption(argc,argv);
216  active_opt.retrieveOption(argc,argv);
217  ogrformat_opt.retrieveOption(argc,argv);
218  nactive_opt.retrieveOption(argc,argv);
219  classname_opt.retrieveOption(argc,argv);
220  classvalue_opt.retrieveOption(argc,argv);
221  verbose_opt.retrieveOption(argc,argv);
222  }
223  catch(string predefinedString){
224  std::cout << predefinedString << std::endl;
225  exit(0);
226  }
227  if(!doProcess){
228  cout << endl;
229  cout << "Usage: pkann -t training [-i input -o output] [-cv value]" << endl;
230  cout << endl;
231  cout << "short option -h shows basic options only, use long option --help to show all options" << endl;
232  exit(0);//help was invoked, stop processing
233  }
234 
235  if(entropy_opt[0]=="")
236  entropy_opt.clear();
237  if(active_opt[0]=="")
238  active_opt.clear();
239  if(priorimg_opt[0]=="")
240  priorimg_opt.clear();
241 
242  if(verbose_opt[0]>=1){
243  if(input_opt.size())
244  cout << "image filename: " << input_opt[0] << endl;
245  if(mask_opt.size())
246  cout << "mask filename: " << mask_opt[0] << endl;
247  if(training_opt.size()){
248  cout << "training vector file: " << endl;
249  for(int ifile=0;ifile<training_opt.size();++ifile)
250  cout << training_opt[ifile] << endl;
251  }
252  else
253  cerr << "no training file set!" << endl;
254  cout << "verbose: " << verbose_opt[0] << endl;
255  }
256  unsigned short nbag=(training_opt.size()>1)?training_opt.size():bag_opt[0];
257  if(verbose_opt[0]>=1)
258  cout << "number of bootstrap aggregations: " << nbag << endl;
259 
260  ImgWriterOgr activeWriter;
261  if(active_opt.size()){
262  ImgReaderOgr trainingReader(training_opt[0]);
263  activeWriter.open(active_opt[0],ogrformat_opt[0]);
264  activeWriter.createLayer(active_opt[0],trainingReader.getProjection(),wkbPoint,NULL);
265  activeWriter.copyFields(trainingReader);
266  }
267  vector<PosValue> activePoints(nactive_opt[0]);
268  for(int iactive=0;iactive<activePoints.size();++iactive){
269  activePoints[iactive].value=1.0;
270  activePoints[iactive].posx=0.0;
271  activePoints[iactive].posy=0.0;
272  }
273 
274  unsigned int totalSamples=0;
275  unsigned int nactive=0;
276  vector<FANN::neural_net> net(nbag);//the neural network
277 
278  unsigned int nclass=0;
279  int nband=0;
280  int startBand=2;//first two bands represent X and Y pos
281 
282  if(priors_opt.size()>1){//priors from argument list
283  priors.resize(priors_opt.size());
284  double normPrior=0;
285  for(int iclass=0;iclass<priors_opt.size();++iclass){
286  priors[iclass]=priors_opt[iclass];
287  normPrior+=priors[iclass];
288  }
289  //normalize
290  for(int iclass=0;iclass<priors_opt.size();++iclass)
291  priors[iclass]/=normPrior;
292  }
293 
294  //sort bands
295  if(band_opt.size())
296  std::sort(band_opt.begin(),band_opt.end());
297 
298  map<string,short> classValueMap;
299  vector<std::string> nameVector;
300  if(classname_opt.size()){
301  assert(classname_opt.size()==classvalue_opt.size());
302  for(int iclass=0;iclass<classname_opt.size();++iclass)
303  classValueMap[classname_opt[iclass]]=classvalue_opt[iclass];
304  }
305  //----------------------------------- Training -------------------------------
307  vector< vector<double> > offset(nbag);
308  vector< vector<double> > scale(nbag);
309  map<string,Vector2d<float> > trainingMap;
310  vector< Vector2d<float> > trainingPixels;//[class][sample][band]
311  vector<string> fields;
312  for(int ibag=0;ibag<nbag;++ibag){
313  //organize training data
314  if(ibag<training_opt.size()){//if bag contains new training pixels
315  trainingMap.clear();
316  trainingPixels.clear();
317  if(verbose_opt[0]>=1)
318  cout << "reading imageVector file " << training_opt[0] << endl;
319  try{
320  ImgReaderOgr trainingReaderBag(training_opt[ibag]);
321  if(band_opt.size())
322  totalSamples=trainingReaderBag.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
323  else
324  totalSamples=trainingReaderBag.readDataImageOgr(trainingMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
325  if(trainingMap.size()<2){
326  string errorstring="Error: could not read at least two classes from training file, did you provide class labels in training sample (see option label)?";
327  throw(errorstring);
328  }
329  trainingReaderBag.close();
330  }
331  catch(string error){
332  cerr << error << std::endl;
333  exit(1);
334  }
335  catch(...){
336  cerr << "error catched" << std::endl;
337  exit(1);
338  }
339  //delete class 0 ?
340  // if(verbose_opt[0]>=1)
341  // std::cout << "erasing class 0 from training set (" << trainingMap[0].size() << " from " << totalSamples << ") samples" << std::endl;
342  // totalSamples-=trainingMap[0].size();
343  // trainingMap.erase(0);
344  //convert map to vector
345  if(verbose_opt[0]>1)
346  std::cout << "training pixels: " << std::endl;
347  map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
348  while(mapit!=trainingMap.end()){
349  //delete small classes
350  if((mapit->second).size()<minSize_opt[0]){
351  trainingMap.erase(mapit);
352  continue;
353  }
354  trainingPixels.push_back(mapit->second);
355  if(verbose_opt[0]>1)
356  std::cout << mapit->first << ": " << (mapit->second).size() << " samples" << std::endl;
357  ++mapit;
358  }
359  if(!ibag){
360  nclass=trainingPixels.size();
361  if(classname_opt.size())
362  assert(nclass==classname_opt.size());
363  nband=(training_opt.size())?trainingPixels[0][0].size()-2:trainingPixels[0][0].size();//X and Y
364  }
365  else{
366  assert(nclass==trainingPixels.size());
367  assert(nband==(training_opt.size())?trainingPixels[0][0].size()-2:trainingPixels[0][0].size());
368  }
369 
370  //do not remove outliers here: could easily be obtained through ogr2ogr -where 'B2<110' output.shp input.shp
371  //balance training data
372  if(balance_opt[0]>0){
373  while(balance_opt.size()<nclass)
374  balance_opt.push_back(balance_opt.back());
375  if(random_opt[0])
376  srand(time(NULL));
377  totalSamples=0;
378  for(int iclass=0;iclass<nclass;++iclass){
379  if(trainingPixels[iclass].size()>balance_opt[iclass]){
380  while(trainingPixels[iclass].size()>balance_opt[iclass]){
381  int index=rand()%trainingPixels[iclass].size();
382  trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
383  }
384  }
385  else{
386  int oldsize=trainingPixels[iclass].size();
387  for(int isample=trainingPixels[iclass].size();isample<balance_opt[iclass];++isample){
388  int index = rand()%oldsize;
389  trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
390  }
391  }
392  totalSamples+=trainingPixels[iclass].size();
393  }
394  }
395 
396  //set scale and offset
397  offset[ibag].resize(nband);
398  scale[ibag].resize(nband);
399  if(offset_opt.size()>1)
400  assert(offset_opt.size()==nband);
401  if(scale_opt.size()>1)
402  assert(scale_opt.size()==nband);
403  for(int iband=0;iband<nband;++iband){
404  if(verbose_opt[0]>=1)
405  cout << "scaling for band" << iband << endl;
406  offset[ibag][iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
407  scale[ibag][iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
408  //search for min and maximum
409  if(scale[ibag][iband]<=0){
410  float theMin=trainingPixels[0][0][iband+startBand];
411  float theMax=trainingPixels[0][0][iband+startBand];
412  for(int iclass=0;iclass<nclass;++iclass){
413  for(int isample=0;isample<trainingPixels[iclass].size();++isample){
414  if(theMin>trainingPixels[iclass][isample][iband+startBand])
415  theMin=trainingPixels[iclass][isample][iband+startBand];
416  if(theMax<trainingPixels[iclass][isample][iband+startBand])
417  theMax=trainingPixels[iclass][isample][iband+startBand];
418  }
419  }
420  offset[ibag][iband]=theMin+(theMax-theMin)/2.0;
421  scale[ibag][iband]=(theMax-theMin)/2.0;
422  if(verbose_opt[0]>=1){
423  std::cout << "Extreme image values for band " << iband << ": [" << theMin << "," << theMax << "]" << std::endl;
424  std::cout << "Using offset, scale: " << offset[ibag][iband] << ", " << scale[ibag][iband] << std::endl;
425  std::cout << "scaled values for band " << iband << ": [" << (theMin-offset[ibag][iband])/scale[ibag][iband] << "," << (theMax-offset[ibag][iband])/scale[ibag][iband] << "]" << std::endl;
426  }
427  }
428  }
429  }
430  else{//use same offset and scale
431  offset[ibag].resize(nband);
432  scale[ibag].resize(nband);
433  for(int iband=0;iband<nband;++iband){
434  offset[ibag][iband]=offset[0][iband];
435  scale[ibag][iband]=scale[0][iband];
436  }
437  }
438 
439  if(!ibag){
440  if(priors_opt.size()==1){//default: equal priors for each class
441  priors.resize(nclass);
442  for(int iclass=0;iclass<nclass;++iclass)
443  priors[iclass]=1.0/nclass;
444  }
445  assert(priors_opt.size()==1||priors_opt.size()==nclass);
446 
447  //set bagsize for each class if not done already via command line
448  while(bagSize_opt.size()<nclass)
449  bagSize_opt.push_back(bagSize_opt.back());
450 
451  if(verbose_opt[0]>=1){
452  std::cout << "number of bands: " << nband << std::endl;
453  std::cout << "number of classes: " << nclass << std::endl;
454  std::cout << "priors:";
455  if(priorimg_opt.empty()){
456  for(int iclass=0;iclass<nclass;++iclass)
457  std::cout << " " << priors[iclass];
458  std::cout << std::endl;
459  }
460  }
461  map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
462  bool doSort=true;
463  while(mapit!=trainingMap.end()){
464  nameVector.push_back(mapit->first);
465  if(classValueMap.size()){
466  //check if name in training is covered by classname_opt (values can not be 0)
467  if(classValueMap[mapit->first]>0){
468  if(cm.getClassIndex(type2string<short>(classValueMap[mapit->first]))<0)
469  cm.pushBackClassName(type2string<short>(classValueMap[mapit->first]),doSort);
470  }
471  else{
472  std::cerr << "Error: names in classname option are not complete, please check names in training vector and make sure classvalue is > 0" << std::endl;
473  exit(1);
474  }
475  }
476  else
477  cm.pushBackClassName(mapit->first,doSort);
478  ++mapit;
479  }
480  if(classname_opt.empty()){
481  //std::cerr << "Warning: no class name and value pair provided for all " << nclass << " classes, using string2type<int> instead!" << std::endl;
482  for(int iclass=0;iclass<nclass;++iclass){
483  if(verbose_opt[0])
484  std::cout << iclass << " " << cm.getClass(iclass) << " -> " << string2type<short>(cm.getClass(iclass)) << std::endl;
485  classValueMap[cm.getClass(iclass)]=string2type<short>(cm.getClass(iclass));
486  }
487  }
488  if(priors_opt.size()==nameVector.size()){
489  std::cerr << "Warning: please check if priors are provided in correct order!!!" << std::endl;
490  for(int iclass=0;iclass<nameVector.size();++iclass)
491  std::cerr << nameVector[iclass] << " " << priors_opt[iclass] << std::endl;
492  }
493  }//if(!ibag)
494 
495  //Calculate features of training set
496  vector< Vector2d<float> > trainingFeatures(nclass);
497  for(int iclass=0;iclass<nclass;++iclass){
498  int nctraining=0;
499  if(verbose_opt[0]>=1)
500  cout << "calculating features for class " << iclass << endl;
501  if(random_opt[0])
502  srand(time(NULL));
503  nctraining=(bagSize_opt[iclass]<100)? trainingPixels[iclass].size()/100.0*bagSize_opt[iclass] : trainingPixels[iclass].size();//bagSize_opt[iclass] given in % of training size
504  if(nctraining<=0)
505  nctraining=1;
506  assert(nctraining<=trainingPixels[iclass].size());
507  int index=0;
508  if(bagSize_opt[iclass]<100)
509  random_shuffle(trainingPixels[iclass].begin(),trainingPixels[iclass].end());
510 
511  trainingFeatures[iclass].resize(nctraining);
512  for(int isample=0;isample<nctraining;++isample){
513  //scale pixel values according to scale and offset!!!
514  for(int iband=0;iband<nband;++iband){
515  float value=trainingPixels[iclass][isample][iband+startBand];
516  trainingFeatures[iclass][isample].push_back((value-offset[ibag][iband])/scale[ibag][iband]);
517  }
518  }
519  assert(trainingFeatures[iclass].size()==nctraining);
520  }
521 
522  unsigned int nFeatures=trainingFeatures[0][0].size();
523  unsigned int ntraining=0;
524  for(int iclass=0;iclass<nclass;++iclass)
525  ntraining+=trainingFeatures[iclass].size();
526 
527  const unsigned int num_layers = nneuron_opt.size()+2;
528  const float desired_error = 0.0003;
529  const unsigned int iterations_between_reports = (verbose_opt[0])? maxit_opt[0]+1:0;
530  if(verbose_opt[0]>=1){
531  cout << "number of features: " << nFeatures << endl;
532  cout << "creating artificial neural network with " << nneuron_opt.size() << " hidden layer, having " << endl;
533  for(int ilayer=0;ilayer<nneuron_opt.size();++ilayer)
534  cout << nneuron_opt[ilayer] << " ";
535  cout << "neurons" << endl;
536  cout << "connection_opt[0]: " << connection_opt[0] << std::endl;
537  cout << "num_layers: " << num_layers << std::endl;
538  cout << "nFeatures: " << nFeatures << std::endl;
539  cout << "nneuron_opt[0]: " << nneuron_opt[0] << std::endl;
540  cout << "number of classes (nclass): " << nclass << std::endl;
541  }
542  switch(num_layers){
543  case(3):{
544  // net[ibag].create_sparse(connection_opt[0],num_layers, nFeatures, nneuron_opt[0], nclass);//replace all create_sparse with create_sparse_array due to bug in FANN!
545  unsigned int layers[3];
546  layers[0]=nFeatures;
547  layers[1]=nneuron_opt[0];
548  layers[2]=nclass;
549  net[ibag].create_sparse_array(connection_opt[0],num_layers,layers);
550  break;
551  }
552  case(4):{
553  unsigned int layers[4];
554  layers[0]=nFeatures;
555  layers[1]=nneuron_opt[0];
556  layers[2]=nneuron_opt[1];
557  layers[3]=nclass;
558  // layers.push_back(nFeatures);
559  // for(int ihidden=0;ihidden<nneuron_opt.size();++ihidden)
560  // layers.push_back(nneuron_opt[ihidden]);
561  // layers.push_back(nclass);
562  net[ibag].create_sparse_array(connection_opt[0],num_layers,layers);
563  break;
564  }
565  default:
566  cerr << "Only 1 or 2 hidden layers are supported!" << endl;
567  exit(1);
568  break;
569  }
570  if(verbose_opt[0]>=1)
571  cout << "network created" << endl;
572 
573  net[ibag].set_learning_rate(learning_opt[0]);
574 
575  // net.set_activation_steepness_hidden(1.0);
576  // net.set_activation_steepness_output(1.0);
577 
578  net[ibag].set_activation_function_hidden(FANN::SIGMOID_SYMMETRIC_STEPWISE);
579  net[ibag].set_activation_function_output(FANN::SIGMOID_SYMMETRIC_STEPWISE);
580 
581  // Set additional properties such as the training algorithm
582  // net.set_training_algorithm(FANN::TRAIN_QUICKPROP);
583 
584  // Output network type and parameters
585  if(verbose_opt[0]>=1){
586  cout << endl << "Network Type : ";
587  switch (net[ibag].get_network_type())
588  {
589  case FANN::LAYER:
590  cout << "LAYER" << endl;
591  break;
592  case FANN::SHORTCUT:
593  cout << "SHORTCUT" << endl;
594  break;
595  default:
596  cout << "UNKNOWN" << endl;
597  break;
598  }
599  net[ibag].print_parameters();
600  }
601 
602  if(cv_opt[0]>1){
603  if(verbose_opt[0])
604  std::cout << "cross validation" << std::endl;
605  vector<unsigned short> referenceVector;
606  vector<unsigned short> outputVector;
607  float rmse=net[ibag].cross_validation(trainingFeatures,
608  ntraining,
609  cv_opt[0],
610  maxit_opt[0],
611  desired_error,
612  referenceVector,
613  outputVector,
614  verbose_opt[0]);
615  map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
616  for(int isample=0;isample<referenceVector.size();++isample){
617  string refClassName=nameVector[referenceVector[isample]];
618  string className=nameVector[outputVector[isample]];
619  if(classValueMap.size())
620  cm.incrementResult(type2string<short>(classValueMap[refClassName]),type2string<short>(classValueMap[className]),1.0/nbag);
621  else
622  cm.incrementResult(cm.getClass(referenceVector[isample]),cm.getClass(outputVector[isample]),1.0/nbag);
623  }
624  }
625 
626  if(verbose_opt[0]>=1)
627  cout << endl << "Set training data" << endl;
628 
629  if(verbose_opt[0]>=1)
630  cout << endl << "Training network" << endl;
631 
632  if(verbose_opt[0]>=1){
633  cout << "Max Epochs " << setw(8) << maxit_opt[0] << ". "
634  << "Desired Error: " << left << desired_error << right << endl;
635  }
636  if(weights_opt.size()==net[ibag].get_total_connections()){//no new training needed (same training sample)
637  vector<fann_connection> convector;
638  net[ibag].get_connection_array(convector);
639  for(int i_connection=0;i_connection<net[ibag].get_total_connections();++i_connection)
640  convector[i_connection].weight=weights_opt[i_connection];
641  net[ibag].set_weight_array(convector);
642  }
643  else{
644  bool initWeights=true;
645  net[ibag].train_on_data(trainingFeatures,ntraining,initWeights, maxit_opt[0],
646  iterations_between_reports, desired_error);
647  }
648 
649 
650  if(verbose_opt[0]>=2){
651  net[ibag].print_connections();
652  vector<fann_connection> convector;
653  net[ibag].get_connection_array(convector);
654  for(int i_connection=0;i_connection<net[ibag].get_total_connections();++i_connection)
655  cout << "connection " << i_connection << ": " << convector[i_connection].weight << endl;
656 
657  }
658  }//for ibag
659  if(cv_opt[0]>1){
660  assert(cm.nReference());
661  cm.setFormat(cmformat_opt[0]);
662  cm.reportSE95(false);
663  std::cout << cm << std::endl;
664  cout << "class #samples userAcc prodAcc" << endl;
665  double se95_ua=0;
666  double se95_pa=0;
667  double se95_oa=0;
668  double dua=0;
669  double dpa=0;
670  double doa=0;
671  for(int iclass=0;iclass<cm.nClasses();++iclass){
672  dua=cm.ua_pct(cm.getClass(iclass),&se95_ua);
673  dpa=cm.pa_pct(cm.getClass(iclass),&se95_pa);
674  cout << cm.getClass(iclass) << " " << cm.nReference(cm.getClass(iclass)) << " " << dua << " (" << se95_ua << ")" << " " << dpa << " (" << se95_pa << ")" << endl;
675  }
676  std::cout << "Kappa: " << cm.kappa() << std::endl;
677  doa=cm.oa_pct(&se95_oa);
678  std::cout << "Overall Accuracy: " << doa << " (" << se95_oa << ")" << std::endl;
679  }
680  //--------------------------------- end of training -----------------------------------
681  if(input_opt.empty())
682  exit(0);
683 
684  const char* pszMessage;
685  void* pProgressArg=NULL;
686  GDALProgressFunc pfnProgress=GDALTermProgress;
687  float progress=0;
688  //-------------------------------- open image file ------------------------------------
689  bool inputIsRaster=false;
690  ImgReaderOgr imgReaderOgr;
691  try{
692  imgReaderOgr.open(input_opt[0]);
693  imgReaderOgr.close();
694  }
695  catch(string errorString){
696  inputIsRaster=true;
697  }
698  if(inputIsRaster){
699  // if(input_opt[0].find(".shp")==string::npos){
700  ImgReaderGdal testImage;
701  try{
702  if(verbose_opt[0]>=1)
703  cout << "opening image " << input_opt[0] << endl;
704  testImage.open(input_opt[0]);
705  }
706  catch(string error){
707  cerr << error << endl;
708  exit(2);
709  }
710  ImgReaderGdal maskReader;
711  if(mask_opt.size()){
712  try{
713  if(verbose_opt[0]>=1)
714  std::cout << "opening mask image file " << mask_opt[0] << std::endl;
715  maskReader.open(mask_opt[0]);
716  }
717  catch(string error){
718  cerr << error << endl;
719  exit(2);
720  }
721  catch(...){
722  cerr << "error catched" << endl;
723  exit(1);
724  }
725  }
726  ImgReaderGdal priorReader;
727  if(priorimg_opt.size()){
728  try{
729  if(verbose_opt[0]>=1)
730  std::cout << "opening prior image " << priorimg_opt[0] << std::endl;
731  priorReader.open(priorimg_opt[0]);
732  assert(priorReader.nrOfCol()==testImage.nrOfCol());
733  assert(priorReader.nrOfRow()==testImage.nrOfRow());
734  }
735  catch(string error){
736  cerr << error << std::endl;
737  exit(2);
738  }
739  catch(...){
740  cerr << "error catched" << std::endl;
741  exit(1);
742  }
743  }
744 
745  int nrow=testImage.nrOfRow();
746  int ncol=testImage.nrOfCol();
747  if(option_opt.findSubstring("INTERLEAVE=")==option_opt.end()){
748  string theInterleave="INTERLEAVE=";
749  theInterleave+=testImage.getInterleave();
750  option_opt.push_back(theInterleave);
751  }
752  vector<char> classOut(ncol);//classified line for writing to image file
753 
754  // assert(nband==testImage.nrOfBand());
755  ImgWriterGdal classImageBag;
756  ImgWriterGdal classImageOut;
757  ImgWriterGdal probImage;
758  ImgWriterGdal entropyImage;
759 
760  string imageType=testImage.getImageType();
761  if(oformat_opt.size())//default
762  imageType=oformat_opt[0];
763  try{
764 
765  if(verbose_opt[0]>=1)
766  cout << "opening class image for writing output " << output_opt[0] << endl;
767  if(classBag_opt.size()){
768  classImageBag.open(classBag_opt[0],ncol,nrow,nbag,GDT_Byte,imageType,option_opt);
769  classImageBag.GDALSetNoDataValue(nodata_opt[0]);
770  classImageBag.copyGeoTransform(testImage);
771  classImageBag.setProjection(testImage.getProjection());
772  }
773  classImageOut.open(output_opt[0],ncol,nrow,1,GDT_Byte,imageType,option_opt);
774  classImageOut.GDALSetNoDataValue(nodata_opt[0]);
775  classImageOut.copyGeoTransform(testImage);
776  classImageOut.setProjection(testImage.getProjection());
777  if(colorTable_opt.size())
778  classImageOut.setColorTable(colorTable_opt[0],0);
779  if(prob_opt.size()){
780  probImage.open(prob_opt[0],ncol,nrow,nclass,GDT_Byte,imageType,option_opt);
781  probImage.GDALSetNoDataValue(nodata_opt[0]);
782  probImage.copyGeoTransform(testImage);
783  probImage.setProjection(testImage.getProjection());
784  }
785  if(entropy_opt.size()){
786  entropyImage.open(entropy_opt[0],ncol,nrow,1,GDT_Byte,imageType,option_opt);
787  entropyImage.GDALSetNoDataValue(nodata_opt[0]);
788  entropyImage.copyGeoTransform(testImage);
789  entropyImage.setProjection(testImage.getProjection());
790  }
791  }
792  catch(string error){
793  cerr << error << endl;
794  }
795 
796  for(int iline=0;iline<nrow;++iline){
797  vector<float> buffer(ncol);
798  vector<short> lineMask;
799  if(mask_opt.size())
800  lineMask.resize(maskReader.nrOfCol());
801  Vector2d<float> linePrior;
802  if(priorimg_opt.size())
803  linePrior.resize(nclass,ncol);//prior prob for each class
804  Vector2d<float> hpixel(ncol);
805  Vector2d<float> fpixel(ncol);
806  Vector2d<float> probOut(nclass,ncol);//posterior prob for each (internal) class
807  vector<float> entropy(ncol);
808  Vector2d<char> classBag;//classified line for writing to image file
809  if(classBag_opt.size())
810  classBag.resize(nbag,ncol);
811  //read all bands of all pixels in this line in hline
812  try{
813  if(band_opt.size()){
814  for(int iband=0;iband<band_opt.size();++iband){
815  if(verbose_opt[0]==2)
816  std::cout << "reading band " << band_opt[iband] << std::endl;
817  assert(band_opt[iband]>=0);
818  assert(band_opt[iband]<testImage.nrOfBand());
819  testImage.readData(buffer,GDT_Float32,iline,band_opt[iband]);
820  for(int icol=0;icol<ncol;++icol)
821  hpixel[icol].push_back(buffer[icol]);
822  }
823  }
824  else{
825  for(int iband=bstart_opt[0];iband<bstart_opt[0]+nband;++iband){
826  if(verbose_opt[0]==2)
827  std::cout << "reading band " << iband << std::endl;
828  assert(iband>=0);
829  assert(iband<testImage.nrOfBand());
830  testImage.readData(buffer,GDT_Float32,iline,iband);
831  for(int icol=0;icol<ncol;++icol)
832  hpixel[icol].push_back(buffer[icol]);
833  }
834  }
835  }
836  catch(string theError){
837  cerr << "Error reading " << input_opt[0] << ": " << theError << std::endl;
838  exit(3);
839  }
840  catch(...){
841  cerr << "error catched" << std::endl;
842  exit(3);
843  }
844  assert(nband==hpixel[0].size());
845  if(verbose_opt[0]==2)
846  cout << "used bands: " << nband << endl;
847  //read prior
848  if(priorimg_opt.size()){
849  try{
850  for(short iclass=0;iclass<nclass;++iclass){
851  if(verbose_opt.size()>1)
852  std::cout << "Reading " << priorimg_opt[0] << " band " << iclass << " line " << iline << std::endl;
853  priorReader.readData(linePrior[iclass],GDT_Float32,iline,iclass);
854  }
855  }
856  catch(string theError){
857  std::cerr << "Error reading " << priorimg_opt[0] << ": " << theError << std::endl;
858  exit(3);
859  }
860  catch(...){
861  cerr << "error catched" << std::endl;
862  exit(3);
863  }
864  }
865  double oldRowMask=-1;//keep track of row mask to optimize number of line readings
866  //process per pixel
867  for(int icol=0;icol<ncol;++icol){
868  assert(hpixel[icol].size()==nband);
869  bool masked=false;
870  if(mask_opt.size()){
871  //read mask
872  double colMask=0;
873  double rowMask=0;
874  double geox=0;
875  double geoy=0;
876 
877  testImage.image2geo(icol,iline,geox,geoy);
878  maskReader.geo2image(geox,geoy,colMask,rowMask);
879  colMask=static_cast<int>(colMask);
880  rowMask=static_cast<int>(rowMask);
881  if(rowMask>=0&&rowMask<maskReader.nrOfRow()&&colMask>=0&&colMask<maskReader.nrOfCol()){
882  if(static_cast<int>(rowMask)!=static_cast<int>(oldRowMask)){
883  assert(rowMask>=0&&rowMask<maskReader.nrOfRow());
884  try{
885  // maskReader.readData(lineMask[imask],GDT_Int32,static_cast<int>(rowMask));
886  maskReader.readData(lineMask,GDT_Int16,static_cast<int>(rowMask));
887  }
888  catch(string errorstring){
889  cerr << errorstring << endl;
890  exit(1);
891  }
892  catch(...){
893  cerr << "error catched" << std::endl;
894  exit(3);
895  }
896  oldRowMask=rowMask;
897  }
898  short theMask=0;
899  for(short ivalue=0;ivalue<msknodata_opt.size();++ivalue){
900  if(msknodata_opt[ivalue]>=0){//values set in msknodata_opt are invalid
901  if(lineMask[colMask]==msknodata_opt[ivalue]){
902  theMask=lineMask[colMask];
903  masked=true;
904  break;
905  }
906  }
907  else{//only values set in msknodata_opt are valid
908  if(lineMask[colMask]!=-msknodata_opt[ivalue]){
909  theMask=lineMask[colMask];
910  masked=true;
911  }
912  else{
913  masked=false;
914  break;
915  }
916  }
917  }
918  if(masked){
919  if(classBag_opt.size())
920  for(int ibag=0;ibag<nbag;++ibag)
921  classBag[ibag][icol]=theMask;
922  classOut[icol]=theMask;
923  continue;
924  }
925  }
926  bool valid=false;
927  for(int iband=0;iband<nband;++iband){
928  if(hpixel[icol][iband]){
929  valid=true;
930  break;
931  }
932  }
933  if(!valid){
934  if(classBag_opt.size())
935  for(int ibag=0;ibag<nbag;++ibag)
936  classBag[ibag][icol]=nodata_opt[0];
937  classOut[icol]=nodata_opt[0];
938  continue;//next column
939  }
940  }
941  for(int iclass=0;iclass<nclass;++iclass)
942  probOut[iclass][icol]=0;
943  if(verbose_opt[0]>1)
944  std::cout << "begin classification " << std::endl;
945  //----------------------------------- classification -------------------
946  for(int ibag=0;ibag<nbag;++ibag){
947  //calculate image features
948  fpixel[icol].clear();
949  for(int iband=0;iband<nband;++iband)
950  fpixel[icol].push_back((hpixel[icol][iband]-offset[ibag][iband])/scale[ibag][iband]);
951  vector<float> result(nclass);
952  result=net[ibag].run(fpixel[icol]);
953  int maxClass=0;
954  vector<float> prValues(nclass);
955  float maxP=0;
956 
957  //calculate posterior prob of bag
958  if(classBag_opt.size()){
959  //search for max prob within bag
960  maxP=0;
961  classBag[ibag][icol]=0;
962  }
963  double normPrior=0;
964  if(priorimg_opt.size()){
965  for(short iclass=0;iclass<nclass;++iclass)
966  normPrior+=linePrior[iclass][icol];
967  }
968  for(int iclass=0;iclass<nclass;++iclass){
969  result[iclass]=(result[iclass]+1.0)/2.0;//bring back to scale [0,1]
970  if(priorimg_opt.size())
971  priors[iclass]=linePrior[iclass][icol]/normPrior;//todo: check if correct for all cases... (automatic classValueMap and manual input for names and values)
972  switch(comb_opt[0]){
973  default:
974  case(0)://sum rule
975  probOut[iclass][icol]+=result[iclass]*priors[iclass];//add probabilities for each bag
976  break;
977  case(1)://product rule
978  probOut[iclass][icol]*=pow(static_cast<float>(priors[iclass]),static_cast<float>(1.0-nbag)/nbag)*result[iclass];//multiply probabilities for each bag
979  break;
980  case(2)://max rule
981  if(priors[iclass]*result[iclass]>probOut[iclass][icol])
982  probOut[iclass][icol]=priors[iclass]*result[iclass];
983  break;
984  }
985  if(classBag_opt.size()){
986  //search for max prob within bag
987  // if(prValues[iclass]>maxP){
988  // maxP=prValues[iclass];
989  // classBag[ibag][icol]=vcode[iclass];
990  // }
991  if(result[iclass]>maxP){
992  maxP=result[iclass];
993  classBag[ibag][icol]=iclass;
994  }
995  }
996  }
997  }//ibag
998 
999  //search for max class prob
1000  float maxBag1=0;//max probability
1001  float maxBag2=0;//second max probability
1002  float normBag=0;
1003  for(short iclass=0;iclass<nclass;++iclass){
1004  if(probOut[iclass][icol]>maxBag1){
1005  maxBag1=probOut[iclass][icol];
1006  classOut[icol]=classValueMap[nameVector[iclass]];
1007  }
1008  else if(probOut[iclass][icol]>maxBag2)
1009  maxBag2=probOut[iclass][icol];
1010  normBag+=probOut[iclass][icol];
1011  }
1012  //normalize probOut and convert to percentage
1013  entropy[icol]=0;
1014  for(short iclass=0;iclass<nclass;++iclass){
1015  float prv=probOut[iclass][icol];
1016  prv/=normBag;
1017  entropy[icol]-=prv*log(prv)/log(2.0);
1018  prv*=100.0;
1019 
1020  probOut[iclass][icol]=static_cast<short>(prv+0.5);
1021  // assert(classValueMap[nameVector[iclass]]<probOut.size());
1022  // assert(classValueMap[nameVector[iclass]]>=0);
1023  // probOut[classValueMap[nameVector[iclass]]][icol]=static_cast<short>(prv+0.5);
1024  }
1025  entropy[icol]/=log(static_cast<double>(nclass))/log(2.0);
1026  entropy[icol]=static_cast<short>(100*entropy[icol]+0.5);
1027  if(active_opt.size()){
1028  if(entropy[icol]>activePoints.back().value){
1029  activePoints.back().value=entropy[icol];//replace largest value (last)
1030  activePoints.back().posx=icol;
1031  activePoints.back().posy=iline;
1032  std::sort(activePoints.begin(),activePoints.end(),Decrease_PosValue());//sort in descending order (largest first, smallest last)
1033  if(verbose_opt[0])
1034  std::cout << activePoints.back().posx << " " << activePoints.back().posy << " " << activePoints.back().value << std::endl;
1035  }
1036  }
1037  }//icol
1038  //----------------------------------- write output ------------------------------------------
1039  if(classBag_opt.size())
1040  for(int ibag=0;ibag<nbag;++ibag)
1041  classImageBag.writeData(classBag[ibag],GDT_Byte,iline,ibag);
1042  if(prob_opt.size()){
1043  for(int iclass=0;iclass<nclass;++iclass)
1044  probImage.writeData(probOut[iclass],GDT_Float32,iline,iclass);
1045  }
1046  if(entropy_opt.size()){
1047  entropyImage.writeData(entropy,GDT_Float32,iline);
1048  }
1049  classImageOut.writeData(classOut,GDT_Byte,iline);
1050  if(!verbose_opt[0]){
1051  progress=static_cast<float>(iline+1.0)/classImageOut.nrOfRow();
1052  pfnProgress(progress,pszMessage,pProgressArg);
1053  }
1054  }
1055  //write active learning points
1056  if(active_opt.size()){
1057  for(int iactive=0;iactive<activePoints.size();++iactive){
1058  std::map<string,double> pointMap;
1059  for(int iband=0;iband<testImage.nrOfBand();++iband){
1060  double value;
1061  testImage.readData(value,GDT_Float64,static_cast<int>(activePoints[iactive].posx),static_cast<int>(activePoints[iactive].posy),iband);
1062  ostringstream fs;
1063  fs << "B" << iband;
1064  pointMap[fs.str()]=value;
1065  }
1066  pointMap[label_opt[0]]=0;
1067  double x, y;
1068  testImage.image2geo(activePoints[iactive].posx,activePoints[iactive].posy,x,y);
1069  std::string fieldname="id";//number of the point
1070  activeWriter.addPoint(x,y,pointMap,fieldname,++nactive);
1071  }
1072  }
1073 
1074  testImage.close();
1075  if(mask_opt.size())
1076  maskReader.close();
1077  if(priorimg_opt.size())
1078  priorReader.close();
1079  if(prob_opt.size())
1080  probImage.close();
1081  if(entropy_opt.size())
1082  entropyImage.close();
1083  if(classBag_opt.size())
1084  classImageBag.close();
1085  classImageOut.close();
1086  }
1087  else{//classify vector file
1088  cm.clearResults();
1089  //notice that fields have already been set by readDataImageOgr (taking into account appropriate bands)
1090  for(int ivalidation=0;ivalidation<input_opt.size();++ivalidation){
1091  if(output_opt.size())
1092  assert(output_opt.size()==input_opt.size());
1093  if(verbose_opt[0])
1094  cout << "opening img reader " << input_opt[ivalidation] << endl;
1095  imgReaderOgr.open(input_opt[ivalidation]);
1096  ImgWriterOgr imgWriterOgr;
1097 
1098  if(output_opt.size()){
1099  if(verbose_opt[0])
1100  std::cout << "opening img writer and copying fields from img reader" << output_opt[ivalidation] << std::endl;
1101  imgWriterOgr.open(output_opt[ivalidation],imgReaderOgr);
1102  }
1103  if(verbose_opt[0])
1104  cout << "number of layers in input ogr file: " << imgReaderOgr.getLayerCount() << endl;
1105  for(int ilayer=0;ilayer<imgReaderOgr.getLayerCount();++ilayer){
1106  if(verbose_opt[0])
1107  cout << "processing input layer " << ilayer << endl;
1108  if(output_opt.size()){
1109  if(verbose_opt[0])
1110  std::cout << "creating field class" << std::endl;
1111  if(classValueMap.size())
1112  imgWriterOgr.createField("class",OFTInteger,ilayer);
1113  else
1114  imgWriterOgr.createField("class",OFTString,ilayer);
1115  }
1116  unsigned int nFeatures=imgReaderOgr.getFeatureCount(ilayer);
1117  unsigned int ifeature=0;
1118  progress=0;
1119  pfnProgress(progress,pszMessage,pProgressArg);
1120  OGRFeature *poFeature;
1121  while( (poFeature = imgReaderOgr.getLayer(ilayer)->GetNextFeature()) != NULL ){
1122  if(verbose_opt[0]>1)
1123  cout << "feature " << ifeature << endl;
1124  if( poFeature == NULL ){
1125  cout << "Warning: could not read feature " << ifeature << " in layer " << imgReaderOgr.getLayerName(ilayer) << endl;
1126  continue;
1127  }
1128  OGRFeature *poDstFeature = NULL;
1129  if(output_opt.size()){
1130  poDstFeature=imgWriterOgr.createFeature(ilayer);
1131  if( poDstFeature->SetFrom( poFeature, TRUE ) != OGRERR_NONE ){
1132  CPLError( CE_Failure, CPLE_AppDefined,
1133  "Unable to translate feature %d from layer %s.\n",
1134  poFeature->GetFID(), imgWriterOgr.getLayerName(ilayer).c_str() );
1135  OGRFeature::DestroyFeature( poFeature );
1136  OGRFeature::DestroyFeature( poDstFeature );
1137  }
1138  }
1139  vector<float> validationPixel;
1140  vector<float> validationFeature;
1141 
1142  imgReaderOgr.readData(validationPixel,OFTReal,fields,poFeature,ilayer);
1143  assert(validationPixel.size()==nband);
1144  vector<float> probOut(nclass);//posterior prob for each class
1145  for(int iclass=0;iclass<nclass;++iclass)
1146  probOut[iclass]=0;
1147  for(int ibag=0;ibag<nbag;++ibag){
1148  for(int iband=0;iband<nband;++iband){
1149  validationFeature.push_back((validationPixel[iband]-offset[ibag][iband])/scale[ibag][iband]);
1150  if(verbose_opt[0]==2)
1151  std:: cout << " " << validationFeature.back();
1152  }
1153  if(verbose_opt[0]==2)
1154  std::cout << std:: endl;
1155  vector<float> result(nclass);
1156  result=net[ibag].run(validationFeature);
1157 
1158  if(verbose_opt[0]>1){
1159  for(int iclass=0;iclass<result.size();++iclass)
1160  std::cout << result[iclass] << " ";
1161  std::cout << std::endl;
1162  }
1163  //calculate posterior prob of bag
1164  for(int iclass=0;iclass<nclass;++iclass){
1165  result[iclass]=(result[iclass]+1.0)/2.0;//bring back to scale [0,1]
1166  switch(comb_opt[0]){
1167  default:
1168  case(0)://sum rule
1169  probOut[iclass]+=result[iclass]*priors[iclass];//add probabilities for each bag
1170  break;
1171  case(1)://product rule
1172  probOut[iclass]*=pow(static_cast<float>(priors[iclass]),static_cast<float>(1.0-nbag)/nbag)*result[iclass];//multiply probabilities for each bag
1173  break;
1174  case(2)://max rule
1175  if(priors[iclass]*result[iclass]>probOut[iclass])
1176  probOut[iclass]=priors[iclass]*result[iclass];
1177  break;
1178  }
1179  }
1180  }//for ibag
1181  //search for max class prob
1182  float maxBag=0;
1183  float normBag=0;
1184  string classOut="Unclassified";
1185  for(int iclass=0;iclass<nclass;++iclass){
1186  if(verbose_opt[0]>1)
1187  std::cout << probOut[iclass] << " ";
1188  if(probOut[iclass]>maxBag){
1189  maxBag=probOut[iclass];
1190  classOut=nameVector[iclass];
1191  }
1192  }
1193  //look for class name
1194  if(verbose_opt[0]>1){
1195  if(classValueMap.size())
1196  std::cout << "->" << classValueMap[classOut] << std::endl;
1197  else
1198  std::cout << "->" << classOut << std::endl;
1199  }
1200  if(output_opt.size()){
1201  if(classValueMap.size())
1202  poDstFeature->SetField("class",classValueMap[classOut]);
1203  else
1204  poDstFeature->SetField("class",classOut.c_str());
1205  poDstFeature->SetFID( poFeature->GetFID() );
1206  }
1207  int labelIndex=poFeature->GetFieldIndex(label_opt[0].c_str());
1208  if(labelIndex>=0){
1209  string classRef=poFeature->GetFieldAsString(labelIndex);
1210  if(classRef!="0"){
1211  if(classValueMap.size())
1212  cm.incrementResult(type2string<short>(classValueMap[classRef]),type2string<short>(classValueMap[classOut]),1);
1213  else
1214  cm.incrementResult(classRef,classOut,1);
1215  }
1216  }
1217  CPLErrorReset();
1218  if(output_opt.size()){
1219  if(imgWriterOgr.createFeature(poDstFeature,ilayer) != OGRERR_NONE){
1220  CPLError( CE_Failure, CPLE_AppDefined,
1221  "Unable to translate feature %d from layer %s.\n",
1222  poFeature->GetFID(), imgWriterOgr.getLayerName(ilayer).c_str() );
1223  OGRFeature::DestroyFeature( poDstFeature );
1224  OGRFeature::DestroyFeature( poDstFeature );
1225  }
1226  }
1227  ++ifeature;
1228  if(!verbose_opt[0]){
1229  progress=static_cast<float>(ifeature+1.0)/nFeatures;
1230  pfnProgress(progress,pszMessage,pProgressArg);
1231  }
1232  OGRFeature::DestroyFeature( poFeature );
1233  OGRFeature::DestroyFeature( poDstFeature );
1234  }//get next feature
1235  }//next layer
1236  imgReaderOgr.close();
1237  if(output_opt.size())
1238  imgWriterOgr.close();
1239  }
1240  if(cm.nReference()){
1241  std::cout << cm << std::endl;
1242  // cout << "class #samples userAcc prodAcc" << endl;
1243  // double se95_ua=0;
1244  // double se95_pa=0;
1245  // double se95_oa=0;
1246  // double dua=0;
1247  // double dpa=0;
1248  // double doa=0;
1249  // for(short iclass=0;iclass<cm.nClasses();++iclass){
1250  // dua=cm.ua_pct(cm.getClass(iclass),&se95_ua);
1251  // dpa=cm.pa_pct(cm.getClass(iclass),&se95_pa);
1252  // cout << cm.getClass(iclass) << " " << cm.nReference(cm.getClass(iclass)) << " " << dua << " (" << se95_ua << ")" << " " << dpa << " (" << se95_pa << ")" << endl;
1253  // }
1254  // std::cout << "Kappa: " << cm.kappa() << std::endl;
1255  // doa=cm.oa_pct(&se95_oa);
1256  // std::cout << "Overall Accuracy: " << doa << " (" << se95_oa << ")" << std::endl;
1257  }
1258  }
1259  try{
1260  if(active_opt.size())
1261  activeWriter.close();
1262  }
1263  catch(string errorString){
1264  std::cerr << "Error: errorString" << std::endl;
1265  }
1266  return 0;
1267 }