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