Changeset 42091
- Timestamp:
- Feb 28, 2022, 2:47:57 PM (4 years ago)
- Location:
- trunk/psModules/src/imcombine
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/psModules/src/imcombine/pmStack.c
r41892 r42091 113 113 // KMM functions to do bimodality rejection of pixels 114 114 double 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))); 116 116 } 117 117 … … 122 122 float *pi2, float *m2, float *s2, 123 123 int xyrdebug) { 124 assert(values);125 assert(values->type.type == PS_TYPE_F32);124 assert(values); 125 assert(values->type.type == PS_TYPE_F32); 126 126 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; 133 133 /* int debug = 0; */ 134 134 135 // Calculate unimodal properties136 *mU = 0;137 *sU = 0;138 logL_unimodal = 0;139 for (i = 0; i < values->n; i++) { // Calculate mean140 *mU += values->data.F32[i];141 }142 *mU /= values->n;143 for (i = 0; i < values->n; i++) { // Calculate sigma144 *sU += pow(values->data.F32[i] - *mU,2);145 146 // Attempt to guess better starting values147 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 } 151 151 152 }153 *sU = sqrt(*sU / values->n);154 for (i = 0; i < values->n; i++) { // Calculate log likelihood155 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 loop165 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 mean178 // 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 equidistant180 // 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). 181 181 182 discrepant_value = values->data.F32[discrepant_index];182 discrepant_value = values->data.F32[discrepant_index]; 183 183 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 } 195 195 196 *pi1 = 0.5;197 *pi2 = 0.5;198 199 //MEH -- need to be double to help avoid 0 in norm200 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; 211 211 /* if (debug == 1) { */ 212 212 /* fprintf(stderr,"KMM: %d %f %f %f %f (%f %f %f) (%f %f %f)\n", */ … … 216 216 /* } */ 217 217 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 218 316 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); 223 319 } 224 320 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); 323 323 } 324 324 325 325 static 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); 333 333 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); 338 338 /* fprintf(stdout,"%g %g : %g %g %g : %g %g %g : %g %d\t", */ 339 339 /* mU,sU, */ … … 347 347 /* } */ 348 348 /* 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; 361 354 } 362 355 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 } 368 368 } 369 369 … … 381 381 const psVector *exps, // Exposure times to combine 382 382 const psVector *weights // Weights to apply 383 )383 ) 384 384 { 385 385 assert(mean); … … 400 400 // variance_combination = variance_individual / N 401 401 // 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 402 404 403 405 float sumValueWeight = 0.0; // Sum of the value multiplied by the weight … … 411 413 sumWeight += weights->data.F32[i]; 412 414 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]; 415 417 } 416 418 if (exps) { … … 427 429 *mean = sumValueWeight / sumWeight; 428 430 if (var) { 429 //*var = sumVarianceWeight / PS_SQR(sumWeight);430 *var = 1 / sumVarianceWeight;431 //*var = sumVarianceWeight / PS_SQR(sumWeight); 432 *var = 1 / sumVarianceWeight; 431 433 } 432 434 if (exp) { … … 445 447 const psVector *values, // Values to combine 446 448 psVector *sortBuffer // Buffer for sorting 447 )449 ) 448 450 { 449 451 assert(values); … … 496 498 float frac, // Fraction to discard 497 499 psVector *sortBuffer // Buffer for sorting 498 )500 ) 499 501 { 500 502 int numGood = values->n; // Number of good values … … 527 529 int x, int y, // Pixel 528 530 int source // Source image index 529 )531 ) 530 532 { 531 533 CHECKPIX(x, y, "Marking image %d, pixel %d,%d for inspection\n", source, x, y); … … 545 547 int x, int y, // Pixel 546 548 int source // Source image index 547 )549 ) 548 550 { 549 551 CHECKPIX(x, y, "Marking pixel image %d, pixel %d,%d for rejection\n", source, x, y); … … 561 563 // least popular mode. 562 564 static 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 interest573 if (!data) {574 k++;575 continue;576 }577 psImage *image = data->readout->image; // Image of interest578 int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout579 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 } 581 583 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 mode595 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 2610 combineMarkReject(inputs,x,y,i);611 }612 if ((pi1 < pi2)&&(P1 > P2)) { // mode 2 is more popular, but this element belongs to mode 1613 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. 619 621 } 620 622 … … 622 624 // faintest mode as determined by the KMM test, and rejecting all inputs that belong to the brighest. 623 625 static 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 interest633 psImage *image = data->readout->image; // Image of interest634 int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout635 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 } 637 639 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 mode648 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 1655 combineMarkReject(inputs,x,y,i);656 }657 if ((m1 < m2)&&(P1 < P2)) { // m2 is larger, and this element belongs to mode 2658 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. 664 666 } 665 667 #endif // End if(0) to prevent KMMReject{Unpopular|Bright} from being defined. … … 682 684 psImageMaskType badMaskBits, // Value to mask as 'bad' 683 685 psImageMaskType suspectMaskBits // Value to mask as 'suspect' 684 )686 ) 685 687 { 686 688 // Rudimentary error checking … … 795 797 // set the mask bits if nGoodBits[i] > f*numGood 796 798 { 797 # define SUSPECT_FRACTION 0.65799 # define SUSPECT_FRACTION 0.65 798 800 *goodMask = 0x0000; 799 801 psImageMaskType value = 0x0001; … … 840 842 int nminpix, // Minimum number of input per pixel 841 843 float invTotalWeight // Inverse of total weight for all inputs 842 )844 ) 843 845 { 844 846 psVector *pixelData = buffer->pixels; // Values for the pixel of interest … … 944 946 bool useVariance, // Use variance for rejection when combining? 945 947 bool safe // Combine safely? 946 )948 ) 947 949 { 948 950 if (iter <= 0) { … … 968 970 bool useKMM = false; 969 971 if (num >= KMM_MINIMUM_INPUTS) { 970 useKMM = true;972 useKMM = true; 971 973 } 972 974 … … 986 988 } 987 989 # 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); 991 993 } 992 994 for (int i = 0; i < num; i++) { … … 994 996 } 995 997 for (int i = 0; i < num; i++) { 996 // Systematic error contributes to the rejection level997 float sysVar;998 if (useKMM) { // If we can trust KMM results, set the systematic variance999 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 } 1004 1006 1005 1007 CHECKPIX(x, y, "Variance %d (%d), pixel %d,%d: %f %f %f\n", … … 1151 1153 default: { 1152 1154 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 stack1160 }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 } 1161 1163 1162 1164 CHECKPIX(x, y, "Flag with variance pixel %d,%d: median = %f\n", x, y, median); … … 1170 1172 float diff2 = PS_SQR(diff); // Square difference 1171 1173 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); 1175 1177 1176 1178 if (diff2 > pixelLimits->data.F32[j]) { … … 1435 1437 1436 1438 bool 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); 1457 1513 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 1516 1550 bool pmStackCombineByPercentile( 1517 pmReadout *combined, 1518 p sArray *stackData,1519 ps F64 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 1521 1555 psImageMaskType badMaskBits, // treat these bits as 'bad' 1522 1556 psImageMaskType suspectMaskBits, // treat these bits as 'suspect' 1523 1557 psImageMaskType blankMaskBits // use this mask value for pixels missing input data (distinguish between Ninput = 0 and Ngood = 0?) 1524 ) {1558 ) { 1525 1559 int minInputCols, maxInputCols, minInputRows, maxInputRows; // Smallest and largest values to combine 1526 1560 int xSize, ySize; // Size of the output image … … 1530 1564 for (int i = 0; i < stackData->n; i++) { 1531 1565 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 } 1536 1572 if (!pmReadoutStackValidate(&minInputCols, &maxInputCols, &minInputRows, &maxInputRows, &xSize, &ySize, stackReadouts)) { 1537 1573 psError(psErrorCodeLast(), false, "Input stack is not valid."); … … 1541 1577 psFree(stackReadouts); 1542 1578 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"); 1545 1594 1546 1595 for (int y = minInputRows; y < maxInputRows; y++) { … … 1548 1597 1549 1598 int nGood = 0; 1599 memset (nGoodBits, 0, 16*sizeof(int)); 1550 1600 for (int i = 0; i < stackData->n; i++) { 1551 1601 1552 1602 pmStackData *data = stackData->data[i]; // Stack data for this input 1603 if (!data) continue; 1553 1604 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; 1556 1613 1557 1614 int xIn = x - data->readout->col0; 1558 1615 int yIn = y - data->readout->row0; // Coordinates on input readout 1559 1616 1617 // skip obviously bad input data 1560 1618 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 1568 1622 // count the number of times a given mask bit is set in the input pixels. 1569 1623 // NOTE: since we have explicitly skipped the pixels with any bad bits, these are only 1570 1624 // 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 ++) { 1574 1628 if (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & value) { 1575 // XXXnGoodBits[nbit] ++;1629 nGoodBits[nbit] ++; 1576 1630 } 1577 1631 value <<= 1; 1578 }1579 1632 } 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; 1580 1714 # 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; 1603 1726 } 1604 1727 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 } 1612 1729 1613 1730 float sum = 0.0; 1731 float varSum = 0.0; 1614 1732 for (int n = Ns; n < Ne; n++) { 1615 1733 sum += pixelData->data.F32[n]; 1734 varSum += pixelVariances->data.F32[n]; 1616 1735 } 1617 1736 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 // } 1623 1744 // 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. 1628 1774 1629 1775 combined->image->data.F32[y][x] = mean; 1630 1776 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 } 1635 1795 psFree(pixelData); 1636 psFree(pixelMask); 1796 psFree(pixelVariances); 1797 psFree(expTime); 1637 1798 1638 1799 return (true); -
trunk/psModules/src/imcombine/pmStack.h
r41892 r42091 49 49 bool pmStackCombineByPercentile( 50 50 pmReadout *combined, 51 pmReadout *expmaps, 51 52 psArray *input, 52 psF64 minRange, 53 psF64 maxRange, 53 psF64 rejectFraction, 54 54 psImageMaskType badMaskBits, // treat these bits as 'bad' 55 55 psImageMaskType suspectMaskBits, // treat these bits as 'suspect'
Note:
See TracChangeset
for help on using the changeset viewer.
