pktools  2.6.7
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=true;
740  ImgReaderOgr imgReaderOgr;
741  if(inputIsRaster){
742  // if(input_opt[0].find(".shp")==string::npos){
743  ImgReaderGdal testImage;
744  try{
745  if(verbose_opt[0]>=1)
746  cout << "opening image " << input_opt[0] << endl;
747  testImage.open(input_opt[0]);
748  }
749  catch(string error){
750  cerr << error << endl;
751  exit(2);
752  }
753  ImgReaderGdal priorReader;
754  if(priorimg_opt.size()){
755  try{
756  if(verbose_opt[0]>=1)
757  std::cout << "opening prior image " << priorimg_opt[0] << std::endl;
758  priorReader.open(priorimg_opt[0]);
759  assert(priorReader.nrOfCol()==testImage.nrOfCol());
760  assert(priorReader.nrOfRow()==testImage.nrOfRow());
761  }
762  catch(string error){
763  cerr << error << std::endl;
764  exit(2);
765  }
766  catch(...){
767  cerr << "error caught" << std::endl;
768  exit(1);
769  }
770  }
771 
772  int nrow=testImage.nrOfRow();
773  int ncol=testImage.nrOfCol();
774  if(option_opt.findSubstring("INTERLEAVE=")==option_opt.end()){
775  string theInterleave="INTERLEAVE=";
776  theInterleave+=testImage.getInterleave();
777  option_opt.push_back(theInterleave);
778  }
779  vector<char> classOut(ncol);//classified line for writing to image file
780 
781  // assert(nband==testImage.nrOfBand());
782  ImgWriterGdal classImageBag;
783  ImgWriterGdal classImageOut;
784  ImgWriterGdal probImage;
785  ImgWriterGdal entropyImage;
786 
787  string imageType;//testImage.getImageType();
788  if(oformat_opt.size())//default
789  imageType=oformat_opt[0];
790  try{
791 
792  if(verbose_opt[0]>=1)
793  cout << "opening class image for writing output " << output_opt[0] << endl;
794  if(classBag_opt.size()){
795  classImageBag.open(classBag_opt[0],ncol,nrow,nbag,GDT_Byte,imageType,option_opt);
796  classImageBag.GDALSetNoDataValue(nodata_opt[0]);
797  classImageBag.copyGeoTransform(testImage);
798  classImageBag.setProjection(testImage.getProjection());
799  }
800  classImageOut.open(output_opt[0],ncol,nrow,1,GDT_Byte,imageType,option_opt);
801  classImageOut.GDALSetNoDataValue(nodata_opt[0]);
802  classImageOut.copyGeoTransform(testImage);
803  classImageOut.setProjection(testImage.getProjection());
804  if(colorTable_opt.size())
805  classImageOut.setColorTable(colorTable_opt[0],0);
806  if(prob_opt.size()){
807  probImage.open(prob_opt[0],ncol,nrow,nclass,GDT_Byte,imageType,option_opt);
808  probImage.GDALSetNoDataValue(nodata_opt[0]);
809  probImage.copyGeoTransform(testImage);
810  probImage.setProjection(testImage.getProjection());
811  }
812  if(entropy_opt.size()){
813  entropyImage.open(entropy_opt[0],ncol,nrow,1,GDT_Byte,imageType,option_opt);
814  entropyImage.GDALSetNoDataValue(nodata_opt[0]);
815  entropyImage.copyGeoTransform(testImage);
816  entropyImage.setProjection(testImage.getProjection());
817  }
818  }
819  catch(string error){
820  cerr << error << endl;
821  }
822 
823  ImgWriterGdal maskWriter;
824 
825  if(maskIsVector){
826  try{
827  maskWriter.open("/vsimem/mask.tif",ncol,nrow,1,GDT_Float32,imageType,option_opt);
828  maskWriter.GDALSetNoDataValue(nodata_opt[0]);
829  maskWriter.copyGeoTransform(testImage);
830  maskWriter.setProjection(testImage.getProjection());
831  vector<double> burnValues(1,1);//burn value is 1 (single band)
832  maskWriter.rasterizeOgr(extentReader,burnValues);
833  extentReader.close();
834  maskWriter.close();
835  }
836  catch(string error){
837  cerr << error << std::endl;
838  exit(2);
839  }
840  catch(...){
841  cerr << "error caught" << std::endl;
842  exit(1);
843  }
844  mask_opt.clear();
845  mask_opt.push_back("/vsimem/mask.tif");
846  }
847  ImgReaderGdal maskReader;
848  if(mask_opt.size()){
849  try{
850  if(verbose_opt[0]>=1)
851  std::cout << "opening mask image file " << mask_opt[0] << std::endl;
852  maskReader.open(mask_opt[0]);
853  }
854  catch(string error){
855  cerr << error << std::endl;
856  exit(2);
857  }
858  catch(...){
859  cerr << "error caught" << std::endl;
860  exit(1);
861  }
862  }
863 
864  for(int iline=0;iline<nrow;++iline){
865  vector<float> buffer(ncol);
866  vector<short> lineMask;
867  if(mask_opt.size())
868  lineMask.resize(maskReader.nrOfCol());
869  Vector2d<float> linePrior;
870  if(priorimg_opt.size())
871  linePrior.resize(nclass,ncol);//prior prob for each class
872  Vector2d<float> hpixel(ncol);
873  Vector2d<float> fpixel(ncol);
874  Vector2d<float> probOut(nclass,ncol);//posterior prob for each (internal) class
875  vector<float> entropy(ncol);
876  Vector2d<char> classBag;//classified line for writing to image file
877  if(classBag_opt.size())
878  classBag.resize(nbag,ncol);
879  //read all bands of all pixels in this line in hline
880  try{
881  if(band_opt.size()){
882  for(int iband=0;iband<band_opt.size();++iband){
883  if(verbose_opt[0]==2)
884  std::cout << "reading band " << band_opt[iband] << std::endl;
885  assert(band_opt[iband]>=0);
886  assert(band_opt[iband]<testImage.nrOfBand());
887  testImage.readData(buffer,iline,band_opt[iband]);
888  for(int icol=0;icol<ncol;++icol)
889  hpixel[icol].push_back(buffer[icol]);
890  }
891  }
892  else{
893  for(int iband=0;iband<nband;++iband){
894  if(verbose_opt[0]==2)
895  std::cout << "reading band " << iband << std::endl;
896  assert(iband>=0);
897  assert(iband<testImage.nrOfBand());
898  testImage.readData(buffer,iline,iband);
899  for(int icol=0;icol<ncol;++icol)
900  hpixel[icol].push_back(buffer[icol]);
901  }
902  }
903  }
904  catch(string theError){
905  cerr << "Error reading " << input_opt[0] << ": " << theError << std::endl;
906  exit(3);
907  }
908  catch(...){
909  cerr << "error caught" << std::endl;
910  exit(3);
911  }
912  assert(nband==hpixel[0].size());
913  if(verbose_opt[0]==2)
914  cout << "used bands: " << nband << endl;
915  //read prior
916  if(priorimg_opt.size()){
917  try{
918  for(short iclass=0;iclass<nclass;++iclass){
919  if(verbose_opt.size()>1)
920  std::cout << "Reading " << priorimg_opt[0] << " band " << iclass << " line " << iline << std::endl;
921  priorReader.readData(linePrior[iclass],iline,iclass);
922  }
923  }
924  catch(string theError){
925  std::cerr << "Error reading " << priorimg_opt[0] << ": " << theError << std::endl;
926  exit(3);
927  }
928  catch(...){
929  cerr << "error caught" << std::endl;
930  exit(3);
931  }
932  }
933  double oldRowMask=-1;//keep track of row mask to optimize number of line readings
934  //process per pixel
935  for(int icol=0;icol<ncol;++icol){
936  assert(hpixel[icol].size()==nband);
937  bool doClassify=true;
938  bool masked=false;
939  double geox=0;
940  double geoy=0;
941  if(maskIsVector){
942  doClassify=false;
943  testImage.image2geo(icol,iline,geox,geoy);
944  //check enveloppe first
945  if(uly>=geoy&&lry<=geoy&&ulx<=geox&&lrx>=geox){
946  doClassify=true;
947  }
948  }
949  if(mask_opt.size()){
950  //read mask
951  double colMask=0;
952  double rowMask=0;
953 
954  testImage.image2geo(icol,iline,geox,geoy);
955  maskReader.geo2image(geox,geoy,colMask,rowMask);
956  colMask=static_cast<int>(colMask);
957  rowMask=static_cast<int>(rowMask);
958  if(rowMask>=0&&rowMask<maskReader.nrOfRow()&&colMask>=0&&colMask<maskReader.nrOfCol()){
959  if(static_cast<int>(rowMask)!=static_cast<int>(oldRowMask)){
960  assert(rowMask>=0&&rowMask<maskReader.nrOfRow());
961  try{
962  // maskReader.readData(lineMask[imask],GDT_Int32,static_cast<int>(rowMask));
963  maskReader.readData(lineMask,static_cast<int>(rowMask));
964  }
965  catch(string errorstring){
966  cerr << errorstring << endl;
967  exit(1);
968  }
969  catch(...){
970  cerr << "error caught" << std::endl;
971  exit(3);
972  }
973  oldRowMask=rowMask;
974  }
975  short theMask=0;
976  for(short ivalue=0;ivalue<msknodata_opt.size();++ivalue){
977  // if(msknodata_opt[ivalue]>=0){//values set in msknodata_opt are invalid
978  if(lineMask[colMask]==msknodata_opt[ivalue]){
979  theMask=lineMask[colMask];
980  masked=true;
981  break;
982  }
983  // }
984  // else{//only values set in msknodata_opt are valid
985  // if(lineMask[colMask]!=-msknodata_opt[ivalue]){
986  // theMask=lineMask[colMask];
987  // masked=true;
988  // }
989  // else{
990  // masked=false;
991  // break;
992  // }
993  // }
994  }
995  if(masked){
996  if(classBag_opt.size())
997  for(int ibag=0;ibag<nbag;++ibag)
998  classBag[ibag][icol]=theMask;
999  classOut[icol]=theMask;
1000  continue;
1001  }
1002  }
1003  bool valid=false;
1004  for(int iband=0;iband<hpixel[icol].size();++iband){
1005  if(hpixel[icol][iband]){
1006  valid=true;
1007  break;
1008  }
1009  }
1010  if(!valid)
1011  doClassify=false;
1012 
1013  }
1014  for(short iclass=0;iclass<nclass;++iclass)
1015  probOut[iclass][icol]=0;
1016  if(!doClassify){
1017  if(classBag_opt.size())
1018  for(int ibag=0;ibag<nbag;++ibag)
1019  classBag[ibag][icol]=nodata_opt[0];
1020  classOut[icol]=nodata_opt[0];
1021  continue;//next column
1022  }
1023  if(verbose_opt[0]>1)
1024  std::cout << "begin classification " << std::endl;
1025  //----------------------------------- classification -------------------
1026  for(int ibag=0;ibag<nbag;++ibag){
1027  //calculate image features
1028  fpixel[icol].clear();
1029  for(int iband=0;iband<nband;++iband)
1030  fpixel[icol].push_back((hpixel[icol][iband]-offset[ibag][iband])/scale[ibag][iband]);
1031  vector<float> result(nclass);
1032  result=net[ibag].run(fpixel[icol]);
1033  int maxClass=0;
1034  vector<float> prValues(nclass);
1035  float maxP=0;
1036 
1037  //calculate posterior prob of bag
1038  if(classBag_opt.size()){
1039  //search for max prob within bag
1040  maxP=0;
1041  classBag[ibag][icol]=0;
1042  }
1043  double normPrior=0;
1044  if(priorimg_opt.size()){
1045  for(short iclass=0;iclass<nclass;++iclass)
1046  normPrior+=linePrior[iclass][icol];
1047  }
1048  for(int iclass=0;iclass<nclass;++iclass){
1049  result[iclass]=(result[iclass]+1.0)/2.0;//bring back to scale [0,1]
1050  if(priorimg_opt.size())
1051  priors[iclass]=linePrior[iclass][icol]/normPrior;//todo: check if correct for all cases... (automatic classValueMap and manual input for names and values)
1052  switch(comb_opt[0]){
1053  default:
1054  case(0)://sum rule
1055  probOut[iclass][icol]+=result[iclass]*priors[iclass];//add probabilities for each bag
1056  break;
1057  case(1)://product rule
1058  probOut[iclass][icol]*=pow(static_cast<float>(priors[iclass]),static_cast<float>(1.0-nbag)/nbag)*result[iclass];//multiply probabilities for each bag
1059  break;
1060  case(2)://max rule
1061  if(priors[iclass]*result[iclass]>probOut[iclass][icol])
1062  probOut[iclass][icol]=priors[iclass]*result[iclass];
1063  break;
1064  }
1065  if(classBag_opt.size()){
1066  //search for max prob within bag
1067  // if(prValues[iclass]>maxP){
1068  // maxP=prValues[iclass];
1069  // classBag[ibag][icol]=vcode[iclass];
1070  // }
1071  if(result[iclass]>maxP){
1072  maxP=result[iclass];
1073  classBag[ibag][icol]=iclass;
1074  }
1075  }
1076  }
1077  }//ibag
1078 
1079  //search for max class prob
1080  float maxBag1=0;//max probability
1081  float maxBag2=0;//second max probability
1082  float normBag=0;
1083  for(short iclass=0;iclass<nclass;++iclass){
1084  if(probOut[iclass][icol]>maxBag1){
1085  maxBag1=probOut[iclass][icol];
1086  classOut[icol]=classValueMap[nameVector[iclass]];
1087  }
1088  else if(probOut[iclass][icol]>maxBag2)
1089  maxBag2=probOut[iclass][icol];
1090  normBag+=probOut[iclass][icol];
1091  }
1092  //normalize probOut and convert to percentage
1093  entropy[icol]=0;
1094  for(short iclass=0;iclass<nclass;++iclass){
1095  float prv=probOut[iclass][icol];
1096  prv/=normBag;
1097  entropy[icol]-=prv*log(prv)/log(2.0);
1098  prv*=100.0;
1099 
1100  probOut[iclass][icol]=static_cast<short>(prv+0.5);
1101  // assert(classValueMap[nameVector[iclass]]<probOut.size());
1102  // assert(classValueMap[nameVector[iclass]]>=0);
1103  // probOut[classValueMap[nameVector[iclass]]][icol]=static_cast<short>(prv+0.5);
1104  }
1105  entropy[icol]/=log(static_cast<double>(nclass))/log(2.0);
1106  entropy[icol]=static_cast<short>(100*entropy[icol]+0.5);
1107  if(active_opt.size()){
1108  if(entropy[icol]>activePoints.back().value){
1109  activePoints.back().value=entropy[icol];//replace largest value (last)
1110  activePoints.back().posx=icol;
1111  activePoints.back().posy=iline;
1112  std::sort(activePoints.begin(),activePoints.end(),Decrease_PosValue());//sort in descending order (largest first, smallest last)
1113  if(verbose_opt[0])
1114  std::cout << activePoints.back().posx << " " << activePoints.back().posy << " " << activePoints.back().value << std::endl;
1115  }
1116  }
1117  }//icol
1118  //----------------------------------- write output ------------------------------------------
1119  if(classBag_opt.size())
1120  for(int ibag=0;ibag<nbag;++ibag)
1121  classImageBag.writeData(classBag[ibag],iline,ibag);
1122  if(prob_opt.size()){
1123  for(int iclass=0;iclass<nclass;++iclass)
1124  probImage.writeData(probOut[iclass],iline,iclass);
1125  }
1126  if(entropy_opt.size()){
1127  entropyImage.writeData(entropy,iline);
1128  }
1129  classImageOut.writeData(classOut,iline);
1130  if(!verbose_opt[0]){
1131  progress=static_cast<float>(iline+1.0)/classImageOut.nrOfRow();
1132  pfnProgress(progress,pszMessage,pProgressArg);
1133  }
1134  }
1135  //write active learning points
1136  if(active_opt.size()){
1137  for(int iactive=0;iactive<activePoints.size();++iactive){
1138  std::map<string,double> pointMap;
1139  for(int iband=0;iband<testImage.nrOfBand();++iband){
1140  double value;
1141  testImage.readData(value,static_cast<int>(activePoints[iactive].posx),static_cast<int>(activePoints[iactive].posy),iband);
1142  ostringstream fs;
1143  fs << "B" << iband;
1144  pointMap[fs.str()]=value;
1145  }
1146  pointMap[label_opt[0]]=0;
1147  double x, y;
1148  testImage.image2geo(activePoints[iactive].posx,activePoints[iactive].posy,x,y);
1149  std::string fieldname="id";//number of the point
1150  activeWriter.addPoint(x,y,pointMap,fieldname,++nactive);
1151  }
1152  }
1153 
1154  testImage.close();
1155  if(mask_opt.size())
1156  maskReader.close();
1157  if(priorimg_opt.size())
1158  priorReader.close();
1159  if(prob_opt.size())
1160  probImage.close();
1161  if(entropy_opt.size())
1162  entropyImage.close();
1163  if(classBag_opt.size())
1164  classImageBag.close();
1165  classImageOut.close();
1166  }
1167  try{
1168  if(active_opt.size())
1169  activeWriter.close();
1170  }
1171  catch(string errorString){
1172  std::cerr << "Error: errorString" << std::endl;
1173  }
1174  return 0;
1175 }
void rasterizeOgr(ImgReaderOgr &ogrReader, const std::vector< double > &burnValues, const std::vector< std::string > &controlOptions=std::vector< std::string >(), const std::vector< std::string > &layernames=std::vector< std::string >())
Rasterize an OGR vector dataset using the gdal algorithm "GDALRasterizeLayers".
bool geo2image(double x, double y, double &i, double &j) const
Convert georeferenced coordinates (x and y) to image coordinates (column and row) ...
int nrOfBand(void) const
Get the number of bands of this dataset.
STL namespace.
void close(void)
Set the memory (in MB) to cache a number of rows in memory.
int nrOfRow(void) const
Get the number of rows of this dataset.
void setColorTable(const std::string &filename, int band=0)
Set the color table using an (ASCII) file with 5 columns (value R G B alpha)
bool image2geo(double i, double j, double &x, double &y) const
Convert image coordinates (column and row) to georeferenced coordinates (x and y) ...
void readData(T &value, int col, int row, int band=0)
Read a single pixel cell value at a specific column and row for a specific band (all indices start co...
Definition: ImgReaderGdal.h:95
bool writeData(T &value, int col, int row, int band=0)
Write a single pixel cell value at a specific column and row for a specific band (all indices start c...
Definition: ImgWriterGdal.h:96
void copyGeoTransform(const ImgRasterGdal &imgSrc)
Copy geotransform information from another georeferenced image.
void open(const std::string &filename, const ImgReaderGdal &imgSrc, const std::vector< std::string > &options=std::vector< std::string >())
Open an image for writing, copying image attributes from a source image. Image is directly written to...
CPLErr setProjection(const std::string &projection)
Set the projection for this dataset in well known text (wkt) format.
std::string getInterleave() const
Get the band coding (interleave)
void close(void)
Close the image.
std::string getProjection(void) const
Get the projection string (deprecated, use getProjectionRef instead)
CPLErr GDALSetNoDataValue(double noDataValue, int band=0)
Set the GDAL (internal) no data value for this data set. Only a single no data value per band is supp...
void open(const std::string &filename, const GDALAccess &readMode=GA_ReadOnly)
Open an image.
int nrOfCol(void) const
Get the number of columns of this dataset.
Definition: ImgRasterGdal.h:98