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"
36 int main(
int argc,
char *argv[])
38 vector<double> priors;
42 Optionpk<string> training_opt(
"t",
"training",
"training vector file. A single vector file contains all training features (must be set as: B0, B1, B2,...) for all classes (class numbers identified by label option). Use multiple training files for bootstrap aggregation (alternative to the bag and bsize options, where a random subset is taken from a single training file)");
44 Optionpk<string> label_opt(
"label",
"label",
"identifier for class label in training vector file.",
"label");
45 Optionpk<unsigned int> balance_opt(
"bal",
"balance",
"balance the input data to this number of samples for each class", 0);
46 Optionpk<bool> random_opt(
"random",
"random",
"in case of balance, randomize input data",
true,2);
47 Optionpk<int> minSize_opt(
"min",
"min",
"if number of training pixels is less then min, do not take this class into account (0: consider all classes)", 0);
48 Optionpk<short> band_opt(
"b",
"band",
"band index (starting from 0, either use band option or use start to end)");
50 Optionpk<double> bend_opt(
"e",
"end",
"end band sequence number (set to 0 to include bands)", 0);
51 Optionpk<double> offset_opt(
"\0",
"offset",
"offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
52 Optionpk<double> scale_opt(
"\0",
"scale",
"scale value for each spectral band input features: refl=(DN[band]-offset[band])/scale[band] (use 0 if scale min and max in each band to -1.0 and 1.0)", 0.0);
53 Optionpk<unsigned short> aggreg_opt(
"a",
"aggreg",
"how to combine aggregated classifiers, see also rc option (1: sum rule, 2: max rule).",1);
54 Optionpk<double> priors_opt(
"prior",
"prior",
"prior probabilities for each class (e.g., -p 0.3 -p 0.3 -p 0.2 )", 0.0);
55 Optionpk<string> priorimg_opt(
"pim",
"priorimg",
"prior probability image (multi-band img with band for each class",
"",2);
57 Optionpk<unsigned int> nneuron_opt(
"nn",
"nneuron",
"number of neurons in hidden layers in neural network (multiple hidden layers are set by defining multiple number of neurons: -n 15 -n 1, default is one hidden layer with 5 neurons)", 5);
58 Optionpk<float> connection_opt(
"\0",
"connection",
"connection reate (default: 1.0 for a fully connected network)", 1.0);
59 Optionpk<float> learning_opt(
"l",
"learning",
"learning rate (default: 0.7)", 0.7);
60 Optionpk<float> weights_opt(
"w",
"weights",
"weights for neural network. Apply to fully connected network only, starting from first input neuron to last output neuron, including the bias neurons (last neuron in each but last layer)", 0.0);
61 Optionpk<unsigned int> maxit_opt(
"\0",
"maxit",
"number of maximum iterations (epoch) (default: 500)", 500);
62 Optionpk<unsigned short> comb_opt(
"comb",
"comb",
"how to combine bootstrap aggregation classifiers (0: sum rule, 1: product rule, 2: max rule). Also used to aggregate classes with rc option. Default is sum rule (0)",0);
64 Optionpk<int> bagSize_opt(
"bs",
"bsize",
"Percentage of features used from available training features for each bootstrap aggregation (one size for all classes, or a different size for each class respectively", 100);
65 Optionpk<string> classBag_opt(
"cb",
"classbag",
"output for each individual bootstrap aggregation (default is blank)");
66 Optionpk<string> mask_opt(
"m",
"mask",
"Use the first band of the specified file as a validity mask. Nodata values can be set with the option msknodata.");
67 Optionpk<short> msknodata_opt(
"msknodata",
"msknodata",
"mask value(s) not to consider for classification (use negative values if only these values should be taken into account). Values will be taken over in classification image. Default is 0", 0);
70 Optionpk<string> otype_opt(
"ot",
"otype",
"Data type for output image ({Byte/Int16/UInt16/UInt32/Int32/Float32/Float64/CInt16/CInt32/CFloat32/CFloat64}). Empty string: inherit type from input image");
71 Optionpk<string> oformat_opt(
"of",
"oformat",
"Output image format (see also gdal_translate). Empty string: inherit from input image");
72 Optionpk<string> option_opt(
"co",
"co",
"Creation option for output file. Multiple options can be specified.");
73 Optionpk<string> colorTable_opt(
"ct",
"ct",
"colour table in ASCII format having 5 columns: id R G B ALFA (0: transparent, 255: solid)");
74 Optionpk<string> prob_opt(
"\0",
"prob",
"probability image. Default is no probability image");
75 Optionpk<string> entropy_opt(
"entropy",
"entropy",
"entropy image (measure for uncertainty of classifier output",
"",2);
76 Optionpk<string> active_opt(
"active",
"active",
"ogr output for active training sample.",
"",2);
77 Optionpk<string> ogrformat_opt(
"f",
"f",
"Output ogr format for active training sample",
"SQLite");
80 Optionpk<short> classvalue_opt(
"r",
"reclass",
"list of class values (use same order as in class opt).");
81 Optionpk<short> verbose_opt(
"v",
"verbose",
"set to: 0 (results only), 1 (confusion matrix), 2 (debug)",0,2);
84 bstart_opt.setHide(1);
86 balance_opt.setHide(1);
87 minSize_opt.setHide(1);
89 bagSize_opt.setHide(1);
91 classBag_opt.setHide(1);
92 minSize_opt.setHide(1);
94 priorimg_opt.setHide(1);
95 minSize_opt.setHide(1);
96 offset_opt.setHide(1);
98 connection_opt.setHide(1);
99 weights_opt.setHide(1);
100 maxit_opt.setHide(1);
101 learning_opt.setHide(1);
105 doProcess=input_opt.retrieveOption(argc,argv);
106 training_opt.retrieveOption(argc,argv);
107 tlayer_opt.retrieveOption(argc,argv);
108 label_opt.retrieveOption(argc,argv);
109 balance_opt.retrieveOption(argc,argv);
110 random_opt.retrieveOption(argc,argv);
111 minSize_opt.retrieveOption(argc,argv);
112 band_opt.retrieveOption(argc,argv);
113 bstart_opt.retrieveOption(argc,argv);
114 bend_opt.retrieveOption(argc,argv);
115 offset_opt.retrieveOption(argc,argv);
116 scale_opt.retrieveOption(argc,argv);
117 aggreg_opt.retrieveOption(argc,argv);
118 priors_opt.retrieveOption(argc,argv);
119 priorimg_opt.retrieveOption(argc,argv);
120 cv_opt.retrieveOption(argc,argv);
121 nneuron_opt.retrieveOption(argc,argv);
122 connection_opt.retrieveOption(argc,argv);
123 weights_opt.retrieveOption(argc,argv);
124 learning_opt.retrieveOption(argc,argv);
125 maxit_opt.retrieveOption(argc,argv);
126 comb_opt.retrieveOption(argc,argv);
127 bag_opt.retrieveOption(argc,argv);
128 bagSize_opt.retrieveOption(argc,argv);
129 classBag_opt.retrieveOption(argc,argv);
130 mask_opt.retrieveOption(argc,argv);
131 msknodata_opt.retrieveOption(argc,argv);
132 nodata_opt.retrieveOption(argc,argv);
133 output_opt.retrieveOption(argc,argv);
134 otype_opt.retrieveOption(argc,argv);
135 oformat_opt.retrieveOption(argc,argv);
136 colorTable_opt.retrieveOption(argc,argv);
137 option_opt.retrieveOption(argc,argv);
138 prob_opt.retrieveOption(argc,argv);
139 entropy_opt.retrieveOption(argc,argv);
140 active_opt.retrieveOption(argc,argv);
141 ogrformat_opt.retrieveOption(argc,argv);
142 nactive_opt.retrieveOption(argc,argv);
143 classname_opt.retrieveOption(argc,argv);
144 classvalue_opt.retrieveOption(argc,argv);
145 verbose_opt.retrieveOption(argc,argv);
147 catch(
string predefinedString){
148 std::cout << predefinedString << std::endl;
153 cout <<
"Usage: pkann -t training [-i input -o output] [-cv value]" << endl;
155 cout <<
"short option -h shows basic options only, use long option --help to show all options" << endl;
159 if(entropy_opt[0]==
"")
161 if(active_opt[0]==
"")
163 if(priorimg_opt[0]==
"")
164 priorimg_opt.clear();
166 if(verbose_opt[0]>=1){
168 cout <<
"image filename: " << input_opt[0] << endl;
170 cout <<
"mask filename: " << mask_opt[0] << endl;
171 if(training_opt.size()){
172 cout <<
"training vector file: " << endl;
173 for(
int ifile=0;ifile<training_opt.size();++ifile)
174 cout << training_opt[ifile] << endl;
177 cerr <<
"no training file set!" << endl;
178 cout <<
"verbose: " << verbose_opt[0] << endl;
180 unsigned short nbag=(training_opt.size()>1)?training_opt.size():bag_opt[0];
181 if(verbose_opt[0]>=1)
182 cout <<
"number of bootstrap aggregations: " << nbag << endl;
185 if(active_opt.size()){
187 activeWriter.open(active_opt[0],ogrformat_opt[0]);
188 activeWriter.createLayer(active_opt[0],trainingReader.getProjection(),wkbPoint,NULL);
189 activeWriter.copyFields(trainingReader);
191 vector<PosValue> activePoints(nactive_opt[0]);
192 for(
int iactive=0;iactive<activePoints.size();++iactive){
193 activePoints[iactive].value=1.0;
194 activePoints[iactive].posx=0.0;
195 activePoints[iactive].posy=0.0;
198 unsigned int totalSamples=0;
199 unsigned int nactive=0;
200 vector<FANN::neural_net> net(nbag);
202 unsigned int nclass=0;
206 if(priors_opt.size()>1){
207 priors.resize(priors_opt.size());
209 for(
int iclass=0;iclass<priors_opt.size();++iclass){
210 priors[iclass]=priors_opt[iclass];
211 normPrior+=priors[iclass];
214 for(
int iclass=0;iclass<priors_opt.size();++iclass)
215 priors[iclass]/=normPrior;
220 std::sort(band_opt.begin(),band_opt.end());
222 map<string,short> classValueMap;
223 vector<std::string> nameVector;
224 if(classname_opt.size()){
225 assert(classname_opt.size()==classvalue_opt.size());
226 for(
int iclass=0;iclass<classname_opt.size();++iclass)
227 classValueMap[classname_opt[iclass]]=classvalue_opt[iclass];
231 vector< vector<double> > offset(nbag);
232 vector< vector<double> > scale(nbag);
233 map<string,Vector2d<float> > trainingMap;
234 vector< Vector2d<float> > trainingPixels;
235 vector<string> fields;
236 for(
int ibag=0;ibag<nbag;++ibag){
238 if(ibag<training_opt.size()){
240 trainingPixels.clear();
241 if(verbose_opt[0]>=1)
242 cout <<
"reading imageVector file " << training_opt[0] << endl;
246 totalSamples=trainingReaderBag.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
248 totalSamples=trainingReaderBag.readDataImageOgr(trainingMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
249 if(trainingMap.size()<2){
250 string errorstring=
"Error: could not read at least two classes from training file, did you provide class labels in training sample (see option label)?";
253 trainingReaderBag.close();
256 cerr << error << std::endl;
260 cerr <<
"error catched" << std::endl;
270 std::cout <<
"training pixels: " << std::endl;
271 map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
272 while(mapit!=trainingMap.end()){
274 if((mapit->second).size()<minSize_opt[0]){
275 trainingMap.erase(mapit);
278 trainingPixels.push_back(mapit->second);
280 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
284 nclass=trainingPixels.size();
285 if(classname_opt.size())
286 assert(nclass==classname_opt.size());
287 nband=(training_opt.size())?trainingPixels[0][0].size()-2:trainingPixels[0][0].size();
290 assert(nclass==trainingPixels.size());
291 assert(nband==(training_opt.size())?trainingPixels[0][0].size()-2:trainingPixels[0][0].size());
296 if(balance_opt[0]>0){
297 while(balance_opt.size()<nclass)
298 balance_opt.push_back(balance_opt.back());
302 for(
int iclass=0;iclass<nclass;++iclass){
303 if(trainingPixels[iclass].size()>balance_opt[iclass]){
304 while(trainingPixels[iclass].size()>balance_opt[iclass]){
305 int index=rand()%trainingPixels[iclass].size();
306 trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
310 int oldsize=trainingPixels[iclass].size();
311 for(
int isample=trainingPixels[iclass].size();isample<balance_opt[iclass];++isample){
312 int index = rand()%oldsize;
313 trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
316 totalSamples+=trainingPixels[iclass].size();
321 offset[ibag].resize(nband);
322 scale[ibag].resize(nband);
323 if(offset_opt.size()>1)
324 assert(offset_opt.size()==nband);
325 if(scale_opt.size()>1)
326 assert(scale_opt.size()==nband);
327 for(
int iband=0;iband<nband;++iband){
328 if(verbose_opt[0]>=1)
329 cout <<
"scaling for band" << iband << endl;
330 offset[ibag][iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
331 scale[ibag][iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
333 if(scale[ibag][iband]<=0){
334 float theMin=trainingPixels[0][0][iband+startBand];
335 float theMax=trainingPixels[0][0][iband+startBand];
336 for(
int iclass=0;iclass<nclass;++iclass){
337 for(
int isample=0;isample<trainingPixels[iclass].size();++isample){
338 if(theMin>trainingPixels[iclass][isample][iband+startBand])
339 theMin=trainingPixels[iclass][isample][iband+startBand];
340 if(theMax<trainingPixels[iclass][isample][iband+startBand])
341 theMax=trainingPixels[iclass][isample][iband+startBand];
344 offset[ibag][iband]=theMin+(theMax-theMin)/2.0;
345 scale[ibag][iband]=(theMax-theMin)/2.0;
346 if(verbose_opt[0]>=1){
347 std::cout <<
"Extreme image values for band " << iband <<
": [" << theMin <<
"," << theMax <<
"]" << std::endl;
348 std::cout <<
"Using offset, scale: " << offset[ibag][iband] <<
", " << scale[ibag][iband] << std::endl;
349 std::cout <<
"scaled values for band " << iband <<
": [" << (theMin-offset[ibag][iband])/scale[ibag][iband] <<
"," << (theMax-offset[ibag][iband])/scale[ibag][iband] <<
"]" << std::endl;
355 offset[ibag].resize(nband);
356 scale[ibag].resize(nband);
357 for(
int iband=0;iband<nband;++iband){
358 offset[ibag][iband]=offset[0][iband];
359 scale[ibag][iband]=scale[0][iband];
364 if(priors_opt.size()==1){
365 priors.resize(nclass);
366 for(
int iclass=0;iclass<nclass;++iclass)
367 priors[iclass]=1.0/nclass;
369 assert(priors_opt.size()==1||priors_opt.size()==nclass);
372 while(bagSize_opt.size()<nclass)
373 bagSize_opt.push_back(bagSize_opt.back());
375 if(verbose_opt[0]>=1){
376 std::cout <<
"number of bands: " << nband << std::endl;
377 std::cout <<
"number of classes: " << nclass << std::endl;
378 std::cout <<
"priors:";
379 if(priorimg_opt.empty()){
380 for(
int iclass=0;iclass<nclass;++iclass)
381 std::cout <<
" " << priors[iclass];
382 std::cout << std::endl;
385 map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
387 while(mapit!=trainingMap.end()){
388 nameVector.push_back(mapit->first);
389 if(classValueMap.size()){
391 if(classValueMap[mapit->first]>0){
392 if(cm.getClassIndex(type2string<short>(classValueMap[mapit->first]))<0)
393 cm.pushBackClassName(type2string<short>(classValueMap[mapit->first]),doSort);
396 std::cerr <<
"Error: names in classname option are not complete, please check names in training vector and make sure classvalue is > 0" << std::endl;
401 cm.pushBackClassName(mapit->first,doSort);
404 if(classname_opt.empty()){
406 for(
int iclass=0;iclass<nclass;++iclass){
408 std::cout << iclass <<
" " << cm.getClass(iclass) <<
" -> " << string2type<short>(cm.getClass(iclass)) << std::endl;
409 classValueMap[cm.getClass(iclass)]=string2type<short>(cm.getClass(iclass));
412 if(priors_opt.size()==nameVector.size()){
413 std::cerr <<
"Warning: please check if priors are provided in correct order!!!" << std::endl;
414 for(
int iclass=0;iclass<nameVector.size();++iclass)
415 std::cerr << nameVector[iclass] <<
" " << priors_opt[iclass] << std::endl;
420 vector< Vector2d<float> > trainingFeatures(nclass);
421 for(
int iclass=0;iclass<nclass;++iclass){
423 if(verbose_opt[0]>=1)
424 cout <<
"calculating features for class " << iclass << endl;
427 nctraining=(bagSize_opt[iclass]<100)? trainingPixels[iclass].size()/100.0*bagSize_opt[iclass] : trainingPixels[iclass].size();
430 assert(nctraining<=trainingPixels[iclass].size());
432 if(bagSize_opt[iclass]<100)
433 random_shuffle(trainingPixels[iclass].begin(),trainingPixels[iclass].end());
435 trainingFeatures[iclass].resize(nctraining);
436 for(
int isample=0;isample<nctraining;++isample){
438 for(
int iband=0;iband<nband;++iband){
439 float value=trainingPixels[iclass][isample][iband+startBand];
440 trainingFeatures[iclass][isample].push_back((value-offset[ibag][iband])/scale[ibag][iband]);
443 assert(trainingFeatures[iclass].size()==nctraining);
446 unsigned int nFeatures=trainingFeatures[0][0].size();
447 unsigned int ntraining=0;
448 for(
int iclass=0;iclass<nclass;++iclass)
449 ntraining+=trainingFeatures[iclass].size();
451 const unsigned int num_layers = nneuron_opt.size()+2;
452 const float desired_error = 0.0003;
453 const unsigned int iterations_between_reports = (verbose_opt[0])? maxit_opt[0]+1:0;
454 if(verbose_opt[0]>=1){
455 cout <<
"number of features: " << nFeatures << endl;
456 cout <<
"creating artificial neural network with " << nneuron_opt.size() <<
" hidden layer, having " << endl;
457 for(
int ilayer=0;ilayer<nneuron_opt.size();++ilayer)
458 cout << nneuron_opt[ilayer] <<
" ";
459 cout <<
"neurons" << endl;
460 cout <<
"connection_opt[0]: " << connection_opt[0] << std::endl;
461 cout <<
"num_layers: " << num_layers << std::endl;
462 cout <<
"nFeatures: " << nFeatures << std::endl;
463 cout <<
"nneuron_opt[0]: " << nneuron_opt[0] << std::endl;
464 cout <<
"number of classes (nclass): " << nclass << std::endl;
469 unsigned int layers[3];
471 layers[1]=nneuron_opt[0];
473 net[ibag].create_sparse_array(connection_opt[0],num_layers,layers);
477 unsigned int layers[4];
479 layers[1]=nneuron_opt[0];
480 layers[2]=nneuron_opt[1];
486 net[ibag].create_sparse_array(connection_opt[0],num_layers,layers);
490 cerr <<
"Only 1 or 2 hidden layers are supported!" << endl;
494 if(verbose_opt[0]>=1)
495 cout <<
"network created" << endl;
497 net[ibag].set_learning_rate(learning_opt[0]);
502 net[ibag].set_activation_function_hidden(FANN::SIGMOID_SYMMETRIC_STEPWISE);
503 net[ibag].set_activation_function_output(FANN::SIGMOID_SYMMETRIC_STEPWISE);
509 if(verbose_opt[0]>=1){
510 cout << endl <<
"Network Type : ";
511 switch (net[ibag].get_network_type())
514 cout <<
"LAYER" << endl;
517 cout <<
"SHORTCUT" << endl;
520 cout <<
"UNKNOWN" << endl;
523 net[ibag].print_parameters();
528 std::cout <<
"cross validation" << std::endl;
529 vector<unsigned short> referenceVector;
530 vector<unsigned short> outputVector;
531 float rmse=net[ibag].cross_validation(trainingFeatures,
539 map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
540 for(
int isample=0;isample<referenceVector.size();++isample){
541 string refClassName=nameVector[referenceVector[isample]];
542 string className=nameVector[outputVector[isample]];
543 if(classValueMap.size())
544 cm.incrementResult(type2string<short>(classValueMap[refClassName]),type2string<short>(classValueMap[className]),1.0/nbag);
546 cm.incrementResult(cm.getClass(referenceVector[isample]),cm.getClass(outputVector[isample]),1.0/nbag);
550 if(verbose_opt[0]>=1)
551 cout << endl <<
"Set training data" << endl;
553 if(verbose_opt[0]>=1)
554 cout << endl <<
"Training network" << endl;
556 if(verbose_opt[0]>=1){
557 cout <<
"Max Epochs " << setw(8) << maxit_opt[0] <<
". "
558 <<
"Desired Error: " << left << desired_error << right << endl;
560 if(weights_opt.size()==net[ibag].get_total_connections()){
561 vector<fann_connection> convector;
562 net[ibag].get_connection_array(convector);
563 for(
int i_connection=0;i_connection<net[ibag].get_total_connections();++i_connection)
564 convector[i_connection].weight=weights_opt[i_connection];
565 net[ibag].set_weight_array(convector);
568 bool initWeights=
true;
569 net[ibag].train_on_data(trainingFeatures,ntraining,initWeights, maxit_opt[0],
570 iterations_between_reports, desired_error);
574 if(verbose_opt[0]>=2){
575 net[ibag].print_connections();
576 vector<fann_connection> convector;
577 net[ibag].get_connection_array(convector);
578 for(
int i_connection=0;i_connection<net[ibag].get_total_connections();++i_connection)
579 cout <<
"connection " << i_connection <<
": " << convector[i_connection].weight << endl;
584 assert(cm.nReference());
585 std::cout << cm << std::endl;
586 cout <<
"class #samples userAcc prodAcc" << endl;
593 for(
int iclass=0;iclass<cm.nClasses();++iclass){
594 dua=cm.ua_pct(cm.getClass(iclass),&se95_ua);
595 dpa=cm.pa_pct(cm.getClass(iclass),&se95_pa);
596 cout << cm.getClass(iclass) <<
" " << cm.nReference(cm.getClass(iclass)) <<
" " << dua <<
" (" << se95_ua <<
")" <<
" " << dpa <<
" (" << se95_pa <<
")" << endl;
598 std::cout <<
"Kappa: " << cm.kappa() << std::endl;
599 doa=cm.oa_pct(&se95_oa);
600 std::cout <<
"Overall Accuracy: " << doa <<
" (" << se95_oa <<
")" << std::endl;
603 if(input_opt.empty())
606 const char* pszMessage;
607 void* pProgressArg=NULL;
608 GDALProgressFunc pfnProgress=GDALTermProgress;
611 bool inputIsRaster=
false;
614 imgReaderOgr.open(input_opt[0]);
615 imgReaderOgr.close();
617 catch(
string errorString){
624 if(verbose_opt[0]>=1)
625 cout <<
"opening image " << input_opt[0] << endl;
626 testImage.open(input_opt[0]);
629 cerr << error << endl;
635 if(verbose_opt[0]>=1)
636 std::cout <<
"opening mask image file " << mask_opt[0] << std::endl;
637 maskReader.open(mask_opt[0]);
640 cerr << error << endl;
644 cerr <<
"error catched" << endl;
649 if(priorimg_opt.size()){
651 if(verbose_opt[0]>=1)
652 std::cout <<
"opening prior image " << priorimg_opt[0] << std::endl;
653 priorReader.open(priorimg_opt[0]);
654 assert(priorReader.nrOfCol()==testImage.nrOfCol());
655 assert(priorReader.nrOfRow()==testImage.nrOfRow());
658 cerr << error << std::endl;
662 cerr <<
"error catched" << std::endl;
667 int nrow=testImage.nrOfRow();
668 int ncol=testImage.nrOfCol();
669 if(option_opt.findSubstring(
"INTERLEAVE=")==option_opt.end()){
670 string theInterleave=
"INTERLEAVE=";
671 theInterleave+=testImage.getInterleave();
672 option_opt.push_back(theInterleave);
674 vector<char> classOut(ncol);
682 string imageType=testImage.getImageType();
683 if(oformat_opt.size())
684 imageType=oformat_opt[0];
687 if(verbose_opt[0]>=1)
688 cout <<
"opening class image for writing output " << output_opt[0] << endl;
689 if(classBag_opt.size()){
690 classImageBag.open(classBag_opt[0],ncol,nrow,nbag,GDT_Byte,imageType,option_opt);
691 classImageBag.GDALSetNoDataValue(nodata_opt[0]);
692 classImageBag.copyGeoTransform(testImage);
693 classImageBag.setProjection(testImage.getProjection());
695 classImageOut.open(output_opt[0],ncol,nrow,1,GDT_Byte,imageType,option_opt);
696 classImageOut.GDALSetNoDataValue(nodata_opt[0]);
697 classImageOut.copyGeoTransform(testImage);
698 classImageOut.setProjection(testImage.getProjection());
699 if(colorTable_opt.size())
700 classImageOut.setColorTable(colorTable_opt[0],0);
702 probImage.open(prob_opt[0],ncol,nrow,nclass,GDT_Byte,imageType,option_opt);
703 probImage.GDALSetNoDataValue(nodata_opt[0]);
704 probImage.copyGeoTransform(testImage);
705 probImage.setProjection(testImage.getProjection());
707 if(entropy_opt.size()){
708 entropyImage.open(entropy_opt[0],ncol,nrow,1,GDT_Byte,imageType,option_opt);
709 entropyImage.GDALSetNoDataValue(nodata_opt[0]);
710 entropyImage.copyGeoTransform(testImage);
711 entropyImage.setProjection(testImage.getProjection());
715 cerr << error << endl;
718 for(
int iline=0;iline<nrow;++iline){
719 vector<float> buffer(ncol);
720 vector<short> lineMask;
722 lineMask.resize(maskReader.nrOfCol());
724 if(priorimg_opt.size())
725 linePrior.resize(nclass,ncol);
729 vector<float> entropy(ncol);
731 if(classBag_opt.size())
732 classBag.resize(nbag,ncol);
736 for(
int iband=0;iband<band_opt.size();++iband){
737 if(verbose_opt[0]==2)
738 std::cout <<
"reading band " << band_opt[iband] << std::endl;
739 assert(band_opt[iband]>=0);
740 assert(band_opt[iband]<testImage.nrOfBand());
741 testImage.readData(buffer,GDT_Float32,iline,band_opt[iband]);
742 for(
int icol=0;icol<ncol;++icol)
743 hpixel[icol].push_back(buffer[icol]);
747 for(
int iband=bstart_opt[0];iband<bstart_opt[0]+nband;++iband){
748 if(verbose_opt[0]==2)
749 std::cout <<
"reading band " << iband << std::endl;
751 assert(iband<testImage.nrOfBand());
752 testImage.readData(buffer,GDT_Float32,iline,iband);
753 for(
int icol=0;icol<ncol;++icol)
754 hpixel[icol].push_back(buffer[icol]);
758 catch(
string theError){
759 cerr <<
"Error reading " << input_opt[0] <<
": " << theError << std::endl;
763 cerr <<
"error catched" << std::endl;
766 assert(nband==hpixel[0].size());
767 if(verbose_opt[0]==2)
768 cout <<
"used bands: " << nband << endl;
770 if(priorimg_opt.size()){
772 for(
short iclass=0;iclass<nclass;++iclass){
773 if(verbose_opt.size()>1)
774 std::cout <<
"Reading " << priorimg_opt[0] <<
" band " << iclass <<
" line " << iline << std::endl;
775 priorReader.readData(linePrior[iclass],GDT_Float32,iline,iclass);
778 catch(
string theError){
779 std::cerr <<
"Error reading " << priorimg_opt[0] <<
": " << theError << std::endl;
783 cerr <<
"error catched" << std::endl;
787 double oldRowMask=-1;
789 for(
int icol=0;icol<ncol;++icol){
790 assert(hpixel[icol].size()==nband);
799 testImage.image2geo(icol,iline,geox,geoy);
800 maskReader.geo2image(geox,geoy,colMask,rowMask);
801 colMask=
static_cast<int>(colMask);
802 rowMask=
static_cast<int>(rowMask);
803 if(rowMask>=0&&rowMask<maskReader.nrOfRow()&&colMask>=0&&colMask<maskReader.nrOfCol()){
804 if(static_cast<int>(rowMask)!=
static_cast<int>(oldRowMask)){
805 assert(rowMask>=0&&rowMask<maskReader.nrOfRow());
808 maskReader.readData(lineMask,GDT_Int16,static_cast<int>(rowMask));
810 catch(
string errorstring){
811 cerr << errorstring << endl;
815 cerr <<
"error catched" << std::endl;
821 for(
short ivalue=0;ivalue<msknodata_opt.size();++ivalue){
822 if(msknodata_opt[ivalue]>=0){
823 if(lineMask[colMask]==msknodata_opt[ivalue]){
824 theMask=lineMask[colMask];
830 if(lineMask[colMask]!=-msknodata_opt[ivalue]){
831 theMask=lineMask[colMask];
841 if(classBag_opt.size())
842 for(
int ibag=0;ibag<nbag;++ibag)
843 classBag[ibag][icol]=theMask;
844 classOut[icol]=theMask;
849 for(
int iband=0;iband<nband;++iband){
850 if(hpixel[icol][iband]){
856 if(classBag_opt.size())
857 for(
int ibag=0;ibag<nbag;++ibag)
858 classBag[ibag][icol]=nodata_opt[0];
859 classOut[icol]=nodata_opt[0];
863 for(
int iclass=0;iclass<nclass;++iclass)
864 probOut[iclass][icol]=0;
866 std::cout <<
"begin classification " << std::endl;
868 for(
int ibag=0;ibag<nbag;++ibag){
870 fpixel[icol].clear();
871 for(
int iband=0;iband<nband;++iband)
872 fpixel[icol].push_back((hpixel[icol][iband]-offset[ibag][iband])/scale[ibag][iband]);
873 vector<float> result(nclass);
874 result=net[ibag].run(fpixel[icol]);
876 vector<float> prValues(nclass);
880 if(classBag_opt.size()){
883 classBag[ibag][icol]=0;
886 if(priorimg_opt.size()){
887 for(
short iclass=0;iclass<nclass;++iclass)
888 normPrior+=linePrior[iclass][icol];
890 for(
int iclass=0;iclass<nclass;++iclass){
891 result[iclass]=(result[iclass]+1.0)/2.0;
892 if(priorimg_opt.size())
893 priors[iclass]=linePrior[iclass][icol]/normPrior;
897 probOut[iclass][icol]+=result[iclass]*priors[iclass];
900 probOut[iclass][icol]*=pow(static_cast<float>(priors[iclass]),static_cast<float>(1.0-nbag)/nbag)*result[iclass];
903 if(priors[iclass]*result[iclass]>probOut[iclass][icol])
904 probOut[iclass][icol]=priors[iclass]*result[iclass];
907 if(classBag_opt.size()){
913 if(result[iclass]>maxP){
915 classBag[ibag][icol]=iclass;
925 for(
short iclass=0;iclass<nclass;++iclass){
926 if(probOut[iclass][icol]>maxBag1){
927 maxBag1=probOut[iclass][icol];
928 classOut[icol]=classValueMap[nameVector[iclass]];
930 else if(probOut[iclass][icol]>maxBag2)
931 maxBag2=probOut[iclass][icol];
932 normBag+=probOut[iclass][icol];
936 for(
short iclass=0;iclass<nclass;++iclass){
937 float prv=probOut[iclass][icol];
939 entropy[icol]-=prv*log(prv)/log(2.0);
942 probOut[iclass][icol]=
static_cast<short>(prv+0.5);
947 entropy[icol]/=log(static_cast<double>(nclass))/log(2.0);
948 entropy[icol]=
static_cast<short>(100*entropy[icol]+0.5);
949 if(active_opt.size()){
950 if(entropy[icol]>activePoints.back().value){
951 activePoints.back().value=entropy[icol];
952 activePoints.back().posx=icol;
953 activePoints.back().posy=iline;
956 std::cout << activePoints.back().posx <<
" " << activePoints.back().posy <<
" " << activePoints.back().value << std::endl;
961 if(classBag_opt.size())
962 for(
int ibag=0;ibag<nbag;++ibag)
963 classImageBag.writeData(classBag[ibag],GDT_Byte,iline,ibag);
965 for(
int iclass=0;iclass<nclass;++iclass)
966 probImage.writeData(probOut[iclass],GDT_Float32,iline,iclass);
968 if(entropy_opt.size()){
969 entropyImage.writeData(entropy,GDT_Float32,iline);
971 classImageOut.writeData(classOut,GDT_Byte,iline);
973 progress=
static_cast<float>(iline+1.0)/classImageOut.nrOfRow();
974 pfnProgress(progress,pszMessage,pProgressArg);
978 if(active_opt.size()){
979 for(
int iactive=0;iactive<activePoints.size();++iactive){
980 std::map<string,double> pointMap;
981 for(
int iband=0;iband<testImage.nrOfBand();++iband){
983 testImage.readData(value,GDT_Float64,static_cast<int>(activePoints[iactive].posx),static_cast<int>(activePoints[iactive].posy),iband);
986 pointMap[fs.str()]=value;
988 pointMap[label_opt[0]]=0;
990 testImage.image2geo(activePoints[iactive].posx,activePoints[iactive].posy,x,y);
991 std::string fieldname=
"id";
992 activeWriter.addPoint(x,y,pointMap,fieldname,++nactive);
999 if(priorimg_opt.size())
1000 priorReader.close();
1003 if(entropy_opt.size())
1004 entropyImage.close();
1005 if(classBag_opt.size())
1006 classImageBag.close();
1007 classImageOut.close();
1012 for(
int ivalidation=0;ivalidation<input_opt.size();++ivalidation){
1013 if(output_opt.size())
1014 assert(output_opt.size()==input_opt.size());
1016 cout <<
"opening img reader " << input_opt[ivalidation] << endl;
1017 imgReaderOgr.open(input_opt[ivalidation]);
1020 if(output_opt.size()){
1022 std::cout <<
"opening img writer and copying fields from img reader" << output_opt[ivalidation] << std::endl;
1023 imgWriterOgr.open(output_opt[ivalidation],imgReaderOgr);
1026 cout <<
"number of layers in input ogr file: " << imgReaderOgr.getLayerCount() << endl;
1027 for(
int ilayer=0;ilayer<imgReaderOgr.getLayerCount();++ilayer){
1029 cout <<
"processing input layer " << ilayer << endl;
1030 if(output_opt.size()){
1032 std::cout <<
"creating field class" << std::endl;
1033 if(classValueMap.size())
1034 imgWriterOgr.createField(
"class",OFTInteger,ilayer);
1036 imgWriterOgr.createField(
"class",OFTString,ilayer);
1038 unsigned int nFeatures=imgReaderOgr.getFeatureCount(ilayer);
1039 unsigned int ifeature=0;
1041 pfnProgress(progress,pszMessage,pProgressArg);
1042 OGRFeature *poFeature;
1043 while( (poFeature = imgReaderOgr.getLayer(ilayer)->GetNextFeature()) != NULL ){
1044 if(verbose_opt[0]>1)
1045 cout <<
"feature " << ifeature << endl;
1046 if( poFeature == NULL ){
1047 cout <<
"Warning: could not read feature " << ifeature <<
" in layer " << imgReaderOgr.getLayerName(ilayer) << endl;
1050 OGRFeature *poDstFeature = NULL;
1051 if(output_opt.size()){
1052 poDstFeature=imgWriterOgr.createFeature(ilayer);
1053 if( poDstFeature->SetFrom( poFeature, TRUE ) != OGRERR_NONE ){
1054 CPLError( CE_Failure, CPLE_AppDefined,
1055 "Unable to translate feature %d from layer %s.\n",
1056 poFeature->GetFID(), imgWriterOgr.getLayerName(ilayer).c_str() );
1057 OGRFeature::DestroyFeature( poFeature );
1058 OGRFeature::DestroyFeature( poDstFeature );
1061 vector<float> validationPixel;
1062 vector<float> validationFeature;
1064 imgReaderOgr.readData(validationPixel,OFTReal,fields,poFeature,ilayer);
1065 assert(validationPixel.size()==nband);
1066 vector<float> probOut(nclass);
1067 for(
int iclass=0;iclass<nclass;++iclass)
1069 for(
int ibag=0;ibag<nbag;++ibag){
1070 for(
int iband=0;iband<nband;++iband){
1071 validationFeature.push_back((validationPixel[iband]-offset[ibag][iband])/scale[ibag][iband]);
1072 if(verbose_opt[0]==2)
1073 std:: cout <<
" " << validationFeature.back();
1075 if(verbose_opt[0]==2)
1076 std::cout << std:: endl;
1077 vector<float> result(nclass);
1078 result=net[ibag].run(validationFeature);
1080 if(verbose_opt[0]>1){
1081 for(
int iclass=0;iclass<result.size();++iclass)
1082 std::cout << result[iclass] <<
" ";
1083 std::cout << std::endl;
1086 for(
int iclass=0;iclass<nclass;++iclass){
1087 result[iclass]=(result[iclass]+1.0)/2.0;
1088 switch(comb_opt[0]){
1091 probOut[iclass]+=result[iclass]*priors[iclass];
1094 probOut[iclass]*=pow(static_cast<float>(priors[iclass]),static_cast<float>(1.0-nbag)/nbag)*result[iclass];
1097 if(priors[iclass]*result[iclass]>probOut[iclass])
1098 probOut[iclass]=priors[iclass]*result[iclass];
1106 string classOut=
"Unclassified";
1107 for(
int iclass=0;iclass<nclass;++iclass){
1108 if(verbose_opt[0]>1)
1109 std::cout << probOut[iclass] <<
" ";
1110 if(probOut[iclass]>maxBag){
1111 maxBag=probOut[iclass];
1112 classOut=nameVector[iclass];
1116 if(verbose_opt[0]>1){
1117 if(classValueMap.size())
1118 std::cout <<
"->" << classValueMap[classOut] << std::endl;
1120 std::cout <<
"->" << classOut << std::endl;
1122 if(output_opt.size()){
1123 if(classValueMap.size())
1124 poDstFeature->SetField(
"class",classValueMap[classOut]);
1126 poDstFeature->SetField(
"class",classOut.c_str());
1127 poDstFeature->SetFID( poFeature->GetFID() );
1129 int labelIndex=poFeature->GetFieldIndex(label_opt[0].c_str());
1131 string classRef=poFeature->GetFieldAsString(labelIndex);
1133 if(classValueMap.size())
1134 cm.incrementResult(type2string<short>(classValueMap[classRef]),type2string<short>(classValueMap[classOut]),1);
1136 cm.incrementResult(classRef,classOut,1);
1140 if(output_opt.size()){
1141 if(imgWriterOgr.createFeature(poDstFeature,ilayer) != OGRERR_NONE){
1142 CPLError( CE_Failure, CPLE_AppDefined,
1143 "Unable to translate feature %d from layer %s.\n",
1144 poFeature->GetFID(), imgWriterOgr.getLayerName(ilayer).c_str() );
1145 OGRFeature::DestroyFeature( poDstFeature );
1146 OGRFeature::DestroyFeature( poDstFeature );
1150 if(!verbose_opt[0]){
1151 progress=
static_cast<float>(ifeature+1.0)/nFeatures;
1152 pfnProgress(progress,pszMessage,pProgressArg);
1154 OGRFeature::DestroyFeature( poFeature );
1155 OGRFeature::DestroyFeature( poDstFeature );
1158 imgReaderOgr.close();
1159 if(output_opt.size())
1160 imgWriterOgr.close();
1162 if(cm.nReference()){
1163 std::cout << cm << std::endl;
1164 cout <<
"class #samples userAcc prodAcc" << endl;
1171 for(
short iclass=0;iclass<cm.nClasses();++iclass){
1172 dua=cm.ua_pct(cm.getClass(iclass),&se95_ua);
1173 dpa=cm.pa_pct(cm.getClass(iclass),&se95_pa);
1174 cout << cm.getClass(iclass) <<
" " << cm.nReference(cm.getClass(iclass)) <<
" " << dua <<
" (" << se95_ua <<
")" <<
" " << dpa <<
" (" << se95_pa <<
")" << endl;
1176 std::cout <<
"Kappa: " << cm.kappa() << std::endl;
1177 doa=cm.oa_pct(&se95_oa);
1178 std::cout <<
"Overall Accuracy: " << doa <<
" (" << se95_oa <<
")" << std::endl;
1182 if(active_opt.size())
1183 activeWriter.close();
1185 catch(
string errorString){
1186 std::cerr <<
"Error: errorString" << std::endl;