pktools  2.6.4
Processing Kernel for geospatial data
pkdiff.cc
1 /**********************************************************************
2 pkdiff.cc: program to compare two raster image files
3 Copyright (C) 2008-2014 Pieter Kempeneers
4 
5 This file is part of pktools
6 
7 pktools is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 pktools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with pktools. If not, see <http://www.gnu.org/licenses/>.
19 ***********************************************************************/
20 #include <assert.h>
21 #include "imageclasses/ImgReaderGdal.h"
22 #include "imageclasses/ImgWriterGdal.h"
23 #include "imageclasses/ImgReaderOgr.h"
24 #include "imageclasses/ImgWriterOgr.h"
25 #include "base/Optionpk.h"
26 #include "algorithms/ConfusionMatrix.h"
27 
28 /******************************************************************************/
92 using namespace std;
93 
94 int main(int argc, char *argv[])
95 {
96  Optionpk<string> input_opt("i", "input", "Input raster dataset.");
97  Optionpk<string> reference_opt("ref", "reference", "Reference (raster or vector) dataset");
98  Optionpk<string> layer_opt("ln", "ln", "Layer name(s) in sample. Leave empty to select all (for vector reference datasets only)");
99  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.");
100  Optionpk<double> msknodata_opt("msknodata", "msknodata", "Mask value(s) where image is invalid. Use negative value for valid data (example: use -t -1: if only -1 is valid value)", 0);
101  Optionpk<double> nodata_opt("nodata", "nodata", "No data value(s) in input or reference dataset are ignored");
102  Optionpk<short> band_opt("b", "band", "Input (reference) raster band. Optionally, you can define different bands for input and reference bands respectively: -b 1 -b 0.", 0);
103  Optionpk<bool> rmse_opt("rmse", "rmse", "Report root mean squared error", false);
104  Optionpk<bool> regression_opt("reg", "reg", "Report linear regression (Input = c0+c1*Reference)", false);
105  Optionpk<bool> confusion_opt("cm", "confusion", "Create confusion matrix (to std out)", false);
106  Optionpk<string> cmformat_opt("cmf","cmf","Format for confusion matrix (ascii or latex)","ascii");
107  Optionpk<string> cmoutput_opt("cmo","cmo","Output file for confusion matrix");
108  Optionpk<bool> se95_opt("se95","se95","Report standard error for 95 confidence interval",false);
109  Optionpk<string> labelref_opt("lr", "lref", "Attribute name of the reference label (for vector reference datasets only)", "label");
110  Optionpk<string> classname_opt("c", "class", "List of class names.");
111  Optionpk<short> classvalue_opt("r", "reclass", "List of class values (use same order as in classname option).");
112  Optionpk<string> output_opt("o", "output", "Output dataset (optional)");
113  Optionpk<string> ogrformat_opt("f", "f", "OGR format for output vector (for vector reference datasets only)","SQLite");
114  Optionpk<string> labelclass_opt("lc", "lclass", "Attribute name of the classified label (for vector reference datasets only)", "class");
115  Optionpk<short> boundary_opt("bnd", "boundary", "Boundary for selecting the sample (for vector reference datasets only)", 1,1);
116  Optionpk<bool> homogeneous_opt("hom", "homogeneous", "Only take regions with homogeneous boundary into account (for reference datasets only)", false,1);
117  Optionpk<bool> disc_opt("circ", "circular", "Use circular boundary (for vector reference datasets only)", false,1);
118  Optionpk<string> colorTable_opt("ct", "ct", "Color table in ASCII format having 5 columns: id R G B ALFA (0: transparent, 255: solid).");
119  Optionpk<string> option_opt("co", "co", "Creation option for output file. Multiple options can be specified.");
120  Optionpk<short> valueE_opt("\0", "correct", "Value for correct pixels", 0,2);
121  Optionpk<short> valueO_opt("\0", "omission", "Value for omission errors: input label > reference label", 1,2);
122  Optionpk<short> valueC_opt("\0", "commission", "Value for commission errors: input label < reference label", 2,1);
123  Optionpk<short> verbose_opt("v", "verbose", "Verbose level", 0,2);
124 
125  output_opt.setHide(1);
126  ogrformat_opt.setHide(1);
127  labelclass_opt.setHide(1);
128  boundary_opt.setHide(1);
129  homogeneous_opt.setHide(1);
130  disc_opt.setHide(1);
131  colorTable_opt.setHide(1);
132  option_opt.setHide(1);
133 
134  bool doProcess;//stop process when program was invoked with help option (-h --help)
135  try{
136  doProcess=input_opt.retrieveOption(argc,argv);
137  reference_opt.retrieveOption(argc,argv);
138  layer_opt.retrieveOption(argc,argv);
139  band_opt.retrieveOption(argc,argv);
140  rmse_opt.retrieveOption(argc,argv);
141  regression_opt.retrieveOption(argc,argv);
142  confusion_opt.retrieveOption(argc,argv);
143  labelref_opt.retrieveOption(argc,argv);
144  classname_opt.retrieveOption(argc,argv);
145  classvalue_opt.retrieveOption(argc,argv);
146  nodata_opt.retrieveOption(argc,argv);
147  mask_opt.retrieveOption(argc,argv);
148  msknodata_opt.retrieveOption(argc,argv);
149  output_opt.retrieveOption(argc,argv);
150  ogrformat_opt.retrieveOption(argc,argv);
151  labelclass_opt.retrieveOption(argc,argv);
152  cmformat_opt.retrieveOption(argc,argv);
153  cmoutput_opt.retrieveOption(argc,argv);
154  se95_opt.retrieveOption(argc,argv);
155  boundary_opt.retrieveOption(argc,argv);
156  homogeneous_opt.retrieveOption(argc,argv);
157  disc_opt.retrieveOption(argc,argv);
158  colorTable_opt.retrieveOption(argc,argv);
159  option_opt.retrieveOption(argc,argv);
160  // class_opt.retrieveOption(argc,argv);
161  valueE_opt.retrieveOption(argc,argv);
162  valueO_opt.retrieveOption(argc,argv);
163  valueC_opt.retrieveOption(argc,argv);
164  verbose_opt.retrieveOption(argc,argv);
165  }
166  catch(string predefinedString){
167  std::cout << predefinedString << std::endl;
168  exit(0);
169  }
170  if(!doProcess){
171  cout << endl;
172  cout << "Usage: pkdiff -i input -ref reference" << endl;
173  cout << endl;
174  std::cout << "short option -h shows basic options only, use long option --help to show all options" << std::endl;
175  exit(0);//help was invoked, stop processing
176  }
177 
178  ImgReaderGdal inputReader;
179  ImgReaderGdal maskReader;
180 
181  if(verbose_opt[0]){
182  cout << "flag(s) set to";
183  for(int iflag=0;iflag<nodata_opt.size();++iflag)
184  cout << " " << nodata_opt[iflag];
185  cout << endl;
186  }
187 
188  if(input_opt.empty()){
189  std::cerr << "No input file provided (use option -i). Use --help for help information" << std::endl;
190  exit(0);
191  }
192  if(reference_opt.empty()){
193  std::cerr << "No reference file provided (use option -ref). Use --help for help information" << std::endl;
194  exit(0);
195  }
196 
197  //band_opt[0] is for input
198  //band_opt[1] is for reference
199  if(band_opt.size()<2)
200  band_opt.push_back(band_opt[0]);
201 
202  if(mask_opt.size())
203  while(mask_opt.size()<input_opt.size())
204  mask_opt.push_back(mask_opt[0]);
205  vector<short> inputRange;
206  vector<short> referenceRange;
208  int nclass=0;
209  map<string,short> classValueMap;
210  vector<std::string> nameVector(255);//the inverse of the classValueMap
211  vector<string> classNames;
212 
213  unsigned int ntotalValidation=0;
214  unsigned int nflagged=0;
215  Vector2d<int> resultClass;
216  vector<float> user;
217  vector<float> producer;
218  vector<unsigned int> nvalidation;
219 
220  if(confusion_opt[0]){
221  // if(class_opt.size()>1)
222  // inputRange=class_opt;
223  // if(classvalue_opt.size()>1)
224  // inputRange=classvalue_opt;
225  // else{
226  try{
227  if(verbose_opt[0])
228  cout << "opening input image file " << input_opt[0] << endl;
229  inputReader.open(input_opt[0]);//,imagicX_opt[0],imagicY_opt[0]);
230  }
231  catch(string error){
232  cerr << error << endl;
233  exit(1);
234  }
235  inputReader.getRange(inputRange,band_opt[0]);
236  inputReader.close();
237  // }
238 
239  for(int iflag=0;iflag<nodata_opt.size();++iflag){
240  vector<short>::iterator fit;
241  fit=find(inputRange.begin(),inputRange.end(),static_cast<short>(nodata_opt[iflag]));
242  if(fit!=inputRange.end())
243  inputRange.erase(fit);
244  }
245  nclass=inputRange.size();
246  if(verbose_opt[0]){
247  cout << "nclass (inputRange.size()): " << nclass << endl;
248  cout << "input range: " << endl;
249  }
250  if(classname_opt.size()){
251  assert(classname_opt.size()==classvalue_opt.size());
252  for(int iclass=0;iclass<classname_opt.size();++iclass){
253  classValueMap[classname_opt[iclass]]=classvalue_opt[iclass];
254  assert(classvalue_opt[iclass]<nameVector.size());
255  nameVector[classvalue_opt[iclass]]=classname_opt[iclass];
256  }
257  }
258  // nclass=classValueMap.size();
259  for(int rc=0;rc<inputRange.size();++rc){
260  classNames.push_back(type2string(inputRange[rc]));
261  if(verbose_opt[0])
262  cout << inputRange[rc] << endl;
263  }
264  cm.setClassNames(classNames);
265  if(verbose_opt[0]){
266  cout << "class names: " << endl;
267  for(int iclass=0;iclass<cm.nClasses();++iclass)
268  cout << iclass << " " << cm.getClass(iclass) << endl;
269  }
270  resultClass.resize(nclass,nclass);
271  user.resize(nclass);
272  producer.resize(nclass);
273  nvalidation.resize(nclass);
274  //initialize
275  for(int rc=0;rc<nclass;++rc){
276  for(int ic=0;ic<nclass;++ic)
277  resultClass[rc][ic]=0;
278  nvalidation[rc]=0;
279  }
280  }
281 
282  bool isDifferent=false;
283  bool refIsRaster=false;
284 
285  ImgReaderOgr referenceReaderOgr;
286  try{
287  referenceReaderOgr.open(reference_opt[0]);
288  referenceReaderOgr.close();
289  }
290  catch(string errorString){
291  refIsRaster=true;
292  }
293  const char* pszMessage;
294  void* pProgressArg=NULL;
295  GDALProgressFunc pfnProgress=GDALTermProgress;
296  float progress=0;
297  // if(reference_opt[0].find(".shp")!=string::npos){
298  if(!refIsRaster){
299  for(int iinput=0;iinput<input_opt.size();++iinput){
300  if(verbose_opt[0])
301  cout << "Processing input " << input_opt[iinput] << endl;
302  if(output_opt.size())
303  assert(reference_opt.size()==output_opt.size());
304  for(int iref=0;iref<reference_opt.size();++iref){
305  cout << "reference " << reference_opt[iref] << endl;
306  // assert(reference_opt[iref].find(".shp")!=string::npos);
307  try{
308  inputReader.open(input_opt[iinput]);//,imagicX_opt[0],imagicY_opt[0]);
309  if(mask_opt.size()){
310  maskReader.open(mask_opt[iinput]);
311  assert(inputReader.nrOfCol()==maskReader.nrOfCol());
312  assert(inputReader.nrOfRow()==maskReader.nrOfRow());
313  }
314  referenceReaderOgr.open(reference_opt[iref]);
315  }
316  catch(string error){
317  cerr << error << endl;
318  exit(1);
319  }
320  if(confusion_opt[0])
321  referenceRange=inputRange;
322 
323  ImgWriterOgr ogrWriter;
324  if(output_opt.size()){
325  try{
326  ogrWriter.open(output_opt[iref],ogrformat_opt[0]);
327  }
328  catch(string error){
329  cerr << error << endl;
330  exit(1);
331  }
332  }
333  int nlayer=referenceReaderOgr.getDataSource()->GetLayerCount();
334  for(int ilayer=0;ilayer<nlayer;++ilayer){
335  progress=0;
336  OGRLayer *readLayer=referenceReaderOgr.getLayer(ilayer);
337  // readLayer = referenceReaderOgr.getDataSource()->GetLayer(ilayer);
338  string currentLayername=readLayer->GetName();
339  if(layer_opt.size())
340  if(find(layer_opt.begin(),layer_opt.end(),currentLayername)==layer_opt.end())
341  continue;
342  if(!verbose_opt[0])
343  pfnProgress(progress,pszMessage,pProgressArg);
344  else
345  cout << "processing layer " << readLayer->GetName() << endl;
346 
347  readLayer->ResetReading();
348  OGRLayer *writeLayer;
349  if(output_opt.size()){
350  if(verbose_opt[0])
351  cout << "creating output vector file " << output_opt[0] << endl;
352  // assert(output_opt[0].find(".shp")!=string::npos);
353  char **papszOptions=NULL;
354  if(verbose_opt[0])
355  cout << "creating layer: " << readLayer->GetName() << endl;
356  // if(ogrWriter.createLayer(layername, referenceReaderOgr.getProjection(ilayer), referenceReaderOgr.getGeometryType(ilayer), papszOptions)==NULL)
357  writeLayer=ogrWriter.createLayer(readLayer->GetName(), referenceReaderOgr.getProjection(ilayer), wkbPoint, papszOptions);
358  assert(writeLayer);
359  if(verbose_opt[0]){
360  cout << "created layer" << endl;
361  cout << "copy fields from " << reference_opt[iref] << endl;
362  }
363  ogrWriter.copyFields(referenceReaderOgr,ilayer,ilayer);
364  //create extra field for classified label
365  short theDim=boundary_opt[0];
366  for(int windowJ=-theDim/2;windowJ<(theDim+1)/2;++windowJ){
367  for(int windowI=-theDim/2;windowI<(theDim+1)/2;++windowI){
368  if(disc_opt[0]&&(windowI*windowI+windowJ*windowJ>(theDim/2)*(theDim/2)))
369  continue;
370  ostringstream fs;
371  if(theDim>1)
372  fs << labelclass_opt[0] << "_" << windowJ << "_" << windowI;
373  else
374  fs << labelclass_opt[0];
375  if(verbose_opt[0])
376  cout << "creating field " << fs.str() << endl;
377  ogrWriter.createField(fs.str(),OFTInteger,ilayer);
378  }
379  }
380  }
381  OGRFeature *readFeature;
382  OGRFeature *writeFeature;
383  int isample=0;
384  unsigned int nfeatureInLayer=readLayer->GetFeatureCount();
385  unsigned int ifeature=0;
386  while( (readFeature = readLayer->GetNextFeature()) != NULL ){
387  if(verbose_opt[0])
388  cout << "sample " << ++isample << endl;
389  //get x and y from readFeature
390  double x,y;
391  OGRGeometry *poGeometry;
392  OGRPoint centroidPoint;
393  OGRPoint *poPoint;
394  poGeometry = readFeature->GetGeometryRef();
395  // assert( poGeometry != NULL && wkbFlatten(poGeometry->getGeometryType()) == wkbPoint );
396  if(poGeometry==NULL)
397  continue;
398  else if(wkbFlatten(poGeometry->getGeometryType()) == wkbMultiPolygon){
399  OGRMultiPolygon readPolygon = *((OGRMultiPolygon *) poGeometry);
400  readPolygon = *((OGRMultiPolygon *) poGeometry);
401  readPolygon.Centroid(&centroidPoint);
402  poPoint=&centroidPoint;
403  }
404  else if(wkbFlatten(poGeometry->getGeometryType()) == wkbPolygon){
405  OGRPolygon readPolygon=*((OGRPolygon *) poGeometry);
406  readPolygon.Centroid(&centroidPoint);
407  poPoint=&centroidPoint;
408  }
409  else if(wkbFlatten(poGeometry->getGeometryType()) == wkbPoint )
410  poPoint = (OGRPoint *) poGeometry;
411  else{
412  std::cerr << "Warning: skipping feature (not of type point or polygon)" << std::endl;
413  continue;
414  }
415  x=poPoint->getX();
416  y=poPoint->getY();
417  double inputValue;
418  vector<double> inputValues;
419  bool isHomogeneous=true;
420  short maskValue;
421  short outputValue;
422  //read referenceValue from feature
423  unsigned short referenceValue;
424  string referenceClassName;
425  if(classValueMap.size()){
426  referenceClassName=readFeature->GetFieldAsString(readFeature->GetFieldIndex(labelref_opt[0].c_str()));
427  referenceValue=classValueMap[referenceClassName];
428  }
429  else
430  referenceValue=readFeature->GetFieldAsInteger(readFeature->GetFieldIndex(labelref_opt[0].c_str()));
431  if(verbose_opt[0])
432  cout << "reference value: " << referenceValue << endl;
433 
434  bool pixelFlagged=false;
435  bool maskFlagged=false;
436  for(int iflag=0;iflag<nodata_opt.size();++iflag){
437  if(referenceValue==nodata_opt[iflag])
438  pixelFlagged=true;
439  }
440  if(pixelFlagged)
441  continue;
442  double i_centre,j_centre;
443  //input reader is georeferenced!
444  inputReader.geo2image(x,y,i_centre,j_centre);
445  // else{
446  // i_centre=x;
447  // j_centre=y;
448  // }
449  //nearest neighbour
450  j_centre=static_cast<int>(j_centre);
451  i_centre=static_cast<int>(i_centre);
452  //check if j_centre is out of bounds
453  if(static_cast<int>(j_centre)<0||static_cast<int>(j_centre)>=inputReader.nrOfRow())
454  continue;
455  //check if i_centre is out of bounds
456  if(static_cast<int>(i_centre)<0||static_cast<int>(i_centre)>=inputReader.nrOfCol())
457  continue;
458 
459  if(output_opt.size()){
460  writeFeature = OGRFeature::CreateFeature(writeLayer->GetLayerDefn());
461  assert(readFeature);
462  int nfield=readFeature->GetFieldCount();
463  writeFeature->SetGeometry(poPoint);
464  if(verbose_opt[0])
465  cout << "copying fields from " << reference_opt[0] << endl;
466  assert(readFeature);
467  assert(writeFeature);
468  vector<int> panMap(nfield);
469  vector<int>::iterator panit=panMap.begin();
470  for(int ifield=0;ifield<nfield;++ifield)
471  panMap[ifield]=ifield;
472  writeFeature->SetFieldsFrom(readFeature,&(panMap[0]));
473  // if(writeFeature->SetFrom(readFeature)!= OGRERR_NONE)
474  // cerr << "writing feature failed" << endl;
475  // if(verbose_opt[0])
476  // cout << "feature written" << endl;
477  }
478  bool windowAllFlagged=true;
479  bool windowHasFlag=false;
480  short theDim=boundary_opt[0];
481  for(int windowJ=-theDim/2;windowJ<(theDim+1)/2;++windowJ){
482  for(int windowI=-theDim/2;windowI<(theDim+1)/2;++windowI){
483  if(disc_opt[0]&&(windowI*windowI+windowJ*windowJ>(theDim/2)*(theDim/2)))
484  continue;
485  int j=j_centre+windowJ;
486  //check if j is out of bounds
487  if(static_cast<int>(j)<0||static_cast<int>(j)>=inputReader.nrOfRow())
488  continue;
489  int i=i_centre+windowI;
490  //check if i is out of bounds
491  if(static_cast<int>(i)<0||static_cast<int>(i)>=inputReader.nrOfCol())
492  continue;
493  if(verbose_opt[0])
494  cout << setprecision(12) << "reading image value at x,y " << x << "," << y << " (" << i << "," << j << "), ";
495  inputReader.readData(inputValue,GDT_Float64,i,j,band_opt[0]);
496  inputValues.push_back(inputValue);
497  if(inputValues.back()!=*(inputValues.begin()))
498  isHomogeneous=false;
499  if(verbose_opt[0])
500  cout << "input value: " << inputValue << endl;
501  pixelFlagged=false;
502  for(int iflag=0;iflag<nodata_opt.size();++iflag){
503  if(inputValue==nodata_opt[iflag]){
504  pixelFlagged=true;
505  break;
506  }
507  }
508  maskFlagged=false;//(msknodata_opt[ivalue]>=0)?false:true;
509  if(mask_opt.size()){
510  maskReader.readData(maskValue,GDT_Float64,i,j,0);
511  for(int ivalue=0;ivalue<msknodata_opt.size();++ivalue){
512  if(msknodata_opt[ivalue]>=0){//values set in msknodata_opt are invalid
513  if(maskValue==msknodata_opt[ivalue]){
514  maskFlagged=true;
515  break;
516  }
517  }
518  else{//only values set in msknodata_opt are valid
519  if(maskValue!=-msknodata_opt[ivalue])
520  maskFlagged=true;
521  else{
522  maskFlagged=false;
523  break;
524  }
525  }
526  }
527  }
528  pixelFlagged=pixelFlagged||maskFlagged;
529  if(pixelFlagged)
530  windowHasFlag=true;
531  else
532  windowAllFlagged=false;//at least one good pixel in neighborhood
533  }
534  }
535  //at this point we know the values for the entire window
536 
537  if(homogeneous_opt[0]){//only centre pixel
538  int j=j_centre;
539  int i=i_centre;
540  //flag if not all pixels are homogeneous or if at least one pixel flagged
541 
542  if(!windowHasFlag&&isHomogeneous){
543  if(output_opt.size())
544  writeFeature->SetField(labelclass_opt[0].c_str(),static_cast<int>(inputValue));
545  if(confusion_opt[0]){
546  ++ntotalValidation;
547  if(classValueMap.size()){
548  assert(inputValue<nameVector.size());
549  string className=nameVector[static_cast<unsigned short>(inputValue)];
550  cm.incrementResult(type2string<short>(classValueMap[referenceClassName]),type2string<short>(classValueMap[className]),1);
551  }
552  else{
553  int rc=distance(referenceRange.begin(),find(referenceRange.begin(),referenceRange.end(),static_cast<unsigned short>(referenceValue)));
554  int ic=distance(inputRange.begin(),find(inputRange.begin(),inputRange.end(),static_cast<unsigned short>(inputValue)));
555  assert(rc<nclass);
556  assert(ic<nclass);
557  ++nvalidation[rc];
558  ++resultClass[rc][ic];
559  if(verbose_opt[0]>1)
560  cout << "increment: " << rc << " " << referenceRange[rc] << " " << ic << " " << inputRange[ic] << endl;
561  cm.incrementResult(cm.getClass(rc),cm.getClass(ic),1);
562  }
563  }
564  if(inputValue==referenceValue){//correct
565  outputValue=valueE_opt[0];
566  if(nodata_opt.size()){
567  if(valueE_opt[0]==nodata_opt[0])
568  outputValue=inputValue;
569  }
570  }
571  else if(inputValue>referenceValue)//1=forest,2=non-forest
572  outputValue=valueO_opt[0];//omission error
573  else
574  outputValue=valueC_opt[0];//commission error
575  }
576  }
577  else{
578  for(int windowJ=-theDim/2;windowJ<(theDim+1)/2;++windowJ){
579  for(int windowI=-theDim/2;windowI<(theDim+1)/2;++windowI){
580  if(disc_opt[0]&&(windowI*windowI+windowJ*windowJ>(theDim/2)*(theDim/2)))
581  continue;
582  int j=j_centre+windowJ;
583  //check if j is out of bounds
584  if(static_cast<int>(j)<0||static_cast<int>(j)>=inputReader.nrOfRow())
585  continue;
586  int i=i_centre+windowI;
587  //check if i is out of bounds
588  if(static_cast<int>(i)<0||static_cast<int>(i)>=inputReader.nrOfCol())
589  continue;
590  if(!windowAllFlagged){
591  ostringstream fs;
592  if(theDim>1)
593  fs << labelclass_opt[0] << "_" << windowJ << "_" << windowI;
594  else
595  fs << labelclass_opt[0];
596  if(output_opt.size())
597  writeFeature->SetField(fs.str().c_str(),inputValue);
598  if(!windowJ&&!windowI){//centre pixel
599  if(confusion_opt[0]){
600  ++ntotalValidation;
601  if(classValueMap.size()){
602  assert(inputValue<nameVector.size());
603  string className=nameVector[static_cast<unsigned short>(inputValue)];
604  cm.incrementResult(type2string<short>(classValueMap[referenceClassName]),type2string<short>(classValueMap[className]),1);
605  }
606  else{
607  int rc=distance(referenceRange.begin(),find(referenceRange.begin(),referenceRange.end(),static_cast<unsigned short>(referenceValue)));
608  int ic=distance(inputRange.begin(),find(inputRange.begin(),inputRange.end(),static_cast<unsigned short>(inputValue)));
609  if(rc>=nclass)
610  continue;
611  if(ic>=nclass)
612  continue;
613  // assert(rc<nclass);
614  // assert(ic<nclass);
615  ++nvalidation[rc];
616  ++resultClass[rc][ic];
617  if(verbose_opt[0]>1)
618  cout << "increment: " << rc << " " << referenceRange[rc] << " " << ic << " " << inputRange[ic] << endl;
619  cm.incrementResult(cm.getClass(rc),cm.getClass(ic),1);
620  }
621  }
622  if(inputValue==referenceValue){//correct
623  outputValue=valueE_opt[0];
624  if(nodata_opt.size()){
625  if(valueE_opt[0]==nodata_opt[0])
626  outputValue=inputValue;
627  }
628  }
629  else if(inputValue>referenceValue)//1=forest,2=non-forest
630  outputValue=valueO_opt[0];//omission error
631  else
632  outputValue=valueC_opt[0];//commission error
633  }
634  }
635  }
636  }
637  }
638  if(output_opt.size()){
639  if(!windowAllFlagged){
640  if(verbose_opt[0])
641  cout << "creating feature" << endl;
642  if(writeLayer->CreateFeature( writeFeature ) != OGRERR_NONE ){
643  string errorString="Failed to create feature in OGR vector file";
644  throw(errorString);
645  }
646  }
647  OGRFeature::DestroyFeature( writeFeature );
648  }
649  ++ifeature;
650  progress=static_cast<float>(ifeature+1)/nfeatureInLayer;
651  pfnProgress(progress,pszMessage,pProgressArg);
652  }//next feature
653  }//next layer
654  if(output_opt.size())
655  ogrWriter.close();
656  referenceReaderOgr.close();
657  inputReader.close();
658  if(mask_opt.size())
659  maskReader.close();
660  }//next reference
661  }//next input
662  pfnProgress(1.0,pszMessage,pProgressArg);
663  }//reference is OGR vector
664  else{//reference is GDAL raster
665  ImgWriterGdal gdalWriter;
666  try{
667  inputReader.open(input_opt[0]);
668  if(mask_opt.size())
669  maskReader.open(mask_opt[0]);
670  if(output_opt.size()){
671  if(verbose_opt[0])
672  cout << "opening output image " << output_opt[0] << endl;
673  if(option_opt.findSubstring("INTERLEAVE=")==option_opt.end()){
674  string theInterleave="INTERLEAVE=";
675  theInterleave+=inputReader.getInterleave();
676  option_opt.push_back(theInterleave);
677  }
678  gdalWriter.open(output_opt[0],inputReader.nrOfCol(),inputReader.nrOfRow(),1,inputReader.getDataType(),inputReader.getImageType(),option_opt);
679  if(nodata_opt.size())
680  gdalWriter.GDALSetNoDataValue(nodata_opt[0]);
681  gdalWriter.copyGeoTransform(inputReader);
682  if(colorTable_opt.size())
683  gdalWriter.setColorTable(colorTable_opt[0]);
684  else if(inputReader.getColorTable()!=NULL){
685  if(verbose_opt[0])
686  cout << "set colortable from input image" << endl;
687  gdalWriter.setColorTable(inputReader.getColorTable());
688  }
689  }
690  else if(verbose_opt[0])
691  cout << "no output image defined" << endl;
692 
693  }
694  catch(string error){
695  cout << error << endl;
696  exit(2);
697  }
698  //todo: support different data types!
699  vector<double> lineInput(inputReader.nrOfCol());
700  vector<double> lineMask(maskReader.nrOfCol());
701  vector<double> lineOutput;
702  vector<double> bufferInput;//for regression
703  vector<double> bufferReference;//for regression
704  if(output_opt.size())
705  lineOutput.resize(inputReader.nrOfCol());
706 
707  int irow=0;
708  int icol=0;
709  double oldreferencerow=-1;
710  double oldmaskrow=-1;
711  ImgReaderGdal referenceReaderGdal;
712  try{
713  referenceReaderGdal.open(reference_opt[0]);//,rmagicX_opt[0],rmagicY_opt[0]);
714  }
715  catch(string error){
716  cerr << error << endl;
717  exit(1);
718  }
719  if(inputReader.isGeoRef()){
720  assert(referenceReaderGdal.isGeoRef());
721  if(inputReader.getProjection()!=referenceReaderGdal.getProjection())
722  cerr << "Warning: projection of input image and reference image are different" << endl;
723  }
724  vector<double> lineReference(referenceReaderGdal.nrOfCol());
725  if(confusion_opt[0]){
726  referenceReaderGdal.getRange(referenceRange,band_opt[1]);
727  for(int iflag=0;iflag<nodata_opt.size();++iflag){
728  vector<short>::iterator fit;
729  fit=find(referenceRange.begin(),referenceRange.end(),static_cast<unsigned short>(nodata_opt[iflag]));
730  if(fit!=referenceRange.end())
731  referenceRange.erase(fit);
732  }
733  if(verbose_opt[0]){
734  cout << "reference range: " << endl;
735  for(int rc=0;rc<referenceRange.size();++rc)
736  cout << referenceRange[rc] << endl;
737  }
738  if(referenceRange.size()!=inputRange.size()){
739  if(confusion_opt[0]||output_opt.size()){
740  cout << "reference range is not equal to input range!" << endl;
741  cout << "Kappa: " << 0 << endl;
742  cout << "total weighted: " << 0 << endl;
743  }
744  else
745  cout << "reference range is not equal to input range!" << endl;
746  cout << input_opt[0] << " and " << reference_opt[0] << " are different" << endl;
747  exit(1);
748  }
749  }
750  double rmse=0;
751  // for(irow=0;irow<inputReader.nrOfRow()&&!isDifferent;++irow){
752  for(irow=0;irow<inputReader.nrOfRow();++irow){
753  //read line in lineInput, lineReference and lineMask
754  inputReader.readData(lineInput,GDT_Float64,irow,band_opt[0]);
755  double x,y;//geo coordinates
756  double ireference,jreference;//image coordinates in reference image
757  double imask,jmask;//image coordinates in mask image
758  for(icol=0;icol<inputReader.nrOfCol();++icol){
759  //find col in reference
760  inputReader.image2geo(icol,irow,x,y);
761  referenceReaderGdal.geo2image(x,y,ireference,jreference);
762  if(ireference<0||ireference>=referenceReaderGdal.nrOfCol()){
763  if(rmse_opt[0]||regression_opt[0])
764  continue;
765  else{
766  cerr << ireference << " out of reference range!" << endl;
767  cerr << x << " " << y << " " << icol << " " << irow << endl;
768  cerr << x << " " << y << " " << ireference << " " << jreference << endl;
769  exit(1);
770  }
771  }
772  if(jreference!=oldreferencerow){
773  if(jreference<0||jreference>=referenceReaderGdal.nrOfRow()){
774  if(rmse_opt[0]||regression_opt[0])
775  continue;
776  else{
777  cerr << jreference << " out of reference range!" << endl;
778  cerr << x << " " << y << " " << icol << " " << irow << endl;
779  cerr << x << " " << y << " " << ireference << " " << jreference << endl;
780  exit(1);
781  }
782  }
783  else{
784  referenceReaderGdal.readData(lineReference,GDT_Float64,static_cast<int>(jreference),band_opt[1]);
785  oldreferencerow=jreference;
786  }
787  }
788  bool flagged=false;
789  for(int iflag=0;iflag<nodata_opt.size();++iflag){
790  if((lineInput[icol]==nodata_opt[iflag])||(lineReference[ireference]==nodata_opt[iflag])){
791  if(output_opt.size())
792  lineOutput[icol]=nodata_opt[iflag];
793  flagged=true;
794  break;
795  }
796  }
797  if(mask_opt.size()){
798  maskReader.geo2image(x,y,imask,jmask);
799  if(jmask>=0&&jmask<maskReader.nrOfRow()){
800  if(jmask!=oldmaskrow)
801  maskReader.readData(lineMask,GDT_Float64,jmask);
802  for(int ivalue=0;ivalue<msknodata_opt.size();++ivalue){
803  if(lineMask[icol]==msknodata_opt[ivalue]){
804  flagged=true;
805  break;
806  }
807  }
808  }
809  }
810  if(!flagged){
811  if(rmse_opt[0]){//divide by image size to prevent overflow. At the end we need to take care about flagged pixels by normalizing...
812  rmse+=static_cast<double>(lineInput[icol]-lineReference[ireference])*(lineInput[icol]-lineReference[ireference])/inputReader.nrOfCol()/inputReader.nrOfRow();
813  }
814  else if(regression_opt[0]){
815  bufferInput.push_back(lineInput[icol]);
816  bufferReference.push_back(lineReference[ireference]);
817  }
818 
819  if(confusion_opt[0]){
820  ++ntotalValidation;
821  int rc=distance(referenceRange.begin(),find(referenceRange.begin(),referenceRange.end(),lineReference[ireference]));
822  int ic=distance(inputRange.begin(),find(inputRange.begin(),inputRange.end(),lineInput[icol]));
823  assert(rc<nclass);
824  assert(ic<nclass);
825  ++nvalidation[rc];
826  ++resultClass[rc][ic];
827  if(verbose_opt[0]>1)
828  cout << "increment: " << rc << " " << referenceRange[rc] << " " << ic << " " << inputRange[ic] << endl;
829  cm.incrementResult(cm.getClass(rc),cm.getClass(ic),1);
830  }
831  if(lineInput[icol]==lineReference[ireference]){//correct
832  if(output_opt.size()){
833  lineOutput[icol]=valueE_opt[0];
834  if(nodata_opt.size()){
835  if(valueE_opt[0]==nodata_opt[0])
836  lineOutput[icol]=lineInput[icol];
837  }
838  }
839  }
840  else{//error
841  if(output_opt.empty()&&!confusion_opt[0]&&!rmse_opt[0]&&!regression_opt[0]){
842  isDifferent=true;
843  break;
844  }
845  if(output_opt.size()){
846  if(lineInput[icol]>lineReference[ireference])
847  lineOutput[icol]=valueO_opt[0];//omission error
848  else
849  lineOutput[icol]=valueC_opt[0];//commission error
850  }
851  }
852  }
853  else{
854  ++nflagged;
855  if(output_opt.size()){
856  if(nodata_opt.size())
857  lineOutput[icol]=nodata_opt[0];
858  else //should never occur?
859  lineOutput[icol]=0;
860  }
861  }
862  }
863  if(output_opt.size()){
864  try{
865  gdalWriter.writeData(lineOutput,GDT_Float64,irow);
866  }
867  catch(string errorstring){
868  cerr << "lineOutput.size(): " << lineOutput.size() << endl;
869  cerr << "gdalWriter.nrOfCol(): " << gdalWriter.nrOfCol() << endl;
870  cerr << errorstring << endl;
871  exit(1);
872  }
873  }
874  else if(isDifferent&&!confusion_opt[0]&&!rmse_opt[0]&&!regression_opt[0]){//we can break off here, files are different...
875  if(!verbose_opt[0])
876  pfnProgress(1.0,pszMessage,pProgressArg);
877  break;
878  }
879  progress=static_cast<float>(irow+1.0)/inputReader.nrOfRow();
880  if(!verbose_opt[0])
881  pfnProgress(progress,pszMessage,pProgressArg);
882  }
883  if(output_opt.size())
884  gdalWriter.close();
885  else if(!confusion_opt[0]){
886  if(rmse_opt[0]){
887  double normalization=1.0*inputReader.nrOfCol()*inputReader.nrOfRow()/(inputReader.nrOfCol()*inputReader.nrOfRow()-nflagged);
888  if(verbose_opt[0]){
889  cout << "normalization: " << normalization << endl;
890  cout << "rmse before sqrt and normalization: " << rmse << endl;
891  }
892  cout << "--rmse " << sqrt(rmse/normalization) << endl;
893  }
894  else if(regression_opt[0]){
895  double err=0;
896  double c0=0;
897  double c1=1;
899  if(bufferInput.size()&&bufferReference.size()){
900  err=stat.linear_regression_err(bufferInput,bufferReference,c0,c1);
901  }
902  if(verbose_opt[0]){
903  cout << "bufferInput.size(): " << bufferInput.size() << endl;
904  cout << "bufferReference.size(): " << bufferReference.size() << endl;
905  double theMin=0;
906  double theMax=0;
907  stat.minmax(bufferInput,bufferInput.begin(),bufferInput.end(),theMin,theMax);
908  cout << "min, max input: " << theMin << ", " << theMax << endl;
909  theMin=0;
910  theMax=0;
911  stat.minmax(bufferReference,bufferReference.begin(),bufferReference.end(),theMin,theMax);
912  cout << "min, max reference: " << theMin << ", " << theMax << endl;
913  }
914  cout << "--c0 " << c0 << "--c1 " << c1 << " --rmse: " << err << endl;
915 
916  }
917  else if(isDifferent)
918  cout << input_opt[0] << " and " << reference_opt[0] << " are different" << endl;
919  else
920  cout << input_opt[0] << " and " << reference_opt[0] << " are identical" << endl;
921  }
922  referenceReaderGdal.close();
923  inputReader.close();
924  if(mask_opt.size())
925  maskReader.close();
926  }//raster dataset
927 
928  if(confusion_opt[0]){
929  cm.setFormat(cmformat_opt[0]);
930  cm.reportSE95(se95_opt[0]);
931  ofstream outputFile;
932  if(cmoutput_opt.size()){
933  outputFile.open(cmoutput_opt[0].c_str(),ios::out);
934  outputFile << cm << endl;
935  }
936  else
937  cout << cm << endl;
938  // cout << "class #samples userAcc prodAcc" << endl;
939  // double se95_ua=0;
940  // double se95_pa=0;
941  // double se95_oa=0;
942  // double dua=0;
943  // double dpa=0;
944  // double doa=0;
945  // for(int iclass=0;iclass<cm.nClasses();++iclass){
946  // dua=cm.ua_pct(classNames[iclass],&se95_ua);
947  // dpa=cm.pa_pct(classNames[iclass],&se95_pa);
948  // cout << cm.getClass(iclass) << " " << cm.nReference(cm.getClass(iclass)) << " " << dua << " (" << se95_ua << ")" << " " << dpa << " (" << se95_pa << ")" << endl;
949  // }
950  // doa=cm.oa(&se95_oa);
951  // cout << "Kappa: " << cm.kappa() << endl;
952  // cout << "Overall Accuracy: " << 100*doa << " (" << 100*se95_oa << ")" << endl;
953  }
954 }