Isis 3.0 Application Source Code Reference |
Home |
00001 // $Id: mdiscal.cpp 2661 2011-06-16 22:40:56Z slambright@GS.DOI.NET $ 00002 #include "Isis.h" 00003 00004 #include <vector> 00005 #include <string> 00006 #include <algorithm> 00007 #include <memory> 00008 #include <cstdio> 00009 #include <cmath> 00010 00011 #include "DarkModelPixel.h" 00012 #include "MdisCalUtils.h" 00013 #include "MultivariateStatistics.h" 00014 #include "ProcessByLine.h" 00015 #include "ProgramLauncher.h" 00016 #include "Spice.h" 00017 #include "Statistics.h" 00018 #include "TextFile.h" 00019 00020 using namespace Isis; 00021 using namespace std; 00022 00023 bool convertDarkToNull; 00024 enum MdisDarkCurrentMode { 00025 DarkCurrentNone, 00026 DarkCurrentStandard, 00027 DarkCurrentLinear, 00028 DarkCurrentModel 00029 }; 00030 00031 00032 MdisDarkCurrentMode darkCurrentMode; 00033 vector<double> calibrationValues; 00034 bool isNarrowAngleCamera; 00035 bool isBinnedData; 00036 double exposureDuration; 00037 double ccdTemperature; 00038 int filterNumber; 00039 static int nDarkColumns(0); 00040 static int nValidDark(0); 00041 static int nSampsToNull(0); 00042 Statistics darkStrip; 00043 vector<double> prevLineData; 00044 vector<double> smearData; 00045 static double smearComponent(3.4); 00046 Pvl configFile; 00047 00048 // Limit functionality for aiding dark analysis 00049 static bool g_flatfield = true; 00050 static bool g_radiometric = true; 00051 00052 // Absolute coefficents 00053 static double abs_coef(1.0); 00054 00055 // I/F variables 00056 static double solarDist(1.0); 00057 static double F_f(1.0); 00058 static double iof(1.0); // I/F value for observation 00059 static DarkModelPixel *model(0); 00060 00061 // Local functions 00062 Filename DetermineFlatFieldFile(); 00063 void GatherDarkStatistics(Buffer &in); 00064 void Calibrate(vector<Buffer *>&in, vector<Buffer *>&out); 00065 00066 void IsisMain() { 00067 00068 const string mdiscal_program = "mdiscal"; 00069 const string mdiscal_version = "1.3"; 00070 const string mdiscal_revision = "$Revision: 2661 $"; 00071 string mdiscal_runtime = Application::DateTime(); 00072 00073 // Specify the version of the CDR generated 00074 const int cdr_version = 3; 00075 00076 // We will be processing by column in case of a linear dark current fit. This will make the 00077 // calibration a one pass system in this case, rather than two. 00078 ProcessByLine p; 00079 Filename calibFile("$messenger/calibration/mdisCalibration????.trn"); 00080 calibFile.HighestVersion(); 00081 configFile.Read(calibFile.Expanded()); 00082 00083 // Initialize variables 00084 calibrationValues.clear(); 00085 prevLineData.clear(); 00086 convertDarkToNull = true; 00087 isNarrowAngleCamera = true; 00088 isBinnedData = true; 00089 darkCurrentMode = (MdisDarkCurrentMode) - 1; 00090 exposureDuration = 0.0; 00091 ccdTemperature = 0.0; // This needs figured out! 00092 filterNumber = 1; // Sufficent for the NAC! 00093 model = 0; 00094 00095 Cube *icube = p.SetInputCube("FROM"); 00096 PvlGroup &inst = icube->getGroup("Instrument"); 00097 isNarrowAngleCamera = ((string)inst["InstrumentId"] == "MDIS-NAC"); 00098 exposureDuration = inst["ExposureDuration"]; 00099 exposureDuration /= 1000.0; // Convert ms to sec 00100 00101 // Determine dark columns 00102 int fpuBin = inst["FpuBinningMode"]; 00103 int pxlBin = inst["PixelBinningMode"]; 00104 00105 nDarkColumns = 4 / (fpuBin + 1); // DPU binning gives 2 dark cols 00106 if (pxlBin > 2) nDarkColumns = 0; // MP binning > 2x2 yields no darks 00107 else if (pxlBin > 0) nDarkColumns /= (pxlBin+1); // Might be 1 if wo/DPU + MP 2x2 00108 00109 // Determine number of valid darks. For no binning will have 3. All combos 00110 // that have 2x2 total binning will give 1 valid dark. All other options 00111 // have no valid dark columns. 00112 nValidDark = MIN(nDarkColumns, 3); 00113 if(nValidDark < 3) { 00114 if((fpuBin + pxlBin) > 1) nValidDark = 0; 00115 else nValidDark = MIN(nValidDark, 1); 00116 } 00117 00118 // Determine number of samples/columns to NULL. For no binning it will yield 4 00119 // columns to NULL. For DPU but no MP binning, 3; For no DPU but MP binning, 00120 // 2x2 yields 3, 4x4 and 8x8 yields 1. 00121 nSampsToNull = (pxlBin < 2) ? 0 : ((pxlBin > 2) ? 1 : 3); // Only MP here 00122 nSampsToNull = MIN(MAX(nDarkColumns+1,nSampsToNull), 4); // No more than 4! 00123 darkStrip.Reset(); 00124 00125 ccdTemperature = icube->getGroup("Archive")["CCDTemperature"]; 00126 00127 // Binned data only applies to FPUBIN mode. Pixel binning must be dealt 00128 // with specially in other calibration support components 00129 isBinnedData = (fpuBin == 1); 00130 00131 // Get the trusted filter number 00132 if(!isNarrowAngleCamera) { 00133 filterNumber = ((int)(icube->getGroup("BandBin")["Number"])) - 1; 00134 } 00135 else { 00136 filterNumber = 1; // For the NAC 00137 } 00138 00139 UserInterface &ui = Application::GetUserInterface(); 00140 convertDarkToNull = !ui.GetBoolean("KEEPDARK"); 00141 if (!convertDarkToNull) nSampsToNull = 0; 00142 00143 00144 iString darkCurr = ui.GetString("DARKCURRENT"); 00145 g_flatfield = ui.GetBoolean("FLATFIELD"); 00146 g_radiometric = ui.GetBoolean("RADIOMETRIC"); 00147 00148 if(icube->getBandCount() != 1) { 00149 throw iException::Message(iException::User, 00150 "MDIS images may only contain one band", _FILEINFO_); 00151 } 00152 00153 if(icube->getSampleCount() < 3) { 00154 throw iException::Message(iException::User, 00155 "Unable to obtain dark current data. Expected a sample dimension of at least 3", _FILEINFO_); 00156 } 00157 00158 if((int)icube->getGroup("Instrument")["Unlutted"] == false) { 00159 throw iException::Message(iException::User, 00160 "Calibration may not be performed on unlutted data.", _FILEINFO_); 00161 } 00162 00163 00164 // Check for cases where certain models cannot be computed. 00165 // These would be for cases where more than two factors of compression 00166 // occur. For this case, only the model can be used and only if the 00167 // exposure time < 2 secs. 00168 if((darkCurr != "NONE") && (nValidDark <= 0)) { 00169 // Both cases require dark pixels, model does not 00170 if((darkCurr == "STANDARD") || (darkCurr == "LINEAR")) { 00171 darkCurr = "MODEL"; 00172 string mess = "There are no valid dark current pixels which are required" 00173 " for " + darkCurr + " calibration... must use MODEL"; 00174 iException &ie = iException::Message(iException::User, mess, _FILEINFO_); 00175 ie.Report(); 00176 ie.Clear(); 00177 } 00178 00179 // Model cannot be used for exposure times > 1.0 <sec> 00180 if((darkCurr == "MODEL") && (exposureDuration > 1.0)) { 00181 darkCurr = "NONE"; 00182 string mess = "There are no valid dark current pixels and the dark model" 00183 " correction can not be used when the exposure duration" 00184 " exceeds 1000...image cannot be calibrated"; 00185 iException &ie = iException::Message(iException::User, mess, _FILEINFO_); 00186 ie.Report(); 00187 ie.Clear(); 00188 } 00189 } 00190 00191 auto_ptr<DarkModelPixel> darkModel; 00192 if(darkCurr == "NONE") { 00193 darkCurrentMode = DarkCurrentNone; 00194 } 00195 else if(darkCurr == "STANDARD") { 00196 darkCurrentMode = DarkCurrentStandard; 00197 calibrationValues.resize(icube->getLineCount()); 00198 } 00199 else if(darkCurr == "LINEAR") { 00200 darkCurrentMode = DarkCurrentLinear; 00201 calibrationValues.resize(icube->getLineCount()); 00202 } 00203 else if(darkCurr == "MODEL") { 00204 if(exposureDuration > 1.0) { 00205 string mess = "Dark model correction can not be used when the " 00206 "exposure duration exceeds 1000...using LINEAR instead."; 00207 iException &ie = iException::Message(iException::User, mess, _FILEINFO_); 00208 ie.Report(); 00209 ie.Clear(); 00210 00211 // set processing to standard 00212 darkCurrentMode = DarkCurrentLinear; 00213 calibrationValues.resize(icube->getLineCount()); 00214 darkCurr = "STANDARD"; 00215 } 00216 else { 00217 darkCurrentMode = DarkCurrentModel; 00218 } 00219 } 00220 else { 00221 // should never happen 00222 throw iException::Message(iException::Programmer, 00223 "Invalid dark current mode [" + 00224 darkCurr + "]", _FILEINFO_); 00225 } 00226 00227 string darkCurrentFile(""); 00228 if(darkCurrentMode != DarkCurrentNone) { 00229 if(darkCurrentMode != DarkCurrentModel) { 00230 p.Progress()->SetText("Gathering Dark Current Statistics"); 00231 p.StartProcess(GatherDarkStatistics); 00232 } 00233 else { 00234 // read in dark current table variables and report the filename used 00235 darkModel = auto_ptr<DarkModelPixel> (new DarkModelPixel(pxlBin, ccdTemperature, exposureDuration)); 00236 darkCurrentFile = darkModel->loadCoefficients(isNarrowAngleCamera, isBinnedData); 00237 model = darkModel.get(); 00238 } 00239 } 00240 00241 // We need to figure out our flat-field file 00242 if(darkCurrentMode == DarkCurrentLinear) { 00243 // We need to perform a linear regression with our data, 00244 // convert statistics to a line. 00245 double *xdata = new double[calibrationValues.size()]; 00246 double *ydata = new double[calibrationValues.size()]; 00247 00248 for(unsigned int x = 0; x < calibrationValues.size(); x++) { 00249 xdata[x] = x; 00250 ydata[x] = calibrationValues[x]; 00251 } 00252 00253 // Perform a regression 00254 MultivariateStatistics stats; 00255 stats.AddData(xdata, ydata, calibrationValues.size()); 00256 00257 // y = A + Bx 00258 double a, b; 00259 stats.LinearRegression(a, b); 00260 delete [] xdata; 00261 delete [] ydata; 00262 00263 // Store a,b in calibration data instead of our line 00264 calibrationValues.resize(2); 00265 calibrationValues[0] = a; 00266 calibrationValues[1] = b; 00267 } 00268 00269 // Compute the (new) absolute calibration 00270 string respfile(""); 00271 vector<double> rsp = loadResponsivity(isNarrowAngleCamera, isBinnedData, 00272 filterNumber + 1, respfile); 00273 // abs_coef = 1.0 / (rsp[0] * ((rsp[2] * ccdTemperature) + rsp[1])); 00274 double T = 1.0; 00275 double Rt = rsp[0]; 00276 double Resp = 0; 00277 for(unsigned int i = 1 ; i < rsp.size() ; i++) { 00278 Resp += Rt * (rsp[i] * T); 00279 T *= ccdTemperature; 00280 } 00281 abs_coef = 1.0 / Resp; 00282 00283 // Retrieve filter dependant SMEAR component 00284 string smearfile(""); 00285 smearComponent = loadSmearComponent(isNarrowAngleCamera, filterNumber + 1, 00286 smearfile); 00287 00288 // Compute I/F if requested by user 00289 iof = 1.0; 00290 bool do_iof = ui.GetBoolean("IOF"); 00291 if (!g_radiometric) do_iof = false; 00292 bool iof_is_good = false; 00293 string solirrfile(""); 00294 if (do_iof) { 00295 PvlGroup &inst = icube->getGroup("Instrument"); 00296 string target = inst["TargetName"]; 00297 string startTime = inst["SpacecraftClockCount"]; 00298 if(sunDistanceAU(startTime, target, solarDist)) { 00299 vector<double> sol = loadSolarIrr(isNarrowAngleCamera, isBinnedData, 00300 filterNumber + 1, solirrfile); 00301 F_f = sol[2]; 00302 iof = pi_c() * (solarDist * solarDist) / F_f; 00303 iof_is_good = true; 00304 } 00305 else { 00306 iof = 1.0; 00307 iof_is_good = false; 00308 } 00309 } 00310 00311 // Determine if we need to subsample the flat field should pixel binning 00312 // occurred 00313 string reducedFlat(""); 00314 Filename flatfield = DetermineFlatFieldFile(); 00315 if(pxlBin > 0) { 00316 iString scale(pxlBin); 00317 Filename newflat; 00318 newflat.Temporary(flatfield.Basename() + "_reduced", "cub"); 00319 reducedFlat = newflat.Expanded(); 00320 string parameters = "FROM=" + flatfield.Expanded() + 00321 " TO=" + newflat.Expanded() + 00322 " MODE=SCALE" + 00323 " LSCALE=" + scale + 00324 " SSCALE=" + scale; 00325 try { 00326 // iApp->Exec("reduce", parameters); 00327 ProgramLauncher::RunIsisProgram("reduce", parameters); 00328 reducedFlat = newflat.Expanded(); 00329 } 00330 catch (iException &ie) { 00331 remove(reducedFlat.c_str()); 00332 throw; 00333 } 00334 CubeAttributeInput att; 00335 p.SetInputCube(reducedFlat, att); 00336 } 00337 else { 00338 CubeAttributeInput att; 00339 p.SetInputCube(flatfield.Expanded(), att); 00340 } 00341 00342 // Set output file for processing 00343 Cube *ocube = p.SetOutputCube("TO"); 00344 00345 try { 00346 p.Progress()->SetText("Calibrating MDIS Cube"); 00347 p.StartProcess(Calibrate); 00348 } 00349 catch(...) { 00350 if(!reducedFlat.empty()) remove(reducedFlat.c_str()); 00351 throw; 00352 } 00353 00354 // Remove the temporary reduced input file if generated 00355 if(!reducedFlat.empty()) remove(reducedFlat.c_str()); 00356 00357 // Log calibration activity 00358 PvlGroup calibrationLog("RadiometricCalibration"); 00359 calibrationLog.AddKeyword(PvlKeyword("SoftwareName", mdiscal_program)); 00360 calibrationLog.AddKeyword(PvlKeyword("SoftwareVersion", mdiscal_version)); 00361 calibrationLog.AddKeyword(PvlKeyword("ProcessDate", mdiscal_runtime)); 00362 calibrationLog.AddKeyword(PvlKeyword("DarkCurrentModel", darkCurr)); 00363 00364 if(darkCurrentMode == DarkCurrentLinear) { 00365 iString equation = "Y = " + iString(calibrationValues[0]) + iString(" + ") + iString(calibrationValues[1]) + iString("x"); 00366 calibrationLog.AddKeyword(PvlKeyword("DarkCurrentEquation", (string)equation)); 00367 } 00368 else if(darkCurrentMode == DarkCurrentModel) { 00369 calibrationLog.AddKeyword(PvlKeyword("DarkCurrentFile", darkCurrentFile)); 00370 } 00371 00372 calibrationLog.AddKeyword(PvlKeyword("BinnedImage", isBinnedData)); 00373 calibrationLog.AddKeyword(PvlKeyword("FilterNumber", filterNumber + 1)); 00374 if (g_flatfield) { 00375 calibrationLog.AddKeyword(PvlKeyword("FlatFieldFile", flatfield.OriginalPath() + "/" + flatfield.Name())); 00376 calibrationLog.AddKeyword(PvlKeyword("CalibrationFile", calibFile.OriginalPath() + "/" + calibFile.Name())); 00377 calibrationLog.AddKeyword(PvlKeyword("ResponsivityFile", respfile)); 00378 calibrationLog.AddKeyword(PvlKeyword("SmearCompFile", smearfile)); 00379 PvlKeyword rspKey("Response", rsp[0]); 00380 for(unsigned int i = 1 ; i < rsp.size() ; i++) { 00381 rspKey.AddValue(rsp[i]); 00382 } 00383 calibrationLog.AddKeyword(rspKey); 00384 calibrationLog.AddKeyword(PvlKeyword("SmearComponent", smearComponent)); 00385 } 00386 else { 00387 calibrationLog.AddKeyword(PvlKeyword("FlatFieldFile", "N/A")); 00388 calibrationLog.AddKeyword(PvlKeyword("CalibrationFile", "N/A")); 00389 calibrationLog.AddKeyword(PvlKeyword("ResponsivityFile", "N/A")); 00390 calibrationLog.AddKeyword(PvlKeyword("SmearCompFile", "N/A")); 00391 PvlKeyword rspKey("Response"); 00392 calibrationLog.AddKeyword(PvlKeyword("Response", "N/A")); 00393 calibrationLog.AddKeyword(PvlKeyword("SmearComponent", "N/A")); 00394 } 00395 00396 string calibType; 00397 if(do_iof && iof_is_good) { 00398 calibrationLog.AddKeyword(PvlKeyword("Units", "I over F")); 00399 calibrationLog.AddKeyword(PvlKeyword("SolarDistance", solarDist, "AU")); 00400 calibrationLog.AddKeyword(PvlKeyword("SolarIrrFile", solirrfile)); 00401 calibrationLog.AddKeyword(PvlKeyword("FilterIrradianceFactor", F_f)); 00402 calibrationLog.AddKeyword(PvlKeyword("IOFFactor", iof)); 00403 calibType = "IF"; 00404 } 00405 else if (g_radiometric) { 00406 calibrationLog.AddKeyword(PvlKeyword("Units", "W / (m**2 micrometer sr )")); 00407 calibType = "RA"; 00408 } 00409 else { 00410 calibrationLog.AddKeyword(PvlKeyword("Units", "DN")); 00411 calibType = "DN"; 00412 } 00413 00414 calibrationLog.AddKeyword(PvlKeyword("DarkStripColumns", nDarkColumns), 00415 Pvl::Replace); 00416 calibrationLog.AddKeyword(PvlKeyword("ValidDarkColumns", nValidDark), 00417 Pvl::Replace); 00418 if(darkStrip.TotalPixels() > 0) { 00419 calibrationLog.AddKeyword(PvlKeyword("DarkStripMean", darkStrip.Average()), 00420 Pvl::Replace); 00421 } 00422 00423 // Report nulled sample count 00424 calibrationLog.AddKeyword(PvlKeyword("LeftSamplesNulled", nSampsToNull)); 00425 00426 // Handle updates of ProductId and SourceProduct Id keywords 00427 PvlGroup &archive = ocube->getGroup("Archive"); 00428 PvlKeyword key = archive["ProductId"]; 00429 iString orgProdId = key[0]; 00430 iString newProdId = orgProdId + "_" + calibType + "_" + iString(cdr_version); 00431 newProdId[0] = 'C'; 00432 key.SetValue(Quote(newProdId)); 00433 archive.AddKeyword(key, Pvl::Replace); 00434 00435 // Now SourceProductId 00436 if(archive.HasKeyword("SourceProductId")) { 00437 key = archive["SourceProductId"]; 00438 for(int i = 0 ; i < key.Size() ; i++) { 00439 key[i] = Quote(key[i]); 00440 } 00441 } 00442 else { 00443 key = PvlKeyword("SourceProductId", Quote(orgProdId)); 00444 } 00445 00446 if(!darkCurrentFile.empty()) { 00447 key.AddValue(Quote(Filename(darkCurrentFile).Basename())); 00448 } 00449 key.AddValue(Quote(flatfield.Basename())); 00450 key.AddValue(Quote(Filename(respfile).Basename())); 00451 // key.AddValue(Quote(Filename(smearfile).Basename())); 00452 if(iof_is_good) { 00453 key.AddValue(Quote(Filename(solirrfile).Basename())); 00454 } 00455 archive.AddKeyword(key, Pvl::Replace); 00456 00457 // Write Calibration group to file and log 00458 ocube->putGroup(calibrationLog); 00459 Application::Log(calibrationLog); 00460 configFile.Clear(); 00461 } 00462 00463 Filename DetermineFlatFieldFile() { 00464 string filename = "$messenger/calibration/FLAT/"; 00465 00466 // Filename consists of binned/notbinned, camera, and filter 00467 filename += "MDIS"; 00468 filename += ((isNarrowAngleCamera) ? "NAC" : "WAC"); 00469 filename += ((isBinnedData) ? "_BINNED_" : "_NOTBIN_"); 00470 filename += "FLAT"; 00471 if(isNarrowAngleCamera) { 00472 // NAC spec is simpler 00473 filename += "_?.cub"; 00474 } 00475 else { 00476 // add a zero if the filter is 1-digit 00477 filename += "_FIL"; 00478 if(filterNumber < 9) filename += "0"; 00479 filename += iString(filterNumber + 1); 00480 filename += "_?.cub"; 00481 } 00482 00483 Filename final(filename); 00484 final.HighestVersion(); 00485 return final; 00486 } 00487 00488 00489 void GatherDarkStatistics(Buffer &in) { 00490 00491 // Some situations cannot use these processes for calibration 00492 calibrationValues[in.Line()-1] = Isis::Null; 00493 00494 if (nValidDark > 0) { 00495 if (darkCurrentMode == DarkCurrentStandard) { 00496 // Figure out the median (Isis::Statistics wont do this for us) 00497 // because we have repeated numbers, put them into a sorted array size 00498 // of no more than 3 and take the middle 00499 vector<double> calibValues; 00500 int nDark = nValidDark; 00501 for(int i = 0 ; i < nDark ; i++) { 00502 calibValues.push_back(in[i]); 00503 } 00504 sort(calibValues.begin(), calibValues.end()); 00505 00506 // grab the middle element in the array for the median 00507 calibrationValues[in.Line()-1] = calibValues[nDark/2]; 00508 } 00509 else if(darkCurrentMode == DarkCurrentLinear) { 00510 // Presently the linear regression only uses the first sample in the 00511 // dark current data 00512 calibrationValues[in.Line()-1] = in[0]; 00513 } 00514 } 00515 } 00516 00517 void Calibrate(vector<Buffer *>&in, vector<Buffer *>&out) { 00518 Buffer &imageIn = *in[0]; 00519 Buffer &flatField = *in[1]; 00520 Buffer &imageOut = *out[0]; 00521 00522 if(imageIn.Line() == 1) { 00523 prevLineData.resize(imageIn.SampleDimension()); 00524 smearData.resize(imageIn.SampleDimension()); 00525 00526 for(unsigned int i = 0; i < prevLineData.size(); i++) { 00527 prevLineData[i] = 0.0; 00528 smearData[i] = 0.0; 00529 } 00530 } 00531 00532 for(int i = 0; i < imageIn.size(); i++) { 00533 00534 // Check for special pixel in input image and pass through 00535 if(Isis::IsSpecial(imageIn[i])) { 00536 imageOut[i] = imageIn[i]; 00537 continue; 00538 } 00539 00540 if (g_flatfield) { 00541 // If the flat field pixel is special, can't calibrate so set to NULL 00542 // and pass through (unlikely). 00543 if(Isis::IsSpecial(flatField[i])) { 00544 imageOut[i] = Isis::Null; 00545 continue; 00546 } 00547 } 00548 //462 00549 //if(i == 25 && imageIn.Line() == 25) std::cout << "In: " << imageIn[i] << std::endl; 00550 00551 // Step 1: Perform dark current corrections 00552 if(darkCurrentMode == DarkCurrentNone) { 00553 imageOut[i] = imageIn[i]; 00554 } 00555 else if(darkCurrentMode == DarkCurrentStandard) { 00556 imageOut[i] = imageIn[i] - calibrationValues[imageIn.Line()-1]; 00557 } 00558 else if(darkCurrentMode == DarkCurrentLinear) { 00559 // Linear: out = in - bestfitline = in - (A + Bx) 00560 imageOut[i] = imageIn[i] - (calibrationValues[0] + calibrationValues[1] * 00561 (imageIn.Line() - 1)); 00562 } 00563 else if(darkCurrentMode == DarkCurrentModel) { 00564 imageOut[i] = imageIn[i] - model->getDarkPixel(i, imageIn.Line() - 1); 00565 } 00566 00567 if (g_flatfield) { 00568 // Step 2: Perform linearity correction 00569 if(isNarrowAngleCamera == true) { 00570 if(imageOut[i] <= 0.0) { 00571 imageOut[i] /= 0.912031; 00572 } 00573 else { 00574 imageOut[i] /= 0.011844 * log10(imageOut[i]) + 0.912031; 00575 } 00576 } 00577 else { 00578 // Wide angle camera 00579 if(imageOut[i] <= 0.0) { 00580 imageOut[i] /= 0.936321; 00581 } 00582 else { 00583 imageOut[i] /= 0.008760 * log10(imageOut[i]) + 0.936321; 00584 } 00585 } 00586 00587 // Step 3: Readout Smear Correction (ms -> seconds) 00588 double t2 = smearComponent / imageIn.SampleDimension() / 1000.0; 00589 00590 if(exposureDuration > 0.0 && imageIn.Line() > 1) { 00591 smearData[i] += t2 / exposureDuration * prevLineData[i]; 00592 imageOut[i] -= smearData[i]; 00593 } 00594 00595 prevLineData[i] = imageOut[i]; 00596 00597 // Step 4: Uniformity (flat field) 00598 imageOut[i] /= flatField[i]; // divide by flat field 00599 00600 // Step 5: Absolute coefficient 00601 if(exposureDuration > 0.0) { 00602 imageOut[i] = imageOut[i] / exposureDuration * abs_coef; 00603 } 00604 } // End of flat field 00605 00606 if (g_radiometric) { 00607 // Step 6: Convert to I/F units 00608 imageOut[i] *= iof; 00609 } 00610 } 00611 00612 // Compute dark current statistics 00613 for(int j = 0 ; j < nValidDark; j++) { 00614 darkStrip.AddData(imageOut[j]); 00615 } 00616 00617 // Null specified columns (2011-04-20 - KJB) 00618 for(int n = 0 ; n < nSampsToNull ; n++) { 00619 imageOut[n] = Isis::Null; 00620 } 00621 00622 } 00623