OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimCorrelationSource.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 
9 #include "AtpOpenCV.h"
10 #include "AtpGenerator.h"
12 #include "atp/AtpCommon.h"
13 
14 namespace ATP
15 {
16 static const int DEFAULT_CMP_PATCH_SIZING_FACTOR = 2;
17 static const double DEFAULT_NOMINAL_POSITION_ERROR = 50; // meters
18 
20 : m_nominalCmpPatchSize(0)
21 {
22  // PRIVATE, SHOULD NEVER BE CALLED. WOULD NEED TO CALL INITIALIZE
23 }
24 
26 : AtpTileSource(inputSources),
27  m_nominalCmpPatchSize(0)
28 {
29  initialize();
30 }
31 
33 : AtpTileSource(generator),
34  m_nominalCmpPatchSize(0)
35 {
36  // PRIVATE, SHOULD NEVER BE CALLED. WOULD NEED TO CALL INITIALIZE
37 }
38 
40 {
41 }
42 
44 {
45  // The base class sets up the input (image-space) protion of the chain:
47 
48  unsigned int refPatchSize = AtpConfig::instance().getParameter("corrWindowSize").asUint(); // min distance between points
49  m_nominalCmpPatchSize = DEFAULT_CMP_PATCH_SIZING_FACTOR*refPatchSize;
50  double total_error = 0;
51  auto refIVT = m_generator->getRefIVT();
52  auto cmpIVT = m_generator->getCmpIVT();
53  if (refIVT.valid() && cmpIVT.valid())
54  {
55  ossimDpt gsd = refIVT->getOutputMetersPerPixel();
56  double nominalGsd = (gsd.x + gsd.y)/2.0;
57 
58  // Fetch the CE90 of both images to determine cmp image search patch size:
59  ossimSensorModel* ref_sm = dynamic_cast<ossimSensorModel*>(
60  refIVT->getImageGeometry()->getProjection());
61  ossimSensorModel* cmp_sm = dynamic_cast<ossimSensorModel*>(
62  cmpIVT->getImageGeometry()->getProjection());
63  if (ref_sm && cmp_sm)
64  {
65  double ref_ce = ref_sm->getNominalPosError();
66  double cmp_ce = cmp_sm->getNominalPosError();
67  total_error = ref_ce + cmp_ce; // meters -- being conservative here and not doing rss
68  double searchSizePixels = 2.0 * total_error / nominalGsd;
69  m_nominalCmpPatchSize = refPatchSize + searchSizePixels;
70  }
71  if (total_error == 0)
72  {
73  double searchSizePixels = 2.0*DEFAULT_NOMINAL_POSITION_ERROR/nominalGsd;
74  m_nominalCmpPatchSize = refPatchSize + searchSizePixels;
75  }
76  }
77 }
78 
80  ossim_uint32 resLevel)
81 {
82  static const char* MODULE = "ossimCorrelationSource::getTile() ";
83  AtpConfig& config = AtpConfig::instance();
84  if (config.diagnosticLevel(2))
85  CINFO<<"\n\nossimCorrelationSource::getTile() -- tileRect UL: "<<tileRect.ul()<<endl;
86  m_tiePoints.clear();
87 
88  if(getNumberOfInputs() < 2)
89  return 0;
90  if (!m_tile)
91  allocate();
92 
93  // The tile rect (in view space) is referenced by the tiepoint filtering code:
94  m_tile->setImageRectangle(tileRect);
95 
96  string sid(""); // Leave blank to have it auto-assigned by AutoTiePoint constructor
97  if (!config.getParameter("useRasterMode").asBool())
98  {
99  m_tile = m_generator->getRefChain()->getTile(tileRect, resLevel);
101  return m_tile;
102 
103  // Need to search for features in the REF tile first, then consider only those locations
104  // for correlating:
105  vector<ossimIpt> featurePoints;
107 
108  findFeatures(tileptr.get(), featurePoints);
109  if (featurePoints.empty())
110  return m_tile;
111 
112  // Loop over features to perform correlations:
113  for (size_t i=0; i<featurePoints.size(); ++i)
114  {
115  shared_ptr<ATP::AutoTiePoint> atp (new ATP::AutoTiePoint(m_generator, sid));
116  atp->setRefViewPt(featurePoints[i]);
117 
118  // Trick here to keep using the same ID until a good correlation is found:
119  if (!correlate(atp))
120  {
121  sid = atp->getTiePointId();
122  //atp->setTiePointId("NP");
123  //m_tiePoints.push_back(atp);
124  }
125  else
126  {
127  m_tiePoints.push_back(atp);
128  sid.clear();
129  }
130  }
131  if (config.diagnosticLevel(2))
132  CINFO<<MODULE<<"Before filtering: num matches in tile = "<<m_tiePoints.size()<<endl;
133  }
134  else // raster mode
135  {
136  // Scan raster-fashion along REF tile and correlate at intervals:
137  unsigned int num_attempts = 0;
138  int corrStepSize = 1;
139  if (config.paramExists("corrStepSize"))
140  corrStepSize = (int) config.getParameter("corrStepSize").asUint();
141  for (int y = tileRect.ul().y; y < tileRect.lr().y; y += corrStepSize)
142  {
143  for (int x = tileRect.ul().x; x < tileRect.lr().x; x += corrStepSize)
144  {
145  ++num_attempts;
146  shared_ptr<AutoTiePoint> atp (new AutoTiePoint(m_generator, sid));
147  atp->setRefViewPt(ossimDpt(x,y));
148 
149  // Trick here to keep using the same ID until a good correlation is found:
150  if (!correlate(atp))
151  sid = atp->getTiePointId();
152  else
153  {
154  m_tiePoints.push_back(atp);
155  sid.clear();
156  }
157  }
158  }
159  if (config.diagnosticLevel(2))
160  CINFO<<MODULE<<"Tile match ratio: "<<m_tiePoints.size()<<" / "<<num_attempts<<endl;
161 
162  }
163 
164  filterPoints();
165 
166  return m_tile;
167 }
168 
170  vector<ossimIpt>& feature_points)
171 {
172  const char* MODULE = "ossimCorrelationSource::findFeatures() -- ";
173 
174  feature_points.clear();
175  if (!imageChip || (imageChip->getDataObjectStatus() == OSSIM_EMPTY))
176  {
177  CWARN<<MODULE<<"WARNING: NULL or empty image data passed in."<<endl;
178  return;
179  }
180 
181  // THE FOLLOWING ARE CRITICAL VALUES AND MAYBE SHOULD BE READ FROM KWL:
182  AtpConfig& config = AtpConfig::instance();
183  double refPatchSize = (double) config.getParameter("corrWindowSize").asUint(); // min distance between points
184  static const double quality = config.getParameter("minFeatureStrength").asFloat();
185  static const int use_harris= (int) config.getParameter("useHarrisCorner").asBool();
186  static const double harris_k = config.getParameter("harrisKFactor").asFloat();
187  const int averaging_block_size = (int) refPatchSize;
188 
189 // imageChip->write("imageChip.ras");//### TODO REMOVE
190  IplImage *ipl_image = convertToIpl(imageChip);
191  if(!ipl_image)
192  {
193  CFATAL<<MODULE << "ERROR: Got null IplImage pointer converting from ossimImageData*!";
194  return;
195  }
196 // cvNamedWindow("ipl_image", 1); //###
197 // cvShowImage("ipl_image", ipl_image); //###
198 
199  // Need to mask pixels outside of intersection:
200  ossimIpt tileUL (imageChip->getImageRectangle().ul());
201  IplImage* mask = 0;
202  double null_pixel = imageChip->getNullPix(0);
203  if (imageChip->getDataObjectStatus() == OSSIM_PARTIAL)
204  {
205  CvSize bufsize = cvGetSize(ipl_image);
206  mask = cvCreateImage(bufsize, IPL_DEPTH_8U, 1 );
207  ossimDpt pt;
208  long offset = 0;
209  for (int y=0; y<bufsize.height; y++)
210  {
211  for (int x=0; x<=bufsize.width-1; x++)
212  {
213  if (imageChip->getPix(offset) != null_pixel)
214  mask->imageData[offset] = (unsigned char) 0xFF;
215  else
216  mask->imageData[offset] = (unsigned char) 0;
217  ++offset;
218  }
219  }
220  }
221 
222  // Use CV to find features in the patch:
223  // Adjust the number of features to look for if we are doing adaptive correlation thresholding
224  // (ACT). This would be more appropriate to call "secondary feature consideration":
225 // IplImage* harris = cvCreateImage(cvGetSize(ipl_image), 32, 1 );
226 // cvCornerHarris(ipl_image, harris, 10, 5);
227 // cvNamedWindow("Harris", CV_WINDOW_AUTOSIZE);
228 // cvShowImage ("Harris", harris);
229 // cvWaitKey();
230 
231  int num_pts = (int) config.getParameter("numFeaturesPerTile").asUint();
232  CvPoint2D32f* points = (CvPoint2D32f*)cvAlloc(num_pts*sizeof(CvPoint2D32f));
233  cvGoodFeaturesToTrack(ipl_image, 0, 0, points, &num_pts, quality, refPatchSize, mask,
234  averaging_block_size, use_harris, harris_k);
235 
236  // Loop over all features found in this patch and assign a feature point at that view
237  // point. IMPORTANT NOTE: it is assumed that the ordering of the points[] array is by
238  // descending strength of feature.
239  unsigned int num_added = 0;
240  for(int i=0; i<num_pts; i++)
241  {
242  // Correct point for the view-space offset of the search patch since points are relative to
243  // patch upper left:
244  CvPoint pt=cvPointFrom32f(points[i]);
245  ossimDpt viewPt (pt.x, pt.y);
246  viewPt += tileUL;
247 
248  // TODO REMOVE
249 // ossimDpt imagePt;
250 // m_generator->getRefIVT()->viewToImage(viewPt, imagePt);
251 // CINFO<<" Feature REF image point: "<<imagePt<<endl;
252 
253  // Good feature found, set the feature point's attributes and save to list:
254  feature_points.push_back(viewPt);
255  ++num_added;
256  }
257 
258  if (AtpConfig::instance().diagnosticLevel(2))
259  {
260  if (num_added)
261  CINFO<<MODULE<<"Number of features found in search patch: "<<num_added<<endl;
262  else
263  CINFO<<MODULE<<"No features found."<<endl;
264  }
265 
266  // Cleanup mem use:
267 // cvReleaseImage(&harris);
268  cvReleaseImage(&mask);
269  cvReleaseImage(&ipl_image);
270  cvFree((void**)&points);
271 
272  return;
273 }
274 
275 bool ossimCorrelationSource::correlate(shared_ptr<AutoTiePoint> atp)
276 {
277  // This method returns TRUE if a match was found
278 
279  const ossimString MODULE("ossimCorrelationSource::correlate() -- ");
280  AtpConfig& config = AtpConfig::instance();
281 
282  // Establish the REF patch rect centered on the viewpoint and the CMP patch rect sized by image
283  // error and offset by prior residuals:
284  unsigned int refPatchSize = AtpConfig::instance().getParameter("corrWindowSize").asUint();
285  ossimDpt refViewPt;
286  atp->getRefViewPoint(refViewPt);
287  ossimIrect refPatchRect(refViewPt, refPatchSize, refPatchSize);
288  ossimIrect cmpPatchRect(refViewPt, m_nominalCmpPatchSize, m_nominalCmpPatchSize);
289 
290  // Grab the image patches from the cmp and ref images.
291  ossimRefPtr<ossimImageData> refpatch (m_generator->getRefChain()->getTile(refPatchRect, 0));
292  if (!refpatch.valid())
293  {
294  CWARN << MODULE << "Error getting ref patch image data." << endl;
295  return false;
296  }
297 
298  if (config.diagnosticLevel(5))
299  {
300  ossimDpt refImgPt, cmpImgPt;
301  m_generator->getRefIVT()->viewToImage(refViewPt, refImgPt);
302  m_generator->getCmpIVT()->viewToImage(refViewPt, cmpImgPt);
303  CINFO<<"\nFeature view pt: "<<refViewPt
304  <<"\n REF img pt: "<<refImgPt
305  <<"\n CMP img pt: "<<cmpImgPt<<endl;
306  refpatch->write("REF_PATCH.RAS");
307 
308  vector<double> min, max;
309  refpatch->computeMinMaxPix(min, max);
310  if (min.size() && max.size())
311  CINFO<<" REF min, max: "<<(int)min[0]<<", "<<(int)max[0]<<endl;
312  }
313 
314  ossimDataObjectStatus stat = refpatch->getDataObjectStatus();
315  if (stat != OSSIM_FULL)
316  {
317  if (config.diagnosticLevel(5))
318  CINFO<<MODULE << "REF patch contained a null pixel. Skipping this point..."<<endl;
319  return false;
320  }
321 
322  ossimRefPtr<ossimImageData> cmppatch (m_generator->getCmpChain()->getTile(cmpPatchRect, 0));
323  if (!cmppatch.valid())
324  {
325  CWARN << MODULE << "Error getting cmp patch image data." << endl;
326  return false;
327  }
328  if (cmppatch->getDataObjectStatus() != OSSIM_FULL)
329  {
330  if (config.diagnosticLevel(5))
331  CINFO<<MODULE << "CMP patch contained a null pixel. Skipping this point..."<<endl;
332  return false;
333  }
334 
335  if (config.diagnosticLevel(5))
336  {
337  cmppatch->write("CMP_PATCH.RAS");
338  vector<double> min, max;
339  cmppatch->computeMinMaxPix(min, max);
340  if (min.size() && max.size())
341  CINFO << " CMP min, max: " << (int) min[0] << ", " << (int) max[0]<< "\n" << endl;
342  }
343 
344  // Invoke the proper correlation code to arive at the peak collection for this TP:
345  bool corr_ok;
346  corr_ok = OpenCVCorrelation(atp, refpatch.get(), cmppatch.get());
347  int numMatches = atp->getNumMatches();
348 
349  // The peaks for the given patch pair have been established and stored in the TPs.
350  if (corr_ok)
351  {
352  if (config.diagnosticLevel(4))
353  {
354  if (numMatches)
355  {
356  double corr_value;
357  atp->getConfidenceMeasure(corr_value);
358  CINFO<<MODULE<<"Match found for TP "<<atp->getTiePointId()<<" = "<<corr_value<<endl;
359  }
360  else
361  CINFO<<MODULE<<"No match found for TP "<<atp->getTiePointId()<<endl;
362  }
363  }
364  else // Correlation problem occurred
365  {
366  CWARN << MODULE << "Error encountered during correlation. Aborting correlation." << endl;
367  return false;
368  }
369 
370  if (config.diagnosticLevel(5))
371  {
372  ossimDpt cmpimgpt,refimgpt,refviewpt,cmpviewpt;
373  ossimGpt refGpt;
374  atp->getRefImagePoint(refimgpt);
375  atp->getRefViewPoint(refviewpt);
376  atp->getCmpImagePoint(cmpimgpt);
377  atp->getCmpViewPoint(cmpviewpt);
378  atp->getRefGroundPoint(refGpt);
379  if(numMatches>0)
380  {
381  double val=0;
382  ossimDpt residual;
383  atp->getConfidenceMeasure(val);
384  atp->getVectorResidual(residual);
385  CINFO<<"AutoTiePoint: ("<<atp->getTiePointId()<<") has "<<numMatches<<" matches,\n"
386  << " best: (" << val << ") residual: " << residual <<"\n"
387  << " ref gpt: "<<refGpt<<"\n"
388  << " cmpimg center: "<<cmpimgpt<<"\n"
389  << " refimg center: "<<refimgpt<<"\n"
390  << " cmpview center: "<<cmpviewpt<<"\n"
391  << " refview center: "<<refviewpt<<endl;
392  CINFO<<endl;
393  }
394  else {
395  CINFO<<"AutoTiePoint: (" << atp->getTiePointId() << ") has no peaks.\n"
396  << " ref gpt: "<<refGpt<<"\n"
397  << " refimg center: "<<refimgpt<<"\n"
398  << " refview center: "<<refviewpt<<endl;
399  }
400  }
401 
402  if (numMatches)
403  return true;
404  return false;
405 }
406 
407 bool ossimCorrelationSource::OpenCVCorrelation(std::shared_ptr<AutoTiePoint> atp,
408  const ossimImageData* refpatch,
409  const ossimImageData* cmppatch)
410 {
411  static const char* MODULE = "ossimCorrelationSource::OpenCVCorrelation() -- ";
412 
413  int ref_patch_size = refpatch->getWidth();
414  int refPatchOffset = ref_patch_size / 2; // assumes square patch
415 
416  IplImage *cmpimage, *refimage, *result;
417  int method;
418 
419  // Convert image data to CV IPL form:
420  cmpimage = convertToIpl32(cmppatch);
421  refimage = convertToIpl32(refpatch);
422  if (!cmpimage || !refimage)
423  {
424  CWARN << MODULE << "Error encountered creating IPL image tiles. Aborting..." << endl;
425  return false;
426  }
427 
428  // Assign the peak threshold depending on whether it is same collect or diff collect:
429  AtpConfig& config = AtpConfig::instance();
430  double peak_threshold = config.getParameter("peakThreshold").asFloat();
431 
432  CvSize resdim;
433  resdim.height = cmpimage->height - refimage->height + 1;
434  resdim.width = cmpimage->width - refimage->width + 1;
435  result = cvCreateImage(resdim, IPL_DEPTH_32F, 1);
436  if (!result)
437  {
438  CWARN << MODULE << "Error encountered creating IPL image tile (result). Aborting..." << endl;
439  return false;
440  }
441 
442  // Do the actual correlation:
443  unsigned int corrMethod = config.getParameter("correlationMethod").asUint();
444  cvMatchTemplate(cmpimage, refimage, result, (int) corrMethod);
445 
446  if (config.diagnosticLevel(5) )
447  {
448  //refpatch->write("ref.ras");
449  //cmppatch->write("cmp.ras");
450  ossimRefPtr<ossimImageData> resultPatch =
451  ossimImageDataFactory::instance()->create(this, OSSIM_UINT8, 1, resdim.width, resdim.height);
452  resultPatch->initialize();
453  copyIpl32ToOid(result, resultPatch.get());
454  resultPatch->write("CORR.RAS");
455  }
456  ossimIpt cmpPeakLocV;
457  float corrCoef;
458 
459  // Search through all the peaks and the tiepoint object will track which is best.
460  multimap<float /* corr_coef */, ossimIpt /* loc */, greater<float> > peaks_map;
461  ossimIpt cmpPatchLocV (cmppatch->getImageRectangle().ul());
462  for (int dy = 0; dy < result->height; dy++)
463  {
464  for (int dx = 0; dx < result->width; dx++)
465  {
466  // Fetch the pixel's corr coef and insert in map to order by strongest peak:
467  corrCoef = ((float*) (result->imageData + result->widthStep * dy))[dx];
468  if (corrCoef >= peak_threshold)
469  {
470  cmpPeakLocV.x = cmpPatchLocV.x + refPatchOffset + dx;
471  cmpPeakLocV.y = cmpPatchLocV.y + refPatchOffset + dy;
472  peaks_map.insert(pair<float, ossimIpt>(corrCoef, cmpPeakLocV));
473  }
474  }
475  }
476 
477  if (peaks_map.size() > 0)
478  {
479  // The peaks are in order by greatest correlation value. Send the best to the CTP:
480  multimap<float, ossimIpt, greater<float> >::iterator peak = peaks_map.begin();
481  unsigned int maxNumPeaks = AtpConfig::instance().getParameter("maxNumMatchesPerFeature").asUint();
482  if (config.paramExists("maxNumPeaksPerFeature")) // support legacy keywords
483  maxNumPeaks = config.getParameter("maxNumPeaksPerFeature").asUint();
484  for (unsigned int i = 0; (i < maxNumPeaks) && (peak != peaks_map.end()); i++)
485  {
486  cmpPeakLocV = peak->second;
487  corrCoef = peak->first;
488  atp->addViewMatch(cmpPeakLocV, corrCoef);
489  peak++;
490  }
491  }
492 
493  cvReleaseImage(&result);
494  cvReleaseImage(&refimage);
495  cvReleaseImage(&cmpimage);
496 
497  return true;
498 }
499 
500 }
501 
502 
virtual ossim_uint32 getWidth() const
ossim_uint32 x
virtual const double & getNominalPosError() const
Returns the estimated Absolute horizontal position error (CE90) of the sensor model.
JsonParam & getParameter(const char *paramName)
Returns a parameter (might be a null parameter if paramName not found in the configuration.
Definition: JsonConfig.cpp:377
virtual void setImageRectangle(const ossimIrect &rect)
Base class for all automatic tiepoints.
Definition: AutoTiePoint.h:30
IplImage * convertToIpl32(const ossimImageData *data)
Definition: AtpOpenCV.cpp:77
ossim_uint32 y
std::shared_ptr< AtpGenerator > m_generator
Definition: AtpTileSource.h:85
bool OpenCVCorrelation(std::shared_ptr< AutoTiePoint > atp, const ossimImageData *refpatch, const ossimImageData *cmppatch)
double y
Definition: ossimDpt.h:165
double asFloat() const
Definition: JsonConfig.cpp:278
unsigned int asUint() const
Definition: JsonConfig.cpp:264
const ossimIpt & ul() const
Definition: ossimIrect.h:274
virtual ossimRefPtr< ossimImageData > getTile(const ossimIrect &origin, ossim_uint32 rLevel=0)
Derived classes implement their particular tiepoint matching algorithms for the requested tile...
virtual ossimDataObjectStatus getDataObjectStatus() const
Base class for tile sources performing auto tie point extraction.
Definition: AtpTileSource.h:26
virtual ossim_float64 getPix(const ossimIpt &position, ossim_uint32 band=0) const
Will return the pixel at location position.
bool paramExists(const char *paramName) const
Definition: JsonConfig.cpp:395
virtual void initialize()
Initialize the data buffer.
void copyIpl32ToOid(IplImage *ipl, ossimImageData *oid)
Definition: AtpOpenCV.cpp:90
virtual bool write(const ossimFilename &f) const
Writes tile to stream.
virtual void initialize()
std::vector< ossimRefPtr< ossimConnectableObject > > ConnectableObjectList
static ossimImageDataFactory * instance()
Base class for OSSIM-based ATP generators.
Definition: AtpGenerator.h:33
bool asBool() const
Definition: JsonConfig.cpp:257
unsigned int ossim_uint32
virtual const ossim_float64 * getNullPix() const
virtual ossimIrect getImageRectangle() const
const ossimIpt & lr() const
Definition: ossimIrect.h:276
virtual ossimRefPtr< ossimImageData > create(ossimSource *owner, ossimScalarType scalar, ossim_uint32 bands=1) const
THESE FUNCTIONS REQUIRE OPENCV.
ossimRefPtr< ossimImageData > m_tile
Definition: AtpTileSource.h:87
bool diagnosticLevel(unsigned int level) const
Convenience method returns TRUE if the currently set diagnostic level is <= level.
Definition: JsonConfig.cpp:461
bool correlate(std::shared_ptr< AutoTiePoint > atp)
void findFeatures(const ossimImageData *data, std::vector< ossimIpt > &featurePts)
Given an image tile, locates prominent features in that tile. Use.
IplImage * convertToIpl(const ossimImageData *data)
Converts an ossimImageData pointer to an IplImage for use in OpenCV.
Definition: AtpOpenCV.cpp:25
#define max(a, b)
Definition: auxiliary.h:76
ossim_int32 y
Definition: ossimIpt.h:142
virtual ossim_uint32 getNumberOfInputs() const
Returns the number of input objects.
double x
Definition: ossimDpt.h:164
#define CFATAL
static AtpConfig & instance()
Singleton implementation.
Definition: AtpConfig.cpp:20
ossim_int32 x
Definition: ossimIpt.h:141
8 bit unsigned integer
#define CWARN
ossimDataObjectStatus
Definitions for data object status.
virtual void allocate()
#define CINFO
Singleton class maintaining parameters affecting the automatic tie point generation.
Definition: AtpConfig.h:24
#define min(a, b)
Definition: auxiliary.h:75