IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Jun 15, 2011, 6:10:15 PM (15 years ago)
Author:
watersc1
Message:

merge from czw branch: improved stacking using KMM outlier identification.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/psModules/src/imcombine/pmStack.c

    r31203 r31634  
    2222#include <pslib.h>
    2323
     24#include <gsl/gsl_cdf.h>
     25
    2426#include "pmHDU.h"
    2527#include "pmFPA.h"
     
    3537
    3638
    37 # if (0)
     39//# if (0)
    3840#define TESTING                         // Enable test output
    39 #define TEST_X 3145                       // x coordinate to examine
    40 #define TEST_Y 2334                       // y coordinate to examine
     41/* #define TEST_X 5745                       // x coordinate to examine */
     42/* #define TEST_Y 5331                       // y coordinate to examine */
     43#define TEST_X 25
     44#define TEST_Y 25
    4145#define TEST_RADIUS 0.5                 // Radius to examine
    42 # endif
     46//# endif
    4347
    4448# ifdef TESTING
     
    100104    return;
    101105}
     106
     107// KMM functions to do bimodality rejection of pixels
     108float gaussian(float x, float m, float s) {
     109  return(pow(s * sqrt(2 * M_PI),-1) * exp(-0.5 * pow( (x - m) / s, 2)));
     110}
     111
     112static void KMMcalculate(const psVector *values,
     113                         float *Punimodal,int *iter,
     114                         float *mU, float *sU,
     115                         float *pi1, float *m1, float *s1,
     116                         float *pi2, float *m2, float *s2) {
     117  assert(values);
     118  assert(values->type.type == PS_TYPE_F32);
     119 
     120  double logL_bimodal = 0, logL_unimodal;
     121  psVector *P1 = psVectorAlloc(values->n,PS_TYPE_F32);
     122  psVector *P2 = psVectorAlloc(values->n,PS_TYPE_F32);
     123  int i;
     124/*   int debug = 0; */
     125 
     126  // Calculate unimodal properties
     127  *mU = 0;
     128  *sU = 0;
     129  logL_unimodal = 0;
     130  for (i = 0; i < values->n; i++) { // Calculate mean
     131    *mU += values->data.F32[i];
     132  }
     133  *mU /= values->n;
     134  for (i = 0; i < values->n; i++) { // Calculate sigma
     135    *sU += pow(values->data.F32[i] - *mU,2);
     136  }
     137  *sU = sqrt(*sU / values->n);
     138  for (i = 0; i < values->n; i++) { // Calculate log likelihood
     139    logL_unimodal += log(gaussian(values->data.F32[i],*mU,*sU));
     140  }
     141
     142  // Do EM loop
     143  float dL = 0;
     144  float oldL = -999;
     145  *iter = 0;
     146  logL_bimodal = logL_unimodal;
     147  *m1 = *mU - 3 * *sU;
     148  *m2 = *mU + 3 * *sU;
     149  *s1 = *sU / 2;
     150  *s2 = *sU / 2;
     151  *pi1 = 0.5;
     152  *pi2 = 0.5;
     153
     154  float g1,g2,norm;
     155  float w1,w2;
     156
     157  // These should be options.
     158  float KMM_TOLERANCE = 1e-6;
     159  int KMM_MAX_ITERATIONS = 500;
     160  float KMM_SMALL_NUMBER = 1e-5;
     161  while (((dL > KMM_TOLERANCE)||(*iter < 3))&&(*iter < KMM_MAX_ITERATIONS)) {
     162    *iter += 1;
     163    dL = fabs(logL_bimodal - oldL);
     164    oldL = logL_bimodal;
     165/*     if (debug == 1) { */
     166/*       fprintf(stderr,"KMM: %d %f %f %f %f (%f %f %f) (%f %f %f)\n", */
     167/*            *iter,logL_unimodal,logL_bimodal,oldL,dL, */
     168/*            *m1,*s1,*pi1, */
     169/*            *m2,*s2,*pi2); */
     170/*     } */
     171    // Expectation/P-stage
     172    for (i = 0; i < values->n; i++) { // Calculate probabilities for each mode
     173      g1 = gaussian(values->data.F32[i],*m1,*s1);
     174      g2 = gaussian(values->data.F32[i],*m2,*s2);
     175      norm = (*pi1 * g1 + *pi2 * g2);
     176      P1->data.F32[i] = (*pi1 * g1) / norm;
     177      P2->data.F32[i] = (*pi2 * g2) / norm;
     178    }
     179    // Maximization/M-stage
     180    logL_bimodal = 0;
     181    w1 = 0;
     182    w2 = 0;
     183    for (i = 0; i < values->n; i++) { // Calculate log likelihood
     184      if (!((*pi1 == 0)||(*pi2 == 0))) {
     185        logL_bimodal += log(*pi1 * gaussian(values->data.F32[i],*m1,*s1) +
     186                            *pi2 * gaussian(values->data.F32[i],*m2,*s2));
     187      }
     188    }
     189    *m1 = 0;
     190    *m2 = 0;
     191    *s1 = 0;
     192    *s2 = 0;
     193    for (i = 0; i < values->n; i++) { // Calculate new means and weights
     194      *m1 += values->data.F32[i] * P1->data.F32[i];
     195      *m2 += values->data.F32[i] * P2->data.F32[i];
     196
     197      w1 += P1->data.F32[i];
     198      w2 += P2->data.F32[i];
     199    }
     200    *m1 /= w1;
     201    *m2 /= w2;
     202    for (i = 0; i < values->n; i++) { // Calculate new sigmas
     203      *s1 += pow(values->data.F32[i] - *m1,2) * P1->data.F32[i];
     204      *s2 += pow(values->data.F32[i] - *m2,2) * P2->data.F32[i];
     205    }
     206    *s1 = sqrt(*s1 / w1);
     207    *s2 = sqrt(*s2 / w2);
     208
     209    *pi1 = w1 / values->n;
     210    *pi2 = w2 / values->n;
     211
     212    if (!isfinite(*pi1)) { // finite checks
     213      *pi1 = 0.0;
     214    }
     215    if (!isfinite(*pi2)) { // finite checks
     216      *pi2 = 0.0;
     217    }
     218    if (*s1 == 0) { // sigma may not be zero
     219      *s1 = KMM_SMALL_NUMBER * *m1;
     220    }
     221    if (*s2 == 0) { // sigma may not be zero
     222      *s2 = KMM_SMALL_NUMBER * *m2;
     223    }
     224  } // End EM phase
     225
     226  // Calculate Punimodal
     227  double lambda = -2.0 * (logL_unimodal - logL_bimodal);
     228  int    df     = 2 + 2 * 1; // I can't find my reference on this.
     229  if (lambda > 0) {
     230    *Punimodal = gsl_cdf_chisq_Q(lambda,df);
     231  }
     232  else { // If lambda <= 0, then logL_unimodal > logL_bimodal, so Punimodal must be by definition 1.0
     233    *Punimodal = 1.0;
     234  }
     235  psFree(P1);
     236  psFree(P2);
     237}
     238
     239static void KMMFindPopular(const psVector *values, float *Punimodal, float *mean, float *sigma, float *pi) {
     240  float KMM_MINIMUM_PVALUE = 0.05; // Should be an option.
     241  float mU,sU;
     242  float pi1,m1,s1,pi2,m2,s2;
     243  int iter;
     244
     245  assert(values);
     246  assert(values->type.type == PS_TYPE_F32);
     247 
     248  KMMcalculate(values,Punimodal,&iter,
     249               &mU,&sU,
     250               &pi1,&m1,&s1,
     251               &pi2,&m2,&s2);
     252
     253  if (*Punimodal > KMM_MINIMUM_PVALUE) {
     254    // Is unimodal
     255    *mean = mU;
     256    *sigma = sU;
     257    *pi = 1.0;
     258  }
     259  else {
     260    // Is bimodal. Select most popular mode.
     261    if (pi1 >= pi2) {
     262      *mean = m1;
     263      *sigma = s1;
     264      *pi = pi1;
     265    }
     266    else {
     267      *mean = m2;
     268      *sigma = s2;
     269      *pi = pi2;
     270    }
     271  } 
     272}
     273
     274                               
    102275
    103276
     
    285458    return;
    286459}
    287 
     460#if (0)
     461// Currently unused function to reject inputs for a given pixel(x,y) based on the selecting the
     462// most popular mode after running the KMM test, and rejecting all inputs that belong to the
     463// least popular mode.
     464static void KMMRejectUnpopular(const psArray *inputs, int x, int y) {
     465  float KMM_MINIMUM_PVALUE = 0.05;
     466  float mU,sU;
     467  float Punimodal,pi1,m1,s1,pi2,m2,s2;
     468  int iter;
     469  int j,k;
     470
     471  psVector *values = psVectorAlloc(inputs->n, PS_TYPE_F32);
     472  k = 0;
     473  for (j = 0; j < inputs->n; j++) {
     474    pmStackData *data = inputs->data[j]; // Stack data of interest
     475    if (!data) {
     476      k++;
     477      continue;
     478    }
     479    psImage *image = data->readout->image; // Image of interest
     480    int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
     481    values->data.F32[j - k] = image->data.F32[yIn][xIn];
     482  }
     483 
     484  KMMcalculate(values,&Punimodal,&iter,
     485               &mU,&sU,
     486               &pi1,&m1,&s1,
     487               &pi2,&m2,&s2);
     488
     489   CHECKPIX(x, y,
     490          "KMM Unpopular Test: %d,%d: Puni: %g in %d",x,y,Punimodal,iter); 
     491  if (Punimodal < KMM_MINIMUM_PVALUE) {
     492    int i;
     493    float g1,g2,norm;
     494    float P1,P2;
     495
     496    for (i = 0; i < values->n; i++) { // Calculate probabilities for each mode
     497      g1 = gaussian(values->data.F32[i],m1,s1);
     498      g2 = gaussian(values->data.F32[i],m2,s2);
     499      norm = (pi1 * g1 + pi2 * g2);
     500      P1 = (pi1 * g1) / norm;
     501      P2 = (pi2 * g2) / norm;
     502
     503      CHECKPIX(x, y, "KMM Unpopular Rejection: %d,%d: %f(%d): %d %f %f:(%f %f %f ) %f:(%f %f %f) rejection? %d %d\n",
     504               x, y,
     505               Punimodal,iter,
     506               i, values->data.F32[i],
     507               P1,m1,s1,pi1,
     508               P2,m2,s2,pi2,
     509               (pi1 > pi2)&&(P1 < P2),
     510               (pi1 < pi2)&&(P1 > P2));
     511      if ((pi1 > pi2)&&(P1 < P2)) { // mode 1 is more popular, but this element belongs to mode 2
     512        combineMarkReject(inputs,x,y,i);
     513      }
     514      if ((pi1 < pi2)&&(P1 > P2)) { // mode 2 is more popular, but this element belongs to mode 1
     515        combineMarkReject(inputs,x,y,i);
     516      }
     517    }
     518  }
     519  psFree(values);
     520  // else do nothing.
     521}
     522
     523// Currently unused function to reject inputs for a given pixel(x,y) based on the selecting the
     524// faintest mode as determined by the KMM test, and rejecting all inputs that belong to the brighest.
     525static void KMMRejectBright(const psArray *inputs, int x, int y) {
     526  float KMM_MINIMUM_PVALUE = 0.05;
     527  float mU,sU;
     528  float Punimodal,pi1,m1,s1,pi2,m2,s2;
     529  int iter;
     530  int j;
     531
     532  psVector *values = psVectorAlloc(inputs->n, PS_TYPE_F32);
     533  for (j = 0; j < inputs->n; j++) {
     534    pmStackData *data = inputs->data[j]; // Stack data of interest
     535    psImage *image = data->readout->image; // Image of interest
     536    int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
     537    values->data.F32[j] = image->data.F32[yIn][xIn];
     538  }
     539 
     540  KMMcalculate(values,&Punimodal,&iter,
     541               &mU,&sU,
     542               &pi1,&m1,&s1,
     543               &pi2,&m2,&s2);
     544  if (Punimodal < KMM_MINIMUM_PVALUE) {
     545    int i;
     546    float g1,g2,norm;
     547    float P1,P2;
     548
     549    for (i = 0; i < values->n; i++) { // Calculate probabilities for each mode
     550      g1 = gaussian(values->data.F32[i],m1,s1);
     551      g2 = gaussian(values->data.F32[i],m2,s2);
     552      norm = (pi1 * g1 + pi2 * g2);
     553      P1 = (pi1 * g1) / norm;
     554      P2 = (pi2 * g2) / norm;
     555
     556      if ((m1 > m2)&&(P1 > P2)) { // m1 is larger, and this element belongs to mode 1
     557        combineMarkReject(inputs,x,y,i);
     558      }
     559      if ((m1 < m2)&&(P1 < P2)) { // m2 is larger, and this element belongs to mode 2
     560        combineMarkReject(inputs,x,y,i);
     561      }
     562    }
     563  }
     564  psFree(values);
     565  // else do nothing.
     566}
     567#endif // End if(0) to prevent KMMReject{Unpopular|Bright} from being defined.
    288568
    289569// Extract vectors for simple combination/rejection operations
     
    332612    memset (nGoodBits, 0, 16*sizeof(int));
    333613
    334     // Extract the pixel and mask data
     614
     615    // Extract the pixel and mask data   
    335616    int numGood = 0;                    // Number of good pixels
    336617    for (int i = 0, j = 0; i < inputs->n; i++) {
     
    364645            continue;
    365646        }
    366 
     647       
    367648        int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
    368649        psImage *mask = data->readout->mask; // Mask of interest
     
    400681        CHECKPIX(x, y, "keep: %d : %x (badMask = %x)\n", i, mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn], *badMask);
    401682    }
     683   
    402684    pixelData->n = numGood;
    403685    if (variance) {
     
    426708    if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    427709        for (int i = 0; i < numGood; i++) {
    428             fprintf(stderr, "Input %d, pixel %d,%d (%" PRIu16 "): %f %f (%f) %f %f %d %x %x -> %x %x\n",
     710            fprintf(stderr,"Input %d, pixel %d,%d (%" PRIu16 "): %f %f (%f) %f %f %d %x %x -> %x %x\n",
    429711                    i, x, y, pixelSources->data.U16[i], pixelData->data.F32[i], pixelVariances->data.F32[i],
    430712                    addVariance->data.F32[i], pixelWeights->data.F32[i], pixelExps->data.F32[i],
     
    567849    psVector *pixelLimits = buffer->limits; // Is the pixel suspect?
    568850
     851    // KMM values;
     852    float Punimodal = 1.0,KMMmean = NAN,KMMsigma = NAN,KMMpi = NAN;
     853    int KMM_MINIMUM_INPUTS = 6;
     854    bool useKMM = false;
     855    if (num >= KMM_MINIMUM_INPUTS) {
     856      useKMM = true;
     857    }
     858   
    569859    // Set up rejection limits
    570860    float rej2 = PS_SQR(rej); // Rejection level squared
     
    573863        // Using squared rejection limit because it's cheaper than sqrts
    574864        double sumWeights = 0.0;
     865
     866        // Determine the systematic error from the most popular population in the sample
     867        // This should probably be an option
     868        if (useKMM) {
     869          KMMFindPopular(pixelData,&Punimodal,&KMMmean,&KMMsigma,&KMMpi);
     870          CHECKPIX(x,y,"KMM Popularity Contest: (%d,%d) Puni: %g Mean: %f Sigma %f Pi: %f\n",
     871                   x,y,Punimodal,KMMmean,KMMsigma,KMMpi);
     872        }
    575873        for (int i = 0; i < num; i++) {
    576874            sumWeights += pixelWeights->data.F32[i];
    577875        }
    578876        for (int i = 0; i < num; i++) {
    579             // Systematic error contributes to the rejection level
    580             float sysVar = PS_SQR(sys * pixelData->data.F32[i]);
     877          // Systematic error contributes to the rejection level
     878          float sysVar;
     879          if (useKMM) { // If we can trust KMM results, set the systematic variance
     880            sysVar = PS_SQR(KMMsigma);
     881          }
     882          else { // Otherwise, use the 10% systematic variance we've done in the past.
     883            sysVar = PS_SQR(sys * pixelData->data.F32[i]);
     884          }
     885
    581886            CHECKPIX(x, y, "Variance %d (%d), pixel %d,%d: %f %f %f\n",
    582887                     i, pixelSources->data.U16[i], x, y,
     
    7271032          default: {
    7281033              if (useVariance) {
    729                   float median = combinationWeightedOlympic(pixelData, pixelWeights,
    730                                                             olympic, buffer->sort); // Median for stack
     1034                float median;
     1035                if ((useKMM)&&(Punimodal < 0.05)) {
     1036                  median = KMMmean;
     1037                }
     1038                else {
     1039                  median = combinationWeightedOlympic(pixelData, pixelWeights,
     1040                                                      olympic, buffer->sort); // Median for stack
     1041                }
     1042               
    7311043                  CHECKPIX(x, y, "Flag with variance pixel %d,%d: median = %f\n", x, y, median);
    7321044                  float worst = -INFINITY; // Largest deviation
    7331045                  for (int j = 0; j < num; j++) {
    734                       float diff = pixelData->data.F32[j] - median; // Difference from expected
     1046                      float diff = pixelData->data.F32[j] - median; // Difference from expected
    7351047                      CHECKPIX(x, y, "Testing input %d for pixel %d,%d: %f\n", j, x, y, diff);
    7361048
     
    7381050                      // pixelVariances includes the rejection limit, from above
    7391051                      float diff2 = PS_SQR(diff); // Square difference
     1052                      CHECKPIX(x,y, "Input %d, pixel %d,%d (%" PRIu16 "): %f %f (%f) %f %f :: %f %f %f %f\n",
     1053                              i, x, y, pixelSources->data.U16[j], pixelData->data.F32[j], pixelVariances->data.F32[j],
     1054                              1.0, pixelWeights->data.F32[j], 1.0,
     1055                              pixelLimits->data.F32[j], diff2, diff2 / pixelLimits->data.F32[j],worst);
     1056
    7401057                      if (diff2 > pixelLimits->data.F32[j]) {
    7411058                          float dev = diff2 / pixelLimits->data.F32[j]; // Deviation
     
    11311448    }
    11321449
     1450/*     // Pre-reject inputs using KMM bimodality test. */
     1451/*     if (1)  { */
     1452/* /\*       KMMRejectUnpopular(input,x,y); *\/ */
     1453/*       rejection = true; */
     1454/*     } */
     1455   
    11331456    // Set up rejection list
    11341457    psArray *pixelMap = NULL;           // Map of pixels to source
     
    11411464        for (int x = minInputCols; x < maxInputCols; x++) {
    11421465            CHECKPIX(x, y, "Combining pixel %d,%d: %x %x %f %f %f %f %d %d %d\n", x, y, badMaskBits, blankMaskBits, iter, rej, sys, olympic, useVariance, safe, rejection);
     1466
     1467/*          // Pre-reject inputs using KMM bimodality test. */
     1468/*        if (1)  { */
     1469/*          KMMRejectUnpopular(input,x,y); */
     1470/* /\*      rejection = true; *\/ */
     1471/*        } */
     1472/*        else { */
     1473/*          KMMRejectBright(input,x,y); */
     1474/*        } */
    11431475
    11441476#ifdef TESTING
Note: See TracChangeset for help on using the changeset viewer.