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"
38 enum SelectorValue { NA=0, SFFS=1, SFS=2, SBS=3, BFS=4};
195 int main(
int argc,
char *argv[])
200 Optionpk<string> input_opt(
"i",
"input",
"input test set (leave empty to perform a cross validation based on training only)");
201 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).");
203 Optionpk<string> label_opt(
"label",
"label",
"identifier for class label in training vector file.",
"label");
204 Optionpk<unsigned short> maxFeatures_opt(
"n",
"nf",
"number of features to select (0 to select optimal number, see also ecost option)", 0);
205 Optionpk<unsigned int> balance_opt(
"bal",
"balance",
"balance the input data to this number of samples for each class", 0);
206 Optionpk<bool> random_opt(
"random",
"random",
"in case of balance, randomize input data",
true);
207 Optionpk<int> minSize_opt(
"min",
"min",
"if number of training pixels is less then min, do not take this class into account", 0);
208 Optionpk<short> band_opt(
"b",
"band",
"band index (starting from 0, either use band option or use start to end)");
210 Optionpk<double> bend_opt(
"e",
"end",
"end band sequence number (set to 0 to include all bands)", 0);
211 Optionpk<double> offset_opt(
"\0",
"offset",
"offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
212 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);
213 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");
214 Optionpk<float> epsilon_cost_opt(
"ecost",
"ecost",
"epsilon for stopping criterion in cost function to determine optimal number of features",0.001);
216 Optionpk<std::string> svm_type_opt(
"svmt",
"svmtype",
"type of SVM (C_SVC, nu_SVC,one_class, epsilon_SVR, nu_SVR)",
"C_SVC");
217 Optionpk<std::string> kernel_type_opt(
"kt",
"kerneltype",
"type of kernel function (linear,polynomial,radial,sigmoid) ",
"radial");
219 Optionpk<float> gamma_opt(
"g",
"gamma",
"gamma in kernel function",1.0);
220 Optionpk<float> coef0_opt(
"c0",
"coef0",
"coef0 in kernel function",0);
221 Optionpk<float> ccost_opt(
"cc",
"ccost",
"the parameter C of C-SVC, epsilon-SVR, and nu-SVR",1000);
222 Optionpk<float> nu_opt(
"nu",
"nu",
"the parameter nu of nu-SVC, one-class SVM, and nu-SVR",0.5);
223 Optionpk<float> epsilon_loss_opt(
"eloss",
"eloss",
"the epsilon in loss function of epsilon-SVR",0.1);
224 Optionpk<int> cache_opt(
"cache",
"cache",
"cache memory size in MB",100);
225 Optionpk<float> epsilon_tol_opt(
"etol",
"etol",
"the tolerance of termination criterion",0.001);
226 Optionpk<bool> shrinking_opt(
"shrink",
"shrink",
"whether to use the shrinking heuristics",
false);
227 Optionpk<bool> prob_est_opt(
"pe",
"probest",
"whether to train a SVC or SVR model for probability estimates",
true,2);
230 Optionpk<short> classvalue_opt(
"r",
"reclass",
"list of class values (use same order as in classname opt.");
231 Optionpk<short> verbose_opt(
"v",
"verbose",
"set to: 0 (results only), 1 (confusion matrix), 2 (debug)",0,2);
233 tlayer_opt.setHide(1);
234 label_opt.setHide(1);
235 balance_opt.setHide(1);
236 random_opt.setHide(1);
237 minSize_opt.setHide(1);
239 bstart_opt.setHide(1);
241 offset_opt.setHide(1);
242 scale_opt.setHide(1);
243 svm_type_opt.setHide(1);
244 kernel_type_opt.setHide(1);
245 kernel_degree_opt.setHide(1);
246 gamma_opt.setHide(1);
247 coef0_opt.setHide(1);
248 ccost_opt.setHide(1);
250 epsilon_loss_opt.setHide(1);
251 cache_opt.setHide(1);
252 epsilon_tol_opt.setHide(1);
253 shrinking_opt.setHide(1);
254 prob_est_opt.setHide(1);
255 selector_opt.setHide(1);
256 epsilon_cost_opt.setHide(1);
258 classname_opt.setHide(1);
259 classvalue_opt.setHide(1);
263 doProcess=input_opt.retrieveOption(argc,argv);
264 training_opt.retrieveOption(argc,argv);
265 maxFeatures_opt.retrieveOption(argc,argv);
266 tlayer_opt.retrieveOption(argc,argv);
267 label_opt.retrieveOption(argc,argv);
268 balance_opt.retrieveOption(argc,argv);
269 random_opt.retrieveOption(argc,argv);
270 minSize_opt.retrieveOption(argc,argv);
271 band_opt.retrieveOption(argc,argv);
272 bstart_opt.retrieveOption(argc,argv);
273 bend_opt.retrieveOption(argc,argv);
274 offset_opt.retrieveOption(argc,argv);
275 scale_opt.retrieveOption(argc,argv);
276 svm_type_opt.retrieveOption(argc,argv);
277 kernel_type_opt.retrieveOption(argc,argv);
278 kernel_degree_opt.retrieveOption(argc,argv);
279 gamma_opt.retrieveOption(argc,argv);
280 coef0_opt.retrieveOption(argc,argv);
281 ccost_opt.retrieveOption(argc,argv);
282 nu_opt.retrieveOption(argc,argv);
283 epsilon_loss_opt.retrieveOption(argc,argv);
284 cache_opt.retrieveOption(argc,argv);
285 epsilon_tol_opt.retrieveOption(argc,argv);
286 shrinking_opt.retrieveOption(argc,argv);
287 prob_est_opt.retrieveOption(argc,argv);
288 selector_opt.retrieveOption(argc,argv);
289 epsilon_cost_opt.retrieveOption(argc,argv);
290 cv_opt.retrieveOption(argc,argv);
291 classname_opt.retrieveOption(argc,argv);
292 classvalue_opt.retrieveOption(argc,argv);
293 verbose_opt.retrieveOption(argc,argv);
295 catch(
string predefinedString){
296 std::cout << predefinedString << std::endl;
301 cout <<
"Usage: pkfssvm -t training -n number" << endl;
303 std::cout <<
"short option -h shows basic options only, use long option --help to show all options" << std::endl;
307 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]);
309 assert(training_opt.size());
311 costfactory.setCv(0);
312 if(verbose_opt[0]>=1){
314 std::cout <<
"input filename: " << input_opt[0] << std::endl;
315 std::cout <<
"training vector file: " << std::endl;
316 for(
int ifile=0;ifile<training_opt.size();++ifile)
317 std::cout << training_opt[ifile] << std::endl;
318 std::cout <<
"verbose: " << verbose_opt[0] << std::endl;
321 static std::map<std::string, SelectorValue> selMap;
328 unsigned int totalSamples=0;
329 unsigned int totalTestSamples=0;
331 unsigned short nclass=0;
349 std::sort(band_opt.begin(),band_opt.end());
351 if(classname_opt.size()){
352 assert(classname_opt.size()==classvalue_opt.size());
353 for(
int iclass=0;iclass<classname_opt.size();++iclass)
354 costfactory.setClassValueMap(classname_opt[iclass],classvalue_opt[iclass]);
358 vector<double> offset;
359 vector<double> scale;
360 vector< Vector2d<float> > trainingPixels;
361 vector< Vector2d<float> > testPixels;
362 map<string,Vector2d<float> > trainingMap;
363 map<string,Vector2d<float> > testMap;
364 vector<string> fields;
368 trainingPixels.clear();
370 if(verbose_opt[0]>=1)
371 std::cout <<
"reading training file " << training_opt[0] << std::endl;
375 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
376 if(input_opt.size()){
378 totalTestSamples=inputReader.readDataImageOgr(testMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
383 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
384 if(input_opt.size()){
386 totalTestSamples=inputReader.readDataImageOgr(testMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
390 if(trainingMap.size()<2){
391 string errorstring=
"Error: could not read at least two classes from training input file";
394 if(input_opt.size()&&testMap.size()<2){
395 string errorstring=
"Error: could not read at least two classes from test input file";
398 trainingReader.close();
401 cerr << error << std::endl;
404 catch(std::exception& e){
405 std::cerr <<
"Error: ";
406 std::cerr << e.what() << std::endl;
407 std::cerr << CPLGetLastErrorMsg() << std::endl;
411 cerr <<
"error catched" << std::endl;
421 std::cout <<
"training pixels: " << std::endl;
422 map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
423 while(mapit!=trainingMap.end()){
425 if((mapit->second).size()<minSize_opt[0]){
426 trainingMap.erase(mapit);
429 costfactory.pushBackName(mapit->first);
430 trainingPixels.push_back(mapit->second);
432 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
435 nclass=trainingPixels.size();
436 if(classname_opt.size())
437 assert(nclass==classname_opt.size());
438 nband=trainingPixels[0][0].size()-2;
440 mapit=testMap.begin();
441 while(mapit!=testMap.end()){
442 if(costfactory.getClassValueMap().size()){
445 if((costfactory.getClassValueMap())[mapit->first]>0){
449 std::cerr <<
"Error: names in classname option are not complete, please check names in test vector and make sure classvalue is > 0" << std::endl;
454 testPixels.push_back(mapit->second);
456 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
459 if(input_opt.size()){
460 assert(nclass==testPixels.size());
461 assert(nband=testPixels[0][0].size()-2);
468 if(balance_opt[0]>0){
472 for(
int iclass=0;iclass<nclass;++iclass){
473 if(trainingPixels[iclass].size()>balance_opt[0]){
474 while(trainingPixels[iclass].size()>balance_opt[0]){
475 int index=rand()%trainingPixels[iclass].size();
476 trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
480 int oldsize=trainingPixels[iclass].size();
481 for(
int isample=trainingPixels[iclass].size();isample<balance_opt[0];++isample){
482 int index = rand()%oldsize;
483 trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
486 totalSamples+=trainingPixels[iclass].size();
488 assert(totalSamples==nclass*balance_opt[0]);
492 offset.resize(nband);
494 if(offset_opt.size()>1)
495 assert(offset_opt.size()==nband);
496 if(scale_opt.size()>1)
497 assert(scale_opt.size()==nband);
498 for(
int iband=0;iband<nband;++iband){
500 std::cout <<
"scaling for band" << iband << std::endl;
501 offset[iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
502 scale[iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
505 float theMin=trainingPixels[0][0][iband+startBand];
506 float theMax=trainingPixels[0][0][iband+startBand];
507 for(
int iclass=0;iclass<nclass;++iclass){
508 for(
int isample=0;isample<trainingPixels[iclass].size();++isample){
509 if(theMin>trainingPixels[iclass][isample][iband+startBand])
510 theMin=trainingPixels[iclass][isample][iband+startBand];
511 if(theMax<trainingPixels[iclass][isample][iband+startBand])
512 theMax=trainingPixels[iclass][isample][iband+startBand];
515 offset[iband]=theMin+(theMax-theMin)/2.0;
516 scale[iband]=(theMax-theMin)/2.0;
517 if(verbose_opt[0]>1){
518 std::cout <<
"Extreme image values for band " << iband <<
": [" << theMin <<
"," << theMax <<
"]" << std::endl;
519 std::cout <<
"Using offset, scale: " << offset[iband] <<
", " << scale[iband] << std::endl;
520 std::cout <<
"scaled values for band " << iband <<
": [" << (theMin-offset[iband])/scale[iband] <<
"," << (theMax-offset[iband])/scale[iband] <<
"]" << std::endl;
532 if(verbose_opt[0]>=1){
533 std::cout <<
"number of bands: " << nband << std::endl;
534 std::cout <<
"number of classes: " << nclass << std::endl;
542 vector<string> nameVector=costfactory.getNameVector();
543 for(
int iname=0;iname<nameVector.size();++iname){
544 if(costfactory.getClassValueMap().empty())
545 costfactory.pushBackClassName(nameVector[iname]);
547 else if(costfactory.getClassIndex(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]))<0)
548 costfactory.pushBackClassName(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]));
554 vector<unsigned int> nctraining;
555 vector<unsigned int> nctest;
556 nctraining.resize(nclass);
557 nctest.resize(nclass);
558 vector< Vector2d<float> > trainingFeatures(nclass);
559 for(
int iclass=0;iclass<nclass;++iclass){
560 if(verbose_opt[0]>=1)
561 std::cout <<
"calculating features for class " << iclass << std::endl;
562 nctraining[iclass]=trainingPixels[iclass].size();
563 if(verbose_opt[0]>=1)
564 std::cout <<
"nctraining[" << iclass <<
"]: " << nctraining[iclass] << std::endl;
565 if(testPixels.size()>iclass){
566 nctest[iclass]=testPixels[iclass].size();
567 if(verbose_opt[0]>=1){
568 std::cout <<
"nctest[" << iclass <<
"]: " << nctest[iclass] << std::endl;
574 trainingFeatures[iclass].resize(nctraining[iclass]+nctest[iclass]);
575 for(
int isample=0;isample<nctraining[iclass];++isample){
577 for(
int iband=0;iband<nband;++iband){
578 assert(trainingPixels[iclass].size()>isample);
579 assert(trainingPixels[iclass][isample].size()>iband+startBand);
580 assert(offset.size()>iband);
581 assert(scale.size()>iband);
582 float value=trainingPixels[iclass][isample][iband+startBand];
583 trainingFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
586 for(
int isample=0;isample<nctest[iclass];++isample){
588 for(
int iband=0;iband<nband;++iband){
589 assert(testPixels[iclass].size()>isample);
590 assert(testPixels[iclass][isample].size()>iband+startBand);
591 assert(offset.size()>iband);
592 assert(scale.size()>iband);
593 float value=testPixels[iclass][isample][iband+startBand];
595 trainingFeatures[iclass][nctraining[iclass]+isample].push_back((value-offset[iband])/scale[iband]);
598 assert(trainingFeatures[iclass].size()==nctraining[iclass]+nctest[iclass]);
601 costfactory.setNcTraining(nctraining);
602 costfactory.setNcTest(nctest);
603 int nFeatures=trainingFeatures[0][0].size();
604 int maxFeatures=(maxFeatures_opt[0])? maxFeatures_opt[0] : 1;
605 double previousCost=-1;
610 if(maxFeatures>=nFeatures){
612 for(
int ifeature=0;ifeature<nFeatures;++ifeature)
613 subset.push_back(ifeature);
614 cost=costfactory.getCost(trainingFeatures);
617 while(fabs(cost-previousCost)>=epsilon_cost_opt[0]){
619 switch(selMap[selector_opt[0]]){
622 cost=selector.floating(trainingFeatures,costfactory,subset,maxFeatures,epsilon_cost_opt[0],verbose_opt[0]);
625 cost=selector.forward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
628 cost=selector.backward(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
632 cost=selector.bruteForce(trainingFeatures,costfactory,subset,maxFeatures,verbose_opt[0]);
635 std::cout <<
"Error: selector not supported, please use sffs, sfs, sbs or bfs" << std::endl;
639 if(verbose_opt[0]>1){
640 std::cout <<
"cost: " << cost << std::endl;
641 std::cout <<
"previousCost: " << previousCost << std::endl;
642 std::cout << std::setprecision(12) <<
"cost-previousCost: " << cost - previousCost <<
" ( " << epsilon_cost_opt[0] <<
")" << std::endl;
644 if(!maxFeatures_opt[0])
652 std::cout <<
"catched feature selection" << std::endl;
657 cout <<
"cost: " << cost << endl;
659 for(list<int>::const_iterator lit=subset.begin();lit!=subset.end();++lit)
660 std::cout <<
" -b " << *lit;
661 std::cout << std::endl;