IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Oct 25, 2010, 2:54:22 PM (16 years ago)
Author:
eugene
Message:

merge changes from eam_branches/ipp-20100823

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/psModules/src/objects/pmSourceMoments.c

    r29044 r29546  
    6464void pmSourceMomentsSetVerbose(bool state){ beVerbose = state; }
    6565
     66// if mode & EXTERNAL or mode2 & MATCHED, do not re-calculate the centroid (use peak as centroid)
     67
    6668bool pmSourceMoments(pmSource *source, psF32 radius, psF32 sigma, psF32 minSN, psImageMaskType maskVal)
    6769{
     
    7173    PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);
    7274
    73     // use sky from moments if defined, 0.0 otherwise
    74 
    75     // XXX this value comes from the sky model at the source center, and tends to over-estimate
    76     // the sky in the vicinity of bright sources.  we are better off assuming the model worked
    77     // well:
     75    // this function assumes the sky has been well-subtracted for the image
    7876    psF32 sky = 0.0;
     77
    7978    if (source->moments == NULL) {
    8079      source->moments = pmMomentsAlloc();
    8180    }
    82     // XXX if (source->moments == NULL) {
    83     // XXX     source->moments = pmMomentsAlloc();
    84     // XXX } else {
    85     // XXX     sky = source->moments->Sky;
    86     // XXX }
    87 
    88     // First Pass: calculate the first moments (these are subtracted from the coordinates below)
    89     // Sum = SUM (z - sky)
    90     // X1  = SUM (x - xc)*(z - sky)
    91     // .. etc
    92 
    93     psF32 peakPixel = -PS_MAX_F32;
    94     psS32 numPixels = 0;
     81
    9582    psF32 Sum = 0.0;
    9683    psF32 Var = 0.0;
    97     psF32 X1 = 0.0;
    98     psF32 Y1 = 0.0;
    9984    psF32 R2 = PS_SQR(radius);
    10085    psF32 minSN2 = PS_SQR(minSN);
     
    10994    // (int) so they can be used in the image index below.
    11095
    111     int xOff  = source->peak->x;
    112     int yOff  = source->peak->y;
    113     int xPeak = source->peak->x - source->pixels->col0; // coord of peak in subimage
    114     int yPeak = source->peak->y - source->pixels->row0; // coord of peak in subimage
    115 
    116     // we are guaranteed to have a valid pixel and variance at this location (right? right?)
    117     // psF32 weightNorm = source->pixels->data.F32[yPeak][xPeak] / sqrt (source->variance->data.F32[yPeak][xPeak]);
    118     // psAssert (isfinite(source->pixels->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
    119     // psAssert (isfinite(source->variance->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
    120     // psAssert (source->variance->data.F32[yPeak][xPeak] > 0, "peak must be on valid pixel");
    121 
    122     for (psS32 row = 0; row < source->pixels->numRows ; row++) {
    123 
    124         psF32 yDiff = row - yPeak;
    125         if (fabs(yDiff) > radius) continue;
    126 
    127         psF32 *vPix = source->pixels->data.F32[row];
    128         psF32 *vWgt = source->variance->data.F32[row];
    129         psImageMaskType *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
    130 
    131         for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
    132             if (vMsk) {
    133                 if (*vMsk & maskVal) {
    134                     vMsk++;
    135                     continue;
    136                 }
    137                 vMsk++;
    138             }
    139             if (isnan(*vPix)) continue;
    140 
    141             psF32 xDiff = col - xPeak;
    142             if (fabs(xDiff) > radius) continue;
    143 
    144             // radius is just a function of (xDiff, yDiff)
    145             if (!VALID_RADIUS(xDiff, yDiff, R2)) continue;
    146 
    147             psF32 pDiff = *vPix - sky;
    148             psF32 wDiff = *vWgt;
    149 
    150             // skip pixels below specified significance level.  for a PSFs, this
    151             // over-weights the wings of bright stars compared to those of faint stars.
    152             // for the estimator used for extended source analysis (where the window
    153             // function is allowed to be arbitrarily large), we need to clip to avoid
    154             // negative second moments.
    155             if (PS_SQR(pDiff) < minSN2*wDiff) continue; //
    156             if ((minSN > 0.0) && (pDiff < 0)) continue; //
    157 
    158             // Apply a Gaussian window function.  Be careful with the window function.  S/N
    159             // weighting over weights the sky for faint sources
    160             if (sigma > 0.0) {
    161                 // XXX a lot of extra flops; can we pre-calculate?
    162                 psF32 z  = (PS_SQR(xDiff) + PS_SQR(yDiff))*rsigma2;
    163                 assert (z >= 0.0);
    164                 psF32 weight  = exp(-z);
    165 
    166                 wDiff *= weight;
    167                 pDiff *= weight;
    168             }
    169 
    170             Var += wDiff;
    171             Sum += pDiff;
    172 
    173             psF32 xWght = xDiff * pDiff;
    174             psF32 yWght = yDiff * pDiff;
    175 
    176             X1  += xWght;
    177             Y1  += yWght;
    178 
    179             peakPixel = PS_MAX (*vPix, peakPixel);
    180             numPixels++;
    181         }
    182     }
    183 
    184     // if we have less than (1/4) of the possible pixels (in circle or box), force a retry
    185     int minPixels = PS_MIN(0.75*R2, source->pixels->numCols*source->pixels->numRows/4.0);
    186 
    187     // XXX EAM - the limit is a bit arbitrary.  make it user defined?
    188     if ((numPixels < minPixels) || (Sum <= 0)) {
    189         psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n", numPixels, minPixels, Sum);
    190         return (false);
    191     }
    192 
    193     // calculate the first moment.
    194     float Mx = X1/Sum;
    195     float My = Y1/Sum;
    196     if ((fabs(Mx) > radius) || (fabs(My) > radius)) {
    197         psTrace ("psModules.objects", 3, "large centroid swing; invalid peak %d, %d\n", source->peak->x, source->peak->y);
    198         return (false);
    199     }
    200 
    201     psTrace ("psModules.objects", 5, "sky: %f  Mx: %f  My: %f  Sum: %f  X1: %f  Y1: %f  Npix: %d\n", sky, Mx, My, Sum, X1, Y1, numPixels);
    202 
    203     // add back offset of peak in primary image
    204     // also offset from pixel index to pixel coordinate
    205     // (the calculation above uses pixel index instead of coordinate)
    206     // 0.5 PIX: moments are calculated using the pixel index and converted here to pixel coords
    207     source->moments->Mx = Mx + xOff + 0.5;
    208     source->moments->My = My + yOff + 0.5;
    209 
    210     source->moments->Sum = Sum;
    211     source->moments->SN  = Sum / sqrt(Var);
    212     source->moments->Peak = peakPixel;
    213     source->moments->nPixels = numPixels;
     96    // do 2 passes : the first pass should use a somewhat smaller radius and no sigma window to
     97    // get an unbiased (but probably noisy) centroid
     98    if (!pmSourceMomentsGetCentroid (source, 0.75*radius, 0.0, minSN, maskVal, source->peak->xf, source->peak->yf)) {
     99        return false;
     100    }
     101    // second pass applies the Gaussian window and uses the centroid from the first pass
     102    if (!pmSourceMomentsGetCentroid (source, radius, sigma, minSN, maskVal, source->moments->Mx, source->moments->My)) {
     103        return false;
     104    }
    214105
    215106    // Now calculate higher-order moments, using the above-calculated first moments to adjust coordinates
     
    234125    Sum = 0.0;  // the second pass may include slightly different pixels, re-determine Sum
    235126
    236     // center of mass in subimage.  Note: the calculation below uses pixel index, so we do not
    237     // correct xCM, yCM to pixel coordinate here.
    238     psF32 xCM = Mx + xPeak; // coord of peak in subimage
    239     psF32 yCM = My + yPeak; // coord of peak in subimage
     127    // center of mass in subimage.  Note: the calculation below uses pixel index, so we correct
     128    // xCM, yCM from pixel coords to pixel index here.
     129    psF32 xCM = source->moments->Mx - 0.5 - source->pixels->col0; // coord of peak in subimage
     130    psF32 yCM = source->moments->My - 0.5 - source->pixels->row0; // coord of peak in subimage
    240131
    241132    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
     
    263154            // radius is just a function of (xDiff, yDiff)
    264155            psF32 r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
    265             psF32 r  = sqrt(r2);
    266             if (r > radius) continue;
     156            if (r2 > R2) continue;
    267157
    268158            psF32 fDiff = *vPix - sky;
     
    274164            // stars.
    275165            if (PS_SQR(pDiff) < minSN2*wDiff) continue;
    276             // if (pDiff < 0) continue;
     166            if ((minSN > 0.0) && (pDiff < 0)) continue; //
    277167
    278168            // Apply a Gaussian window function.  Be careful with the window function.  S/N
    279169            // weighting over weights the sky for faint sources
    280170            if (sigma > 0.0) {
    281                 // XXX a lot of extra flops; can we do pre-calculate?
    282                 // XXX we were re-calculating r2 (maybe the compiler caught this?)
    283                 // psF32 z  = (PS_SQR(xDiff) + PS_SQR(yDiff))*rsigma2;
    284                 psF32 z  = r2 * rsigma2;
     171                psF32 z = r2 * rsigma2;
    285172                assert (z >= 0.0);
    286173                psF32 weight  = exp(-z);
     
    292179            Sum += pDiff;
    293180
    294 # if (1)
    295 # if (0)
    296             if (fDiff < 0) continue;
    297 # endif
     181            // Kron Flux uses the 1st radial moment (NOT Gaussian windowed?)
     182            psF32 r = sqrt(r2);
    298183            psF32 rf = r * fDiff;
    299184            psF32 rh = sqrt(r) * fDiff;
    300185            psF32 rs = fDiff;
    301 # else
    302             psF32 rf = r * pDiff;
    303             psF32 rh = sqrt(r) * pDiff;
    304             psF32 rs = pDiff;
    305 # endif
    306186
    307187            psF32 x = xDiff * pDiff;
     
    363243
    364244    // Calculate the Kron magnitude (make this block optional?)
    365     // float radKron = 2.5*source->moments->Mrf;
    366245    float radKinner = 1.0*source->moments->Mrf;
    367246    float radKron   = 2.5*source->moments->Mrf;
     
    397276            // radKron is just a function of (xDiff, yDiff)
    398277            psF32 r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
    399             psF32 r  = sqrt(r2);
    400278
    401279            psF32 pDiff = *vPix - sky;
     
    407285            if (PS_SQR(pDiff) < minSN2*wDiff) continue;
    408286
     287            psF32 r  = sqrt(r2);
    409288            if (r < radKron) {
    410289                Sum += pDiff;
     
    434313
    435314    psTrace ("psModules.objects", 3, "peak %f %f (%f = %f) Mx: %f  My: %f  Sum: %f  Mxx: %f  Mxy: %f  Myy: %f  sky: %f  Npix: %d\n",
    436              source->peak->xf, source->peak->yf, source->peak->flux, source->peak->SN, source->moments->Mx,   source->moments->My, Sum, source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy, sky, numPixels);
     315             source->peak->xf, source->peak->yf, source->peak->flux, source->peak->SN, source->moments->Mx,   source->moments->My, Sum, source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy, sky, source->moments->nPixels);
    437316
    438317    return(true);
    439318}
     319
     320bool pmSourceMomentsGetCentroid(pmSource *source, psF32 radius, psF32 sigma, psF32 minSN, psImageMaskType maskVal, float xGuess, float yGuess) {
     321
     322    // First Pass: calculate the first moments (these are subtracted from the coordinates below)
     323    // Sum = SUM (z - sky)
     324    // X1  = SUM (x - xc)*(z - sky)
     325    // .. etc
     326
     327    psF32 sky = 0.0;
     328
     329    psF32 peakPixel = -PS_MAX_F32;
     330    psS32 numPixels = 0;
     331    psF32 Sum = 0.0;
     332    psF32 Var = 0.0;
     333    psF32 X1 = 0.0;
     334    psF32 Y1 = 0.0;
     335    psF32 R2 = PS_SQR(radius);
     336    psF32 minSN2 = PS_SQR(minSN);
     337    psF32 rsigma2 = 0.5 / PS_SQR(sigma);
     338
     339    float xPeak = xGuess - source->pixels->col0; // coord of peak in subimage
     340    float yPeak = yGuess - source->pixels->row0; // coord of peak in subimage
     341
     342    // we are guaranteed to have a valid pixel and variance at this location (right? right?)
     343    // psF32 weightNorm = source->pixels->data.F32[yPeak][xPeak] / sqrt (source->variance->data.F32[yPeak][xPeak]);
     344    // psAssert (isfinite(source->pixels->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
     345    // psAssert (isfinite(source->variance->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
     346    // psAssert (source->variance->data.F32[yPeak][xPeak] > 0, "peak must be on valid pixel");
     347
     348    // the moments [Sum(x*f) / Sum(f)] are calculated in pixel index values, and should
     349    // not depend on the fractional pixel location of the source.  However, the aperture
     350    // (radius) and the Gaussian window (sigma) depend subtly on the fractional pixel
     351    // position of the expected centroid
     352
     353    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
     354
     355        psF32 yDiff = row + 0.5 - yPeak;
     356        if (fabs(yDiff) > radius) continue;
     357
     358        psF32 *vPix = source->pixels->data.F32[row];
     359        psF32 *vWgt = source->variance->data.F32[row];
     360        psImageMaskType *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
     361
     362        for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
     363            if (vMsk) {
     364                if (*vMsk & maskVal) {
     365                    vMsk++;
     366                    continue;
     367                }
     368                vMsk++;
     369            }
     370            if (isnan(*vPix)) continue;
     371
     372            psF32 xDiff = col + 0.5 - xPeak;
     373            if (fabs(xDiff) > radius) continue;
     374
     375            // radius is just a function of (xDiff, yDiff)
     376            psF32 r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
     377            if (r2 > R2) continue;
     378
     379            psF32 pDiff = *vPix - sky;
     380            psF32 wDiff = *vWgt;
     381
     382            // skip pixels below specified significance level.  for a PSFs, this
     383            // over-weights the wings of bright stars compared to those of faint stars.
     384            // for the estimator used for extended source analysis (where the window
     385            // function is allowed to be arbitrarily large), we need to clip to avoid
     386            // negative second moments.
     387            if (PS_SQR(pDiff) < minSN2*wDiff) continue; //
     388            if ((minSN > 0.0) && (pDiff < 0)) continue; //
     389
     390            // Apply a Gaussian window function.  Be careful with the window function.  S/N
     391            // weighting over weights the sky for faint sources
     392            if (sigma > 0.0) {
     393                psF32 z  = r2*rsigma2;
     394                assert (z >= 0.0);
     395                psF32 weight  = exp(-z);
     396
     397                wDiff *= weight;
     398                pDiff *= weight;
     399            }
     400
     401            Var += wDiff;
     402            Sum += pDiff;
     403
     404            psF32 xWght = xDiff * pDiff;
     405            psF32 yWght = yDiff * pDiff;
     406
     407            X1  += xWght;
     408            Y1  += yWght;
     409
     410            peakPixel = PS_MAX (*vPix, peakPixel);
     411            numPixels++;
     412        }
     413    }
     414
     415    // if we have less than (1/4) of the possible pixels (in circle or box), force a retry
     416    int minPixels = PS_MIN(0.75*R2, source->pixels->numCols*source->pixels->numRows/4.0);
     417
     418    // XXX EAM - the limit is a bit arbitrary.  make it user defined?
     419    if ((numPixels < minPixels) || (Sum <= 0)) {
     420        psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n", numPixels, minPixels, Sum);
     421        return (false);
     422    }
     423
     424    // calculate the first moment.
     425    float Mx = X1/Sum;
     426    float My = Y1/Sum;
     427    if ((fabs(Mx) > radius) || (fabs(My) > radius)) {
     428        psTrace ("psModules.objects", 3, "large centroid swing; invalid peak %d, %d\n", source->peak->x, source->peak->y);
     429        return (false);
     430    }
     431
     432    psTrace ("psModules.objects", 5, "sky: %f  Mx: %f  My: %f  Sum: %f  X1: %f  Y1: %f  Npix: %d\n", sky, Mx, My, Sum, X1, Y1, numPixels);
     433
     434    // add back offset of peak in primary image
     435    // also offset from pixel index to pixel coordinate
     436    // (the calculation above uses pixel index instead of coordinate)
     437    // 0.5 PIX: moments are calculated using the pixel index and converted here to pixel coords
     438
     439    // we only update the centroid if the position is not supplied from elsewhere
     440    bool skipCentroid = false;
     441    skipCentroid |= (source->mode  & PM_SOURCE_MODE_EXTERNAL); // skip externally supplied positions
     442    skipCentroid |= (source->mode2 & PM_SOURCE_MODE2_MATCHED); // skip sources defined by other image positions
     443
     444    if (skipCentroid) {
     445        source->moments->Mx = source->peak->xf;
     446        source->moments->My = source->peak->yf;
     447    } else {
     448        source->moments->Mx = Mx + xGuess;
     449        source->moments->My = My + yGuess;
     450    }
     451
     452    source->moments->Sum = Sum;
     453    source->moments->SN  = Sum / sqrt(Var);
     454    source->moments->Peak = peakPixel;
     455    source->moments->nPixels = numPixels;
     456
     457    return true;
     458}
Note: See TracChangeset for help on using the changeset viewer.