pktools  2.6.6
Processing Kernel for geospatial data
pklas2img.cc
1 /**********************************************************************
2 pklas2img.cc: Rasterize LAS/LAZ point clouds with filtering/compositing options
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 <iostream>
21 #include "base/Optionpk.h"
22 #include "imageclasses/ImgReaderGdal.h"
23 #include "imageclasses/ImgWriterGdal.h"
24 #include "imageclasses/ImgReaderOgr.h"
25 #include "lasclasses/FileReaderLas.h"
26 #include "algorithms/StatFactory.h"
27 #include "algorithms/Filter2d.h"
28 
29 /******************************************************************************/
83 using namespace std;
84 
85 int main(int argc,char **argv) {
86  Optionpk<string> input_opt("i", "input", "Input las file");
87  Optionpk<string> attribute_opt("n", "name", "names of the point attribute to select: intensity, angle, return, nreturn, angle, z", "z");
88  // Optionpk<bool> disc_opt("circ", "circular", "circular disc kernel for dilation and erosion", false);
89  // Optionpk<double> maxSlope_opt("s", "maxSlope", "Maximum slope used for morphological filtering", 0.0);
90  // Optionpk<double> hThreshold_opt("ht", "maxHeight", "initial and maximum height threshold for progressive morphological filtering (e.g., -ht 0.2 -ht 2.5)", 0.2);
91  // Optionpk<short> maxIter_opt("maxit", "maxit", "Maximum number of iterations in post filter", 5);
92  Optionpk<unsigned short> returns_opt("ret", "ret", "number(s) of returns to include");
93  Optionpk<unsigned short> classes_opt("class", "class", "classes to keep: 0 (created, never classified), 1 (unclassified), 2 (ground), 3 (low vegetation), 4 (medium vegetation), 5 (high vegetation), 6 (building), 7 (low point, noise), 8 (model key-point), 9 (water), 10 (reserved), 11 (reserved), 12 (overlap)");
94  Optionpk<string> composite_opt("comp", "comp", "composite for multiple points in cell (min, max, median, mean, sum, first, last, profile (percentile height values), percentile, number (point density)). Last: overwrite cells with latest point", "last");
95  Optionpk<string> filter_opt("fir", "filter", "filter las points (first,last,single,multiple,all).", "all");
96  Optionpk<unsigned short> angle_min_opt("angle_min", "angle_min", "Minimum scan angle to read points.");
97  Optionpk<unsigned short> angle_max_opt("angle_max", "angle_max", "Maximum scan angle to read points.");
98  // Optionpk<string> postFilter_opt("pf", "pfilter", "post processing filter (etew_min,promorph (progressive morphological filter),bunting (adapted promorph),open,close,none).", "none");
99  // Optionpk<short> dimx_opt("dimx", "dimx", "Dimension X of postFilter", 3);
100  // Optionpk<short> dimy_opt("dimy", "dimy", "Dimension Y of postFilter", 3);
101  Optionpk<string> output_opt("o", "output", "Output image file");
102  Optionpk<string> projection_opt("a_srs", "a_srs", "assign the projection for the output file in epsg code, e.g., epsg:3035 for European LAEA projection");
103  Optionpk<double> ulx_opt("ulx", "ulx", "Upper left x value bounding box (in geocoordinates if georef is true). 0 is read from input file", 0.0);
104  Optionpk<double> uly_opt("uly", "uly", "Upper left y value bounding box (in geocoordinates if georef is true). 0 is read from input file", 0.0);
105  Optionpk<double> lrx_opt("lrx", "lrx", "Lower right x value bounding box (in geocoordinates if georef is true). 0 is read from input file", 0.0);
106  Optionpk<double> lry_opt("lry", "lry", "Lower right y value bounding box (in geocoordinates if georef is true). 0 is read from input file", 0.0);
107  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");
108  Optionpk<string> oformat_opt("of", "oformat", "Output image format (see also gdal_translate).", "GTiff");
109  Optionpk<double> dx_opt("dx", "dx", "Output resolution in x (in meter)", 1.0);
110  Optionpk<double> dy_opt("dy", "dy", "Output resolution in y (in meter)", 1.0);
111  Optionpk<short> nbin_opt("nbin", "nbin", "Number of percentile bins for calculating percentile height value profile (=number of output bands)", 10.0);
112  Optionpk<double> percentile_opt("perc","percentile","Percentile value used for rule percentile",95);
113  Optionpk<short> nodata_opt("nodata", "nodata", "nodata value to put in image", 0);
114  Optionpk<string> option_opt("co", "co", "Creation option for output file. Multiple options can be specified.");
115  Optionpk<string> colorTable_opt("ct", "ct", "color table (file with 5 columns: id R G B ALFA (0: transparent, 255: solid)");
116  Optionpk<short> verbose_opt("v", "verbose", "verbose mode", 0,2);
117 
118  nbin_opt.setHide(1);
119  percentile_opt.setHide(1);
120  nodata_opt.setHide(1);
121  option_opt.setHide(1);
122  colorTable_opt.setHide(1);
123 
124  bool doProcess;//stop process when program was invoked with help option (-h --help)
125  try{
126  doProcess=input_opt.retrieveOption(argc,argv);
127  attribute_opt.retrieveOption(argc,argv);
128  returns_opt.retrieveOption(argc,argv);
129  classes_opt.retrieveOption(argc,argv);
130  composite_opt.retrieveOption(argc,argv);
131  filter_opt.retrieveOption(argc,argv);
132  angle_min_opt.retrieveOption(argc,argv);
133  angle_max_opt.retrieveOption(argc,argv);
134  output_opt.retrieveOption(argc,argv);
135  projection_opt.retrieveOption(argc,argv);
136  ulx_opt.retrieveOption(argc,argv);
137  uly_opt.retrieveOption(argc,argv);
138  lrx_opt.retrieveOption(argc,argv);
139  lry_opt.retrieveOption(argc,argv);
140  otype_opt.retrieveOption(argc,argv);
141  oformat_opt.retrieveOption(argc,argv);
142  dx_opt.retrieveOption(argc,argv);
143  dy_opt.retrieveOption(argc,argv);
144  nbin_opt.retrieveOption(argc,argv);
145  percentile_opt.retrieveOption(argc,argv);
146  nodata_opt.retrieveOption(argc,argv);
147  option_opt.retrieveOption(argc,argv);
148  colorTable_opt.retrieveOption(argc,argv);
149  verbose_opt.retrieveOption(argc,argv);
150  }
151  catch(string predefinedString){
152  std::cout << predefinedString << std::endl;
153  exit(0);
154  }
155 
156  if(!doProcess){
157  cout << endl;
158  cout << "pklas2img -i lasfile -o output" << endl;
159  cout << endl;
160  std::cout << "short option -h shows basic options only, use long option --help to show all options" << std::endl;
161  exit(0);//help was invoked, stop processing
162  }
163  //todo: is this needed?
164  GDALAllRegister();
165 
166  double dfComplete=0.0;
167  const char* pszMessage;
168  void* pProgressArg=NULL;
169  GDALProgressFunc pfnProgress=GDALTermProgress;
170  double progress=0;
171 
172  Vector2d<vector<double> > inputData;//row,col,point
173 
174 
175  ImgReaderGdal maskReader;
176  ImgWriterGdal outputWriter;
177  GDALDataType theType=GDT_Unknown;
178  if(verbose_opt[0])
179  cout << "possible output data types: ";
180  for(int iType = 0; iType < GDT_TypeCount; ++iType){
181  if(verbose_opt[0])
182  cout << " " << GDALGetDataTypeName((GDALDataType)iType);
183  if( GDALGetDataTypeName((GDALDataType)iType) != NULL
184  && EQUAL(GDALGetDataTypeName((GDALDataType)iType),
185  otype_opt[0].c_str()))
186  theType=(GDALDataType) iType;
187  }
188  if(verbose_opt[0]){
189  if(theType==GDT_Unknown)
190  cout << "Unknown output pixel type: " << otype_opt[0] << endl;
191  else
192  cout << "Output pixel type: " << GDALGetDataTypeName(theType) << endl;
193  }
194 
195  double maxLRX=0;
196  double maxULY=0;
197  double minULX=0;
198  double minLRY=0;
199 
200  unsigned long int totalPoints=0;
201  unsigned long int nPoints=0;
202  unsigned long int ipoint=0;
203  for(int iinput=0;iinput<input_opt.size();++iinput){
204  // assert(input_opt[iinput].find(".las")!=string::npos);
205  FileReaderLas lasReader;
206  try{
207  lasReader.open(input_opt[iinput]);
208  }
209  catch(string errorString){
210  cerr << errorString << endl;
211  exit(1);
212  }
213  catch(...){
214  cerr << "Error opening input " << input_opt[iinput] << endl;
215  exit(2);
216  }
217  nPoints=lasReader.getPointCount();
218  totalPoints+=nPoints;
219 
220  if(ulx_opt[0]>=lrx_opt[0]||uly_opt[0]<=lry_opt[0]){
221  double ulx,uly,lrx,lry;
222  lasReader.getExtent(ulx,uly,lrx,lry);
223  lrx+=dx_opt[0];//pixel coordinates are referenced to upper left corner (las coordinates are centres)
224  lry-=dy_opt[0];//pixel coordinates are referenced to upper left corner (las coordinates are centres)
225  if(ulx>=lrx){
226  ulx=ulx-dx_opt[0]/2.0;
227  lrx=ulx+dx_opt[0]/2.0;
228  }
229  if(uly<=lry){
230  uly=lry+dy_opt[0]/2.0;
231  lry=lry-dy_opt[0]/2.0;
232  }
233  if(maxLRX>minULX){
234  maxLRX=(lrx>maxLRX)?lrx:maxLRX;
235  maxULY=(uly>maxULY)?uly:maxULY;
236  minULX=(ulx<minULX)?ulx:minULX;
237  minLRY=(lry<minLRY)?lry:minLRY;
238  }
239  else{//initialize
240  maxLRX=lrx;
241  maxULY=uly;
242  minULX=ulx;
243  minLRY=lry;
244  }
245  }
246  else{
247  maxLRX=lrx_opt[0];
248  maxULY=uly_opt[0];
249  minULX=ulx_opt[0];
250  minLRY=lry_opt[0];
251  }
252  lasReader.close();
253  }
254  if(verbose_opt[0]){
255  std::cout << setprecision(12) << "--ulx=" << minULX << " --uly=" << maxULY << " --lrx=" << maxLRX << " --lry=" << minLRY << std::endl;
256  std::cout << "total number of points before filtering: " << totalPoints << std::endl;
257  std::cout << "filter set to " << filter_opt[0] << std::endl;
258  if(angle_min_opt.size())
259  std::cout << "minimum scan angle: " << angle_min_opt[0] << std::endl;
260  if(angle_max_opt.size())
261  std::cout << "maximum scan angle: " << angle_max_opt[0] << std::endl;
262  // std::cout << "postFilter set to " << postFilter_opt[0] << std::endl;
263  }
264  int ncol=ceil(maxLRX-minULX)/dx_opt[0];//number of columns in outputGrid
265  int nrow=ceil(maxULY-minLRY)/dy_opt[0];//number of rows in outputGrid
266  //todo: multiple bands
267  int nband=(composite_opt[0]=="profile")? nbin_opt[0] : 1;
268  if(!output_opt.size()){
269  cerr << "Error: no output file defined" << endl;
270  exit(1);
271  }
272  if(verbose_opt[0])
273  cout << "opening output file " << output_opt[0] << endl;
274  outputWriter.open(output_opt[0],ncol,nrow,nband,theType,oformat_opt[0],option_opt);
275  outputWriter.GDALSetNoDataValue(nodata_opt[0]);
276  //set projection
277  double gt[6];
278  gt[0]=minULX;
279  gt[1]=dx_opt[0];
280  gt[2]=0;
281  gt[3]=maxULY;
282  gt[4]=0;
283  gt[5]=-dy_opt[0];
284  outputWriter.setGeoTransform(gt);
285  if(projection_opt.size()){
286  string projectionString=outputWriter.setProjectionProj4(projection_opt[0]);
287  if(verbose_opt[0])
288  cout << "projection: " << projectionString << endl;
289  }
290  if(!outputWriter.isGeoRef())
291  cout << "Warning: output image " << output_opt[0] << " is not georeferenced!" << endl;
292  if(colorTable_opt.size())
293  outputWriter.setColorTable(colorTable_opt[0]);
294 
295  inputData.clear();
296  inputData.resize(nrow,ncol);
297  Vector2d<double> outputData(nrow,ncol);
298  for(int irow=0;irow<nrow;++irow)
299  for(int icol=0;icol<ncol;++icol)
300  outputData[irow][icol]=0;
301 
302  cout << "Reading " << input_opt.size() << " point cloud files" << endl;
303  pfnProgress(progress,pszMessage,pProgressArg);
304  for(int iinput=0;iinput<input_opt.size();++iinput){
305  FileReaderLas lasReader;
306  try{
307  lasReader.open(input_opt[iinput]);
308  }
309  catch(string errorString){
310  cout << errorString << endl;
311  exit(1);
312  }
313  if(verbose_opt[0]){
314  if(lasReader.isCompressed())
315  cout << "Reading compressed point cloud " << input_opt[iinput]<< endl;
316  else
317  cout << "Reading uncompressed point cloud " << input_opt[iinput] << endl;
318  }
319  //set bounding filter
320  // lasReader.addBoundsFilter(minULX,maxULY,maxLRX,minLRY);
321  //set returns filter
322  if(returns_opt.size())
323  lasReader.addReturnsFilter(returns_opt);
324  if(classes_opt.size())
325  lasReader.addClassFilter(classes_opt);
326  lasReader.setFilters();
327 
328  if(attribute_opt[0]!="z"){
329  vector<boost::uint16_t> returnsVector;
330  vector<string>::iterator ait=attribute_opt.begin();
331  while(ait!=attribute_opt.end()){
332  if(*ait=="intensity"){
333  if(verbose_opt[0])
334  std::cout << "writing intensity" << std::endl;
335  ++ait;
336  }
337  if(*ait=="angle"){
338  if(verbose_opt[0])
339  std::cout << "writing angle" << std::endl;
340  ++ait;
341  }
342  else if(*ait=="return"){
343  if(verbose_opt[0])
344  std::cout << "writing return number" << std::endl;
345  ++ait;
346  }
347  else if(*ait=="nreturn"){
348  if(verbose_opt[0])
349  std::cout << "writing number of returns" << std::endl;
350  ++ait;
351  }
352  else
353  attribute_opt.erase(ait);
354  }
355  }
356  liblas::Point thePoint(&(lasReader.getHeader()));
357  while(lasReader.readNextPoint(thePoint)){
358  progress=static_cast<float>(ipoint)/totalPoints;
359  pfnProgress(progress,pszMessage,pProgressArg);
360  if(verbose_opt[0]>1)
361  cout << "reading point " << ipoint << endl;
362  if(thePoint.GetX()<minULX||thePoint.GetX()>=maxLRX||thePoint.GetY()>=maxULY||thePoint.GetY()<minLRY)
363  continue;
364  if((filter_opt[0]=="single")&&(thePoint.GetNumberOfReturns()!=1))
365  continue;
366  if((filter_opt[0]=="multiple")&&(thePoint.GetNumberOfReturns()<2))
367  continue;
368  if((filter_opt[0]=="last")&&(thePoint.GetReturnNumber()!=thePoint.GetNumberOfReturns()))
369  continue;
370  if((filter_opt[0]=="first")&&(thePoint.GetReturnNumber()!=1))
371  continue;
372  if(angle_min_opt.size()){
373  if(angle_min_opt[0]>thePoint.GetScanAngleRank())
374  continue;
375  }
376  if(angle_max_opt.size()){
377  if(angle_max_opt[0]<thePoint.GetScanAngleRank())
378  continue;
379  }
380  double dcol,drow;
381  outputWriter.geo2image(thePoint.GetX(),thePoint.GetY(),dcol,drow);
382  int icol=static_cast<int>(dcol);
383  int irow=static_cast<int>(drow);
384  if(irow<0||irow>=nrow){
385  // //test
386  // cout << "Error: thePoint.GetX(),thePoint.GetY(),dcol,drow" << thePoint.GetX() << ", " << thePoint.GetY() << ", " << dcol << ", " << drow << endl;
387  continue;
388  }
389  if(icol<0||icol>=ncol){
390  // //test
391  // cout << "Error: thePoint.GetX(),thePoint.GetY(),dcol,drow" << thePoint.GetX() << ", " << thePoint.GetY() << ", " << dcol << ", " << drow << endl;
392  continue;
393  }
394  assert(irow>=0);
395  assert(irow<nrow);
396  assert(icol>=0);
397  assert(icol<ncol);
398  if(composite_opt[0]=="number")
399  outputData[irow][icol]+=1;
400  else if(attribute_opt[0]=="z")
401  inputData[irow][icol].push_back(thePoint.GetZ());
402  else if(attribute_opt[0]=="intensity")
403  inputData[irow][icol].push_back(thePoint.GetIntensity());
404  else if(attribute_opt[0]=="angle")
405  inputData[irow][icol].push_back(thePoint.GetScanAngleRank());
406  else if(attribute_opt[0]=="return")
407  inputData[irow][icol].push_back(thePoint.GetReturnNumber());
408  else if(attribute_opt[0]=="nreturn")
409  inputData[irow][icol].push_back(thePoint.GetNumberOfReturns());
410  else{
411  std::string errorString="attribute not supported";
412  throw(errorString);
413  }
414  ++ipoint;
415  }
416  if(verbose_opt[0])
417  std::cout << "number of points: " << ipoint << std::endl;
418  lasReader.close();
419  }
420  progress=1;
421  pfnProgress(progress,pszMessage,pProgressArg);
422 
423  std::cout << "processing LiDAR points" << std::endl;
424  progress=0;
425  pfnProgress(progress,pszMessage,pProgressArg);
427  //fill inputData in outputData
428  // if(composite_opt[0]=="profile"){
429  // assert(postFilter_opt[0]=="none");
430  // for(int iband=0;iband<nband;++iband)
431  // outputProfile[iband].resize(nrow,ncol);
432  // }
433  for(int irow=0;irow<nrow;++irow){
434  if(composite_opt[0]=="number")
435  continue;//outputData already set
436  Vector2d<double> outputProfile(nband,ncol);
437  for(int icol=0;icol<ncol;++icol){
438  std::vector<double> profile;
439  if(!inputData[irow][icol].size())
440  outputData[irow][icol]=(static_cast<double>((nodata_opt[0])));
441  else{
443  if(composite_opt[0]=="min")
444  outputData[irow][icol]=stat.mymin(inputData[irow][icol]);
445  else if(composite_opt[0]=="max")
446  outputData[irow][icol]=stat.mymax(inputData[irow][icol]);
447  else if(composite_opt[0]=="median")
448  outputData[irow][icol]=stat.median(inputData[irow][icol]);
449  else if(composite_opt[0]=="percentile")
450  outputData[irow][icol]=stat.percentile(inputData[irow][icol],inputData[irow][icol].begin(),inputData[irow][icol].end(),percentile_opt[0]);
451  else if(composite_opt[0]=="mean")
452  outputData[irow][icol]=stat.mean(inputData[irow][icol]);
453  else if(composite_opt[0]=="sum")
454  outputData[irow][icol]=stat.sum(inputData[irow][icol]);
455  else if(composite_opt[0]=="first")
456  outputData[irow][icol]=inputData[irow][icol][0];
457  else if(composite_opt[0]=="last")
458  outputData[irow][icol]=inputData[irow][icol].back();
459  else if(composite_opt[0]=="profile"){
460  if(inputData[irow][icol].size()<2){
461  for(int iband=0;iband<nband;++iband)
462  outputProfile[iband][icol]=static_cast<double>(nodata_opt[0]);
463  continue;
464  }
465  double min=0;
466  double max=0;
467  stat.minmax(inputData[irow][icol],inputData[irow][icol].begin(),inputData[irow][icol].end(),min,max);
468  if(verbose_opt[0])
469  std::cout << "min,max: " << min << "," << max << std::endl;
470  if(max>min){
471  stat.percentiles(inputData[irow][icol],inputData[irow][icol].begin(),inputData[irow][icol].end(),profile,nband,min,max);
472  assert(profile.size()==nband);
473  for(int iband=0;iband<nband;++iband)
474  outputProfile[iband][icol]=profile[iband];
475  }
476  else{
477  for(int iband=0;iband<nband;++iband)
478  outputProfile[iband][icol]=max;
479  }
480  }
481  else{
482  std::cout << "Error: composite_opt " << composite_opt[0] << " not supported" << std::endl;
483  exit(2);
484  }
485  }
486  }
487  if(composite_opt[0]=="profile"){
488  for(int iband=0;iband<nband;++iband){
489  // assert(outputProfile[iband].size()==outputWriter.nrOfRow());
490  assert(outputProfile[iband].size()==outputWriter.nrOfCol());
491  try{
492  outputWriter.writeData(outputProfile[iband],GDT_Float64,irow,iband);
493  }
494  catch(std::string errorString){
495  cout << errorString << endl;
496  exit(1);
497  }
498  }
499  }
500  progress=static_cast<float>(irow)/outputWriter.nrOfRow();
501  pfnProgress(progress,pszMessage,pProgressArg);
502  }
503  progress=1;
504  pfnProgress(progress,pszMessage,pProgressArg);
505  inputData.clear();//clean up memory
506  //apply post filter
507  // std::cout << "Applying post processing filter: " << postFilter_opt[0] << std::endl;
508  // if(postFilter_opt[0]=="etew_min"){
509  // if(composite_opt[0]!="min")
510  // std::cout << "Warning: composite option is not set to min!" << std::endl;
511  // //Elevation Threshold with Expand Window (ETEW) Filter (p.73 frmo Airborne LIDAR Data Processing and Analysis Tools ALDPAT 1.0)
512  // //first iteration is performed assuming only minima are selected using options -fir all -comp min
513  // unsigned long int nchange=1;
514  // //increase cells and thresholds until no points from the previous iteration are discarded.
515  // int dimx=dimx_opt[0];
516  // int dimy=dimy_opt[0];
517  // filter2d::Filter2d morphFilter;
518  // // morphFilter.setNoValue(0);
519  // Vector2d<float> currentOutput=outputData;
520  // int iteration=1;
521  // while(nchange&&iteration<=maxIter_opt[0]){
522  // double hThreshold=maxSlope_opt[0]*dimx;
523  // Vector2d<float> newOutput;
524  // nchange=morphFilter.morphology(currentOutput,newOutput,"erode",dimx,dimy,disc_opt[0],hThreshold);
525  // currentOutput=newOutput;
526  // dimx+=2;//change from theory: originally double cellCize
527  // dimy+=2;//change from theory: originally double cellCize
528  // std::cout << "iteration " << iteration << ": " << nchange << " pixels changed" << std::endl;
529  // ++iteration;
530  // }
531  // outputData=currentOutput;
532  // }
533  // else if(postFilter_opt[0]=="promorph"||postFilter_opt[0]=="bunting"){
534  // if(composite_opt[0]!="min")
535  // std::cout << "Warning: composite option is not set to min!" << std::endl;
536  // assert(hThreshold_opt.size()>1);
537  // //Progressive morphological filter tgrs2003_zhang vol41 pp 872-882
538  // //first iteration is performed assuming only minima are selected using options -fir all -comp min
539  // //increase cells and thresholds until no points from the previous iteration are discarded.
540  // int dimx=dimx_opt[0];
541  // int dimy=dimy_opt[0];
542  // filter2d::Filter2d theFilter;
543  // // theFilter.setNoValue(0);
544  // Vector2d<float> currentOutput=outputData;
545  // double hThreshold=hThreshold_opt[0];
546  // int iteration=1;
547  // while(iteration<=maxIter_opt[0]){
548  // std::cout << "iteration " << iteration << " with window size " << dimx << " and dh_max: " << hThreshold << std::endl;
549  // Vector2d<float> newOutput;
550  // try{
551  // theFilter.morphology(outputData,currentOutput,"erode",dimx,dimy,disc_opt[0],hThreshold);
552  // theFilter.morphology(currentOutput,outputData,"dilate",dimx,dimy,disc_opt[0],hThreshold);
553  // if(postFilter_opt[0]=="bunting"){
554  // theFilter.doit(outputData,currentOutput,"median",dimx,dimy,1,disc_opt[0]);
555  // outputData=currentOutput;
556  // }
557  // }
558  // catch(std::string errorString){
559  // cout << errorString << endl;
560  // exit(1);
561  // }
562  // int newdimx=(dimx==1)? 3: 2*(dimx-1)+1;
563  // int newdimy=(dimx==1)? 3: 2*(dimy-1)+1;//from PE&RS vol 71 pp313-324
564  // hThreshold=hThreshold_opt[0]+maxSlope_opt[0]*(newdimx-dimx)*dx_opt[0];
565  // dimx=newdimx;
566  // dimy=newdimy;
567  // if(hThreshold>hThreshold_opt[1])
568  // hThreshold=hThreshold_opt[1];
569  // ++iteration;
570  // }
571  // outputData=currentOutput;
572  // }
573  // else if(postFilter_opt[0]=="open"){
574  // if(composite_opt[0]!="min")
575  // std::cout << "Warning: composite option is not set to min!" << std::endl;
576  // filter2d::Filter2d morphFilter;
577  // // morphFilter.setNoValue(0);
578  // Vector2d<float> filterInput=outputData;
579  // try{
580  // morphFilter.morphology(outputData,filterInput,"erode",dimx_opt[0],dimy_opt[0],disc_opt[0],maxSlope_opt[0]);
581  // morphFilter.morphology(filterInput,outputData,"dilate",dimx_opt[0],dimy_opt[0],disc_opt[0],maxSlope_opt[0]);
582  // }
583  // catch(std::string errorString){
584  // cout << errorString << endl;
585  // exit(1);
586  // }
587  // }
588  // else if(postFilter_opt[0]=="close"){
589  // if(composite_opt[0]!="max")
590  // std::cout << "Warning: composite option is not set to max!" << std::endl;
591  // filter2d::Filter2d morphFilter;
592  // // morphFilter.setNoValue(0);
593  // Vector2d<float> filterInput=outputData;
594  // try{
595  // morphFilter.morphology(outputData,filterInput,"dilate",dimx_opt[0],dimy_opt[0],disc_opt[0],maxSlope_opt[0]);
596  // morphFilter.morphology(filterInput,outputData,"erode",dimx_opt[0],dimy_opt[0],disc_opt[0],maxSlope_opt[0]);
597  // }
598  // catch(std::string errorString){
599  // cout << errorString << endl;
600  // exit(1);
601  // }
602  // }
603  if(composite_opt[0]!="profile"){
604  //write output file
605  std::cout << "writing output raster file" << std::endl;
606  progress=0;
607  pfnProgress(progress,pszMessage,pProgressArg);
608  for(int irow=0;irow<nrow;++irow){
609  try{
610  assert(outputData.size()==outputWriter.nrOfRow());
611  assert(outputData[0].size()==outputWriter.nrOfCol());
612  outputWriter.writeData(outputData[irow],GDT_Float64,irow,0);
613  }
614  catch(std::string errorString){
615  cout << errorString << endl;
616  exit(1);
617  }
618  progress=static_cast<float>(irow)/outputWriter.nrOfRow();
619  pfnProgress(progress,pszMessage,pProgressArg);
620  }
621  }
622  progress=1;
623  pfnProgress(progress,pszMessage,pProgressArg);
624  if(verbose_opt[0])
625  std::cout << "closing lasReader" << std::endl;
626  outputWriter.close();
627 }