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