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"
110 int main(
int argc,
char *argv[])
112 vector<double> priors;
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)");
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)");
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);
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);
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);
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");
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);
159 bstart_opt.setHide(1);
161 balance_opt.setHide(1);
162 minSize_opt.setHide(1);
164 bagSize_opt.setHide(1);
166 classBag_opt.setHide(1);
167 minSize_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);
178 verbose_opt.setHide(2);
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);
225 catch(
string predefinedString){
226 std::cout << predefinedString << std::endl;
231 cout <<
"Usage: pkann -t training [-i input -o output] [-cv value]" << endl;
233 cout <<
"short option -h shows basic options only, use long option --help to show all options" << endl;
237 if(entropy_opt[0]==
"")
239 if(active_opt[0]==
"")
241 if(priorimg_opt[0]==
"")
242 priorimg_opt.clear();
244 if(verbose_opt[0]>=1){
246 cout <<
"image filename: " << input_opt[0] << endl;
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;
255 cerr <<
"no training file set!" << endl;
256 cout <<
"verbose: " << verbose_opt[0] << endl;
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;
270 bool maskIsVector=
false;
273 extentReader.open(mask_opt[0]);
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;
281 catch(
string errorString){
287 if(active_opt.size()){
289 activeWriter.open(active_opt[0],ogrformat_opt[0]);
290 activeWriter.createLayer(active_opt[0],trainingReader.getProjection(),wkbPoint,NULL);
291 activeWriter.copyFields(trainingReader);
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;
300 unsigned int totalSamples=0;
301 unsigned int nactive=0;
302 vector<FANN::neural_net> net(nbag);
304 unsigned int nclass=0;
308 if(priors_opt.size()>1){
309 priors.resize(priors_opt.size());
311 for(
int iclass=0;iclass<priors_opt.size();++iclass){
312 priors[iclass]=priors_opt[iclass];
313 normPrior+=priors[iclass];
316 for(
int iclass=0;iclass<priors_opt.size();++iclass)
317 priors[iclass]/=normPrior;
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";
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";
333 for(
int iband=bstart_opt[ipair];iband<=bend_opt[ipair];++iband)
334 band_opt.push_back(iband);
339 cerr << error << std::endl;
344 std::sort(band_opt.begin(),band_opt.end());
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];
355 vector< vector<double> > offset(nbag);
356 vector< vector<double> > scale(nbag);
357 map<string,Vector2d<float> > trainingMap;
358 vector< Vector2d<float> > trainingPixels;
359 vector<string> fields;
360 for(
int ibag=0;ibag<nbag;++ibag){
362 if(ibag<training_opt.size()){
364 trainingPixels.clear();
365 if(verbose_opt[0]>=1)
366 cout <<
"reading imageVector file " << training_opt[0] << endl;
370 totalSamples=trainingReaderBag.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
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)?";
377 trainingReaderBag.close();
380 cerr << error << std::endl;
384 cerr <<
"error catched" << std::endl;
394 std::cout <<
"training pixels: " << std::endl;
395 map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
396 while(mapit!=trainingMap.end()){
398 if((mapit->second).size()<minSize_opt[0]){
399 trainingMap.erase(mapit);
402 trainingPixels.push_back(mapit->second);
404 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
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();
414 assert(nclass==trainingPixels.size());
415 assert(nband==(training_opt.size())?trainingPixels[0][0].size()-2:trainingPixels[0][0].size());
420 if(balance_opt[0]>0){
421 while(balance_opt.size()<nclass)
422 balance_opt.push_back(balance_opt.back());
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);
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]);
440 totalSamples+=trainingPixels[iclass].size();
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];
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];
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;
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];
488 if(priors_opt.size()==1){
489 priors.resize(nclass);
490 for(
int iclass=0;iclass<nclass;++iclass)
491 priors[iclass]=1.0/nclass;
493 assert(priors_opt.size()==1||priors_opt.size()==nclass);
496 while(bagSize_opt.size()<nclass)
497 bagSize_opt.push_back(bagSize_opt.back());
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;
509 map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
511 while(mapit!=trainingMap.end()){
512 nameVector.push_back(mapit->first);
513 if(classValueMap.size()){
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);
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;
525 cm.pushBackClassName(mapit->first,doSort);
528 if(classname_opt.empty()){
530 for(
int iclass=0;iclass<nclass;++iclass){
532 std::cout << iclass <<
" " << cm.getClass(iclass) <<
" -> " << string2type<short>(cm.getClass(iclass)) << std::endl;
533 classValueMap[cm.getClass(iclass)]=string2type<short>(cm.getClass(iclass));
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;
544 vector< Vector2d<float> > trainingFeatures(nclass);
545 for(
int iclass=0;iclass<nclass;++iclass){
547 if(verbose_opt[0]>=1)
548 cout <<
"calculating features for class " << iclass << endl;
551 nctraining=(bagSize_opt[iclass]<100)? trainingPixels[iclass].size()/100.0*bagSize_opt[iclass] : trainingPixels[iclass].size();
554 assert(nctraining<=trainingPixels[iclass].size());
556 if(bagSize_opt[iclass]<100)
557 random_shuffle(trainingPixels[iclass].begin(),trainingPixels[iclass].end());
559 trainingFeatures[iclass].resize(nctraining);
560 for(
int isample=0;isample<nctraining;++isample){
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]);
567 assert(trainingFeatures[iclass].size()==nctraining);
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();
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;
593 unsigned int layers[3];
595 layers[1]=nneuron_opt[0];
597 net[ibag].create_sparse_array(connection_opt[0],num_layers,layers);
601 unsigned int layers[4];
603 layers[1]=nneuron_opt[0];
604 layers[2]=nneuron_opt[1];
610 net[ibag].create_sparse_array(connection_opt[0],num_layers,layers);
614 cerr <<
"Only 1 or 2 hidden layers are supported!" << endl;
618 if(verbose_opt[0]>=1)
619 cout <<
"network created" << endl;
621 net[ibag].set_learning_rate(learning_opt[0]);
626 net[ibag].set_activation_function_hidden(FANN::SIGMOID_SYMMETRIC_STEPWISE);
627 net[ibag].set_activation_function_output(FANN::SIGMOID_SYMMETRIC_STEPWISE);
633 if(verbose_opt[0]>=1){
634 cout << endl <<
"Network Type : ";
635 switch (net[ibag].get_network_type())
638 cout <<
"LAYER" << endl;
641 cout <<
"SHORTCUT" << endl;
644 cout <<
"UNKNOWN" << endl;
647 net[ibag].print_parameters();
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,
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);
670 cm.incrementResult(cm.getClass(referenceVector[isample]),cm.getClass(outputVector[isample]),1.0/nbag);
674 if(verbose_opt[0]>=1)
675 cout << endl <<
"Set training data" << endl;
677 if(verbose_opt[0]>=1)
678 cout << endl <<
"Training network" << endl;
680 if(verbose_opt[0]>=1){
681 cout <<
"Max Epochs " << setw(8) << maxit_opt[0] <<
". "
682 <<
"Desired Error: " << left << desired_error << right << endl;
684 if(weights_opt.size()==net[ibag].get_total_connections()){
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);
692 bool initWeights=
true;
693 net[ibag].train_on_data(trainingFeatures,ntraining,initWeights, maxit_opt[0],
694 iterations_between_reports, desired_error);
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;
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;
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;
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;
729 if(input_opt.empty())
732 const char* pszMessage;
733 void* pProgressArg=NULL;
734 GDALProgressFunc pfnProgress=GDALTermProgress;
737 bool inputIsRaster=
false;
740 imgReaderOgr.open(input_opt[0]);
741 imgReaderOgr.close();
743 catch(
string errorString){
750 if(verbose_opt[0]>=1)
751 cout <<
"opening image " << input_opt[0] << endl;
752 testImage.open(input_opt[0]);
755 cerr << error << endl;
759 if(priorimg_opt.size()){
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());
768 cerr << error << std::endl;
772 cerr <<
"error catched" << std::endl;
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);
784 vector<char> classOut(ncol);
792 string imageType=testImage.getImageType();
793 if(oformat_opt.size())
794 imageType=oformat_opt[0];
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());
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);
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());
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());
825 cerr << error << endl;
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);
837 maskWriter.rasterizeOgr(extentReader,burnValues);
838 extentReader.close();
842 cerr << error << std::endl;
846 cerr <<
"error catched" << std::endl;
850 mask_opt.push_back(
"/vsimem/mask.tif");
855 if(verbose_opt[0]>=1)
856 std::cout <<
"opening mask image file " << mask_opt[0] << std::endl;
857 maskReader.open(mask_opt[0]);
860 cerr << error << std::endl;
864 cerr <<
"error catched" << std::endl;
869 for(
int iline=0;iline<nrow;++iline){
870 vector<float> buffer(ncol);
871 vector<short> lineMask;
873 lineMask.resize(maskReader.nrOfCol());
875 if(priorimg_opt.size())
876 linePrior.resize(nclass,ncol);
880 vector<float> entropy(ncol);
882 if(classBag_opt.size())
883 classBag.resize(nbag,ncol);
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]);
898 for(
int iband=0;iband<nband;++iband){
899 if(verbose_opt[0]==2)
900 std::cout <<
"reading band " << iband << std::endl;
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]);
909 catch(
string theError){
910 cerr <<
"Error reading " << input_opt[0] <<
": " << theError << std::endl;
914 cerr <<
"error catched" << std::endl;
917 assert(nband==hpixel[0].size());
918 if(verbose_opt[0]==2)
919 cout <<
"used bands: " << nband << endl;
921 if(priorimg_opt.size()){
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);
929 catch(
string theError){
930 std::cerr <<
"Error reading " << priorimg_opt[0] <<
": " << theError << std::endl;
934 cerr <<
"error catched" << std::endl;
938 double oldRowMask=-1;
940 for(
int icol=0;icol<ncol;++icol){
941 assert(hpixel[icol].size()==nband);
942 bool doClassify=
true;
948 testImage.image2geo(icol,iline,geox,geoy);
950 if(uly>=geoy&&lry<=geoy&&ulx<=geox&&lrx>=geox){
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());
968 maskReader.readData(lineMask,GDT_Int16,static_cast<int>(rowMask));
970 catch(
string errorstring){
971 cerr << errorstring << endl;
975 cerr <<
"error catched" << std::endl;
981 for(
short ivalue=0;ivalue<msknodata_opt.size();++ivalue){
982 if(msknodata_opt[ivalue]>=0){
983 if(lineMask[colMask]==msknodata_opt[ivalue]){
984 theMask=lineMask[colMask];
990 if(lineMask[colMask]!=-msknodata_opt[ivalue]){
991 theMask=lineMask[colMask];
1001 if(classBag_opt.size())
1002 for(
int ibag=0;ibag<nbag;++ibag)
1003 classBag[ibag][icol]=theMask;
1004 classOut[icol]=theMask;
1009 for(
int iband=0;iband<hpixel[icol].size();++iband){
1010 if(hpixel[icol][iband]){
1019 for(
short iclass=0;iclass<nclass;++iclass)
1020 probOut[iclass][icol]=0;
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];
1028 if(verbose_opt[0]>1)
1029 std::cout <<
"begin classification " << std::endl;
1031 for(
int ibag=0;ibag<nbag;++ibag){
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]);
1039 vector<float> prValues(nclass);
1043 if(classBag_opt.size()){
1046 classBag[ibag][icol]=0;
1049 if(priorimg_opt.size()){
1050 for(
short iclass=0;iclass<nclass;++iclass)
1051 normPrior+=linePrior[iclass][icol];
1053 for(
int iclass=0;iclass<nclass;++iclass){
1054 result[iclass]=(result[iclass]+1.0)/2.0;
1055 if(priorimg_opt.size())
1056 priors[iclass]=linePrior[iclass][icol]/normPrior;
1057 switch(comb_opt[0]){
1060 probOut[iclass][icol]+=result[iclass]*priors[iclass];
1063 probOut[iclass][icol]*=pow(static_cast<float>(priors[iclass]),static_cast<float>(1.0-nbag)/nbag)*result[iclass];
1066 if(priors[iclass]*result[iclass]>probOut[iclass][icol])
1067 probOut[iclass][icol]=priors[iclass]*result[iclass];
1070 if(classBag_opt.size()){
1076 if(result[iclass]>maxP){
1077 maxP=result[iclass];
1078 classBag[ibag][icol]=iclass;
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]];
1093 else if(probOut[iclass][icol]>maxBag2)
1094 maxBag2=probOut[iclass][icol];
1095 normBag+=probOut[iclass][icol];
1099 for(
short iclass=0;iclass<nclass;++iclass){
1100 float prv=probOut[iclass][icol];
1102 entropy[icol]-=prv*log(prv)/log(2.0);
1105 probOut[iclass][icol]=
static_cast<short>(prv+0.5);
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];
1115 activePoints.back().posx=icol;
1116 activePoints.back().posy=iline;
1119 std::cout << activePoints.back().posx <<
" " << activePoints.back().posy <<
" " << activePoints.back().value << std::endl;
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);
1131 if(entropy_opt.size()){
1132 entropyImage.writeData(entropy,GDT_Float32,iline);
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);
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){
1146 testImage.readData(value,GDT_Float64,static_cast<int>(activePoints[iactive].posx),static_cast<int>(activePoints[iactive].posy),iband);
1149 pointMap[fs.str()]=value;
1151 pointMap[label_opt[0]]=0;
1153 testImage.image2geo(activePoints[iactive].posx,activePoints[iactive].posy,x,y);
1154 std::string fieldname=
"id";
1155 activeWriter.addPoint(x,y,pointMap,fieldname,++nactive);
1162 if(priorimg_opt.size())
1163 priorReader.close();
1166 if(entropy_opt.size())
1167 entropyImage.close();
1168 if(classBag_opt.size())
1169 classImageBag.close();
1170 classImageOut.close();
1175 for(
int ivalidation=0;ivalidation<input_opt.size();++ivalidation){
1176 if(output_opt.size())
1177 assert(output_opt.size()==input_opt.size());
1179 cout <<
"opening img reader " << input_opt[ivalidation] << endl;
1180 imgReaderOgr.open(input_opt[ivalidation]);
1183 if(output_opt.size()){
1185 std::cout <<
"opening img writer and copying fields from img reader" << output_opt[ivalidation] << std::endl;
1186 imgWriterOgr.open(output_opt[ivalidation],imgReaderOgr);
1189 cout <<
"number of layers in input ogr file: " << imgReaderOgr.getLayerCount() << endl;
1190 for(
int ilayer=0;ilayer<imgReaderOgr.getLayerCount();++ilayer){
1192 cout <<
"processing input layer " << ilayer << endl;
1193 if(output_opt.size()){
1195 std::cout <<
"creating field class" << std::endl;
1196 if(classValueMap.size())
1197 imgWriterOgr.createField(
"class",OFTInteger,ilayer);
1199 imgWriterOgr.createField(
"class",OFTString,ilayer);
1201 unsigned int nFeatures=imgReaderOgr.getFeatureCount(ilayer);
1202 unsigned int ifeature=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;
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 );
1224 vector<float> validationPixel;
1225 vector<float> validationFeature;
1227 imgReaderOgr.readData(validationPixel,OFTReal,fields,poFeature,ilayer);
1228 assert(validationPixel.size()==nband);
1229 vector<float> probOut(nclass);
1230 for(
int iclass=0;iclass<nclass;++iclass)
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();
1238 if(verbose_opt[0]==2)
1239 std::cout << std:: endl;
1240 vector<float> result(nclass);
1241 result=net[ibag].run(validationFeature);
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;
1249 for(
int iclass=0;iclass<nclass;++iclass){
1250 result[iclass]=(result[iclass]+1.0)/2.0;
1251 switch(comb_opt[0]){
1254 probOut[iclass]+=result[iclass]*priors[iclass];
1257 probOut[iclass]*=pow(static_cast<float>(priors[iclass]),static_cast<float>(1.0-nbag)/nbag)*result[iclass];
1260 if(priors[iclass]*result[iclass]>probOut[iclass])
1261 probOut[iclass]=priors[iclass]*result[iclass];
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];
1279 if(verbose_opt[0]>1){
1280 if(classValueMap.size())
1281 std::cout <<
"->" << classValueMap[classOut] << std::endl;
1283 std::cout <<
"->" << classOut << std::endl;
1285 if(output_opt.size()){
1286 if(classValueMap.size())
1287 poDstFeature->SetField(
"class",classValueMap[classOut]);
1289 poDstFeature->SetField(
"class",classOut.c_str());
1290 poDstFeature->SetFID( poFeature->GetFID() );
1292 int labelIndex=poFeature->GetFieldIndex(label_opt[0].c_str());
1294 string classRef=poFeature->GetFieldAsString(labelIndex);
1296 if(classValueMap.size())
1297 cm.incrementResult(type2string<short>(classValueMap[classRef]),type2string<short>(classValueMap[classOut]),1);
1299 cm.incrementResult(classRef,classOut,1);
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 );
1313 if(!verbose_opt[0]){
1314 progress=
static_cast<float>(ifeature+1.0)/nFeatures;
1315 pfnProgress(progress,pszMessage,pProgressArg);
1317 OGRFeature::DestroyFeature( poFeature );
1318 OGRFeature::DestroyFeature( poDstFeature );
1321 imgReaderOgr.close();
1322 if(output_opt.size())
1323 imgWriterOgr.close();
1325 if(cm.nReference()){
1326 std::cout << cm << std::endl;
1345 if(active_opt.size())
1346 activeWriter.close();
1348 catch(
string errorString){
1349 std::cerr <<
"Error: errorString" << std::endl;