Changeset 42679
- Timestamp:
- May 21, 2024, 8:50:38 PM (2 years ago)
- Location:
- branches/2dbias
- Files:
-
- 8 edited
-
ippconfig/gpc1/format_20100723.config (modified) (2 diffs)
-
ippconfig/gpc1/ppImage.config (modified) (1 diff)
-
ippconfig/recipes/ppImage.config (modified) (1 diff)
-
ppImage/src/ppImage.h (modified) (1 diff)
-
ppImage/src/ppImageDetrendPattern.c (modified) (16 diffs)
-
ppImage/src/ppImageDetrendReadout.c (modified) (12 diffs)
-
psModules/src/detrend/pmOverscan.c (modified) (5 diffs)
-
psModules/src/detrend/pmOverscan.h (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
branches/2dbias/ippconfig/gpc1/format_20100723.config
r42666 r42679 166 166 # 2D bias subtraction requires ppImage.config OVERSCAN.2D to be TRUE 167 167 CELL.BIASSEC.SOURCE STR VALUE 168 CELL.BIASSEC STR [591:624,1:598],[1: 590,598:608]168 CELL.BIASSEC STR [591:624,1:598],[1:624,599:608] 169 169 # 170 170 # a single region will be used for constant and 1D bias subtraction … … 174 174 END 175 175 176 OVERSCAN.2D.SUBSET METADATA 177 XY00 BOOL TRUE 178 XY01 BOOL TRUE 179 XY02 BOOL TRUE 180 XY03 BOOL TRUE 181 XY04 BOOL TRUE 182 XY05 BOOL TRUE 183 XY06 BOOL TRUE 184 XY07 BOOL TRUE 185 XY10 BOOL TRUE 186 XY11 BOOL TRUE 187 XY12 BOOL TRUE 188 XY13 BOOL TRUE 189 XY14 BOOL TRUE 190 XY15 BOOL TRUE 191 XY16 BOOL TRUE 192 XY17 BOOL TRUE 193 XY20 BOOL TRUE 194 XY21 BOOL TRUE 195 XY22 BOOL TRUE 196 XY23 BOOL TRUE 197 XY24 BOOL TRUE 198 XY25 BOOL TRUE 199 XY26 BOOL TRUE 200 XY27 BOOL TRUE 201 XY30 STR 1111100011111000111100001111000011110000111110001111000011110000 202 XY31 BOOL TRUE 203 XY32 BOOL TRUE 204 XY33 BOOL TRUE 205 XY34 BOOL TRUE 206 XY35 BOOL TRUE 207 XY36 BOOL TRUE 208 XY37 BOOL TRUE 209 XY40 BOOL TRUE 210 XY41 BOOL TRUE 211 XY42 BOOL TRUE 212 XY43 BOOL TRUE 213 XY44 BOOL TRUE 214 XY45 BOOL TRUE 215 XY46 BOOL TRUE 216 XY47 BOOL TRUE 217 XY50 BOOL TRUE 218 XY51 BOOL TRUE 219 XY52 BOOL TRUE 220 XY53 BOOL TRUE 221 XY54 BOOL TRUE 222 XY55 BOOL TRUE 223 XY56 BOOL TRUE 224 XY57 BOOL TRUE 225 XY60 BOOL TRUE 226 XY61 BOOL TRUE 227 XY62 BOOL TRUE 228 XY63 BOOL TRUE 229 XY64 BOOL TRUE 230 XY65 BOOL TRUE 231 XY66 BOOL TRUE 232 XY67 BOOL TRUE 233 XY70 BOOL TRUE 234 XY71 BOOL TRUE 235 XY72 BOOL TRUE 236 XY73 BOOL TRUE 237 XY74 BOOL TRUE 238 XY75 BOOL TRUE 239 XY76 BOOL TRUE 240 XY77 BOOL TRUE 241 END 176 242 177 243 # How to translate PS concepts into FITS headers -
branches/2dbias/ippconfig/gpc1/ppImage.config
r42623 r42679 9 9 OVERSCAN.STAT STR MEDIAN 10 10 OVERSCAN.BOXCAR S32 3 11 12 OVERSCAN.2D.SUBSET METADATA 13 XY00 BOOL TRUE 14 XY01 BOOL TRUE 15 XY02 BOOL TRUE 16 XY03 BOOL TRUE 17 XY04 BOOL TRUE 18 XY05 BOOL TRUE 19 XY06 BOOL TRUE 20 XY07 BOOL TRUE 21 XY10 BOOL TRUE 22 XY11 BOOL TRUE 23 XY12 BOOL TRUE 24 XY13 BOOL TRUE 25 XY14 BOOL TRUE 26 XY15 BOOL TRUE 27 XY16 BOOL TRUE 28 XY17 BOOL TRUE 29 XY20 BOOL TRUE 30 XY21 BOOL TRUE 31 XY22 BOOL TRUE 32 XY23 BOOL TRUE 33 XY24 BOOL TRUE 34 XY25 BOOL TRUE 35 XY26 BOOL TRUE 36 XY27 BOOL TRUE 37 XY30 BOOL TRUE 38 XY31 BOOL TRUE 39 XY32 BOOL TRUE 40 XY33 BOOL TRUE 41 XY34 BOOL TRUE 42 XY35 BOOL TRUE 43 XY36 BOOL TRUE 44 XY37 BOOL TRUE 45 XY40 BOOL TRUE 46 XY41 BOOL TRUE 47 XY42 BOOL TRUE 48 XY43 BOOL TRUE 49 XY44 BOOL TRUE 50 XY45 BOOL TRUE 51 XY46 BOOL TRUE 52 XY47 BOOL TRUE 53 XY50 BOOL TRUE 54 XY51 BOOL TRUE 55 XY52 BOOL TRUE 56 XY53 BOOL TRUE 57 XY54 BOOL TRUE 58 XY55 BOOL TRUE 59 XY56 BOOL TRUE 60 XY57 BOOL TRUE 61 XY60 BOOL TRUE 62 XY61 BOOL TRUE 63 XY62 BOOL TRUE 64 XY63 BOOL TRUE 65 XY64 BOOL TRUE 66 XY65 BOOL TRUE 67 XY66 BOOL TRUE 68 XY67 BOOL TRUE 69 XY70 BOOL TRUE 70 XY71 BOOL TRUE 71 XY72 BOOL TRUE 72 XY73 BOOL TRUE 73 XY74 BOOL TRUE 74 XY75 BOOL TRUE 75 XY76 BOOL TRUE 76 XY77 BOOL TRUE 77 END 11 78 12 79 # Normalization class -
branches/2dbias/ippconfig/recipes/ppImage.config
r42666 r42679 110 110 111 111 OVERSCAN.2D BOOL FALSE # use a 2D model for the overscan subtraction? 112 OVERSCAN.2D.SUBSET METADATA 113 END 112 114 113 115 # statistics to use to convert 2D top-overscan region to a vector -
branches/2dbias/ppImage/src/ppImage.h
r42382 r42679 188 188 189 189 bool ppImageDetrendFree(pmConfig *config, pmFPAview *view); 190 bool ppImageDoPatternForView(bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipeName, const char *recipeValue); 190 191 bool ppImageFringeFree(pmConfig *config, pmFPAview *view); 191 192 -
branches/2dbias/ppImage/src/ppImageDetrendPattern.c
r42382 r42679 5 5 #include "ppImage.h" 6 6 7 #define ESCAPE(STATUS,...) { \ 8 psError(PS_ERR_UNKNOWN, STATUS, __VA_ARGS__); \ 9 psFree(view); \ 10 return false; \ 11 } 12 13 static bool doPatternForView (bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipename, const char *recipevalue); 7 #define ESCAPE(STATUS, ...) \ 8 { \ 9 psError(PS_ERR_UNKNOWN, STATUS, __VA_ARGS__); \ 10 psFree(view); \ 11 return false; \ 12 } 13 14 bool ppImageDoPatternForView(bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipename, const char *recipevalue); 14 15 15 16 bool ppImageDetrendPatternRowApply(pmConfig *config, pmChip *chip, const pmFPAview *inputView, ppImageOptions *options); … … 26 27 assert(inputView->readout == -1); 27 28 28 if (!ppImageDetrendPatternRowApply (config, chip, inputView, options)) { 29 return false; 30 } 31 if (!ppImageDetrendPatternContinuityApply (config, chip, inputView, options)) { 32 return false; 33 } 34 if (!ppImageDetrendPatternCellApply (config, chip, inputView, options)) { 35 return false; 36 } 37 if (!ppImageDetrendPatternDeadCellsApply (config, chip, inputView, options)) { 38 return false; 39 } 40 return(true); 29 if (!ppImageDetrendPatternRowApply(config, chip, inputView, options)) 30 { 31 return false; 32 } 33 if (!ppImageDetrendPatternContinuityApply(config, chip, inputView, options)) 34 { 35 return false; 36 } 37 if (!ppImageDetrendPatternCellApply(config, chip, inputView, options)) 38 { 39 return false; 40 } 41 if (!ppImageDetrendPatternDeadCellsApply(config, chip, inputView, options)) 42 { 43 return false; 44 } 45 return (true); 41 46 } 42 47 … … 45 50 bool status; 46 51 47 if (!options->doPatternRow) return true; 48 52 if (!options->doPatternRow) 53 return true; 54 49 55 assert(inputView->chip != -1); 50 56 assert(inputView->cell == -1); … … 73 79 74 80 pmHDU *hdu = pmHDUFromChip(chip); 75 if (psMetadataLookupBool(&status, hdu->header, "PTRN_ROW")) { 76 psLogMsg("ppImage", PS_LOG_INFO, "Not performing row pattern correction as it has already been done."); 77 return true; 81 if (psMetadataLookupBool(&status, hdu->header, "PTRN_ROW")) 82 { 83 psLogMsg("ppImage", PS_LOG_INFO, "Not performing row pattern correction as it has already been done."); 84 return true; 78 85 } 79 86 … … 85 92 86 93 // grab the PATTERN.ROW.AMP file 87 pmFPAfile *PRAfile = psMetadataLookupPtr (&status, config->files, "PPIMAGE.PATTERN.ROW.AMP"); 88 if (!PRAfile) { 89 psLogMsg("ppImage", PS_LOG_INFO, "Pattern Row Amplitude file not found, applying to all (with limits from ROW.SUBSET) "); 90 } 91 94 pmFPAfile *PRAfile = psMetadataLookupPtr(&status, config->files, "PPIMAGE.PATTERN.ROW.AMP"); 95 if (!PRAfile) 96 { 97 psLogMsg("ppImage", PS_LOG_INFO, "Pattern Row Amplitude file not found, applying to all (with limits from ROW.SUBSET) "); 98 } 99 92 100 pmFPAview *view = pmFPAviewAlloc(0); // View for local processing 93 101 *view = *inputView; 94 102 95 103 pmCell *cell = NULL; 96 while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) { 97 // const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); 98 // const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); 99 // psWarning ("Looping through %s, %s\n", chipName, cellName); 100 101 if (!cell->process || !cell->file_exists) { 102 continue; 103 } 104 105 // this forces pmFPAfileRead of the PATTERN.ROW.AMP file (XXX but is this needed?) 106 if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) { 107 ESCAPE(false, "load failure for Cell"); 108 } 109 110 if (!cell->data_exists) { 111 continue; 112 } 113 114 if (cell->readouts->n > 1) { 115 psWarning ("Skipping Video Cell for ppImageDetrendPatternApply"); 116 continue; 117 } 118 119 // grab the corresponding cell 120 if (PRAfile) { 121 pmCell *PRAcell = pmFPAviewThisCell (view, PRAfile->fpa); 122 psAssert (PRAcell, "found Pattern Row Amplitude file, but not cell?"); 123 124 // find the nominal signal amplitude (check the ghost and/or crosstalk recipe file) 125 float amplitude = psMetadataLookupF32 (&status, PRAcell->analysis, "PTN.ROW.AMP"); 126 if (!status) amplitude = NAN; 127 128 // put the value on the science cell 129 psMetadataAddF32 (cell->analysis, PS_LIST_TAIL, "PTN.ROW.AMP", PS_META_REPLACE, "", amplitude); 130 } 131 132 bool doPattern = false; 133 if (!doPatternForView(&doPattern, config, chip, view, RECIPE_NAME, "PATTERN.ROW.SUBSET")) { 134 ESCAPE(false, "Unable to determine whether row pattern matching should be applied."); 135 } 136 if (!doPattern) continue; 137 138 // switch to test threaded version 139 if (true) { 140 // I need to allocate a view here to be freed by the 141 // called function below. 142 pmFPAview *myView = pmFPAviewAlloc(0); // View for local processing 143 *myView = *view; 144 145 // allocate a job, construct the arguments for this job 146 psThreadJob *job = psThreadJobAlloc("PPIMAGE_PATTERN_ROW_CELL"); 147 psArrayAdd(job->args, 1, config); 148 psArrayAdd(job->args, 1, input->fpa); 149 psArrayAdd(job->args, 1, chip); 150 psArrayAdd(job->args, 1, cell); 151 psArrayAdd(job->args, 1, myView); 152 psArrayAdd(job->args, 1, options); 153 if (!psThreadJobAddPending(job)) { 154 return false; 155 } 156 } else { 157 // bump the counter since it must be freed by the function below. 158 psMemIncrRefCounter (view); // View for local processing 159 if (!ppImageDetrendPatternApplyCell (config, input->fpa, chip, cell, view, options)) { 160 return false; 161 } 162 } 104 while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) 105 { 106 // const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); 107 // const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); 108 // psWarning ("Looping through %s, %s\n", chipName, cellName); 109 110 if (!cell->process || !cell->file_exists) 111 { 112 continue; 113 } 114 115 // this forces pmFPAfileRead of the PATTERN.ROW.AMP file (XXX but is this needed?) 116 if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) 117 { 118 ESCAPE(false, "load failure for Cell"); 119 } 120 121 if (!cell->data_exists) 122 { 123 continue; 124 } 125 126 if (cell->readouts->n > 1) 127 { 128 psWarning("Skipping Video Cell for ppImageDetrendPatternApply"); 129 continue; 130 } 131 132 // grab the corresponding cell 133 if (PRAfile) 134 { 135 pmCell *PRAcell = pmFPAviewThisCell(view, PRAfile->fpa); 136 psAssert(PRAcell, "found Pattern Row Amplitude file, but not cell?"); 137 138 // find the nominal signal amplitude (check the ghost and/or crosstalk recipe file) 139 float amplitude = psMetadataLookupF32(&status, PRAcell->analysis, "PTN.ROW.AMP"); 140 if (!status) 141 amplitude = NAN; 142 143 // put the value on the science cell 144 psMetadataAddF32(cell->analysis, PS_LIST_TAIL, "PTN.ROW.AMP", PS_META_REPLACE, "", amplitude); 145 } 146 147 bool doPattern = false; 148 if (!ppImageDoPatternForView(&doPattern, config, chip, view, RECIPE_NAME, "PATTERN.ROW.SUBSET")) 149 { 150 ESCAPE(false, "Unable to determine whether row pattern matching should be applied."); 151 } 152 if (!doPattern) 153 continue; 154 155 // switch to test threaded version 156 if (true) 157 { 158 // I need to allocate a view here to be freed by the 159 // called function below. 160 pmFPAview *myView = pmFPAviewAlloc(0); // View for local processing 161 *myView = *view; 162 163 // allocate a job, construct the arguments for this job 164 psThreadJob *job = psThreadJobAlloc("PPIMAGE_PATTERN_ROW_CELL"); 165 psArrayAdd(job->args, 1, config); 166 psArrayAdd(job->args, 1, input->fpa); 167 psArrayAdd(job->args, 1, chip); 168 psArrayAdd(job->args, 1, cell); 169 psArrayAdd(job->args, 1, myView); 170 psArrayAdd(job->args, 1, options); 171 if (!psThreadJobAddPending(job)) 172 { 173 return false; 174 } 175 } 176 else 177 { 178 // bump the counter since it must be freed by the function below. 179 psMemIncrRefCounter(view); // View for local processing 180 if (!ppImageDetrendPatternApplyCell(config, input->fpa, chip, cell, view, options)) 181 { 182 return false; 183 } 184 } 163 185 } 164 186 165 187 // wait here for the threaded jobs to finish 166 // if no threads are allocated, this 167 if (!psThreadPoolWait(true, true)) { 168 psError(PS_ERR_UNKNOWN, false, "Unable to apply bias correction."); 169 return false; 170 } 171 172 psMetadataAddBool(hdu->header, PS_LIST_TAIL, "PTRN_ROW",PS_META_REPLACE,"PATTERN.ROW correction applied",true); 188 // if no threads are allocated, this 189 if (!psThreadPoolWait(true, true)) 190 { 191 psError(PS_ERR_UNKNOWN, false, "Unable to apply bias correction."); 192 return false; 193 } 194 195 psMetadataAddBool(hdu->header, PS_LIST_TAIL, "PTRN_ROW", PS_META_REPLACE, "PATTERN.ROW correction applied", true); 173 196 psFree(view); 174 197 175 198 return true; 176 199 } … … 180 203 bool status; 181 204 182 if (!options->doPatternContinuity) return true; 205 if (!options->doPatternContinuity) 206 return true; 183 207 184 208 assert(inputView->chip != -1); … … 188 212 // see the comment for PATTERN.ROW; the same rules apply for PATTERN.CELL 189 213 190 int numCells = chip->cells->n; // Number of cells214 int numCells = chip->cells->n; // Number of cells 191 215 psVector *tweak = psVectorAlloc(numCells, PS_TYPE_U8); // Tweak cell? 192 pmFPAview *view = pmFPAviewAlloc(0); // View for local processing216 pmFPAview *view = pmFPAviewAlloc(0); // View for local processing 193 217 *view = *inputView; 194 218 195 219 pmHDU *hdu = pmHDUFromChip(chip); 196 if (psMetadataLookupBool(&status, hdu->header, "PTRN_CON")) { 197 psLogMsg("ppImage", PS_LOG_INFO, "Not performing cell continuity correction as it has already been done."); 198 return true; 220 if (psMetadataLookupBool(&status, hdu->header, "PTRN_CON")) 221 { 222 psLogMsg("ppImage", PS_LOG_INFO, "Not performing cell continuity correction as it has already been done."); 223 return true; 199 224 } 200 225 … … 202 227 psLogMsg("ppImage", PS_LOG_INFO, "Performing pattern continuity correction for %s\n", chipName); 203 228 204 for (int i = 0; i < chip->cells->n; i++) { 205 view->cell = i; 206 207 pmCell *cell = chip->cells->data[i]; // Cell of interest 208 209 if (cell->readouts->n > 1) { 210 psLogMsg("ppImage", PS_LOG_INFO, "Not performing cell continuity correction on video cell."); 211 continue; 212 } 213 214 bool doPattern = false; 215 if (!doPatternForView(&doPattern, config, chip, view, RECIPE_NAME, "PATTERN.CONTINUITY.SUBSET")) { 216 ESCAPE(false, "Unable to determine whether row pattern matching should be applied."); 217 } 218 219 if (doPattern) { 220 tweak->data.U8[i] = 0xFF; 221 } 229 for (int i = 0; i < chip->cells->n; i++) 230 { 231 view->cell = i; 232 233 pmCell *cell = chip->cells->data[i]; // Cell of interest 234 235 if (cell->readouts->n > 1) 236 { 237 psLogMsg("ppImage", PS_LOG_INFO, "Not performing cell continuity correction on video cell."); 238 continue; 239 } 240 241 bool doPattern = false; 242 if (!ppImageDoPatternForView(&doPattern, config, chip, view, RECIPE_NAME, "PATTERN.CONTINUITY.SUBSET")) 243 { 244 ESCAPE(false, "Unable to determine whether row pattern matching should be applied."); 245 } 246 247 if (doPattern) 248 { 249 tweak->data.U8[i] = 0xFF; 250 } 222 251 } 223 252 224 253 // Tweak the cells 225 254 if (!pmPatternContinuity(chip, tweak, options->patternCellBG, options->patternCellMean, 226 options->maskValue, options->darkMask,options->patternContinuityEdgeWidth)) { 227 psFree(tweak); 228 psFree(view); 229 return false; 255 options->maskValue, options->darkMask, options->patternContinuityEdgeWidth)) 256 { 257 psFree(tweak); 258 psFree(view); 259 return false; 230 260 } 231 261 psFree(tweak); 232 262 psFree(view); 233 263 234 psMetadataAddBool(hdu->header, PS_LIST_TAIL, "PTRN_CON", PS_META_REPLACE,"PATTERN.CONTINUITY correction applied",true);264 psMetadataAddBool(hdu->header, PS_LIST_TAIL, "PTRN_CON", PS_META_REPLACE, "PATTERN.CONTINUITY correction applied", true); 235 265 return true; 236 266 } … … 240 270 bool status; 241 271 242 if (!options->doPatternCell) return true; 272 if (!options->doPatternCell) 273 return true; 243 274 244 275 assert(inputView->chip != -1); … … 246 277 assert(inputView->readout == -1); 247 278 248 int numCells = chip->cells->n; // Number of cells279 int numCells = chip->cells->n; // Number of cells 249 280 psVector *tweak = psVectorAlloc(numCells, PS_TYPE_U8); // Tweak cell? 250 pmFPAview *view = pmFPAviewAlloc(0); // View for local processing281 pmFPAview *view = pmFPAviewAlloc(0); // View for local processing 251 282 *view = *inputView; 252 283 253 284 pmHDU *hdu = pmHDUFromChip(chip); 254 if (psMetadataLookupBool(&status, hdu->header, "PTRN_CEL")) { 255 psLogMsg("ppImage", PS_LOG_INFO, "Not performing cell pattern correction as it has already been done."); 256 return true; 285 if (psMetadataLookupBool(&status, hdu->header, "PTRN_CEL")) 286 { 287 psLogMsg("ppImage", PS_LOG_INFO, "Not performing cell pattern correction as it has already been done."); 288 return true; 257 289 } 258 290 … … 260 292 psLogMsg("ppImage", PS_LOG_INFO, "Performing cell pattern correction for %s\n", chipName); 261 293 262 for (int i = 0; i < chip->cells->n; i++) { 263 view->cell = i; 264 265 pmCell *cell = chip->cells->data[i]; // Cell of interest 266 267 if (cell->readouts->n > 1) { 268 psLogMsg("ppImage", PS_LOG_INFO, "Not performing cell pattern correction on video cell."); 269 continue; 270 } 271 272 bool doPattern = false; 273 if (!doPatternForView(&doPattern, config, chip, view, RECIPE_NAME, "PATTERN.CELL.SUBSET")) { 274 ESCAPE(false, "Unable to determine whether row pattern matching should be applied."); 275 } 276 if (doPattern) { 277 tweak->data.U8[i] = 0xFF; 278 } 294 for (int i = 0; i < chip->cells->n; i++) 295 { 296 view->cell = i; 297 298 pmCell *cell = chip->cells->data[i]; // Cell of interest 299 300 if (cell->readouts->n > 1) 301 { 302 psLogMsg("ppImage", PS_LOG_INFO, "Not performing cell pattern correction on video cell."); 303 continue; 304 } 305 306 bool doPattern = false; 307 if (!ppImageDoPatternForView(&doPattern, config, chip, view, RECIPE_NAME, "PATTERN.CELL.SUBSET")) 308 { 309 ESCAPE(false, "Unable to determine whether row pattern matching should be applied."); 310 } 311 if (doPattern) 312 { 313 tweak->data.U8[i] = 0xFF; 314 } 279 315 } 280 316 281 317 // Tweak the cells 282 318 if (!pmPatternCell(chip, tweak, options->patternCellBG, options->patternCellMean, 283 options->maskValue, options->darkMask)) { 284 psFree(tweak); 285 psFree(view); 286 return false; 319 options->maskValue, options->darkMask)) 320 { 321 psFree(tweak); 322 psFree(view); 323 return false; 287 324 } 288 325 psFree(tweak); 289 326 psFree(view); 290 327 291 psMetadataAddBool(hdu->header, PS_LIST_TAIL, "PTRN_CEL", PS_META_REPLACE,"PATTERN.CELL correction applied",true);328 psMetadataAddBool(hdu->header, PS_LIST_TAIL, "PTRN_CEL", PS_META_REPLACE, "PATTERN.CELL correction applied", true); 292 329 return true; 293 330 } 294 331 295 bool ppImageDetrendPatternDeadCellsMask (pmChip *chip, psImageMaskType maskVal) { 296 297 int numCells = chip->cells->n; // Number of cells 332 bool ppImageDetrendPatternDeadCellsMask(pmChip *chip, psImageMaskType maskVal) 333 { 334 335 int numCells = chip->cells->n; // Number of cells 298 336 299 337 // now mask bad cells 300 for (int i = 0; i < numCells; i++) { 301 pmCell *cell = chip->cells->data[i]; // Cell of interest 338 for (int i = 0; i < numCells; i++) 339 { 340 pmCell *cell = chip->cells->data[i]; // Cell of interest 302 341 pmReadout *ro = cell->readouts->data[0]; // Readout of interest 303 342 304 psImage *mask = ro->mask; // mask of interest 305 int numCols = mask->numCols, numRows = mask->numRows; // Size of image 306 307 for (int y = 0; y < numRows; y++) { 308 for (int x = 0; x < numCols; x++) { 309 mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= maskVal; 310 } 311 } 343 psImage *mask = ro->mask; // mask of interest 344 int numCols = mask->numCols, numRows = mask->numRows; // Size of image 345 346 for (int y = 0; y < numRows; y++) 347 { 348 for (int x = 0; x < numCols; x++) 349 { 350 mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= maskVal; 351 } 352 } 312 353 } 313 354 return true; 314 355 } 315 356 316 bool ppImageDetrendPatternDeadCellsXY42 (pmChip *chip, psImageMaskType maskVal) { 357 bool ppImageDetrendPatternDeadCellsXY42(pmChip *chip, psImageMaskType maskVal) 358 { 317 359 318 360 bool status; 319 361 320 362 // extract the MEDIAN_CELL_BACKGROUND values to check for problems 321 int numCells = chip->cells->n; // Number of cells 322 psVector *cellBackground = psVectorAllocEmpty (numCells, PS_DATA_F32); 323 324 for (int i = 0; i < numCells; i++) { 325 pmCell *cell = chip->cells->data[i];; 326 327 if (!cell->process || !cell->file_exists || !cell->data_exists) continue; 328 329 // select the HDU for this cell 330 pmHDU *cellHDU = pmHDUFromCell(cell); // HDU of interest 331 332 psF32 BackValue = psMetadataLookupF32(&status, cellHDU->header, "BACK_VAL"); 333 if (!status) continue; 334 335 psF32 BackError = psMetadataLookupF32(&status, cellHDU->header, "BACK_ERR"); 336 if (!status) continue; 337 338 if (BackValue == 0.0) continue; 339 if (BackError == 0.0) continue; 340 341 psVectorAppend (cellBackground, BackValue / BackError); 342 } 343 344 if (cellBackground->n < 0.375*numCells) { 345 // Chip is bad, mask the whole thing 346 ppImageDetrendPatternDeadCellsMask (chip, maskVal); 347 psFree (cellBackground); 348 return true; 363 int numCells = chip->cells->n; // Number of cells 364 psVector *cellBackground = psVectorAllocEmpty(numCells, PS_DATA_F32); 365 366 for (int i = 0; i < numCells; i++) 367 { 368 pmCell *cell = chip->cells->data[i]; 369 ; 370 371 if (!cell->process || !cell->file_exists || !cell->data_exists) 372 continue; 373 374 // select the HDU for this cell 375 pmHDU *cellHDU = pmHDUFromCell(cell); // HDU of interest 376 377 psF32 BackValue = psMetadataLookupF32(&status, cellHDU->header, "BACK_VAL"); 378 if (!status) 379 continue; 380 381 psF32 BackError = psMetadataLookupF32(&status, cellHDU->header, "BACK_ERR"); 382 if (!status) 383 continue; 384 385 if (BackValue == 0.0) 386 continue; 387 if (BackError == 0.0) 388 continue; 389 390 psVectorAppend(cellBackground, BackValue / BackError); 391 } 392 393 if (cellBackground->n < 0.375 * numCells) 394 { 395 // Chip is bad, mask the whole thing 396 ppImageDetrendPatternDeadCellsMask(chip, maskVal); 397 psFree(cellBackground); 398 return true; 349 399 } 350 400 351 401 // Second, calculate the median 352 psVectorSortInPlace (cellBackground);402 psVectorSortInPlace(cellBackground); 353 403 int midPt = cellBackground->n / 2.0; 354 float median = cellBackground->n % 2 ? cellBackground->data.F32[midPt] : 0.5 *(cellBackground->data.F32[midPt] + cellBackground->data.F32[midPt-1]);355 psFree (cellBackground);404 float median = cellBackground->n % 2 ? cellBackground->data.F32[midPt] : 0.5 * (cellBackground->data.F32[midPt] + cellBackground->data.F32[midPt - 1]); 405 psFree(cellBackground); 356 406 357 407 // XXX hardwired number 358 if (median < 3.0) { 359 // Chip is bad, mask the whole thing 360 ppImageDetrendPatternDeadCellsMask (chip, maskVal); 361 } 362 psFree (cellBackground); 408 if (median < 3.0) 409 { 410 // Chip is bad, mask the whole thing 411 ppImageDetrendPatternDeadCellsMask(chip, maskVal); 412 } 413 psFree(cellBackground); 363 414 return true; 364 415 } 365 416 366 417 // to apply the dead cell pattern, we need to transfer the pattern for this chip from the 367 // pmFPAfile for the pattern to the one for the image being processed. 418 // pmFPAfile for the pattern to the one for the image being processed. 368 419 bool ppImageDetrendPatternDeadCellsApply(pmConfig *config, pmChip *chip, const pmFPAview *inputView, ppImageOptions *options) 369 420 { 370 421 bool status; 371 422 372 if (!options->doPatternDeadCells) return true; 423 if (!options->doPatternDeadCells) 424 return true; 373 425 374 426 assert(inputView->chip != -1); … … 380 432 381 433 pmHDU *hdu = pmHDUFromChip(chip); 382 if (psMetadataLookupBool(&status, hdu->header, "PTRN_DED")) { 383 psLogMsg("ppImage", PS_LOG_INFO, "Not performing dead cell pattern correction as it has already been done."); 384 return true; 385 } 386 387 if (!strcmp (chipName, "XY40") || !strcmp (chipName, "XY42")) { 388 // special case : check for BACK_VAL / BACK_ERR > 3 or < 3 389 psLogMsg("ppImage", PS_LOG_INFO, "Using special case for XY40 and XY42"); 390 ppImageDetrendPatternDeadCellsXY42 (chip, options->blankMask); 391 return true; 434 if (psMetadataLookupBool(&status, hdu->header, "PTRN_DED")) 435 { 436 psLogMsg("ppImage", PS_LOG_INFO, "Not performing dead cell pattern correction as it has already been done."); 437 return true; 438 } 439 440 if (!strcmp(chipName, "XY40") || !strcmp(chipName, "XY42")) 441 { 442 // special case : check for BACK_VAL / BACK_ERR > 3 or < 3 443 psLogMsg("ppImage", PS_LOG_INFO, "Using special case for XY40 and XY42"); 444 ppImageDetrendPatternDeadCellsXY42(chip, options->blankMask); 445 return true; 392 446 } 393 447 394 448 pmFPAfile *pattern = psMetadataLookupPtr(&status, config->files, "PPIMAGE.PATTERN.DEAD.CELLS"); 395 if (!pattern) { 396 psLogMsg("ppImage", PS_LOG_INFO, "Pattern Dead Cells file not found, skipping"); 397 return true; 449 if (!pattern) 450 { 451 psLogMsg("ppImage", PS_LOG_INFO, "Pattern Dead Cells file not found, skipping"); 452 return true; 398 453 } 399 454 … … 401 456 *view = *inputView; 402 457 403 pmChip *patternChip = pmFPAviewThisChip (view, pattern->fpa);458 pmChip *patternChip = pmFPAviewThisChip(view, pattern->fpa); 404 459 405 460 // grab the pattern from the input pattern file chip 406 psImage *deadCellPattern = (psImage *) psMetadataLookupPtr (&status, patternChip->analysis, "PTN.DEAD.CELL"); 407 if (!deadCellPattern) { 461 psImage *deadCellPattern = (psImage *)psMetadataLookupPtr(&status, patternChip->analysis, "PTN.DEAD.CELL"); 462 if (!deadCellPattern) 463 { 408 464 psLogMsg("psModules.detrend", PS_LOG_DETAIL, "No DEAD CELL pattern for chip, skipping\n"); 409 psFree(view);410 return true;465 psFree(view); 466 return true; 411 467 } 412 468 413 469 // copy the pattern pointer to the chip of the image being processed 414 psMetadataAddImage (chip->analysis, PS_LIST_TAIL, "PTN.DEAD.CELL", PS_META_REPLACE, "", deadCellPattern);470 psMetadataAddImage(chip->analysis, PS_LIST_TAIL, "PTN.DEAD.CELL", PS_META_REPLACE, "", deadCellPattern); 415 471 416 472 // extract the MEDIAN_CELL_BACKGROUND values to check for problems 417 int numCells = chip->cells->n; // Number of cells418 psVector *cellBackground = psVectorAllocEmpty (numCells, PS_DATA_F32);473 int numCells = chip->cells->n; // Number of cells 474 psVector *cellBackground = psVectorAllocEmpty(numCells, PS_DATA_F32); 419 475 420 476 pmCell *cell = NULL; 421 while ((cell = pmFPAviewNextCell(view, chip->parent, 1)) != NULL) { 422 if (!cell->process || !cell->file_exists || !cell->data_exists) { 423 psVectorAppend (cellBackground, NAN); 424 continue; 425 } 426 427 // select the HDU for this cell 428 pmHDU *cellHDU = pmHDUFromCell(cell); // HDU of interest 429 430 psF32 value = psMetadataLookupF32(&status, cellHDU->header, "BACK_VAL"); 431 if (!status) { 432 psVectorAppend (cellBackground, NAN); 433 continue; 434 } 435 psVectorAppend (cellBackground, value); 477 while ((cell = pmFPAviewNextCell(view, chip->parent, 1)) != NULL) 478 { 479 if (!cell->process || !cell->file_exists || !cell->data_exists) 480 { 481 psVectorAppend(cellBackground, NAN); 482 continue; 483 } 484 485 // select the HDU for this cell 486 pmHDU *cellHDU = pmHDUFromCell(cell); // HDU of interest 487 488 psF32 value = psMetadataLookupF32(&status, cellHDU->header, "BACK_VAL"); 489 if (!status) 490 { 491 psVectorAppend(cellBackground, NAN); 492 continue; 493 } 494 psVectorAppend(cellBackground, value); 436 495 } 437 496 438 497 // match cellBackground pattern to registered patterns and mask as needed 439 if (!pmPatternDeadCells(chip, cellBackground, options->blankMask)) { 440 psFree(cellBackground); 441 psFree(view); 442 return false; 498 if (!pmPatternDeadCells(chip, cellBackground, options->blankMask)) 499 { 500 psFree(cellBackground); 501 psFree(view); 502 return false; 443 503 } 444 504 psFree(cellBackground); … … 449 509 } 450 510 451 static bool doPatternForView (bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipeName, const char *recipeValue) { 511 bool ppImageDoPatternForView(bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipeName, const char *recipeValue) 512 { 452 513 453 514 *doit = false; … … 456 517 457 518 doPattern = pmConfigRecipeValueByView(config, recipeName, recipeValue, chip->parent, view); 458 459 if (!doPattern) { 519 520 if (!doPattern) 521 { 460 522 psError(PS_ERR_UNKNOWN, false, "Unable to determine whether row pattern matching should be applied."); 461 523 return false; 462 524 } 463 if (doPattern->type == PS_DATA_BOOL) { 525 if (doPattern->type == PS_DATA_BOOL) 526 { 464 527 *doit = doPattern->data.B; 465 528 return true; 466 529 } 467 if (doPattern->type == PS_DATA_STRING) { 530 if (doPattern->type == PS_DATA_STRING) 531 { 468 532 // expect a string of the form "000110001001" with at least view->cell entries 469 533 char *string = doPattern->data.str; 470 if (strlen(string) < view->cell) { 471 psError(PS_ERR_UNKNOWN, true, "error in PATTERN.ROW.SUBSET chip string (too few elements %d)", (int) strlen(string)); 534 if (strlen(string) < view->cell) 535 { 536 psError(PS_ERR_UNKNOWN, true, "error in PATTERN.ROW.SUBSET chip string (too few elements %d)", (int)strlen(string)); 472 537 return false; 473 538 } 474 switch (string[view->cell]) { 475 case '0': 476 case 'f': 477 case 'F': 478 case 'n': 479 case 'N': 539 switch (string[view->cell]) 540 { 541 case '0': 542 case 'f': 543 case 'F': 544 case 'n': 545 case 'N': 480 546 *doit = false; 481 547 return true; 482 case '1':483 case 't':484 case 'T':485 case 'y':486 case 'Y':548 case '1': 549 case 't': 550 case 'T': 551 case 'y': 552 case 'Y': 487 553 *doit = true; 488 554 return true; 489 default:555 default: 490 556 psError(PS_ERR_UNKNOWN, true, "error in PATTERN.ROW.SUBSET chip string %s (unknown value %c))", string, string[view->cell]); 491 557 return false; … … 497 563 } 498 564 499 // thread safety : 500 bool ppImageDetrendPatternApplyCell (pmConfig *config, pmFPA *fpa, pmChip *chip, pmCell *cell, pmFPAview *view, ppImageOptions *options) { 501 565 // thread safety : 566 bool ppImageDetrendPatternApplyCell(pmConfig *config, pmFPA *fpa, pmChip *chip, pmCell *cell, pmFPAview *view, ppImageOptions *options) 567 { 568 502 569 // process each of the readouts 503 pmReadout *readout; // Readout from cell 504 while ((readout = pmFPAviewNextReadout (view, fpa, 1)) != NULL) { 505 if (!readout->data_exists) { 506 continue; 507 } 508 509 // Perform pattern correction 510 if (!pmPatternRow(readout, options->patternRowOrder, options->patternRowIter, 511 options->patternRowRej, options->patternRowThresh, options->patternRowMean, 512 options->patternRowStdev, options->maskValue, options->darkMask)) { 513 psFree (view); 514 return false; 515 } 516 } 517 psFree (view); // supplied view must be freeable 570 pmReadout *readout; // Readout from cell 571 while ((readout = pmFPAviewNextReadout(view, fpa, 1)) != NULL) 572 { 573 if (!readout->data_exists) 574 { 575 continue; 576 } 577 578 // Perform pattern correction 579 if (!pmPatternRow(readout, options->patternRowOrder, options->patternRowIter, 580 options->patternRowRej, options->patternRowThresh, options->patternRowMean, 581 options->patternRowStdev, options->maskValue, options->darkMask)) 582 { 583 psFree(view); 584 return false; 585 } 586 } 587 psFree(view); // supplied view must be freeable 518 588 return true; 519 589 } 520 590 521 psVector *ppImageDetrendPatternCellFailures(pmFPAfile *input, const pmFPAview *inputView) { 522 591 psVector *ppImageDetrendPatternCellFailures(pmFPAfile *input, const pmFPAview *inputView) 592 { 593 523 594 bool status = false; 524 595 pmCell *cell = NULL; 525 596 526 597 pmFPAview *view = pmFPAviewAlloc(0); // View for local processing 527 598 *view = *inputView; 528 599 529 600 // extract the MEDIAN_CELL_BACKGROUND values to check for problems 530 psVector *cellBackground = psVectorAllocEmpty (64, PS_DATA_F32); 531 while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) { 532 if (!cell->process || !cell->file_exists || !cell->data_exists) { 533 psVectorAppend (cellBackground, NAN); 534 continue; 535 } 536 537 // select the HDU for this cell 538 pmHDU *hdu = pmHDUFromCell(cell); // HDU of interest 539 540 psF32 value = psMetadataLookupF32(&status, hdu->header, "BACK_VAL"); 541 psVectorAppend (cellBackground, value); 601 psVector *cellBackground = psVectorAllocEmpty(64, PS_DATA_F32); 602 while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) 603 { 604 if (!cell->process || !cell->file_exists || !cell->data_exists) 605 { 606 psVectorAppend(cellBackground, NAN); 607 continue; 608 } 609 610 // select the HDU for this cell 611 pmHDU *hdu = pmHDUFromCell(cell); // HDU of interest 612 613 psF32 value = psMetadataLookupF32(&status, hdu->header, "BACK_VAL"); 614 psVectorAppend(cellBackground, value); 542 615 } 543 616 return cellBackground; -
branches/2dbias/ppImage/src/ppImageDetrendReadout.c
r42382 r42679 5 5 #include "ppImage.h" 6 6 7 #define ESCAPE(STATUS, ...) \ 8 { \ 9 psError(PS_ERR_UNKNOWN, STATUS, __VA_ARGS__); \ 10 psFree(view); \ 11 return false; \ 12 } 13 7 14 bool ppImageDetrendReadout(pmConfig *config, ppImageOptions *options, pmFPAview *view) 8 15 { 9 // psTimerStart("detrend.readout");16 // psTimerStart("detrend.readout"); 10 17 11 18 // construct a view for the detrend images (which have only one readout) … … 18 25 19 26 // Check that the gain is set (this is used by both pmReadoutGenerateMask and pmReadoutGenerateVariance) 20 { 21 float gain = psMetadataLookupF32(NULL, input->parent->concepts, "CELL.GAIN"); // Gain for cell 22 if (!isfinite(gain)) { 23 psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, RECIPE_NAME); // Recipe 24 psAssert(recipe, "Should be there!"); 25 bool override = psMetadataLookupBool(NULL, recipe, "GAIN.OVERRIDE"); // Override the bad gain? 26 if (override) { 27 psWarning("CELL.GAIN is not set for readout (%d,%d,%d) --- setting to unity.", view->chip, view->cell, view->readout); 28 psMetadataItem *item = psMetadataLookup(input->parent->concepts, "CELL.GAIN"); // Gain item 29 psAssert(item, "Should be there!"); 30 item->data.F32 = 1.0; 31 32 // for unity gain, there is no modification for the readnoise, note that it has (effectively) been updated 33 psMetadataRemoveKey(input->parent->concepts, "CELL.READNOISE.UPDATE"); 34 } else { 35 psWarning("CELL.GAIN is NAN for readout (%d,%d,%d), image will be masked.", view->chip, view->cell, view->readout); 36 } 37 } 27 { 28 float gain = psMetadataLookupF32(NULL, input->parent->concepts, "CELL.GAIN"); // Gain for cell 29 if (!isfinite(gain)) 30 { 31 psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, RECIPE_NAME); // Recipe 32 psAssert(recipe, "Should be there!"); 33 bool override = psMetadataLookupBool(NULL, recipe, "GAIN.OVERRIDE"); // Override the bad gain? 34 if (override) 35 { 36 psWarning("CELL.GAIN is not set for readout (%d,%d,%d) --- setting to unity.", view->chip, view->cell, view->readout); 37 psMetadataItem *item = psMetadataLookup(input->parent->concepts, "CELL.GAIN"); // Gain item 38 psAssert(item, "Should be there!"); 39 item->data.F32 = 1.0; 40 41 // for unity gain, there is no modification for the readnoise, note that it has (effectively) been updated 42 psMetadataRemoveKey(input->parent->concepts, "CELL.READNOISE.UPDATE"); 43 } 44 else 45 { 46 psWarning("CELL.GAIN is NAN for readout (%d,%d,%d), image will be masked.", view->chip, view->cell, view->readout); 47 } 48 } 38 49 } 39 50 // psLogMsg ("ppImage", 6, "check gain: %f sec\n", psTimerMark ("detrend.readout")); … … 43 54 bool hasVideo = false; 44 55 { 45 // XXX test for GPC1? or CAN_HAVE_VIDEO in camera config? 46 bool status = false; 47 if (!input) goto done_video_check; 48 if (!input->parent) goto done_video_check; 49 if (!input->parent->parent) goto done_video_check; 50 if (!input->parent->parent->hdu) goto done_video_check; 51 if (!input->parent->parent->hdu->header) goto done_video_check; 52 char *ptr = psMetadataLookupStr(&status,input->parent->parent->hdu->header,"CELLMODE"); 53 if (status) { 54 psLogMsg ("ppImage.detrend", PS_LOG_DETAIL, "VIDEO: %d %d %d\n",(int) options->hasVideo,(int) options->useVideoDark, (int) options->useVideoMask); 55 char *Vptr = strchr(ptr,'V'); 56 if (Vptr) { 57 hasVideo = options->hasVideo = true; 58 psLogMsg ("ppImage.detrend", PS_LOG_INFO, "VIDEO: %d %d %d\n",(int) options->hasVideo,(int) options->useVideoDark, (int) options->useVideoMask); 59 } 60 } 61 } 62 done_video_check: 56 // XXX test for GPC1? or CAN_HAVE_VIDEO in camera config? 57 bool status = false; 58 if (!input) 59 goto done_video_check; 60 if (!input->parent) 61 goto done_video_check; 62 if (!input->parent->parent) 63 goto done_video_check; 64 if (!input->parent->parent->hdu) 65 goto done_video_check; 66 if (!input->parent->parent->hdu->header) 67 goto done_video_check; 68 char *ptr = psMetadataLookupStr(&status, input->parent->parent->hdu->header, "CELLMODE"); 69 if (status) 70 { 71 psLogMsg("ppImage.detrend", PS_LOG_DETAIL, "VIDEO: %d %d %d\n", (int)options->hasVideo, (int)options->useVideoDark, (int)options->useVideoMask); 72 char *Vptr = strchr(ptr, 'V'); 73 if (Vptr) 74 { 75 hasVideo = options->hasVideo = true; 76 psLogMsg("ppImage.detrend", PS_LOG_INFO, "VIDEO: %d %d %d\n", (int)options->hasVideo, (int)options->useVideoDark, (int)options->useVideoMask); 77 } 78 } 79 } 80 done_video_check: 63 81 // psLogMsg ("ppImage", 6, "check video: %f sec\n", psTimerMark ("detrend.readout")); 64 82 … … 74 92 75 93 // Masking on the basis of pixel value needs to be done before anything else, so the values are pristine. 76 if (options->doMaskBuild) { 94 if (options->doMaskBuild) 95 { 77 96 psImageMaskType satMask = options->doMaskSat ? options->satMask : 0; 78 97 psImageMaskType lowMask = options->doMaskLow ? options->lowMask : 0; 79 if (!pmReadoutGenerateMask(input, satMask, lowMask)) { 80 psError(PS_ERR_UNKNOWN, false, "Unable to generate a mask."); 81 psFree(detview); 82 return false; 83 } 84 // psLogMsg ("ppImage", 6, "generate mask: %f sec\n", psTimerMark ("detrend.readout")); 98 if (!pmReadoutGenerateMask(input, satMask, lowMask)) 99 { 100 psError(PS_ERR_UNKNOWN, false, "Unable to generate a mask."); 101 psFree(detview); 102 return false; 103 } 104 // psLogMsg ("ppImage", 6, "generate mask: %f sec\n", psTimerMark ("detrend.readout")); 85 105 } 86 106 // apply the externally supplied mask to the input->mask pixels 87 if (options->doMask) { 88 pmReadout *mask; 89 if ((options->useVideoMask)&&(hasVideo)) { 90 mask = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.VIDEOMASK"); 91 } 92 else { 93 mask = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.MASK"); 94 } 95 pmMaskBadPixels(input, mask, options->maskValue); 96 // psLogMsg ("ppImage", 6, "apply mask: %f sec\n", psTimerMark ("detrend.readout")); 107 if (options->doMask) 108 { 109 pmReadout *mask; 110 if ((options->useVideoMask) && (hasVideo)) 111 { 112 mask = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.VIDEOMASK"); 113 } 114 else 115 { 116 mask = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.MASK"); 117 } 118 pmMaskBadPixels(input, mask, options->maskValue); 119 // psLogMsg ("ppImage", 6, "apply mask: %f sec\n", psTimerMark ("detrend.readout")); 97 120 } 98 121 … … 101 124 // extern bool ppImageBurntoolApply(pmConfig *, ppImageOptions *, pmFPAview *, pmReadout *); 102 125 ppImageBurntoolApply(config, options, view, input); 103 // psLogMsg ("ppImage", 6, "apply burntool: %f sec\n", psTimerMark ("detrend.readout")); 104 } 105 106 if (options->doMaskBurntool) { 107 if (options->doApplyBurntool) { 126 // psLogMsg ("ppImage", 6, "apply burntool: %f sec\n", psTimerMark ("detrend.readout")); 127 } 128 129 if (options->doMaskBurntool) 130 { 131 if (options->doApplyBurntool) 132 { 108 133 // build burntool mask from data input burntool table 109 ppImageBurntoolMaskFromTable(config,options,view,input); 110 } else { 134 ppImageBurntoolMaskFromTable(config, options, view, input); 135 } 136 else 137 { 111 138 // build burntool mask from data in the input image's fits extension 112 ppImageBurntoolMask(config, options,view,input);113 } 114 // psLogMsg ("ppImage", 6, "apply burntool mask: %f sec\n", psTimerMark ("detrend.readout"));139 ppImageBurntoolMask(config, options, view, input); 140 } 141 // psLogMsg ("ppImage", 6, "apply burntool mask: %f sec\n", psTimerMark ("detrend.readout")); 115 142 } 116 143 117 144 // Subtract the overscan 118 if (options->doOverscan) { 119 if (!pmOverscanSubtract (input, options->overscan)) { 120 psError(PS_ERR_UNKNOWN, false, "Unable to subtract overscan."); 121 psFree(detview); 122 return false; 123 } 124 // psLogMsg ("ppImage", 6, "subtract overscan: %f sec\n", psTimerMark ("detrend.readout")); 145 if (options->doOverscan) 146 { 147 bool doTwoDOverscan = false; 148 if (options->overscan->TwoD) 149 { 150 pmChip *chip = pmFPAfileThisChip(config->files, view, "PPIMAGE.INPUT"); 151 if (!ppImageDoPatternForView(&doTwoDOverscan, config, chip, view, RECIPE_NAME, "OVERSCAN.2D.SUBSET")) 152 { 153 ESCAPE(false, "Unable to determine whether 2D Overscan subtraction should be applied."); 154 } 155 } 156 if (!pmOverscanSubtract(input, options->overscan, doTwoDOverscan)) 157 { 158 psError(PS_ERR_UNKNOWN, false, "Unable to subtract overscan."); 159 psFree(detview); 160 return false; 161 } 162 // psLogMsg ("ppImage", 6, "subtract overscan: %f sec\n", psTimerMark ("detrend.readout")); 125 163 } 126 164 127 165 // measure the overscan-subtracted readoutBackground here (or subtract the overscan value?) 128 166 // XXX this is the measurements and should be independent of the pattern masking 129 if (options->doPatternDeadCells) { 130 psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); 131 psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); 132 psImageBackground (stats, NULL, input->image, NULL, 0xffff, rng); 133 134 // save this value somewhere 135 pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest 136 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "BACK_VAL", PS_META_REPLACE, "Median cell background", stats->robustMedian); 137 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "BACK_ERR", PS_META_REPLACE, "Stdev of cell background", stats->robustStdev); 138 psFree (stats); 139 psFree (rng); 167 if (options->doPatternDeadCells) 168 { 169 psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); 170 psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); 171 psImageBackground(stats, NULL, input->image, NULL, 0xffff, rng); 172 173 // save this value somewhere 174 pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest 175 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "BACK_VAL", PS_META_REPLACE, "Median cell background", stats->robustMedian); 176 psMetadataAddF32(hdu->header, PS_LIST_TAIL, "BACK_ERR", PS_META_REPLACE, "Stdev of cell background", stats->robustStdev); 177 psFree(stats); 178 psFree(rng); 140 179 } 141 180 142 181 // Non-linearity correction 143 if (options->doNonLin) { 144 if (!ppImageDetrendNonLinear(input,detview,config)) { 145 psError(PS_ERR_UNKNOWN, false, "Unable to correct NonLinearity"); 146 psFree(detview); 147 return(false); 148 } 149 // psLogMsg ("ppImage", 6, "nonlinear correction: %f sec\n", psTimerMark ("detrend.readout")); 182 if (options->doNonLin) 183 { 184 if (!ppImageDetrendNonLinear(input, detview, config)) 185 { 186 psError(PS_ERR_UNKNOWN, false, "Unable to correct NonLinearity"); 187 psFree(detview); 188 return (false); 189 } 190 // psLogMsg ("ppImage", 6, "nonlinear correction: %f sec\n", psTimerMark ("detrend.readout")); 150 191 } 151 192 // New Non-linearity correction (exclusive of the above) 152 if (options->doNewNonLin) { 153 if (!ppImageDetrendNewNonLinear(input, detview, config)) { 154 psError(PS_ERR_UNKNOWN, false, "Unable to correct Non-Linearity with new version (2023)"); 155 psFree(detview); 156 return(false); 157 } 158 // psLogMsg ("ppImage", 6, "nonlinear correction: %f sec\n", psTimerMark ("detrend.readout")); 193 if (options->doNewNonLin) 194 { 195 if (!ppImageDetrendNewNonLinear(input, detview, config)) 196 { 197 psError(PS_ERR_UNKNOWN, false, "Unable to correct Non-Linearity with new version (2023)"); 198 psFree(detview); 199 return (false); 200 } 201 // psLogMsg ("ppImage", 6, "nonlinear correction: %f sec\n", psTimerMark ("detrend.readout")); 159 202 } 160 203 161 204 // set up the dark and bias 162 pmCell *dark = NULL; // Multi-dark 163 pmReadout *oldDark = NULL; // Old-fashioned dark 164 pmReadout *bias = NULL; 165 if (options->doBias) { 205 pmCell *dark = NULL; // Multi-dark 206 pmReadout *oldDark = NULL; // Old-fashioned dark 207 pmReadout *bias = NULL; 208 if (options->doBias) 209 { 166 210 bias = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.BIAS"); 167 211 } 168 if (options->doDark) { 169 bool mdok; // Status of MD lookup 170 psMetadata *recipe = psMetadataLookupPtr (&mdok, config->recipes, RECIPE_NAME); 212 if (options->doDark) 213 { 214 bool mdok; // Status of MD lookup 215 psMetadata *recipe = psMetadataLookupPtr(&mdok, config->recipes, RECIPE_NAME); 171 216 assert(mdok && recipe); 172 217 173 if ((options->useVideoDark)&&(hasVideo)) { 174 if (psMetadataLookupBool(&mdok, recipe, "OLDDARK")) { 175 oldDark = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.VIDEODARK"); 176 } else { 177 dark = pmFPAfileThisCell(config->files, detview, "PPIMAGE.VIDEODARK"); 178 } 179 } 180 else { 181 if (psMetadataLookupBool(&mdok, recipe, "OLDDARK")) { 182 oldDark = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.DARK"); 183 } else { 184 dark = pmFPAfileThisCell(config->files, detview, "PPIMAGE.DARK"); 185 } 186 } 218 if ((options->useVideoDark) && (hasVideo)) 219 { 220 if (psMetadataLookupBool(&mdok, recipe, "OLDDARK")) 221 { 222 oldDark = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.VIDEODARK"); 223 } 224 else 225 { 226 dark = pmFPAfileThisCell(config->files, detview, "PPIMAGE.VIDEODARK"); 227 } 228 } 229 else 230 { 231 if (psMetadataLookupBool(&mdok, recipe, "OLDDARK")) 232 { 233 oldDark = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.DARK"); 234 } 235 else 236 { 237 dark = pmFPAfileThisCell(config->files, detview, "PPIMAGE.DARK"); 238 } 239 } 187 240 } 188 241 189 242 // Bias and temperature-independent-dark subtraction are merged. 190 if (options->doBias) { 191 if (!pmBiasSubtract(input, bias, oldDark, view)) { 243 if (options->doBias) 244 { 245 if (!pmBiasSubtract(input, bias, oldDark, view)) 246 { 192 247 psError(PS_ERR_UNKNOWN, false, "Unable to subtract bias."); 193 248 psFree(detview); 194 249 return false; 195 250 } 196 // psLogMsg ("ppImage", 6, "apply bias: %f sec\n", psTimerMark ("detrend.readout"));197 } 198 251 // psLogMsg ("ppImage", 6, "apply bias: %f sec\n", psTimerMark ("detrend.readout")); 252 } 253 199 254 // Weight on the basis of pixel value needs to be done after the overscan has been subtracted 200 if (options->doVarianceBuild) { 255 if (options->doVarianceBuild) 256 { 201 257 // create the target mask and variance images 202 258 psImage *noiseImage = NULL; 203 if (options->doNoiseMap) { 259 if (options->doNoiseMap) 260 { 204 261 // XXX convert the noiseMap image to a binned image 205 262 pmReadout *noiseMap = NULL; 206 263 noiseMap = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.NOISEMAP"); 207 noiseImage = psImageCopy (NULL, input->image, PS_TYPE_F32);208 psImageInit (noiseImage, 0.0);264 noiseImage = psImageCopy(NULL, input->image, PS_TYPE_F32); 265 psImageInit(noiseImage, 0.0); 209 266 210 267 // XXX this works, but is not really quite right: the model shoud include the 211 268 // offset information, we are not really getting exactly the right mapping from the 212 269 // original file. 213 // CZW 2012-03-21: I do not believe this is true anymore. In any case, this seems to be what214 // ppImageReplaceBackground does to do sky subtraction.270 // CZW 2012-03-21: I do not believe this is true anymore. In any case, this seems to be what 271 // ppImageReplaceBackground does to do sky subtraction. 215 272 psImageBinning *binning = psImageBinningAlloc(); 216 273 binning->nXruff = noiseMap->image->numCols; … … 220 277 psImageBinningSetScale(binning, PS_IMAGE_BINNING_LEFT); 221 278 222 psImageUnbin (noiseImage, noiseMap->image, binning); 223 psFree (binning); 224 } 225 226 if (!pmReadoutGenerateVariance(input, noiseImage, true)) { 227 psError(PS_ERR_UNKNOWN, false, "Unable to generate a variance image."); 228 psFree(detview); 229 return false; 230 } 231 psFree (noiseImage); 232 // psLogMsg ("ppImage", 6, "generate variance: %f sec\n", psTimerMark ("detrend.readout")); 233 } 234 235 if (options->doDark && dark) { 236 if (!pmDarkApply(input, dark, options->darkMask)) { 279 psImageUnbin(noiseImage, noiseMap->image, binning); 280 psFree(binning); 281 } 282 283 if (!pmReadoutGenerateVariance(input, noiseImage, true)) 284 { 285 psError(PS_ERR_UNKNOWN, false, "Unable to generate a variance image."); 286 psFree(detview); 287 return false; 288 } 289 psFree(noiseImage); 290 // psLogMsg ("ppImage", 6, "generate variance: %f sec\n", psTimerMark ("detrend.readout")); 291 } 292 293 if (options->doDark && dark) 294 { 295 if (!pmDarkApply(input, dark, options->darkMask)) 296 { 237 297 psError(PS_ERR_UNKNOWN, false, "Unable to subtract dark."); 238 298 psFree(detview); 239 299 return false; 240 300 } 241 // psLogMsg ("ppImage", 6, "apply dark: %f sec\n", psTimerMark ("detrend.readout")); 242 } 243 244 if (options->doRemnance) { 301 // psLogMsg ("ppImage", 6, "apply dark: %f sec\n", psTimerMark ("detrend.readout")); 302 } 303 304 if (options->doRemnance) 305 { 245 306 if (!pmRemnance(input, options->maskValue, options->lowMask, 246 options->remnanceSize, options->remnanceThresh)) { 307 options->remnanceSize, options->remnanceThresh)) 308 { 247 309 psError(PS_ERR_UNKNOWN, false, "Unable to mask remnance."); 248 310 psFree(detview); 249 311 return false; 250 312 } 251 // psLogMsg ("ppImage", 6, "mask remnance: %f sec\n", psTimerMark ("detrend.readout"));313 // psLogMsg ("ppImage", 6, "mask remnance: %f sec\n", psTimerMark ("detrend.readout")); 252 314 } 253 315 254 316 // Shutter correction 255 if (options->doShutter) { 317 if (options->doShutter) 318 { 256 319 pmReadout *shutter = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.SHUTTER"); 257 if (!pmShutterCorrectionApply(input, shutter, pmConfigMaskGet("FLAT", config))) { 258 psFree(detview); 259 return false; 260 } 261 // psLogMsg ("ppImage", 6, "shutter correction: %f sec\n", psTimerMark ("detrend.readout")); 320 if (!pmShutterCorrectionApply(input, shutter, pmConfigMaskGet("FLAT", config))) 321 { 322 psFree(detview); 323 return false; 324 } 325 // psLogMsg ("ppImage", 6, "shutter correction: %f sec\n", psTimerMark ("detrend.readout")); 262 326 } 263 327 264 328 // Flat-field correction (no options used?) 265 if (options->doFlat) { 329 if (options->doFlat) 330 { 266 331 pmReadout *flat = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.FLAT"); 267 if (!pmFlatField(input, flat, options->flatMask)) { 268 psFree(detview); 269 return false; 270 } 271 // psLogMsg ("ppImage", 6, "apply flat: %f sec\n", psTimerMark ("detrend.readout")); 272 } 273 274 /* // Pattern noise correction */ 275 /* if (options->doPattern) { */ 276 /* if (!pmPatternRow(input, options->patternOrder, options->patternIter, options->patternRej, */ 277 /* options->patternThresh, options->patternMean, options->patternStdev, */ 278 /* options->maskValue, options->darkMask)) { */ 279 /* psFree(detview); */ 280 /* return false; */ 281 /* } */ 282 /* } */ 332 if (!pmFlatField(input, flat, options->flatMask)) 333 { 334 psFree(detview); 335 return false; 336 } 337 // psLogMsg ("ppImage", 6, "apply flat: %f sec\n", psTimerMark ("detrend.readout")); 338 } 339 340 /* // Pattern noise correction */ 341 /* if (options->doPattern) { */ 342 /* if (!pmPatternRow(input, options->patternOrder, options->patternIter, options->patternRej, */ 343 /* options->patternThresh, options->patternMean, options->patternStdev, */ 344 /* options->maskValue, options->darkMask)) { */ 345 /* psFree(detview); */ 346 /* return false; */ 347 /* } */ 348 /* } */ 283 349 284 350 // Normalization by a single (known) constant 285 bool mdok; // Status of MD lookup351 bool mdok; // Status of MD lookup 286 352 float norm = psMetadataLookupF32(&mdok, config->arguments, "NORMALIZATION"); 287 if (mdok && isfinite(norm) && norm != 1.0) { 353 if (mdok && isfinite(norm) && norm != 1.0) 354 { 288 355 pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest 289 psString comment = NULL; // Comment to add356 psString comment = NULL; // Comment to add 290 357 psStringAppend(&comment, "Normalization: %f", norm); 291 358 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); … … 293 360 294 361 psBinaryOp(input->image, input->image, "*", psScalarAlloc(norm, PS_TYPE_F32)); 295 // psLogMsg ("ppImage", 6, "renormalize: %f sec\n", psTimerMark ("detrend.readout"));296 } 297 298 # if (1)362 // psLogMsg ("ppImage", 6, "renormalize: %f sec\n", psTimerMark ("detrend.readout")); 363 } 364 365 #if (1) 299 366 // Normalization by per-class values 300 367 psMetadata *normlist = psMetadataLookupMetadata(&mdok, config->arguments, "NORMALIZATION.TABLE"); 301 if (normlist) { 368 if (normlist) 369 { 302 370 pmFPAfile *inputFile = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.INPUT"); 303 371 304 372 // get the menu of class IDs 305 373 psMetadata *menu = psMetadataLookupMetadata(&mdok, inputFile->camera, "CLASSID"); 306 if (!menu) { 374 if (!menu) 375 { 307 376 psError(PS_ERR_IO, false, "Unable to find CLASSID metadata in camera configuration"); 308 377 psFree(detview); … … 311 380 // get the rule for class_id for the desired class 312 381 const char *rule = psMetadataLookupStr(&mdok, menu, options->normClass); 313 if (!rule) { 382 if (!rule) 383 { 314 384 psError(PS_ERR_IO, false, "Unable to find NORM.CLASS value %s in CLASSID in camera configuration", options->normClass); 315 385 psFree(detview); … … 318 388 // get the class_id from the rule 319 389 char *classID = pmFPAfileNameFromRule(rule, inputFile, view); 320 if (!classID) { 390 if (!classID) 391 { 321 392 psError(PS_ERR_IO, false, "error converting CLASSID rule %s to name\n", rule); 322 393 psFree(detview); … … 325 396 326 397 // get normalization from the class_id 327 float norm = psMetadataLookupF32 (&mdok, normlist, classID); 328 if (!mdok) { 398 float norm = psMetadataLookupF32(&mdok, normlist, classID); 399 if (!mdok) 400 { 329 401 psError(PS_ERR_IO, false, "failed to find class ID %s in normalization table\n", classID); 330 402 psFree(detview); … … 333 405 334 406 pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest 335 psString comment = NULL; // Comment to add407 psString comment = NULL; // Comment to add 336 408 psStringAppend(&comment, "Normalization: %f", norm); 337 409 psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, ""); … … 341 413 psBinaryOp(input->image, input->image, "*", psScalarAlloc(norm, PS_TYPE_F32)); 342 414 343 psFree (classID); 344 345 // psLogMsg ("ppImage", 6, "renormalize by class: %f sec\n", psTimerMark ("detrend.readout")); 346 } 347 # endif 348 349 if (options->doFringe) { 415 psFree(classID); 416 417 // psLogMsg ("ppImage", 6, "renormalize by class: %f sec\n", psTimerMark ("detrend.readout")); 418 } 419 #endif 420 421 if (options->doFringe) 422 { 350 423 pmCell *fringe = pmFPAfileThisCell(config->files, detview, "PPIMAGE.FRINGE"); 351 if (!ppImageDetrendFringeMeasure(input, fringe, false, options)) { 352 psFree(detview); 353 return false; 354 } 355 // psLogMsg ("ppImage", 6, "measure fringe: %f sec\n", psTimerMark ("detrend.readout")); 424 if (!ppImageDetrendFringeMeasure(input, fringe, false, options)) 425 { 426 psFree(detview); 427 return false; 428 } 429 // psLogMsg ("ppImage", 6, "measure fringe: %f sec\n", psTimerMark ("detrend.readout")); 356 430 } 357 431 -
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 -
branches/2dbias/psModules/src/detrend/pmOverscan.h
r42664 r42679 81 81 // bool pmOverscanUpdateHeader (pmHDU *hdu, pmOverscanOptions *overscanOpts, float chi2); 82 82 83 bool pmOverscanSubtract (pmReadout *input, pmOverscanOptions *overscanOpts );83 bool pmOverscanSubtract (pmReadout *input, pmOverscanOptions *overscanOpts, bool doTwoDOverscan); 84 84 85 85 /// @}
Note:
See TracChangeset
for help on using the changeset viewer.
