OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimEquationUtil.cpp
Go to the documentation of this file.
1 //**************************************************************************************************
2 //
3 // OSSIM Open Source Geospatial Data Processing Library
4 // See top level LICENSE.txt file for license information
5 //
6 //**************************************************************************************************
7 
8 #include <iostream>
9 #include <sstream>
10 #include <fstream>
11 using namespace std;
12 
18 #include <ossim/base/ossimString.h>
25 #include <ossim/init/ossimInit.h>
27 #include <ossim/base/ossimTrace.h>
28 #include <ossim/base/ossimRefPtr.h>
29 #include <ossim/base/ossimCommon.h>
31 #include <ossim/base/ossimRtti.h>
32 
33 static ossimTrace traceDebug(ossimString("mosaic:main"));
34 
35 // Copied from ossimEquationCombiner.h:
36 static const char* EQ_SPEC = "\nEquation Specification:\n\
37 \n\
38 sin(x) takes the sine of the input \n\
39 sind(x) takes the sin of the input and assumes degree input \n\
40 cos(x) takes cosine of input \n\
41 cosd(x) takes the cosine of input and assumes input in degrees \n\
42 sqrt(x) takes square root of input \n\
43 log(x) takes the natural log of input \n\
44 log10(x) takes the log base 10 of the input \n\
45 exp(x) takes the e raised to the passed in argument \n\
46 abs(x) takes the absolute value of the passed in value \n\
47 min(x1, x2, ... xn) takes the min of all values in the list \n\
48 max(x1, x2, ... xn) takes the max of all values in the list. \n\
49 \n\
50 clamp(image_data, min, max) \n\
51  will clamp all data to be between the min max values. \n\
52  will set anything less than min to min and anythin \n\
53  larger than max to max \n\
54 \n\
55 band(image_data, num) returns a single band image object \n\
56  by selecting band num from input image i1. Note \n\
57  the first argument must be an image \n\
58  and the second argument must be a number \n\
59 \n\
60 shift(index, num_x, num_y) \n\
61  currently, the first argument must be an image \n\
62  variable(i1, i2, ... in) and x, and y must b numbers \n\
63  indicating the delta in that direction to shift the \n\
64  input. \n\
65 \n\
66 blurr(index, rows, cols) \n\
67  Will blurr the input image i with a \n\
68  rows-by-cols kernel. All values are equal \n\
69  weight. Note the fist argument must by an image \n\
70  variable (ex: i1, i2,....in). \n\
71 \n\
72 conv(index, rows, cols, <row ordered list of values> ) \n\
73  this allows you to define an arbitrary matrix. The \n\
74  <row ordered list of values> is a comma separated \n\
75  list of constant values. \n\
76 \n\
77 assign_band(image_data, num1, data2, num2) \n\
78  will take band num2 from image data2 and assign it to \n\
79  band num1 in data 1. \n\
80 \n\
81 assign_band(image_data, num1, data2) \n\
82  will take band 1 from image data2 and assign it to \n\
83  band num1 in data 1. \n\
84 \n\
85 assign_band(image_data, num1, num2) \n\
86  will assin to band num1 of data 1 the value of num2 \n\
87 \n\
88 x1 * x2 will multiply x1 and x2 \n\
89 x1 + x2 will add x1 and x2 \n\
90 x1 - x2 will subtract x1 and x2 \n\
91 x1 / x2 will divide x1 and x2 \n\
92 x1 ^ x2 will do a power, raises x1 to x2 \n\
93 x1 | x2 will do a bitwise or operation \n\
94  ( will do it in unisgned char precision) \n\
95 \n\
96 x1 & x2 will do a bitwise and operation \n\
97  ( will do it in unsigned char precision) \n\
98 \n\
99 ~x1 will do the ones complement of the input \n\
100 \n\
101 x1 xor x2 will do an xclusive or operation \n\
102  (will do it in unsigned char precision) \n\
103 \n\
104 - x1 will negative of x1 \n\
105 \n\
106 Boolean ops: 1=true, 0=false \n\
107 x1 > x2 \n\
108 x1 >= x2 \n\
109 x1 == x2 \n\
110 x1 <= x2 \n\
111 x1 < x2 \n\
112 x1 <> x2 \n\
113 \n\
114 Note: \n\
115 \n\
116 Currently an image input is reference by the variable in[<I>] where \n\
117 <I> is the input image index beginning at 0. So 1 referes to the second image \n\
118 in the input list. \n\
119 \n\
120 (in[0] + in[1])/2 \n\
121 Will take image 1 and add it to image 2 and average them. \n\
122 \n\
123 exp(sqrt(in[0])/4) \n\
124 Will take the root of the image and divide by 4 and then raise e to that \n\
125 amount. \n\
126 \n\
127 128 \n\
128 Will return a constant value of 128 for all input bands. \n\
129 \n\
130 min(1,in[2],in[3], max(in[1],in[0])) \n\
131 \n\
132 shift(0, 1, 1) - i1 \n\
133 Will shift input 0 by 1 pixel along the diagonal and then subtract it \n\
134 from input 1. \n\
135 \n\
136 assign_band(in[0], 1, blurr(in[0], 5, 5), 2) \n\
137 Will assign to the first band of i1 the 2nd band of the 5x5 blurr of i1. \n\
138 \n\
139 conv(0, 3, 3, -1, -2, -1, 0, 0, 0, 1, 2, 1) \n\
140 Will convolve the first input connection with a 3x3 matrix. \n\
141 The args are row ordered: \n\
142  -1, -2, -1 \n\
143  0, 0, 0 \n\
144  1, 2, 1 \n\
145 \n\
146 NDVI: \n\
147 N=(in[0]-in[1])/(in[0]+in[1]) \n\
148 \n\
149 For indexed-type values,like NDVI, (with limited values) it is better \n\
150 to rescale between 0.0 and 1.0 and use type NormalizedFloat. \n\
151 \n\
152 Rescaled NDVI between 0 and 1: \n\
153 (N+1)/2 = in[0]/(in[0]+in[1]) \n\
154 \n";
155 
157 : m_argumentParser(new ossimArgumentParser(ap))
158 {
161  " Takes a list of images and performs the specified equation. The inputs"
162  "must be in the same projection as the operations are done at the pixel"
163  "level with no renderer in th chain. The output file will contain the same"
164  "geospatial header info as the first image in the input list.");
166  " \"<equation spec>\" <input_file1> <input_file2> <input_file...> <output_file>");
167  m_usage.addCommandLineOption("-h or --help","Display this information");
168  m_usage.addCommandLineOption("-k <filename>", "keyword list to load from");
169  m_usage.addCommandLineOption("-w <type>", "Writer type (tiff_strip, jpeg, etc... see ossim-info --writers) (default=tiff_strip)");
170  m_usage.addCommandLineOption("--writer-prop <string>", "Adds a property to send to the writer. format is name=value");
171  m_usage.addCommandLineOption("-t <filename>", "output a keyword list template");
172 }
173 
175 {
176  // Allocate some stuff up front for use later
177  ossimString writerType = "tiff_strip";
178  ossimString equationSpec;
179  std::string tempString;
180  ossimArgumentParser::ossimParameter stringParam(tempString);
181  std::map<ossimString, ossimString, ossimStringLtstr> writerPropertyMap;
182  vector<ossimFilename> infiles;
183  ossimFilename outfile;
184 
185  // Display Help
186  if (m_argumentParser->read("-h") || m_argumentParser->read("--help") || (m_argumentParser->argc() < 4))
187  {
189  ossimNotify(ossimNotifyLevel_INFO)<<EQ_SPEC<<endl;
190  return false;
191  }
192 
193  // Output KWL template
194  if (m_argumentParser->read("-t", stringParam))
195  {
196  ossimFilename templateFilename = tempString.c_str();
197  outputTemplateKeywordlist(templateFilename);
198  return false;
199  }
200 
201  // Keyword list to load from
202  if (m_argumentParser->read("-k", stringParam))
203  {
204  if (!parseKwl(tempString.c_str(), infiles, equationSpec, outfile, writerType))
205  return false;
206  }
207  else
208  {
209  // Everything on command line:
210  // user input writer props, should use those
211  while(m_argumentParser->read("--writer-prop", stringParam))
212  {
213  std::vector<ossimString> nameValue;
214  ossimString(tempString).split(nameValue, "=");
215  if(nameValue.size() == 2)
216  writerPropertyMap.insert(std::make_pair(nameValue[0], nameValue[1]));
217  }
218 
219  // User input a writer type
220  if (m_argumentParser->read("-w", stringParam))
221  writerType = tempString;
222 
223  int argCount = m_argumentParser->argc();
224  if (argCount < 4)
225  {
227  ossimNotify(ossimNotifyLevel_INFO)<<EQ_SPEC<<endl;
228  return false;
229  }
230 
231  // First the equation spec:
232  equationSpec = m_argumentParser->argv()[1];
233 
234  // Get the input files.
235  for (int i=2; i< (argCount-1); ++i)
236  infiles.push_back(ossimFilename(m_argumentParser->argv()[i]));
237 
238  // Get the output file.
239  outfile = m_argumentParser->argv()[argCount-1];
240  }
241 
242  if (!initInputSources(infiles))
243  return false;
244 
245  // Create combiner object
247  combiner->setEquation(equationSpec);
248 
249  // Create writer:
252  if(!writer.valid())
253  {
254  ossimNotify(ossimNotifyLevel_FATAL)<<"Could not create writer of type <"<<writerType<<">"<<endl;
255  return false;
256  }
257  writer->connectMyInputTo(combiner.get());
258  writer->setFilename(outfile);
259  ossimIrect bounding_irect;
260  m_prodGeometry->getBoundingRect(bounding_irect);
261  writer->setAreaOfInterest(bounding_irect);
262  writer->initialize();
263 
264  if ( writerPropertyMap.size() )
265  {
266  ossimPropertyInterface* propInterface = (ossimPropertyInterface*) writer.get();
267  std::map<ossimString, ossimString, ossimStringLtstr>::iterator iter = writerPropertyMap.begin();
268  while(iter!=writerPropertyMap.end())
269  {
270  propInterface->setProperty(iter->first, iter->second);
271  ++iter;
272  }
273  }
274 
275  // Execute:
276  ossimStdOutProgress listener;
277  writer->addListener(&listener);
278  if (!writer->execute())
279  {
280  ossimNotify(ossimNotifyLevel_WARN)<< "Error encountered writing file..."<<endl;
281  return false;
282  }
283 
284  writer->close();
285  return true;
286 }
287 
289 {
290  ofstream out(templateFilename.c_str());
291 
292  out << "file1.filename: <full path and file name>" << endl
293  << "file2.filename: <full path and file name>" << endl
294  << "// :\n"
295  << "// :\n"
296  << "// fileN.filename:: <full path and file name>" << endl
297  << "\n// currently this option has been tested\n"
298  << "// with ossimTiffWriter and ossimJpegWriter\n"
299  << "writer.type: tiff_strip" << endl
300  << "writer.filename: <full path to output file>" << endl
301  << "\n// Equation specification:\n"
302  << "equation: <equation spec string>"<<endl;
303 
304  ossimNotify(ossimNotifyLevel_NOTICE) << "Wrote file: " << templateFilename << std::endl;
305 }
306 
308  vector<ossimFilename>& inputs,
309  ossimString& equationSpec,
310  ossimFilename& output,
311  ossimString& writerType)
312 {
313  ossimKeywordlist kwl;
314  if (!kwl.addFile(kwl_file))
315  return false;
316 
317  ossim_int32 index = 0;
318  ossim_int32 result = kwl.getNumberOfSubstringKeys("file[0-9]+\\.filename");
319  const char* lookup = NULL;
320  ossim_int32 numberOfMatches = 0;
321 
322  while(numberOfMatches < result)
323  {
324  ossimString searchValue = "file" + ossimString::toString(index);
325 
326  ossimString filename = searchValue + ".filename";
327  lookup = kwl.find(filename.c_str());
328  if(lookup)
329  {
330  inputs.push_back(ossimFilename(lookup));
331  ++numberOfMatches;
332  }
333  ++index;
334  }
335 
336  equationSpec = kwl.find("equation");
337  if (equationSpec.empty())
338  {
339  ossimNotify(ossimNotifyLevel_FATAL)<<"No equation specified in KWL"<<endl;
340  return false;
341  }
342 
343  output = kwl.find("writer.filename");
344  if (output.empty())
345  {
346  ossimNotify(ossimNotifyLevel_FATAL)<<"No output filename specified in KWL"<<endl;
347  return false;
348  }
349 
350  writerType = kwl.find("writer.type");
351  if (writerType.empty())
352  writerType = "tiff_strip";
353 
354  return true;
355 }
356 
357 bool ossimEquationUtil::initInputSources(vector<ossimFilename>& fileList)
358 {
359  bool result = true;
360  m_inputSources.clear();
362 
363  for(ossim_int32 index = 0; index < (ossim_int32)fileList.size();++index)
364  {
366  ossimImageHandlerRegistry::instance()->open(fileList[index]);
367  if(!handler.valid())
368  {
369  cerr << "Error: Unable to load image " << fileList[index] << endl;
370  result = false;
371  break;
372  }
373 
374  input_geom = handler->getImageGeometry();
375 
376  if (index == 0)
377  {
378  m_prodGeometry = (ossimImageGeometry*) input_geom->dup();
379  ossimGpt origin;
380  m_prodGeometry->getTiePoint(origin, false);
382 
384  {
385  // Need to ortho to UTM, so define output geometry shared by all input chains:
386  ossimMapProjection* prod_proj = new ossimUtmProjection;
387  prod_proj->setOrigin(origin);
389  m_prodGeometry->setProjection(prod_proj);
390  }
391  }
392 
393  // Set up the IVT for this input's renderer:
395  transform->setImageGeometry(input_geom.get());
396  transform->setViewGeometry(m_prodGeometry.get());
397 
399  renderer->connectMyInputTo(0, handler.get());
400  renderer->setImageViewTransform(transform.get());
401  renderer->initialize();
402 
403  m_inputSources.push_back(renderer.get());
404  }
405 
406  return result;
407 }
408 
void write(std::ostream &output, const UsageMap &um, unsigned int widthOfOutput=80)
std::string getApplicationName() const
return the application name, as specified by argv[0]
void addCommandLineOption(const ossimString &option, const ossimString &explanation)
void setProjection(ossimProjection *projection)
Sets the projection to be used for local-to-world coordinate transformation.
ossimConnectableObject::ConnectableObjectList m_inputSources
bool isAffectedByElevation() const
Returns TRUE if this geometry is sensitive to elevation.
virtual void close()=0
ossim_uint32 getNumberOfSubstringKeys(const ossimString &regularExpression) const
ossimApplicationUsage m_usage
virtual ossimImageHandler * open(const ossimFilename &fileName, bool trySuffixFirst=true, bool openOverview=true) const
open that takes a filename.
Represents serializable keyword/value map.
bool addFile(const char *file)
bool valid() const
Definition: ossimRefPtr.h:75
const char * find(const char *key) const
virtual bool addListener(ossimListener *listener)
Overrides base "addListener" this will capture the pointer and then call the base class "addListener"...
bool read(const std::string &str)
search for an occurance of a string in the argument list, on sucess remove that occurance from the li...
void getBoundingRect(ossimIrect &bounding_rect) const
Get the bounding rect of (0, 0) to (imageSize.x-1, imageSize.y-1).
virtual void setOrigin(const ossimGpt &origin)
Sets theOrigin to origin.
Will combine the input data based on a supplied equation.
static ossimImageWriterFactoryRegistry * instance()
static ossimString toString(bool aValue)
Numeric to string methods.
void split(std::vector< ossimString > &result, const ossimString &separatorList, bool skipBlankFields=false) const
Splits this string into a vector of strings (fields) using the delimiter list specified.
void setCommandLineUsage(const ossimString &explanation)
ossimRefPtr< ossimImageGeometry > m_prodGeometry
void setViewGeometry(ossimImageGeometry *g)
Assigns the geometry to use for output view. This object does NOT own the geometry.
virtual void setEquation(const ossimString &equ)
void setImageViewTransform(ossimImageViewTransform *transform)
virtual void setMetersPerPixel(const ossimDpt &gsd)
ossimArgumentParser * m_argumentParser
virtual ossimRefPtr< ossimImageGeometry > getImageGeometry()
Returns the image geometry object associated with this tile source or NULL if non defined...
virtual bool execute()
Returns true if successful.
ossimDpt getMetersPerPixel() const
Returns the GSD associated with this image in the active projection.
void setApplicationName(const ossimString &name)
virtual ossim_int32 connectMyInputTo(ossimConnectableObject *inputObject, bool makeOutputConnection=true, bool createEventFlag=true)
Will try to connect this objects input to the passed in object.
Container class that holds both 2D transform and 3D projection information for an image Only one inst...
virtual void setFilename(const ossimFilename &file)
void getTiePoint(ossimGpt &tie, bool edge) const
Get the latitude, longitude of the tie point.
bool parseKwl(const ossimFilename &kwl_file, std::vector< ossimFilename > &inputs, ossimString &equationSpec, ossimFilename &output, ossimString &writerType)
virtual void initialize()
Initialize method.
void setDescription(const ossimString &desc)
virtual void setProperty(const ossimString &name, const ossimString &value)
virtual void setAreaOfInterest(const ossimIrect &inputRect)
const char * c_str() const
Returns a pointer to a null-terminated array of characters representing the string&#39;s contents...
Definition: ossimString.h:396
bool empty() const
Definition: ossimString.h:411
bool initInputSources(std::vector< ossimFilename > &fileList)
ossimImageFileWriter * createWriter(const ossimFilename &filename) const
static ossimImageHandlerRegistry * instance()
std::basic_ofstream< char > ofstream
Class for char output file streams.
Definition: ossimIosFwd.h:47
char ** argv()
return the argument array.
virtual ossimObject * dup() const
Creates a new instance of ossimImageGeometry with the same transform and projection.
void outputTemplateKeywordlist(const ossimFilename &templateFilename)
int & argc()
return the argument count.
OSSIMDLLEXPORT std::ostream & ossimNotify(ossimNotifyLevel level=ossimNotifyLevel_WARN)
virtual bool execute()
Calls: writeFile() writeMetaDataFiles()
void setImageGeometry(ossimImageGeometry *g)
Assigns the geometry to use for input image. This object does NOT own the geometry.
int ossim_int32