Changeset 42889
- Timestamp:
- Jun 5, 2025, 3:33:01 PM (14 months ago)
- Location:
- trunk/psModules/src/detrend
- Files:
-
- 3 edited
-
pmOverscan.c (modified) (1 diff)
-
pmOverscan.h (modified) (2 diffs)
-
pmPattern.c (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psModules/src/detrend/pmOverscan.c
r42379 r42889 17 17 #include "pmOverscan.h" 18 18 19 #define SMOOTH_NSIGMA 4.0 // Number of Gaussian sigma the smoothing kernel extends 19 #define SMOOTH_NSIGMA 4.0 // Number of Gaussian sigma the smoothing kernel extends 20 21 static void pmOverscanOptionsFree(pmOverscanOptions *options); 22 static void pmOverscanStatOptionsFree(pmOverscanStatOptions *options); 23 bool pmOverscanUpdateHeaderVector(pmReadout *input, pmHDU *hdu, pmOverscanOptions *overscanOpts, psVector *reduced); 24 25 bool pmOverscanSubtract(pmReadout *input, pmOverscanOptions *overscanOpts, bool doTwoDOverscan) 26 { 27 28 assert(input); 29 30 if (overscanOpts == NULL) 31 return true; // no overscan subtraction requested 32 33 pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest 34 psImage *image = input->image; 35 36 // check for 'soft bias' (simple, fixed offset to be subtracted) 37 if (overscanOpts->constant) 38 { 39 // write metadata header value 40 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan value", overscanOpts->value); 41 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", NAN); 42 43 // NOTE psBinaryOp frees arg2 if it is a scalar 44 (void)psBinaryOp(input->image, input->image, "-", psScalarAlloc((float)overscanOpts->value, PS_TYPE_F32)); 45 46 return true; 47 } 48 49 // we are performing a statitical analysis of the overscan region 50 51 // Check for an unallowable pmFit. 52 if (overscanOpts->primary) 53 { 54 if (overscanOpts->primary->fitType != PM_FIT_NONE && 55 overscanOpts->primary->fitType != PM_FIT_POLY_ORD && 56 overscanOpts->primary->fitType != PM_FIT_POLY_CHEBY && 57 overscanOpts->primary->fitType != PM_FIT_SPLINE) 58 { 59 psError(PS_ERR_UNKNOWN, true, "Invalid fit type (%d). Returning original image.\n", 60 overscanOpts->primary->fitType); 61 return false; 62 } 63 } 64 if (overscanOpts->secondary) 65 { 66 if (overscanOpts->secondary->fitType != PM_FIT_NONE && 67 overscanOpts->secondary->fitType != PM_FIT_POLY_ORD && 68 overscanOpts->secondary->fitType != PM_FIT_POLY_CHEBY && 69 overscanOpts->secondary->fitType != PM_FIT_SPLINE) 70 { 71 psError(PS_ERR_UNKNOWN, true, "Invalid fit type (2D) (%d). Returning original image.\n", 72 overscanOpts->secondary->fitType); 73 return false; 74 } 75 } 76 77 psList *overscans = input->bias; // List of the overscan images 78 79 // Reduce all overscan pixels to a single value 80 if (overscanOpts->single) 81 { 82 83 // extract overscan pixels to a single vector 84 psVector *pixels = psVectorAlloc(0, PS_TYPE_F32); 85 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 86 psImage *overscan = NULL; // Overscan image from iterator 87 while ((overscan = psListGetAndIncrement(iter))) 88 { 89 int index = pixels->n; // Index 90 pixels = psVectorRealloc(pixels, pixels->n + overscan->numRows * overscan->numCols); 91 pixels->n += overscan->numRows * overscan->numCols; 92 for (int i = 0; i < overscan->numRows; i++) 93 { 94 memcpy(&pixels->data.F32[index], overscan->data.F32[i], 95 overscan->numCols * sizeof(psF32)); 96 index += overscan->numCols; 97 } 98 } 99 psFree(iter); 100 101 // statistic to be calculated 102 psStatsOptions statistic = psStatsSingleOption(overscanOpts->primary->stat->options); // Statistic to use 103 if (!statistic) 104 { 105 psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Multiple or no statistics options set: %p\n", 106 overscanOpts->primary->stat); 107 return false; 108 } 109 psStats *stats = psStatsAlloc(statistic); // A new psStats, to avoid clobbering original 110 111 if (!psVectorStats(stats, pixels, NULL, NULL, 0)) 112 { 113 psError(PS_ERR_UNKNOWN, false, "failure to measure stats"); 114 return false; 115 } 116 psFree(pixels); 117 double reduced = psStatsGetValue(stats, statistic); // Result of statistics 118 119 psString comment = NULL; // Comment to add 120 psStringAppend(&comment, "Overscan value: %f", reduced); 121 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); 122 psFree(comment); 123 124 // write metadata header value 125 // XXX EAM : this could / should write the stdev of the overscan region 126 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan value", reduced); 127 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", NAN); 128 129 psScalar *reducedScalar = psScalarAlloc(reduced, PS_TYPE_F32); 130 psBinaryOp(image, image, "-", psMemIncrRefCounter(reducedScalar)); // NOTE: psBinaryOp frees arg2 if it a scalar, so we need to bump to re-use 131 132 // subtract the measured value from each overscan region as well 133 iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 134 overscan = NULL; // Overscan image from iterator 135 while ((overscan = psListGetAndIncrement(iter))) 136 { 137 psBinaryOp(overscan, overscan, "-", psMemIncrRefCounter(reducedScalar)); // NOTE: psBinaryOp frees arg2 if it a scalar, so we need to bump to re-use 138 } 139 psFree(iter); 140 psFree(reducedScalar); 141 142 // EAM 2022.03.29 : if the calculated overscan value is below the threshold, 143 // declare the readout dead and mask 144 145 if ((reduced < overscanOpts->minValid) || (reduced > overscanOpts->maxValid)) 146 { 147 fprintf(stderr, "bad overscan (1) %f, masking readout\n", reduced); 148 psImage *mask = input->mask; 149 for (int y = 0; y < mask->numRows; y++) 150 { 151 for (int x = 0; x < mask->numCols; x++) 152 { 153 mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= overscanOpts->maskVal; 154 } 155 } 156 } 157 158 psFree(stats); 159 return true; 160 } 161 162 bool mdok = false; 163 164 // We are performing a row-by-row overscan subtraction 165 int cellreaddir = psMetadataLookupS32(&mdok, input->parent->concepts, "CELL.READDIR"); // Read direction 166 167 if ((cellreaddir != 1) && (cellreaddir != 2)) 168 { 169 psError(PS_ERR_UNKNOWN, true, "CELL.READDIR must be 1 (rows) or 2 (cols)\n"); 170 return false; 171 } 172 173 float chi2 = NAN; // chi^2 from fit 174 175 // adjust operation depending on the read direction : need to re-org pixels for columns 176 if (!doTwoDOverscan && (cellreaddir == 1)) 177 { 178 // The read direction is rows 179 psArray *pixels = psArrayAlloc(image->numRows); // Array of vectors containing pixels 180 for (int i = 0; i < pixels->n; i++) 181 { 182 pixels->data[i] = psVectorAlloc(0, PS_TYPE_F32); 183 } 184 185 // Pull the pixels out into the vectors 186 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 187 psImage *overscan = NULL; // Overscan image from iterator 188 // XXX HG: investigate whether the y-overscan is being used or the x-overscan 189 while ((overscan = psListGetAndIncrement(iter))) 190 { 191 192 // the overscan and image might not be aligned. pixels->data represents 193 // the image row pixels. 194 int diff = overscan->row0 - image->row0; // Offset between the two regions 195 for (int i = PS_MAX(0, diff); i < PS_MIN(image->numRows, overscan->numRows + diff); i++) 196 { 197 int j = i - diff; 198 // i is row on image 199 // j is row on overscan 200 psVector *values = pixels->data[i]; 201 int index = values->n; // Index in the vector 202 values = psVectorRealloc(values, values->n + overscan->numCols); 203 values->n += overscan->numCols; 204 // XXX double-check the range of values->n here 205 memcpy(&values->data.F32[index], overscan->data.F32[j], 206 overscan->numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32)); 207 index += overscan->numCols; 208 pixels->data[i] = values; // Update the pointer in case it's moved 209 } 210 } 211 psFree(iter); 212 213 // Reduce the overscans 214 psVector *reduced = pmOverscanVector(&chi2, overscanOpts->primary, pixels, false); 215 psFree(pixels); 216 if (!reduced) 217 { 218 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate overscan vector.\n"); 219 return false; 220 } 221 222 if (!pmOverscanUpdateHeaderVector(input, hdu, overscanOpts, reduced)) 223 { 224 psError(PS_ERR_UNKNOWN, false, "failure to update header"); 225 return false; 226 } 227 228 // Subtract row by row 229 for (int i = 0; i < image->numRows; i++) 230 { 231 for (int j = 0; j < image->numCols; j++) 232 { 233 image->data.F32[i][j] -= reduced->data.F32[i]; 234 } 235 } 236 237 // subtract from the overscan regions 238 { 239 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 240 psImage *overscan = NULL; // Overscan image from iterator 241 while ((overscan = psListGetAndIncrement(iter))) 242 { 243 // the overscan and image might not be aligned. 244 int diff = overscan->row0 - image->row0; // Offset between the two regions 245 for (int i = PS_MAX(0, diff); i < PS_MIN(image->numRows, overscan->numRows + diff); i++) 246 { 247 int j = i - diff; 248 // i is row on image 249 // j is row on overscan 250 for (int k = 0; k < overscan->numCols; k++) 251 { 252 overscan->data.F32[j][k] -= reduced->data.F32[j]; 253 } 254 } 255 } 256 psFree(iter); 257 } 258 psFree(reduced); 259 } 260 261 if (!doTwoDOverscan && (cellreaddir == 2)) 262 { 263 // The read direction is columns 264 psArray *pixels = psArrayAlloc(image->numCols); // Array of vectors containing pixels 265 for (int i = 0; i < pixels->n; i++) 266 { 267 psVector *values = psVectorAlloc(0, PS_TYPE_F32); 268 pixels->data[i] = values; 269 } 270 271 // Pull the pixels out into the vectors 272 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 273 psImage *overscan = NULL; // Overscan image from iterator 274 while ((overscan = psListGetAndIncrement(iter))) 275 { 276 // the overscan and image might not be aligned. pixels->data represents 277 // the image row pixels. 278 int diff = overscan->col0 - image->col0; // Offset between the two regions 279 for (int i = PS_MAX(0, diff); i < PS_MIN(image->numCols, overscan->numCols + diff); i++) 280 { 281 int iFixed = i - diff; 282 // i is column on image 283 // iFixed is column on overscan 284 psVector *values = pixels->data[i]; 285 int index = values->n; // Index in the vector 286 values = psVectorRealloc(values, values->n + overscan->numRows); 287 for (int j = 0; j < overscan->numRows; j++) 288 { 289 values->data.F32[index++] = overscan->data.F32[j][iFixed]; 290 } 291 values->n += overscan->numRows; 292 pixels->data[i] = values; // Update the pointer in case it's moved 293 } 294 } 295 psFree(iter); 296 297 // Reduce the overscans 298 psVector *reduced = pmOverscanVector(&chi2, overscanOpts->primary, pixels, false); 299 psFree(pixels); 300 if (!reduced) 301 { 302 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate overscan vector.\n"); 303 return false; 304 } 305 306 if (!pmOverscanUpdateHeaderVector(input, hdu, overscanOpts, reduced)) 307 { 308 psError(PS_ERR_UNKNOWN, false, "failure to update header"); 309 return false; 310 } 311 312 // Subtract column by column 313 for (int j = 0; j < image->numRows; j++) 314 { 315 for (int i = 0; i < image->numCols; i++) 316 { 317 image->data.F32[j][i] -= reduced->data.F32[i]; 318 } 319 } 320 321 // subtract from the overscan regions 322 { 323 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 324 psImage *overscan = NULL; // Overscan image from iterator 325 while ((overscan = psListGetAndIncrement(iter))) 326 { 327 // the overscan and image might not be aligned. 328 int diff = overscan->col0 - image->col0; // Offset between the two regions 329 for (int i = PS_MAX(0, diff); i < PS_MIN(image->numCols, overscan->numCols + diff); i++) 330 { 331 int j = i - diff; 332 // i is col on image 333 // j is col on overscan 334 for (int k = 0; i < overscan->numRows; k++) 335 { 336 overscan->data.F32[k][j] -= reduced->data.F32[j]; 337 } 338 } 339 } 340 psFree(iter); 341 } 342 343 psFree(reduced); 344 } 345 346 // 2D bias subtraction with x-dir readout direction: the 347 // bias is constructed by combining a 1D pattern in the 348 // readout direction from the top overscan region and a second 349 // 1D pattern in the cross direction from the overscan 350 if (doTwoDOverscan && (cellreaddir == 1)) 351 { 352 psAssert(overscanOpts->secondary, "2D overscan subtraction requires OVERSCAN.2D parameters"); 353 354 //parse 2D overscan BISASEC region information 355 psRegion yRegion = overscanOpts->secondary->biassecslow; 356 psRegion xRegion = overscanOpts->secondary->biassecfast; 357 358 // Cut out relevant region image. Need to use the parent uncut image 359 psImage *parent = (psImage *) image->parent; 360 361 // the serial (fast readout) direction is columns (x-dir) 362 psImage *yscan = psImageSubset(parent, yRegion); // overscan region spanning all rows 363 psImage *xscan = psImageSubset(parent, xRegion); // overscan region spanning all columns 364 365 // Extract the y-dir overscan vector. The overscan and image might not be aligned: 366 // diff represents the offset between the rows in the image data and the overscan. 367 // pixels->data represents the image row pixels. For example, the image region may be 368 // inset in the y-direction but the overscan could cover the entire y-range 369 370 // The read direction is rows 371 psArray *yscanPixels = psArrayAlloc(yscan->numRows); // Array of vectors containing pixels 372 for (int i = 0; i < yscanPixels->n; i++) 373 { 374 yscanPixels->data[i] = psVectorAlloc(0, PS_TYPE_F32); 375 } 376 377 // XXX this code allows multiple yscans to be appended, but this does not 378 // match the concept of how they are assigned above: biassec[0] = yscan 379 // int yDiff = yscan->row0 - image->row0; // Offset between the two regions 380 for (int i = 0; i < yscanPixels->n; i++) 381 { 382 psVector *values = yscanPixels->data[i]; 383 int index = values->n; // Index in the vector 384 values = psVectorRealloc(values, values->n + yscan->numCols); 385 values->n += yscan->numCols; 386 // XXX double-check the range of values->n here 387 memcpy(&values->data.F32[index], yscan->data.F32[i], 388 yscan->numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32)); 389 yscanPixels->data[i] = values; // Update the pointer in case it's moved 390 } 391 392 // Extract the x-dir overscan vector. The overscan and image might not be aligned: 393 // diff represents the offset between the rows in the image data and the overscan. 394 // pixels->data represents the image row pixels. For example, the image region may be 395 // inset in the x-direction but the overscan could cover the entire y-range 396 397 // Extract the top region as a vector of the columns 398 psArray *xscanPixels = psArrayAlloc(xscan->numCols); // Array of vectors containing pixels 399 for (int i = 0; i < xscanPixels->n; i++) 400 { 401 xscanPixels->data[i] = psVectorAlloc(0, PS_TYPE_F32); 402 } 403 404 // int xDiff = xscan->col0 - image->col0; // Offset between the two regions 405 for (int ix = 0; ix < xscanPixels->n; ix++) 406 { 407 psVector *values = xscanPixels->data[ix]; 408 values = psVectorRealloc(values, xscan->numRows); 409 values->n = xscan->numRows; 410 for (int iy = 0; iy < xscan->numRows; iy++) 411 { 412 values->data.F32[iy] = xscan->data.F32[iy][ix]; 413 } 414 xscanPixels->data[ix] = values; // Update the pointer in case it's moved 415 } 416 417 // Reduce the overscans 418 // XXX need to save 2 different chi-square values 419 psVector *yReduced = pmOverscanVector(&chi2, overscanOpts->primary, yscanPixels, true); 420 psFree(yscanPixels); 421 if (!yReduced) 422 { 423 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate y-dir overscan vector.\n"); 424 return false; 425 } 426 if (!pmOverscanUpdateHeaderVector(input, hdu, overscanOpts, yReduced)) 427 { 428 psError(PS_ERR_UNKNOWN, false, "failure to update header"); 429 return false; 430 } 431 432 // Reduce the overscans 433 // XXX need to save 2 different chi-square values 434 psVector *xReduced = pmOverscanVector(&chi2, overscanOpts->secondary, xscanPixels, true); 435 psFree(xscanPixels); 436 if (!xReduced) 437 { 438 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate x-dir overscan vector.\n"); 439 return false; 440 } 441 442 // subtract the 2D bias from the image 443 if (yscan->col0 >= xscan->col0 && yscan->col0 + yscan->numCols <= xscan->col0 + xscan->numCols) 444 { 445 // define how to normalize the xReduced vector 446 // slice the part overlapping withe yoverscan from xReduced 447 long startIndex = yscan->col0 - xscan->col0; 448 long normSize = xReduced->n - startIndex; // Calculate the size of the new vector 449 // Allocate the new vector 450 psVector *normVector = psVectorAlloc(normSize, PS_TYPE_F32); 451 // Copy the data from xReduced starting from startIndex to the end 452 for (long i = startIndex; i < xReduced->n; i++) 453 { 454 normVector->data.F32[i - startIndex] = xReduced->data.F32[i]; 455 } 456 // Now normVector holds the sliced data; then, compute its robust mean 457 psStatsOptions statistic = PS_STAT_ROBUST_MEDIAN; 458 psStats *stats = psStatsAlloc(statistic); 459 if (!psVectorStats(stats, normVector, NULL, NULL, 0)) 460 { 461 psError(PS_ERR_UNKNOWN, false, "failure to measure robust median as the normalization of xReduced"); 462 psFree(stats); 463 psFree(normVector); 464 return NULL; 465 } 466 float xReducedNormalized = psStatsGetValue(stats, statistic); 467 psFree(stats); 468 psFree(normVector); 469 // XXX apply the 2D bias correction here 470 int yDiff = yscan->row0 - image->row0; // y offset between the science and the yoverscan region 471 int xDiff = xscan->col0 - image->col0; // x offset between the science and the xoverscan region 472 for (int i = 0; i < image->numRows; i++) 473 { 474 for (int j = 0; j < image->numCols; j++) 475 { 476 int iy = i + yDiff; 477 int jx = j + xDiff; 478 image->data.F32[i][j] -= yReduced->data.F32[iy] - xReducedNormalized + xReduced->data.F32[jx]; 479 } 480 } 481 } 482 else 483 { 484 psError(PS_ERR_UNKNOWN, true, "x dimension of yscan is not fully contained by xscan\n"); 485 return false; 486 } 487 488 // subtract the y-dir vector from the y-dir overscan regions (why?) 489 { 490 // the overscan and image might not be aligned. 491 // int diff = yscan->row0 - image->row0; // Offset between the two regions 492 for (int i = 0; i < yscan->numRows; i++) 493 { 494 for (int j = 0; j < yscan->numCols; j++) 495 { 496 yscan->data.F32[i][j] -= yReduced->data.F32[i]; 497 } 498 } 499 } 500 501 // subtract the x-dir vector from the x-dir overscan regions (why?) 502 { 503 // the overscan and image might not be aligned. 504 // int diff = xscan->col0 - image->col0; // Offset between the two regions 505 for (int i = 0; i < xscan->numCols; i++) 506 { 507 // int j = i - diff; 508 // i is column on image (aligned with xReduced) 509 // j is column on xscan 510 for (int j = 0; j < xscan->numRows; j++) 511 { 512 xscan->data.F32[j][i] -= xReduced->data.F32[i]; 513 } 514 } 515 } 516 psFree(xReduced); 517 psFree(yReduced); 518 } 519 // pmOverscanUpdateHeader (hdu, overscanOpts, chi2); 520 return true; 521 522 } // End of overscan subtraction 20 523 21 524 static void pmOverscanOptionsFree(pmOverscanOptions *options) 22 525 { 23 psFree(options->stat); 24 psFree(options->poly); 25 psFree(options->spline); 526 psFree(options->primary); 527 psFree(options->secondary); 26 528 } 27 529 28 pmOverscanOptions *pmOverscanOptionsAlloc(bool single, pmFit fitType, unsigned int order, psStats *stat, 29 int boxcar, float gauss) 530 static void pmOverscanStatOptionsFree(pmOverscanStatOptions *options) 30 531 { 31 pmOverscanOptions *opts = psAlloc(sizeof(pmOverscanOptions)); 32 psMemSetDeallocator(opts, (psFreeFunc)pmOverscanOptionsFree); 33 34 // Inputs 35 opts->single = single; 36 opts->constant = false; 37 opts->fitType = fitType; 38 opts->order = order; 39 opts->stat = psMemIncrRefCounter(stat); 40 41 opts->minValid = 0.0; // default value if not defined 42 opts->maxValid = (float) 0x10000; // default value if not defined 43 opts->maskVal = 0x0001; // default value if not defined 44 45 // Smoothing 46 opts->boxcar = boxcar; 47 opts->gauss = gauss; 48 49 // Outputs 50 opts->poly = NULL; 51 opts->spline = NULL; 52 53 return opts; 532 psFree(options->stat); 533 psFree(options->poly); 534 psFree(options->spline); 54 535 } 55 536 537 // Globally defined 538 pmOverscanStatOptions *pmOverscanStatOptionsAlloc(void) 539 { 540 pmOverscanStatOptions *opts = psAlloc(sizeof(pmOverscanStatOptions)); 541 psMemSetDeallocator(opts, (psFreeFunc)pmOverscanStatOptionsFree); 542 543 // Inputs 544 opts->fitType = PM_FIT_NONE; 545 opts->order = 0; 546 opts->stat = NULL; 547 548 // Smoothing 549 opts->boxcar = 0; 550 opts->gauss = 0.0; 551 552 // Outputs 553 opts->poly = NULL; 554 opts->spline = NULL; 555 556 return opts; 557 } 558 559 // Globally defined 560 pmOverscanOptions *pmOverscanOptionsAlloc(void) 561 { 562 pmOverscanOptions *opts = psAlloc(sizeof(pmOverscanOptions)); 563 psMemSetDeallocator(opts, (psFreeFunc)pmOverscanOptionsFree); 564 565 // Inputs 566 opts->single = false; 567 opts->constant = false; 568 opts->TwoD = false; 569 570 opts->value = 0.0; 571 572 opts->minValid = 0.0; // default value if not defined 573 opts->maxValid = (float)0x10000; // default value if not defined 574 opts->maskVal = 0x0001; // default value if not defined 575 576 // stat options 577 opts->primary = NULL; 578 opts->secondary = NULL; 579 580 return opts; 581 } 582 56 583 // Produce an overscan vector from an array of pixels 57 psVector *pmOverscanVector(float *chi2, // chi^2 from fit 58 pmOverscanOptions *overscanOpts, // Overscan options 59 const psArray *pixels, // Array of vectors containing the pixel values 60 psStats *myStats // Statistic to use in reducing the overscan 61 ) 584 psVector *pmOverscanVector(float *chi2, // chi^2 from fit 585 pmOverscanStatOptions *overscanOpts, // Overscan statistic options 586 const psArray *pixels, // Array of vectors containing the pixel values 587 bool robust) // Use robust statistics for boxcar smoothing 62 588 { 63 assert(overscanOpts); 64 assert(pixels); 65 assert(myStats); 66 67 psStatsOptions statistic = psStatsSingleOption(myStats->options); // Statistic to use 68 assert(statistic != 0); 69 70 // Reduce the overscans 71 psVector *reduced = psVectorAlloc(pixels->n, PS_TYPE_F32); // Overscan for each row 72 psVector *ordinate = psVectorAlloc(pixels->n, PS_TYPE_F32); // Ordinate 73 psVector *mask = psVectorAlloc(pixels->n, PS_TYPE_VECTOR_MASK); // Mask for fitting 74 75 for (int i = 0; i < pixels->n; i++) { 76 psVector *values = pixels->data[i]; // Vector with overscan values 77 if (values->n > 0) { 78 mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0; 79 ordinate->data.F32[i] = 2.0*(float)i/(float)pixels->n - 1.0; // Scale to [-1,1] 80 if (!psVectorStats(myStats, values, NULL, NULL, 0)) { 589 assert(overscanOpts); 590 assert(pixels); 591 592 // statisctic to be calculated 593 psStatsOptions statistic = psStatsSingleOption(overscanOpts->stat->options); // Statistic to use 594 if (!statistic) 595 { 596 psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Multiple or no statistics options set: %p\n", overscanOpts->stat); 597 return false; 598 } 599 psStats *stats = psStatsAlloc(statistic); // A new psStats, to avoid clobbering original 600 601 // Reduce the overscans 602 psVector *reduced = psVectorAlloc(pixels->n, PS_TYPE_F32); // Overscan for each row 603 psVector *ordinate = psVectorAlloc(pixels->n, PS_TYPE_F32); // Ordinate 604 psVector *mask = psVectorAlloc(pixels->n, PS_TYPE_VECTOR_MASK); // Mask for fitting 605 606 for (int i = 0; i < pixels->n; i++) 607 { 608 psVector *values = pixels->data[i]; // Vector with overscan values 609 if (values->n > 0) 610 { 611 mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0; 612 ordinate->data.F32[i] = 2.0 * (float)i / (float)pixels->n - 1.0; // Scale to [-1,1] 613 if (!psVectorStats(stats, values, NULL, NULL, 0)) 614 { 615 psError(PS_ERR_UNKNOWN, false, "failure to measure stats"); 616 goto escape; 617 } 618 reduced->data.F32[i] = psStatsGetValue(stats, statistic); 619 } 620 else 621 { 622 if (overscanOpts->fitType == PM_FIT_NONE) 623 { 624 psError(PS_ERR_UNKNOWN, true, "The overscan is not supplied for all points on the image, and no fit is requested.\n"); 625 goto escape; 626 } 627 else 628 { 629 // We'll fit this one out 630 mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 1; 631 } 632 } 633 } 634 // Smooth the reduced vector 635 if (overscanOpts->boxcar > 0) 636 { 637 psVector *smoothed = psVectorBoxcar(NULL, reduced, overscanOpts->boxcar, robust); // Smoothed vector 638 psFree(reduced); 639 reduced = smoothed; 640 } 641 if (isfinite(overscanOpts->gauss) && overscanOpts->gauss > 0) 642 { 643 if (overscanOpts->boxcar > 0) 644 { 645 psWarning("Gaussian smoothing the boxcar smoothed overscan --- you asked for it."); 646 } 647 psVector *smoothed = psVectorSmooth(NULL, reduced, overscanOpts->gauss, SMOOTH_NSIGMA); 648 psFree(reduced); 649 reduced = smoothed; 650 } 651 652 // Fit the overscan, if required 653 psVector *fitted = NULL; // Fitted overscan values 654 switch (overscanOpts->fitType) 655 { 656 case PM_FIT_NONE: 657 // No fitting --- that's easy. 658 fitted = psMemIncrRefCounter(reduced); 659 break; 660 case PM_FIT_POLY_ORD: 661 if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order || 662 overscanOpts->poly->type != PS_POLYNOMIAL_ORD)) 663 { 664 psFree(overscanOpts->poly); 665 overscanOpts->poly = NULL; 666 } 667 if (!overscanOpts->poly) 668 { 669 overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, overscanOpts->order); 670 } 671 psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate); 672 fitted = psPolynomial1DEvalVector(overscanOpts->poly, ordinate); 673 break; 674 case PM_FIT_POLY_CHEBY: 675 if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order || 676 overscanOpts->poly->type != PS_POLYNOMIAL_CHEB)) 677 { 678 psFree(overscanOpts->poly); 679 overscanOpts->poly = NULL; 680 } 681 if (!overscanOpts->poly) 682 { 683 overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, overscanOpts->order); 684 } 685 psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate); 686 fitted = psPolynomial1DEvalVector(overscanOpts->poly, ordinate); 687 break; 688 case PM_FIT_SPLINE: 689 690 // XXX I don't think psSpline1D is up to scratch yet --- it has no mask, and it assumes 691 // a knot for every input point. it needs an argument like 'number of knots' for the 692 // output spline. EAM: still true 2023.01.22 693 694 // overscanOpts->spline = psVectorFitSpline1D(reduced, ordinate); 695 // fitted = psSpline1DEvalVector(overscanOpts->spline, ordinate); 696 psError(PS_ERR_UNKNOWN, true, "Spline overscan fitting is broken\n"); 697 break; 698 default: 699 psError(PS_ERR_UNKNOWN, true, "Unknown value for the fitting type: %d\n", overscanOpts->fitType); 700 goto escape; 701 } 702 703 if (chi2) 704 { 705 *chi2 = 0.0; // chi^2 (sort of) 706 for (int i = 0; i < reduced->n; i++) 707 { 708 *chi2 += PS_SQR(fitted->data.F32[i] - reduced->data.F32[i]); 709 } 710 } 711 712 psFree(reduced); 713 psFree(ordinate); 714 psFree(mask); 715 psFree(stats); 716 return fitted; 717 718 escape: 719 psFree(reduced); 720 psFree(ordinate); 721 psFree(mask); 722 psFree(stats); 723 return NULL; 724 } 725 726 // XXX EAM 2024.04.13 : this function seems poorly conceived. it is replacing the stats from 727 // the reduced overscan vector with the 0-order element from the polynomial fit. Not clear is 728 // adding any useful information 729 bool pmOverscanUpdateHeader(pmHDU *hdu, pmOverscanStatOptions *overscanOpts, float chi2) 730 { 731 732 psString comment = NULL; // Comment to add 733 734 switch (overscanOpts->fitType) 735 { 736 case PM_FIT_POLY_ORD: 737 case PM_FIT_POLY_CHEBY: 738 { 739 psStringAppend(&comment, "Overscan fit (chi2: %.2f): ", chi2); 740 psPolynomial1D *poly = overscanOpts->poly; // The polynomial 741 for (int i = 0; i < poly->nX; i++) 742 { 743 psStringAppend(&comment, "%.1f ", poly->coeff[i]); 744 } 745 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); 746 psFree(comment); 747 comment = NULL; 748 749 // write metadata header value 750 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan value", poly->coeff[0]); 751 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", poly->coeffErr[0]); 752 break; 753 } 754 case PM_FIT_SPLINE: 755 { 756 /* 757 psSpline1D *spline = overscanOpts->spline; // The spline 758 for (int i = 0; i < spline->n; i++) { 759 psStringAppend(&comment, "Overscan fit (chi2: %.2f) %d:", chi2, i); 760 psPolynomial1D *poly = spline->spline[i]; // i-th polynomial 761 for (int j = 0; j < poly->nX; j++) { 762 psStringAppend(&comment, "%.1f ", poly->coeff[i]); 763 } 764 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, 765 comment, ""); 766 psFree(comment); 767 comment = NULL; 768 } 769 */ 770 // write metadata header value 771 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, 772 "Overscan value", NAN); 773 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, 774 "Overscan stdev", NAN); 775 break; 776 } 777 case PM_FIT_NONE: 778 break; 779 default: 780 psAbort("Should never get here!!!\n"); 781 } 782 return true; 783 } 784 785 // generate stats of overscan vector for header 786 // reduced: 1D vector with overscan stats 787 bool pmOverscanUpdateHeaderVector(pmReadout *input, pmHDU *hdu, pmOverscanOptions *overscanOpts, psVector *reduced) 788 { 789 790 psString comment = NULL; // Comment to add 791 psStats *vectorStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV); 792 if (!psVectorStats(vectorStats, reduced, NULL, NULL, 0)) 793 { 81 794 psError(PS_ERR_UNKNOWN, false, "failure to measure stats"); 82 795 return false; 83 } 84 reduced->data.F32[i] = psStatsGetValue(myStats, statistic); 85 } else if (overscanOpts->fitType == PM_FIT_NONE) { 86 psError(PS_ERR_UNKNOWN, true, "The overscan is not supplied for all points on the " 87 "image, and no fit is requested.\n"); 88 return NULL; 89 } else { 90 // We'll fit this one out 91 mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 1; 92 } 93 } 94 95 // Smooth the reduced vector 96 if (overscanOpts->boxcar > 0) { 97 psVector *smoothed = psVectorBoxcar(NULL, reduced, overscanOpts->boxcar); // Smoothed vector 98 psFree(reduced); 99 reduced = smoothed; 100 } 101 if (isfinite(overscanOpts->gauss) && overscanOpts->gauss > 0) { 102 if (overscanOpts->boxcar > 0) { 103 psWarning("Gaussian smoothing the boxcar smoothed overscan --- you asked for it."); 104 } 105 psVector *smoothed = psVectorSmooth(NULL, reduced, overscanOpts->gauss, SMOOTH_NSIGMA); 106 psFree(reduced); 107 reduced = smoothed; 108 } 109 110 // Fit the overscan, if required 111 psVector *fitted = NULL; // Fitted overscan values 112 switch (overscanOpts->fitType) { 113 case PM_FIT_NONE: 114 // No fitting --- that's easy. 115 fitted = psMemIncrRefCounter(reduced); 116 break; 117 case PM_FIT_POLY_ORD: 118 if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order || 119 overscanOpts->poly->type != PS_POLYNOMIAL_ORD)) { 120 psFree(overscanOpts->poly); 121 overscanOpts->poly = NULL; 122 } 123 if (! overscanOpts->poly) { 124 overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, overscanOpts->order); 125 } 126 psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate); 127 fitted = psPolynomial1DEvalVector(overscanOpts->poly, ordinate); 128 break; 129 case PM_FIT_POLY_CHEBY: 130 if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order || 131 overscanOpts->poly->type != PS_POLYNOMIAL_CHEB)) { 132 psFree(overscanOpts->poly); 133 overscanOpts->poly = NULL; 134 } 135 if (! overscanOpts->poly) { 136 overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, overscanOpts->order); 137 } 138 psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate); 139 fitted = psPolynomial1DEvalVector(overscanOpts->poly, ordinate); 140 break; 141 case PM_FIT_SPLINE: 142 143 // XXX I don't think psSpline1D is up to scratch yet --- it has no mask, and it assumes 144 // a knot for every input point. it needs an argument like 'number of knots' for the 145 // output spline. EAM: still true 2023.01.22 146 147 // overscanOpts->spline = psVectorFitSpline1D(reduced, ordinate); 148 // fitted = psSpline1DEvalVector(overscanOpts->spline, ordinate); 149 psError(PS_ERR_UNKNOWN, true, "Spline overscan fitting is broken\n"); 150 break; 151 default: 152 psError(PS_ERR_UNKNOWN, true, "Unknown value for the fitting type: %d\n", overscanOpts->fitType); 153 return NULL; 154 break; 155 } 156 157 if (chi2) { 158 *chi2 = 0.0; // chi^2 (sort of) 159 for (int i = 0; i < reduced->n; i++) { 160 *chi2 += PS_SQR(fitted->data.F32[i] - reduced->data.F32[i]); 161 } 162 } 163 164 psFree(reduced); 165 psFree(ordinate); 166 psFree(mask); 167 168 return fitted; 169 } 170 171 bool pmOverscanUpdateHeader (pmHDU *hdu, pmOverscanOptions *overscanOpts, float chi2) { 172 173 psString comment = NULL; // Comment to add 174 175 switch (overscanOpts->fitType) { 176 case PM_FIT_POLY_ORD: 177 case PM_FIT_POLY_CHEBY: { 178 psStringAppend(&comment, "Overscan fit (chi2: %.2f): ", chi2); 179 psPolynomial1D *poly = overscanOpts->poly; // The polynomial 180 for (int i = 0; i < poly->nX; i++) { 181 psStringAppend(&comment, "%.1f ", poly->coeff[i]); 182 } 183 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); 184 psFree(comment); 185 comment = NULL; 186 187 // write metadata header value 188 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, 189 "Overscan value", poly->coeff[0]); 190 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, 191 "Overscan stdev", poly->coeffErr[0]); 192 break; 193 } 194 case PM_FIT_SPLINE: { 195 /* 196 psSpline1D *spline = overscanOpts->spline; // The spline 197 for (int i = 0; i < spline->n; i++) { 198 psStringAppend(&comment, "Overscan fit (chi2: %.2f) %d:", chi2, i); 199 psPolynomial1D *poly = spline->spline[i]; // i-th polynomial 200 for (int j = 0; j < poly->nX; j++) { 201 psStringAppend(&comment, "%.1f ", poly->coeff[i]); 202 } 203 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, 204 comment, ""); 205 psFree(comment); 206 comment = NULL; 207 } 208 */ 209 // write metadata header value 210 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, 211 "Overscan value", NAN); 212 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, 213 "Overscan stdev", NAN); 214 break; 215 } 216 case PM_FIT_NONE: 217 break; 218 default: 219 psAbort("Should never get here!!!\n"); 220 } 221 return true; 222 } 223 224 bool pmOverscanSubtract (pmReadout *input, pmOverscanOptions *overscanOpts) { 225 226 assert (input); 227 228 if (overscanOpts == NULL) return true; // no overscan subtraction requested 229 230 pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest 231 psImage *image = input->image; 232 233 // check for 'soft bias' (simple, fixed offset to be subtracted) 234 if (overscanOpts->constant) { 235 // write metadata header value 236 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan value", 237 overscanOpts->value); 238 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", NAN); 239 240 // NOTE psBinaryOp frees arg2 if it is a scalar 241 (void)psBinaryOp(input->image, input->image, "-", psScalarAlloc((float)overscanOpts->value, PS_TYPE_F32)); 242 243 return true; 244 } 245 246 // we are performing a statitical analysis of the overscan region 247 248 // Check for an unallowable pmFit. 249 if (overscanOpts->fitType != PM_FIT_NONE && overscanOpts->fitType != PM_FIT_POLY_ORD && 250 overscanOpts->fitType != PM_FIT_POLY_CHEBY && overscanOpts->fitType != PM_FIT_SPLINE) { 251 psError(PS_ERR_UNKNOWN, true, "Invalid fit type (%d). Returning original image.\n", 252 overscanOpts->fitType); 253 return false; 254 } 255 256 psList *overscans = input->bias; // List of the overscan images 257 258 psStatsOptions statistic = psStatsSingleOption(overscanOpts->stat->options); // Statistic to use 259 if (statistic == 0) { 260 psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Multiple or no statistics options set: %p\n", 261 overscanOpts->stat); 262 return false; 263 } 264 psStats *stats = psStatsAlloc(statistic); // A new psStats, to avoid clobbering original 265 266 psString comment = NULL; // Comment to add 267 psStringAppend(&comment, "Subtracting overscan (stat %x; type %x; order %d)", 268 statistic, overscanOpts->fitType, overscanOpts->order); 269 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, 270 comment, ""); 271 psTrace ("psModules.detrend", 4, "%s\n", comment); 272 psFree(comment); 273 274 // Reduce all overscan pixels to a single value 275 if (overscanOpts->single) { 276 psVector *pixels = psVectorAlloc(0, PS_TYPE_F32); 277 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 278 psImage *overscan = NULL; // Overscan image from iterator 279 while ((overscan = psListGetAndIncrement(iter))) { 280 int index = pixels->n; // Index 281 pixels = psVectorRealloc(pixels, pixels->n + overscan->numRows * overscan->numCols); 282 pixels->n += overscan->numRows * overscan->numCols; 283 for (int i = 0; i < overscan->numRows; i++) { 284 memcpy(&pixels->data.F32[index], overscan->data.F32[i], 285 overscan->numCols * sizeof(psF32)); 286 index += overscan->numCols; 287 } 288 } 289 psFree(iter); 290 291 if (!psVectorStats(stats, pixels, NULL, NULL, 0)) { 292 psError(PS_ERR_UNKNOWN, false, "failure to measure stats"); 293 return false; 294 } 295 psFree(pixels); 296 double reduced = psStatsGetValue(stats, statistic); // Result of statistics 297 298 psString comment = NULL; // Comment to add 299 psStringAppend(&comment, "Overscan value: %f", reduced); 796 } 797 psStringAppend(&comment, "Mean Overscan value: %f", vectorStats->sampleMean); 300 798 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); 301 799 psFree(comment); 302 800 303 801 // write metadata header value 304 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan value", 305 reduced); 306 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", NAN); 307 308 psScalar *reducedScalar = psScalarAlloc(reduced, PS_TYPE_F32); 309 psBinaryOp (image, image, "-", psMemIncrRefCounter(reducedScalar)); // NOTE: psBinaryOp frees arg2 if it a scalar, so we need to bump to re-use 310 311 // subtract the measured value from each overscan region as well 312 iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 313 overscan = NULL; // Overscan image from iterator 314 while ((overscan = psListGetAndIncrement(iter))) { 315 psBinaryOp(overscan, overscan, "-", psMemIncrRefCounter(reducedScalar)); // NOTE: psBinaryOp frees arg2 if it a scalar, so we need to bump to re-use 316 } 317 psFree(iter); 318 psFree(reducedScalar); 802 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan mean", vectorStats->sampleMean); 803 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", vectorStats->sampleStdev); 319 804 320 805 // EAM 2022.03.29 : if the calculated overscan value is below the threshold, 321 806 // declare the readout dead and mask 322 807 323 if ((reduced < overscanOpts->minValid) || (reduced > overscanOpts->maxValid)) { 324 fprintf (stderr, "bad overscan (1) %f, masking readout\n", reduced); 325 psImage *mask = input->mask; 326 for (int y = 0; y < mask->numRows; y++) { 327 for (int x = 0; x < mask->numCols; x++) { 328 mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= overscanOpts->maskVal; 329 } 330 } 331 } 332 333 psFree(stats); 808 if ((vectorStats->sampleMean < overscanOpts->minValid) || (vectorStats->sampleMean > overscanOpts->maxValid)) 809 { 810 fprintf(stderr, "bad overscan (2) %f, masking readout\n", vectorStats->sampleMean); 811 psImage *mask = input->mask; 812 for (int y = 0; y < mask->numRows; y++) 813 { 814 for (int x = 0; x < mask->numCols; x++) 815 { 816 mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= overscanOpts->maskVal; 817 } 818 } 819 } 820 821 psFree(vectorStats); 334 822 return true; 335 } 336 337 bool mdok = false; 338 339 // We are performing a row-by-row overscan subtraction 340 int cellreaddir = psMetadataLookupS32(&mdok, input->parent->concepts, "CELL.READDIR"); // Read direction 341 if ((cellreaddir != 1) && (cellreaddir != 2)) { 342 psError(PS_ERR_UNKNOWN, true, "CELL.READDIR must be 1 (rows) or 2 (cols)\n"); 343 return false; 344 } 345 346 float chi2 = NAN; // chi^2 from fit 347 348 // adjust operation depending on the read direction : need to re-org pixels for columns 349 if (cellreaddir == 1) { 350 // The read direction is rows 351 psArray *pixels = psArrayAlloc(image->numRows); // Array of vectors containing pixels 352 for (int i = 0; i < pixels->n; i++) { 353 pixels->data[i] = psVectorAlloc(0, PS_TYPE_F32); 354 } 355 356 // Pull the pixels out into the vectors 357 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 358 psImage *overscan = NULL; // Overscan image from iterator 359 while ((overscan = psListGetAndIncrement(iter))) { 360 // the overscan and image might not be aligned. pixels->data represents 361 // the image row pixels. 362 int diff = overscan->row0 - image->row0; // Offset between the two regions 363 for (int i = PS_MAX(0,diff); i < PS_MIN(image->numRows, overscan->numRows + diff); i++) { 364 int j = i - diff; 365 // i is row on image 366 // j is row on overscan 367 psVector *values = pixels->data[i]; 368 int index = values->n; // Index in the vector 369 values = psVectorRealloc(values, values->n + overscan->numCols); 370 values->n += overscan->numCols; 371 memcpy(&values->data.F32[index], overscan->data.F32[j], 372 overscan->numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32)); 373 index += overscan->numCols; 374 pixels->data[i] = values; // Update the pointer in case it's moved 375 } 376 } 377 psFree(iter); 378 379 // Reduce the overscans 380 psVector *reduced = pmOverscanVector(&chi2, overscanOpts, pixels, stats); 381 psFree(pixels); 382 if (! reduced) { 383 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate overscan vector.\n"); 384 psFree(stats); 385 return false; 386 } 387 388 // generate stats of overscan vector for header 389 { 390 psString comment = NULL; // Comment to add 391 psStats *vectorStats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV); 392 if (!psVectorStats (vectorStats, reduced, NULL, NULL, 0)) { 393 psError(PS_ERR_UNKNOWN, false, "failure to measure stats"); 394 return false; 395 } 396 psStringAppend(&comment, "Mean Overscan value: %f", vectorStats->sampleMean); 397 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); 398 psFree(comment); 399 400 // write metadata header value 401 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan mean", vectorStats->sampleMean); 402 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", vectorStats->sampleStdev); 403 404 // EAM 2022.03.29 : if the calculated overscan value is below the threshold, 405 // declare the readout dead and mask 406 407 if ((vectorStats->sampleMean < overscanOpts->minValid) || (vectorStats->sampleMean > overscanOpts->maxValid)) { 408 fprintf (stderr, "bad overscan (2) %f, masking readout\n", vectorStats->sampleMean); 409 psImage *mask = input->mask; 410 for (int y = 0; y < mask->numRows; y++) { 411 for (int x = 0; x < mask->numCols; x++) { 412 mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= overscanOpts->maskVal; 413 } 414 } 415 } 416 417 psFree (vectorStats); 418 } 419 420 // Subtract row by row 421 for (int i = 0; i < image->numRows; i++) { 422 for (int j = 0; j < image->numCols; j++) { 423 image->data.F32[i][j] -= reduced->data.F32[i]; 424 } 425 } 426 427 // subtract from the overscan regions 428 { 429 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 430 psImage *overscan = NULL; // Overscan image from iterator 431 while ((overscan = psListGetAndIncrement(iter))) { 432 // the overscan and image might not be aligned. 433 int diff = overscan->row0 - image->row0; // Offset between the two regions 434 for (int i = PS_MAX(0,diff); i < PS_MIN(image->numRows, overscan->numRows + diff); i++) { 435 int j = i - diff; 436 // i is row on image 437 // j is row on overscan 438 for (int k = 0; k < overscan->numCols; k++) { 439 overscan->data.F32[j][k] -= reduced->data.F32[j]; 440 } 441 } 442 } 443 psFree(iter); 444 } 445 psFree(reduced); 446 } 447 if (cellreaddir == 2) { 448 // The read direction is columns 449 psArray *pixels = psArrayAlloc(image->numCols); // Array of vectors containing pixels 450 for (int i = 0; i < pixels->n; i++) { 451 psVector *values = psVectorAlloc(0, PS_TYPE_F32); 452 pixels->data[i] = values; 453 } 454 455 // Pull the pixels out into the vectors 456 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 457 psImage *overscan = NULL; // Overscan image from iterator 458 while ((overscan = psListGetAndIncrement(iter))) { 459 // the overscan and image might not be aligned. pixels->data represents 460 // the image row pixels. 461 int diff = overscan->col0 - image->col0; // Offset between the two regions 462 for (int i = PS_MAX(0,diff); i < PS_MIN(image->numCols, overscan->numCols + diff); i++) { 463 int iFixed = i - diff; 464 // i is column on image 465 // iFixed is column on overscan 466 psVector *values = pixels->data[i]; 467 int index = values->n; // Index in the vector 468 values = psVectorRealloc(values, values->n + overscan->numRows); 469 for (int j = 0; j < overscan->numRows; j++) { 470 values->data.F32[index++] = overscan->data.F32[j][iFixed]; 471 } 472 values->n += overscan->numRows; 473 pixels->data[i] = values; // Update the pointer in case it's moved 474 } 475 } 476 psFree(iter); 477 478 // Reduce the overscans 479 psVector *reduced = pmOverscanVector(&chi2, overscanOpts, pixels, stats); 480 psFree(pixels); 481 if (! reduced) { 482 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate overscan vector.\n"); 483 psFree(stats); 484 return false; 485 } 486 487 // generate stats of overscan vector for header 488 { 489 psString comment = NULL; // Comment to add 490 psStats *vectorStats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV); 491 if (!psVectorStats (vectorStats, reduced, NULL, NULL, 0)) { 492 psError(PS_ERR_UNKNOWN, false, "failure to measure stats"); 493 return false; 494 } 495 psStringAppend(&comment, "Mean Overscan value: %f", vectorStats->sampleMean); 496 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); 497 psFree(comment); 498 499 // write metadata header value 500 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan mean", vectorStats->sampleMean); 501 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", vectorStats->sampleStdev); 502 503 // EAM 2022.03.29 : if the calculated overscan value is below the threshold, 504 // declare the readout dead and mask 505 506 if ((vectorStats->sampleMean < overscanOpts->minValid) || (vectorStats->sampleMean > overscanOpts->maxValid)) { 507 fprintf (stderr, "bad overscan (3) %f, masking readout\n", vectorStats->sampleMean); 508 psImage *mask = input->mask; 509 for (int y = 0; y < mask->numRows; y++) { 510 for (int x = 0; x < mask->numCols; x++) { 511 mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= overscanOpts->maskVal; 512 } 513 } 514 } 515 psFree (vectorStats); 516 } 517 518 // Subtract column by column 519 for (int j = 0; j < image->numRows; j++) { 520 for (int i = 0; i < image->numCols; i++) { 521 image->data.F32[j][i] -= reduced->data.F32[i]; 522 } 523 } 524 525 // subtract from the overscan regions 526 { 527 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 528 psImage *overscan = NULL; // Overscan image from iterator 529 while ((overscan = psListGetAndIncrement(iter))) { 530 // the overscan and image might not be aligned. 531 int diff = overscan->col0 - image->col0; // Offset between the two regions 532 for (int i = PS_MAX(0,diff); i < PS_MIN(image->numCols, overscan->numCols + diff); i++) { 533 int j = i - diff; 534 // i is col on image 535 // j is col on overscan 536 for (int k = 0; i < overscan->numRows; k++) { 537 overscan->data.F32[k][j] -= reduced->data.F32[j]; 538 } 539 } 540 } 541 psFree(iter); 542 } 543 544 psFree(reduced); 545 } 546 547 pmOverscanUpdateHeader (hdu, overscanOpts, chi2); 548 psFree(stats); 549 550 return true; 551 552 } // End of overscan subtraction 553 823 } -
trunk/psModules/src/detrend/pmOverscan.h
r42379 r42889 18 18 19 19 /// Type of fit to perform 20 typedef enum { 21 PM_FIT_NONE, ///< No fit 22 PM_FIT_POLY_ORD, ///< Fit ordinary polynomial 23 PM_FIT_POLY_CHEBY, ///< Fit Chebyshev polynomial 24 PM_FIT_SPLINE ///< Fit cubic splines 20 typedef enum 21 { 22 PM_FIT_NONE, ///< No fit 23 PM_FIT_POLY_ORD, ///< Fit ordinary polynomial 24 PM_FIT_POLY_CHEBY, ///< Fit Chebyshev polynomial 25 PM_FIT_SPLINE ///< Fit cubic splines 25 26 } pmFit; 26 27 … … 35 36 { 36 37 // Inputs 37 bool single; ///< Reduce all overscan regions to a single value? 38 bool constant; ///< use a supplied constant value (do not measure region) 39 float value; ///< supplied value if needed (per above) 40 pmFit fitType; ///< Type of fit to overscan 41 unsigned int order; ///< Order of polynomial, or number of spline pieces 42 psStats *stat; ///< Statistic to use when reducing the minor direction 43 int boxcar; ///< Boxcar smoothing radius 44 float gauss; ///< Gaussian smoothing sigma 45 float minValid; ///< if overscan is too low, readout is dead : mask 46 float maxValid; ///< if overscan is too high, readout is dead : mask 47 psImageMaskType maskVal; ///< Mask value to give dead readouts 38 pmFit fitType; ///< Type of fit to overscan 39 unsigned int order; ///< Order of polynomial, or number of spline pieces 40 psStats *stat; ///< Statistic to use when reducing the minor direction 41 int boxcar; ///< Boxcar smoothing radius 42 float gauss; ///< Gaussian smoothing sigma 43 psRegion biassecslow; ///< The 2D slow bias section 44 psRegion biassecfast; ///< The 2D fast bias section 48 45 49 // Outputs 50 psPolynomial1D *poly; ///< Result of polynomial fit 51 psSpline1D *spline; ///< Result of spline fit 52 } 53 pmOverscanOptions; 46 47 // These are used to carry the fitted functions, but are only used to generate 48 // an updated reduced vector 49 psPolynomial1D *poly; ///< Result of polynomial fit 50 psSpline1D *spline; ///< Result of spline fit 51 } pmOverscanStatOptions; 52 53 /// Options for overscan subtraction 54 /// 55 /// The overscan subtraction may be performed by reducing all overscan regions to a single value (e.g., if 56 /// there is no structure); or the overscan may be fit perpendicular to the read direction (usually the 57 /// columns) with a particular functional form; or a single value may be subtracted for each read/scan without 58 /// fitting (if the structure defies characterisation). In any case, statistics are required to reduce 59 /// multiple values to a single value (either for the scan, or for the entire overscan regions). 60 typedef struct 61 { 62 // Inputs 63 bool single; ///< Reduce all overscan regions to a single value? 64 bool constant; ///< use a supplied constant value (do not measure region) 65 bool TwoD; ///< use a supplied constant value (do not measure region) 66 float value; ///< supplied value if needed (per above) 67 float minValid; ///< if overscan is too low, readout is dead : mask 68 float maxValid; ///< if overscan is too high, readout is dead : mask 69 psImageMaskType maskVal; ///< Mask value to give dead readouts 70 71 pmOverscanStatOptions *primary; 72 pmOverscanStatOptions *secondary; 73 } pmOverscanOptions; 54 74 55 75 /// Allocator for overscan options 56 pmOverscanOptions *pmOverscanOptionsAlloc(bool single, ///< Reduce all overscan regions to a single value? 57 pmFit fitType, ///< Type of fit to overscan 58 unsigned int order, ///< Order of polynomial, or number of splines 59 psStats *stat, ///< Statistic to use 60 int boxcar, ///< Boxcar smoothing radius 61 float gauss ///< Gaussian smoothing sigma 62 ); 76 pmOverscanOptions *pmOverscanOptionsAlloc(void); 63 77 64 psVector *pmOverscanVector(float *chi2, // chi^2 from fit 65 pmOverscanOptions *overscanOpts, // Overscan options 66 const psArray *pixels, // Array of vectors containing the pixel values 67 psStats *myStats // Statistic to use in reducing the overscan 68 ); 78 pmOverscanStatOptions *pmOverscanStatOptionsAlloc(void); 69 79 70 bool pmOverscanUpdateHeader (pmHDU *hdu, pmOverscanOptions *overscanOpts, float chi2); 80 psVector *pmOverscanVector(float *chi2, // chi^2 from fit 81 pmOverscanStatOptions *overscanOpts, // Overscan options 82 const psArray *pixels, // Array of vectors containing the pixel values 83 bool robust // use robust statistics for boxcar smoothing 84 ); 71 85 72 bool pmOverscanSubtract (pmReadout *input, pmOverscanOptions *overscanOpts); 86 // bool pmOverscanUpdateHeader (pmHDU *hdu, pmOverscanOptions *overscanOpts, float chi2); 87 88 bool pmOverscanSubtract(pmReadout *input, pmOverscanOptions *overscanOpts, bool doTwoDOverscan); 73 89 74 90 /// @} 75 91 #endif 76 -
trunk/psModules/src/detrend/pmPattern.c
r42379 r42889 239 239 # define READNOISE 10 /* arbitrary number */ 240 240 float sigma = sqrt(stats->robustMedian + PS_SQR(READNOISE)); 241 if(isnan(sigma)) sigma=20.; 241 242 float delta = PS_MIN (thresh * sigma, 2*nominalAmplitude); 242 243 float lower = stats->robustMedian - delta; // Lower bound for data … … 246 247 // if the noise from the background is too large 247 248 float significance = nominalAmplitude / sigma; 249 significance = abs(significance); 248 250 249 251 // XXX EAM : arbitrary number 250 252 if (isfinite(nominalAmplitude) && (significance < 1.0/6.0)) { 253 psLogMsg("ppImage", PS_LOG_INFO, "Skipping row pattern correction for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance); 254 return true; 255 } 256 //Add some safeties in case of bad fits 257 if (isnan(significance) || (background < 0.0)) { 251 258 psLogMsg("ppImage", PS_LOG_INFO, "Skipping row pattern correction for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance); 252 259 return true; … … 554 561 # define READNOISE 10 /* arbitrary number */ 555 562 float sigma = sqrt(stats->robustMedian + PS_SQR(READNOISE)); 563 if(isnan(sigma)) sigma=20.; // XXX EAM : somewhat arbitrary number 556 564 float delta = PS_MIN (thresh * sigma, 5*nominalAmplitude); 557 565 float lower = stats->robustMedian - delta; // Lower bound for data … … 561 569 // if the noise from the background is too large 562 570 float significance = nominalAmplitude / sigma; 563 571 significance = abs(significance); 572 564 573 // XXX EAM : arbitrary number 565 574 if (isfinite(nominalAmplitude) && (significance < 1.0/6.0)) { … … 569 578 return true; 570 579 } 580 //Add some safeties in case of bad fits 581 if (isnan(significance) || (background < 0.0)) { 582 psLogMsg("ppImage", PS_LOG_INFO, "Skipping row pattern correction due to bad fit for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance); 583 psFree(stats); 584 psFree(rng); 585 return true; 586 } 587 571 588 psLogMsg("ppImage", PS_LOG_INFO, "Performing row pattern correction for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance); 572 589 # endif
Note:
See TracChangeset
for help on using the changeset viewer.
