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"
106 #define Malloc(type,n) (type *)malloc((n)*sizeof(type))
108 double objFunction(
const std::vector<double> &x, std::vector<double> &grad,
void *my_func_data);
111 map<string,short> classValueMap;
112 vector<std::string> nameVector;
113 vector<unsigned int> nctraining;
114 vector<unsigned int> nctest;
115 Optionpk<std::string> svm_type_opt(
"svmt",
"svmtype",
"type of SVM (C_SVC, nu_SVC,one_class, epsilon_SVR, nu_SVR)",
"C_SVC");
116 Optionpk<std::string> kernel_type_opt(
"kt",
"kerneltype",
"type of kernel function (linear,polynomial,radial,sigmoid) ",
"radial");
118 Optionpk<float> coef0_opt(
"c0",
"coef0",
"coef0 in kernel function",0);
119 Optionpk<float> nu_opt(
"nu",
"nu",
"the parameter nu of nu-SVC, one-class SVM, and nu-SVR",0.5);
120 Optionpk<float> epsilon_loss_opt(
"eloss",
"eloss",
"the epsilon in loss function of epsilon-SVR",0.1);
121 Optionpk<int> cache_opt(
"cache",
"cache",
"cache memory size in MB",100);
122 Optionpk<float> epsilon_tol_opt(
"etol",
"etol",
"the tolerance of termination criterion",0.001);
123 Optionpk<bool> shrinking_opt(
"shrink",
"shrink",
"whether to use the shrinking heuristics",
false);
124 Optionpk<bool> prob_est_opt(
"pe",
"probest",
"whether to train a SVC or SVR model for probability estimates",
true,2);
125 Optionpk<bool> costfunction_opt(
"cf",
"cf",
"use Overall Accuracy instead of kappa",
false);
129 Optionpk<short> classvalue_opt(
"r",
"reclass",
"list of class values (use same order as in class opt).");
130 Optionpk<short> verbose_opt(
"v",
"verbose",
"use 1 to output intermediate results for plotting",0,2);
132 double objFunction(
const std::vector<double> &x, std::vector<double> &grad,
void *my_func_data){
134 assert(grad.empty());
135 vector<Vector2d<float> > *tf=
reinterpret_cast<vector<Vector2d<float>
>*> (my_func_data);
138 double error=1.0/epsilon_tol_opt[0];
142 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]);
148 costfactory.setCv(cv_opt[0]);
150 if(classname_opt.size()){
151 assert(classname_opt.size()==classvalue_opt.size());
152 for(
int iclass=0;iclass<classname_opt.size();++iclass)
153 costfactory.setClassValueMap(classname_opt[iclass],classvalue_opt[iclass]);
156 costfactory.setNameVector(nameVector);
158 for(
int iname=0;iname<nameVector.size();++iname){
159 if(costfactory.getClassValueMap().empty()){
160 costfactory.pushBackClassName(nameVector[iname]);
163 else if(costfactory.getClassIndex(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]))<0)
164 costfactory.pushBackClassName(type2string<short>((costfactory.getClassValueMap())[nameVector[iname]]));
167 costfactory.setNcTraining(nctraining);
168 costfactory.setNcTest(nctest);
170 kappa=costfactory.getCost(*tf);
174 int main(
int argc,
char *argv[])
176 map<short,int> reclassMap;
177 vector<int> vreclass;
178 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).");
179 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);
180 Optionpk<float> gamma_opt(
"g",
"gamma",
"min max boundaries for gamma in kernel function (optional: initial value)",0);
181 Optionpk<double> stepcc_opt(
"stepcc",
"stepcc",
"multiplicative step for ccost in GRID search",2);
182 Optionpk<double> stepg_opt(
"stepg",
"stepg",
"multiplicative step for gamma in GRID search",2);
185 Optionpk<string> label_opt(
"label",
"label",
"identifier for class label in training vector file.",
"label");
187 Optionpk<unsigned int> balance_opt(
"bal",
"balance",
"balance the input data to this number of samples for each class", 0);
188 Optionpk<bool> random_opt(
"random",
"random",
"in case of balance, randomize input data",
true);
189 Optionpk<int> minSize_opt(
"min",
"min",
"if number of training pixels is less then min, do not take this class into account", 0);
190 Optionpk<unsigned short> band_opt(
"b",
"band",
"band index (starting from 0, either use band option or use start to end)");
193 Optionpk<double> offset_opt(
"offset",
"offset",
"offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
194 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);
197 Optionpk<double> tolerance_opt(
"tol",
"tolerance",
"relative tolerance for stopping criterion",0.0001);
199 input_opt.setHide(1);
200 tlayer_opt.setHide(1);
201 label_opt.setHide(1);
202 balance_opt.setHide(1);
203 random_opt.setHide(1);
204 minSize_opt.setHide(1);
206 bstart_opt.setHide(1);
208 offset_opt.setHide(1);
209 scale_opt.setHide(1);
210 svm_type_opt.setHide(1);
211 kernel_type_opt.setHide(1);
212 kernel_degree_opt.setHide(1);
213 coef0_opt.setHide(1);
215 epsilon_loss_opt.setHide(1);
216 cache_opt.setHide(1);
217 epsilon_tol_opt.setHide(1);
218 shrinking_opt.setHide(1);
219 prob_est_opt.setHide(1);
221 costfunction_opt.setHide(1);
222 maxit_opt.setHide(1);
223 tolerance_opt.setHide(1);
225 classname_opt.setHide(1);
226 classvalue_opt.setHide(1);
230 doProcess=training_opt.retrieveOption(argc,argv);
231 ccost_opt.retrieveOption(argc,argv);
232 gamma_opt.retrieveOption(argc,argv);
233 stepcc_opt.retrieveOption(argc,argv);
234 stepg_opt.retrieveOption(argc,argv);
235 input_opt.retrieveOption(argc,argv);
236 tlayer_opt.retrieveOption(argc,argv);
237 label_opt.retrieveOption(argc,argv);
238 balance_opt.retrieveOption(argc,argv);
239 random_opt.retrieveOption(argc,argv);
240 minSize_opt.retrieveOption(argc,argv);
241 band_opt.retrieveOption(argc,argv);
242 bstart_opt.retrieveOption(argc,argv);
243 bend_opt.retrieveOption(argc,argv);
244 offset_opt.retrieveOption(argc,argv);
245 scale_opt.retrieveOption(argc,argv);
246 svm_type_opt.retrieveOption(argc,argv);
247 kernel_type_opt.retrieveOption(argc,argv);
248 kernel_degree_opt.retrieveOption(argc,argv);
249 coef0_opt.retrieveOption(argc,argv);
250 nu_opt.retrieveOption(argc,argv);
251 epsilon_loss_opt.retrieveOption(argc,argv);
252 cache_opt.retrieveOption(argc,argv);
253 epsilon_tol_opt.retrieveOption(argc,argv);
254 shrinking_opt.retrieveOption(argc,argv);
255 prob_est_opt.retrieveOption(argc,argv);
256 cv_opt.retrieveOption(argc,argv);
257 costfunction_opt.retrieveOption(argc,argv);
258 maxit_opt.retrieveOption(argc,argv);
259 tolerance_opt.retrieveOption(argc,argv);
261 classname_opt.retrieveOption(argc,argv);
262 classvalue_opt.retrieveOption(argc,argv);
263 verbose_opt.retrieveOption(argc,argv);
265 catch(
string predefinedString){
266 std::cout << predefinedString << std::endl;
271 cout <<
"Usage: pkoptsvm -t training" << endl;
273 std::cout <<
"short option -h shows basic options only, use long option --help to show all options" << std::endl;
277 assert(training_opt.size());
281 if(verbose_opt[0]>=1){
283 std::cout <<
"input filename: " << input_opt[0] << std::endl;
284 std::cout <<
"training vector file: " << std::endl;
285 for(
int ifile=0;ifile<training_opt.size();++ifile)
286 std::cout << training_opt[ifile] << std::endl;
287 std::cout <<
"verbose: " << verbose_opt[0] << std::endl;
290 unsigned int totalSamples=0;
291 unsigned int totalTestSamples=0;
293 unsigned short nclass=0;
297 vector<double> offset;
298 vector<double> scale;
299 vector< Vector2d<float> > trainingPixels;
300 vector< Vector2d<float> > testPixels;
316 if(bstart_opt.size()){
317 if(bend_opt.size()!=bstart_opt.size()){
318 string errorstring=
"Error: options for start and end band indexes must be provided as pairs, missing end band";
322 for(
int ipair=0;ipair<bstart_opt.size();++ipair){
323 if(bend_opt[ipair]<=bstart_opt[ipair]){
324 string errorstring=
"Error: index for end band must be smaller then start band";
327 for(
int iband=bstart_opt[ipair];iband<=bend_opt[ipair];++iband)
328 band_opt.push_back(iband);
333 cerr << error << std::endl;
338 std::sort(band_opt.begin(),band_opt.end());
341 if(classname_opt.size()){
342 assert(classname_opt.size()==classvalue_opt.size());
343 for(
int iclass=0;iclass<classname_opt.size();++iclass)
344 classValueMap[classname_opt[iclass]]=classvalue_opt[iclass];
349 vector<string> fields;
351 trainingPixels.clear();
353 map<string,Vector2d<float> > trainingMap;
354 map<string,Vector2d<float> > testMap;
355 if(verbose_opt[0]>=1)
356 std::cout <<
"reading training file " << training_opt[0] << std::endl;
360 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
361 if(input_opt.size()){
363 totalTestSamples=inputReader.readDataImageOgr(testMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
368 totalSamples=trainingReader.readDataImageOgr(trainingMap,fields,0,0,label_opt[0],tlayer_opt,verbose_opt[0]);
369 if(input_opt.size()){
371 totalTestSamples=inputReader.readDataImageOgr(testMap,fields,0,0,label_opt[0],tlayer_opt,verbose_opt[0]);
374 trainingReader.close();
376 if(trainingMap.size()<2){
380 string errorstring=
"Error: could not read at least two classes from training input file";
383 if(input_opt.size()&&testMap.size()<2){
384 string errorstring=
"Error: could not read at least two classes from test input file";
389 cerr << error << std::endl;
393 cerr <<
"error catched" << std::endl;
403 std::cout <<
"training pixels: " << std::endl;
404 map<string,Vector2d<float> >::iterator mapit;
405 mapit=trainingMap.begin();
406 while(mapit!=trainingMap.end()){
407 if(classValueMap.size()){
409 if(classValueMap[mapit->first]>0){
411 std::cout << mapit->first <<
" -> " << classValueMap[mapit->first] << std::endl;
414 std::cerr <<
"Error: names in classname option are not complete, please check names in training vector and make sure classvalue is > 0" << std::endl;
419 if((mapit->second).size()<minSize_opt[0]){
420 trainingMap.erase(mapit);
423 nameVector.push_back(mapit->first);
424 trainingPixels.push_back(mapit->second);
426 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
431 nclass=trainingPixels.size();
432 if(classname_opt.size())
433 assert(nclass==classname_opt.size());
434 nband=trainingPixels[0][0].size()-2;
436 mapit=testMap.begin();
437 while(mapit!=testMap.end()){
438 if(classValueMap.size()){
440 if(classValueMap[mapit->first]>0){
444 std::cerr <<
"Error: names in classname option are not complete, please check names in test vector and make sure classvalue is > 0" << std::endl;
449 testPixels.push_back(mapit->second);
451 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
454 if(input_opt.size()){
455 assert(nclass==testPixels.size());
456 assert(nband=testPixels[0][0].size()-2);
462 if(balance_opt[0]>0){
466 for(
int iclass=0;iclass<nclass;++iclass){
467 if(trainingPixels[iclass].size()>balance_opt[0]){
468 while(trainingPixels[iclass].size()>balance_opt[0]){
469 int index=rand()%trainingPixels[iclass].size();
470 trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
474 int oldsize=trainingPixels[iclass].size();
475 for(
int isample=trainingPixels[iclass].size();isample<balance_opt[0];++isample){
476 int index = rand()%oldsize;
477 trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
480 totalSamples+=trainingPixels[iclass].size();
482 assert(totalSamples==nclass*balance_opt[0]);
487 offset.resize(nband);
489 if(offset_opt.size()>1)
490 assert(offset_opt.size()==nband);
491 if(scale_opt.size()>1)
492 assert(scale_opt.size()==nband);
493 for(
int iband=0;iband<nband;++iband){
495 std::cout <<
"scaling for band" << iband << std::endl;
496 offset[iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
497 scale[iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
500 float theMin=trainingPixels[0][0][iband+startBand];
501 float theMax=trainingPixels[0][0][iband+startBand];
502 for(
int iclass=0;iclass<nclass;++iclass){
503 for(
int isample=0;isample<trainingPixels[iclass].size();++isample){
504 if(theMin>trainingPixels[iclass][isample][iband+startBand])
505 theMin=trainingPixels[iclass][isample][iband+startBand];
506 if(theMax<trainingPixels[iclass][isample][iband+startBand])
507 theMax=trainingPixels[iclass][isample][iband+startBand];
510 offset[iband]=theMin+(theMax-theMin)/2.0;
511 scale[iband]=(theMax-theMin)/2.0;
512 if(verbose_opt[0]>1){
513 std::cout <<
"Extreme image values for band " << iband <<
": [" << theMin <<
"," << theMax <<
"]" << std::endl;
514 std::cout <<
"Using offset, scale: " << offset[iband] <<
", " << scale[iband] << std::endl;
515 std::cout <<
"scaled values for band " << iband <<
": [" << (theMin-offset[iband])/scale[iband] <<
"," << (theMax-offset[iband])/scale[iband] <<
"]" << std::endl;
527 if(verbose_opt[0]>=1){
528 std::cout <<
"number of bands: " << nband << std::endl;
529 std::cout <<
"number of classes: " << nclass << std::endl;
537 nctraining.resize(nclass);
538 nctest.resize(nclass);
539 vector< Vector2d<float> > trainingFeatures(nclass);
540 for(
int iclass=0;iclass<nclass;++iclass){
541 if(verbose_opt[0]>=1)
542 std::cout <<
"calculating features for class " << iclass << std::endl;
543 nctraining[iclass]=trainingPixels[iclass].size();
544 if(verbose_opt[0]>=1)
545 std::cout <<
"nctraining[" << iclass <<
"]: " << nctraining[iclass] << std::endl;
546 if(testPixels.size()>iclass){
547 nctest[iclass]=testPixels[iclass].size();
548 if(verbose_opt[0]>=1){
549 std::cout <<
"nctest[" << iclass <<
"]: " << nctest[iclass] << std::endl;
555 trainingFeatures[iclass].resize(nctraining[iclass]+nctest[iclass]);
556 for(
int isample=0;isample<nctraining[iclass];++isample){
558 for(
int iband=0;iband<nband;++iband){
559 assert(trainingPixels[iclass].size()>isample);
560 assert(trainingPixels[iclass][isample].size()>iband+startBand);
561 assert(offset.size()>iband);
562 assert(scale.size()>iband);
563 float value=trainingPixels[iclass][isample][iband+startBand];
564 trainingFeatures[iclass][isample].push_back((value-offset[iband])/scale[iband]);
568 for(
int isample=0;isample<nctest[iclass];++isample){
570 for(
int iband=0;iband<nband;++iband){
571 assert(testPixels[iclass].size()>isample);
572 assert(testPixels[iclass][isample].size()>iband+startBand);
573 assert(offset.size()>iband);
574 assert(scale.size()>iband);
575 float value=testPixels[iclass][isample][iband+startBand];
577 trainingFeatures[iclass][nctraining[iclass]+isample].push_back((value-offset[iband])/scale[iband]);
580 assert(trainingFeatures[iclass].size()==nctraining[iclass]+nctest[iclass]);
583 assert(ccost_opt.size()>1);
584 if(ccost_opt.size()<3)
585 ccost_opt.push_back(sqrt(ccost_opt[0]*ccost_opt[1]));
586 assert(gamma_opt.size()>1);
587 if(gamma_opt.size()<3)
588 gamma_opt.push_back(sqrt(gamma_opt[0]*gamma_opt[1]));
589 assert(ccost_opt.size()==3);
590 assert(gamma_opt.size()==3);
591 assert(gamma_opt[0]<gamma_opt[1]);
592 assert(gamma_opt[0]<gamma_opt[2]);
593 assert(gamma_opt[2]<gamma_opt[1]);
594 assert(ccost_opt[0]<ccost_opt[1]);
595 assert(ccost_opt[0]<ccost_opt[2]);
596 assert(ccost_opt[2]<ccost_opt[1]);
598 std::vector<double> x(2);
607 const char* pszMessage;
608 void* pProgressArg=NULL;
609 GDALProgressFunc pfnProgress=GDALTermProgress;
612 pfnProgress(progress,pszMessage,pProgressArg);
613 double ncost=log(ccost_opt[1])/log(stepcc_opt[0])-log(ccost_opt[0])/log(stepcc_opt[0]);
614 double ngamma=log(gamma_opt[1])/log(stepg_opt[0])-log(gamma_opt[0])/log(stepg_opt[0]);
615 for(
double ccost=ccost_opt[0];ccost<=ccost_opt[1];ccost*=stepcc_opt[0]){
616 for(
double gamma=gamma_opt[0];gamma<=gamma_opt[1];gamma*=stepg_opt[0]){
619 std::vector<double> theGrad;
621 kappa=objFunction(x,theGrad,&trainingFeatures);
628 std::cout << ccost <<
" " << gamma <<
" " << kappa<< std::endl;
629 progress+=1.0/ncost/ngamma;
631 pfnProgress(progress,pszMessage,pProgressArg);
636 pfnProgress(progress,pszMessage,pProgressArg);
687 std::cout <<
" --ccost " << x[0];
688 std::cout <<
" --gamma " << x[1];
689 std::cout << std::endl;