IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 42091


Ignore:
Timestamp:
Feb 28, 2022, 2:47:57 PM (4 years ago)
Author:
eugene
Message:

finalize options for pmStackCombineByPercentile, including calculation of pixel variance and mask values

Location:
trunk/psModules/src/imcombine
Files:
2 edited

Legend:

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

    r41892 r42091  
    113113// KMM functions to do bimodality rejection of pixels
    114114double gaussian(float x, float m, float s) {
    115   return(pow(s * sqrt(2 * M_PI),-1) * exp(-0.5 * pow( (x - m) / s, 2)));
     115    return(pow(s * sqrt(2 * M_PI),-1) * exp(-0.5 * pow( (x - m) / s, 2)));
    116116}
    117117
     
    122122                         float *pi2, float *m2, float *s2,
    123123                         int xyrdebug) {
    124   assert(values);
    125   assert(values->type.type == PS_TYPE_F32);
     124    assert(values);
     125    assert(values->type.type == PS_TYPE_F32);
    126126 
    127   double logL_bimodal = 0, logL_unimodal;
    128   psVector *P1 = psVectorAlloc(values->n,PS_TYPE_F32);
    129   psVector *P2 = psVectorAlloc(values->n,PS_TYPE_F32);
    130   int i;
    131   int discrepant_index = -1;
    132   double discrepant_value = 0;
     127    double logL_bimodal = 0, logL_unimodal;
     128    psVector *P1 = psVectorAlloc(values->n,PS_TYPE_F32);
     129    psVector *P2 = psVectorAlloc(values->n,PS_TYPE_F32);
     130    int i;
     131    int discrepant_index = -1;
     132    double discrepant_value = 0;
    133133/*   int debug = 0; */
    134134 
    135   // Calculate unimodal properties
    136   *mU = 0;
    137   *sU = 0;
    138   logL_unimodal = 0;
    139   for (i = 0; i < values->n; i++) { // Calculate mean
    140     *mU += values->data.F32[i];
    141   }
    142   *mU /= values->n;
    143   for (i = 0; i < values->n; i++) { // Calculate sigma
    144     *sU += pow(values->data.F32[i] - *mU,2);
    145 
    146     // Attempt to guess better starting values
    147     if (pow(values->data.F32[i] - *mU,2) > discrepant_value) {
    148       discrepant_value = pow(values->data.F32[i] - *mU,2);
    149       discrepant_index = i;
    150     }
     135    // Calculate unimodal properties
     136    *mU = 0;
     137    *sU = 0;
     138    logL_unimodal = 0;
     139    for (i = 0; i < values->n; i++) { // Calculate mean
     140        *mU += values->data.F32[i];
     141    }
     142    *mU /= values->n;
     143    for (i = 0; i < values->n; i++) { // Calculate sigma
     144        *sU += pow(values->data.F32[i] - *mU,2);
     145
     146        // Attempt to guess better starting values
     147        if (pow(values->data.F32[i] - *mU,2) > discrepant_value) {
     148            discrepant_value = pow(values->data.F32[i] - *mU,2);
     149            discrepant_index = i;
     150        }
    151151   
    152   }
    153   *sU = sqrt(*sU / values->n);
    154   for (i = 0; i < values->n; i++) { // Calculate log likelihood
    155     logL_unimodal += log(gaussian(values->data.F32[i],*mU,*sU));
    156   }
    157 
    158   if (xyrdebug == 1) {
    159       fprintf(stderr,"KMM uni: %d %f %d (%f %f)\n",
    160               xyrdebug,logL_unimodal,discrepant_index,
    161               *mU,*sU);
    162   }
    163 
    164   // Do EM loop
    165   float dL = 0;
    166   float oldL = -999;
    167   *iter = 0;
    168   logL_bimodal = logL_unimodal;
    169 
    170   if (discrepant_index == -1) {
    171     *m1 = *mU - 3 * *sU;
    172     *m2 = *mU + 3 * *sU;
    173     *s1 = *sU / 2;
    174     *s2 = *sU / 2;
    175   }
    176   else {
    177     // This is an attempt to speed up convergence. Find the largest contributor to sigma, and set one mean
    178     // to that value.  Set the other mean to the mean of all other points with this one removed.  Next,
    179     // set the sigmas to be equal to each other.  Take the value of sigma to be such that a point equidistant
    180     // to the initial values of the two modes is equally not believed by either mode (2.5 sigma away).
     152    }
     153    *sU = sqrt(*sU / values->n);
     154    for (i = 0; i < values->n; i++) { // Calculate log likelihood
     155        logL_unimodal += log(gaussian(values->data.F32[i],*mU,*sU));
     156    }
     157
     158    if (xyrdebug == 1) {
     159        fprintf(stderr,"KMM uni: %d %f %d (%f %f)\n",
     160                xyrdebug,logL_unimodal,discrepant_index,
     161                *mU,*sU);
     162    }
     163
     164    // Do EM loop
     165    float dL = 0;
     166    float oldL = -999;
     167    *iter = 0;
     168    logL_bimodal = logL_unimodal;
     169
     170    if (discrepant_index == -1) {
     171        *m1 = *mU - 3 * *sU;
     172        *m2 = *mU + 3 * *sU;
     173        *s1 = *sU / 2;
     174        *s2 = *sU / 2;
     175    }
     176    else {
     177        // This is an attempt to speed up convergence. Find the largest contributor to sigma, and set one mean
     178        // to that value.  Set the other mean to the mean of all other points with this one removed.  Next,
     179        // set the sigmas to be equal to each other.  Take the value of sigma to be such that a point equidistant
     180        // to the initial values of the two modes is equally not believed by either mode (2.5 sigma away).
    181181   
    182     discrepant_value = values->data.F32[discrepant_index];
     182        discrepant_value = values->data.F32[discrepant_index];
    183183   
    184     if (discrepant_value >  *mU) {
    185       *m1 = ((*mU * values->n) - discrepant_value) / (values->n - 1);
    186       *m2 = discrepant_value;
    187     }
    188     else {
    189       *m1 = discrepant_value;
    190       *m2 = ((*mU * values->n) - discrepant_value) / (values->n - 1);
    191     }
    192     *s1 = fabs((*m1 - *m2) / 5);
    193     *s2 = *s1;
    194   }
     184        if (discrepant_value >  *mU) {
     185            *m1 = ((*mU * values->n) - discrepant_value) / (values->n - 1);
     186            *m2 = discrepant_value;
     187        }
     188        else {
     189            *m1 = discrepant_value;
     190            *m2 = ((*mU * values->n) - discrepant_value) / (values->n - 1);
     191        }
     192        *s1 = fabs((*m1 - *m2) / 5);
     193        *s2 = *s1;
     194    }
    195195   
    196   *pi1 = 0.5;
    197   *pi2 = 0.5;
    198 
    199   //MEH -- need to be double to help avoid 0 in norm
    200   double g1,g2,norm;
    201   float w1,w2;
    202 
    203   // These should be options.
    204   float KMM_TOLERANCE = 1e-6;
    205   int KMM_MAX_ITERATIONS = 30;
    206   float KMM_SMALL_NUMBER = 1e-5;
    207   while (((dL > KMM_TOLERANCE)||(*iter < 3))&&(*iter < KMM_MAX_ITERATIONS)) {
    208     *iter += 1;
    209     dL = fabs(logL_bimodal - oldL);
    210     oldL = logL_bimodal;
     196    *pi1 = 0.5;
     197    *pi2 = 0.5;
     198
     199    //MEH -- need to be double to help avoid 0 in norm
     200    double g1,g2,norm;
     201    float w1,w2;
     202
     203    // These should be options.
     204    float KMM_TOLERANCE = 1e-6;
     205    int KMM_MAX_ITERATIONS = 30;
     206    float KMM_SMALL_NUMBER = 1e-5;
     207    while (((dL > KMM_TOLERANCE)||(*iter < 3))&&(*iter < KMM_MAX_ITERATIONS)) {
     208        *iter += 1;
     209        dL = fabs(logL_bimodal - oldL);
     210        oldL = logL_bimodal;
    211211/*     if (debug == 1) { */
    212212/*       fprintf(stderr,"KMM: %d %f %f %f %f (%f %f %f) (%f %f %f)\n", */
     
    216216/*     } */
    217217
     218        if (xyrdebug == 1) {
     219            fprintf(stderr,"KMM EM iter: %d %f %f %f %f (%f %f %e) (%f %f %e)\n",
     220                    *iter,logL_unimodal,logL_bimodal,oldL,dL,
     221                    *m1,*s1,*pi1,
     222                    *m2,*s2,*pi2);
     223        }
     224
     225        // Expectation/P-stage
     226        for (i = 0; i < values->n; i++) { // Calculate probabilities for each mode
     227            g1 = gaussian(values->data.F32[i],*m1,*s1);
     228            g2 = gaussian(values->data.F32[i],*m2,*s2);
     229            norm = (*pi1 * g1 + *pi2 * g2);
     230            //MEH -- must protect denom from norm=0
     231            if (norm > 0) {
     232                P1->data.F32[i] = (*pi1 * g1) / norm;
     233                P2->data.F32[i] = (*pi2 * g2) / norm;
     234            } else {
     235                P1->data.F32[i] = 0.0;
     236                P2->data.F32[i] = 0.0;
     237            }   
     238 
     239            if (xyrdebug == 1) {
     240                fprintf(stderr,"KMM EM-P loop: %d %d %le %le %le\n",
     241                        *iter,i,norm,g1,g2);
     242            }
     243
     244        }
     245        // Maximization/M-stage
     246        logL_bimodal = 0;
     247        w1 = 0;
     248        w2 = 0;
     249        for (i = 0; i < values->n; i++) { // Calculate log likelihood
     250            if (!((*pi1 == 0)||(*pi2 == 0))) {
     251                logL_bimodal += log(*pi1 * gaussian(values->data.F32[i],*m1,*s1) +
     252                                    *pi2 * gaussian(values->data.F32[i],*m2,*s2));
     253            }
     254        }
     255        *m1 = 0;
     256        *m2 = 0;
     257        *s1 = 0;
     258        *s2 = 0;
     259        for (i = 0; i < values->n; i++) { // Calculate new means and weights
     260            *m1 += values->data.F32[i] * P1->data.F32[i];
     261            *m2 += values->data.F32[i] * P2->data.F32[i];
     262
     263            w1 += P1->data.F32[i];
     264            w2 += P2->data.F32[i];
     265
     266            if (xyrdebug == 1) {
     267                fprintf(stderr,"KMM EM-M loop: %d %d (%f %f %f %e) (%f %f %f %e)\n",
     268                        *iter,i,*m1,values->data.F32[i],w1,P1->data.F32[i],*m2,values->data.F32[i],w2,P2->data.F32[i]);
     269            }
     270
     271        }
     272        *m1 /= w1;
     273        *m2 /= w2;
     274        for (i = 0; i < values->n; i++) { // Calculate new sigmas
     275            *s1 += pow(values->data.F32[i] - *m1,2) * P1->data.F32[i];
     276            *s2 += pow(values->data.F32[i] - *m2,2) * P2->data.F32[i];
     277        }
     278        *s1 = sqrt(*s1 / w1);
     279        *s2 = sqrt(*s2 / w2);
     280
     281        *pi1 = w1 / values->n;
     282        *pi2 = w2 / values->n;
     283
     284        if (!isfinite(*pi1)) { // finite checks
     285            *pi1 = 0.0;
     286        }
     287        if (!isfinite(*pi2)) { // finite checks
     288            *pi2 = 0.0;
     289        }
     290        if (*s1 == 0) { // sigma may not be zero -- MEH -- nor <0 and need additive offset if m~0
     291            *s1 = fabsf(KMM_SMALL_NUMBER * *m1) + KMM_SMALL_NUMBER;
     292        }
     293        if (*s2 == 0) { // sigma may not be zero
     294            *s2 = fabsf(KMM_SMALL_NUMBER * *m2) + KMM_SMALL_NUMBER;
     295        }
     296
     297        if (xyrdebug == 1) {
     298            fprintf(stderr,"KMM EM end: %d %f %f %f %f (%f %e %e %f) (%f %e %e %f)\n",
     299                    *iter,logL_unimodal,logL_bimodal,oldL,dL,
     300                    *m1,*s1,*pi1,w1,
     301                    *m2,*s2,*pi2,w2);
     302        }
     303
     304    } // End EM phase
     305
     306    // Calculate Punimodal
     307    double lambda = -2.0 * (logL_unimodal - logL_bimodal);
     308    int    df     = 2 + 2 * 1; // I can't find my reference on this.
     309    if (lambda > 0) {
     310        *Punimodal = gsl_cdf_chisq_Q(lambda,df);
     311    }
     312    else { // If lambda <= 0, then logL_unimodal > logL_bimodal, so Punimodal must be by definition 1.0
     313        *Punimodal = 1.0;
     314    }
     315
    218316    if (xyrdebug == 1) {
    219         fprintf(stderr,"KMM EM iter: %d %f %f %f %f (%f %f %e) (%f %f %e)\n",
    220                 *iter,logL_unimodal,logL_bimodal,oldL,dL,
    221                 *m1,*s1,*pi1,
    222                 *m2,*s2,*pi2);
     317        fprintf(stderr,"KMM calc Puni: %d %f %d %f\n",
     318                xyrdebug,lambda,df,*Punimodal);
    223319    }
    224320
    225     // Expectation/P-stage
    226     for (i = 0; i < values->n; i++) { // Calculate probabilities for each mode
    227       g1 = gaussian(values->data.F32[i],*m1,*s1);
    228       g2 = gaussian(values->data.F32[i],*m2,*s2);
    229       norm = (*pi1 * g1 + *pi2 * g2);
    230       //MEH -- must protect denom from norm=0
    231       if (norm > 0) {
    232           P1->data.F32[i] = (*pi1 * g1) / norm;
    233           P2->data.F32[i] = (*pi2 * g2) / norm;
    234       } else {
    235           P1->data.F32[i] = 0.0;
    236           P2->data.F32[i] = 0.0;
    237       } 
    238  
    239       if (xyrdebug == 1) {
    240           fprintf(stderr,"KMM EM-P loop: %d %d %le %le %le\n",
    241                          *iter,i,norm,g1,g2);
    242       }
    243 
    244     }
    245     // Maximization/M-stage
    246     logL_bimodal = 0;
    247     w1 = 0;
    248     w2 = 0;
    249     for (i = 0; i < values->n; i++) { // Calculate log likelihood
    250       if (!((*pi1 == 0)||(*pi2 == 0))) {
    251         logL_bimodal += log(*pi1 * gaussian(values->data.F32[i],*m1,*s1) +
    252                             *pi2 * gaussian(values->data.F32[i],*m2,*s2));
    253       }
    254     }
    255     *m1 = 0;
    256     *m2 = 0;
    257     *s1 = 0;
    258     *s2 = 0;
    259     for (i = 0; i < values->n; i++) { // Calculate new means and weights
    260       *m1 += values->data.F32[i] * P1->data.F32[i];
    261       *m2 += values->data.F32[i] * P2->data.F32[i];
    262 
    263       w1 += P1->data.F32[i];
    264       w2 += P2->data.F32[i];
    265 
    266       if (xyrdebug == 1) {
    267           fprintf(stderr,"KMM EM-M loop: %d %d (%f %f %f %e) (%f %f %f %e)\n",
    268                          *iter,i,*m1,values->data.F32[i],w1,P1->data.F32[i],*m2,values->data.F32[i],w2,P2->data.F32[i]);
    269       }
    270 
    271     }
    272     *m1 /= w1;
    273     *m2 /= w2;
    274     for (i = 0; i < values->n; i++) { // Calculate new sigmas
    275       *s1 += pow(values->data.F32[i] - *m1,2) * P1->data.F32[i];
    276       *s2 += pow(values->data.F32[i] - *m2,2) * P2->data.F32[i];
    277     }
    278     *s1 = sqrt(*s1 / w1);
    279     *s2 = sqrt(*s2 / w2);
    280 
    281     *pi1 = w1 / values->n;
    282     *pi2 = w2 / values->n;
    283 
    284     if (!isfinite(*pi1)) { // finite checks
    285       *pi1 = 0.0;
    286     }
    287     if (!isfinite(*pi2)) { // finite checks
    288       *pi2 = 0.0;
    289     }
    290     if (*s1 == 0) { // sigma may not be zero -- MEH -- nor <0 and need additive offset if m~0
    291       *s1 = fabsf(KMM_SMALL_NUMBER * *m1) + KMM_SMALL_NUMBER;
    292     }
    293     if (*s2 == 0) { // sigma may not be zero
    294       *s2 = fabsf(KMM_SMALL_NUMBER * *m2) + KMM_SMALL_NUMBER;
    295     }
    296 
    297     if (xyrdebug == 1) {
    298         fprintf(stderr,"KMM EM end: %d %f %f %f %f (%f %e %e %f) (%f %e %e %f)\n",
    299                 *iter,logL_unimodal,logL_bimodal,oldL,dL,
    300                 *m1,*s1,*pi1,w1,
    301                 *m2,*s2,*pi2,w2);
    302     }
    303 
    304   } // End EM phase
    305 
    306   // Calculate Punimodal
    307   double lambda = -2.0 * (logL_unimodal - logL_bimodal);
    308   int    df     = 2 + 2 * 1; // I can't find my reference on this.
    309   if (lambda > 0) {
    310     *Punimodal = gsl_cdf_chisq_Q(lambda,df);
    311   }
    312   else { // If lambda <= 0, then logL_unimodal > logL_bimodal, so Punimodal must be by definition 1.0
    313     *Punimodal = 1.0;
    314   }
    315 
    316   if (xyrdebug == 1) {
    317       fprintf(stderr,"KMM calc Puni: %d %f %d %f\n",
    318               xyrdebug,lambda,df,*Punimodal);
    319   }
    320 
    321   psFree(P1);
    322   psFree(P2);
     321    psFree(P1);
     322    psFree(P2);
    323323}
    324324
    325325static void KMMFindPopular(const psVector *values, float *Punimodal, float *mean, float *sigma, float *pi, int xyrdebug) {
    326   float KMM_MINIMUM_PVALUE = 0.05; // Should be an option.
    327   float mU,sU;
    328   float pi1,m1,s1,pi2,m2,s2;
    329   int iter;
    330 
    331   assert(values);
    332   assert(values->type.type == PS_TYPE_F32);
     326    float KMM_MINIMUM_PVALUE = 0.05; // Should be an option.
     327    float mU,sU;
     328    float pi1,m1,s1,pi2,m2,s2;
     329    int iter;
     330
     331    assert(values);
     332    assert(values->type.type == PS_TYPE_F32);
    333333 
    334   KMMcalculate(values,Punimodal,&iter,
    335               &mU,&sU,
    336               &pi1,&m1,&s1,
    337               &pi2,&m2,&s2,xyrdebug);
     334    KMMcalculate(values,Punimodal,&iter,
     335                &mU,&sU,
     336                &pi1,&m1,&s1,
     337                &pi2,&m2,&s2,xyrdebug);
    338338/*   fprintf(stdout,"%g %g : %g %g %g : %g %g %g : %g %d\t", */
    339339/*        mU,sU, */
     
    347347/*   } */
    348348/*   fprintf(stdout,"\n"); */
    349   if (*Punimodal > KMM_MINIMUM_PVALUE) {
    350     // Is unimodal
    351     *mean = mU;
    352     *sigma = sU;
    353     *pi = 1.0;
    354   }
    355   else {
    356     // Is bimodal. Select most popular mode.
    357     if (pi1 >= pi2) {
    358       *mean = m1;
    359       *sigma = s1;
    360       *pi = pi1;
     349    if (*Punimodal > KMM_MINIMUM_PVALUE) {
     350        // Is unimodal
     351        *mean = mU;
     352        *sigma = sU;
     353        *pi = 1.0;
    361354    }
    362355    else {
    363       *mean = m2;
    364       *sigma = s2;
    365       *pi = pi2;
    366     }
    367   } 
     356        // Is bimodal. Select most popular mode.
     357        if (pi1 >= pi2) {
     358            *mean = m1;
     359            *sigma = s1;
     360            *pi = pi1;
     361        }
     362        else {
     363            *mean = m2;
     364            *sigma = s2;
     365            *pi = pi2;
     366        }
     367    } 
    368368}
    369369
     
    381381                                    const psVector *exps,      // Exposure times to combine
    382382                                    const psVector *weights // Weights to apply
    383                                     )
     383    )
    384384{
    385385    assert(mean);
     
    400400    //     variance_combination = variance_individual / N
    401401    // which makes sense --- the standard deviation of the combination is reduced by a factor of sqrt(N).
     402    // NOTE: in 2012.07.13, the variance calculation was changed without justification to the variance
     403    // appropriate to a weighted mean, while the pixel mean was kept as the average unweighted by pixel variance
    402404
    403405    float sumValueWeight = 0.0;         // Sum of the value multiplied by the weight
     
    411413        sumWeight += weights->data.F32[i];
    412414        if (variances) {
    413           //            sumVarianceWeight += variances->data.F32[i] * PS_SQR(weights->data.F32[i]);
    414           sumVarianceWeight += 1 / variances->data.F32[i];
     415            //            sumVarianceWeight += variances->data.F32[i] * PS_SQR(weights->data.F32[i]);
     416            sumVarianceWeight += 1 / variances->data.F32[i];
    415417        }
    416418        if (exps) {
     
    427429    *mean = sumValueWeight / sumWeight;
    428430    if (var) {
    429       //*var = sumVarianceWeight / PS_SQR(sumWeight);
    430       *var = 1 / sumVarianceWeight;
     431        //*var = sumVarianceWeight / PS_SQR(sumWeight);
     432        *var = 1 / sumVarianceWeight;
    431433    }
    432434    if (exp) {
     
    445447                                   const psVector *values, // Values to combine
    446448                                   psVector *sortBuffer // Buffer for sorting
    447                                    )
     449    )
    448450{
    449451    assert(values);
     
    496498                                        float frac, // Fraction to discard
    497499                                        psVector *sortBuffer // Buffer for sorting
    498                                         )
     500    )
    499501{
    500502    int numGood = values->n;            // Number of good values
     
    527529                                      int x, int y, // Pixel
    528530                                      int source // Source image index
    529                                       )
     531    )
    530532{
    531533    CHECKPIX(x, y, "Marking image %d, pixel %d,%d for inspection\n", source, x, y);
     
    545547                                     int x, int y, // Pixel
    546548                                     int source // Source image index
    547                                      )
     549    )
    548550{
    549551    CHECKPIX(x, y, "Marking pixel image %d, pixel %d,%d for rejection\n", source, x, y);
     
    561563// least popular mode.
    562564static void KMMRejectUnpopular(const psArray *inputs, int x, int y) {
    563   float KMM_MINIMUM_PVALUE = 0.05;
    564   float mU,sU;
    565   float Punimodal,pi1,m1,s1,pi2,m2,s2;
    566   int iter;
    567   int j,k;
    568 
    569   psVector *values = psVectorAlloc(inputs->n, PS_TYPE_F32);
    570   k = 0;
    571   for (j = 0; j < inputs->n; j++) {
    572     pmStackData *data = inputs->data[j]; // Stack data of interest
    573     if (!data) {
    574       k++;
    575       continue;
    576     }
    577     psImage *image = data->readout->image; // Image of interest
    578     int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
    579     values->data.F32[j - k] = image->data.F32[yIn][xIn];
    580   }
     565    float KMM_MINIMUM_PVALUE = 0.05;
     566    float mU,sU;
     567    float Punimodal,pi1,m1,s1,pi2,m2,s2;
     568    int iter;
     569    int j,k;
     570
     571    psVector *values = psVectorAlloc(inputs->n, PS_TYPE_F32);
     572    k = 0;
     573    for (j = 0; j < inputs->n; j++) {
     574        pmStackData *data = inputs->data[j]; // Stack data of interest
     575        if (!data) {
     576            k++;
     577            continue;
     578        }
     579        psImage *image = data->readout->image; // Image of interest
     580        int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
     581        values->data.F32[j - k] = image->data.F32[yIn][xIn];
     582    }
    581583 
    582   KMMcalculate(values,&Punimodal,&iter,
    583               &mU,&sU,
    584               &pi1,&m1,&s1,
    585               &pi2,&m2,&s2);
    586 
    587    CHECKPIX(x, y,
    588           "KMM Unpopular Test: %d,%d: Puni: %g in %d",x,y,Punimodal,iter); 
    589   if (Punimodal < KMM_MINIMUM_PVALUE) {
    590     int i;
    591     float g1,g2,norm;
    592     float P1,P2;
    593 
    594     for (i = 0; i < values->n; i++) { // Calculate probabilities for each mode
    595       g1 = gaussian(values->data.F32[i],m1,s1);
    596       g2 = gaussian(values->data.F32[i],m2,s2);
    597       norm = (pi1 * g1 + pi2 * g2);
    598       P1 = (pi1 * g1) / norm;
    599       P2 = (pi2 * g2) / norm;
    600 
    601       CHECKPIX(x, y, "KMM Unpopular Rejection: %d,%d: %f(%d): %d %f %f:(%f %f %f ) %f:(%f %f %f) rejection? %d %d\n",
    602                x, y,
    603                Punimodal,iter,
    604                i, values->data.F32[i],
    605                P1,m1,s1,pi1,
    606                P2,m2,s2,pi2,
    607                (pi1 > pi2)&&(P1 < P2),
    608                (pi1 < pi2)&&(P1 > P2));
    609       if ((pi1 > pi2)&&(P1 < P2)) { // mode 1 is more popular, but this element belongs to mode 2
    610         combineMarkReject(inputs,x,y,i);
    611       }
    612       if ((pi1 < pi2)&&(P1 > P2)) { // mode 2 is more popular, but this element belongs to mode 1
    613         combineMarkReject(inputs,x,y,i);
    614       }
    615     }
    616   }
    617   psFree(values);
    618   // else do nothing.
     584    KMMcalculate(values,&Punimodal,&iter,
     585                &mU,&sU,
     586                &pi1,&m1,&s1,
     587                &pi2,&m2,&s2);
     588
     589    CHECKPIX(x, y,
     590             "KMM Unpopular Test: %d,%d: Puni: %g in %d",x,y,Punimodal,iter); 
     591    if (Punimodal < KMM_MINIMUM_PVALUE) {
     592        int i;
     593        float g1,g2,norm;
     594        float P1,P2;
     595
     596        for (i = 0; i < values->n; i++) { // Calculate probabilities for each mode
     597            g1 = gaussian(values->data.F32[i],m1,s1);
     598            g2 = gaussian(values->data.F32[i],m2,s2);
     599            norm = (pi1 * g1 + pi2 * g2);
     600            P1 = (pi1 * g1) / norm;
     601            P2 = (pi2 * g2) / norm;
     602
     603            CHECKPIX(x, y, "KMM Unpopular Rejection: %d,%d: %f(%d): %d %f %f:(%f %f %f ) %f:(%f %f %f) rejection? %d %d\n",
     604                     x, y,
     605                     Punimodal,iter,
     606                     i, values->data.F32[i],
     607                     P1,m1,s1,pi1,
     608                     P2,m2,s2,pi2,
     609                     (pi1 > pi2)&&(P1 < P2),
     610                     (pi1 < pi2)&&(P1 > P2));
     611            if ((pi1 > pi2)&&(P1 < P2)) { // mode 1 is more popular, but this element belongs to mode 2
     612                combineMarkReject(inputs,x,y,i);
     613            }
     614            if ((pi1 < pi2)&&(P1 > P2)) { // mode 2 is more popular, but this element belongs to mode 1
     615                combineMarkReject(inputs,x,y,i);
     616            }
     617        }
     618    }
     619    psFree(values);
     620    // else do nothing.
    619621}
    620622
     
    622624// faintest mode as determined by the KMM test, and rejecting all inputs that belong to the brighest.
    623625static void KMMRejectBright(const psArray *inputs, int x, int y) {
    624   float KMM_MINIMUM_PVALUE = 0.05;
    625   float mU,sU;
    626   float Punimodal,pi1,m1,s1,pi2,m2,s2;
    627   int iter;
    628   int j;
    629 
    630   psVector *values = psVectorAlloc(inputs->n, PS_TYPE_F32);
    631   for (j = 0; j < inputs->n; j++) {
    632     pmStackData *data = inputs->data[j]; // Stack data of interest
    633     psImage *image = data->readout->image; // Image of interest
    634     int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
    635     values->data.F32[j] = image->data.F32[yIn][xIn];
    636   }
     626    float KMM_MINIMUM_PVALUE = 0.05;
     627    float mU,sU;
     628    float Punimodal,pi1,m1,s1,pi2,m2,s2;
     629    int iter;
     630    int j;
     631
     632    psVector *values = psVectorAlloc(inputs->n, PS_TYPE_F32);
     633    for (j = 0; j < inputs->n; j++) {
     634        pmStackData *data = inputs->data[j]; // Stack data of interest
     635        psImage *image = data->readout->image; // Image of interest
     636        int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
     637        values->data.F32[j] = image->data.F32[yIn][xIn];
     638    }
    637639 
    638   KMMcalculate(values,&Punimodal,&iter,
    639               &mU,&sU,
    640               &pi1,&m1,&s1,
    641               &pi2,&m2,&s2);
    642   if (Punimodal < KMM_MINIMUM_PVALUE) {
    643     int i;
    644     float g1,g2,norm;
    645     float P1,P2;
    646 
    647     for (i = 0; i < values->n; i++) { // Calculate probabilities for each mode
    648       g1 = gaussian(values->data.F32[i],m1,s1);
    649       g2 = gaussian(values->data.F32[i],m2,s2);
    650       norm = (pi1 * g1 + pi2 * g2);
    651       P1 = (pi1 * g1) / norm;
    652       P2 = (pi2 * g2) / norm;
    653 
    654       if ((m1 > m2)&&(P1 > P2)) { // m1 is larger, and this element belongs to mode 1
    655         combineMarkReject(inputs,x,y,i);
    656       }
    657       if ((m1 < m2)&&(P1 < P2)) { // m2 is larger, and this element belongs to mode 2
    658         combineMarkReject(inputs,x,y,i);
    659       }
    660     }
    661   }
    662   psFree(values);
    663   // else do nothing.
     640    KMMcalculate(values,&Punimodal,&iter,
     641                &mU,&sU,
     642                &pi1,&m1,&s1,
     643                &pi2,&m2,&s2);
     644    if (Punimodal < KMM_MINIMUM_PVALUE) {
     645        int i;
     646        float g1,g2,norm;
     647        float P1,P2;
     648
     649        for (i = 0; i < values->n; i++) { // Calculate probabilities for each mode
     650            g1 = gaussian(values->data.F32[i],m1,s1);
     651            g2 = gaussian(values->data.F32[i],m2,s2);
     652            norm = (pi1 * g1 + pi2 * g2);
     653            P1 = (pi1 * g1) / norm;
     654            P2 = (pi2 * g2) / norm;
     655
     656            if ((m1 > m2)&&(P1 > P2)) { // m1 is larger, and this element belongs to mode 1
     657                combineMarkReject(inputs,x,y,i);
     658            }
     659            if ((m1 < m2)&&(P1 < P2)) { // m2 is larger, and this element belongs to mode 2
     660                combineMarkReject(inputs,x,y,i);
     661            }
     662        }
     663    }
     664    psFree(values);
     665    // else do nothing.
    664666}
    665667#endif // End if(0) to prevent KMMReject{Unpopular|Bright} from being defined.
     
    682684                           psImageMaskType badMaskBits, // Value to mask as 'bad'
    683685                           psImageMaskType suspectMaskBits // Value to mask as 'suspect'
    684                            )
     686    )
    685687{
    686688    // Rudimentary error checking
     
    795797    // set the mask bits if nGoodBits[i] > f*numGood
    796798    {
    797         # define SUSPECT_FRACTION 0.65
     799# define SUSPECT_FRACTION 0.65
    798800        *goodMask = 0x0000;
    799801        psImageMaskType value = 0x0001;
     
    840842                          int nminpix,         // Minimum number of input per pixel
    841843                          float invTotalWeight    // Inverse of total weight for all inputs
    842                           )
     844    )
    843845{
    844846    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
     
    944946                        bool useVariance, // Use variance for rejection when combining?
    945947                        bool safe    // Combine safely?
    946                         )
     948    )
    947949{
    948950    if (iter <= 0) {
     
    968970    bool useKMM = false;
    969971    if (num >= KMM_MINIMUM_INPUTS) {
    970       useKMM = true;
     972        useKMM = true;
    971973    }
    972974   
     
    986988            }
    987989# endif
    988           KMMFindPopular(pixelData,&Punimodal,&KMMmean,&KMMsigma,&KMMpi,xyrdebug);
    989           CHECKPIX(x,y,"KMM Popularity Contest: (%d,%d) Puni: %g Mean: %f Sigma %f Pi: %f\n",
    990                    x,y,Punimodal,KMMmean,KMMsigma,KMMpi);
     990            KMMFindPopular(pixelData,&Punimodal,&KMMmean,&KMMsigma,&KMMpi,xyrdebug);
     991            CHECKPIX(x,y,"KMM Popularity Contest: (%d,%d) Puni: %g Mean: %f Sigma %f Pi: %f\n",
     992                     x,y,Punimodal,KMMmean,KMMsigma,KMMpi);
    991993        }
    992994        for (int i = 0; i < num; i++) {
     
    994996        }
    995997        for (int i = 0; i < num; i++) {
    996           // Systematic error contributes to the rejection level
    997           float sysVar;
    998           if (useKMM) { // If we can trust KMM results, set the systematic variance
    999             sysVar = PS_SQR(KMMsigma);
    1000           }
    1001           else { // Otherwise, use the 10% systematic variance we've done in the past.
    1002             sysVar = PS_SQR(sys * pixelData->data.F32[i]);
    1003           }
     998            // Systematic error contributes to the rejection level
     999            float sysVar;
     1000            if (useKMM) { // If we can trust KMM results, set the systematic variance
     1001                sysVar = PS_SQR(KMMsigma);
     1002            }
     1003            else { // Otherwise, use the 10% systematic variance we've done in the past.
     1004                sysVar = PS_SQR(sys * pixelData->data.F32[i]);
     1005            }
    10041006
    10051007            CHECKPIX(x, y, "Variance %d (%d), pixel %d,%d: %f %f %f\n",
     
    11511153          default: {
    11521154              if (useVariance) {
    1153                 float median;
    1154                 if ((useKMM)&&(Punimodal < 0.05)) {
    1155                   median = KMMmean;
    1156                 }
    1157                 else {
    1158                   median = combinationWeightedOlympic(pixelData, pixelWeights,
    1159                                                       olympic, buffer->sort); // Median for stack
    1160                 }
     1155                  float median;
     1156                  if ((useKMM)&&(Punimodal < 0.05)) {
     1157                      median = KMMmean;
     1158                  }
     1159                  else {
     1160                      median = combinationWeightedOlympic(pixelData, pixelWeights,
     1161                                                          olympic, buffer->sort); // Median for stack
     1162                  }
    11611163               
    11621164                  CHECKPIX(x, y, "Flag with variance pixel %d,%d: median = %f\n", x, y, median);
     
    11701172                      float diff2 = PS_SQR(diff); // Square difference
    11711173                      CHECKPIX(x,y, "Input %d, pixel %d,%d (%" PRIu16 "): %f %f (%f) %f %f :: %f %f %f %f\n",
    1172                               i, x, y, pixelSources->data.U16[j], pixelData->data.F32[j], pixelVariances->data.F32[j],
    1173                               1.0, pixelWeights->data.F32[j], 1.0,
    1174                               pixelLimits->data.F32[j], diff2, diff2 / pixelLimits->data.F32[j],worst);
     1174                               i, x, y, pixelSources->data.U16[j], pixelData->data.F32[j], pixelVariances->data.F32[j],
     1175                               1.0, pixelWeights->data.F32[j], 1.0,
     1176                               pixelLimits->data.F32[j], diff2, diff2 / pixelLimits->data.F32[j],worst);
    11751177
    11761178                      if (diff2 > pixelLimits->data.F32[j]) {
     
    14351437
    14361438bool pmStackSimpleMedianCombine(
    1437                                 pmReadout *combined,
    1438                                 psArray *input) {
    1439   int num = input->n;
    1440   //  int numCols, numRows;
    1441   int minInputCols, maxInputCols, minInputRows, maxInputRows; // Smallest and largest values to combine
    1442   int xSize, ySize;                   // Size of the output image
    1443 
    1444   psArray *stack = psArrayAlloc(num); // Stack of readouts 
    1445   for (int i = 0; i < num; i++) {
    1446     //    pmStackData *data = input->data[i]; // Stack data for this input
    1447     pmReadout *ro = input->data[i]; // data->readout;  // Readout of interest
    1448     if (!ro) {
    1449       continue;
    1450     }
    1451     stack->data[i] = psMemIncrRefCounter(ro);
    1452   }   
    1453 
    1454   if (!pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows, &xSize, &ySize,
    1455                               stack)) {
    1456     psError(psErrorCodeLast(), false, "Input stack is not valid.");
     1439    pmReadout *combined,
     1440    psArray *input) {
     1441    int num = input->n;
     1442    //  int numCols, numRows;
     1443    int minInputCols, maxInputCols, minInputRows, maxInputRows; // Smallest and largest values to combine
     1444    int xSize, ySize;                   // Size of the output image
     1445
     1446    psArray *stack = psArrayAlloc(num); // Stack of readouts 
     1447    for (int i = 0; i < num; i++) {
     1448        //    pmStackData *data = input->data[i]; // Stack data for this input
     1449        pmReadout *ro = input->data[i]; // data->readout;  // Readout of interest
     1450        if (!ro) {
     1451            continue;
     1452        }
     1453        stack->data[i] = psMemIncrRefCounter(ro);
     1454    }   
     1455
     1456    if (!pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows, &xSize, &ySize,
     1457                                stack)) {
     1458        psError(psErrorCodeLast(), false, "Input stack is not valid.");
     1459        psFree(stack);
     1460        return false;
     1461    }
     1462
     1463    psVector *pixelData = psVectorAlloc(input->n,PS_TYPE_F32);
     1464    psVector *pixelMask = psVectorAlloc(input->n,PS_TYPE_VECTOR_MASK);
     1465    psStats  *stats     = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
     1466
     1467    for (int y = minInputRows; y < maxInputRows; y++) {
     1468        for (int x = minInputCols; x < maxInputCols; x++) {
     1469            for (int i = 0; i < input->n; i++) {
     1470                pmReadout *ro  = stack->data[i];
     1471                psImage *image = ro->image;
     1472                pixelMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0;
     1473                pixelData->data.F32[i] = image->data.F32[y][x];
     1474                if (isfinite(image->data.F32[y][x])&&
     1475                    (fabs(image->data.F32[y][x]) < 1e5)) {
     1476                    pixelMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0;
     1477                }
     1478                else {
     1479                    pixelMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 1;
     1480                }
     1481#if (0)
     1482                if ((x == 59)&&(y > 40)&&(y < 50)) {
     1483                    fprintf(stderr,"%d %d %d %d %g\n",
     1484                            x,y,i,pixelMask->data.PS_TYPE_VECTOR_MASK_DATA[i],pixelData->data.F32[i]);
     1485                }
     1486#endif
     1487            }
     1488            if (!psVectorStats(stats,pixelData,NULL,pixelMask,1)) {
     1489                psError(PS_ERR_UNKNOWN, false, "Unable to calculate median");
     1490                psFree(stats);
     1491                psFree(pixelData);
     1492                psFree(pixelMask);
     1493                psFree(stack);
     1494                return(false);
     1495            }
     1496            combined->image->data.F32[y][x] = stats->robustMedian;
     1497            if (combined->mask) {
     1498                combined->mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = 0;
     1499            }
     1500#if (0)
     1501            if ((x == 59)&&(y > 40)&&(y < 50)) {
     1502                fprintf(stderr,"%d %d %d %d %g\n",
     1503                        x,y,-1,combined->mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x],
     1504                        combined->image->data.F32[y][x]);
     1505            }
     1506#endif
     1507        }
     1508    }
     1509 
     1510    psFree(stats);
     1511    psFree(pixelData);
     1512    psFree(pixelMask);
    14571513    psFree(stack);
    1458     return false;
    1459   }
    1460 
    1461   psVector *pixelData = psVectorAlloc(input->n,PS_TYPE_F32);
    1462   psVector *pixelMask = psVectorAlloc(input->n,PS_TYPE_VECTOR_MASK);
    1463   psStats  *stats     = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
    1464 
    1465   for (int y = minInputRows; y < maxInputRows; y++) {
    1466     for (int x = minInputCols; x < maxInputCols; x++) {
    1467       for (int i = 0; i < input->n; i++) {
    1468         pmReadout *ro  = stack->data[i];
    1469         psImage *image = ro->image;
    1470         pixelMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0;
    1471         pixelData->data.F32[i] = image->data.F32[y][x];
    1472         if (isfinite(image->data.F32[y][x])&&
    1473             (fabs(image->data.F32[y][x]) < 1e5)) {
    1474           pixelMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0;
    1475         }
    1476         else {
    1477           pixelMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 1;
    1478         }
    1479 #if (0)
    1480       if ((x == 59)&&(y > 40)&&(y < 50)) {
    1481           fprintf(stderr,"%d %d %d %d %g\n",
    1482                   x,y,i,pixelMask->data.PS_TYPE_VECTOR_MASK_DATA[i],pixelData->data.F32[i]);
    1483         }
    1484 #endif
    1485       }
    1486       if (!psVectorStats(stats,pixelData,NULL,pixelMask,1)) {
    1487         psError(PS_ERR_UNKNOWN, false, "Unable to calculate median");
    1488         psFree(stats);
    1489         psFree(pixelData);
    1490         psFree(pixelMask);
    1491         psFree(stack);
    1492         return(false);
    1493       }
    1494       combined->image->data.F32[y][x] = stats->robustMedian;
    1495       if (combined->mask) {
    1496         combined->mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = 0;
    1497       }
    1498 #if (0)
    1499       if ((x == 59)&&(y > 40)&&(y < 50)) {
    1500         fprintf(stderr,"%d %d %d %d %g\n",
    1501                 x,y,-1,combined->mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x],
    1502                 combined->image->data.F32[y][x]);
    1503       }
    1504 #endif
    1505     }
    1506   }
    1507  
    1508   psFree(stats);
    1509   psFree(pixelData);
    1510   psFree(pixelMask);
    1511   psFree(stack);
    1512   return (true);
    1513 }
    1514 
    1515 # define MIN_GOOD_PERCENTILE 2
     1514    return (true);
     1515}
     1516
     1517# define SUSPECT_FRACTION 0.65
     1518
     1519// Comparison and swap functions for sorting values directly
     1520#define SORT_VV_COMPARE(A,B) (pixelData->data.F32[A] < pixelData->data.F32[B])
     1521#define SORT_VV_SWAP(TYPE,A,B) {                                        \
     1522        if (A != B) {                                                   \
     1523            psF32 tempVal = pixelData->data.F32[A];                     \
     1524            pixelData->data.F32[A] = pixelData->data.F32[B];            \
     1525            pixelData->data.F32[B] = tempVal;                           \
     1526            psF32 tempVar = pixelVariances->data.F32[A];                \
     1527            pixelVariances->data.F32[A] = pixelVariances->data.F32[B];  \
     1528            pixelVariances->data.F32[B] = tempVar;                      \
     1529            if (expTime) {                                              \
     1530                psF32 tempExp = expTime->data.F32[A];                   \
     1531                expTime->data.F32[A] = expTime->data.F32[B];            \
     1532                expTime->data.F32[B] = tempExp;                         \
     1533            }                                                           \
     1534        }                                                               \
     1535    }
     1536
     1537// this macro uses the macros above which assume pixelData, pixelVariances, expTime
     1538#define SORT_VALUES(NVALUES) { PSSORT(NVALUES, SORT_VV_COMPARE, SORT_VV_SWAP, F32); }
     1539
     1540#define ESCAPE  {                                                       \
     1541  combined->image->data.F32[y][x] = NAN;                                \
     1542  combined->variance->data.F32[y][x] = NAN;                             \
     1543  combined->mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = blankMaskBits;   \
     1544  if (expmaps) {                                                        \
     1545    expmaps->image->data.F32[y][x] = 0.0;                               \
     1546    expmaps->variance->data.F32[y][x] = 0.0;                            \
     1547    expmaps->mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = 0;              \
     1548  } continue; }
     1549
    15161550bool pmStackCombineByPercentile(
    1517     pmReadout *combined,
    1518     psArray *stackData,
    1519     psF64 minRange,
    1520     psF64 maxRange,
     1551    pmReadout *combined,                // output stacked readout
     1552    pmReadout *expmaps,                 // output exposure map information
     1553    psArray *stackData,                 // input exposures
     1554    psF64 rejectFraction,               // outlier fraction of pixels to reject
    15211555    psImageMaskType badMaskBits,        // treat these bits as 'bad'
    15221556    psImageMaskType suspectMaskBits,    // treat these bits as 'suspect'
    15231557    psImageMaskType blankMaskBits       // use this mask value for pixels missing input data (distinguish between Ninput = 0 and Ngood = 0?)
    1524 ) {
     1558    ) {
    15251559    int minInputCols, maxInputCols, minInputRows, maxInputRows; // Smallest and largest values to combine
    15261560    int xSize, ySize;                   // Size of the output image
     
    15301564    for (int i = 0; i < stackData->n; i++) {
    15311565        pmStackData *data = stackData->data[i]; // Stack data for this input
    1532         pmReadout *ro = data->readout;  // Readout of interest
    1533         stackReadouts->data[i] = psMemIncrRefCounter(ro);
    1534     }
    1535 
     1566        stackReadouts->data[i] = NULL;
     1567        if (!data) continue;
     1568        pmReadout *ro = data->readout;  // Readout of interest
     1569        if (!ro) continue;
     1570        stackReadouts->data[i] = psMemIncrRefCounter(ro); // need to bump the counter since the free below will decrement
     1571    }
    15361572    if (!pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows, &xSize, &ySize, stackReadouts)) {
    15371573        psError(psErrorCodeLast(), false, "Input stack is not valid.");
     
    15411577    psFree(stackReadouts);
    15421578
    1543     psVector *pixelData = psVectorAlloc(stackData->n, PS_TYPE_F32);
    1544     psVector *pixelMask = psVectorAlloc(stackData->n, PS_TYPE_VECTOR_MASK);
     1579    // make sure the output readout matches the inputs, and set to blank by default
     1580    pmReadoutUpdateSize(combined, minInputCols, minInputRows, xSize, ySize, true, true, blankMaskBits);
     1581    if (expmaps) {
     1582        // if we are generating the expmaps, update to match the images, set blank mask areas to 0
     1583        pmReadoutUpdateSize(expmaps, minInputCols, minInputRows, xSize, ySize, expmaps->mask != NULL, expmaps->variance != NULL, 0);
     1584    }
     1585   
     1586    psVector *pixelData      = psVectorAlloc(stackData->n, PS_TYPE_F32);
     1587    psVector *pixelVariances = psVectorAlloc(stackData->n, PS_TYPE_F32);
     1588
     1589    // if we are asking for the exptime maps, generate a storage vector expTime
     1590    psVector *expTime        = expmaps && expmaps->image ? psVectorAlloc(stackData->n, PS_TYPE_F32) : NULL;
     1591
     1592    int nGoodBits[16]; // accumulate the good pixel bits here for fuzzy logic
     1593    psAssert (sizeof(psImageMaskType) == 2, "psImageMaskType is not the expected size");
    15451594
    15461595    for (int y = minInputRows; y < maxInputRows; y++) {
     
    15481597
    15491598            int nGood = 0;
     1599            memset (nGoodBits, 0, 16*sizeof(int));
    15501600            for (int i = 0; i < stackData->n; i++) {
    15511601
    15521602                pmStackData *data = stackData->data[i]; // Stack data for this input
     1603                if (!data) continue;
    15531604                pmReadout *ro = data->readout;  // Readout of interest
    1554                 psImage *image = ro->image;
    1555                 psImage *mask = ro->mask;
     1605                if (!ro) continue;
     1606
     1607                psAssert (ro->mask,     "must must exist, but does not");
     1608                psAssert (ro->variance, "variance must exist, but does not");
     1609
     1610                psImage *image    = ro->image;
     1611                psImage *variance = ro->variance;
     1612                psImage *mask     = ro->mask;
    15561613
    15571614                int xIn = x - data->readout->col0;
    15581615                int yIn = y - data->readout->row0; // Coordinates on input readout
    15591616
     1617                // skip obviously bad input data
    15601618                if (!isfinite(image->data.F32[yIn][xIn])) continue;
    1561                 if (fabs(image->data.F32[yIn][xIn]) > 1e5) continue;
    1562                 // XXX need to test the input mask as well here
    1563                 if (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & badMaskBits);
    1564 
    1565                 // XXX save the count of suspect bits
    1566 
    1567 # if (0)
     1619                if (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & badMaskBits) continue;
     1620                if (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & suspectMaskBits) continue;
     1621
    15681622                // count the number of times a given mask bit is set in the input pixels.
    15691623                // NOTE: since we have explicitly skipped the pixels with any bad bits, these are only
    15701624                // the suspect bits (nGoodBits is a bit of a misnomer: it is more like 'nSuspectBitsForGoodInputs'
    1571                 if (1) {
    1572                   psImageMaskType value = 0x0001;
    1573                   for (int nbit = 0; nbit < 16; nbit ++) {
     1625                // NOTE: skip the full bit-by-bit check if we know the mask byte is empty
     1626                psImageMaskType value = 0x0001;
     1627                for (int nbit = 0; mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] && (nbit < 16); nbit ++) {
    15741628                    if (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & value) {
    1575                       // XXX nGoodBits[nbit] ++;
     1629                        nGoodBits[nbit] ++;
    15761630                    }
    15771631                    value <<= 1;
    1578                   }
    15791632                }
     1633
     1634                // accumulate pixel data and variance values:
     1635                pixelData->data.F32[nGood] = image->data.F32[yIn][xIn];
     1636                pixelVariances->data.F32[nGood] = variance->data.F32[yIn][xIn];
     1637
     1638                // accumulate exposure times if required
     1639                if (expTime)  { expTime->data.F32[nGood] = data->exp; }
     1640                nGood ++;
     1641            }
     1642# define MIN_GOOD_PERCENTILE 3
     1643
     1644            if (nGood < MIN_GOOD_PERCENTILE) ESCAPE;
     1645           
     1646            pixelData->n = nGood;
     1647
     1648            // sort pixelData, pixelVariance, expTime (if it exists)
     1649            SORT_VALUES (pixelData->n);
     1650
     1651            // we now have a sorted vector of values.  We can make a very coarse outlier
     1652            // cut based on the median and interquartile range.  This will let us reject
     1653            // some extreme outliers that bias the signal otherwise.
     1654
     1655            int Nlo = 0;     
     1656            int Nhi = nGood;
     1657
     1658            if (nGood >= 5) {
     1659              int midPoint = nGood / 2;
     1660              float rawMedian = (nGood % 2) ? pixelData->data.F32[midPoint] : 0.5*(pixelData->data.F32[midPoint] + pixelData->data.F32[midPoint-1]);
     1661             
     1662              // XXX measure interquartile range
     1663              int P25 = 0.25*nGood;
     1664              int P75 = 0.75*nGood;
     1665              float rawSigma = 0.74*(pixelData->data.F32[P75] - pixelData->data.F32[P25]);
     1666              float minThresh = rawMedian - 5.0*rawSigma;
     1667              float maxThresh = rawMedian + 5.0*rawSigma;
     1668
     1669              // find the entries which are in the range
     1670              // these should be safe since minThresh and maxThresh are guaranteed to contain the median
     1671              while (pixelData->data.F32[Nlo    ] < minThresh) { Nlo ++; }
     1672              while (pixelData->data.F32[Nhi - 1] > maxThresh) { Nhi --; }
     1673            }
     1674
     1675            // if we did not clip above (either nGood < 5 or no rejection), Nlo will be 0, Nhi will be nGood
     1676            // In either case, Nlo is the offset of the first unclipped point.
     1677            int nGoodClip = Nhi - Nlo;
     1678
     1679            int isTest = false;
     1680            isTest = isTest || ((x == 4743) && (y == 2903));
     1681            isTest = isTest || ((x == 4953) && (y == 2919));
     1682            isTest = isTest || ((x == 4751) && (y == 2725));
     1683            isTest = false;
     1684
     1685            if (isTest) {
     1686              char testname[256];
     1687              snprintf (testname, 256, "testpix.%04d.%04d.txt", (int) x, (int) y);
     1688              FILE *f = fopen (testname, "w");
     1689              int fd = fileno (f);
     1690
     1691              fprintf (f, "# nGood: %d, Nlo: %d, Nhi: %d, nGoodClip: %d\n", nGood, Nlo, Nhi, nGoodClip);
     1692              p_psVectorPrint (fd, pixelData, "pixelData");
     1693              fclose (f);
     1694            }
     1695            // rather than define a min and max value,
     1696            // what we really want is a symmetric selection about the middle,
     1697            // or the output is biased.  If N % 2 = 1, then
     1698           
     1699            // we are going to exclude rejectFraction*nGood measurements.  But the
     1700            // rejection needs to be symmetric
     1701
     1702            // 'AT_LEAST' means we reject at least 'rejectFraction' of values, but it could
     1703            // be a higher percentage for smaller numbers of inputs.
     1704# define AT_LEAST 1
     1705# define MIN_GOOD_PERCENTILE 3
     1706# if (AT_LEAST)
     1707            int Ns = MIN(MAX(1, 0.5*rejectFraction * nGoodClip), nGoodClip) + Nlo;
     1708            int Ne = nGoodClip - Ns + Nlo;
     1709            int Npt = Ne - Ns;
     1710# else
     1711            int Ns = MIN(MAX(0, 0.5*rejectFraction * nGoodClip), nGoodClip) + Nlo;
     1712            int Ne = nGoodClip - Ns + Nlo;
     1713            int Npt = Ne - Ns;
    15801714# endif
    1581 
    1582                 pixelData->data.F32[nGood] = image->data.F32[yIn][xIn];
    1583                 nGood ++;
    1584             }
    1585             pixelData->n = nGood;
    1586             psVectorSortInPlace (pixelData);
    1587 
    1588             if (nGood < MIN_GOOD_PERCENTILE) {
    1589                 combined->image->data.F32[y][x] = NAN;
    1590                 combined->variance->data.F32[y][x] = NAN;
    1591                 combined->mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = 1;
    1592                 continue;
    1593             }
    1594 
    1595 # if (0)
    1596 # define SUSPECT_FRACTION 0.65
    1597             // set the mask bits if nGoodBits[i] > f*numGood
    1598             if (0) {
    1599               psImageMaskType value = 0x0001;
    1600               for (int nbit = 0; nbit < 16; nbit ++) {
    1601                 if (nGoodBits[nbit] > SUSPECT_FRACTION*numGood) {
    1602                   *goodMask |= value;
     1715            if ((Npt < 1) || (nGood < MIN_GOOD_PERCENTILE)) ESCAPE;
     1716
     1717            // Set the given (suspect) mask bit if nGoodBits[i] > f*nGood in other words
     1718            // if more than 65% of the good inputs had one of these bits set, then we
     1719            // should set that bit in the output mask.  Note that this analysis counts the
     1720            // mask bits of pixels rejected by the clipping above.
     1721            psImageMaskType value = 0x0001;
     1722            psImageMaskType outputMask = 0x0000;
     1723            for (int nbit = 0; nbit < 16; nbit ++) {
     1724                if (nGoodBits[nbit] > SUSPECT_FRACTION*nGood) {
     1725                    outputMask |= value;
    16031726                }
    16041727                value <<= 1;
    1605               }
    1606             }
    1607 # endif
    1608 
    1609             int Ns = MIN(MAX(0, minRange * nGood),nGood); // e.g., 0.1 * 50 = 5
    1610             int Ne = MIN(MAX(0, maxRange * nGood),nGood); // e.g., 0.9 * 50 = 45
    1611             int Npt = Ne - Ns;
     1728            }
    16121729
    16131730            float sum = 0.0;
     1731            float varSum = 0.0;
    16141732            for (int n = Ns; n < Ne; n++) {
    16151733                sum += pixelData->data.F32[n];
     1734                varSum += pixelVariances->data.F32[n];
    16161735            }
    16171736            float mean = sum / (float) Npt;
    1618 
    1619             float varSum = 0.0;
    1620             for (int n = Ns; n < Ne; n++) {
    1621                 varSum += SQ(pixelData->data.F32[n] - mean);
    1622             }
     1737            float varValue = varSum / (float) (nGoodClip*nGoodClip);
     1738
     1739            // alternative: calculate the stdev of the pixel values
     1740            // float varSum = 0.0;
     1741            // for (int n = Ns; n < Ne; n++) {
     1742            //  varSum += SQ(pixelData->data.F32[n] - mean);
     1743            // }
    16231744            // variance on the mean (stdev / sqrt(N))^2
    1624             float varValue = varSum / (Npt - 1) / Npt;
    1625 
    1626             // XXX this is probably not the correct variance to save
    1627             // the predicted variance should be the 1 / sum (1/var)
     1745
     1746            // the reported variance values can be extremely high / wrong.
     1747            // if we have enough measurements, let's just use the interquartile range
     1748            // of the data to estimate the per-pixel variance.  NOTE: this is not valid
     1749            // if the inputs have been significantly smoothed.  In that case we need
     1750            // to include the covariance explicitly.  But this algorithm should be used
     1751            // without convolution.
     1752            // XXX How do we choose the cutoff here?
     1753            if (nGoodClip >= 9) {
     1754              // Measure interquartile range
     1755              int P25 = 0.25*nGoodClip + Nlo;
     1756              int P75 = 0.75*nGoodClip + Nlo;
     1757              float rawSigma = 0.74*(pixelData->data.F32[P75] - pixelData->data.F32[P25]);
     1758              varValue = PS_MIN(9e7, PS_SQR(rawSigma) / (float) nGoodClip); // sigma_mean = sigma_meas / sqrt(Nmeas) -> var_mean = var_meas / Nmeas
     1759              // XXX the upper limit of 9e7 is set to match the output
     1760              // format (STK_UNIONS) which can only represent values up to that limit.
     1761              // perhaps it would be better to saturate the output image in psFits
     1762              // rather than here. 
     1763            }
     1764
     1765            // Note: since we are calculating the average of a subset of a sorted
     1766            // list of values, the denominator should not be the number of measurements
     1767            // in the calculation above (Npt): in the extreme case of a median, we would
     1768            // have a single value (Npt = 1), but the variance of a median is only ~1.4 x
     1769            // the variance of the average / sqrt(N).  We should use nGood (the total number
     1770            // of values in the sorted list), but the variance should be scaled by a factor
     1771            // which depends on the fraction of values included. 
     1772
     1773            // this coefficient varies between 1.4 (for pure median) and 1.05 for 68% range.
    16281774
    16291775            combined->image->data.F32[y][x] = mean;
    16301776            combined->variance->data.F32[y][x] = varValue;
    1631             combined->mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = 0;
    1632         }
    1633     }
    1634  
     1777            combined->mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = outputMask;
     1778
     1779            // The exposure time of interest should be the total number of values, after
     1780            // rejection of known bad measurements, not the sorted and clipped number.
     1781            // Note that if we were to take the median, the relevant exposure time would
     1782            // still be the total of all inputs, not the single exposure for which the
     1783            // median was generated.
     1784
     1785            if (expTime) {
     1786                float sum = 0.0;
     1787                for (int n = Nlo; n < Nhi; n++) {
     1788                    sum += expTime->data.F32[n];
     1789                }
     1790                expmaps->image->data.F32[y][x] = sum;
     1791            }
     1792            if (expmaps) { expmaps->mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = nGoodClip; }
     1793        }
     1794    }
    16351795    psFree(pixelData);
    1636     psFree(pixelMask);
     1796    psFree(pixelVariances);
     1797    psFree(expTime);
    16371798
    16381799    return (true);
  • trunk/psModules/src/imcombine/pmStack.h

    r41892 r42091  
    4949bool pmStackCombineByPercentile(
    5050    pmReadout *combined,
     51    pmReadout *expmaps,
    5152    psArray *input,
    52     psF64 minRange,
    53     psF64 maxRange,
     53    psF64 rejectFraction,
    5454    psImageMaskType badMaskBits,        // treat these bits as 'bad'
    5555    psImageMaskType suspectMaskBits,    // treat these bits as 'suspect'
Note: See TracChangeset for help on using the changeset viewer.