Isis 3.0 Programmer Reference
Back | Home
ProcessRubberSheet.cpp
Go to the documentation of this file.
1 
23 #include <iostream>
24 #include <iomanip>
25 
26 #include <QVector>
27 
28 #include "Affine.h"
29 #include "BasisFunction.h"
30 #include "BoxcarCachingAlgorithm.h"
31 #include "Brick.h"
32 #include "Interpolator.h"
33 #include "LeastSquares.h"
34 #include "Portal.h"
35 #include "ProcessRubberSheet.h"
36 #include "TileManager.h"
37 #include "Transform.h"
39 
40 using namespace std;
41 namespace Isis {
48  ProcessRubberSheet::ProcessRubberSheet(int startSize, int endSize) {
49  p_bandChangeFunct = NULL;
50 
51  // Information used only in the tile transform method (StartProcess)
52  p_forceSamp = Null;
53  p_forceLine = Null;
54  p_startQuadSize = startSize;
55  p_endQuadSize = endSize;
56 
57  // Patch parameters used only in the patch transform (processPatchTransform)
58  m_patchStartSample = 1;
59  m_patchStartLine = 1;
60  m_patchSamples = 5;
61  m_patchLines = 5;
62  m_patchSampleIncrement = 4;
63  m_patchLineIncrement = 4;
64  };
65 
113  void ProcessRubberSheet::setPatchParameters(int startSample, int startLine,
114  int samples, int lines,
115  int sampleIncrement, int lineIncrement) {
116  m_patchStartSample = startSample;
117  m_patchStartLine = startLine;
118  m_patchSamples = samples;
119  m_patchLines = lines;
120  m_patchSampleIncrement = sampleIncrement;
121  m_patchLineIncrement = lineIncrement;
122  }
123 
141  void ProcessRubberSheet::StartProcess(Transform &trans,
142  Interpolator &interp) {
143  // Error checks ... there must be one input and one output
144  if(InputCubes.size() != 1) {
145  string m = "You must specify exactly one input cube";
146  throw IException(IException::Programmer, m, _FILEINFO_);
147  }
148  else if(OutputCubes.size() != 1) {
149  string m = "You must specify exactly one output cube";
150  throw IException(IException::Programmer, m, _FILEINFO_);
151  }
152 
153  // allocate the sampMap/lineMap vectors
154  p_lineMap.resize(p_startQuadSize);
155  p_sampMap.resize(p_startQuadSize);
156 
157  for(unsigned int pos = 0; pos < p_lineMap.size(); pos++) {
158  p_lineMap[pos].resize(p_startQuadSize);
159  p_sampMap[pos].resize(p_startQuadSize);
160  }
161 
162  // Create a tile manager for the output file
163  TileManager otile(*OutputCubes[0], p_startQuadSize, p_startQuadSize);
164 
165  // Create a portal buffer for the input file
166  Portal iportal(interp.Samples(), interp.Lines(),
167  InputCubes[0]->pixelType() ,
168  interp.HotSample(), interp.HotLine());
169 
170  // Start the progress meter
171  p_progress->SetMaximumSteps(otile.Tiles());
172  p_progress->CheckStatus();
173 
174  if(p_bandChangeFunct == NULL) {
175  // A portal could read up to four chunks so we need to cache four times the number of bands to
176  // minimize I/O thrashing
177  InputCubes[0]->addCachingAlgorithm(new UniqueIOCachingAlgorithm(2 * InputCubes[0]->bandCount()));
178  OutputCubes[0]->addCachingAlgorithm(new BoxcarCachingAlgorithm());
179 
180  int tilesPerBand = otile.Tiles() / OutputCubes[0]->bandCount();
181 
182  for(int tile = 1; tile <= tilesPerBand; tile++) {
183  bool useLastTileMap = false;
184  for(int band = 1; band <= OutputCubes[0]->bandCount(); band++) {
185  otile.SetTile(tile, band);
186 
187  if(p_startQuadSize <= 2) {
188  SlowGeom(otile, iportal, trans, interp);
189  }
190  else {
191  QuadTree(otile, iportal, trans, interp, useLastTileMap);
192  }
193 
194  useLastTileMap = true;
195 
196  OutputCubes[0]->write(otile);
197  p_progress->CheckStatus();
198  }
199  }
200  }
201  else {
202  int lastOutputBand = -1;
203 
204  for(otile.begin(); !otile.end(); otile++) {
205  // Keep track of the current band
206  if(lastOutputBand != otile.Band()) {
207  lastOutputBand = otile.Band();
208  // Call an application function if the band number changes
209  p_bandChangeFunct(lastOutputBand);
210  }
211 
212  if(p_startQuadSize <= 2) {
213  SlowGeom(otile, iportal, trans, interp);
214  }
215  else {
216  QuadTree(otile, iportal, trans, interp, false);
217  }
218 
219  OutputCubes[0]->write(otile);
220  p_progress->CheckStatus();
221  }
222  }
223 
224  p_sampMap.clear();
225  p_lineMap.clear();
226  }
237  void ProcessRubberSheet::BandChange(void (*funct)(const int band)) {
238  p_bandChangeFunct = funct;
239  }
240 
241  void ProcessRubberSheet::SlowGeom(TileManager &otile, Portal &iportal,
242  Transform &trans, Interpolator &interp) {
243  double outputSamp, outputLine;
244  double inputSamp, inputLine;
245  int outputBand = otile.Band();
246 
247  for(int i = 0; i < otile.size(); i++) {
248  outputSamp = otile.Sample(i);
249  outputLine = otile.Line(i);
250  // Use the defined transform to find out what input pixel the output
251  // pixel came from
252  if(trans.Xform(inputSamp, inputLine, outputSamp, outputLine)) {
253  if((inputSamp < 0.5) || (inputLine < 0.5) ||
254  (inputLine > InputCubes[0]->lineCount() + 0.5) ||
255  (inputSamp > InputCubes[0]->sampleCount() + 0.5)) {
256  otile[i] = NULL8;
257  }
258  else {
259  // Set the position of the portal in the input cube
260  iportal.SetPosition(inputSamp, inputLine, outputBand);
261  InputCubes[0]->read(iportal);
262  otile[i] = interp.Interpolate(inputSamp, inputLine,
263  iportal.DoubleBuffer());
264  }
265  }
266  else {
267  otile[i] = NULL8;
268  }
269  }
270  }
271 
272  void ProcessRubberSheet::QuadTree(TileManager &otile, Portal &iportal,
273  Transform &trans, Interpolator &interp,
274  bool useLastTileMap) {
275  // Initializations
276  vector<Quad *> quadTree;
277 
278  if(!useLastTileMap) {
279  // Set up the boundaries of the full tile
280  Quad *quad = new Quad;
281  quad->sline = otile.Line();
282  quad->ssamp = otile.Sample();
283 
284  quad->eline = otile.Line(otile.size() - 1);
285  quad->esamp = otile.Sample(otile.size() - 1);
286  quad->slineTile = otile.Line();
287  quad->ssampTile = otile.Sample();
288 
289  quadTree.push_back(quad);
290 
291  // Loop and compute the input coordinates filling the maps
292  // until the quad tree is empty
293  while(quadTree.size() > 0) {
294  ProcessQuad(quadTree, trans, p_lineMap, p_sampMap);
295  }
296  }
297 
298  // Apply the map to the output tile
299  int outputBand = otile.Band();
300  for(int i = 0, line = 0; line < p_startQuadSize; line++) {
301  for(int samp = 0; samp < p_startQuadSize; samp++, i++) {
302  double inputLine = p_lineMap[line][samp];
303  double inputSamp = p_sampMap[line][samp];
304  if(inputLine != NULL8) {
305  iportal.SetPosition(inputSamp, inputLine, outputBand);
306  InputCubes[0]->read(iportal);
307  otile[i] = interp.Interpolate(inputSamp, inputLine,
308  iportal.DoubleBuffer());
309  }
310  else {
311  otile[i] = NULL8;
312  }
313  }
314  }
315  }
316 
317 
330  bool ProcessRubberSheet::TestLine(Transform &trans, int ssamp, int esamp,
331  int sline, int eline, int increment) {
332  for(int line = sline; line <= eline; line += increment) {
333  for(int sample = ssamp; sample <= esamp; sample += increment) {
334  double sjunk = 0.0;
335  double ljunk = 0.0;
336 
337  if(trans.Xform(sjunk, ljunk, sample, line)) {
338  return true;
339  }
340  }
341  }
342 
343  return false;
344  }
345 
346  // Process a quad trying to find input positions for output positions
347  void ProcessRubberSheet::ProcessQuad(std::vector<Quad *> &quadTree,
348  Transform &trans,
349  std::vector< std::vector<double> > &lineMap,
350  std::vector< std::vector<double> > &sampMap) {
351  Quad *quad = quadTree[0];
352  double oline[4], osamp[4];
353  double iline[4], isamp[4];
354 
355  // Try to convert the upper left corner to input coordinates
356  int badCorner = 0;
357  oline[0] = quad->sline;
358  osamp[0] = quad->ssamp;
359  if(!trans.Xform(isamp[0], iline[0], osamp[0], oline[0])) {
360  badCorner++;
361  }
362 
363  // Now try the upper right corner
364  oline[1] = quad->sline;
365  osamp[1] = quad->esamp;
366  if(!trans.Xform(isamp[1], iline[1], osamp[1], oline[1])) {
367  badCorner++;
368  }
369 
370  // Now try the lower left corner
371  oline[2] = quad->eline;
372  osamp[2] = quad->ssamp;
373  if(!trans.Xform(isamp[2], iline[2], osamp[2], oline[2])) {
374  badCorner++;
375  }
376 
377  // Now try the lower right corner
378  oline[3] = quad->eline;
379  osamp[3] = quad->esamp;
380  if(!trans.Xform(isamp[3], iline[3], osamp[3], oline[3])) {
381  badCorner++;
382  }
383 
384  // If all four corners are bad then walk the edges. If any points
385  // on the edges transform we will split the quad or
386  // if the quad is already small just transform everything
387  if(badCorner == 4) {
388  if((quad->eline - quad->sline) < p_endQuadSize) {
389  SlowQuad(quadTree, trans, lineMap, sampMap);
390  }
391  else {
392  if(p_forceSamp != Null && p_forceLine != Null) {
393  if(p_forceSamp >= quad->ssamp && p_forceSamp <= quad->esamp &&
394  p_forceLine >= quad->sline && p_forceLine <= quad->eline) {
395  SplitQuad(quadTree);
396  return;
397  }
398  }
399 
400  int centerSample = (quad->ssamp + quad->esamp) / 2;
401  int centerLine = (quad->sline + quad->eline) / 2;
402 
403  // All 4 corner points have failed tests.
404  //
405  // If we find data around the quad by walking around a 2x2 grid in the
406  // box, then we need to split the quad. Check outside the box and
407  // interior crosshair.
408  //
409  // This is what we're walking:
410  // -----------
411  // | | |
412  // | | |
413  // |----|----|
414  // | | |
415  // | | |
416  // -----------
417  // Top Edge
418  if(TestLine(trans, quad->ssamp + 1, quad->esamp - 1,
419  quad->sline, quad->sline, 4) ||
420  // Bottom Edge
421  TestLine(trans, quad->ssamp + 1, quad->esamp - 1,
422  quad->eline, quad->eline, 4) ||
423  // Left Edge
424  TestLine(trans, quad->ssamp, quad->ssamp,
425  quad->sline + 1, quad->eline - 1, 4) ||
426  // Right Edge
427  TestLine(trans, quad->esamp, quad->esamp,
428  quad->sline + 1, quad->eline - 1, 4) ||
429  // Center Column
430  TestLine(trans, centerSample, centerSample,
431  quad->sline + 1, quad->eline - 1, 4) ||
432  // Center Row
433  TestLine(trans, quad->ssamp + 1, quad->esamp - 1,
434  centerLine, centerLine, 4)) {
435 
436 
437  SplitQuad(quadTree);
438  return;
439  }
440 
441  // Nothing in quad, fill with nulls
442  for(int i = quad->sline; i <= quad->eline; i++) {
443  for(int j = quad->ssamp; j <= quad->esamp; j++) {
444  lineMap[i-quad->slineTile][j-quad->ssampTile] = NULL8;
445  }
446  }
447  delete quad;
448  quadTree.erase(quadTree.begin());
449  }
450  return;
451  }
452 
453  // If all four corners are bad then assume the whole tile is bad
454  // Load the maps with nulls and delete the quad from the list
455  // Free memory too
456  // if (badCorner == 4) {
457  // for (int i=quad->sline; i<=quad->eline; i++) {
458  // for (int j=quad->ssamp; j<=quad->esamp; j++) {
459  // lineMap[i-quad->slineTile][j-quad->ssampTile] = NULL8;
460  // }
461  // }
462  // delete quad;
463  // quadTree.erase(quadTree.begin());
464  // return;
465  // }
466 
467  // See if any other corners are bad in which case we will need to
468  // split the quad into finer pieces. But lets not get ridiculous.
469  // If the split distance is small we might as well compute at every
470  // point
471  if(badCorner > 0) {
472  if((quad->eline - quad->sline) < p_endQuadSize) {
473  SlowQuad(quadTree, trans, lineMap, sampMap);
474  }
475  else {
476  SplitQuad(quadTree);
477  }
478  return;
479  }
480 
481  // We have good corners ... create two equations using them
482  // iline = a*oline + b*osamp + c*oline*osamp + d
483  // isamp = e*oline + f*osamp + g*oline*osamp + h
484  // Start by setting up a 4x4 matrix
485  double A[4][4];
486  for(int i = 0; i < 4; i++) {
487  A[i][0] = oline[i];
488  A[i][1] = osamp[i];
489  A[i][2] = oline[i] * osamp[i];
490  A[i][3] = 1.0;
491  }
492 
493  // Make sure the determinate is non-zero, otherwise split it up again
494  // and hope for the best. If this happens it probably is because the
495  // transform is lame (bugged)
496  double detA;
497  if((detA = Det4x4(A)) == 0.0) {
498  if((quad->eline - quad->sline) < p_endQuadSize) {
499  SlowQuad(quadTree, trans, lineMap, sampMap);
500  }
501  else {
502  SplitQuad(quadTree);
503  }
504  return;
505  }
506 
507  // Substitute our desired answers into B to get the coefficients for the
508  // line dimension (Cramers Rule!!)
509  double B[4][4];
510  double lineCoef[4];
511  for(int j = 0; j < 4; j++) {
512  memmove(B, A, 16 * sizeof(double));
513 
514  for(int i = 0; i < 4; i++) {
515  B[i][j] = iline[i];
516  }
517 
518  lineCoef[j] = Det4x4(B) / detA;
519  }
520 
521  // Do it again to get the sample coefficients
522  double sampCoef[4];
523  for(int j = 0; j < 4; j++) {
524  memmove(B, A, 16 * sizeof(double));
525 
526  for(int i = 0; i < 4; i++) {
527  B[i][j] = isamp[i];
528  }
529 
530  sampCoef[j] = Det4x4(B) / detA;
531  }
532 
533  // Test the middle point to see if the equations are good
534  double quadMidLine = (quad->sline + quad->eline) / 2.0;
535  double quadMidSamp = (quad->ssamp + quad->esamp) / 2.0;
536  double midLine, midSamp;
537 
538  if(!trans.Xform(midSamp, midLine, quadMidSamp, quadMidLine)) {
539  if((quad->eline - quad->sline) < p_endQuadSize) {
540  SlowQuad(quadTree, trans, lineMap, sampMap);
541  }
542  else {
543  SplitQuad(quadTree);
544  }
545  return;
546  }
547 
548  double cmidLine = lineCoef[0] * quadMidLine +
549  lineCoef[1] * quadMidSamp +
550  lineCoef[2] * quadMidLine * quadMidSamp +
551  lineCoef[3];
552 
553  double cmidSamp = sampCoef[0] * quadMidLine +
554  sampCoef[1] * quadMidSamp +
555  sampCoef[2] * quadMidLine * quadMidSamp +
556  sampCoef[3];
557 
558  if((abs(cmidSamp - midSamp) > 0.5) || (abs(cmidLine - midLine) > 0.5)) {
559  if((quad->eline - quad->sline) < p_endQuadSize) {
560  SlowQuad(quadTree, trans, lineMap, sampMap);
561  }
562  else {
563  SplitQuad(quadTree);
564  }
565  return;
566  }
567 
568  // Equations are suitably accurate.
569  // First compute input at the top corner of the output quad
570  double ulLine = lineCoef[0] * (double) quad->sline +
571  lineCoef[1] * (double) quad->ssamp +
572  lineCoef[2] * (double) quad->sline * (double) quad->ssamp +
573  lineCoef[3];
574 
575  double ulSamp = sampCoef[0] * (double) quad->sline +
576  sampCoef[1] * (double) quad->ssamp +
577  sampCoef[2] * (double) quad->sline * (double) quad->ssamp +
578  sampCoef[3];
579 
580  // Compute the derivate of the equations with respect to the
581  // output line as we will be changing the output line in a loop
582  double lineChangeWrLine = lineCoef[0] + lineCoef[2] * (double) quad->ssamp;
583  double sampChangeWrLine = sampCoef[0] + sampCoef[2] * (double) quad->ssamp;
584 
585  for(int ol = quad->sline; ol <= quad->eline; ol++) {
586  // Now Compute the derivates of the equations with respect to the
587  // output sample at the current line
588  double lineChangeWrSamp = lineCoef[1] + lineCoef[2] * (double) ol;
589  double sampChangeWrSamp = sampCoef[1] + sampCoef[2] * (double) ol;
590 
591  // Set first computed line to the left-edge position
592  double cline = ulLine;
593  double csamp = ulSamp;
594 
595  // Get pointers to speed processing
596  int startSamp = quad->ssamp - quad->ssampTile;
597  std::vector<double> &lineVect = lineMap[ol-quad->slineTile];
598  std::vector<double> &sampleVect = sampMap[ol-quad->slineTile];
599 
600  // Loop computing input positions for respective output positions
601  for(int os = quad->ssamp; os <= quad->esamp; os++) {
602  lineVect[startSamp] = cline;
603  sampleVect[startSamp] = csamp;
604 
605  startSamp ++;
606 
607  cline += lineChangeWrSamp;
608  csamp += sampChangeWrSamp;
609  }
610 
611  // Reposition at the left edge of the tile for the next line
612  ulLine += lineChangeWrLine;
613  ulSamp += sampChangeWrLine;
614  }
615 
616  // All done so remove the quad
617  delete quad;
618  quadTree.erase(quadTree.begin());
619  }
620 
621  // Break input quad into four pieces
622  void ProcessRubberSheet::SplitQuad(std::vector<Quad *> &quadTree) {
623  // Get the quad to split
624  Quad *quad = quadTree[0];
625  int n = (quad->eline - quad->sline + 1) / 2;
626 
627  // New upper left quad
628  Quad *q1 = new Quad;
629  *q1 = *quad;
630  q1->eline = quad->sline + n - 1;
631  q1->esamp = quad->ssamp + n - 1;
632  quadTree.push_back(q1);
633 
634  // New upper right quad
635  Quad *q2 = new Quad;
636  *q2 = *quad;
637  q2->eline = quad->sline + n - 1;
638  q2->ssamp = quad->ssamp + n;
639  quadTree.push_back(q2);
640 
641  // New lower left quad
642  Quad *q3 = new Quad;
643  *q3 = *quad;
644  q3->sline = quad->sline + n;
645  q3->esamp = quad->ssamp + n - 1;
646  quadTree.push_back(q3);
647 
648  // New lower right quad
649  Quad *q4 = new Quad;
650  *q4 = *quad;
651  q4->sline = quad->sline + n;
652  q4->ssamp = quad->ssamp + n;
653  quadTree.push_back(q4);
654 
655  // Remove the old quad since it has been split up
656  delete quad;
657  quadTree.erase(quadTree.begin());
658  }
659 
660 
661  // Slow quad computation for every output pixel
662  void ProcessRubberSheet::SlowQuad(std::vector<Quad *> &quadTree,
663  Transform &trans,
664  std::vector< std::vector<double> > &lineMap,
665  std::vector< std::vector<double> > &sampMap)
666  {
667  // Get the quad
668  Quad *quad = quadTree[0];
669  double iline, isamp;
670 
671  // Loop and do the slow computation of input position from output position
672  for(int oline = quad->sline; oline <= quad->eline; oline++) {
673  int lineIndex = oline - quad->slineTile;
674  for(int osamp = quad->ssamp; osamp <= quad->esamp; osamp++) {
675  int sampIndex = osamp - quad->ssampTile;
676  lineMap[lineIndex][sampIndex] = NULL8;
677  if(trans.Xform(isamp, iline, (double) osamp, (double) oline)) {
678  if((isamp >= 0.5) ||
679  (iline >= 0.5) ||
680  (iline <= InputCubes[0]->lineCount() + 0.5) ||
681  (isamp <= InputCubes[0]->sampleCount() + 0.5)) {
682  lineMap[lineIndex][sampIndex] = iline;
683  sampMap[lineIndex][sampIndex] = isamp;
684  }
685  }
686  }
687  }
688 
689  // All done with the quad
690  delete quad;
691  quadTree.erase(quadTree.begin());
692  }
693 
694  // Determinate method for 4x4 matrix using cofactor expansion
695  double ProcessRubberSheet::Det4x4(double m[4][4]) {
696  double cofact[3][3];
697 
698  cofact [0][0] = m[1][1];
699  cofact [0][1] = m[1][2];
700  cofact [0][2] = m[1][3];
701  cofact [1][0] = m[2][1];
702  cofact [1][1] = m[2][2];
703  cofact [1][2] = m[2][3];
704  cofact [2][0] = m[3][1];
705  cofact [2][1] = m[3][2];
706  cofact [2][2] = m[3][3];
707  double det = m[0][0] * Det3x3(cofact);
708 
709  cofact [0][0] = m[1][0];
710  cofact [0][1] = m[1][2];
711  cofact [0][2] = m[1][3];
712  cofact [1][0] = m[2][0];
713  cofact [1][1] = m[2][2];
714  cofact [1][2] = m[2][3];
715  cofact [2][0] = m[3][0];
716  cofact [2][1] = m[3][2];
717  cofact [2][2] = m[3][3];
718  det -= m[0][1] * Det3x3(cofact);
719 
720  cofact [0][0] = m[1][0];
721  cofact [0][1] = m[1][1];
722  cofact [0][2] = m[1][3];
723  cofact [1][0] = m[2][0];
724  cofact [1][1] = m[2][1];
725  cofact [1][2] = m[2][3];
726  cofact [2][0] = m[3][0];
727  cofact [2][1] = m[3][1];
728  cofact [2][2] = m[3][3];
729  det += m[0][2] * Det3x3(cofact);
730 
731  cofact [0][0] = m[1][0];
732  cofact [0][1] = m[1][1];
733  cofact [0][2] = m[1][2];
734  cofact [1][0] = m[2][0];
735  cofact [1][1] = m[2][1];
736  cofact [1][2] = m[2][2];
737  cofact [2][0] = m[3][0];
738  cofact [2][1] = m[3][1];
739  cofact [2][2] = m[3][2];
740  det -= m[0][3] * Det3x3(cofact);
741 
742  return det;
743  }
744 
745  // Determinate for 3x3 matrix
746  double ProcessRubberSheet::Det3x3(double m[3][3]) {
747  return m[0][0] * m[1][1] * m[2][2] -
748  m[0][0] * m[1][2] * m[2][1] -
749  m[0][1] * m[1][0] * m[2][2] +
750  m[0][1] * m[1][2] * m[2][0] +
751  m[0][2] * m[1][0] * m[2][1] -
752  m[0][2] * m[1][1] * m[2][0];
753  }
754 
778  void ProcessRubberSheet::processPatchTransform(Transform &trans,
779  Interpolator &interp) {
780  // Error checks ... there must be one input and one output
781  if(InputCubes.size() != 1) {
782  string m = "You must specify exactly one input cube";
783  throw IException(IException::Programmer, m, _FILEINFO_);
784  }
785  else if(OutputCubes.size() != 1) {
786  string m = "You must specify exactly one output cube";
787  throw IException(IException::Programmer, m, _FILEINFO_);
788  }
789 
790  // Create a portal buffer for reading from the input file
791  Portal iportal(interp.Samples(), interp.Lines(),
792  InputCubes[0]->pixelType() ,
793  interp.HotSample(), interp.HotLine());
794 
795  // Create a buffer for writing to the output file
796  Brick obrick(*OutputCubes[0],1,1,1);
797 
798  // Setup the progress meter
799  int patchesPerBand = 0;
800  for (int line = m_patchStartLine; line <= InputCubes[0]->lineCount();
801  line += m_patchLineIncrement) {
802  for (int samp = m_patchStartSample;
803  samp <= InputCubes[0]->sampleCount();
804  samp += m_patchSampleIncrement) {
805  patchesPerBand++;
806  }
807  }
808 
809  int patchCount = InputCubes[0]->bandCount() * patchesPerBand;
810  p_progress->SetMaximumSteps(patchCount);
811  p_progress->CheckStatus();
812 
813  // For each band we will loop through the input file and work on small
814  // spatial patches (nxm). The objective is to determine where each patch
815  // falls in the output file and transform that output patch. As we loop
816  // we want some overlap in the input patches which guarantees there will
817  // be no gaps in the output cube.
818  //
819  // We use the variables m_patchLines and m_patchSamples to define
820  // the patch size
821  //
822  // We use the variables m_patchStartLine and m_patchStartSample to define
823  // where to start in the cube. In almost all cases these should be (1,1).
824  // An expection would be for PushFrameCameras which which need to have
825  // a different starting line for the even framed cubes
826  //
827  // We use the variables m_patchLineIncrement and m_patchSampleIncrement
828  // to skip to the next patch. Typically these are one less than the
829  // patch size which guarantees a pixel of overlap in the patches. An
830  // expection would be for PushFrameCameras which should increment lines by
831  // twice the framelet height.
832 
833  for (int band=1; band <= InputCubes[0]->bandCount(); band++) {
834  if(p_bandChangeFunct != NULL) p_bandChangeFunct(band);
835  iportal.SetPosition(1,1,band);
836 
837  for (int line = m_patchStartLine; line <= InputCubes[0]->lineCount();
838  line += m_patchLineIncrement) {
839  for (int samp = m_patchStartSample;
840  samp <= InputCubes[0]->sampleCount();
841  samp += m_patchSampleIncrement, p_progress->CheckStatus()) {
842  transformPatch((double)samp, (double)(samp + m_patchSamples - 1),
843  (double)line, (double)(line + m_patchLines - 1),
844  obrick, iportal, trans, interp);
845  }
846  }
847  }
848  }
849 
850 
851  // Private method to process a small patch of the input cube
852  void ProcessRubberSheet::transformPatch (double ssamp, double esamp,
853  double sline, double eline,
854  Brick &obrick, Portal &iportal,
855  Transform &trans,
856  Interpolator &interp) {
857  // Let's make sure our patch is contained in the input file
858  // TODO: Think about the image edges should I be adding 0.5
859  if (esamp > InputCubes[0]->sampleCount()) {
860  esamp = InputCubes[0]->sampleCount();
861  if (ssamp == esamp) ssamp = esamp - 1;
862  }
863  if (eline > InputCubes[0]->lineCount()) {
864  eline = InputCubes[0]->lineCount();
865  if (sline == eline) sline = eline - 1;
866  }
867 
868  // Use these to build a list of four corner control points
869  QVector<double> isamps;
870  QVector<double> ilines;
871  QVector<double> osamps;
872  QVector<double> olines;
873 
874  // Upper left control point
875  double tline, tsamp;
876  if (trans.Xform(tsamp,tline,ssamp,sline)) {
877  isamps.push_back(ssamp);
878  ilines.push_back(sline);
879  osamps.push_back(tsamp);
880  olines.push_back(tline);
881  }
882 
883  // Upper right control point
884  if (trans.Xform(tsamp,tline,esamp,sline)) {
885  isamps.push_back(esamp);
886  ilines.push_back(sline);
887  osamps.push_back(tsamp);
888  olines.push_back(tline);
889  }
890 
891  // Lower left control point
892  if (trans.Xform(tsamp,tline,ssamp,eline)) {
893  isamps.push_back(ssamp);
894  ilines.push_back(eline);
895  osamps.push_back(tsamp);
896  olines.push_back(tline);
897  }
898 
899  // Lower right control point
900  if (trans.Xform(tsamp,tline,esamp,eline)) {
901  isamps.push_back(esamp);
902  ilines.push_back(eline);
903  osamps.push_back(tsamp);
904  olines.push_back(tline);
905  }
906 
907  // If no points we are lost in space. It's possible a very small
908  // target could be completely contained in the patch. Unlikely and if
909  // so why would we be projecting it??
910  if (isamps.size() == 0) return;
911 
912  // All four corners must be good. If not break it down more
913  if (isamps.size() < 4) {
914  splitPatch(ssamp, esamp, sline, eline, obrick, iportal, trans, interp);
915  return;
916  }
917 
918  // Get the min/max output line/sample patch
919  int osampMin = (int) osamps[0];
920  int osampMax = (int) (osamps[0] + 0.5);
921  int olineMin = (int) olines[0];
922  int olineMax = (int) (olines[0] + 0.5);
923  for (int i = 1; i < osamps.size(); i++) {
924  if (osamps[i] < osampMin) osampMin = (int) osamps[i];
925  if (osamps[i] > osampMax) osampMax = (int) (osamps[i] + 0.5);
926  if (olines[i] < olineMin) olineMin = (int) olines[i];
927  if (olines[i] > olineMax) olineMax = (int) (olines[i] + 0.5);
928  }
929 
930  // Check to see if the output patch is completely outside the image. No
931  // sense in computing the affine if we don't have to.
932  if (osampMax < 1) return;
933  if (olineMax < 1) return;
934  if (osampMin > OutputCubes[0]->sampleCount()) return;
935  if (olineMin > OutputCubes[0]->lineCount()) return;
936 
937  // Adjust our output patch if it extends outside the output cube
938  if (osampMin < 1) osampMin = 1;
939  if (olineMin < 1) olineMin = 1;
940  if (osampMax > OutputCubes[0]->sampleCount()) {
941  osampMax = OutputCubes[0]->sampleCount();
942  }
943  if (olineMax > OutputCubes[0]->lineCount()) {
944  olineMax = OutputCubes[0]->lineCount();
945  }
946 
947  /* A small input patch should create a small output patch. If we had
948  * the 0-360 seam (or -180/180) in our patch it could be split across
949  * a cylindrical projection (e.g., equirectangular, simple, etc). So
950  * If the output patch looks like it will span the full output image,
951  * either lines or sample then resplit the input patch. When the patch
952  * spans more than 50% (was 99% but there were problems with double
953  * rounding error on different machines) of the image it is split.
954  */
955 
956  if (osampMax - osampMin + 1.0 > OutputCubes[0]->sampleCount() * 0.50) {
957  splitPatch(ssamp, esamp, sline, eline, obrick, iportal, trans, interp);
958  return;
959  }
960  if (olineMax - olineMin + 1.0 > OutputCubes[0]->lineCount() * 0.50) {
961  splitPatch(ssamp, esamp, sline, eline, obrick, iportal, trans, interp);
962  return;
963  }
964 
965  // Can we create an affine transform from output to input coordinates
966  BasisFunction isampFunc("Ax+By+C",3,3);
967  LeastSquares isampLSQ(isampFunc);
968 
969  BasisFunction ilineFunc("Dx+Ey+F",3,3);
970  LeastSquares ilineLSQ(ilineFunc);
971 
972  try {
973  for (int i=0; i < isamps.size(); i++) {
974  std::vector<double> vars;
975  vars.push_back(osamps[i]);
976  vars.push_back(olines[i]);
977  vars.push_back(1.0);
978 
979  isampLSQ.AddKnown(vars,isamps[i]);
980  ilineLSQ.AddKnown(vars,ilines[i]);
981  }
982 
983  isampLSQ.Solve(LeastSquares::QRD);
984  ilineLSQ.Solve(LeastSquares::QRD);
985  }
986  catch (IException &e) {
987  splitPatch(ssamp, esamp, sline, eline, obrick, iportal, trans, interp);
988  return;
989  }
990 
991  // If the fit at any corner isn't good enough break it down
992  for (int i=0; i<isamps.size(); i++) {
993  if (fabs(isampLSQ.Residual(i)) > 0.5) {
994  splitPatch(ssamp, esamp, sline, eline, obrick, iportal, trans, interp);
995  return;
996  }
997  if (fabs(ilineLSQ.Residual(i)) > 0.5) {
998  splitPatch(ssamp, esamp, sline, eline, obrick, iportal, trans, interp);
999  return;
1000  }
1001  }
1002 
1003 #if 0
1004  // What about the center point
1005  // TODO: The patches are so small so maybe we don't care if the
1006  // corners worked
1007  double csamp = (ssamp + esamp) / 2.0;
1008  double cline = (sline + eline) / 2.0;
1009  if (trans.Xform(tsamp,tline,csamp,cline)) {
1010  std::vector<double> vars;
1011  vars.push_back(tsamp);
1012  vars.push_back(tline);
1013  vars.push_back(1.0);
1014 
1015  double isamp = isampFunc.Evaluate(vars);
1016  double iline = ilineFunc.Evaluate(vars);
1017 
1018  double err = (csamp - isamp) * (csamp - isamp) +
1019  (cline - iline) * (cline - iline);
1020  if (err > 0.25) {
1021  splitPatch(ssamp, esamp, sline, eline, obrick, iportal, trans, interp);
1022  return;
1023  }
1024  }
1025  else {
1026  splitPatch(ssamp, esamp, sline, eline, obrick, iportal, trans, interp);
1027  return;
1028  }
1029 #endif
1030 
1031  double A = isampFunc.Coefficient(0);
1032  double B = isampFunc.Coefficient(1);
1033  double C = isampFunc.Coefficient(2);
1034 
1035  double D = ilineFunc.Coefficient(0);
1036  double E = ilineFunc.Coefficient(1);
1037  double F = ilineFunc.Coefficient(2);
1038 
1039  // Now we can do our typical backwards geom. Loop over the output cube
1040  // coordinates and compute input cube coordinates writing pixels 1-by-1
1041  for (int oline = olineMin; oline <= olineMax; oline++) {
1042  double isamp = A * osampMin + B * oline + C;
1043  double iline = D * osampMin + E * oline + F;
1044  double isampChangeWRTosamp = A;
1045  double ilineChangeWRTosamp = D;
1046  for (int osamp = osampMin; osamp <= osampMax; osamp++,
1047  isamp += isampChangeWRTosamp, iline += ilineChangeWRTosamp) {
1048 
1049  // Now read the data around the input coordinate and interpolate a DN
1050  iportal.SetPosition(isamp, iline, iportal.Band());
1051  InputCubes[0]->read(iportal);
1052  double dn = interp.Interpolate(isamp, iline, iportal.DoubleBuffer());
1053 
1054  // Write the output value at the appropriate location
1055  if (dn != Null) {
1056  obrick.SetBasePosition(osamp,oline,iportal.Band());
1057  obrick[0] = dn;
1058  OutputCubes[0]->write(obrick);
1059  }
1060  }
1061  }
1062  }
1063 
1064  // Private method used to split up input patches if the patch is too big to
1065  // process
1066  void ProcessRubberSheet::splitPatch (double ssamp, double esamp,
1067  double sline, double eline,
1068  Brick &obrick, Portal &iportal,
1069  Transform &trans, Interpolator &interp) {
1070 
1071  // Is the input patch too small to even worry about transforming?
1072  if ((esamp - ssamp < 0.1) && (eline - sline < 0.1)) return;
1073 
1074  // It's big enough so break it into four pieces
1075  double midSamp = (esamp + ssamp) / 2.0;
1076  double midLine = (eline + sline) / 2.0;
1077 
1078  transformPatch(ssamp, midSamp,
1079  sline, midLine,
1080  obrick, iportal, trans, interp);
1081  transformPatch(midSamp, esamp,
1082  sline, midLine,
1083  obrick, iportal, trans, interp);
1084  transformPatch(ssamp, midSamp,
1085  midLine, eline,
1086  obrick, iportal, trans, interp);
1087  transformPatch(midSamp, esamp,
1088  midLine, eline,
1089  obrick, iportal, trans, interp);
1090 
1091  return;
1092  }
1093 
1094 
1095 } // end namespace isis
int Line(const int index=0) const
Returns the line position associated with a shape buffer index.
Definition: Buffer.cpp:161
double HotSample()
Returns the sample coordinate of the center pixel in the buffer for the interpolator.
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:109
const double E(2.7182818284590452354)
Sets some basic constants for use in ISIS programming.
bool end() const
Returns true if the shape buffer has accessed the end of the cube.
double Interpolate(const double isamp, const double iline, const double buf[])
Performs an interpolation on the data according to the parameters set in the constructor.
Buffer for containing a two dimensional section of an image.
Definition: Portal.h:52
Buffer for containing a three dimensional section of an image.
Definition: Brick.h:60
void SetBasePosition(const int start_sample, const int start_line, const int start_band)
This method is used to set the base position of the shape buffer.
Definition: Brick.h:112
virtual bool Xform(double &inSample, double &inLine, const double outSample, const double outLine)=0
Transforms the given output line and sample to the corresponding output line and sample.
double * DoubleBuffer() const
Returns the value of the shape buffer.
Definition: Buffer.h:153
int Sample(const int index=0) const
Returns the sample position associated with a shape buffer index.
Definition: Buffer.cpp:143
This algorithm is designed for applications that jump around between a couple of spots in the cube wi...
bool begin()
Moves the shape buffer to the first position.
This algorithm is designed for applications that jump around between a couple of spots in the cube wi...
int Tiles()
Returns the number of Tiles in the cube.
Definition: TileManager.h:76
double HotLine()
Returns the line coordinate of the center pixel in the buffer for the interpolator.
int Samples()
Returns the number of samples needed by the interpolator.
int Band(const int index=0) const
Returns the band position associated with a shape buffer index.
Definition: Buffer.cpp:178
bool SetTile(const int Tile, const int band=1)
Sets the current tile as requested.
Definition: TileManager.cpp:66
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:38
void SetPosition(const double sample, const double line, const int band)
Sets the line and sample position of the buffer.
Definition: Portal.h:109
int Lines()
Returns the number of lines needed by the interpolator.
Pixel interpolator.
Definition: Interpolator.h:51
int size() const
Returns the total number of pixels in the shape buffer.
Definition: Buffer.h:112
Isis exception class.
Definition: IException.h:99
Pixel transformation.
Definition: Transform.h:89
Buffer manager, for moving through a cube in tiles.
Definition: TileManager.h:55

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the ISIS Support Center
File Modified: 07/12/2023 23:26:35