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