USGS

Isis 3.0 Object Programmers' Reference

Home

Equalization.cpp
1 #include "Equalization.h"
2 
3 #include <iomanip>
4 
5 #include "Buffer.h"
6 #include "Cube.h"
7 #include "FileList.h"
8 #include "IException.h"
9 #include "LeastSquares.h"
10 #include "LineManager.h"
11 #include "OverlapNormalization.h"
12 #include "OverlapStatistics.h"
13 #include "Process.h"
14 #include "ProcessByLine.h"
15 #include "Projection.h"
16 #include "Pvl.h"
17 #include "PvlGroup.h"
18 #include "Statistics.h"
19 
20 using namespace std;
21 
22 namespace Isis {
23 
24 
25  Equalization::Equalization() {
26  init();
27  }
28 
29 
34  Equalization::Equalization(OverlapNormalization::SolutionType sType, QString fromListName) {
35  init();
36 
37  m_sType = sType;
38  loadInputs(fromListName);
39  }
40 
41 
42  Equalization::~Equalization() {
43  clearAdjustments();
44  m_holdIndices.clear();
45 
46  if (m_results != NULL) {
47  delete m_results;
48  m_results = NULL;
49  }
50  }
51 
52 
53  void Equalization::addHolds(QString holdListName) {
54  FileList holdList;
55  holdList.read(FileName(holdListName));
56 
57  if (holdList.size() > m_imageList.size()) {
58  QString msg = "The list of identifiers to be held must be less than or ";
59  msg += "equal to the total number of identitifers.";
60  throw IException(IException::User, msg, _FILEINFO_);
61  }
62 
63  // Make sure each file in the holdlist matches a file in the fromlist
64  for (int i = 0; i < holdList.size(); i++) {
65  bool matched = false;
66  for (int j = 0; j < m_imageList.size(); j++) {
67  if (holdList[i] == m_imageList[j]) {
68  matched = true;
69  m_holdIndices.push_back(j);
70  break;
71  }
72  }
73  if (!matched) {
74  QString msg = "The hold list file [" + holdList[i].toString() +
75  "] does not match a file in the from list";
76  throw IException(IException::User, msg, _FILEINFO_);
77  }
78  }
79  }
80 
81 
91  void Equalization::calculateStatistics(double percent, int mincnt,
92  bool wtopt, LeastSquares::SolveMethod methodType) {
93 
94  clearAdjustments();
95 
96  m_mincnt = mincnt;
97  m_wtopt = wtopt;
98 
99  // Loop through all the input cubes, calculating statistics for each cube
100  // to use later
101  vector<OverlapNormalization *> oNormList;
102  for (int band = 1; band <= m_maxBand; band++) {
103  vector<Statistics *> statsList;
104  for (int img = 0; img < (int) m_imageList.size(); img++) {
105  ProcessByLine p;
106  QString bandStr(toString(band));
107  QString statMsg = "Calculating Statistics for Band " + bandStr +
108  " of " + toString(m_maxBand) + " in Cube " + toString(img + 1) +
109  " of " + toString(m_maxCube);
110  p.Progress()->SetText(statMsg);
111  CubeAttributeInput att("+" + bandStr);
112  QString inp = m_imageList[img].toString();
113  p.SetInputCube(inp, att);
114 
115  Statistics *stats = new Statistics();
116 
117  CalculateFunctor func(stats, percent);
118  p.ProcessCubeInPlace(func, false);
119  p.EndProcess();
120 
121  statsList.push_back(stats);
122  }
123 
124  // Create a separate OverlapNormalization object for every band
125  OverlapNormalization *oNorm = new OverlapNormalization(statsList);
126  loadHolds(oNorm);
127  oNormList.push_back(oNorm);
128  }
129 
130  // A list for keeping track of which input cubes are known to overlap
131  // another
132  vector<bool> doesOverlapList;
133  for (int i = 0; i < m_imageList.size(); i++) {
134  doesOverlapList.push_back(false);
135  }
136 
137  // Find overlapping areas and add them to the set of known overlaps for
138  // each band shared amongst cubes
139  vector<OverlapStatistics> overlapList;
140  for (int i = 0; i < m_imageList.size(); i++) {
141  addAdjustment(new ImageAdjustment(m_sType));
142 
143  Cube cube1;
144  cube1.open(m_imageList[i].toString());
145 
146  for (int j = (i + 1); j < m_imageList.size(); j++) {
147  Cube cube2;
148  cube2.open(m_imageList[j].toString());
149  QString cubeStr1 = toString((int)(i + 1));
150  QString cubeStr2 = toString((int)(j + 1));
151  QString statMsg = "Gathering Overlap Statisitcs for Cube " +
152  cubeStr1 + " vs " + cubeStr2 + " of " +
153  toString(m_maxCube);
154 
155  // Get overlap statistics for cubes
156  OverlapStatistics oStats(cube1, cube2, statMsg, percent);
157 
158  // Only push the stats onto the oList vector if there is an overlap in at
159  // least one of the bands
160  if (oStats.HasOverlap()) {
161  oStats.SetMincount(mincnt);
162  overlapList.push_back(oStats);
163  for (int band = 1; band <= m_maxBand; band++) {
164  // Fill wt vector with 1's if the overlaps are not to be weighted, or
165  // fill the vector with the number of valid pixels in each overlap
166  int weight = 1;
167  if (wtopt) weight = oStats.GetMStats(band).ValidPixels();
168 
169  // Make sure overlap has at least MINCOUNT valid pixels and add
170  if (oStats.GetMStats(band).ValidPixels() >= mincnt) {
171  oNormList[band - 1]->AddOverlap(
172  oStats.GetMStats(band).X(), i,
173  oStats.GetMStats(band).Y(), j, weight);
174  doesOverlapList[i] = true;
175  doesOverlapList[j] = true;
176  }
177  }
178  }
179  }
180  }
181 
182  // Print an error if one or more of the images does not overlap another
183  {
184  QString badFiles = "";
185  for (int img = 0; img < m_imageList.size(); img++) {
186  // Print the name of each input cube without an overlap
187  if (!doesOverlapList[img]) {
188  badFiles += "[" + m_imageList[img].toString() + "] ";
189  }
190  }
191  if (badFiles != "") {
192  QString msg = "File(s) " + badFiles;
193  msg += " do(es) not overlap any other input images with enough valid pixels";
194  throw IException(IException::User, msg, _FILEINFO_);
195  }
196  }
197 
198  // Loop through each band making all necessary calculations
199  try {
200  for (int band = 0; band < m_maxBand; band++) {
201  oNormList[band]->Solve(m_sType, methodType);
202 
203  for (unsigned int img = 0; img < m_adjustments.size(); img++) {
204  m_adjustments[img]->addGain(oNormList[band]->Gain(img));
205  if (qFuzzyCompare(oNormList[band]->Gain(img), 0.0)) {// if gain == 0
206 cout << img << endl;
207 cout << band << endl;
208 cout << oNormList[band]->Gain(img) << endl;
209  throw IException(IException::Unknown,
210  "Calculation for equalization statistics failed. Gain = 0.",
211  _FILEINFO_);
212  }
213  m_adjustments[img]->addOffset(oNormList[band]->Offset(img));
214  m_adjustments[img]->addAverage(oNormList[band]->Average(img));
215  }
216  }
217 
218  }
219  catch (IException &e) {
220  string msg = "Unable to calculate the equalization statistics. You may "
221  "want to try another LeastSquares::SolveMethod.";
222  throw IException(e, IException::Unknown, msg, _FILEINFO_);
223  }
224  // Compute the number valid and invalid overlaps
225  for (unsigned int o = 0; o < overlapList.size(); o++) {
226  for (int band = 1; band <= m_maxBand; band++) {
227  (overlapList[o].IsValid(band)) ? m_validCnt++ : m_invalidCnt++;
228  }
229  }
230 
231  setResults(overlapList);
232  }
233 
234 
235  void Equalization::setResults(vector<OverlapStatistics> &overlapStats) {
236  setResults();
237 
238  for (unsigned int i = 0; i < overlapStats.size(); i++) {
239  m_results->addObject(overlapStats[i].toPvl());
240  }
241  }
242 
243 
244  void Equalization::setResults() {
245  if (m_results != NULL) {
246  delete m_results;
247  m_results = NULL;
248  }
249 
250  m_results = new Pvl();
251  m_results->setTerminator("");
252 
253  PvlObject equ("EqualizationInformation");
254  PvlGroup gen("General");
255  gen += PvlKeyword("TotalOverlaps", toString(m_validCnt + m_invalidCnt));
256  gen += PvlKeyword("ValidOverlaps", toString(m_validCnt));
257  gen += PvlKeyword("InvalidOverlaps", toString(m_invalidCnt));
258  gen += PvlKeyword("Weighted", (m_wtopt) ? "true" : "false");
259  gen += PvlKeyword("MinCount", toString(m_mincnt));
260  equ.addGroup(gen);
261  for (int img = 0; img < m_imageList.size(); img++) {
262  // Format and name information
263  PvlGroup norm("Normalization");
264  norm.addComment("Formula: newDN = (oldDN - AVERAGE) * GAIN + AVERAGE + OFFSET");
265  norm.addComment("BandN = (GAIN, OFFSET, AVERAGE)");
266  norm += PvlKeyword("FileName", m_imageList[img].original());
267 
268  // Band by band statistics
269  for (int band = 1; band <= m_maxBand; band++) {
270  QString mult = toString(m_adjustments[img]->getGain(band - 1));
271  QString base = toString(m_adjustments[img]->getOffset(band - 1));
272  QString avg = toString(m_adjustments[img]->getAverage(band - 1));
273  QString bandNum = toString(band);
274  QString bandStr = "Band" + bandNum;
275  PvlKeyword bandStats(bandStr);
276  bandStats += mult;
277  bandStats += base;
278  bandStats += avg;
279  norm += bandStats;
280  }
281  equ.addGroup(norm);
282  }
283 
284  m_results->addObject(equ);
285  }
286 
287 
288  void Equalization::importStatistics(QString instatsFileName) {
289  // Check for errors with the input statistics
290  vector<int> normIndices = validateInputStatistics(instatsFileName);
291 
292  clearAdjustments();
293  for (int img = 0; img < (int) m_imageList.size(); img++) {
294  // Apply correction based on pre-determined statistics information
295  Pvl inStats(instatsFileName);
296  PvlObject &equalInfo = inStats.findObject("EqualizationInformation");
297  PvlGroup &normalization = equalInfo.group(normIndices[img]);
298 
299  // TODO should we also get the valid and invalid count?
300 
301  ImageAdjustment *adjustment = new ImageAdjustment(m_sType);
302 
303  // Get and store the modifiers for each band
304  for (int band = 1; band < normalization.keywords(); band++) {
305  adjustment->addGain(toDouble(normalization[band][0]));
306  adjustment->addOffset(toDouble(normalization[band][1]));
307  adjustment->addAverage(toDouble(normalization[band][2]));
308  }
309 
310  addAdjustment(adjustment);
311  }
312  }
313 
314 
315  void Equalization::applyCorrection(QString toListName="") {
316  FileList outList;
317  fillOutList(outList, toListName);
318 
319  QString maxCubeStr = toString((int) m_imageList.size());
320  for (int img = 0; img < m_imageList.size(); img++) {
321  // Set up for progress bar
322  ProcessByLine p;
323  p.Progress()->SetText("Equalizing Cube " + toString((int) img + 1) +
324  " of " + maxCubeStr);
325 
326  // Open input cube
327  CubeAttributeInput att;
328  const QString inp = m_imageList[img].toString();
329  Cube *icube = p.SetInputCube(inp, att);
330 
331  // Allocate output cube
332  QString out = outList[img].toString();
333  CubeAttributeOutput outAtt;
334  p.SetOutputCube(out, outAtt, icube->sampleCount(),
335  icube->lineCount(), icube->bandCount());
336 
337  // Apply gain/offset to the image
338  ApplyFunctor func(m_adjustments[img]);
339  p.ProcessCube(func, false);
340  p.EndProcess();
341  }
342  }
343 
344 
345  PvlGroup Equalization::getResults() {
346  // TODO combine summary info into getSummary method and use with write
347  PvlGroup results("Results");
348  results += PvlKeyword("TotalOverlaps", toString(m_validCnt + m_invalidCnt));
349  results += PvlKeyword("ValidOverlaps", toString(m_validCnt));
350  results += PvlKeyword("InvalidOverlaps", toString(m_invalidCnt));
351  results += PvlKeyword("Weighted", (m_wtopt) ? "true" : "false");
352  results += PvlKeyword("MinCount", toString(m_mincnt));
353 
354  // Name and band modifiers for each image
355  for (int img = 0; img < m_imageList.size(); img++) {
356  results += PvlKeyword("FileName", m_imageList[img].toString());
357 
358  // Band by band statistics
359  for (int band = 1; band <= m_maxBand; band++) {
360  QString mult = toString(m_adjustments[img]->getGain(band - 1));
361  QString base = toString(m_adjustments[img]->getOffset(band - 1));
362  QString avg = toString(m_adjustments[img]->getAverage(band - 1));
363  QString bandNum = toString(band);
364  QString bandStr = "Band" + bandNum;
365  PvlKeyword bandStats(bandStr);
366  bandStats += mult;
367  bandStats += base;
368  bandStats += avg;
369  results += bandStats;
370  }
371  }
372 
373  return results;
374  }
375 
376 
377  void Equalization::write(QString outstatsFileName) {
378  // Write the equalization and overlap statistics to the file
379  m_results->write(outstatsFileName);
380  }
381 
382 
383  double Equalization::evaluate(double dn,
384  int imageIndex, int bandIndex) const {
385  return m_adjustments[imageIndex]->evaluate(dn, bandIndex);
386  }
387 
388 
389  void Equalization::loadInputs(QString fromListName) {
390  // Get the list of cubes to mosaic
391  m_imageList.read(fromListName);
392  m_maxCube = m_imageList.size();
393 
394  if (m_imageList.size() < 2) {
395  QString msg = "The input file [" + fromListName +
396  "] must contain at least 2 file names";
397  throw IException(IException::User, msg, _FILEINFO_);
398  }
399 
400  Cube tempCube;
401  tempCube.open(m_imageList[0].toString());
402  m_maxBand = tempCube.bandCount();
403 
404  errorCheck(fromListName);
405  }
406 
407 
408  void Equalization::setInput(int index, QString value) {
409  m_imageList[index] = value;
410  }
411 
412 
413  const FileList & Equalization::getInputs() const {
414  return m_imageList;
415  }
416 
417 
418  void Equalization::fillOutList(FileList &outList, QString toListName) {
419  if (toListName.isEmpty()) {
420  generateOutputs(outList);
421  }
422  else {
423  loadOutputs(outList, toListName);
424  }
425  }
426 
427 
428  void Equalization::errorCheck(QString fromListName) {
429  for (int i = 0; i < m_imageList.size(); i++) {
430  Cube cube1;
431  cube1.open(m_imageList[i].toString());
432 
433  for (int j = (i + 1); j < m_imageList.size(); j++) {
434  Cube cube2;
435  cube2.open(m_imageList[j].toString());
436 
437  // Make sure number of bands match
438  if (m_maxBand != cube2.bandCount()) {
439  QString msg = "Number of bands do not match between cubes [" +
440  m_imageList[i].toString() + "] and [" + m_imageList[j].toString() + "]";
441  throw IException(IException::User, msg, _FILEINFO_);
442  }
443 
444  //Create projection from each cube
445  Projection *proj1 = cube1.projection();
446  Projection *proj2 = cube2.projection();
447 
448  // Test to make sure projection parameters match
449  if (*proj1 != *proj2) {
450  QString msg = "Mapping groups do not match between cubes [" +
451  m_imageList[i].toString() + "] and [" + m_imageList[j].toString() + "]";
452  throw IException(IException::User, msg, _FILEINFO_);
453  }
454  }
455  }
456  }
457 
458 
459  void Equalization::generateOutputs(FileList &outList) {
460  for (int img = 0; img < m_imageList.size(); img++) {
461  FileName file(m_imageList[img]);
462  QString filename = file.path() + "/" + file.baseName() +
463  ".equ." + file.extension();
464  outList.push_back(FileName(filename));
465  }
466  }
467 
468 
469  void Equalization::loadOutputs(FileList &outList, QString toListName) {
470  outList.read(FileName(toListName));
471 
472  // Make sure each file in the tolist matches a file in the fromlist
473  if (outList.size() != m_imageList.size()) {
474  QString msg = "Each input file in the FROM LIST must have a ";
475  msg += "corresponding output file in the TO LIST.";
476  throw IException(IException::User, msg, _FILEINFO_);
477  }
478 
479  // Make sure that all output files do not have the same names as their
480  // corresponding input files
481  for (int i = 0; i < outList.size(); i++) {
482  if (outList[i].toString().compare(m_imageList[i].toString()) == 0) {
483  QString msg = "The to list file [" + outList[i].toString() +
484  "] has the same name as its corresponding from list file.";
485  throw IException(IException::User, msg, _FILEINFO_);
486  }
487  }
488  }
489 
490 
491  void Equalization::loadHolds(OverlapNormalization *oNorm) {
492  for (unsigned int h = 0; h < m_holdIndices.size(); h++)
493  oNorm->AddHold(m_holdIndices[h]);
494  }
495 
496 
497  void Equalization::clearAdjustments() {
498  m_adjustments.clear();
499  }
500 
501 
502  void Equalization::addAdjustment(ImageAdjustment *adjustment) {
503  m_adjustments.push_back(adjustment);
504  }
505 
506 
507  void Equalization::addValid(int count) {
508  m_validCnt += count;
509  }
510 
511 
512  void Equalization::addInvalid(int count) {
513  m_invalidCnt += count;
514  }
515 
516 
517  void Equalization::init() {
518  m_validCnt = 0;
519  m_invalidCnt = 0;
520 
521  m_mincnt = 1000;
522  m_wtopt = false;
523 
524  m_maxCube = 0;
525  m_maxBand = 0;
526 
527  m_sType = OverlapNormalization::Both;
528 
529  m_results = NULL;
530  }
531 
532 
533  vector<int> Equalization::validateInputStatistics(QString instatsFileName) {
534  Pvl inStats(instatsFileName);
535  PvlObject &equalInfo = inStats.findObject("EqualizationInformation");
536 
537  // Make sure each file in the instats matches a file in the fromlist
538  if (m_imageList.size() > equalInfo.groups() - 1) {
539  QString msg = "Each input file in the FROM LIST must have a ";
540  msg += "corresponding input file in the INPUT STATISTICS.";
541  throw IException(IException::User, msg, _FILEINFO_);
542  }
543 
544  vector<int> normIndices;
545 
546  // Check that each file in the FROM LIST is present in the INPUT STATISTICS
547  for (int i = 0; i < m_imageList.size(); i++) {
548  QString fromFile = m_imageList[i].original();
549  bool foundFile = false;
550  for (int j = 1; j < equalInfo.groups(); j++) {
551  PvlGroup &normalization = equalInfo.group(j);
552  QString normFile = normalization["FileName"][0];
553  if (fromFile == normFile) {
554 
555  // Store the index in INPUT STATISTICS file corresponding to the
556  // current FROM LIST file
557  normIndices.push_back(j);
558  foundFile = true;
559  }
560  }
561  if (!foundFile) {
562  QString msg = "The from list file [" + fromFile +
563  "] does not have any corresponding file in the stats list.";
564  throw IException(IException::User, msg, _FILEINFO_);
565  }
566  }
567 
568  return normIndices;
569  }
570 
571 
572  void Equalization::CalculateFunctor::operator()(Buffer &in) const {
573  // Make sure we consider the last line
574  if ((in.Line() - 1) % m_linc == 0 || in.Line() == in.LineDimension()) {
575  addStats(in);
576  }
577  }
578 
579 
580  void Equalization::CalculateFunctor::addStats(Buffer &in) const {
581  // Add data to Statistics object by line
582  m_stats->AddData(&in[0], in.size());
583  }
584 
585 
586  void Equalization::ApplyFunctor::operator()(Buffer &in, Buffer &out) const {
587  int index = in.Band() - 1;
588  for (int i = 0; i < in.size(); i++) {
589  out[i] = (IsSpecial(in[i])) ?
590  in[i] : m_adjustment->evaluate(in[i], index);
591  }
592  }
593 
594 
595 }