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