IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 42679


Ignore:
Timestamp:
May 21, 2024, 8:50:38 PM (2 years ago)
Author:
hgao
Message:

implement 2D overscan subtraction per cell as configured in OVERSCAN.2D.SUBSET

Location:
branches/2dbias
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • branches/2dbias/ippconfig/gpc1/format_20100723.config

    r42666 r42679  
    166166# 2D bias subtraction requires ppImage.config OVERSCAN.2D to be TRUE
    167167               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]
    169169#
    170170# a single region will be used for constant and 1D bias subtraction
     
    174174END
    175175
     176OVERSCAN.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
     241END
    176242
    177243# How to translate PS concepts into FITS headers
  • branches/2dbias/ippconfig/gpc1/ppImage.config

    r42623 r42679  
    99OVERSCAN.STAT           STR     MEDIAN
    1010OVERSCAN.BOXCAR         S32     3
     11
     12OVERSCAN.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
     77END
    1178
    1279# Normalization class
  • branches/2dbias/ippconfig/recipes/ppImage.config

    r42666 r42679  
    110110
    111111OVERSCAN.2D             BOOL    FALSE           # use a 2D model for the overscan subtraction?
     112OVERSCAN.2D.SUBSET METADATA
     113END
    112114
    113115# statistics to use to convert 2D top-overscan region to a vector
  • branches/2dbias/ppImage/src/ppImage.h

    r42382 r42679  
    188188
    189189bool ppImageDetrendFree(pmConfig *config, pmFPAview *view);
     190bool ppImageDoPatternForView(bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipeName, const char *recipeValue);
    190191bool ppImageFringeFree(pmConfig *config, pmFPAview *view);
    191192
  • branches/2dbias/ppImage/src/ppImageDetrendPattern.c

    r42382 r42679  
    55#include "ppImage.h"
    66
    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
     14bool ppImageDoPatternForView(bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipename, const char *recipevalue);
    1415
    1516bool ppImageDetrendPatternRowApply(pmConfig *config, pmChip *chip, const pmFPAview *inputView, ppImageOptions *options);
     
    2627    assert(inputView->readout == -1);
    2728
    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);
    4146}
    4247
     
    4550    bool status;
    4651
    47     if (!options->doPatternRow) return true;
    48    
     52    if (!options->doPatternRow)
     53        return true;
     54
    4955    assert(inputView->chip != -1);
    5056    assert(inputView->cell == -1);
     
    7379
    7480    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;
    7885    }
    7986
     
    8592
    8693    // 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
    92100    pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
    93101    *view = *inputView;
    94102
    95103    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        }
    163185    }
    164186
    165187    // 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);
    173196    psFree(view);
    174    
     197
    175198    return true;
    176199}
     
    180203    bool status;
    181204
    182     if (!options->doPatternContinuity) return true;
     205    if (!options->doPatternContinuity)
     206        return true;
    183207
    184208    assert(inputView->chip != -1);
     
    188212    // see the comment for PATTERN.ROW; the same rules apply for PATTERN.CELL
    189213
    190     int numCells = chip->cells->n;       // Number of cells
     214    int numCells = chip->cells->n;                         // Number of cells
    191215    psVector *tweak = psVectorAlloc(numCells, PS_TYPE_U8); // Tweak cell?
    192     pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
     216    pmFPAview *view = pmFPAviewAlloc(0);                   // View for local processing
    193217    *view = *inputView;
    194218
    195219    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;
    199224    }
    200225
     
    202227    psLogMsg("ppImage", PS_LOG_INFO, "Performing pattern continuity correction for %s\n", chipName);
    203228
    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        }
    222251    }
    223252
    224253    // Tweak the cells
    225254    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;
    230260    }
    231261    psFree(tweak);
    232262    psFree(view);
    233263
    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);
    235265    return true;
    236266}
     
    240270    bool status;
    241271
    242     if (!options->doPatternCell) return true;
     272    if (!options->doPatternCell)
     273        return true;
    243274
    244275    assert(inputView->chip != -1);
     
    246277    assert(inputView->readout == -1);
    247278
    248     int numCells = chip->cells->n;       // Number of cells
     279    int numCells = chip->cells->n;                         // Number of cells
    249280    psVector *tweak = psVectorAlloc(numCells, PS_TYPE_U8); // Tweak cell?
    250     pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
     281    pmFPAview *view = pmFPAviewAlloc(0);                   // View for local processing
    251282    *view = *inputView;
    252283
    253284    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;
    257289    }
    258290
     
    260292    psLogMsg("ppImage", PS_LOG_INFO, "Performing cell pattern correction for %s\n", chipName);
    261293
    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        }
    279315    }
    280316
    281317    // Tweak the cells
    282318    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;
    287324    }
    288325    psFree(tweak);
    289326    psFree(view);
    290327
    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);
    292329    return true;
    293330}
    294331
    295 bool ppImageDetrendPatternDeadCellsMask (pmChip *chip, psImageMaskType maskVal) {
    296 
    297     int numCells = chip->cells->n;       // Number of cells
     332bool ppImageDetrendPatternDeadCellsMask(pmChip *chip, psImageMaskType maskVal)
     333{
     334
     335    int numCells = chip->cells->n; // Number of cells
    298336
    299337    // 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
    302341        pmReadout *ro = cell->readouts->data[0]; // Readout of interest
    303342
    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        }
    312353    }
    313354    return true;
    314355}
    315356
    316 bool ppImageDetrendPatternDeadCellsXY42 (pmChip *chip, psImageMaskType maskVal) {
     357bool ppImageDetrendPatternDeadCellsXY42(pmChip *chip, psImageMaskType maskVal)
     358{
    317359
    318360    bool status;
    319361
    320362    // 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;
    349399    }
    350400
    351401    // Second, calculate the median
    352     psVectorSortInPlace (cellBackground);
     402    psVectorSortInPlace(cellBackground);
    353403    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);
    356406
    357407    // 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);
    363414    return true;
    364415}
    365416
    366417// 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.
    368419bool ppImageDetrendPatternDeadCellsApply(pmConfig *config, pmChip *chip, const pmFPAview *inputView, ppImageOptions *options)
    369420{
    370421    bool status;
    371422
    372     if (!options->doPatternDeadCells) return true;
     423    if (!options->doPatternDeadCells)
     424        return true;
    373425
    374426    assert(inputView->chip != -1);
     
    380432
    381433    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;
    392446    }
    393447
    394448    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;
    398453    }
    399454
     
    401456    *view = *inputView;
    402457
    403     pmChip *patternChip = pmFPAviewThisChip (view, pattern->fpa);
     458    pmChip *patternChip = pmFPAviewThisChip(view, pattern->fpa);
    404459
    405460    // 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    {
    408464        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;
    411467    }
    412468
    413469    // 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);
    415471
    416472    // extract the MEDIAN_CELL_BACKGROUND values to check for problems
    417     int numCells = chip->cells->n;       // Number of cells
    418     psVector *cellBackground = psVectorAllocEmpty (numCells, PS_DATA_F32);
     473    int numCells = chip->cells->n; // Number of cells
     474    psVector *cellBackground = psVectorAllocEmpty(numCells, PS_DATA_F32);
    419475
    420476    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);
    436495    }
    437496
    438497    // 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;
    443503    }
    444504    psFree(cellBackground);
     
    449509}
    450510
    451 static bool doPatternForView (bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipeName, const char *recipeValue) {
     511bool ppImageDoPatternForView(bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipeName, const char *recipeValue)
     512{
    452513
    453514    *doit = false;
     
    456517
    457518    doPattern = pmConfigRecipeValueByView(config, recipeName, recipeValue, chip->parent, view);
    458    
    459     if (!doPattern) {
     519
     520    if (!doPattern)
     521    {
    460522        psError(PS_ERR_UNKNOWN, false, "Unable to determine whether row pattern matching should be applied.");
    461523        return false;
    462524    }
    463     if (doPattern->type == PS_DATA_BOOL) {
     525    if (doPattern->type == PS_DATA_BOOL)
     526    {
    464527        *doit = doPattern->data.B;
    465528        return true;
    466529    }
    467     if (doPattern->type == PS_DATA_STRING) {
     530    if (doPattern->type == PS_DATA_STRING)
     531    {
    468532        // expect a string of the form "000110001001" with at least view->cell entries
    469533        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));
    472537            return false;
    473538        }
    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':
    480546            *doit = false;
    481547            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':
    487553            *doit = true;
    488554            return true;
    489           default:
     555        default:
    490556            psError(PS_ERR_UNKNOWN, true, "error in PATTERN.ROW.SUBSET chip string %s (unknown value %c))", string, string[view->cell]);
    491557            return false;
     
    497563}
    498564
    499 // thread safety :
    500 bool ppImageDetrendPatternApplyCell (pmConfig *config, pmFPA *fpa, pmChip *chip, pmCell *cell, pmFPAview *view, ppImageOptions *options) {
    501  
     565// thread safety :
     566bool ppImageDetrendPatternApplyCell(pmConfig *config, pmFPA *fpa, pmChip *chip, pmCell *cell, pmFPAview *view, ppImageOptions *options)
     567{
     568
    502569    // 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
    518588    return true;
    519589}
    520590
    521 psVector *ppImageDetrendPatternCellFailures(pmFPAfile *input, const pmFPAview *inputView) {
    522    
     591psVector *ppImageDetrendPatternCellFailures(pmFPAfile *input, const pmFPAview *inputView)
     592{
     593
    523594    bool status = false;
    524595    pmCell *cell = NULL;
    525    
     596
    526597    pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
    527598    *view = *inputView;
    528599
    529600    // 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);
    542615    }
    543616    return cellBackground;
  • branches/2dbias/ppImage/src/ppImageDetrendReadout.c

    r42382 r42679  
    55#include "ppImage.h"
    66
     7#define ESCAPE(STATUS, ...)                           \
     8    {                                                 \
     9        psError(PS_ERR_UNKNOWN, STATUS, __VA_ARGS__); \
     10        psFree(view);                                 \
     11        return false;                                 \
     12    }
     13
    714bool ppImageDetrendReadout(pmConfig *config, ppImageOptions *options, pmFPAview *view)
    815{
    9   // psTimerStart("detrend.readout");
     16    // psTimerStart("detrend.readout");
    1017
    1118    // construct a view for the detrend images (which have only one readout)
     
    1825
    1926    // 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        }
    3849    }
    3950    // psLogMsg ("ppImage", 6, "check gain: %f sec\n", psTimerMark ("detrend.readout"));
     
    4354    bool hasVideo = false;
    4455    {
    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    }
     80done_video_check:
    6381    // psLogMsg ("ppImage", 6, "check video: %f sec\n", psTimerMark ("detrend.readout"));
    6482
     
    7492
    7593    // 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    {
    7796        psImageMaskType satMask = options->doMaskSat ? options->satMask : 0;
    7897        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"));
    85105    }
    86106    // 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"));
    97120    }
    98121
     
    101124        // extern bool ppImageBurntoolApply(pmConfig *, ppImageOptions *, pmFPAview *, pmReadout *);
    102125        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        {
    108133            // 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        {
    111138            // 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"));
    115142    }
    116143
    117144    // 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"));
    125163    }
    126164
    127165    // measure the overscan-subtracted readoutBackground here (or subtract the overscan value?)
    128166    // 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);
    140179    }
    141180
    142181    // 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"));
    150191    }
    151192    // 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"));
    159202    }
    160203
    161204    // 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    {
    166210        bias = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.BIAS");
    167211    }
    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);
    171216        assert(mdok && recipe);
    172217
    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        }
    187240    }
    188241
    189242    // 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        {
    192247            psError(PS_ERR_UNKNOWN, false, "Unable to subtract bias.");
    193248            psFree(detview);
    194249            return false;
    195250        }
    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
    199254    // 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    {
    201257        // create the target mask and variance images
    202258        psImage *noiseImage = NULL;
    203         if (options->doNoiseMap) {
     259        if (options->doNoiseMap)
     260        {
    204261            // XXX convert the noiseMap image to a binned image
    205262            pmReadout *noiseMap = NULL;
    206263            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);
    209266
    210267            // XXX this works, but is not really quite right: the model shoud include the
    211268            // offset information, we are not really getting exactly the right mapping from the
    212269            // original file.
    213             // CZW 2012-03-21: I do not believe this is true anymore.  In any case, this seems to be what
    214             //                 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.
    215272            psImageBinning *binning = psImageBinningAlloc();
    216273            binning->nXruff = noiseMap->image->numCols;
     
    220277            psImageBinningSetScale(binning, PS_IMAGE_BINNING_LEFT);
    221278
    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        {
    237297            psError(PS_ERR_UNKNOWN, false, "Unable to subtract dark.");
    238298            psFree(detview);
    239299            return false;
    240300        }
    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    {
    245306        if (!pmRemnance(input, options->maskValue, options->lowMask,
    246                         options->remnanceSize, options->remnanceThresh)) {
     307                        options->remnanceSize, options->remnanceThresh))
     308        {
    247309            psError(PS_ERR_UNKNOWN, false, "Unable to mask remnance.");
    248310            psFree(detview);
    249311            return false;
    250312        }
    251         // psLogMsg ("ppImage", 6, "mask remnance: %f sec\n", psTimerMark ("detrend.readout"));
     313        // psLogMsg ("ppImage", 6, "mask remnance: %f sec\n", psTimerMark ("detrend.readout"));
    252314    }
    253315
    254316    // Shutter correction
    255     if (options->doShutter) {
     317    if (options->doShutter)
     318    {
    256319        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"));
    262326    }
    263327
    264328    // Flat-field correction (no options used?)
    265     if (options->doFlat) {
     329    if (options->doFlat)
     330    {
    266331        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    /*     } */
    283349
    284350    // Normalization by a single (known) constant
    285     bool mdok;                          // Status of MD lookup
     351    bool mdok; // Status of MD lookup
    286352    float norm = psMetadataLookupF32(&mdok, config->arguments, "NORMALIZATION");
    287     if (mdok && isfinite(norm) && norm != 1.0) {
     353    if (mdok && isfinite(norm) && norm != 1.0)
     354    {
    288355        pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest
    289         psString comment = NULL;        // Comment to add
     356        psString comment = NULL;              // Comment to add
    290357        psStringAppend(&comment, "Normalization: %f", norm);
    291358        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");
     
    293360
    294361        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)
    299366    // Normalization by per-class values
    300367    psMetadata *normlist = psMetadataLookupMetadata(&mdok, config->arguments, "NORMALIZATION.TABLE");
    301     if (normlist) {
     368    if (normlist)
     369    {
    302370        pmFPAfile *inputFile = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.INPUT");
    303371
    304372        // get the menu of class IDs
    305373        psMetadata *menu = psMetadataLookupMetadata(&mdok, inputFile->camera, "CLASSID");
    306         if (!menu) {
     374        if (!menu)
     375        {
    307376            psError(PS_ERR_IO, false, "Unable to find CLASSID metadata in camera configuration");
    308377            psFree(detview);
     
    311380        // get the rule for class_id for the desired class
    312381        const char *rule = psMetadataLookupStr(&mdok, menu, options->normClass);
    313         if (!rule) {
     382        if (!rule)
     383        {
    314384            psError(PS_ERR_IO, false, "Unable to find NORM.CLASS value %s in CLASSID in camera configuration", options->normClass);
    315385            psFree(detview);
     
    318388        // get the class_id from the rule
    319389        char *classID = pmFPAfileNameFromRule(rule, inputFile, view);
    320         if (!classID) {
     390        if (!classID)
     391        {
    321392            psError(PS_ERR_IO, false, "error converting CLASSID rule %s to name\n", rule);
    322393            psFree(detview);
     
    325396
    326397        // 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        {
    329401            psError(PS_ERR_IO, false, "failed to find class ID %s in normalization table\n", classID);
    330402            psFree(detview);
     
    333405
    334406        pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest
    335         psString comment = NULL;        // Comment to add
     407        psString comment = NULL;              // Comment to add
    336408        psStringAppend(&comment, "Normalization: %f", norm);
    337409        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");
     
    341413        psBinaryOp(input->image, input->image, "*", psScalarAlloc(norm, PS_TYPE_F32));
    342414
    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    {
    350423        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"));
    356430    }
    357431
  • branches/2dbias/psModules/src/detrend/pmOverscan.c

    r42664 r42679  
    1717#include "pmOverscan.h"
    1818
    19 #define SMOOTH_NSIGMA 4.0               // Number of Gaussian sigma the smoothing kernel extends
     19#define SMOOTH_NSIGMA 4.0 // Number of Gaussian sigma the smoothing kernel extends
    2020
    2121static void pmOverscanOptionsFree(pmOverscanOptions *options);
    2222static 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 
     23bool pmOverscanUpdateHeaderVector(pmReadout *input, pmHDU *hdu, pmOverscanOptions *overscanOpts, psVector *reduced);
     24
     25bool 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);
    43496        return true;
    44     }
    45 
    46     // we are performing a statitical analysis of the overscan region
    47 
    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 images
    71 
    72     // Reduce all overscan pixels to a single value
    73     if (overscanOpts->single) {
    74 
    75         // extract overscan pixels to a single vector
    76         psVector *pixels = psVectorAlloc(0, PS_TYPE_F32);
    77         psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
    78         psImage *overscan = NULL;   // Overscan image from iterator
    79         while ((overscan = psListGetAndIncrement(iter))) {
    80             int index = pixels->n;  // Index
    81             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 calculated
    92         psStatsOptions statistic = psStatsSingleOption(overscanOpts->primary->stat->options); // Statistic to use
    93         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 original
    99 
    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 statistics
    106 
    107         psString comment = NULL;    // Comment to add
    108         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 value
    113         // XXX EAM : this could / should write the stdev of the overscan region
    114         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-use
    119 
    120         // subtract the measured value from each overscan region as well
    121         iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
    122         overscan = NULL;   // Overscan image from iterator
    123         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-use
    125         }
    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 mask
    131 
    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 subtraction
    149     int cellreaddir = psMetadataLookupS32(&mdok, input->parent->concepts, "CELL.READDIR"); // Read direction
    150     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 fit
    156 
    157     // adjust operation depending on the read direction : need to re-org pixels for columns
    158     if (!overscanOpts->TwoD && (cellreaddir == 1)) {
    159         // The read direction is rows
    160         psArray *pixels = psArrayAlloc(image->numRows); // Array of vectors containing pixels
    161         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 vectors
    166         psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
    167         psImage *overscan = NULL; // Overscan image from iterator
    168         while ((overscan = psListGetAndIncrement(iter))) {
    169             // the overscan and image might not be aligned.  pixels->data represents
    170             // the image row pixels.
    171             int diff = overscan->row0 - image->row0; // Offset between the two regions
    172             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 image
    175                 // j is row on overscan
    176                 psVector *values = pixels->data[i];
    177                 int index = values->n; // Index in the vector
    178                 values = psVectorRealloc(values, values->n + overscan->numCols);
    179                 values->n += overscan->numCols;
    180                 // XXX double-check the range of values->n here
    181                 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 moved
    185             }
    186         }
    187         psFree(iter);
    188 
    189         // Reduce the overscans
    190         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 row
    203         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 regions
    210         {
    211             psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
    212             psImage *overscan = NULL; // Overscan image from iterator
    213             while ((overscan = psListGetAndIncrement(iter))) {
    214                 // the overscan and image might not be aligned.
    215                 int diff = overscan->row0 - image->row0; // Offset between the two regions
    216                 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 image
    219                     // j is row on overscan
    220                     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 columns
    232         psArray *pixels = psArrayAlloc(image->numCols); // Array of vectors containing pixels
    233         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 vectors
    239         psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
    240         psImage *overscan = NULL; // Overscan image from iterator
    241         while ((overscan = psListGetAndIncrement(iter))) {
    242             // the overscan and image might not be aligned.  pixels->data represents
    243             // the image row pixels.
    244             int diff = overscan->col0 - image->col0; // Offset between the two regions
    245             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 image
    248                 // iFixed is column on overscan
    249                 psVector *values = pixels->data[i];
    250                 int index = values->n; // Index in the vector
    251                 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 moved
    257             }
    258         }
    259         psFree(iter);
    260 
    261         // Reduce the overscans
    262         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 column
    275         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 regions
    282         {
    283             psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
    284             psImage *overscan = NULL; // Overscan image from iterator
    285             while ((overscan = psListGetAndIncrement(iter))) {
    286                 // the overscan and image might not be aligned.
    287                 int diff = overscan->col0 - image->col0; // Offset between the two regions
    288                 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 image
    291                     // j is col on overscan
    292                     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: the
    304     // bias is constructed by combining a 1D pattern in the
    305     // readout direction from the top overscan region and a second
    306     // 1D pattern in the cross direction from the overscan
    307     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 rows
    319         psImage *xscan = psListGet (overscans, 1); // overscan region spanning all columns
    320 
    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 be
    324         // inset in the y-direction but the overscan could cover the entire y-range
    325 
    326         // The read direction is rows
    327         psArray *yscanPixels = psArrayAlloc(image->numRows); // Array of vectors containing pixels
    328         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 not
    333         // match the concept of how they are assigned above: biassec[0] = yscan
    334         int yDiff = yscan->row0 - image->row0; // Offset between the two regions
    335         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 yscan
    338             psVector *values = yscanPixels->data[i];
    339             int index = values->n; // Index in the vector
    340             values = psVectorRealloc(values, values->n + yscan->numCols);
    341             values->n += yscan->numCols;
    342             // XXX double-check the range of values->n here
    343             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 moved
    347         }
    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 be
    352         // inset in the x-direction but the overscan could cover the entire y-range
    353 
    354         // Extract the top region as a vector of the columns
    355         psArray *xscanPixels = psArrayAlloc(image->numCols); // Array of vectors containing pixels
    356         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 regions
    361         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 xscan
    364             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 moved
    371         }
    372 
    373         // Reduce the overscans
    374         // XXX need to save 2 different chi-square values
    375         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 overscans
    387         // XXX need to save 2 different chi-square values
    388         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 here
    396         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 regions
    406             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 yscan
    410                 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 regions
    420             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 xscan
    424                 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;
    434497
    435498} // End of overscan subtraction
     
    437500static void pmOverscanOptionsFree(pmOverscanOptions *options)
    438501{
    439     psFree(options->primary);
    440     psFree(options->secondary);
     502        psFree(options->primary);
     503        psFree(options->secondary);
    441504}
    442505
    443506static void pmOverscanStatOptionsFree(pmOverscanStatOptions *options)
    444507{
    445     psFree(options->stat);
    446     psFree(options->poly);
    447     psFree(options->spline);
     508        psFree(options->stat);
     509        psFree(options->poly);
     510        psFree(options->spline);
    448511}
    449512
     
    451514pmOverscanStatOptions *pmOverscanStatOptionsAlloc(void)
    452515{
    453     pmOverscanStatOptions *opts = psAlloc(sizeof(pmOverscanStatOptions));
    454     psMemSetDeallocator(opts, (psFreeFunc)pmOverscanStatOptionsFree);
    455 
    456     // Inputs
    457     opts->fitType = PM_FIT_NONE;
    458     opts->order  = 0;
    459     opts->stat    = NULL;
    460 
    461     // Smoothing
    462     opts->boxcar = 0;
    463     opts->gauss = 0.0;
    464 
    465     // Outputs
    466     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;
    470533}
    471534
     
    473536pmOverscanOptions *pmOverscanOptionsAlloc(void)
    474537{
    475     pmOverscanOptions *opts = psAlloc(sizeof(pmOverscanOptions));
    476     psMemSetDeallocator(opts, (psFreeFunc)pmOverscanOptionsFree);
    477 
    478     // Inputs
    479     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 defined
    486     opts->maxValid = (float) 0x10000; // default value if not defined
    487     opts->maskVal  = 0x0001; // default value if not defined
    488 
    489     // stat options
    490     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;
    494557}
    495558
    496559// Produce an overscan vector from an array of pixels
    497 psVector *pmOverscanVector(float *chi2, // chi^2 from fit
    498                            pmOverscanStatOptions *overscanOpts, // Overscan statistic options
    499                            const psArray *pixels // Array of vectors containing the pixel values
    500     )
     560psVector *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)
    501564{
    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);
    525676                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
    541688        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
     694escape:
    549695        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;
    618700}
    619701
     
    621703// the reduced overscan vector with the 0-order element from the polynomial fit.  Not clear is
    622704// 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;
     705bool 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;
    672759}
    673760
    674761// generate stats of overscan vector for header
    675762// 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;
     763bool 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;
    707799}
    708 
  • branches/2dbias/psModules/src/detrend/pmOverscan.h

    r42664 r42679  
    8181// bool pmOverscanUpdateHeader (pmHDU *hdu, pmOverscanOptions *overscanOpts, float chi2);
    8282
    83 bool pmOverscanSubtract (pmReadout *input, pmOverscanOptions *overscanOpts);
     83bool pmOverscanSubtract (pmReadout *input, pmOverscanOptions *overscanOpts, bool doTwoDOverscan);
    8484
    8585/// @}
Note: See TracChangeset for help on using the changeset viewer.