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