25 #include "base/Optionpk.h" 26 #include "algorithms/ConfusionMatrix.h" 27 #include "algorithms/CostFactorySVM.h" 28 #include "algorithms/FeatureSelector.h" 29 #include "algorithms/svm.h" 30 #include "imageclasses/ImgReaderOgr.h" 100 enum SelectorValue { NA=0, SFFS=1, SFS=2, SBS=3, BFS=4};
257 int main(
int argc,
char *argv[])
262 Optionpk<string> input_opt(
"i",
"input",
"input test set (leave empty to perform a cross validation based on training only)");
263 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).");
265 Optionpk<string> label_opt(
"label",
"label",
"identifier for class label in training vector file.",
"label");
266 Optionpk<unsigned short> maxFeatures_opt(
"n",
"nf",
"number of features to select (0 to select optimal number, see also ecost option)", 0);
267 Optionpk<unsigned int> balance_opt(
"bal",
"balance",
"balance the input data to this number of samples for each class", 0);
268 Optionpk<bool> random_opt(
"random",
"random",
"in case of balance, randomize input data",
true);
269 Optionpk<int> minSize_opt(
"min",
"min",
"if number of training pixels is less then min, do not take this class into account", 0);
270 Optionpk<unsigned short> band_opt(
"b",
"band",
"band index (starting from 0, either use band option or use start to end)");
273 Optionpk<double> offset_opt(
"offset",
"offset",
"offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
274 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);
275 Optionpk<string> selector_opt(
"sm",
"sm",
"feature selection method (sffs=sequential floating forward search,sfs=sequential forward search, sbs, sequential backward search ,bfs=brute force search)",
"sffs");
276 Optionpk<float> epsilon_cost_opt(
"ecost",
"ecost",
"epsilon for stopping criterion in cost function to determine optimal number of features",0.001);
278 Optionpk<std::string> svm_type_opt(
"svmt",
"svmtype",
"type of SVM (C_SVC, nu_SVC,one_class, epsilon_SVR, nu_SVR)",
"C_SVC");
279 Optionpk<std::string> kernel_type_opt(
"kt",
"kerneltype",
"type of kernel function (linear,polynomial,radial,sigmoid) ",
"radial");
281 Optionpk<float> gamma_opt(
"g",
"gamma",
"gamma in kernel function",1.0);
282 Optionpk<float> coef0_opt(
"c0",
"coef0",
"coef0 in kernel function",0);
283 Optionpk<float> ccost_opt(
"cc",
"ccost",
"the parameter C of C-SVC, epsilon-SVR, and nu-SVR",1000);
284 Optionpk<float> nu_opt(
"nu",
"nu",
"the parameter nu of nu-SVC, one-class SVM, and nu-SVR",0.5);
285 Optionpk<float> epsilon_loss_opt(
"eloss",
"eloss",
"the epsilon in loss function of epsilon-SVR",0.1);
286 Optionpk<int> cache_opt(
"cache",
"cache",
"cache memory size in MB",100);
287 Optionpk<float> epsilon_tol_opt(
"etol",
"etol",
"the tolerance of termination criterion",0.001);
288 Optionpk<bool> shrinking_opt(
"shrink",
"shrink",
"whether to use the shrinking heuristics",
false);
289 Optionpk<bool> prob_est_opt(
"pe",
"probest",
"whether to train a SVC or SVR model for probability estimates",
true,2);
292 Optionpk<short> classvalue_opt(
"r",
"reclass",
"list of class values (use same order as in classname opt.");
293 Optionpk<short> verbose_opt(
"v",
"verbose",
"set to: 0 (results only), 1 (confusion matrix), 2 (debug)",0,2);
295 tlayer_opt.setHide(1);
296 label_opt.setHide(1);
297 balance_opt.setHide(1);
298 random_opt.setHide(1);
299 minSize_opt.setHide(1);
301 bstart_opt.setHide(1);
303 offset_opt.setHide(1);
304 scale_opt.setHide(1);
305 svm_type_opt.setHide(1);
306 kernel_type_opt.setHide(1);
307 kernel_degree_opt.setHide(1);
308 gamma_opt.setHide(1);
309 coef0_opt.setHide(1);
310 ccost_opt.setHide(1);
312 epsilon_loss_opt.setHide(1);
313 cache_opt.setHide(1);
314 epsilon_tol_opt.setHide(1);
315 shrinking_opt.setHide(1);
316 prob_est_opt.setHide(1);
317 selector_opt.setHide(1);
318 epsilon_cost_opt.setHide(1);
320 classname_opt.setHide(1);
321 classvalue_opt.setHide(1);
325 doProcess=input_opt.retrieveOption(argc,argv);
326 training_opt.retrieveOption(argc,argv);
327 maxFeatures_opt.retrieveOption(argc,argv);
328 tlayer_opt.retrieveOption(argc,argv);
329 label_opt.retrieveOption(argc,argv);
330 balance_opt.retrieveOption(argc,argv);
331 random_opt.retrieveOption(argc,argv);
332 minSize_opt.retrieveOption(argc,argv);
333 band_opt.retrieveOption(argc,argv);
334 bstart_opt.retrieveOption(argc,argv);
335 bend_opt.retrieveOption(argc,argv);
336 offset_opt.retrieveOption(argc,argv);
337 scale_opt.retrieveOption(argc,argv);
338 svm_type_opt.retrieveOption(argc,argv);
339 kernel_type_opt.retrieveOption(argc,argv);
340 kernel_degree_opt.retrieveOption(argc,argv);
341 gamma_opt.retrieveOption(argc,argv);
342 coef0_opt.retrieveOption(argc,argv);
343 ccost_opt.retrieveOption(argc,argv);
344 nu_opt.retrieveOption(argc,argv);
345 epsilon_loss_opt.retrieveOption(argc,argv);
346 cache_opt.retrieveOption(argc,argv);
347 epsilon_tol_opt.retrieveOption(argc,argv);
348 shrinking_opt.retrieveOption(argc,argv);
349 prob_est_opt.retrieveOption(argc,argv);
350 selector_opt.retrieveOption(argc,argv);
351 epsilon_cost_opt.retrieveOption(argc,argv);
352 cv_opt.retrieveOption(argc,argv);
353 classname_opt.retrieveOption(argc,argv);
354 classvalue_opt.retrieveOption(argc,argv);
355 verbose_opt.retrieveOption(argc,argv);
357 catch(
string predefinedString){
358 std::cout << predefinedString << std::endl;
363 cout <<
"Usage: pkfssvm -t training -n number" << endl;
365 std::cout <<
"short option -h shows basic options only, use long option --help to show all options" << std::endl;
369 CostFactorySVM costfactory(svm_type_opt[0], kernel_type_opt[0], kernel_degree_opt[0], gamma_opt[0], coef0_opt[0], ccost_opt[0], nu_opt[0], epsilon_loss_opt[0], cache_opt[0], epsilon_tol_opt[0], shrinking_opt[0], prob_est_opt[0], cv_opt[0], verbose_opt[0]);
371 assert(training_opt.size());
373 costfactory.setCv(0);
374 if(verbose_opt[0]>=1){
376 std::cout <<
"input filename: " << input_opt[0] << std::endl;
377 std::cout <<
"training vector file: " << std::endl;
378 for(
int ifile=0;ifile<training_opt.size();++ifile)
379 std::cout << training_opt[ifile] << std::endl;
380 std::cout <<
"verbose: " << verbose_opt[0] << std::endl;
383 static std::map<std::string, SelectorValue> selMap;
390 unsigned int totalSamples=0;
391 unsigned int totalTestSamples=0;
393 unsigned short nclass=0;
411 if(bstart_opt.size()){
412 if(bend_opt.size()!=bstart_opt.size()){
413 string errorstring=
"Error: options for start and end band indexes must be provided as pairs, missing end band";
417 for(
int ipair=0;ipair<bstart_opt.size();++ipair){
418 if(bend_opt[ipair]<=bstart_opt[ipair]){
419 string errorstring=
"Error: index for end band must be smaller then start band";
422 for(
int iband=bstart_opt[ipair];iband<=bend_opt[ipair];++iband)
423 band_opt.push_back(iband);
428 cerr << error << std::endl;
433 std::sort(band_opt.begin(),band_opt.end());
435 if(classname_opt.size()){
436 assert(classname_opt.size()==classvalue_opt.size());
437 for(
int iclass=0;iclass<classname_opt.size();++iclass)
438 costfactory.setClassValueMap(classname_opt[iclass],classvalue_opt[iclass]);
442 vector<double> offset;
443 vector<double> scale;
444 vector< Vector2d<float> > trainingPixels;
445 vector< Vector2d<float> > testPixels;
446 map<string,Vector2d<float> > trainingMap;
447 map<string,Vector2d<float> > testMap;
448 vector<string> fields;
452 trainingPixels.clear();
454 if(verbose_opt[0]>=1)
455 std::cout <<
"reading training file " << training_opt[0] << std::endl;
459 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
460 if(input_opt.size()){
462 totalTestSamples=inputReader.readDataImageOgr(testMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
467 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,0,0,label_opt[0],tlayer_opt,verbose_opt[0]);
468 if(input_opt.size()){
470 totalTestSamples=inputReader.readDataImageOgr(testMap,fields,0,0,label_opt[0],tlayer_opt,verbose_opt[0]);
474 if(trainingMap.size()<2){
475 string errorstring=
"Error: could not read at least two classes from training input file";
478 if(input_opt.size()&&testMap.size()<2){
479 string errorstring=
"Error: could not read at least two classes from test input file";
482 trainingReader.close();
485 cerr << error << std::endl;
488 catch(std::exception& e){
489 std::cerr <<
"Error: ";
490 std::cerr << e.what() << std::endl;
491 std::cerr << CPLGetLastErrorMsg() << std::endl;
495 cerr <<
"error caught" << std::endl;
505 std::cout <<
"training pixels: " << std::endl;
506 map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
507 while(mapit!=trainingMap.end()){
509 if((mapit->second).size()<minSize_opt[0]){
510 trainingMap.erase(mapit);
513 costfactory.pushBackName(mapit->first);
514 trainingPixels.push_back(mapit->second);
516 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
519 nclass=trainingPixels.size();
520 if(classname_opt.size())
521 assert(nclass==classname_opt.size());
522 nband=trainingPixels[0][0].size()-2;
524 mapit=testMap.begin();
525 while(mapit!=testMap.end()){
526 if(costfactory.getClassValueMap().size()){
529 if((costfactory.getClassValueMap())[mapit->first]>0){
533 std::cerr <<
"Error: names in classname option are not complete, please check names in test vector and make sure classvalue is > 0" << std::endl;
538 testPixels.push_back(mapit->second);
540 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
543 if(input_opt.size()){
544 assert(nclass==testPixels.size());
545 assert(nband=testPixels[0][0].size()-2);
552 if(balance_opt[0]>0){
556 for(
int iclass=0;iclass<nclass;++iclass){
557 if(trainingPixels[iclass].size()>balance_opt[0]){
558 while(trainingPixels[iclass].size()>balance_opt[0]){
559 int index=rand()%trainingPixels[iclass].size();
560 trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
564 int oldsize=trainingPixels[iclass].size();
565 for(
int isample=trainingPixels[iclass].size();isample<balance_opt[0];++isample){
566 int index = rand()%oldsize;
567 trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
570 totalSamples+=trainingPixels[iclass].size();
572 assert(totalSamples==nclass*balance_opt[0]);
576 offset.resize(nband);
578 if(offset_opt.size()>1)
579 assert(offset_opt.size()==nband);
580 if(scale_opt.size()>1)
581 assert(scale_opt.size()==nband);
582 for(
int iband=0;iband<nband;++iband){
584 std::cout <<
"scaling for band" << iband << std::endl;
585 offset[iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
586 scale[iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
589 float theMin=trainingPixels[0][0][iband+startBand];
590 float theMax=trainingPixels[0][0][iband+startBand];
591 for(
int iclass=0;iclass<nclass;++iclass){
592 for(
int isample=0;isample<trainingPixels[iclass].size();++isample){
593 if(theMin>trainingPixels[iclass][isample][iband+startBand])
594 theMin=trainingPixels[iclass][isample][iband+startBand];
595 if(theMax<trainingPixels[iclass][isample][iband+startBand])
596 theMax=trainingPixels[iclass][isample][iband+startBand];
599 offset[iband]=theMin+(theMax-theMin)/2.0;
600 scale[iband]=(theMax-theMin)/2.0;
601 if(verbose_opt[0]>1){
602 std::cout <<
"Extreme image values for band " << iband <<
": [" << theMin <<
"," << theMax <<
"]" << std::endl;
603 std::cout <<
"Using offset, scale: " << offset[iband] <<
", " << scale[iband] << std::endl;
604 std::cout <<
"scaled values for band " << iband <<
": [" << (theMin-offset[iband])/scale[iband] <<
"," << (theMax-offset[iband])/scale[iband] <<
"]" << std::endl;
616 if(verbose_opt[0]>=1){
617 std::cout <<
"number of bands: " << nband << std::endl;
618 std::cout <<
"number of classes: " << nclass << std::endl;
626 vector<string> nameVector=costfactory.getNameVector();
627 for(
int iname=0;iname<nameVector.size();++iname){
628 if(costfactory.getClassValueMap().empty())
629 costfactory.pushBackClassName(nameVector[iname]);
631 else if(costfactory.getClassIndex(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]))<0)
632 costfactory.pushBackClassName(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]));
638 vector<unsigned int> nctraining;
639 vector<unsigned int> nctest;
640 nctraining.resize(nclass);
641 nctest.resize(nclass);
642 vector< Vector2d<float> > trainingFeatures(nclass);
643 for(
int iclass=0;iclass<nclass;++iclass){
644 if(verbose_opt[0]>=1)
645 std::cout <<
"calculating features for class " << iclass << std::endl;
646 nctraining[iclass]=trainingPixels[iclass].size();
647 if(verbose_opt[0]>=1)
648 std::cout <<
"nctraining[" << iclass <<
"]: " << nctraining[iclass] << std::endl;
649 if(testPixels.size()>iclass){
650 nctest[iclass]=testPixels[iclass].size();
651 if(verbose_opt[0]>=1){
652 std::cout <<
"nctest[" << iclass <<
"]: " << nctest[iclass] << std::endl;
658 trainingFeatures[iclass].resize(nctraining[iclass]+nctest[iclass]);
659 for(
int isample=0;isample<nctraining[iclass];++isample){
661 for(
int iband=0;iband<nband;++iband){
662 assert(trainingPixels[iclass].size()>isample);
663 assert(trainingPixels[iclass][isample].size()>iband+startBand);
664 assert(offset.size()>iband);
665 assert(scale.size()>iband);
666 float value=trainingPixels[iclass][isample][iband+startBand];
667 trainingFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
670 for(
int isample=0;isample<nctest[iclass];++isample){
672 for(
int iband=0;iband<nband;++iband){
673 assert(testPixels[iclass].size()>isample);
674 assert(testPixels[iclass][isample].size()>iband+startBand);
675 assert(offset.size()>iband);
676 assert(scale.size()>iband);
677 float value=testPixels[iclass][isample][iband+startBand];
679 trainingFeatures[iclass][nctraining[iclass]+isample].push_back((value-offset[iband])/scale[iband]);
682 assert(trainingFeatures[iclass].size()==nctraining[iclass]+nctest[iclass]);
685 costfactory.setNcTraining(nctraining);
686 costfactory.setNcTest(nctest);
687 int nFeatures=trainingFeatures[0][0].size();
688 int maxFeatures=(maxFeatures_opt[0])? maxFeatures_opt[0] : 1;
689 double previousCost=-1;
694 if(maxFeatures>=nFeatures){
696 for(
int ifeature=0;ifeature<nFeatures;++ifeature)
697 subset.push_back(ifeature);
698 cost=costfactory.getCost(trainingFeatures);
701 while(fabs(cost-previousCost)>=epsilon_cost_opt[0]){
703 switch(selMap[selector_opt[0]]){
706 cost=selector.floating(trainingFeatures,costfactory,subset,maxFeatures,epsilon_cost_opt[0],verbose_opt[0]);
709 cost=selector.forward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
712 cost=selector.backward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
716 cost=selector.bruteForce(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
719 std::cout <<
"Error: selector not supported, please use sffs, sfs, sbs or bfs" << std::endl;
723 if(verbose_opt[0]>1){
724 std::cout <<
"cost: " << cost << std::endl;
725 std::cout <<
"previousCost: " << previousCost << std::endl;
726 std::cout << std::setprecision(12) <<
"cost-previousCost: " << cost - previousCost <<
" ( " << epsilon_cost_opt[0] <<
")" << std::endl;
728 if(!maxFeatures_opt[0])
736 std::cout <<
"caught feature selection" << std::endl;
741 cout <<
"cost: " << cost << endl;
743 for(list<int>::const_iterator lit=subset.begin();lit!=subset.end();++lit)
744 std::cout <<
" -b " << *lit;
745 std::cout << std::endl;