26 #include "base/Optionpk.h"
27 #include "base/Optionpk.h"
28 #include "algorithms/ConfusionMatrix.h"
29 #include "algorithms/FeatureSelector.h"
30 #include "algorithms/OptFactory.h"
31 #include "algorithms/CostFactorySVM.h"
32 #include "algorithms/svm.h"
33 #include "imageclasses/ImgReaderOgr.h"
41 #define Malloc(type,n) (type *)malloc((n)*sizeof(type))
43 double objFunction(
const std::vector<double> &x, std::vector<double> &grad,
void *my_func_data);
46 map<string,short> classValueMap;
47 vector<std::string> nameVector;
48 vector<unsigned int> nctraining;
49 vector<unsigned int> nctest;
50 Optionpk<std::string> svm_type_opt(
"svmt",
"svmtype",
"type of SVM (C_SVC, nu_SVC,one_class, epsilon_SVR, nu_SVR)",
"C_SVC");
51 Optionpk<std::string> kernel_type_opt(
"kt",
"kerneltype",
"type of kernel function (linear,polynomial,radial,sigmoid) ",
"radial");
54 Optionpk<float> nu_opt(
"nu",
"nu",
"the parameter nu of nu-SVC, one-class SVM, and nu-SVR",0.5);
55 Optionpk<float> epsilon_loss_opt(
"eloss",
"eloss",
"the epsilon in loss function of epsilon-SVR",0.1);
56 Optionpk<int> cache_opt(
"cache",
"cache",
"cache memory size in MB",100);
57 Optionpk<float> epsilon_tol_opt(
"etol",
"etol",
"the tolerance of termination criterion",0.001);
58 Optionpk<bool> shrinking_opt(
"shrink",
"shrink",
"whether to use the shrinking heuristics",
false);
59 Optionpk<bool> prob_est_opt(
"pe",
"probest",
"whether to train a SVC or SVR model for probability estimates",
true,2);
60 Optionpk<bool> costfunction_opt(
"cf",
"cf",
"use Overall Accuracy instead of kappa",
false);
64 Optionpk<short> classvalue_opt(
"r",
"reclass",
"list of class values (use same order as in class opt).");
65 Optionpk<short> verbose_opt(
"v",
"verbose",
"use 1 to output intermediate results for plotting",0,2);
67 double objFunction(
const std::vector<double> &x, std::vector<double> &grad,
void *my_func_data){
70 vector<Vector2d<float> > *tf=
reinterpret_cast<vector<Vector2d<float>
>*> (my_func_data);
73 double error=1.0/epsilon_tol_opt[0];
77 CostFactorySVM costfactory(svm_type_opt[0], kernel_type_opt[0], kernel_degree_opt[0], gamma, coef0_opt[0], ccost, 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]);
83 costfactory.setCv(cv_opt[0]);
85 if(classname_opt.size()){
86 assert(classname_opt.size()==classvalue_opt.size());
87 for(
int iclass=0;iclass<classname_opt.size();++iclass)
88 costfactory.setClassValueMap(classname_opt[iclass],classvalue_opt[iclass]);
91 costfactory.setNameVector(nameVector);
93 for(
int iname=0;iname<nameVector.size();++iname){
94 if(costfactory.getClassValueMap().empty()){
95 costfactory.pushBackClassName(nameVector[iname]);
98 else if(costfactory.getClassIndex(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]))<0)
99 costfactory.pushBackClassName(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]));
102 costfactory.setNcTraining(nctraining);
103 costfactory.setNcTest(nctest);
105 kappa=costfactory.getCost(*tf);
257 int main(
int argc,
char *argv[])
259 map<short,int> reclassMap;
260 vector<int> vreclass;
261 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).");
262 Optionpk<float> ccost_opt(
"cc",
"ccost",
"min and max boundaries the parameter C of C-SVC, epsilon-SVR, and nu-SVR (optional: initial value)",1);
263 Optionpk<float> gamma_opt(
"g",
"gamma",
"min max boundaries for gamma in kernel function (optional: initial value)",0);
264 Optionpk<double> stepcc_opt(
"stepcc",
"stepcc",
"multiplicative step for ccost in GRID search",2);
265 Optionpk<double> stepg_opt(
"stepg",
"stepg",
"multiplicative step for gamma in GRID search",2);
268 Optionpk<string> label_opt(
"label",
"label",
"identifier for class label in training vector file.",
"label");
270 Optionpk<unsigned int> balance_opt(
"bal",
"balance",
"balance the input data to this number of samples for each class", 0);
271 Optionpk<bool> random_opt(
"random",
"random",
"in case of balance, randomize input data",
true);
272 Optionpk<int> minSize_opt(
"min",
"min",
"if number of training pixels is less then min, do not take this class into account", 0);
273 Optionpk<short> band_opt(
"b",
"band",
"band index (starting from 0, either use band option or use start to end)");
275 Optionpk<double> bend_opt(
"e",
"end",
"end band sequence number (set to 0 to include all bands)", 0);
276 Optionpk<double> offset_opt(
"\0",
"offset",
"offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
277 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);
279 Optionpk<string> algorithm_opt(
"a",
"algorithm",
"GRID, or any optimization algorithm from http://ab-initio.mit.edu/wiki/index.php/NLopt_Algorithms",
"GRID");
280 Optionpk<double> tolerance_opt(
"tol",
"tolerance",
"relative tolerance for stopping criterion",0.0001);
282 input_opt.setHide(1);
283 tlayer_opt.setHide(1);
284 label_opt.setHide(1);
285 balance_opt.setHide(1);
286 random_opt.setHide(1);
287 minSize_opt.setHide(1);
289 bstart_opt.setHide(1);
291 offset_opt.setHide(1);
292 scale_opt.setHide(1);
293 svm_type_opt.setHide(1);
294 kernel_type_opt.setHide(1);
295 kernel_degree_opt.setHide(1);
296 coef0_opt.setHide(1);
298 epsilon_loss_opt.setHide(1);
299 cache_opt.setHide(1);
300 epsilon_tol_opt.setHide(1);
301 shrinking_opt.setHide(1);
302 prob_est_opt.setHide(1);
304 costfunction_opt.setHide(1);
305 maxit_opt.setHide(1);
306 tolerance_opt.setHide(1);
307 algorithm_opt.setHide(1);
308 classname_opt.setHide(1);
309 classvalue_opt.setHide(1);
313 doProcess=training_opt.retrieveOption(argc,argv);
314 ccost_opt.retrieveOption(argc,argv);
315 gamma_opt.retrieveOption(argc,argv);
316 stepcc_opt.retrieveOption(argc,argv);
317 stepg_opt.retrieveOption(argc,argv);
318 input_opt.retrieveOption(argc,argv);
319 tlayer_opt.retrieveOption(argc,argv);
320 label_opt.retrieveOption(argc,argv);
321 balance_opt.retrieveOption(argc,argv);
322 random_opt.retrieveOption(argc,argv);
323 minSize_opt.retrieveOption(argc,argv);
324 band_opt.retrieveOption(argc,argv);
325 bstart_opt.retrieveOption(argc,argv);
326 bend_opt.retrieveOption(argc,argv);
327 offset_opt.retrieveOption(argc,argv);
328 scale_opt.retrieveOption(argc,argv);
329 svm_type_opt.retrieveOption(argc,argv);
330 kernel_type_opt.retrieveOption(argc,argv);
331 kernel_degree_opt.retrieveOption(argc,argv);
332 coef0_opt.retrieveOption(argc,argv);
333 nu_opt.retrieveOption(argc,argv);
334 epsilon_loss_opt.retrieveOption(argc,argv);
335 cache_opt.retrieveOption(argc,argv);
336 epsilon_tol_opt.retrieveOption(argc,argv);
337 shrinking_opt.retrieveOption(argc,argv);
338 prob_est_opt.retrieveOption(argc,argv);
339 cv_opt.retrieveOption(argc,argv);
340 costfunction_opt.retrieveOption(argc,argv);
341 maxit_opt.retrieveOption(argc,argv);
342 tolerance_opt.retrieveOption(argc,argv);
343 algorithm_opt.retrieveOption(argc,argv);
344 classname_opt.retrieveOption(argc,argv);
345 classvalue_opt.retrieveOption(argc,argv);
346 verbose_opt.retrieveOption(argc,argv);
348 catch(
string predefinedString){
349 std::cout << predefinedString << std::endl;
354 cout <<
"Usage: pkoptsvm -t training" << endl;
356 std::cout <<
"short option -h shows basic options only, use long option --help to show all options" << std::endl;
360 assert(training_opt.size());
364 if(verbose_opt[0]>=1){
366 std::cout <<
"input filename: " << input_opt[0] << std::endl;
367 std::cout <<
"training vector file: " << std::endl;
368 for(
int ifile=0;ifile<training_opt.size();++ifile)
369 std::cout << training_opt[ifile] << std::endl;
370 std::cout <<
"verbose: " << verbose_opt[0] << std::endl;
373 unsigned int totalSamples=0;
374 unsigned int totalTestSamples=0;
376 unsigned short nclass=0;
380 vector<double> offset;
381 vector<double> scale;
382 vector< Vector2d<float> > trainingPixels;
383 vector< Vector2d<float> > testPixels;
399 std::sort(band_opt.begin(),band_opt.end());
402 if(classname_opt.size()){
403 assert(classname_opt.size()==classvalue_opt.size());
404 for(
int iclass=0;iclass<classname_opt.size();++iclass)
405 classValueMap[classname_opt[iclass]]=classvalue_opt[iclass];
410 vector<string> fields;
412 trainingPixels.clear();
414 map<string,Vector2d<float> > trainingMap;
415 map<string,Vector2d<float> > testMap;
416 if(verbose_opt[0]>=1)
417 std::cout <<
"reading training file " << training_opt[0] << std::endl;
421 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
422 if(input_opt.size()){
424 totalTestSamples=inputReader.readDataImageOgr(testMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
429 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
430 if(input_opt.size()){
432 totalTestSamples=inputReader.readDataImageOgr(testMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
435 trainingReader.close();
437 if(trainingMap.size()<2){
441 string errorstring=
"Error: could not read at least two classes from training input file";
444 if(input_opt.size()&&testMap.size()<2){
445 string errorstring=
"Error: could not read at least two classes from test input file";
450 cerr << error << std::endl;
454 cerr <<
"error catched" << std::endl;
464 std::cout <<
"training pixels: " << std::endl;
465 map<string,Vector2d<float> >::iterator mapit;
466 mapit=trainingMap.begin();
467 while(mapit!=trainingMap.end()){
468 if(classValueMap.size()){
470 if(classValueMap[mapit->first]>0){
472 std::cout << mapit->first <<
" -> " << classValueMap[mapit->first] << std::endl;
475 std::cerr <<
"Error: names in classname option are not complete, please check names in training vector and make sure classvalue is > 0" << std::endl;
480 if((mapit->second).size()<minSize_opt[0]){
481 trainingMap.erase(mapit);
484 nameVector.push_back(mapit->first);
485 trainingPixels.push_back(mapit->second);
487 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
492 nclass=trainingPixels.size();
493 if(classname_opt.size())
494 assert(nclass==classname_opt.size());
495 nband=trainingPixels[0][0].size()-2;
497 mapit=testMap.begin();
498 while(mapit!=testMap.end()){
499 if(classValueMap.size()){
501 if(classValueMap[mapit->first]>0){
505 std::cerr <<
"Error: names in classname option are not complete, please check names in test vector and make sure classvalue is > 0" << std::endl;
510 testPixels.push_back(mapit->second);
512 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
515 if(input_opt.size()){
516 assert(nclass==testPixels.size());
517 assert(nband=testPixels[0][0].size()-2);
523 if(balance_opt[0]>0){
527 for(
int iclass=0;iclass<nclass;++iclass){
528 if(trainingPixels[iclass].size()>balance_opt[0]){
529 while(trainingPixels[iclass].size()>balance_opt[0]){
530 int index=rand()%trainingPixels[iclass].size();
531 trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
535 int oldsize=trainingPixels[iclass].size();
536 for(
int isample=trainingPixels[iclass].size();isample<balance_opt[0];++isample){
537 int index = rand()%oldsize;
538 trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
541 totalSamples+=trainingPixels[iclass].size();
543 assert(totalSamples==nclass*balance_opt[0]);
548 offset.resize(nband);
550 if(offset_opt.size()>1)
551 assert(offset_opt.size()==nband);
552 if(scale_opt.size()>1)
553 assert(scale_opt.size()==nband);
554 for(
int iband=0;iband<nband;++iband){
556 std::cout <<
"scaling for band" << iband << std::endl;
557 offset[iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
558 scale[iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
561 float theMin=trainingPixels[0][0][iband+startBand];
562 float theMax=trainingPixels[0][0][iband+startBand];
563 for(
int iclass=0;iclass<nclass;++iclass){
564 for(
int isample=0;isample<trainingPixels[iclass].size();++isample){
565 if(theMin>trainingPixels[iclass][isample][iband+startBand])
566 theMin=trainingPixels[iclass][isample][iband+startBand];
567 if(theMax<trainingPixels[iclass][isample][iband+startBand])
568 theMax=trainingPixels[iclass][isample][iband+startBand];
571 offset[iband]=theMin+(theMax-theMin)/2.0;
572 scale[iband]=(theMax-theMin)/2.0;
573 if(verbose_opt[0]>1){
574 std::cout <<
"Extreme image values for band " << iband <<
": [" << theMin <<
"," << theMax <<
"]" << std::endl;
575 std::cout <<
"Using offset, scale: " << offset[iband] <<
", " << scale[iband] << std::endl;
576 std::cout <<
"scaled values for band " << iband <<
": [" << (theMin-offset[iband])/scale[iband] <<
"," << (theMax-offset[iband])/scale[iband] <<
"]" << std::endl;
588 if(verbose_opt[0]>=1){
589 std::cout <<
"number of bands: " << nband << std::endl;
590 std::cout <<
"number of classes: " << nclass << std::endl;
598 nctraining.resize(nclass);
599 nctest.resize(nclass);
600 vector< Vector2d<float> > trainingFeatures(nclass);
601 for(
int iclass=0;iclass<nclass;++iclass){
602 if(verbose_opt[0]>=1)
603 std::cout <<
"calculating features for class " << iclass << std::endl;
604 nctraining[iclass]=trainingPixels[iclass].size();
605 if(verbose_opt[0]>=1)
606 std::cout <<
"nctraining[" << iclass <<
"]: " << nctraining[iclass] << std::endl;
607 if(testPixels.size()>iclass){
608 nctest[iclass]=testPixels[iclass].size();
609 if(verbose_opt[0]>=1){
610 std::cout <<
"nctest[" << iclass <<
"]: " << nctest[iclass] << std::endl;
616 trainingFeatures[iclass].resize(nctraining[iclass]+nctest[iclass]);
617 for(
int isample=0;isample<nctraining[iclass];++isample){
619 for(
int iband=0;iband<nband;++iband){
620 assert(trainingPixels[iclass].size()>isample);
621 assert(trainingPixels[iclass][isample].size()>iband+startBand);
622 assert(offset.size()>iband);
623 assert(scale.size()>iband);
624 float value=trainingPixels[iclass][isample][iband+startBand];
625 trainingFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
629 for(
int isample=0;isample<nctest[iclass];++isample){
631 for(
int iband=0;iband<nband;++iband){
632 assert(testPixels[iclass].size()>isample);
633 assert(testPixels[iclass][isample].size()>iband+startBand);
634 assert(offset.size()>iband);
635 assert(scale.size()>iband);
636 float value=testPixels[iclass][isample][iband+startBand];
638 trainingFeatures[iclass][nctraining[iclass]+isample].push_back((value-offset[iband])/scale[iband]);
641 assert(trainingFeatures[iclass].size()==nctraining[iclass]+nctest[iclass]);
644 assert(ccost_opt.size()>1);
645 if(ccost_opt.size()<3)
646 ccost_opt.push_back(sqrt(ccost_opt[0]*ccost_opt[1]));
647 assert(gamma_opt.size()>1);
648 if(gamma_opt.size()<3)
649 gamma_opt.push_back(sqrt(gamma_opt[0]*gamma_opt[1]));
650 assert(ccost_opt.size()==3);
651 assert(gamma_opt.size()==3);
652 assert(gamma_opt[0]<gamma_opt[1]);
653 assert(gamma_opt[0]<gamma_opt[2]);
654 assert(gamma_opt[2]<gamma_opt[1]);
655 assert(ccost_opt[0]<ccost_opt[1]);
656 assert(ccost_opt[0]<ccost_opt[2]);
657 assert(ccost_opt[2]<ccost_opt[1]);
659 std::vector<double> x(2);
660 if(algorithm_opt[0]==
"GRID"){
667 const char* pszMessage;
668 void* pProgressArg=NULL;
669 GDALProgressFunc pfnProgress=GDALTermProgress;
672 pfnProgress(progress,pszMessage,pProgressArg);
673 double ncost=log(ccost_opt[1])/log(stepcc_opt[0])-log(ccost_opt[0])/log(stepcc_opt[0]);
674 double ngamma=log(gamma_opt[1])/log(stepg_opt[0])-log(gamma_opt[0])/log(stepg_opt[0]);
675 for(
double ccost=ccost_opt[0];ccost<=ccost_opt[1];ccost*=stepcc_opt[0]){
676 for(
double gamma=gamma_opt[0];gamma<=gamma_opt[1];gamma*=stepg_opt[0]){
679 std::vector<double> theGrad;
681 kappa=objFunction(x,theGrad,&trainingFeatures);
688 std::cout << ccost <<
" " << gamma <<
" " << kappa<< std::endl;
689 progress+=1.0/ncost/ngamma;
691 pfnProgress(progress,pszMessage,pProgressArg);
696 pfnProgress(progress,pszMessage,pProgressArg);
701 nlopt::opt optimizer=OptFactory::getOptimizer(algorithm_opt[0],2);
703 std::cout <<
"optimization algorithm: " << optimizer.get_algorithm_name() <<
"..." << std::endl;
704 std::vector<double> lb(2);
705 std::vector<double> init(2);
706 std::vector<double> ub(2);
709 lb[1]=(gamma_opt[0]>0)? gamma_opt[0] : 1.0/trainingFeatures[0][0].size();
710 init[0]=ccost_opt[2];
711 init[1]=(gamma_opt[2]>0)? gamma_opt[1] : 1.0/trainingFeatures[0][0].size();
713 ub[1]=(gamma_opt[1]>0)? gamma_opt[1] : 1.0/trainingFeatures[0][0].size();
715 optimizer.set_max_objective(objFunction, &trainingFeatures);
716 optimizer.set_lower_bounds(lb);
717 optimizer.set_upper_bounds(ub);
719 std::cout <<
"set stopping criteria" << std::endl;
722 optimizer.set_maxeval(maxit_opt[0]);
724 optimizer.set_xtol_rel(tolerance_opt[0]);
728 optimizer.optimize(x, minf);
731 cerr << error << std::endl;
734 catch (exception& e){
735 cout << e.what() << endl;
738 cerr <<
"error catched" << std::endl;
745 std::cout <<
"optimized with " << optimizer.get_algorithm_name() <<
"..." << std::endl;
747 std::cout <<
" --ccost " << x[0];
748 std::cout <<
" --gamma " << x[1];
749 std::cout << std::endl;