IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 36340


Ignore:
Timestamp:
Dec 4, 2013, 1:57:28 PM (13 years ago)
Author:
eugene
Message:

trim down psphotSourceSize to the core functions : it now only determines the extendedness / psfness / crness of the detections without making any decisions about whether the object should be processed in a specific stage or not; also strip out the old version of the analysis functions, moved to psphotOldCode.c

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/eam_branches/ipp-20130904/psphot/src/psphotSourceSize.c

    r35180 r36340  
    11# include "psphotInternal.h"
    2 # include <gsl/gsl_sf_gamma.h>
    3 
     2
     3// this structure is only used internally to simplify the function parameters
    44typedef struct {
    55    psImageMaskType maskVal;
     
    1313    bool altDiffExt;
    1414    float altDiffExtThresh;
    15     bool extFitAll;
    16     bool extFitAllReadout;
    17     float extFitAllThresh;
    1815    float soft;
    1916    int grow;
     
    3330bool psphotSourceSelectCR (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options);
    3431bool psphotMaskCosmicRay (pmReadout *readout, pmSource *source, psImageMaskType maskVal, int maxWindowCR);
    35 bool psphotMaskCosmicRayFootprintCheck (psArray *sources);
    3632int  psphotMaskCosmicRayConnected (int xPeak, int yPeak, psImage *mymask, psImage *myvar, psImage *edges, int binning, float sigma_thresh);
    3733float psphotSourceSizeFindThreshold (psVector *value, psVector *mask, int maskValue, float minValue, float maxValue, float delta, float guess, float fraction);
     
    4137
    4238// we need to call this function after sources have been fitted to the PSF model and
    43 // subtracted.  To determine the CR-nature, this function examines the 9 pixels in the 3x3
    44 // square containing the peak and compares the observed flux to the model.  To determine
    45 // the EXT-nature, this function measures the amount of positive or negative total
    46 // deviation from the psf model at the r = FWHM/2 position
     39// subtracted. 
    4740
    4841// for now, let's store the detections on the readout->analysis for each readout
     
    121114    assert (status);
    122115
    123     // XXX recipe name is not great
     116    // XXX recipe name is not great (NOTE : not used!)
    124117    options.nSigmaMoments = psMetadataLookupF32 (&status, recipe, "PSPHOT.EXT.NSIGMA.MOMENTS");
    125118    assert (status);
    126119
    127     // Optional extended source measurement algorithm to improve diff image trails
     120    // Optional algorithm to define if a source is extended (used by DIFF analysis)
    128121    options.altDiffExt = psMetadataLookupBool(&status, recipe, "PSPHOT.EXT.DIFF.ALTERNATE");
    129122    assert (status);
     123
    130124    // Threshold for this alternate method
    131125    options.altDiffExtThresh = psMetadataLookupF32(&status, recipe, "PSPHOT.EXT.DIFF.ALTERNATE.THRESH");
    132126    assert (status);
    133     // Option to enable fitting of all objects with extended model.
    134     options.extFitAll = psMetadataLookupBool(&status, recipe, "PSPHOT.EXT.FIT.ALL.SOURCES");
    135     assert (status);
    136     // Fitting everything is fine, but if the source density is high, we probably shouldn't.
    137     options.extFitAllThresh = psMetadataLookupF32(&status, recipe, "PSPHOT.EXT.FIT.ALL.THRESH");
    138     assert (status);
    139    
     127
    140128    // location of a single test source
    141129    options.xtest = psMetadataLookupS32 (&status, recipe, "PSPHOT.CRMASK.XTEST");
     
    422410
    423411    psLogMsg("psModules.objects", PS_LOG_INFO, "Source Size classifications: %4s %4s %4s %4s %4s", "Npsf", "Next", "Nsat", "Ncr", "Nskip");
    424     // Determine if this readout is above the threshold to ext fit all sources
    425     options->extFitAllReadout = false;
    426     if (options->extFitAll) {
    427       float maskFrac = psMetadataLookupF32(&status,readout->analysis,"READOUT.MASK.FRAC");
    428       if (status) {
    429         maskFrac = 0.0;
    430       }
    431       if (sources->n * (1.0 - maskFrac) > options->extFitAllThresh) {
    432         options->extFitAllReadout = false;
    433       }
    434       else {
    435         options->extFitAllReadout = true;
    436       }
    437     }
    438    
     412
    439413    if (!psphotSourceClassRegion (NULL, &psfClump, sources, recipe, psf, options)) {
    440414        psLogMsg ("psphot", 4, "Failed to determine source classification for full image\n");
     
    444418    return true;
    445419   
     420    // NOTE : this section is deactivated (EAM : I think we were getting poor boundary effects?)
    446421    int nRegions = psMetadataLookupS32 (&status, readout->analysis, "PSF.CLUMP.NREGIONS");
    447422    for (int i = 0; i < nRegions; i ++) {
     
    582557        // * SAT stars should not be faint, but defects may?
    583558
    584         // If the recipe requests we do extended source fits to everything, set
    585         // the EXT_LIMIT flag
    586         if (options->extFitAllReadout) {
    587           psTrace("psphotSourceClassRegion.EXTALT",10,"In extFitAll: %d %d\n",options->extFitAll,options->extFitAllReadout);
    588           source->mode |= PM_SOURCE_MODE_EXT_LIMIT;
    589         }
    590559        // Defects may not always match CRs from peak curvature analysis
    591560        // Defects may also be marked as SATSTAR -- XXX deactivate this flag?
     
    595564
    596565        // saturated star (too many saturated pixels or peak above saturation limit).  These
    597         // may also be saturated galaxies, or just large saturated regions.
     566        // may also be saturated galaxies, or just large saturated regions.  They are never
     567        // marked as 'extended'
    598568        if (source->mode & PM_SOURCE_MODE_SATSTAR) {
    599569            psTrace("psphotSourceClassRegion.SAT",4,"CLASS: %g %g\t%g %g\t%g %g\t%g %g\t%g SAT\n",
     
    604574        }
    605575
    606         // any sources missing a large fraction should just be treated as PSFs
     576        // any sources missing a large fraction should just be treated as PSFs. They are never
     577        // marked as 'extended'
    607578        if ((source->pixWeightNotBad < 0.9) || (source->pixWeightNotPoor < 0.9)) {
    608579            psTrace("psphotSourceClassRegion.PSF",4,"CLASS: %g %g\t%g %g  %g %g  %g %g\t%g %g\t%g PSF\t%g %g\n",
     
    639610            continue;
    640611        }
     612
    641613        // Alternate extended source limit calculation
    642614        if (options->altDiffExt) {
     
    968940}
    969941
    970 bool psphotMaskCosmicRayFootprintCheck (psArray *sources) {
    971 #ifdef CHECK_FOOTPRINTS
    972     // This gets really expensive for complex images
    973     for (int i = 0; i < sources->n; i++) {
    974         pmSource *source = sources->data[i];
    975         pmPeak *peak = source->peak;
    976         pmFootprint *footprint = peak->footprint;
    977         if (!footprint) continue;
    978         for (int j = 0; j < footprint->spans->n; j++) {
    979             pmSpan *sp = footprint->spans->data[j];
    980             psAssert (sp, "missing span");
    981         }
    982     }
    983 #endif
    984     return true;
    985 }
    986 
    987 /**** ------ old versions of cosmic ray masking ----- ****/
    988 
    989 bool psphotMaskCosmicRayIsophot (pmSource *source, psImageMaskType maskVal, psImageMaskType crMask);
    990 
    991 // This attempt to mask the cosmic rays used the isophotal boundary
    992 bool psphotMaskCosmicRay_V1 (psImage *mask, pmSource *source, psImageMaskType maskVal, psImageMaskType crMask) {
    993 
    994     // replace the source flux
    995     pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
    996 
    997     // flag this as a CR
    998     source->mode |= PM_SOURCE_MODE_CR_LIMIT;
    999     pmPeak *peak = source->peak;
    1000     psAssert (peak, "NULL peak");
    1001 
    1002     // grab the matching footprint
    1003     pmFootprint *footprint = peak->footprint;
    1004     if (!footprint) {
    1005       psTrace("psphot.czw",2,"Using isophot CR mask code.");
    1006 
    1007         // if we have not footprint, use the old code to mask by isophot
    1008         psphotMaskCosmicRayIsophot (source, maskVal, crMask);
    1009         return true;
    1010     }
    1011 
    1012     if (!footprint->spans) {
    1013       psTrace("psphot.czw",2,"Using isophot CR mask code.");
    1014 
    1015         // if we have no footprint, use the old code to mask by isophot
    1016         psphotMaskCosmicRayIsophot (source, maskVal, crMask);
    1017         return true;
    1018     }
    1019     psphotMaskCosmicRayIsophot (source, maskVal, crMask);
    1020     // mask all of the pixels covered by the spans of the footprint
    1021     for (int j = 1; j < footprint->spans->n; j++) {
    1022         pmSpan *span1 = footprint->spans->data[j];
    1023 
    1024         int iy = span1->y;
    1025         int xs = span1->x0;
    1026         int xe = span1->x1;
    1027 
    1028         for (int ix = xs; ix < xe; ix++) {
    1029             mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
    1030         }
    1031     }
    1032     return true;
    1033 }
    1034 
    1035942# define VERBOSE 0
    1036943int psphotMaskCosmicRayConnected (int xPeak, int yPeak, psImage *mymask, psImage *myvar, psImage *edges, int binning, float sigma_thresh) {
     
    11761083}
    11771084
    1178 bool psphotMaskCosmicRayIsophot (pmSource *source, psImageMaskType maskVal, psImageMaskType crMask) {
    1179 
    1180     source->mode |= PM_SOURCE_MODE_CR_LIMIT;
    1181     pmPeak *peak = source->peak;
    1182     psAssert (peak, "NULL peak");
    1183 
    1184     psImage *mask   = source->maskView;
    1185     psImage *pixels = source->pixels;
    1186     psImage *variance = source->variance;
    1187 
    1188     // XXX This should be a recipe variable
    1189 # define SN_LIMIT 5.0
    1190 
    1191     int xo = peak->x - pixels->col0;
    1192     int yo = peak->y - pixels->row0;
    1193 
    1194     // mark the pixels in this row to the left, then the right
    1195     for (int ix = xo; ix >= 0; ix--) {
    1196         float SN = pixels->data.F32[yo][ix] / sqrt(variance->data.F32[yo][ix]);
    1197         if (SN > SN_LIMIT) {
    1198             mask->data.PS_TYPE_IMAGE_MASK_DATA[yo][ix] |= crMask;
    1199         }
    1200     }
    1201     for (int ix = xo + 1; ix < pixels->numCols; ix++) {
    1202         float SN = pixels->data.F32[yo][ix] / sqrt(variance->data.F32[yo][ix]);
    1203         if (SN > SN_LIMIT) {
    1204             mask->data.PS_TYPE_IMAGE_MASK_DATA[yo][ix] |= crMask;
    1205         }
    1206     }
    1207 
    1208     // for each of the neighboring rows, mark the high pixels if they have a marked neighbor
    1209     // first go up:
    1210     for (int iy = PS_MIN(yo, mask->numRows-2); iy >= 0; iy--) {
    1211         // mark the pixels in this row to the left, then the right
    1212         for (int ix = 0; ix < pixels->numCols; ix++) {
    1213             float SN = pixels->data.F32[iy][ix] / sqrt(variance->data.F32[iy][ix]);
    1214             if (SN < SN_LIMIT) continue;
    1215 
    1216             bool valid = false;
    1217             valid |= (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix] & crMask);
    1218             valid |= (ix > 0) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix-1] & crMask) : 0;
    1219             valid |= (ix <= mask->numCols) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy+1][ix+1] & crMask) : 0;
    1220 
    1221             if (!valid) continue;
    1222             mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
    1223         }
    1224     }
    1225     // next go down:
    1226     for (int iy = PS_MIN(yo+1, mask->numRows-1); iy < pixels->numRows; iy++) {
    1227         // mark the pixels in this row to the left, then the right
    1228         for (int ix = 0; ix < pixels->numCols; ix++) {
    1229             float SN = pixels->data.F32[iy][ix] / sqrt(variance->data.F32[iy][ix]);
    1230             if (SN < SN_LIMIT) continue;
    1231 
    1232             bool valid = false;
    1233             valid |= (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix] & crMask);
    1234             valid |= (ix > 0) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix-1] & crMask) : 0;
    1235             valid |= (ix <= mask->numCols) ? (mask->data.PS_TYPE_IMAGE_MASK_DATA[iy-1][ix+1] & crMask) : 0;
    1236 
    1237             if (!valid) continue;
    1238             mask->data.PS_TYPE_IMAGE_MASK_DATA[iy][ix] |= crMask;
    1239         }
    1240     }
    1241     return true;
    1242 }
    1243 
    1244 // given the PSF ellipse parameters, navigate around the 1sigma contour, return the total
    1245 // deviation in sigmas.  This is measured on the residual image - should we ignore negative
    1246 // deviations?  NOTE: This function was an early attempt to classify extended objects, and is
    1247 // no longer used by psphot.
    1248 float psphotModelContour(const psImage *image, const psImage *variance, const psImage *mask,
    1249                          psImageMaskType maskVal, const pmModel *model, float Ro)
    1250 {
    1251     psF32 *PAR = model->params->data.F32; // Model parameters
    1252     float sxx = PAR[PM_PAR_SXX], sxy = PAR[PM_PAR_SXY], syy = PAR[PM_PAR_SYY]; // Ellipse parameters
    1253 
    1254     // We treat the contour as an ellipse:
    1255     // Ro = (x / SXX)^2 + (y / SYY)^2 + x y SXY
    1256     // y^2 (1/SYY^2) + y (x SXY) + (x / SXX)^2 - Ro = 0;
    1257     // This is a quadratic, Ay^2 + By + C with A = 1/SYY^2, B = x*SXY, C = (x / SXX)^2 - Ro
    1258     // The solution is y = [-B +/- sqrt (B^2 - 4 A C)] / [2 A], so:
    1259     // y = [-(x SXY) +/- sqrt ((x SXY)^2 - 4 (1/SYY^2) ((x/SXX)^2 - Ro))] * [SYY^2 / 2]
    1260 
    1261     // min/max value of x is where B^2 - 4AC = 0; solve this for x
    1262     float Q = Ro * PS_SQR(sxx) / (1.0 - PS_SQR(sxx * syy * sxy) / 4.0);
    1263     if (Q < 0.0) {
    1264         // ellipse is imaginary
    1265         return NAN;
    1266     }
    1267 
    1268     int radius = sqrtf(Q) + 0.5;        // Radius of ellipse
    1269     int nPts = 0;                       // Number of points in ellipse
    1270     float nSigma = 0.0;                 //
    1271 
    1272     for (int x = -radius; x <= radius; x++) {
    1273         // Polynomial coefficients
    1274         // XXX Should we be using the centre of the pixel as x or x+0.5?
    1275         float A = PS_SQR (1.0 / syy);
    1276         float B = x * sxy;
    1277         float C = PS_SQR (x / sxx) - Ro;
    1278         float T = PS_SQR(B) - 4*A*C;
    1279         if (T < 0.0) {
    1280             continue;
    1281         }
    1282 
    1283         // y position in source frame
    1284         float yP = (-B + sqrt (T)) / (2.0 * A);
    1285         float yM = (-B - sqrt (T)) / (2.0 * A);
    1286 
    1287         // Get the closest pixel positions (image frame)
    1288         int xPix  = x  + PAR[PM_PAR_XPOS] - image->col0 + 0.5;
    1289         int yPixM = yM + PAR[PM_PAR_YPOS] - image->row0 + 0.5;
    1290         int yPixP = yP + PAR[PM_PAR_YPOS] - image->row0 + 0.5;
    1291 
    1292         if (xPix < 0 || xPix >= image->numCols) {
    1293             continue;
    1294         }
    1295 
    1296         if (yPixM >= 0 && yPixM < image->numRows &&
    1297             !(mask && (mask->data.PS_TYPE_IMAGE_MASK_DATA[yPixM][xPix] & maskVal))) {
    1298             float dSigma = image->data.F32[yPixM][xPix] / sqrtf(variance->data.F32[yPixM][xPix]);
    1299             nSigma += dSigma;
    1300             nPts++;
    1301         }
    1302 
    1303         if (yPixM == yPixP) {
    1304             continue;
    1305         }
    1306 
    1307         if (yPixP >= 0 && yPixP < image->numRows &&
    1308             !(mask && (mask->data.PS_TYPE_IMAGE_MASK_DATA[yPixP][xPix] & maskVal))) {
    1309             float dSigma = image->data.F32[yPixP][xPix] / sqrtf(variance->data.F32[yPixP][xPix]);
    1310             nSigma += dSigma;
    1311             nPts++;
    1312         }
    1313     }
    1314     nSigma /= nPts;
    1315     return nSigma;
    1316 }
    1317 
    1318 // this was an old attempt to identify cosmic rays based on the peak curvature
    1319 bool psphotSourcePeakCurvature (pmReadout *readout, psArray *sources, psphotSourceSizeOptions *options) {
    1320 
    1321     // classify the sources based on the CR test (place this in a function?)
    1322     // XXX use an internal flag to mark sources which have already been measured
    1323     for (int i = 0; i < sources->n; i++) {
    1324         pmSource *source = sources->data[i];
    1325 
    1326         // skip source if it was already measured
    1327         if (source->tmpFlags & PM_SOURCE_TMPF_SIZE_MEASURED) {
    1328             psTrace("psphot", 7, "Not calculating source size since it has already been measured\n");
    1329             continue;
    1330         }
    1331 
    1332         // source must have been subtracted
    1333         if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) {
    1334             source->mode |= PM_SOURCE_MODE_SIZE_SKIPPED;
    1335             psTrace("psphot", 7, "Not calculating source size since source is not subtracted\n");
    1336             continue;
    1337         }
    1338 
    1339         psF32 **resid  = source->pixels->data.F32;
    1340         psF32 **variance = source->variance->data.F32;
    1341         psImageMaskType **mask = source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA;
    1342 
    1343         // Integer position of peak
    1344         int xPeak = source->peak->xf - source->pixels->col0 + 0.5;
    1345         int yPeak = source->peak->yf - source->pixels->row0 + 0.5;
    1346 
    1347         // Skip sources which are too close to a boundary.  These are mostly caught as DEFECT
    1348         if (xPeak < 1 || xPeak > source->pixels->numCols - 2 ||
    1349             yPeak < 1 || yPeak > source->pixels->numRows - 2) {
    1350             psTrace("psphot", 7, "Not calculating crNsigma due to edge\n");
    1351             continue;
    1352         }
    1353 
    1354         // Skip sources with masked pixels.  These are mostly caught as DEFECT
    1355         bool keep = true;
    1356         for (int iy = -1; (iy <= +1) && keep; iy++) {
    1357             for (int ix = -1; (ix <= +1) && keep; ix++) {
    1358                 if (mask[yPeak+iy][xPeak+ix] & options->maskVal) {
    1359                     keep = false;
    1360                 }
    1361             }
    1362         }
    1363         if (!keep) {
    1364             psTrace("psphot", 7, "Not calculating crNsigma due to masked pixels\n");
    1365             continue;
    1366         }
    1367 
    1368         // Compare the central pixel with those on either side, for the four possible lines through it.
    1369 
    1370         // Soften variances (add systematic error)
    1371         float softening = options->soft * PS_SQR(source->peak->rawFlux); // Softening for variances
    1372 
    1373         // Across the middle: y = 0
    1374         float cX = 2*resid[yPeak][xPeak]   - resid[yPeak+0][xPeak-1]  - resid[yPeak+0][xPeak+1];
    1375         float dcX = 4*variance[yPeak][xPeak] + variance[yPeak+0][xPeak-1] + variance[yPeak+0][xPeak+1];
    1376         float nX = cX / sqrtf(dcX + softening);
    1377 
    1378         // Up the centre: x = 0
    1379         float cY = 2*resid[yPeak][xPeak]   - resid[yPeak-1][xPeak+0]  - resid[yPeak+1][xPeak+0];
    1380         float dcY = 4*variance[yPeak][xPeak] + variance[yPeak-1][xPeak+0] + variance[yPeak+1][xPeak+0];
    1381         float nY = cY / sqrtf(dcY + softening);
    1382 
    1383         // Diagonal: x = y
    1384         float cL = 2*resid[yPeak][xPeak]   - resid[yPeak-1][xPeak-1]  - resid[yPeak+1][xPeak+1];
    1385         float dcL = 4*variance[yPeak][xPeak] + variance[yPeak-1][xPeak-1] + variance[yPeak+1][xPeak+1];
    1386         float nL = cL / sqrtf(dcL + softening);
    1387 
    1388         // Diagonal: x = - y
    1389         float cR = 2*resid[yPeak][xPeak]   - resid[yPeak+1][xPeak-1]  - resid[yPeak-1][xPeak+1];
    1390         float dcR = 4*variance[yPeak][xPeak] + variance[yPeak+1][xPeak-1] + variance[yPeak-1][xPeak+1];
    1391         float nR = cR / sqrtf(dcR + softening);
    1392 
    1393         // P(chisq > chisq_obs; Ndof) = gamma_Q (Ndof/2, chisq/2)
    1394         // Ndof = 4 ? (four measurements, no free parameters)
    1395         // XXX this value is going to be biased low because of systematic errors.
    1396         // we need to calibrate it somehow
    1397         // source->psfProb = gsl_sf_gamma_inc_Q (2, 0.5*chisq);
    1398 
    1399         // not strictly accurate: overcounts the chisq contribution from the center pixel (by
    1400         // factor of 4); also biases a bit low if any pixels are masked
    1401         // XXX I am not sure I want to keep this value...
    1402         source->psfChisq = PS_SQR(nX) + PS_SQR(nY) + PS_SQR(nL) + PS_SQR(nR);
    1403 
    1404         float fCR = 0.0;
    1405         int nCR = 0;
    1406         if (nX > 0.0) {
    1407             fCR += nX;
    1408             nCR ++;
    1409         }
    1410         if (nY > 0.0) {
    1411             fCR += nY;
    1412             nCR ++;
    1413         }
    1414         if (nL > 0.0) {
    1415             fCR += nL;
    1416             nCR ++;
    1417         }
    1418         if (nR > 0.0) {
    1419             fCR += nR;
    1420             nCR ++;
    1421         }
    1422         source->crNsigma  = (nCR > 0)  ? fCR / nCR : 0.0;
    1423         source->tmpFlags |= PM_SOURCE_TMPF_SIZE_MEASURED;
    1424 
    1425         if (!isfinite(source->crNsigma)) {
    1426             continue;
    1427         }
    1428 
    1429         // this source is thought to be a cosmic ray.  flag the detection and mask the pixels
    1430         if (source->crNsigma > options->nSigmaCR) {
    1431             source->mode |= PM_SOURCE_MODE_CR_LIMIT;
    1432             // XXX still testing... : psphotMaskCosmicRay (readout->mask, source, maskVal, crMask);
    1433             // XXX acting strange... psphotMaskCosmicRay_Old (source, maskVal, crMask);
    1434         }
    1435     }
    1436 
    1437     // now that we have masked pixels associated with CRs, we can grow the mask
    1438     if (options->grow > 0) {
    1439         bool oldThreads = psImageConvolveSetThreads(true); // Old value of threading for psImageConvolveMask
    1440         psImage *newMask = psImageConvolveMask(NULL, readout->mask, options->crMask, options->crMask, -options->grow, options->grow, -options->grow, options->grow);
    1441         psImageConvolveSetThreads(oldThreads);
    1442         if (!newMask) {
    1443             psError(PS_ERR_UNKNOWN, false, "Unable to grow CR mask");
    1444             return false;
    1445         }
    1446         psFree(readout->mask);
    1447         readout->mask = newMask;
    1448     }
    1449     return true;
    1450 }
    1451 
    14521085float psphotSourceSizeFindThreshold (psVector *value, psVector *mask, int maskValue, float minValue, float maxValue, float delta, float guess, float fraction) {
    14531086
Note: See TracChangeset for help on using the changeset viewer.