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 "algorithms/svm.h"
120 enum SVM_TYPE {C_SVC=0, nu_SVC=1,one_class=2, epsilon_SVR=3, nu_SVR=4};
121 enum KERNEL_TYPE {linear=0,polynomial=1,radial=2,sigmoid=3};
124 #define Malloc(type,n) (type *)malloc((n)*sizeof(type))
128 int main(
int argc,
char *argv[])
130 vector<double> priors;
134 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)");
136 Optionpk<string> label_opt(
"label",
"label",
"Attribute name for class label in training vector file.",
"label");
137 Optionpk<unsigned int> balance_opt(
"bal",
"balance",
"Balance the input data to this number of samples for each class", 0);
138 Optionpk<bool> random_opt(
"random",
"random",
"Randomize training data for balancing and bagging",
true, 2);
139 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);
140 Optionpk<short> band_opt(
"b",
"band",
"Band index (starting from 0, either use band option or use start to end)");
142 Optionpk<double> bend_opt(
"e",
"end",
"End band sequence number (set to 0 to include all bands)", 0);
143 Optionpk<double> offset_opt(
"\0",
"offset",
"Offset value for each spectral band input features: refl[band]=(DN[band]-offset[band])/scale[band]", 0.0);
144 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);
145 Optionpk<double> priors_opt(
"prior",
"prior",
"Prior probabilities for each class (e.g., -p 0.3 -p 0.3 -p 0.2 ). Used for input only (ignored for cross validation)", 0.0);
146 Optionpk<string> priorimg_opt(
"pim",
"priorimg",
"Prior probability image (multi-band img with band for each class",
"",2);
148 Optionpk<string> cmformat_opt(
"cmf",
"cmf",
"Format for confusion matrix (ascii or latex)",
"ascii");
149 Optionpk<std::string> svm_type_opt(
"svmt",
"svmtype",
"Type of SVM (C_SVC, nu_SVC,one_class, epsilon_SVR, nu_SVR)",
"C_SVC");
150 Optionpk<std::string> kernel_type_opt(
"kt",
"kerneltype",
"Type of kernel function (linear,polynomial,radial,sigmoid) ",
"radial");
152 Optionpk<float> gamma_opt(
"g",
"gamma",
"Gamma in kernel function",1.0);
153 Optionpk<float> coef0_opt(
"c0",
"coef0",
"Coef0 in kernel function",0);
154 Optionpk<float> ccost_opt(
"cc",
"ccost",
"The parameter C of C_SVC, epsilon_SVR, and nu_SVR",1000);
155 Optionpk<float> nu_opt(
"nu",
"nu",
"The parameter nu of nu_SVC, one_class SVM, and nu_SVR",0.5);
156 Optionpk<float> epsilon_loss_opt(
"eloss",
"eloss",
"The epsilon in loss function of epsilon_SVR",0.1);
157 Optionpk<int> cache_opt(
"cache",
"cache",
"Cache memory size in MB",100);
158 Optionpk<float> epsilon_tol_opt(
"etol",
"etol",
"The tolerance of termination criterion",0.001);
159 Optionpk<bool> shrinking_opt(
"shrink",
"shrink",
"Whether to use the shrinking heuristics",
false);
160 Optionpk<bool> prob_est_opt(
"pe",
"probest",
"Whether to train a SVC or SVR model for probability estimates",
true,2);
162 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.",0);
164 Optionpk<int> bagSize_opt(
"bagsize",
"bagsize",
"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);
165 Optionpk<string> classBag_opt(
"cb",
"classbag",
"Output for each individual bootstrap aggregation");
166 Optionpk<string> mask_opt(
"m",
"mask",
"Use the first band of the specified file as a validity mask. Nodata values can be set with the option msknodata.");
167 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.", 0);
170 Optionpk<string> oformat_opt(
"of",
"oformat",
"Output image format (see also gdal_translate). Empty string: inherit from input image");
171 Optionpk<string> option_opt(
"co",
"co",
"Creation option for output file. Multiple options can be specified.");
172 Optionpk<string> colorTable_opt(
"ct",
"ct",
"Color table in ASCII format having 5 columns: id R G B ALFA (0: transparent, 255: solid)");
174 Optionpk<string> entropy_opt(
"entropy",
"entropy",
"Entropy image (measure for uncertainty of classifier output",
"",2);
175 Optionpk<string> active_opt(
"active",
"active",
"Ogr output for active training sample.",
"",2);
176 Optionpk<string> ogrformat_opt(
"f",
"f",
"Output ogr format for active training sample",
"SQLite");
179 Optionpk<short> classvalue_opt(
"r",
"reclass",
"List of class values (use same order as in class opt).");
180 Optionpk<string> extent_opt(
"extent",
"extent",
"get boundary to classify from extent from polygons in vector file");
184 bstart_opt.setHide(1);
186 balance_opt.setHide(1);
187 minSize_opt.setHide(1);
189 bagSize_opt.setHide(1);
191 classBag_opt.setHide(1);
193 priorimg_opt.setHide(1);
194 offset_opt.setHide(1);
195 scale_opt.setHide(1);
196 svm_type_opt.setHide(1);
197 kernel_type_opt.setHide(1);
198 kernel_degree_opt.setHide(1);
199 coef0_opt.setHide(1);
201 epsilon_loss_opt.setHide(1);
202 cache_opt.setHide(1);
203 epsilon_tol_opt.setHide(1);
204 shrinking_opt.setHide(1);
205 prob_est_opt.setHide(1);
206 entropy_opt.setHide(1);
207 active_opt.setHide(1);
208 nactive_opt.setHide(1);
209 random_opt.setHide(1);
210 extent_opt.setHide(1);
212 verbose_opt.setHide(2);
216 doProcess=training_opt.retrieveOption(argc,argv);
217 input_opt.retrieveOption(argc,argv);
218 output_opt.retrieveOption(argc,argv);
219 cv_opt.retrieveOption(argc,argv);
220 cmformat_opt.retrieveOption(argc,argv);
221 tlayer_opt.retrieveOption(argc,argv);
222 classname_opt.retrieveOption(argc,argv);
223 classvalue_opt.retrieveOption(argc,argv);
224 oformat_opt.retrieveOption(argc,argv);
225 ogrformat_opt.retrieveOption(argc,argv);
226 option_opt.retrieveOption(argc,argv);
227 colorTable_opt.retrieveOption(argc,argv);
228 label_opt.retrieveOption(argc,argv);
229 priors_opt.retrieveOption(argc,argv);
230 gamma_opt.retrieveOption(argc,argv);
231 ccost_opt.retrieveOption(argc,argv);
232 mask_opt.retrieveOption(argc,argv);
233 msknodata_opt.retrieveOption(argc,argv);
234 nodata_opt.retrieveOption(argc,argv);
236 band_opt.retrieveOption(argc,argv);
237 bstart_opt.retrieveOption(argc,argv);
238 bend_opt.retrieveOption(argc,argv);
239 balance_opt.retrieveOption(argc,argv);
240 minSize_opt.retrieveOption(argc,argv);
241 bag_opt.retrieveOption(argc,argv);
242 bagSize_opt.retrieveOption(argc,argv);
243 comb_opt.retrieveOption(argc,argv);
244 classBag_opt.retrieveOption(argc,argv);
245 prob_opt.retrieveOption(argc,argv);
246 priorimg_opt.retrieveOption(argc,argv);
247 offset_opt.retrieveOption(argc,argv);
248 scale_opt.retrieveOption(argc,argv);
249 svm_type_opt.retrieveOption(argc,argv);
250 kernel_type_opt.retrieveOption(argc,argv);
251 kernel_degree_opt.retrieveOption(argc,argv);
252 coef0_opt.retrieveOption(argc,argv);
253 nu_opt.retrieveOption(argc,argv);
254 epsilon_loss_opt.retrieveOption(argc,argv);
255 cache_opt.retrieveOption(argc,argv);
256 epsilon_tol_opt.retrieveOption(argc,argv);
257 shrinking_opt.retrieveOption(argc,argv);
258 prob_est_opt.retrieveOption(argc,argv);
259 entropy_opt.retrieveOption(argc,argv);
260 active_opt.retrieveOption(argc,argv);
261 nactive_opt.retrieveOption(argc,argv);
262 verbose_opt.retrieveOption(argc,argv);
263 random_opt.retrieveOption(argc,argv);
264 extent_opt.retrieveOption(argc,argv);
266 catch(
string predefinedString){
267 std::cout << predefinedString << std::endl;
272 cout <<
"Usage: pksvm -t training [-i input -o output] [-cv value]" << endl;
274 std::cout <<
"short option -h shows basic options only, use long option --help to show all options" << std::endl;
278 if(entropy_opt[0]==
"")
280 if(active_opt[0]==
"")
282 if(priorimg_opt[0]==
"")
283 priorimg_opt.clear();
286 std::map<std::string, svm::SVM_TYPE> svmMap;
288 svmMap[
"C_SVC"]=svm::C_SVC;
289 svmMap[
"nu_SVC"]=svm::nu_SVC;
290 svmMap[
"one_class"]=svm::one_class;
291 svmMap[
"epsilon_SVR"]=svm::epsilon_SVR;
292 svmMap[
"nu_SVR"]=svm::nu_SVR;
294 std::map<std::string, svm::KERNEL_TYPE> kernelMap;
296 kernelMap[
"linear"]=svm::linear;
297 kernelMap[
"polynomial"]=svm::polynomial;
298 kernelMap[
"radial"]=svm::radial;
299 kernelMap[
"sigmoid;"]=svm::sigmoid;
301 assert(training_opt.size());
303 if(verbose_opt[0]>=1){
305 std::cout <<
"input filename: " << input_opt[0] << std::endl;
307 std::cout <<
"mask filename: " << mask_opt[0] << std::endl;
308 std::cout <<
"training vector file: " << std::endl;
309 for(
int ifile=0;ifile<training_opt.size();++ifile)
310 std::cout << training_opt[ifile] << std::endl;
311 std::cout <<
"verbose: " << verbose_opt[0] << std::endl;
313 unsigned short nbag=(training_opt.size()>1)?training_opt.size():bag_opt[0];
314 if(verbose_opt[0]>=1)
315 std::cout <<
"number of bootstrap aggregations: " << nbag << std::endl;
319 OGRFeature *readFeature;
326 if(extent_opt.size()){
327 extentReader.open(extent_opt[0]);
328 readLayer = extentReader.getDataSource()->GetLayer(0);
329 if(!(extentReader.getExtent(ulx,uly,lrx,lry))){
330 cerr <<
"Error: could not get extent from " << extent_opt[0] << endl;
336 if(active_opt.size()){
337 prob_est_opt[0]=
true;
339 activeWriter.open(active_opt[0],ogrformat_opt[0]);
340 activeWriter.createLayer(active_opt[0],trainingReader.getProjection(),wkbPoint,NULL);
341 activeWriter.copyFields(trainingReader);
343 vector<PosValue> activePoints(nactive_opt[0]);
344 for(
int iactive=0;iactive<activePoints.size();++iactive){
345 activePoints[iactive].value=1.0;
346 activePoints[iactive].posx=0.0;
347 activePoints[iactive].posy=0.0;
350 unsigned int totalSamples=0;
351 unsigned int nactive=0;
352 vector<struct svm_model*> svm(nbag);
353 vector<struct svm_parameter> param(nbag);
360 if(priors_opt.size()>1){
361 priors.resize(priors_opt.size());
363 for(
short iclass=0;iclass<priors_opt.size();++iclass){
364 priors[iclass]=priors_opt[iclass];
365 normPrior+=priors[iclass];
368 for(
short iclass=0;iclass<priors_opt.size();++iclass)
369 priors[iclass]/=normPrior;
374 std::sort(band_opt.begin(),band_opt.end());
376 map<string,short> classValueMap;
377 vector<std::string> nameVector;
378 if(classname_opt.size()){
379 assert(classname_opt.size()==classvalue_opt.size());
380 for(
int iclass=0;iclass<classname_opt.size();++iclass)
381 classValueMap[classname_opt[iclass]]=classvalue_opt[iclass];
386 vector< vector<double> > offset(nbag);
387 vector< vector<double> > scale(nbag);
388 map<string,Vector2d<float> > trainingMap;
389 vector< Vector2d<float> > trainingPixels;
390 vector<string> fields;
392 vector<struct svm_problem> prob(nbag);
393 vector<struct svm_node *> x_space(nbag);
395 for(
int ibag=0;ibag<nbag;++ibag){
397 if(ibag<training_opt.size()){
399 trainingPixels.clear();
400 if(verbose_opt[0]>=1)
401 std::cout <<
"reading imageVector file " << training_opt[0] << std::endl;
405 totalSamples=trainingReaderBag.readDataImageOgr(trainingMap,fields,band_opt,label_opt[0],tlayer_opt,verbose_opt[0]);
407 totalSamples=trainingReaderBag.readDataImageOgr(trainingMap,fields,bstart_opt[0],bend_opt[0],label_opt[0],tlayer_opt,verbose_opt[0]);
408 if(trainingMap.size()<2){
409 string errorstring=
"Error: could not read at least two classes from training file, did you provide class labels in training sample (see option label)?";
412 trainingReaderBag.close();
415 cerr << error << std::endl;
418 catch(std::exception& e){
419 std::cerr <<
"Error: ";
420 std::cerr << e.what() << std::endl;
421 std::cerr << CPLGetLastErrorMsg() << std::endl;
425 cerr <<
"error catched" << std::endl;
432 std::cout <<
"training pixels: " << std::endl;
433 map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
434 while(mapit!=trainingMap.end()){
436 if((mapit->second).size()<minSize_opt[0]){
437 trainingMap.erase(mapit);
440 trainingPixels.push_back(mapit->second);
442 std::cout << mapit->first <<
": " << (mapit->second).size() <<
" samples" << std::endl;
446 nclass=trainingPixels.size();
447 if(classname_opt.size())
448 assert(nclass==classname_opt.size());
449 nband=trainingPixels[0][0].size()-2;
452 assert(nclass==trainingPixels.size());
453 assert(nband==trainingPixels[0][0].size()-2);
458 if(balance_opt[0]>0){
459 while(balance_opt.size()<nclass)
460 balance_opt.push_back(balance_opt.back());
464 for(
short iclass=0;iclass<nclass;++iclass){
465 if(trainingPixels[iclass].size()>balance_opt[iclass]){
466 while(trainingPixels[iclass].size()>balance_opt[iclass]){
467 int index=rand()%trainingPixels[iclass].size();
468 trainingPixels[iclass].erase(trainingPixels[iclass].begin()+index);
472 int oldsize=trainingPixels[iclass].size();
473 for(
int isample=trainingPixels[iclass].size();isample<balance_opt[iclass];++isample){
474 int index = rand()%oldsize;
475 trainingPixels[iclass].push_back(trainingPixels[iclass][index]);
478 totalSamples+=trainingPixels[iclass].size();
483 offset[ibag].resize(nband);
484 scale[ibag].resize(nband);
485 if(offset_opt.size()>1)
486 assert(offset_opt.size()==nband);
487 if(scale_opt.size()>1)
488 assert(scale_opt.size()==nband);
489 for(
int iband=0;iband<nband;++iband){
490 if(verbose_opt[0]>=1)
491 std::cout <<
"scaling for band" << iband << std::endl;
492 offset[ibag][iband]=(offset_opt.size()==1)?offset_opt[0]:offset_opt[iband];
493 scale[ibag][iband]=(scale_opt.size()==1)?scale_opt[0]:scale_opt[iband];
495 if(scale[ibag][iband]<=0){
496 float theMin=trainingPixels[0][0][iband+startBand];
497 float theMax=trainingPixels[0][0][iband+startBand];
498 for(
short iclass=0;iclass<nclass;++iclass){
499 for(
int isample=0;isample<trainingPixels[iclass].size();++isample){
500 if(theMin>trainingPixels[iclass][isample][iband+startBand])
501 theMin=trainingPixels[iclass][isample][iband+startBand];
502 if(theMax<trainingPixels[iclass][isample][iband+startBand])
503 theMax=trainingPixels[iclass][isample][iband+startBand];
506 offset[ibag][iband]=theMin+(theMax-theMin)/2.0;
507 scale[ibag][iband]=(theMax-theMin)/2.0;
508 if(verbose_opt[0]>=1){
509 std::cout <<
"Extreme image values for band " << iband <<
": [" << theMin <<
"," << theMax <<
"]" << std::endl;
510 std::cout <<
"Using offset, scale: " << offset[ibag][iband] <<
", " << scale[ibag][iband] << std::endl;
511 std::cout <<
"scaled values for band " << iband <<
": [" << (theMin-offset[ibag][iband])/scale[ibag][iband] <<
"," << (theMax-offset[ibag][iband])/scale[ibag][iband] <<
"]" << std::endl;
517 offset[ibag].resize(nband);
518 scale[ibag].resize(nband);
519 for(
int iband=0;iband<nband;++iband){
520 offset[ibag][iband]=offset[0][iband];
521 scale[ibag][iband]=scale[0][iband];
526 if(priors_opt.size()==1){
527 priors.resize(nclass);
528 for(
short iclass=0;iclass<nclass;++iclass)
529 priors[iclass]=1.0/nclass;
531 assert(priors_opt.size()==1||priors_opt.size()==nclass);
534 while(bagSize_opt.size()<nclass)
535 bagSize_opt.push_back(bagSize_opt.back());
537 if(verbose_opt[0]>=1){
538 std::cout <<
"number of bands: " << nband << std::endl;
539 std::cout <<
"number of classes: " << nclass << std::endl;
540 if(priorimg_opt.empty()){
541 std::cout <<
"priors:";
542 for(
short iclass=0;iclass<nclass;++iclass)
543 std::cout <<
" " << priors[iclass];
544 std::cout << std::endl;
547 map<string,Vector2d<float> >::iterator mapit=trainingMap.begin();
550 while(mapit!=trainingMap.end()){
551 nameVector.push_back(mapit->first);
552 if(classValueMap.size()){
554 if(classValueMap[mapit->first]>0){
555 if(cm.getClassIndex(type2string<short>(classValueMap[mapit->first]))<0){
556 cm.pushBackClassName(type2string<short>(classValueMap[mapit->first]),doSort);
560 std::cerr <<
"Error: names in classname option are not complete, please check names in training vector and make sure classvalue is > 0" << std::endl;
565 cm.pushBackClassName(mapit->first,doSort);
570 std::cerr <<
"Error: did you provide class pairs names (-c) and integer values (-r) for each class in training vector?" << std::endl;
573 if(classname_opt.empty()){
575 for(
int iclass=0;iclass<nclass;++iclass){
577 std::cout << iclass <<
" " << cm.getClass(iclass) <<
" -> " << string2type<short>(cm.getClass(iclass)) << std::endl;
578 classValueMap[cm.getClass(iclass)]=string2type<short>(cm.getClass(iclass));
590 vector< Vector2d<float> > trainingFeatures(nclass);
591 for(
short iclass=0;iclass<nclass;++iclass){
593 if(verbose_opt[0]>=1)
594 std::cout <<
"calculating features for class " << iclass << std::endl;
597 nctraining=(bagSize_opt[iclass]<100)? trainingPixels[iclass].size()/100.0*bagSize_opt[iclass] : trainingPixels[iclass].size();
600 assert(nctraining<=trainingPixels[iclass].size());
602 if(bagSize_opt[iclass]<100)
603 random_shuffle(trainingPixels[iclass].begin(),trainingPixels[iclass].end());
605 std::cout <<
"nctraining (class " << iclass <<
"): " << nctraining << std::endl;
606 trainingFeatures[iclass].resize(nctraining);
607 for(
int isample=0;isample<nctraining;++isample){
609 for(
int iband=0;iband<nband;++iband){
610 float value=trainingPixels[iclass][isample][iband+startBand];
611 trainingFeatures[iclass][isample].push_back((value-offset[ibag][iband])/scale[ibag][iband]);
614 assert(trainingFeatures[iclass].size()==nctraining);
617 unsigned int nFeatures=trainingFeatures[0][0].size();
618 if(verbose_opt[0]>=1)
619 std::cout <<
"number of features: " << nFeatures << std::endl;
620 unsigned int ntraining=0;
621 for(
short iclass=0;iclass<nclass;++iclass)
622 ntraining+=trainingFeatures[iclass].size();
623 if(verbose_opt[0]>=1)
624 std::cout <<
"training size over all classes: " << ntraining << std::endl;
626 prob[ibag].l=ntraining;
627 prob[ibag].y = Malloc(
double,prob[ibag].l);
628 prob[ibag].x = Malloc(
struct svm_node *,prob[ibag].l);
629 x_space[ibag] = Malloc(
struct svm_node,(nFeatures+1)*ntraining);
630 unsigned long int spaceIndex=0;
632 for(
short iclass=0;iclass<nclass;++iclass){
633 for(
int isample=0;isample<trainingFeatures[iclass].size();++isample){
634 prob[ibag].x[lIndex]=&(x_space[ibag][spaceIndex]);
635 for(
int ifeature=0;ifeature<nFeatures;++ifeature){
636 x_space[ibag][spaceIndex].index=ifeature+1;
637 x_space[ibag][spaceIndex].value=trainingFeatures[iclass][isample][ifeature];
640 x_space[ibag][spaceIndex++].index=-1;
641 prob[ibag].y[lIndex]=iclass;
645 assert(lIndex==prob[ibag].l);
648 param[ibag].svm_type = svmMap[svm_type_opt[0]];
649 param[ibag].kernel_type = kernelMap[kernel_type_opt[0]];
650 param[ibag].degree = kernel_degree_opt[0];
651 param[ibag].gamma = (gamma_opt[0]>0)? gamma_opt[0] : 1.0/nFeatures;
652 param[ibag].coef0 = coef0_opt[0];
653 param[ibag].nu = nu_opt[0];
654 param[ibag].cache_size = cache_opt[0];
655 param[ibag].C = ccost_opt[0];
656 param[ibag].eps = epsilon_tol_opt[0];
657 param[ibag].p = epsilon_loss_opt[0];
658 param[ibag].shrinking = (shrinking_opt[0])? 1 : 0;
659 param[ibag].probability = (prob_est_opt[0])? 1 : 0;
660 param[ibag].nr_weight = 0;
661 param[ibag].weight_label = NULL;
662 param[ibag].weight = NULL;
663 param[ibag].verbose=(verbose_opt[0]>1)?
true:
false;
666 std::cout <<
"checking parameters" << std::endl;
667 svm_check_parameter(&prob[ibag],¶m[ibag]);
669 std::cout <<
"parameters ok, training" << std::endl;
670 svm[ibag]=svm_train(&prob[ibag],¶m[ibag]);
672 std::cout <<
"SVM is now trained" << std::endl;
675 std::cout <<
"Cross validating" << std::endl;
676 double *target = Malloc(
double,prob[ibag].l);
677 svm_cross_validation(&prob[ibag],¶m[ibag],cv_opt[0],target);
678 assert(param[ibag].svm_type != EPSILON_SVR&¶m[ibag].svm_type != NU_SVR);
680 for(
int i=0;i<prob[ibag].l;i++){
681 string refClassName=nameVector[prob[ibag].y[i]];
682 string className=nameVector[target[i]];
683 if(classValueMap.size())
684 cm.incrementResult(type2string<short>(classValueMap[refClassName]),type2string<short>(classValueMap[className]),1.0/nbag);
686 cm.incrementResult(cm.getClass(prob[ibag].y[i]),cm.getClass(target[i]),1.0/nbag);
695 assert(cm.nReference());
696 cm.setFormat(cmformat_opt[0]);
697 cm.reportSE95(
false);
698 std::cout << cm << std::endl;
717 if(input_opt.empty())
720 const char* pszMessage;
721 void* pProgressArg=NULL;
722 GDALProgressFunc pfnProgress=GDALTermProgress;
725 pfnProgress(progress,pszMessage,pProgressArg);
727 bool inputIsRaster=
false;
730 imgReaderOgr.open(input_opt[0]);
731 imgReaderOgr.close();
733 catch(
string errorString){
739 if(verbose_opt[0]>=1)
740 std::cout <<
"opening image " << input_opt[0] << std::endl;
741 testImage.open(input_opt[0]);
744 cerr << error << std::endl;
748 if(priorimg_opt.size()){
750 if(verbose_opt[0]>=1)
751 std::cout <<
"opening prior image " << priorimg_opt[0] << std::endl;
752 priorReader.open(priorimg_opt[0]);
753 assert(priorReader.nrOfCol()==testImage.nrOfCol());
754 assert(priorReader.nrOfRow()==testImage.nrOfRow());
757 cerr << error << std::endl;
761 cerr <<
"error catched" << std::endl;
766 int nrow=testImage.nrOfRow();
767 int ncol=testImage.nrOfCol();
768 if(option_opt.findSubstring(
"INTERLEAVE=")==option_opt.end()){
769 string theInterleave=
"INTERLEAVE=";
770 theInterleave+=testImage.getInterleave();
771 option_opt.push_back(theInterleave);
773 vector<char> classOut(ncol);
781 string imageType=testImage.getImageType();
782 if(oformat_opt.size())
783 imageType=oformat_opt[0];
785 assert(output_opt.size());
786 if(verbose_opt[0]>=1)
787 std::cout <<
"opening class image for writing output " << output_opt[0] << std::endl;
788 if(classBag_opt.size()){
789 classImageBag.open(classBag_opt[0],ncol,nrow,nbag,GDT_Byte,imageType,option_opt);
790 classImageBag.GDALSetNoDataValue(nodata_opt[0]);
791 classImageBag.copyGeoTransform(testImage);
792 classImageBag.setProjection(testImage.getProjection());
794 classImageOut.open(output_opt[0],ncol,nrow,1,GDT_Byte,imageType,option_opt);
795 classImageOut.GDALSetNoDataValue(nodata_opt[0]);
796 classImageOut.copyGeoTransform(testImage);
797 classImageOut.setProjection(testImage.getProjection());
798 if(colorTable_opt.size())
799 classImageOut.setColorTable(colorTable_opt[0],0);
801 probImage.open(prob_opt[0],ncol,nrow,nclass,GDT_Byte,imageType,option_opt);
802 probImage.GDALSetNoDataValue(nodata_opt[0]);
803 probImage.copyGeoTransform(testImage);
804 probImage.setProjection(testImage.getProjection());
806 if(entropy_opt.size()){
807 entropyImage.open(entropy_opt[0],ncol,nrow,1,GDT_Byte,imageType,option_opt);
808 entropyImage.GDALSetNoDataValue(nodata_opt[0]);
809 entropyImage.copyGeoTransform(testImage);
810 entropyImage.setProjection(testImage.getProjection());
814 cerr << error << std::endl;
818 if(extent_opt.size()){
820 maskWriter.open(
"/vsimem/mask.tif",ncol,nrow,1,GDT_Float32,imageType,option_opt);
821 maskWriter.GDALSetNoDataValue(nodata_opt[0]);
822 maskWriter.copyGeoTransform(testImage);
823 maskWriter.setProjection(testImage.getProjection());
824 vector<double> burnValues(1,1);
825 maskWriter.rasterizeOgr(extentReader,burnValues);
829 cerr << error << std::endl;
833 cerr <<
"error catched" << std::endl;
837 mask_opt.push_back(
"/vsimem/mask.tif");
842 if(verbose_opt[0]>=1)
843 std::cout <<
"opening mask image file " << mask_opt[0] << std::endl;
844 maskReader.open(mask_opt[0]);
847 cerr << error << std::endl;
851 cerr <<
"error catched" << std::endl;
856 for(
int iline=0;iline<nrow;++iline){
857 vector<float> buffer(ncol);
858 vector<short> lineMask;
860 if(priorimg_opt.size())
861 linePrior.resize(nclass,ncol);
864 vector<float> entropy(ncol);
866 if(classBag_opt.size())
867 classBag.resize(nbag,ncol);
870 for(
int iband=0;iband<band_opt.size();++iband){
871 if(verbose_opt[0]==2)
872 std::cout <<
"reading band " << band_opt[iband] << std::endl;
873 assert(band_opt[iband]>=0);
874 assert(band_opt[iband]<testImage.nrOfBand());
875 testImage.readData(buffer,GDT_Float32,iline,band_opt[iband]);
876 for(
int icol=0;icol<ncol;++icol)
877 hpixel[icol].push_back(buffer[icol]);
881 for(
int iband=bstart_opt[0];iband<bstart_opt[0]+nband;++iband){
882 if(verbose_opt[0]==2)
883 std::cout <<
"reading band " << iband << std::endl;
885 assert(iband<testImage.nrOfBand());
886 testImage.readData(buffer,GDT_Float32,iline,iband);
887 for(
int icol=0;icol<ncol;++icol)
888 hpixel[icol].push_back(buffer[icol]);
892 catch(
string theError){
893 cerr <<
"Error reading " << input_opt[0] <<
": " << theError << std::endl;
897 cerr <<
"error catched" << std::endl;
900 assert(nband==hpixel[0].size());
902 std::cout <<
"used bands: " << nband << std::endl;
904 if(priorimg_opt.size()){
906 for(
short iclass=0;iclass<nclass;++iclass){
907 if(verbose_opt.size()>1)
908 std::cout <<
"Reading " << priorimg_opt[0] <<
" band " << iclass <<
" line " << iline << std::endl;
909 priorReader.readData(linePrior[iclass],GDT_Float32,iline,iclass);
912 catch(
string theError){
913 std::cerr <<
"Error reading " << priorimg_opt[0] <<
": " << theError << std::endl;
917 cerr <<
"error catched" << std::endl;
921 double oldRowMask=-1;
923 for(
int icol=0;icol<ncol;++icol){
924 assert(hpixel[icol].size()==nband);
925 bool doClassify=
true;
929 if(extent_opt.size()){
931 testImage.image2geo(icol,iline,geox,geoy);
933 if(uly>=geoy&&lry<=geoy&&ulx<=geox&&lrx>=geox){
957 testImage.image2geo(icol,iline,geox,geoy);
958 maskReader.geo2image(geox,geoy,colMask,rowMask);
959 colMask=
static_cast<int>(colMask);
960 rowMask=
static_cast<int>(rowMask);
961 if(rowMask>=0&&rowMask<maskReader.nrOfRow()&&colMask>=0&&colMask<maskReader.nrOfCol()){
962 if(static_cast<int>(rowMask)!=
static_cast<int>(oldRowMask)){
963 assert(rowMask>=0&&rowMask<maskReader.nrOfRow());
966 maskReader.readData(lineMask,GDT_Int16,static_cast<int>(rowMask));
968 catch(
string errorstring){
969 cerr << errorstring << endl;
973 cerr <<
"error catched" << std::endl;
979 for(
short ivalue=0;ivalue<msknodata_opt.size();++ivalue){
980 if(msknodata_opt[ivalue]>=0){
981 if(lineMask[colMask]==msknodata_opt[ivalue]){
982 theMask=lineMask[colMask];
988 if(lineMask[colMask]!=-msknodata_opt[ivalue]){
989 theMask=lineMask[colMask];
999 if(classBag_opt.size())
1000 for(
int ibag=0;ibag<nbag;++ibag)
1001 classBag[ibag][icol]=theMask;
1002 classOut[icol]=theMask;
1007 for(
int iband=0;iband<hpixel[icol].size();++iband){
1008 if(hpixel[icol][iband]){
1016 for(
short iclass=0;iclass<nclass;++iclass)
1017 probOut[iclass][icol]=0;
1019 if(classBag_opt.size())
1020 for(
int ibag=0;ibag<nbag;++ibag)
1021 classBag[ibag][icol]=nodata_opt[0];
1022 classOut[icol]=nodata_opt[0];
1025 if(verbose_opt[0]>1)
1026 std::cout <<
"begin classification " << std::endl;
1028 for(
int ibag=0;ibag<nbag;++ibag){
1029 vector<double> result(nclass);
1032 for(
int iband=0;iband<nband;++iband){
1033 x[iband].index=iband+1;
1034 x[iband].value=(hpixel[icol][iband]-offset[ibag][iband])/scale[ibag][iband];
1037 double predict_label=0;
1038 vector<float> prValues(nclass);
1040 if(!prob_est_opt[0]){
1041 predict_label = svm_predict(svm[ibag],x);
1042 for(
short iclass=0;iclass<nclass;++iclass){
1043 if(iclass==static_cast<short>(predict_label))
1050 assert(svm_check_probability_model(svm[ibag]));
1051 predict_label = svm_predict_probability(svm[ibag],x,&(result[0]));
1054 if(classBag_opt.size()){
1057 classBag[ibag][icol]=0;
1060 if(priorimg_opt.size()){
1061 for(
short iclass=0;iclass<nclass;++iclass)
1062 normPrior+=linePrior[iclass][icol];
1064 for(
short iclass=0;iclass<nclass;++iclass){
1065 if(priorimg_opt.size())
1066 priors[iclass]=linePrior[iclass][icol]/normPrior;
1067 switch(comb_opt[0]){
1070 probOut[iclass][icol]+=result[iclass]*priors[iclass];
1073 probOut[iclass][icol]*=pow(static_cast<float>(priors[iclass]),static_cast<float>(1.0-nbag)/nbag)*result[iclass];
1076 if(priors[iclass]*result[iclass]>probOut[iclass][icol])
1077 probOut[iclass][icol]=priors[iclass]*result[iclass];
1080 if(classBag_opt.size()){
1086 if(result[iclass]>maxP){
1087 maxP=result[iclass];
1088 classBag[ibag][icol]=iclass;
1099 for(
short iclass=0;iclass<nclass;++iclass){
1100 if(probOut[iclass][icol]>maxBag1){
1101 maxBag1=probOut[iclass][icol];
1102 classOut[icol]=classValueMap[nameVector[iclass]];
1104 else if(probOut[iclass][icol]>maxBag2)
1105 maxBag2=probOut[iclass][icol];
1106 normBag+=probOut[iclass][icol];
1110 for(
short iclass=0;iclass<nclass;++iclass){
1111 float prv=probOut[iclass][icol];
1113 entropy[icol]-=prv*log(prv)/log(2.0);
1116 probOut[iclass][icol]=
static_cast<short>(prv+0.5);
1121 entropy[icol]/=log(static_cast<double>(nclass))/log(2.0);
1122 entropy[icol]=
static_cast<short>(100*entropy[icol]+0.5);
1123 if(active_opt.size()){
1124 if(entropy[icol]>activePoints.back().value){
1125 activePoints.back().value=entropy[icol];
1126 activePoints.back().posx=icol;
1127 activePoints.back().posy=iline;
1130 std::cout << activePoints.back().posx <<
" " << activePoints.back().posy <<
" " << activePoints.back().value << std::endl;
1135 if(classBag_opt.size())
1136 for(
int ibag=0;ibag<nbag;++ibag)
1137 classImageBag.writeData(classBag[ibag],GDT_Byte,iline,ibag);
1138 if(prob_opt.size()){
1139 for(
short iclass=0;iclass<nclass;++iclass)
1140 probImage.writeData(probOut[iclass],GDT_Float32,iline,iclass);
1142 if(entropy_opt.size()){
1143 entropyImage.writeData(entropy,GDT_Float32,iline);
1145 classImageOut.writeData(classOut,GDT_Byte,iline);
1146 if(!verbose_opt[0]){
1147 progress=
static_cast<float>(iline+1.0)/classImageOut.nrOfRow();
1148 pfnProgress(progress,pszMessage,pProgressArg);
1152 if(active_opt.size()){
1153 for(
int iactive=0;iactive<activePoints.size();++iactive){
1154 std::map<string,double> pointMap;
1155 for(
int iband=0;iband<testImage.nrOfBand();++iband){
1157 testImage.readData(value,GDT_Float64,static_cast<int>(activePoints[iactive].posx),static_cast<int>(activePoints[iactive].posy),iband);
1160 pointMap[fs.str()]=value;
1162 pointMap[label_opt[0]]=0;
1164 testImage.image2geo(activePoints[iactive].posx,activePoints[iactive].posy,x,y);
1165 std::string fieldname=
"id";
1166 activeWriter.addPoint(x,y,pointMap,fieldname,++nactive);
1173 if(priorimg_opt.size())
1174 priorReader.close();
1177 if(entropy_opt.size())
1178 entropyImage.close();
1179 if(classBag_opt.size())
1180 classImageBag.close();
1181 classImageOut.close();
1186 for(
int ivalidation=0;ivalidation<input_opt.size();++ivalidation){
1187 if(output_opt.size())
1188 assert(output_opt.size()==input_opt.size());
1190 std::cout <<
"opening img reader " << input_opt[ivalidation] << std::endl;
1191 imgReaderOgr.open(input_opt[ivalidation]);
1194 if(output_opt.size()){
1196 std::cout <<
"opening img writer and copying fields from img reader" << output_opt[ivalidation] << std::endl;
1197 imgWriterOgr.open(output_opt[ivalidation],imgReaderOgr);
1200 cout <<
"number of layers in input ogr file: " << imgReaderOgr.getLayerCount() << endl;
1201 for(
int ilayer=0;ilayer<imgReaderOgr.getLayerCount();++ilayer){
1203 cout <<
"processing input layer " << ilayer << endl;
1204 if(output_opt.size()){
1206 std::cout <<
"creating field class" << std::endl;
1207 if(classValueMap.size())
1208 imgWriterOgr.createField(
"class",OFTInteger,ilayer);
1210 imgWriterOgr.createField(
"class",OFTString,ilayer);
1212 unsigned int nFeatures=imgReaderOgr.getFeatureCount(ilayer);
1213 unsigned int ifeature=0;
1215 pfnProgress(progress,pszMessage,pProgressArg);
1216 OGRFeature *poFeature;
1217 while( (poFeature = imgReaderOgr.getLayer(ilayer)->GetNextFeature()) != NULL ){
1218 if(verbose_opt[0]>1)
1219 std::cout <<
"feature " << ifeature << std::endl;
1220 if( poFeature == NULL ){
1221 cout <<
"Warning: could not read feature " << ifeature <<
" in layer " << imgReaderOgr.getLayerName(ilayer) << endl;
1224 OGRFeature *poDstFeature = NULL;
1225 if(output_opt.size()){
1226 poDstFeature=imgWriterOgr.createFeature(ilayer);
1227 if( poDstFeature->SetFrom( poFeature, TRUE ) != OGRERR_NONE ){
1228 CPLError( CE_Failure, CPLE_AppDefined,
1229 "Unable to translate feature %d from layer %s.\n",
1230 poFeature->GetFID(), imgWriterOgr.getLayerName(ilayer).c_str() );
1231 OGRFeature::DestroyFeature( poFeature );
1232 OGRFeature::DestroyFeature( poDstFeature );
1235 vector<float> validationPixel;
1236 vector<float> validationFeature;
1238 imgReaderOgr.readData(validationPixel,OFTReal,fields,poFeature,ilayer);
1239 assert(validationPixel.size()==nband);
1240 vector<float> probOut(nclass);
1241 for(
short iclass=0;iclass<nclass;++iclass)
1243 for(
int ibag=0;ibag<nbag;++ibag){
1244 for(
int iband=0;iband<nband;++iband){
1245 validationFeature.push_back((validationPixel[iband]-offset[ibag][iband])/scale[ibag][iband]);
1246 if(verbose_opt[0]==2)
1247 std::cout <<
" " << validationFeature.back();
1249 if(verbose_opt[0]==2)
1250 std::cout << std::endl;
1251 vector<double> result(nclass);
1253 x = (
struct svm_node *) malloc((validationFeature.size()+1)*
sizeof(
struct svm_node));
1254 for(
int i=0;i<validationFeature.size();++i){
1256 x[i].value=validationFeature[i];
1259 x[validationFeature.size()].index=-1;
1260 double predict_label=0;
1261 if(!prob_est_opt[0]){
1262 predict_label = svm_predict(svm[ibag],x);
1263 for(
short iclass=0;iclass<nclass;++iclass){
1264 if(iclass==static_cast<short>(predict_label))
1271 assert(svm_check_probability_model(svm[ibag]));
1272 predict_label = svm_predict_probability(svm[ibag],x,&(result[0]));
1274 if(verbose_opt[0]>1){
1275 std::cout <<
"predict_label: " << predict_label << std::endl;
1276 for(
int iclass=0;iclass<result.size();++iclass)
1277 std::cout << result[iclass] <<
" ";
1278 std::cout << std::endl;
1282 for(
short iclass=0;iclass<nclass;++iclass){
1283 switch(comb_opt[0]){
1286 probOut[iclass]+=result[iclass]*priors[iclass];
1289 probOut[iclass]*=pow(static_cast<float>(priors[iclass]),static_cast<float>(1.0-nbag)/nbag)*result[iclass];
1292 if(priors[iclass]*result[iclass]>probOut[iclass])
1293 probOut[iclass]=priors[iclass]*result[iclass];
1303 string classOut=
"Unclassified";
1304 for(
short iclass=0;iclass<nclass;++iclass){
1305 if(verbose_opt[0]>1)
1306 std::cout << probOut[iclass] <<
" ";
1307 if(probOut[iclass]>maxBag){
1308 maxBag=probOut[iclass];
1309 classOut=nameVector[iclass];
1313 if(verbose_opt[0]>1){
1314 if(classValueMap.size())
1315 std::cout <<
"->" << classValueMap[classOut] << std::endl;
1317 std::cout <<
"->" << classOut << std::endl;
1319 if(output_opt.size()){
1320 if(classValueMap.size())
1321 poDstFeature->SetField(
"class",classValueMap[classOut]);
1323 poDstFeature->SetField(
"class",classOut.c_str());
1324 poDstFeature->SetFID( poFeature->GetFID() );
1326 int labelIndex=poFeature->GetFieldIndex(label_opt[0].c_str());
1328 string classRef=poFeature->GetFieldAsString(labelIndex);
1330 if(classValueMap.size())
1331 cm.incrementResult(type2string<short>(classValueMap[classRef]),type2string<short>(classValueMap[classOut]),1);
1333 cm.incrementResult(classRef,classOut,1);
1337 if(output_opt.size()){
1338 if(imgWriterOgr.createFeature(poDstFeature,ilayer) != OGRERR_NONE){
1339 CPLError( CE_Failure, CPLE_AppDefined,
1340 "Unable to translate feature %d from layer %s.\n",
1341 poFeature->GetFID(), imgWriterOgr.getLayerName(ilayer).c_str() );
1342 OGRFeature::DestroyFeature( poDstFeature );
1343 OGRFeature::DestroyFeature( poDstFeature );
1347 if(!verbose_opt[0]){
1348 progress=
static_cast<float>(ifeature+1.0)/nFeatures;
1349 pfnProgress(progress,pszMessage,pProgressArg);
1351 OGRFeature::DestroyFeature( poFeature );
1352 OGRFeature::DestroyFeature( poDstFeature );
1355 imgReaderOgr.close();
1356 if(output_opt.size())
1357 imgWriterOgr.close();
1359 if(cm.nReference()){
1360 std::cout << cm << std::endl;
1361 cout <<
"class #samples userAcc prodAcc" << endl;
1368 for(
short iclass=0;iclass<cm.nClasses();++iclass){
1369 dua=cm.ua_pct(cm.getClass(iclass),&se95_ua);
1370 dpa=cm.pa_pct(cm.getClass(iclass),&se95_pa);
1371 cout << cm.getClass(iclass) <<
" " << cm.nReference(cm.getClass(iclass)) <<
" " << dua <<
" (" << se95_ua <<
")" <<
" " << dpa <<
" (" << se95_pa <<
")" << endl;
1373 std::cout <<
"Kappa: " << cm.kappa() << std::endl;
1374 doa=cm.oa(&se95_oa);
1375 std::cout <<
"Overall Accuracy: " << 100*doa <<
" (" << 100*se95_oa <<
")" << std::endl;
1379 if(active_opt.size())
1380 activeWriter.close();
1381 if(extent_opt.size())
1382 extentReader.close();
1384 catch(
string errorString){
1385 std::cerr <<
"Error: errorString" << std::endl;
1388 for(
int ibag=0;ibag<nbag;++ibag){
1390 svm_destroy_param(¶m[ibag]);
1393 free(x_space[ibag]);
1394 svm_free_and_destroy_model(&(svm[ibag]));
throw this class when syntax error in command line option