pktools  2.6.7
Processing Kernel for geospatial data
pkndvi.cc
1 /**********************************************************************
2 pkndvi.cc: program to calculate vegetation index image
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 <vector>
22 #include "imageclasses/ImgReaderGdal.h"
23 #include "imageclasses/ImgWriterGdal.h"
24 #include "base/Optionpk.h"
25 
26 using namespace std;
27 
28 int main(int argc, char *argv[])
29 {
30  //command line options
31  Optionpk<string> input_opt("i","input","input image file");
32  Optionpk<string> output_opt("o","output","output image file containing ndvi");
33  Optionpk<short> band_opt("b", "band", "Bands to be used for vegetation index (see rule option)", 0);
34  Optionpk<string> rule_opt("r", "rule", "Rule for index. ndvi (b1-b0)/(b1+b0), ndvi2 (b1-b0)/(b2+b3), gvmi (b0+0.1)-(b1+0.02))/((b0+0.1)+(b1+0.02))), vari (b1-b2)/(b1+b2-b0), osavi, mcari, tcari, diff (b1-b0), scale, ratio.", "ndvi");
35  Optionpk<double> invalid_opt("t", "invalid", "Mask value where image is invalid.", 0);
36  Optionpk<int> nodata_opt("nodata", "nodata", "Flag value to put in image if not valid (0)", 0);
37  Optionpk<string> colorTable_opt("ct", "ct", "color table (file with 5 columns: id R G B ALFA (0: transparent, 255: solid)");
38  Optionpk<string> description_opt("d", "description", "Set image description");
39  Optionpk<double> min_opt("min", "min", "minimum value for ndvi after scaling (set all values smaller than min to min)", 0);
40  Optionpk<double> max_opt("max", "max", "maximum value for ndvi after scaling (limit all values to max)");
41  Optionpk<double> eps_opt("e", "eps", "epsilon, contraint division by zero", 0);
42  Optionpk<double> src_scale_opt("src_s", "src_scale", "scale used for input, scale[1] is used for output: DN=scale[1]*ndvi+offset[1]", 1);
43  Optionpk<double> dst_scale_opt("dst_s", "src_scale", "scale used for output: DN=dst_s*ndvi+dst_offset", 1);
44  Optionpk<double> src_offset_opt("src_o", "src_offset", "offset used for input", 0);
45  Optionpk<double> dst_offset_opt("dst_o", "dst_offset", "offset is used for output: DN=dst_s*ndvi+dst_offset", 0);
46  Optionpk<string> otype_opt("ot", "otype", "Data type for output image ({Byte/Int16/UInt16/UInt32/Int32/Float32/Float64/CInt16/CInt32/CFloat32/CFloat64}). Empty string: inherit type from input image", "Byte");
47  Optionpk<string> oformat_opt("of", "oformat", "Output image format (see also gdal_translate). Empty string: inherit from input image", "GTiff");
48  Optionpk<string> option_opt("co", "co", "Creation option for output file. Multiple options can be specified.");
49  Optionpk<short> verbose_opt("v", "verbose", "verbose mode if > 0", 0);
50 
51  bool doProcess;//stop process when program was invoked with help option (-h --help)
52  try{
53  doProcess=input_opt.retrieveOption(argc,argv);
54  output_opt.retrieveOption(argc,argv);
55  band_opt.retrieveOption(argc,argv);
56  rule_opt.retrieveOption(argc,argv);
57  invalid_opt.retrieveOption(argc,argv);
58  nodata_opt.retrieveOption(argc,argv);
59  colorTable_opt.retrieveOption(argc,argv);
60  description_opt.retrieveOption(argc,argv);
61  min_opt.retrieveOption(argc,argv);
62  max_opt.retrieveOption(argc,argv);
63  eps_opt.retrieveOption(argc,argv);
64  src_scale_opt.retrieveOption(argc,argv);
65  src_offset_opt.retrieveOption(argc,argv);
66  dst_scale_opt.retrieveOption(argc,argv);
67  dst_offset_opt.retrieveOption(argc,argv);
68  otype_opt.retrieveOption(argc,argv);
69  oformat_opt.retrieveOption(argc,argv);
70  option_opt.retrieveOption(argc,argv);
71  verbose_opt.retrieveOption(argc,argv);
72  }
73  catch(string predefinedString){
74  std::cout << predefinedString << std::endl;
75  exit(0);
76  }
77  if(!doProcess){
78  std::cout << "short option -h shows basic options only, use long option --help to show all options" << std::endl;
79  exit(0);//help was invoked, stop processing
80  }
81 
82  if(input_opt.empty()){
83  std::cerr << "No input file provided (use option -i). Use --help for help information" << std::endl;
84  exit(0);
85  }
86  if(output_opt.empty()){
87  std::cerr << "No output file provided (use option -o). Use --help for help information" << std::endl;
88  exit(0);
89  }
90 
91  int reqBand=0;
92  if(rule_opt[0]=="scale")
93  reqBand=1;
94  else if(rule_opt[0]=="vari"||rule_opt[0]=="mcari"||rule_opt[0]=="tcari")
95  reqBand=3;
96  else if(rule_opt[0]=="ndvi2")
97  reqBand=4;
98  else
99  reqBand=2;
100  while(band_opt.size()<reqBand)//bands can be explicitly provided by user or
101  band_opt.push_back(band_opt[0]);//default is to use band 0 for each input
102  if(verbose_opt[0])
103  std::cout << band_opt;
104 
105  //todo: a bit stupid to duplicate input reader, but it works
106  while(input_opt.size()<reqBand)
107  input_opt.push_back(input_opt[0]);
108  if(verbose_opt[0])
109  std::cout << input_opt;
110 
111  vector<ImgReaderGdal> inputReader(reqBand);
112  for(int ifile=0;ifile<reqBand;++ifile){
113  inputReader[ifile].open(input_opt[ifile]);
114  assert(inputReader[ifile].nrOfBand()>band_opt[ifile]);
115  }
116 
117  if(verbose_opt[0]){
118  cout << "opening output image file " << output_opt[0] << endl;
119  cout << "data type: " << otype_opt[0] << endl;
120  }
121  //create output image with user defined data type
122  GDALDataType theType=GDT_Unknown;
123  if(verbose_opt[0])
124  cout << "possible output data types: ";
125  for(int iType = 0; iType < GDT_TypeCount; ++iType){
126  if(verbose_opt[0])
127  cout << " " << GDALGetDataTypeName((GDALDataType)iType);
128  if( GDALGetDataTypeName((GDALDataType)iType) != NULL
129  && EQUAL(GDALGetDataTypeName((GDALDataType)iType),
130  otype_opt[0].c_str()))
131  theType=(GDALDataType) iType;
132  }
133  if(theType==GDT_Unknown)
134  theType=inputReader[0].getDataType();
135  if(verbose_opt[0])
136  cout << endl << "Output pixel type: " << GDALGetDataTypeName(theType) << endl;
137 
138  ImgWriterGdal outputWriter;
139  if(verbose_opt[0])
140  cout << "opening output image file " << output_opt[0] << endl;
141 
142  if(option_opt.findSubstring("INTERLEAVE=")==option_opt.end()){
143  string theInterleave="INTERLEAVE=";
144  theInterleave+=inputReader[0].getInterleave();
145  option_opt.push_back(theInterleave);
146  }
147  outputWriter.open(output_opt[0],inputReader[0].nrOfCol(),inputReader[0].nrOfRow(),1,theType,oformat_opt[0],option_opt);
148  outputWriter.GDALSetNoDataValue(nodata_opt[0]);
149 
150  if(description_opt.size())
151  outputWriter.setImageDescription(description_opt[0]);
152  //if input image is georeferenced, copy projection info to output image
153 
154  outputWriter.setProjection(inputReader[0].getProjection());
155  double ulx,uly,lrx,lry;
156  inputReader[0].getBoundingBox(ulx,uly,lrx,lry);
157  outputWriter.copyGeoTransform(inputReader[0]);
158 
159  if(colorTable_opt.size()){
160  if(colorTable_opt[0]!="none")
161  outputWriter.setColorTable(colorTable_opt[0]);
162  }
163  else if (inputReader[0].getColorTable()!=NULL)//copy colorTable from first input image
164  outputWriter.setColorTable(inputReader[0].getColorTable());
165 
166  Vector2d<double> lineInput(reqBand,inputReader[0].nrOfCol());
167  vector<double> lineOutput(outputWriter.nrOfCol());
168 
169  int irow=0;
170  int icol=0;
171  const char* pszMessage;
172  void* pProgressArg=NULL;
173  GDALProgressFunc pfnProgress=GDALTermProgress;
174  float progress=0;
175  pfnProgress(progress,pszMessage,pProgressArg);
176  for(irow=0;irow<inputReader[0].nrOfRow();++irow){
177  //read line in lineInput buffer
178  try{
179  if(rule_opt[0]=="scale")
180  inputReader[0].readData(lineInput[0],GDT_Float64,irow,band_opt[0]);
181  else if(rule_opt[0]=="vari"||rule_opt[0]=="tcari"){
182  inputReader[0].readData(lineInput[0],GDT_Float64,irow,band_opt[0]);
183  inputReader[1].readData(lineInput[1],GDT_Float64,irow,band_opt[1]);
184  inputReader[2].readData(lineInput[2],GDT_Float64,irow,band_opt[2]);
185  }
186  else if(rule_opt[0]=="ndvi2"){
187  inputReader[0].readData(lineInput[0],GDT_Float64,irow,band_opt[0]);
188  inputReader[1].readData(lineInput[1],GDT_Float64,irow,band_opt[1]);
189  inputReader[2].readData(lineInput[2],GDT_Float64,irow,band_opt[2]);
190  inputReader[3].readData(lineInput[3],GDT_Float64,irow,band_opt[3]);
191  }
192  else{
193  inputReader[0].readData(lineInput[0],GDT_Float64,irow,band_opt[0]);
194  inputReader[1].readData(lineInput[1],GDT_Float64,irow,band_opt[1]);
195  }
196  }
197  catch(string errorstring){
198  cerr << errorstring << endl;
199  exit(1);
200  }
201  assert(invalid_opt.size()==nodata_opt.size());
202  for(icol=0;icol<inputReader[0].nrOfCol();++icol){
203  double ndvi=min_opt[0];
204  double flagValue=nodata_opt[0];
205  bool valid=true;
206  for(int iflag=0;valid&&iflag<invalid_opt.size();++iflag){
207  for(int iband=0;iband<lineInput.size();++iband){
208  if(lineInput[iband][icol]==invalid_opt[iflag]){
209  flagValue=nodata_opt[iflag];
210  valid=false;
211  break;
212  }
213  }
214  }
215  double denom;
216  double nom;
217  if(valid){
218  if(rule_opt[0]=="ndvi"){
219  //Example of indices addressed by ndvi:
220  //structural indices
221  //NDVI (Rouse1974): b0=b_680, b1=b_800
222  //Chlorophyll indices:
223  //Normalized Phaeophytinization index (NPQI Barnes1992): b0=R_435, b1=R_415
224  //Photochemical Reflectance index (PRI1 Gamon1992): b0=R_567, b1=R_528
225  //Photochemical Reflectance index (PRI2 Gamon1992): b0=R_570, b1=R_531
226  //Normalized Phaeophytinization index (NPQI Barnes1992): b0=R_435, b1=R_415
227  //Normalized Pigment Chlorophyll index (NPCI Penuelas1994): b0=R_430, b1=R_680
228  //Structure Intensive Pigment index (SIPI Penuelas 1995): b0=R_450, b1=R_800
229  //Lichtenthaler index 1 (Lic1 Lichtenthaler1996): b0=R_680, b2=R_800
230  denom=(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]-(lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0];
231  nom=(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]+(lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0];
232  }
233  else if(rule_opt[0]=="ndvi2"){//normalized difference with different wavelengths used in denom and nom
234  //Example of indices addressed by ndvi2
235  //Structure Intensive Pigment index (SIPI Penuelas 1995): b0=R_450, b1=R_800, b2=R_650, b=R_800
236  //Vogelmann index 2 (Vog2 Vogelmann1993): b0=R_747, b1=R_735, b2=R_715, b3=R_726
237  //Vogelmann index 3 (Vog3 Vogelmann1993): b0=R_747, b1=R_734, b2=R_715, b3=R_720
238  denom=(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]-(lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0];
239  nom=(lineInput[2][icol]-src_offset_opt[0])/src_scale_opt[0]+(lineInput[3][icol]-src_offset_opt[0])/src_scale_opt[0];
240  }
241  else if(rule_opt[0]=="gvmi"){
242  denom=((lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0]+0.1)-((lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]+0.02);
243  nom=((lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0]+0.1)+((lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]+0.02);
244  }
245  else if(rule_opt[0]=="vari"){
246  denom=(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]-(lineInput[2][icol]-src_offset_opt[0])/src_scale_opt[0];
247  nom=(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]+(lineInput[2][icol]-src_offset_opt[0])/src_scale_opt[0]-(lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0];
248  }
249  else if(rule_opt[0]=="osavi"){//structural index (Rondeaux1996): //b0=R_670, b1=R_800
250  denom=(1.0+0.16)*(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]-(lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0];
251  nom=(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]+(lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0]+0.16;
252  }
253  else if(rule_opt[0]=="mcari"){//chlorophyll index (Daughtry2000): b0=R_550, b1=R_670, b2=R_700
254  denom=((lineInput[2][icol]-src_offset_opt[0])/src_scale_opt[0]-(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]-0.2*((lineInput[2][icol]-src_offset_opt[0])/src_scale_opt[0]-(lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0]))*(lineInput[2][icol]-src_offset_opt[0])/src_scale_opt[0];
255  nom=(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0];
256  }
257  else if(rule_opt[0]=="tcari"){//chlorophyll index (Haboudane2002): b0=R_550, b1=R_670, B2=R_700
258  denom=3*((lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]*(lineInput[2][icol]-src_offset_opt[0])/src_scale_opt[0]-(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]-0.2*((lineInput[2][icol]-src_offset_opt[0])/src_scale_opt[0]-(lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0])*(lineInput[2][icol]-src_offset_opt[0])/src_scale_opt[0]);
259  nom=(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0];
260  }
261  else if(rule_opt[0]=="diff"){
262  denom=(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0]-(lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0];
263  nom=1.0;
264  }
265  else if(rule_opt[0]=="scale"){
266  denom=(lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0];
267  nom=1.0;
268  }
269  else if(rule_opt[0]=="ratio"){
270  //Examples of indices addressed by ratio:
271  //structural indices:
272  //Simple Ratio Index (SR Jordan1969, Rouse1974): b0=R_NIR/R_RED
273  //chlorophyll indices:
274  //Greenness Index: b0=R_554, b1=R_677;
275  //Zarco-Tejada&Miller (Zarco2001): b0=R_750,b1=R_710
276  //Simple Red Pigment Index (SRPI Penuelas1995): b0=R_430, b1=R_680
277  //Carter index 1 (Ctr1 Carter1994): b0=R_695, b1=R_420
278  //Carter index 2 (Ctr2 Carter1994): b0=R_695, b1=R_760
279  //Lichtenthaler index 2 (Lic2 Lichtenthaler1996): b0=R_440, b2=R_690
280  //Vogelmann index 1 (Vog1 Vogelmann1993): b0=R_740, b1=R_720
281  //Gitelson and Merzlyak 1 (GM1 Gitelson1997): b0=R_750 b1=R_550
282  //Gitelson and Merzlyak (GM2 Gitelson1997) b0=R_750 b1=R_700
283  denom=(lineInput[0][icol]-src_offset_opt[0])/src_scale_opt[0];
284  nom=(lineInput[1][icol]-src_offset_opt[0])/src_scale_opt[0];
285  }
286  else{
287  std::cout << "Error: rule " << rule_opt[0] << " not supported" << std::endl;
288  exit(1);
289  }
290  if(nom>eps_opt[0]||nom<-eps_opt[0])
291  ndvi=denom/nom;
292  switch(theType){
293  case(GDT_Byte):
294  case(GDT_Int16):
295  case(GDT_UInt16):
296  case(GDT_UInt32):
297  case(GDT_Int32):
298  lineOutput[icol]=static_cast<int>(0.5+ndvi*dst_scale_opt[0]+dst_offset_opt[0]);
299  break;
300  default:
301  lineOutput[icol]=ndvi*dst_scale_opt[0]+dst_offset_opt[0];
302  break;
303  }
304  if(lineOutput[icol]<min_opt[0])
305  lineOutput[icol]=min_opt[0];
306  else if(max_opt.size()){
307  if(lineOutput[icol]>max_opt[0])
308  lineOutput[icol]=max_opt[0];
309  }
310  }
311  else
312  lineOutput[icol]=flagValue;
313  }
314  //write buffer lineOutput to output file
315  try{
316  outputWriter.writeData(lineOutput,GDT_Float64,irow);
317  }
318  catch(string errorstring){
319  cerr << errorstring << endl;
320  exit(1);
321  }
322  //progress bar
323  progress=static_cast<float>(irow+1.0)/outputWriter.nrOfRow();
324  pfnProgress(progress,pszMessage,pProgressArg);
325  }
326  for(int ifile=0;ifile<inputReader.size();++ifile)
327  inputReader[ifile].close();
328  outputWriter.close();
329 }
void setImageDescription(const std::string &imageDescription)
Set the image description (only for GeoTiff format: TIFFTAG_IMAGEDESCRIPTION)
Definition: ImgWriterGdal.h:55
STL namespace.
int nrOfRow(void) const
Get the number of rows of this dataset.
void setColorTable(const std::string &filename, int band=0)
Set the color table using an (ASCII) file with 5 columns (value R G B alpha)
bool writeData(T &value, int col, int row, int band=0)
Write a single pixel cell value at a specific column and row for a specific band (all indices start c...
Definition: ImgWriterGdal.h:96
void copyGeoTransform(const ImgRasterGdal &imgSrc)
Copy geotransform information from another georeferenced image.
void open(const std::string &filename, const ImgReaderGdal &imgSrc, const std::vector< std::string > &options=std::vector< std::string >())
Open an image for writing, copying image attributes from a source image. Image is directly written to...
CPLErr setProjection(const std::string &projection)
Set the projection for this dataset in well known text (wkt) format.
std::string getInterleave() const
Get the band coding (interleave)
void close(void)
Close the image.
CPLErr GDALSetNoDataValue(double noDataValue, int band=0)
Set the GDAL (internal) no data value for this data set. Only a single no data value per band is supp...
int nrOfCol(void) const
Get the number of columns of this dataset.
Definition: ImgRasterGdal.h:98