Changeset 42679 for branches/2dbias/psModules/src/detrend/pmOverscan.c
- Timestamp:
- May 21, 2024, 8:50:38 PM (2 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2dbias/psModules/src/detrend/pmOverscan.c
r42664 r42679 17 17 #include "pmOverscan.h" 18 18 19 #define SMOOTH_NSIGMA 4.0 // Number of Gaussian sigma the smoothing kernel extends19 #define SMOOTH_NSIGMA 4.0 // Number of Gaussian sigma the smoothing kernel extends 20 20 21 21 static void pmOverscanOptionsFree(pmOverscanOptions *options); 22 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) { 26 27 assert (input); 28 29 if (overscanOpts == NULL) return true; // no overscan subtraction requested 30 31 pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest 32 psImage *image = input->image; 33 34 // check for 'soft bias' (simple, fixed offset to be subtracted) 35 if (overscanOpts->constant) { 36 // write metadata header value 37 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan value", overscanOpts->value); 38 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", NAN); 39 40 // NOTE psBinaryOp frees arg2 if it is a scalar 41 (void)psBinaryOp(input->image, input->image, "-", psScalarAlloc((float)overscanOpts->value, PS_TYPE_F32)); 42 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 if ((cellreaddir != 1) && (cellreaddir != 2)) 167 { 168 psError(PS_ERR_UNKNOWN, true, "CELL.READDIR must be 1 (rows) or 2 (cols)\n"); 169 return false; 170 } 171 172 float chi2 = NAN; // chi^2 from fit 173 174 // adjust operation depending on the read direction : need to re-org pixels for columns 175 if (!doTwoDOverscan && (cellreaddir == 1)) 176 { 177 // The read direction is rows 178 psArray *pixels = psArrayAlloc(image->numRows); // Array of vectors containing pixels 179 for (int i = 0; i < pixels->n; i++) 180 { 181 pixels->data[i] = psVectorAlloc(0, PS_TYPE_F32); 182 } 183 184 // Pull the pixels out into the vectors 185 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 186 psImage *overscan = NULL; // Overscan image from iterator 187 while ((overscan = psListGetAndIncrement(iter))) 188 { 189 // the overscan and image might not be aligned. pixels->data represents 190 // the image row pixels. 191 int diff = overscan->row0 - image->row0; // Offset between the two regions 192 for (int i = PS_MAX(0, diff); i < PS_MIN(image->numRows, overscan->numRows + diff); i++) 193 { 194 int j = i - diff; 195 // i is row on image 196 // j is row on overscan 197 psVector *values = pixels->data[i]; 198 int index = values->n; // Index in the vector 199 values = psVectorRealloc(values, values->n + overscan->numCols); 200 values->n += overscan->numCols; 201 // XXX double-check the range of values->n here 202 memcpy(&values->data.F32[index], overscan->data.F32[j], 203 overscan->numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32)); 204 index += overscan->numCols; 205 pixels->data[i] = values; // Update the pointer in case it's moved 206 } 207 } 208 psFree(iter); 209 210 // Reduce the overscans 211 psVector *reduced = pmOverscanVector(&chi2, overscanOpts->primary, pixels); 212 psFree(pixels); 213 if (!reduced) 214 { 215 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate overscan vector.\n"); 216 return false; 217 } 218 219 if (!pmOverscanUpdateHeaderVector(input, hdu, overscanOpts, reduced)) 220 { 221 psError(PS_ERR_UNKNOWN, false, "failure to update header"); 222 return false; 223 } 224 225 // Subtract row by row 226 for (int i = 0; i < image->numRows; i++) 227 { 228 for (int j = 0; j < image->numCols; j++) 229 { 230 image->data.F32[i][j] -= reduced->data.F32[i]; 231 } 232 } 233 234 // subtract from the overscan regions 235 { 236 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 237 psImage *overscan = NULL; // Overscan image from iterator 238 while ((overscan = psListGetAndIncrement(iter))) 239 { 240 // the overscan and image might not be aligned. 241 int diff = overscan->row0 - image->row0; // Offset between the two regions 242 for (int i = PS_MAX(0, diff); i < PS_MIN(image->numRows, overscan->numRows + diff); i++) 243 { 244 int j = i - diff; 245 // i is row on image 246 // j is row on overscan 247 for (int k = 0; k < overscan->numCols; k++) 248 { 249 overscan->data.F32[j][k] -= reduced->data.F32[j]; 250 } 251 } 252 } 253 psFree(iter); 254 } 255 psFree(reduced); 256 } 257 258 if (!doTwoDOverscan && (cellreaddir == 2)) 259 { 260 // The read direction is columns 261 psArray *pixels = psArrayAlloc(image->numCols); // Array of vectors containing pixels 262 for (int i = 0; i < pixels->n; i++) 263 { 264 psVector *values = psVectorAlloc(0, PS_TYPE_F32); 265 pixels->data[i] = values; 266 } 267 268 // Pull the pixels out into the vectors 269 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 270 psImage *overscan = NULL; // Overscan image from iterator 271 while ((overscan = psListGetAndIncrement(iter))) 272 { 273 // the overscan and image might not be aligned. pixels->data represents 274 // the image row pixels. 275 int diff = overscan->col0 - image->col0; // Offset between the two regions 276 for (int i = PS_MAX(0, diff); i < PS_MIN(image->numCols, overscan->numCols + diff); i++) 277 { 278 int iFixed = i - diff; 279 // i is column on image 280 // iFixed is column on overscan 281 psVector *values = pixels->data[i]; 282 int index = values->n; // Index in the vector 283 values = psVectorRealloc(values, values->n + overscan->numRows); 284 for (int j = 0; j < overscan->numRows; j++) 285 { 286 values->data.F32[index++] = overscan->data.F32[j][iFixed]; 287 } 288 values->n += overscan->numRows; 289 pixels->data[i] = values; // Update the pointer in case it's moved 290 } 291 } 292 psFree(iter); 293 294 // Reduce the overscans 295 psVector *reduced = pmOverscanVector(&chi2, overscanOpts->primary, pixels); 296 psFree(pixels); 297 if (!reduced) 298 { 299 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate overscan vector.\n"); 300 return false; 301 } 302 303 if (!pmOverscanUpdateHeaderVector(input, hdu, overscanOpts, reduced)) 304 { 305 psError(PS_ERR_UNKNOWN, false, "failure to update header"); 306 return false; 307 } 308 309 // Subtract column by column 310 for (int j = 0; j < image->numRows; j++) 311 { 312 for (int i = 0; i < image->numCols; i++) 313 { 314 image->data.F32[j][i] -= reduced->data.F32[i]; 315 } 316 } 317 318 // subtract from the overscan regions 319 { 320 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator 321 psImage *overscan = NULL; // Overscan image from iterator 322 while ((overscan = psListGetAndIncrement(iter))) 323 { 324 // the overscan and image might not be aligned. 325 int diff = overscan->col0 - image->col0; // Offset between the two regions 326 for (int i = PS_MAX(0, diff); i < PS_MIN(image->numCols, overscan->numCols + diff); i++) 327 { 328 int j = i - diff; 329 // i is col on image 330 // j is col on overscan 331 for (int k = 0; i < overscan->numRows; k++) 332 { 333 overscan->data.F32[k][j] -= reduced->data.F32[j]; 334 } 335 } 336 } 337 psFree(iter); 338 } 339 340 psFree(reduced); 341 } 342 343 // 2D bias subtraction with x-dir readout direction: the 344 // bias is constructed by combining a 1D pattern in the 345 // readout direction from the top overscan region and a second 346 // 1D pattern in the cross direction from the overscan 347 if (doTwoDOverscan && (cellreaddir == 1)) 348 { 349 psAssert(overscanOpts->secondary, "2D overscan subtraction requires OVERSCAN.2D parameters"); 350 // we require 2 overscan regions, and they must match these directions: 351 if (overscans->n != 2) 352 { 353 psLogMsg(__func__, PS_LOG_ERROR, "OVERSCAN.2D requires 2 overscan regions but %d supplied", (int)overscans->n); 354 psLogMsg(__func__, PS_LOG_ERROR, "e.g.: CELL.BIASSEC STR [591:624,1:608],[1:624,599:608]"); 355 psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to generate overscan vector.\n"); 356 return false; 357 } 358 359 // the serial (fast readout) direction is columns (x-dir) 360 psImage *yscan = psListGet(overscans, 0); // overscan region spanning all rows 361 psImage *xscan = psListGet(overscans, 1); // overscan region spanning all columns 362 363 // Extract the y-dir overscan vector. The overscan and image might not be aligned: 364 // diff represents the offset between the rows in the image data and the overscan. 365 // pixels->data represents the image row pixels. For example, the image region may be 366 // inset in the y-direction but the overscan could cover the entire y-range 367 368 // The read direction is rows 369 psArray *yscanPixels = psArrayAlloc(yscan->numRows); // Array of vectors containing pixels 370 for (int i = 0; i < yscanPixels->n; i++) 371 { 372 yscanPixels->data[i] = psVectorAlloc(0, PS_TYPE_F32); 373 } 374 375 // XXX this code allows multiple yscans to be appended, but this does not 376 // match the concept of how they are assigned above: biassec[0] = yscan 377 // int yDiff = yscan->row0 - image->row0; // Offset between the two regions 378 for (int i = 0; i < yscanPixels->n; i++) 379 { 380 psVector *values = yscanPixels->data[i]; 381 int index = values->n; // Index in the vector 382 values = psVectorRealloc(values, values->n + yscan->numCols); 383 values->n += yscan->numCols; 384 // XXX double-check the range of values->n here 385 memcpy(&values->data.F32[index], yscan->data.F32[i], 386 yscan->numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32)); 387 yscanPixels->data[i] = values; // Update the pointer in case it's moved 388 } 389 390 // Extract the x-dir overscan vector. The overscan and image might not be aligned: 391 // diff represents the offset between the rows in the image data and the overscan. 392 // pixels->data represents the image row pixels. For example, the image region may be 393 // inset in the x-direction but the overscan could cover the entire y-range 394 395 // Extract the top region as a vector of the columns 396 psArray *xscanPixels = psArrayAlloc(xscan->numCols); // Array of vectors containing pixels 397 for (int i = 0; i < xscanPixels->n; i++) 398 { 399 xscanPixels->data[i] = psVectorAlloc(0, PS_TYPE_F32); 400 } 401 402 // int xDiff = xscan->col0 - image->col0; // Offset between the two regions 403 for (int ix = 0; ix < xscanPixels->n; ix++) 404 { 405 psVector *values = xscanPixels->data[ix]; 406 values = psVectorRealloc(values, xscan->numRows); 407 values->n = xscan->numRows; 408 for (int iy = 0; iy < xscan->numRows; iy++) 409 { 410 values->data.F32[iy] = xscan->data.F32[iy][ix]; 411 } 412 xscanPixels->data[ix] = values; // Update the pointer in case it's moved 413 } 414 415 // Reduce the overscans 416 // XXX need to save 2 different chi-square values 417 psVector *yReduced = pmOverscanVector(&chi2, overscanOpts->primary, yscanPixels); 418 psFree(yscanPixels); 419 if (!yReduced) 420 { 421 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate y-dir overscan vector.\n"); 422 return false; 423 } 424 if (!pmOverscanUpdateHeaderVector(input, hdu, overscanOpts, yReduced)) 425 { 426 psError(PS_ERR_UNKNOWN, false, "failure to update header"); 427 return false; 428 } 429 430 // Reduce the overscans 431 // XXX need to save 2 different chi-square values 432 psVector *xReduced = pmOverscanVector(&chi2, overscanOpts->secondary, xscanPixels); 433 psFree(xscanPixels); 434 if (!xReduced) 435 { 436 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate x-dir overscan vector.\n"); 437 return false; 438 } 439 440 // subtract the 2D bias from the image 441 if (yscan->col0 >= xscan->col0 && yscan->col0 + yscan->numCols <= xscan->col0 + xscan->numCols) 442 { 443 // define where to normalize the xReduced vector 444 int j0 = yscan->col0 - xscan->col0 + (int)(yscan->numCols / 2) - 1; 445 // XXX apply the 2D bias correction here 446 int yDiff = yscan->row0 - image->row0; // y offset between the science and the yoverscan region 447 int xDiff = xscan->col0 - image->col0; // x offset between the science and the xoverscan region 448 for (int i = 0; i < image->numRows; i++) 449 { 450 for (int j = 0; j < image->numCols; j++) 451 { 452 int iy = i + yDiff; 453 int jx = j + xDiff; 454 image->data.F32[i][j] -= yReduced->data.F32[iy] - xReduced->data.F32[j0] + xReduced->data.F32[jx]; 455 } 456 } 457 } 458 else 459 { 460 psError(PS_ERR_UNKNOWN, true, "x dimension of yscan is not fully contained by xscan\n"); 461 return false; 462 } 463 464 // subtract the y-dir vector from the y-dir overscan regions (why?) 465 { 466 // the overscan and image might not be aligned. 467 // int diff = yscan->row0 - image->row0; // Offset between the two regions 468 for (int i = 0; i < yscan->numRows; i++) 469 { 470 for (int j = 0; j < yscan->numCols; j++) 471 { 472 yscan->data.F32[i][j] -= yReduced->data.F32[i]; 473 } 474 } 475 } 476 477 // subtract the x-dir vector from the x-dir overscan regions (why?) 478 { 479 // the overscan and image might not be aligned. 480 // int diff = xscan->col0 - image->col0; // Offset between the two regions 481 for (int i = 0; i < xscan->numCols; i++) 482 { 483 // int j = i - diff; 484 // i is column on image (aligned with xReduced) 485 // j is column on xscan 486 for (int j = 0; j < xscan->numRows; j++) 487 { 488 xscan->data.F32[j][i] -= xReduced->data.F32[i]; 489 } 490 } 491 } 492 psFree(xReduced); 493 psFree(yReduced); 494 } 495 // pmOverscanUpdateHeader (hdu, overscanOpts, chi2); 43 496 return true; 44 }45 46 // we are performing a statitical analysis of the overscan region47 48 // Check for an unallowable pmFit.49 if (overscanOpts->primary) {50 if (overscanOpts->primary->fitType != PM_FIT_NONE &&51 overscanOpts->primary->fitType != PM_FIT_POLY_ORD &&52 overscanOpts->primary->fitType != PM_FIT_POLY_CHEBY &&53 overscanOpts->primary->fitType != PM_FIT_SPLINE) {54 psError(PS_ERR_UNKNOWN, true, "Invalid fit type (%d). Returning original image.\n",55 overscanOpts->primary->fitType);56 return false;57 }58 }59 if (overscanOpts->secondary) {60 if (overscanOpts->secondary->fitType != PM_FIT_NONE &&61 overscanOpts->secondary->fitType != PM_FIT_POLY_ORD &&62 overscanOpts->secondary->fitType != PM_FIT_POLY_CHEBY &&63 overscanOpts->secondary->fitType != PM_FIT_SPLINE) {64 psError(PS_ERR_UNKNOWN, true, "Invalid fit type (2D) (%d). Returning original image.\n",65 overscanOpts->secondary->fitType);66 return false;67 }68 }69 70 psList *overscans = input->bias; // List of the overscan images71 72 // Reduce all overscan pixels to a single value73 if (overscanOpts->single) {74 75 // extract overscan pixels to a single vector76 psVector *pixels = psVectorAlloc(0, PS_TYPE_F32);77 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator78 psImage *overscan = NULL; // Overscan image from iterator79 while ((overscan = psListGetAndIncrement(iter))) {80 int index = pixels->n; // Index81 pixels = psVectorRealloc(pixels, pixels->n + overscan->numRows * overscan->numCols);82 pixels->n += overscan->numRows * overscan->numCols;83 for (int i = 0; i < overscan->numRows; i++) {84 memcpy(&pixels->data.F32[index], overscan->data.F32[i],85 overscan->numCols * sizeof(psF32));86 index += overscan->numCols;87 }88 }89 psFree(iter);90 91 // statistic to be calculated92 psStatsOptions statistic = psStatsSingleOption(overscanOpts->primary->stat->options); // Statistic to use93 if (!statistic) {94 psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Multiple or no statistics options set: %p\n",95 overscanOpts->primary->stat);96 return false;97 }98 psStats *stats = psStatsAlloc(statistic); // A new psStats, to avoid clobbering original99 100 if (!psVectorStats(stats, pixels, NULL, NULL, 0)) {101 psError(PS_ERR_UNKNOWN, false, "failure to measure stats");102 return false;103 }104 psFree(pixels);105 double reduced = psStatsGetValue(stats, statistic); // Result of statistics106 107 psString comment = NULL; // Comment to add108 psStringAppend(&comment, "Overscan value: %f", reduced);109 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");110 psFree(comment);111 112 // write metadata header value113 // XXX EAM : this could / should write the stdev of the overscan region114 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan value", reduced);115 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", NAN);116 117 psScalar *reducedScalar = psScalarAlloc(reduced, PS_TYPE_F32);118 psBinaryOp (image, image, "-", psMemIncrRefCounter(reducedScalar)); // NOTE: psBinaryOp frees arg2 if it a scalar, so we need to bump to re-use119 120 // subtract the measured value from each overscan region as well121 iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator122 overscan = NULL; // Overscan image from iterator123 while ((overscan = psListGetAndIncrement(iter))) {124 psBinaryOp(overscan, overscan, "-", psMemIncrRefCounter(reducedScalar)); // NOTE: psBinaryOp frees arg2 if it a scalar, so we need to bump to re-use125 }126 psFree(iter);127 psFree(reducedScalar);128 129 // EAM 2022.03.29 : if the calculated overscan value is below the threshold,130 // declare the readout dead and mask131 132 if ((reduced < overscanOpts->minValid) || (reduced > overscanOpts->maxValid)) {133 fprintf (stderr, "bad overscan (1) %f, masking readout\n", reduced);134 psImage *mask = input->mask;135 for (int y = 0; y < mask->numRows; y++) {136 for (int x = 0; x < mask->numCols; x++) {137 mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= overscanOpts->maskVal;138 }139 }140 }141 142 psFree(stats);143 return true;144 }145 146 bool mdok = false;147 148 // We are performing a row-by-row overscan subtraction149 int cellreaddir = psMetadataLookupS32(&mdok, input->parent->concepts, "CELL.READDIR"); // Read direction150 if ((cellreaddir != 1) && (cellreaddir != 2)) {151 psError(PS_ERR_UNKNOWN, true, "CELL.READDIR must be 1 (rows) or 2 (cols)\n");152 return false;153 }154 155 float chi2 = NAN; // chi^2 from fit156 157 // adjust operation depending on the read direction : need to re-org pixels for columns158 if (!overscanOpts->TwoD && (cellreaddir == 1)) {159 // The read direction is rows160 psArray *pixels = psArrayAlloc(image->numRows); // Array of vectors containing pixels161 for (int i = 0; i < pixels->n; i++) {162 pixels->data[i] = psVectorAlloc(0, PS_TYPE_F32);163 }164 165 // Pull the pixels out into the vectors166 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator167 psImage *overscan = NULL; // Overscan image from iterator168 while ((overscan = psListGetAndIncrement(iter))) {169 // the overscan and image might not be aligned. pixels->data represents170 // the image row pixels.171 int diff = overscan->row0 - image->row0; // Offset between the two regions172 for (int i = PS_MAX(0,diff); i < PS_MIN(image->numRows, overscan->numRows + diff); i++) {173 int j = i - diff;174 // i is row on image175 // j is row on overscan176 psVector *values = pixels->data[i];177 int index = values->n; // Index in the vector178 values = psVectorRealloc(values, values->n + overscan->numCols);179 values->n += overscan->numCols;180 // XXX double-check the range of values->n here181 memcpy(&values->data.F32[index], overscan->data.F32[j],182 overscan->numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32));183 index += overscan->numCols;184 pixels->data[i] = values; // Update the pointer in case it's moved185 }186 }187 psFree(iter);188 189 // Reduce the overscans190 psVector *reduced = pmOverscanVector(&chi2, overscanOpts->primary, pixels);191 psFree(pixels);192 if (! reduced) {193 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate overscan vector.\n");194 return false;195 }196 197 if (!pmOverscanUpdateHeaderVector (input, hdu, overscanOpts, reduced)) {198 psError(PS_ERR_UNKNOWN, false, "failure to update header");199 return false;200 }201 202 // Subtract row by row203 for (int i = 0; i < image->numRows; i++) {204 for (int j = 0; j < image->numCols; j++) {205 image->data.F32[i][j] -= reduced->data.F32[i];206 }207 }208 209 // subtract from the overscan regions210 {211 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator212 psImage *overscan = NULL; // Overscan image from iterator213 while ((overscan = psListGetAndIncrement(iter))) {214 // the overscan and image might not be aligned.215 int diff = overscan->row0 - image->row0; // Offset between the two regions216 for (int i = PS_MAX(0,diff); i < PS_MIN(image->numRows, overscan->numRows + diff); i++) {217 int j = i - diff;218 // i is row on image219 // j is row on overscan220 for (int k = 0; k < overscan->numCols; k++) {221 overscan->data.F32[j][k] -= reduced->data.F32[j];222 }223 }224 }225 psFree(iter);226 }227 psFree(reduced);228 }229 230 if (!overscanOpts->TwoD && (cellreaddir == 2)) {231 // The read direction is columns232 psArray *pixels = psArrayAlloc(image->numCols); // Array of vectors containing pixels233 for (int i = 0; i < pixels->n; i++) {234 psVector *values = psVectorAlloc(0, PS_TYPE_F32);235 pixels->data[i] = values;236 }237 238 // Pull the pixels out into the vectors239 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator240 psImage *overscan = NULL; // Overscan image from iterator241 while ((overscan = psListGetAndIncrement(iter))) {242 // the overscan and image might not be aligned. pixels->data represents243 // the image row pixels.244 int diff = overscan->col0 - image->col0; // Offset between the two regions245 for (int i = PS_MAX(0,diff); i < PS_MIN(image->numCols, overscan->numCols + diff); i++) {246 int iFixed = i - diff;247 // i is column on image248 // iFixed is column on overscan249 psVector *values = pixels->data[i];250 int index = values->n; // Index in the vector251 values = psVectorRealloc(values, values->n + overscan->numRows);252 for (int j = 0; j < overscan->numRows; j++) {253 values->data.F32[index++] = overscan->data.F32[j][iFixed];254 }255 values->n += overscan->numRows;256 pixels->data[i] = values; // Update the pointer in case it's moved257 }258 }259 psFree(iter);260 261 // Reduce the overscans262 psVector *reduced = pmOverscanVector(&chi2, overscanOpts->primary, pixels);263 psFree(pixels);264 if (! reduced) {265 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate overscan vector.\n");266 return false;267 }268 269 if (!pmOverscanUpdateHeaderVector (input, hdu, overscanOpts, reduced)) {270 psError(PS_ERR_UNKNOWN, false, "failure to update header");271 return false;272 }273 274 // Subtract column by column275 for (int j = 0; j < image->numRows; j++) {276 for (int i = 0; i < image->numCols; i++) {277 image->data.F32[j][i] -= reduced->data.F32[i];278 }279 }280 281 // subtract from the overscan regions282 {283 psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator284 psImage *overscan = NULL; // Overscan image from iterator285 while ((overscan = psListGetAndIncrement(iter))) {286 // the overscan and image might not be aligned.287 int diff = overscan->col0 - image->col0; // Offset between the two regions288 for (int i = PS_MAX(0,diff); i < PS_MIN(image->numCols, overscan->numCols + diff); i++) {289 int j = i - diff;290 // i is col on image291 // j is col on overscan292 for (int k = 0; i < overscan->numRows; k++) {293 overscan->data.F32[k][j] -= reduced->data.F32[j];294 }295 }296 }297 psFree(iter);298 }299 300 psFree(reduced);301 }302 303 // 2D bias subtraction with x-dir readout direction: the304 // bias is constructed by combining a 1D pattern in the305 // readout direction from the top overscan region and a second306 // 1D pattern in the cross direction from the overscan307 if (overscanOpts->TwoD && (cellreaddir == 1)) {308 psAssert (overscanOpts->secondary, "2D overscan subtraction requires OVERSCAN.2D parameters");309 // we require 2 overscan regions, and they must match these directions:310 if (overscans->n != 2) {311 psLogMsg (__func__, PS_LOG_ERROR, "OVERSCAN.2D requires 2 overscan regions but %d supplied", (int) overscans->n);312 psLogMsg (__func__, PS_LOG_ERROR, "e.g.: CELL.BIASSEC STR [591:624,1:608],[1:590,598:608]");313 psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to generate overscan vector.\n");314 return false;315 }316 317 // the serial (fast readout) direction is columns (x-dir)318 psImage *yscan = psListGet (overscans, 0); // overscan region spanning all rows319 psImage *xscan = psListGet (overscans, 1); // overscan region spanning all columns320 321 // Extract the y-dir overscan vector. The overscan and image might not be aligned:322 // diff represents the offset between the rows in the image data and the overscan.323 // pixels->data represents the image row pixels. For example, the image region may be324 // inset in the y-direction but the overscan could cover the entire y-range325 326 // The read direction is rows327 psArray *yscanPixels = psArrayAlloc(image->numRows); // Array of vectors containing pixels328 for (int i = 0; i < yscanPixels->n; i++) {329 yscanPixels->data[i] = psVectorAlloc(0, PS_TYPE_F32);330 }331 332 // XXX this code allows multiple yscans to be appended, but this does not333 // match the concept of how they are assigned above: biassec[0] = yscan334 int yDiff = yscan->row0 - image->row0; // Offset between the two regions335 for (int i = PS_MAX(0,yDiff); i < PS_MIN(image->numRows, yscan->numRows + yDiff); i++) {336 int j = i - yDiff;337 // i is row on image, j is row on yscan338 psVector *values = yscanPixels->data[i];339 int index = values->n; // Index in the vector340 values = psVectorRealloc(values, values->n + yscan->numCols);341 values->n += yscan->numCols;342 // XXX double-check the range of values->n here343 memcpy(&values->data.F32[index], yscan->data.F32[j],344 yscan->numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32));345 index += yscan->numCols;346 yscanPixels->data[i] = values; // Update the pointer in case it's moved347 }348 349 // Extract the x-dir overscan vector. The overscan and image might not be aligned:350 // diff represents the offset between the rows in the image data and the overscan.351 // pixels->data represents the image row pixels. For example, the image region may be352 // inset in the x-direction but the overscan could cover the entire y-range353 354 // Extract the top region as a vector of the columns355 psArray *xscanPixels = psArrayAlloc(image->numCols); // Array of vectors containing pixels356 for (int i = 0; i < xscanPixels->n; i++) {357 xscanPixels->data[i] = psVectorAlloc(0, PS_TYPE_F32);358 }359 360 int xDiff = xscan->col0 - image->col0; // Offset between the two regions361 for (int ix = PS_MAX(0,xDiff); ix < PS_MIN(image->numCols, xscan->numCols + xDiff); ix++) {362 int jx = ix - xDiff;363 // ix is row on image, jx is column on xscan364 psVector *values = xscanPixels->data[ix];365 values = psVectorRealloc(values, xscan->numRows);366 values->n = xscan->numRows;367 for (int iy = 0; iy < xscan->numRows; iy++) {368 values->data.F32[iy] = xscan->data.F32[iy][jx];369 }370 xscanPixels->data[ix] = values; // Update the pointer in case it's moved371 }372 373 // Reduce the overscans374 // XXX need to save 2 different chi-square values375 psVector *yReduced = pmOverscanVector(&chi2, overscanOpts->primary, yscanPixels);376 psFree(yscanPixels);377 if (!yReduced) {378 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate y-dir overscan vector.\n");379 return false;380 }381 if (!pmOverscanUpdateHeaderVector (input, hdu, overscanOpts, yReduced)) {382 psError(PS_ERR_UNKNOWN, false, "failure to update header");383 return false;384 }385 386 // Reduce the overscans387 // XXX need to save 2 different chi-square values388 psVector *xReduced = pmOverscanVector(&chi2, overscanOpts->secondary, xscanPixels);389 psFree(xscanPixels);390 if (!xReduced) {391 psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate x-dir overscan vector.\n");392 return false;393 }394 395 // XXX apply the 2D bias correction here396 for (int i = 0; i < image->numRows; i++) {397 for (int j = 0; j < image->numCols; j++) {398 image->data.F32[i][j] -= yReduced->data.F32[i];399 }400 }401 402 // subtract the y-dir vector from the y-dir overscan regions (why?)403 {404 // the overscan and image might not be aligned.405 int diff = yscan->row0 - image->row0; // Offset between the two regions406 for (int i = PS_MAX(0,diff); i < PS_MIN(image->numRows, yscan->numRows + diff); i++) {407 int j = i - diff;408 // i is row on image (aligned with yReduced)409 // j is row on yscan410 for (int k = 0; k < yscan->numCols; k++) {411 yscan->data.F32[j][k] -= yReduced->data.F32[i];412 }413 }414 }415 416 // subtract the x-dir vector from the x-dir overscan regions (why?)417 {418 // the overscan and image might not be aligned.419 int diff = xscan->col0 - image->col0; // Offset between the two regions420 for (int i = PS_MAX(0,diff); i < PS_MIN(image->numCols, xscan->numCols + diff); i++) {421 int j = i - diff;422 // i is column on image (aligned with xReduced)423 // j is column on xscan424 for (int k = 0; k < xscan->numRows; k++) {425 xscan->data.F32[k][j] -= xReduced->data.F32[i];426 }427 }428 }429 psFree(xReduced);430 psFree(yReduced);431 }432 // pmOverscanUpdateHeader (hdu, overscanOpts, chi2);433 return true;434 497 435 498 } // End of overscan subtraction … … 437 500 static void pmOverscanOptionsFree(pmOverscanOptions *options) 438 501 { 439 psFree(options->primary);440 psFree(options->secondary);502 psFree(options->primary); 503 psFree(options->secondary); 441 504 } 442 505 443 506 static void pmOverscanStatOptionsFree(pmOverscanStatOptions *options) 444 507 { 445 psFree(options->stat);446 psFree(options->poly);447 psFree(options->spline);508 psFree(options->stat); 509 psFree(options->poly); 510 psFree(options->spline); 448 511 } 449 512 … … 451 514 pmOverscanStatOptions *pmOverscanStatOptionsAlloc(void) 452 515 { 453 pmOverscanStatOptions *opts = psAlloc(sizeof(pmOverscanStatOptions));454 psMemSetDeallocator(opts, (psFreeFunc)pmOverscanStatOptionsFree);455 456 // Inputs457 opts->fitType = PM_FIT_NONE;458 opts->order= 0;459 opts->stat= NULL;460 461 // Smoothing462 opts->boxcar = 0;463 opts->gauss= 0.0;464 465 // Outputs466 opts->poly = NULL;467 opts->spline = NULL;468 469 return opts;516 pmOverscanStatOptions *opts = psAlloc(sizeof(pmOverscanStatOptions)); 517 psMemSetDeallocator(opts, (psFreeFunc)pmOverscanStatOptionsFree); 518 519 // Inputs 520 opts->fitType = PM_FIT_NONE; 521 opts->order = 0; 522 opts->stat = NULL; 523 524 // Smoothing 525 opts->boxcar = 0; 526 opts->gauss = 0.0; 527 528 // Outputs 529 opts->poly = NULL; 530 opts->spline = NULL; 531 532 return opts; 470 533 } 471 534 … … 473 536 pmOverscanOptions *pmOverscanOptionsAlloc(void) 474 537 { 475 pmOverscanOptions *opts = psAlloc(sizeof(pmOverscanOptions));476 psMemSetDeallocator(opts, (psFreeFunc)pmOverscanOptionsFree);477 478 // Inputs479 opts->single= false;480 opts->constant = false;481 opts->TwoD= false;482 483 opts->value= 0.0;484 485 opts->minValid = 0.0;// default value if not defined486 opts->maxValid = (float)0x10000; // default value if not defined487 opts->maskVal = 0x0001;// default value if not defined488 489 // stat options490 opts->primary = NULL;491 opts->secondary = NULL;492 493 return opts;538 pmOverscanOptions *opts = psAlloc(sizeof(pmOverscanOptions)); 539 psMemSetDeallocator(opts, (psFreeFunc)pmOverscanOptionsFree); 540 541 // Inputs 542 opts->single = false; 543 opts->constant = false; 544 opts->TwoD = false; 545 546 opts->value = 0.0; 547 548 opts->minValid = 0.0; // default value if not defined 549 opts->maxValid = (float)0x10000; // default value if not defined 550 opts->maskVal = 0x0001; // default value if not defined 551 552 // stat options 553 opts->primary = NULL; 554 opts->secondary = NULL; 555 556 return opts; 494 557 } 495 558 496 559 // Produce an overscan vector from an array of pixels 497 psVector *pmOverscanVector(float *chi2, // chi^2 from fit498 pmOverscanStatOptions *overscanOpts, // Overscan statistic options499 const psArray *pixels// Array of vectors containing the pixel values500 )560 psVector *pmOverscanVector(float *chi2, // chi^2 from fit 561 pmOverscanStatOptions *overscanOpts, // Overscan statistic options 562 const psArray *pixels // Array of vectors containing the pixel values 563 ) 501 564 { 502 assert(overscanOpts); 503 assert(pixels); 504 505 // statisctic to be calculated 506 psStatsOptions statistic = psStatsSingleOption(overscanOpts->stat->options); // Statistic to use 507 if (!statistic) { 508 psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Multiple or no statistics options set: %p\n", overscanOpts->stat); 509 return false; 510 } 511 psStats *stats = psStatsAlloc(statistic); // A new psStats, to avoid clobbering original 512 513 // Reduce the overscans 514 psVector *reduced = psVectorAlloc(pixels->n, PS_TYPE_F32); // Overscan for each row 515 psVector *ordinate = psVectorAlloc(pixels->n, PS_TYPE_F32); // Ordinate 516 psVector *mask = psVectorAlloc(pixels->n, PS_TYPE_VECTOR_MASK); // Mask for fitting 517 518 for (int i = 0; i < pixels->n; i++) { 519 psVector *values = pixels->data[i]; // Vector with overscan values 520 if (values->n > 0) { 521 mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0; 522 ordinate->data.F32[i] = 2.0*(float)i/(float)pixels->n - 1.0; // Scale to [-1,1] 523 if (!psVectorStats(stats, values, NULL, NULL, 0)) { 524 psError(PS_ERR_UNKNOWN, false, "failure to measure stats"); 565 assert(overscanOpts); 566 assert(pixels); 567 568 // statisctic to be calculated 569 psStatsOptions statistic = psStatsSingleOption(overscanOpts->stat->options); // Statistic to use 570 if (!statistic) 571 { 572 psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Multiple or no statistics options set: %p\n", overscanOpts->stat); 573 return false; 574 } 575 psStats *stats = psStatsAlloc(statistic); // A new psStats, to avoid clobbering original 576 577 // Reduce the overscans 578 psVector *reduced = psVectorAlloc(pixels->n, PS_TYPE_F32); // Overscan for each row 579 psVector *ordinate = psVectorAlloc(pixels->n, PS_TYPE_F32); // Ordinate 580 psVector *mask = psVectorAlloc(pixels->n, PS_TYPE_VECTOR_MASK); // Mask for fitting 581 582 for (int i = 0; i < pixels->n; i++) 583 { 584 psVector *values = pixels->data[i]; // Vector with overscan values 585 if (values->n > 0) 586 { 587 mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0; 588 ordinate->data.F32[i] = 2.0 * (float)i / (float)pixels->n - 1.0; // Scale to [-1,1] 589 if (!psVectorStats(stats, values, NULL, NULL, 0)) 590 { 591 psError(PS_ERR_UNKNOWN, false, "failure to measure stats"); 592 goto escape; 593 } 594 reduced->data.F32[i] = psStatsGetValue(stats, statistic); 595 } 596 else 597 { 598 if (overscanOpts->fitType == PM_FIT_NONE) 599 { 600 psError(PS_ERR_UNKNOWN, true, "The overscan is not supplied for all points on the image, and no fit is requested.\n"); 601 goto escape; 602 } 603 else 604 { 605 // We'll fit this one out 606 mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 1; 607 } 608 } 609 } 610 // Smooth the reduced vector 611 if (overscanOpts->boxcar > 0) 612 { 613 psVector *smoothed = psVectorBoxcar(NULL, reduced, overscanOpts->boxcar); // Smoothed vector 614 psFree(reduced); 615 reduced = smoothed; 616 } 617 if (isfinite(overscanOpts->gauss) && overscanOpts->gauss > 0) 618 { 619 if (overscanOpts->boxcar > 0) 620 { 621 psWarning("Gaussian smoothing the boxcar smoothed overscan --- you asked for it."); 622 } 623 psVector *smoothed = psVectorSmooth(NULL, reduced, overscanOpts->gauss, SMOOTH_NSIGMA); 624 psFree(reduced); 625 reduced = smoothed; 626 } 627 628 // Fit the overscan, if required 629 psVector *fitted = NULL; // Fitted overscan values 630 switch (overscanOpts->fitType) 631 { 632 case PM_FIT_NONE: 633 // No fitting --- that's easy. 634 fitted = psMemIncrRefCounter(reduced); 635 break; 636 case PM_FIT_POLY_ORD: 637 if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order || 638 overscanOpts->poly->type != PS_POLYNOMIAL_ORD)) 639 { 640 psFree(overscanOpts->poly); 641 overscanOpts->poly = NULL; 642 } 643 if (!overscanOpts->poly) 644 { 645 overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, overscanOpts->order); 646 } 647 psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate); 648 fitted = psPolynomial1DEvalVector(overscanOpts->poly, ordinate); 649 break; 650 case PM_FIT_POLY_CHEBY: 651 if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order || 652 overscanOpts->poly->type != PS_POLYNOMIAL_CHEB)) 653 { 654 psFree(overscanOpts->poly); 655 overscanOpts->poly = NULL; 656 } 657 if (!overscanOpts->poly) 658 { 659 overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, overscanOpts->order); 660 } 661 psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate); 662 fitted = psPolynomial1DEvalVector(overscanOpts->poly, ordinate); 663 break; 664 case PM_FIT_SPLINE: 665 666 // XXX I don't think psSpline1D is up to scratch yet --- it has no mask, and it assumes 667 // a knot for every input point. it needs an argument like 'number of knots' for the 668 // output spline. EAM: still true 2023.01.22 669 670 // overscanOpts->spline = psVectorFitSpline1D(reduced, ordinate); 671 // fitted = psSpline1DEvalVector(overscanOpts->spline, ordinate); 672 psError(PS_ERR_UNKNOWN, true, "Spline overscan fitting is broken\n"); 673 break; 674 default: 675 psError(PS_ERR_UNKNOWN, true, "Unknown value for the fitting type: %d\n", overscanOpts->fitType); 525 676 goto escape; 526 } 527 reduced->data.F32[i] = psStatsGetValue(stats, statistic); 528 } else { 529 if (overscanOpts->fitType == PM_FIT_NONE) { 530 psError(PS_ERR_UNKNOWN, true, "The overscan is not supplied for all points on the image, and no fit is requested.\n"); 531 goto escape; 532 } else { 533 // We'll fit this one out 534 mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 1; 535 } 536 } 537 } 538 // Smooth the reduced vector 539 if (overscanOpts->boxcar > 0) { 540 psVector *smoothed = psVectorBoxcar(NULL, reduced, overscanOpts->boxcar); // Smoothed vector 677 } 678 679 if (chi2) 680 { 681 *chi2 = 0.0; // chi^2 (sort of) 682 for (int i = 0; i < reduced->n; i++) 683 { 684 *chi2 += PS_SQR(fitted->data.F32[i] - reduced->data.F32[i]); 685 } 686 } 687 541 688 psFree(reduced); 542 reduced = smoothed; 543 } 544 if (isfinite(overscanOpts->gauss) && overscanOpts->gauss > 0) { 545 if (overscanOpts->boxcar > 0) { 546 psWarning("Gaussian smoothing the boxcar smoothed overscan --- you asked for it."); 547 } 548 psVector *smoothed = psVectorSmooth(NULL, reduced, overscanOpts->gauss, SMOOTH_NSIGMA); 689 psFree(ordinate); 690 psFree(mask); 691 psFree(stats); 692 return fitted; 693 694 escape: 549 695 psFree(reduced); 550 reduced = smoothed; 551 } 552 553 // Fit the overscan, if required 554 psVector *fitted = NULL; // Fitted overscan values 555 switch (overscanOpts->fitType) { 556 case PM_FIT_NONE: 557 // No fitting --- that's easy. 558 fitted = psMemIncrRefCounter(reduced); 559 break; 560 case PM_FIT_POLY_ORD: 561 if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order || 562 overscanOpts->poly->type != PS_POLYNOMIAL_ORD)) { 563 psFree(overscanOpts->poly); 564 overscanOpts->poly = NULL; 565 } 566 if (! overscanOpts->poly) { 567 overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, overscanOpts->order); 568 } 569 psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate); 570 fitted = psPolynomial1DEvalVector(overscanOpts->poly, ordinate); 571 break; 572 case PM_FIT_POLY_CHEBY: 573 if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order || 574 overscanOpts->poly->type != PS_POLYNOMIAL_CHEB)) { 575 psFree(overscanOpts->poly); 576 overscanOpts->poly = NULL; 577 } 578 if (! overscanOpts->poly) { 579 overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, overscanOpts->order); 580 } 581 psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate); 582 fitted = psPolynomial1DEvalVector(overscanOpts->poly, ordinate); 583 break; 584 case PM_FIT_SPLINE: 585 586 // XXX I don't think psSpline1D is up to scratch yet --- it has no mask, and it assumes 587 // a knot for every input point. it needs an argument like 'number of knots' for the 588 // output spline. EAM: still true 2023.01.22 589 590 // overscanOpts->spline = psVectorFitSpline1D(reduced, ordinate); 591 // fitted = psSpline1DEvalVector(overscanOpts->spline, ordinate); 592 psError(PS_ERR_UNKNOWN, true, "Spline overscan fitting is broken\n"); 593 break; 594 default: 595 psError(PS_ERR_UNKNOWN, true, "Unknown value for the fitting type: %d\n", overscanOpts->fitType); 596 goto escape; 597 } 598 599 if (chi2) { 600 *chi2 = 0.0; // chi^2 (sort of) 601 for (int i = 0; i < reduced->n; i++) { 602 *chi2 += PS_SQR(fitted->data.F32[i] - reduced->data.F32[i]); 603 } 604 } 605 606 psFree(reduced); 607 psFree(ordinate); 608 psFree(mask); 609 psFree(stats); 610 return fitted; 611 612 escape: 613 psFree(reduced); 614 psFree(ordinate); 615 psFree(mask); 616 psFree(stats); 617 return NULL; 696 psFree(ordinate); 697 psFree(mask); 698 psFree(stats); 699 return NULL; 618 700 } 619 701 … … 621 703 // the reduced overscan vector with the 0-order element from the polynomial fit. Not clear is 622 704 // adding any useful information 623 bool pmOverscanUpdateHeader (pmHDU *hdu, pmOverscanStatOptions *overscanOpts, float chi2) { 624 625 psString comment = NULL; // Comment to add 626 627 switch (overscanOpts->fitType) { 628 case PM_FIT_POLY_ORD: 629 case PM_FIT_POLY_CHEBY: { 630 psStringAppend(&comment, "Overscan fit (chi2: %.2f): ", chi2); 631 psPolynomial1D *poly = overscanOpts->poly; // The polynomial 632 for (int i = 0; i < poly->nX; i++) { 633 psStringAppend(&comment, "%.1f ", poly->coeff[i]); 634 } 635 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); 636 psFree(comment); 637 comment = NULL; 638 639 // write metadata header value 640 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan value", poly->coeff[0]); 641 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", poly->coeffErr[0]); 642 break; 643 } 644 case PM_FIT_SPLINE: { 645 /* 646 psSpline1D *spline = overscanOpts->spline; // The spline 647 for (int i = 0; i < spline->n; i++) { 648 psStringAppend(&comment, "Overscan fit (chi2: %.2f) %d:", chi2, i); 649 psPolynomial1D *poly = spline->spline[i]; // i-th polynomial 650 for (int j = 0; j < poly->nX; j++) { 651 psStringAppend(&comment, "%.1f ", poly->coeff[i]); 652 } 653 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, 654 comment, ""); 655 psFree(comment); 656 comment = NULL; 657 } 658 */ 659 // write metadata header value 660 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, 661 "Overscan value", NAN); 662 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, 663 "Overscan stdev", NAN); 664 break; 665 } 666 case PM_FIT_NONE: 667 break; 668 default: 669 psAbort("Should never get here!!!\n"); 670 } 671 return true; 705 bool pmOverscanUpdateHeader(pmHDU *hdu, pmOverscanStatOptions *overscanOpts, float chi2) 706 { 707 708 psString comment = NULL; // Comment to add 709 710 switch (overscanOpts->fitType) 711 { 712 case PM_FIT_POLY_ORD: 713 case PM_FIT_POLY_CHEBY: 714 { 715 psStringAppend(&comment, "Overscan fit (chi2: %.2f): ", chi2); 716 psPolynomial1D *poly = overscanOpts->poly; // The polynomial 717 for (int i = 0; i < poly->nX; i++) 718 { 719 psStringAppend(&comment, "%.1f ", poly->coeff[i]); 720 } 721 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); 722 psFree(comment); 723 comment = NULL; 724 725 // write metadata header value 726 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan value", poly->coeff[0]); 727 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", poly->coeffErr[0]); 728 break; 729 } 730 case PM_FIT_SPLINE: 731 { 732 /* 733 psSpline1D *spline = overscanOpts->spline; // The spline 734 for (int i = 0; i < spline->n; i++) { 735 psStringAppend(&comment, "Overscan fit (chi2: %.2f) %d:", chi2, i); 736 psPolynomial1D *poly = spline->spline[i]; // i-th polynomial 737 for (int j = 0; j < poly->nX; j++) { 738 psStringAppend(&comment, "%.1f ", poly->coeff[i]); 739 } 740 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, 741 comment, ""); 742 psFree(comment); 743 comment = NULL; 744 } 745 */ 746 // write metadata header value 747 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, 748 "Overscan value", NAN); 749 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, 750 "Overscan stdev", NAN); 751 break; 752 } 753 case PM_FIT_NONE: 754 break; 755 default: 756 psAbort("Should never get here!!!\n"); 757 } 758 return true; 672 759 } 673 760 674 761 // generate stats of overscan vector for header 675 762 // reduced: 1D vector with overscan stats 676 bool pmOverscanUpdateHeaderVector (pmReadout *input, pmHDU *hdu, pmOverscanOptions *overscanOpts, psVector *reduced) { 677 678 psString comment = NULL; // Comment to add 679 psStats *vectorStats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV); 680 if (!psVectorStats (vectorStats, reduced, NULL, NULL, 0)) { 681 psError(PS_ERR_UNKNOWN, false, "failure to measure stats"); 682 return false; 683 } 684 psStringAppend(&comment, "Mean Overscan value: %f", vectorStats->sampleMean); 685 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); 686 psFree(comment); 687 688 // write metadata header value 689 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan mean", vectorStats->sampleMean); 690 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", vectorStats->sampleStdev); 691 692 // EAM 2022.03.29 : if the calculated overscan value is below the threshold, 693 // declare the readout dead and mask 694 695 if ((vectorStats->sampleMean < overscanOpts->minValid) || (vectorStats->sampleMean > overscanOpts->maxValid)) { 696 fprintf (stderr, "bad overscan (2) %f, masking readout\n", vectorStats->sampleMean); 697 psImage *mask = input->mask; 698 for (int y = 0; y < mask->numRows; y++) { 699 for (int x = 0; x < mask->numCols; x++) { 700 mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= overscanOpts->maskVal; 701 } 702 } 703 } 704 705 psFree (vectorStats); 706 return true; 763 bool pmOverscanUpdateHeaderVector(pmReadout *input, pmHDU *hdu, pmOverscanOptions *overscanOpts, psVector *reduced) 764 { 765 766 psString comment = NULL; // Comment to add 767 psStats *vectorStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV); 768 if (!psVectorStats(vectorStats, reduced, NULL, NULL, 0)) 769 { 770 psError(PS_ERR_UNKNOWN, false, "failure to measure stats"); 771 return false; 772 } 773 psStringAppend(&comment, "Mean Overscan value: %f", vectorStats->sampleMean); 774 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); 775 psFree(comment); 776 777 // write metadata header value 778 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_VAL", PS_META_REPLACE, "Overscan mean", vectorStats->sampleMean); 779 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "OVER_SIG", PS_META_REPLACE, "Overscan stdev", vectorStats->sampleStdev); 780 781 // EAM 2022.03.29 : if the calculated overscan value is below the threshold, 782 // declare the readout dead and mask 783 784 if ((vectorStats->sampleMean < overscanOpts->minValid) || (vectorStats->sampleMean > overscanOpts->maxValid)) 785 { 786 fprintf(stderr, "bad overscan (2) %f, masking readout\n", vectorStats->sampleMean); 787 psImage *mask = input->mask; 788 for (int y = 0; y < mask->numRows; y++) 789 { 790 for (int x = 0; x < mask->numCols; x++) 791 { 792 mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= overscanOpts->maskVal; 793 } 794 } 795 } 796 797 psFree(vectorStats); 798 return true; 707 799 } 708
Note:
See TracChangeset
for help on using the changeset viewer.
