USGS

Isis 3.0 Application Source Code Reference

Home

hicubenorm.cpp

Go to the documentation of this file.
00001 #include "Isis.h"
00002 
00003 // system include files go first
00004 #include <string>
00005 #include <iostream>
00006 #include <iomanip>
00007 #include <vector>
00008 #include <algorithm>
00009 
00010 // Isis specific include files go next
00011 #include "ProcessByTile.h"
00012 #include "ProcessByLine.h"
00013 #include "SpecialPixel.h"
00014 #include "iException.h"
00015 #include "Pvl.h"
00016 #include "Statistics.h"
00017 #include "VecFilter.h"
00018 
00019 using namespace std;
00020 using namespace Isis;
00021 
00022 // These global vectors are used to keep track of info for columns 
00023 // of image data.  For example, a 100 sample x 200 line x 2 band cube will
00024 // have a vectors of 200 columns (100 samples x 2 bands).
00025 vector<double> stddev;
00026 vector<int> validpixels;
00027 vector<double> minimum;
00028 vector<double> maximum;
00029 vector<int> band;
00030 vector<int> element;
00031 vector<double> median;
00032 vector<double> average;
00033 vector<double> normalizer;
00034 
00035 // Size of the cube
00036 int totalLines;
00037 int totalSamples;
00038 
00039 enum Mode {SUBTRACT,DIVIDE};
00040 
00041 // function prototypes
00042 void getStats(Buffer &in);
00043 void multiply(Buffer &in, Buffer &out);
00044 void subtract(Buffer &in, Buffer &out);
00045 void pvlOut(const string &pv);
00046 void tableOut(const string &pv);
00047 void PVLIn(const Isis::Filename & filename);
00048 void tableIn(const Isis::Filename & filename);
00049 void keepSame(int &totalBands, int &rowcol, Mode mode);
00050 void filterStats(vector<double> &filter,int &filtsize, bool &pause_crop,
00051   int &channel);
00052 
00053 // Main Program
00054 void IsisMain() {
00055   vector<double> filter;
00056   int rowcol;                    // how many rows or cols per band
00057   bool normalizeUsingAverage;    // mult/sub using average or median?
00058   int totalBands;
00059   // Used for filtering the initial cubenorm averages and median values
00060   int filtsize;
00061   bool pause_crop;
00062   int channel;
00063   // ERROR CHECK:  The user must specify at least the TO or STATS
00064   // parameters.
00065   UserInterface &ui = Application::GetUserInterface();
00066   if (!(ui.WasEntered("TO")) && !(ui.WasEntered("STATS"))) {
00067     string msg = "User must specify a TO and/or STATS file.";
00068     throw iException::Message(iException::User,msg,_FILEINFO_);
00069   }
00070 
00071   // We will be processing by tile.
00072   ProcessByTile p;
00073 
00074   // Setup the input cube;
00075   // Obtain information from the input file
00076   Cube *icube = p.SetInputCube("FROM");
00077   totalSamples = icube->Samples();
00078   totalLines   = icube->Lines();
00079   totalBands   = icube->Bands();
00080   channel = icube->GetGroup("Instrument")["ChannelNumber"];
00081 
00082   // Setup the tile size for columnar processing
00083   p.SetTileSize(1, totalLines);
00084   rowcol = totalSamples;
00085 
00086   // Now get the statistics for each column
00087   normalizeUsingAverage = ui.GetString("NORMALIZER") == "AVERAGE";
00088 
00089   //gather statistics
00090   if (ui.GetString("STATSOURCE") == "CUBE"){
00091     p.StartProcess(getStats);
00092   }
00093   else if (ui.GetString("STATSOURCE") == "TABLE"){
00094     tableIn(ui.GetFilename("FROMSTATS"));
00095   }
00096   else{
00097     PVLIn(ui.GetFilename("FROMSTATS"));
00098   }
00099  
00100   //check to make sure the first vector has as many elements as the last
00101   // vector, and that there is a vector element for each row/col
00102   if (band.size() != (unsigned int)(rowcol*totalBands)) { 
00103     string message = "You have entered an invalid input file " + 
00104       ui.GetFilename("FROMSTATS");
00105     throw  iException::Message(Isis::iException::Io, message,
00106          _FILEINFO_);
00107   }
00108 
00109   //get information needed to filter the statistics
00110   filtsize = ui.GetInteger("FILTER");
00111   pause_crop = ui.GetBoolean("PAUSECROP");
00112 
00113   //filter the column averages
00114   filter = average;
00115   filterStats(filter,filtsize,pause_crop,channel);
00116   average = filter;
00117 
00118   //filter the column medians
00119   filter = median;
00120   filterStats(filter,filtsize,pause_crop,channel);
00121   median = filter;
00122 
00123   //If a STATS file was specified then create statistics file
00124   if (ui.WasEntered("STATS")) {
00125      string op = ui.GetString("FORMAT");
00126      if (op == "PVL")    pvlOut(ui.GetFilename("STATS"));
00127      if (op == "TABLE")  tableOut(ui.GetFilename("STATS"));
00128   }
00129 
00130   // Update the statistics vectors before creating the output
00131   // file
00132   if (normalizeUsingAverage) {
00133     normalizer = average;
00134   } else {
00135     normalizer = median;
00136   }
00137 
00138   // If an output file was specified then normalize the cube
00139   if (ui.WasEntered ("TO")) {
00140     // Before creating a normalized cube check to see if there
00141     // are any column averages less than or equal to zero.
00142     if (ui.GetString("MODE") == "MULTIPLY") {
00143       for (unsigned int i=0; i<band.size(); i++) {
00144         if (IsValidPixel(normalizer[i]) && normalizer[i]<=0.0) {
00145           string msg = "Cube file can not be normalized with [MULTIPLY] ";
00146           msg += "option, some column averages <= 0.0";
00147           throw iException::Message(iException::User,msg,_FILEINFO_);
00148         }
00149       }
00150     }
00151 
00152     // Setup the output file and apply the coefficients by either
00153     // subtracting or multipling them
00154     p.SetOutputCube("TO");
00155 
00156     // Should we preserve the average/median of the input image???
00157     if (ui.GetBoolean("PRESERVE")) {
00158       if (ui.GetString("MODE") == "SUBTRACT") {
00159         keepSame(totalBands,rowcol,SUBTRACT);
00160       } else {
00161         keepSame(totalBands,rowcol,DIVIDE);
00162       }
00163     }
00164 
00165     // Process based on the mode
00166     if (ui.GetString("MODE") == "SUBTRACT") {
00167       p.StartProcess(subtract);
00168     }
00169     else {
00170       p.StartProcess(multiply);
00171     }
00172   }
00173 
00174   // Cleanup
00175   p.EndProcess();
00176   stddev.clear();
00177   validpixels.clear();
00178   minimum.clear();
00179   maximum.clear();
00180   band.clear();
00181   element.clear();
00182   median.clear();
00183   average.clear();
00184   normalizer.clear();
00185   filter.clear();
00186 }
00187 
00188 //**********************************************************
00189 // DOUSER - Get statistics on a column or row of pixels
00190 //**********************************************************
00191 void getStats(Buffer &in) {
00192   Statistics stats;
00193   stats.AddData(in.DoubleBuffer(),in.size());
00194   
00195   band.push_back(in.Band());
00196   element.push_back(in.Sample());
00197 
00198   // Sort the input buffer
00199   vector<double> pixels;
00200   for (int i=0; i<in.size(); i++) {
00201     if (IsValidPixel(in[i])) pixels.push_back(in[i]);
00202   }
00203   sort(pixels.begin(),pixels.end());
00204 
00205   // Now obtain the median value and store in the median vector
00206   int size = pixels.size();
00207   if (size != 0) {
00208     int med = size/2;
00209     if (size%2 == 0) {
00210       median.push_back((pixels[med-1]+pixels[med])/2.0);
00211     }
00212     else {
00213       median.push_back(pixels[med]);
00214     }
00215   }
00216   else {
00217     median.push_back(Isis::Null);
00218   }
00219 
00220   // Store the statistics in the appropriate vectors
00221   average.push_back(stats.Average());
00222   stddev.push_back(stats.StandardDeviation());
00223   validpixels.push_back(stats.ValidPixels());
00224   minimum.push_back(stats.Minimum());
00225   maximum.push_back(stats.Maximum());
00226 }
00227 
00228 //********************************************************
00229 // Create PVL output of statistics
00230 //*******************************************************
00231 void pvlOut(const string &StatFile) {
00232   PvlGroup results ("Results");
00233   for (unsigned int i=0; i <band.size(); i++) {
00234     results += PvlKeyword("Band",band[i]);
00235     results += PvlKeyword("RowCol",element[i]);
00236     results += PvlKeyword("ValidPixels",validpixels[i]);
00237     if (validpixels[i] > 0) {
00238       results += PvlKeyword("Mean",average[i]);
00239       results += PvlKeyword("Median",median[i]);
00240       results += PvlKeyword("Std",stddev[i]);
00241       results += PvlKeyword("Minimum",minimum[i]);
00242       results += PvlKeyword("Maximum",maximum[i]);
00243     }
00244     else {
00245       results += PvlKeyword("Mean",0.0);
00246       results += PvlKeyword("Median",0.0);
00247       results += PvlKeyword("Std",0.0);
00248       results += PvlKeyword("Minimum",0.0);
00249       results += PvlKeyword("Maximum",0.0);
00250     }
00251   }
00252 
00253   Pvl t;
00254   t.AddGroup(results);
00255   t.Write(StatFile);
00256 }
00257 
00258 //********************************************************
00259 // Create Tabular output of statistics
00260 //*******************************************************
00261 void tableOut(const string &StatFile) {
00262   // Open output file
00263   // TODO check status and throw error
00264   ofstream out;
00265   out.open(StatFile.c_str(),std::ios::out);
00266 
00267   // Output a header
00268   out << std::setw(8)  << "Band";
00269   out << std::setw(8)  << "RowCol";
00270   out << std::setw(15) << "ValidPoints";
00271   out << std::setw(15) << "Average";
00272   out << std::setw(15) << "Median";
00273   out << std::setw(15) << "StdDev";
00274   out << std::setw(15) << "Minimum";
00275   out << std::setw(15) << "Maximum";
00276   out << endl;
00277 
00278   // Print out the table results
00279   for (unsigned int i=0; i<band.size(); i++) {
00280     out << std::setw(8)  << band[i];
00281     out << std::setw(8)  << element[i];
00282     out << std::setw(15) << validpixels[i];
00283     if (validpixels[i] > 0) {
00284       out << std::setw(15) << average[i];
00285       out << std::setw(15) << median[i];
00286       //Make sure the table's SD is 0 for RowCols with 1 or less valid pixels
00287       if( validpixels[i] > 1 ) {
00288         out << std::setw(15) << stddev[i];
00289       }
00290       else {
00291         out << std::setw(15) << 0;
00292       }
00293       out << std::setw(15) << minimum[i];
00294       out << std::setw(15) << maximum[i];
00295     }
00296     else {
00297       out << std::setw(15) << 0;
00298       out << std::setw(15) << 0;
00299       out << std::setw(15) << 0;
00300       out << std::setw(15) << 0;
00301       out << std::setw(15) << 0;
00302     }
00303     out << endl;
00304   }
00305   out.close();
00306 }
00307 
00308 //********************************************************
00309 // Gather statistics from a PVL input file
00310 //*******************************************************
00311 void PVLIn(const Isis::Filename &filename){
00312   Pvl pvlFileIn;
00313   pvlFileIn.Read(filename.Name());
00314   PvlGroup results = pvlFileIn.FindGroup("Results");
00315   PvlObject::PvlKeywordIterator itr = results.Begin();
00316   
00317   while (itr != results.End()){
00318     band.push_back((*itr)[0]);
00319     itr++;
00320     element.push_back((*itr)[0]);
00321     itr++;
00322     validpixels.push_back((*itr)[0]);
00323     itr++;
00324     average.push_back((*itr)[0]);
00325     itr++;
00326     median.push_back((*itr)[0]);
00327     itr++;
00328     stddev.push_back((*itr)[0]);
00329     itr++;
00330     minimum.push_back((*itr)[0]);
00331     itr++;
00332     maximum.push_back((*itr)[0]);
00333     itr++;
00334   }
00335 }
00336 
00337 //********************************************************
00338 // Gather statistics from a table input file
00339 //*******************************************************
00340 void tableIn(const Isis::Filename & filename){
00341   ifstream in;
00342   string expanded(filename.Expanded());
00343   in.open(expanded.c_str(),std::ios::in);
00344   
00345   
00346   if (!in){
00347     string message = "Error opening " + filename.Expanded();
00348     throw  iException::Message(Isis::iException::Io, message,
00349          _FILEINFO_);
00350   }
00351   
00352   //skip the header (106 bytes)
00353   in.seekg(106);
00354   
00355 
00356   //read it
00357   iString inString;
00358   while (in >> inString){
00359     band.push_back(inString);
00360     in>> inString;
00361     element.push_back(inString);
00362     in>> inString;
00363     validpixels.push_back(inString);
00364     in >> inString;
00365     average.push_back(inString);
00366     in >> inString;
00367     median.push_back(inString);
00368     in >> inString;
00369     stddev.push_back(inString);
00370     in >> inString;
00371     minimum.push_back(inString);
00372     in >> inString;
00373     maximum.push_back(inString);
00374     //Make sure Standard Deviation is not < 0 when reading in from a table
00375     vector<double>::iterator p;
00376     p = stddev.end() - 1;
00377     if( *p < 0 ) {
00378       *p = 0;
00379     }
00380   }
00381   in.close();
00382 }
00383 
00384 //********************************************************
00385 // Compute coefficients such that when we subtract/divide 
00386 // using the coefficient the average or median of the 
00387 // output image stays the same
00388 void keepSame(int &totalBands,int &rowcol,Mode mode) {
00389   // Loop for each band
00390   for (int iband=1; iband<=totalBands; iband++) {
00391     double sumAverage = 0.0;
00392     double sumValidPixels = 0;
00393     for (int i=0; i<rowcol; i++) {
00394       int index = (iband - 1) * rowcol + i;
00395       if (IsValidPixel(normalizer[index])) {
00396         sumAverage += normalizer[index] * validpixels[index];
00397         sumValidPixels += validpixels[index];
00398       }
00399     }
00400 
00401     // Neither sumValidPixels nor totalAverage will be zero
00402     // because of a test done earlier in IsisMain
00403     double totalAverage = sumAverage/sumValidPixels;
00404     for (int i=0; i<rowcol; i++) {
00405       int index = (iband - 1) * rowcol + i;
00406       if (IsValidPixel(normalizer[index])) {
00407         if (mode == SUBTRACT) {
00408           normalizer[index] = normalizer[index] - totalAverage;
00409         } else {
00410           normalizer[index] = normalizer[index] / totalAverage;
00411         }
00412       }
00413     }
00414   }
00415 }
00416 
00417 // Apply coefficients multiplicatively
00418 void multiply(Buffer &in, Buffer &out) {
00419   // Compute the index into the normalizer array
00420   // We have to tweak the index based on the shape of the buffer
00421   // either a column or line
00422   int index;
00423   if (in.SampleDimension() == 1) {
00424     index = (in.Band() - 1) * totalSamples;   // Get to the proper band
00425     index += in.Sample() - 1;                 // Get to the proper column
00426   }
00427   else {
00428     index = (in.Band() - 1) * totalLines;     // Get to the proper band
00429     index += in.Line() - 1;                   // Get to the proper row
00430   }
00431   double coeff = normalizer[index];
00432 
00433   // Now loop and apply the coefficents
00434   for (int i=0; i<in.size(); i++) {
00435     if (IsSpecial(in[i])) {
00436       out[i] = in[i];
00437     }
00438     else {
00439       out[i] = Null;
00440       if (coeff != 0.0 && IsValidPixel(coeff)) {
00441         out[i] = in[i]/coeff;
00442       }
00443     }
00444   }
00445 }
00446 
00447 // Apply coefficients subtractively
00448 void subtract(Buffer &in, Buffer &out) {
00449   // Compute the index into the normalizer array
00450   // We have to tweak the index based on the shape of the buffer
00451   // either a column or line
00452   int index;
00453   if (in.SampleDimension() == 1) {
00454     index = (in.Band() - 1) * totalSamples;   // Get to the proper band
00455     index += in.Sample() - 1;                 // Get to the proper column
00456   }
00457   else {
00458     index = (in.Band() - 1) * totalLines;     // Get to the proper band
00459     index += in.Line() - 1;                   // Get to the proper row
00460   }
00461   double coeff = normalizer[index];
00462 
00463   // Now loop and apply the coefficents
00464   for (int i=0; i<in.size(); i++) {
00465     if (IsSpecial(in[i])) {
00466       out[i] = in[i];
00467     }
00468     else {
00469       out[i] = Null;
00470       if (IsValidPixel(coeff)) {
00471         out[i] = in[i] - coeff;
00472       }
00473     }
00474   }
00475 }
00476 
00477 // Perform lowpass and highpass filters on statistics
00478 void filterStats(vector<double> &filter, int &filtsize, bool &pause_crop,
00479       int &channel) {
00480   int fsize = (int)filter.size();
00481   const int left_cut = 4;
00482   const int right_cut = 4;
00483   const int ch_pause_cnt = 3;
00484   const int ch_pause[2][ch_pause_cnt] = {{252,515,778},{247,510,773}};
00485   const int ch_width[2][ch_pause_cnt] = {{11,11,11},{11,11,11}};
00486   const string ch_direc[2] = {"RIGHT","LEFT"};
00487   const int iterations = 10;
00488   vector<double> filtin;
00489   vector<double> filtout;
00490   vector<double> filtorig;
00491   VecFilter vfilter;
00492 
00493   filtorig = filter;
00494 
00495   // To avoid filter ringing, cut out those areas in the data that
00496   // are especially problematic such as the left and right edges and
00497   // at the pause points
00498   for (int i=0; i<=left_cut-1; i++) {
00499     filter[i] = 0.0;
00500   }
00501   for (int i=fsize-1; i>=fsize-right_cut; i--) {
00502     filter[i] = 0.0;
00503   }
00504 
00505   // Zero out the pause point pixels if requested and the input
00506   // image file has a bin mode of 1
00507   if (pause_crop && fsize == 1024) {
00508     for (int i=0; i<ch_pause_cnt; i++) {
00509       int i1;
00510       int i2;
00511       if (ch_direc[channel] == "LEFT") {
00512         i1 = ch_pause[channel][i] - ch_width[channel][i];
00513         i2 = ch_pause[channel][i] - 1;
00514       } else {
00515         i1 = ch_pause[channel][i] - 1;
00516         i2 = ch_pause[channel][i] + ch_width[channel][i] - 2;
00517       }
00518       if (i1 < 0) i1 = 0;
00519       if (i2 > fsize-1) i2 = fsize - 1;
00520       for (int j=i1; j<=i2; j++) {
00521         filter[j] = 0.0;
00522       }
00523     }
00524   }
00525 
00526   // Here is the boxfilter - the outer most loop is for the number
00527   // of filter iterations
00528   filtin = filter;
00529 
00530   for (int pass=1; pass<=3; pass++) {
00531     for (int it=1; it<=iterations; it++) {
00532       filtout = vfilter.LowPass(filter,filtsize);
00533       filter = filtout;
00534     }
00535 
00536     // Zero out any columns that are different from the average by more
00537     // than a specific percent
00538     if (pass < 3) {
00539       double frac = .25;
00540       if (pass == 2) frac = .125;
00541       for (int k=0; k<fsize; k++) {
00542         if (filter[k] != 0.0 && filtorig[k] != 0.0) {
00543           if (fabs(filtorig[k] - filter[k])/filter[k] > frac) {
00544             filtin[k] = 0.0;
00545           }
00546         }
00547       }
00548       filter = filtin;
00549     }
00550   }
00551 
00552   // Perform the highpass by differencing the original from the lowpass
00553   filter = vfilter.HighPass(filtorig,filtout);
00554 
00555   filtin.clear();
00556   filtout.clear();
00557   filtorig.clear();
00558 }