IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 42889 for trunk/psModules


Ignore:
Timestamp:
Jun 5, 2025, 3:33:01 PM (14 months ago)
Author:
tdeboer
Message:

updates to detrend functions for 2D overscan correction

Location:
trunk/psModules/src/detrend
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/psModules/src/detrend/pmOverscan.c

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

    r42379 r42889  
    1818
    1919/// Type of fit to perform
    20 typedef enum {
    21     PM_FIT_NONE,                        ///< No fit
    22     PM_FIT_POLY_ORD,                    ///< Fit ordinary polynomial
    23     PM_FIT_POLY_CHEBY,                  ///< Fit Chebyshev polynomial
    24     PM_FIT_SPLINE                       ///< Fit cubic splines
     20typedef enum
     21{
     22    PM_FIT_NONE,       ///< No fit
     23    PM_FIT_POLY_ORD,   ///< Fit ordinary polynomial
     24    PM_FIT_POLY_CHEBY, ///< Fit Chebyshev polynomial
     25    PM_FIT_SPLINE      ///< Fit cubic splines
    2526} pmFit;
    2627
     
    3536{
    3637    // Inputs
    37     bool single;                        ///< Reduce all overscan regions to a single value?
    38     bool constant;                      ///< use a supplied constant value (do not measure region)
    39     float value;                        ///< supplied value if needed (per above)
    40     pmFit fitType;                      ///< Type of fit to overscan
    41     unsigned int order;                 ///< Order of polynomial, or number of spline pieces
    42     psStats *stat;                      ///< Statistic to use when reducing the minor direction
    43     int boxcar;                         ///< Boxcar smoothing radius
    44     float gauss;                        ///< Gaussian smoothing sigma
    45     float minValid;                     ///< if overscan is too low, readout is dead : mask
    46     float maxValid;                     ///< if overscan is too high, readout is dead : mask
    47     psImageMaskType maskVal;            ///< Mask value to give dead readouts
     38    pmFit fitType;      ///< Type of fit to overscan
     39    unsigned int order; ///< Order of polynomial, or number of spline pieces
     40    psStats *stat;      ///< Statistic to use when reducing the minor direction
     41    int boxcar;         ///< Boxcar smoothing radius
     42    float gauss;        ///< Gaussian smoothing sigma
     43    psRegion biassecslow; ///< The 2D slow bias section
     44    psRegion biassecfast; ///< The 2D fast bias section
    4845
    49     // Outputs
    50     psPolynomial1D *poly;               ///< Result of polynomial fit
    51     psSpline1D *spline;                 ///< Result of spline fit
    52 }
    53 pmOverscanOptions;
     46   
     47    // These are used to carry the fitted functions, but are only used to generate
     48    // an updated reduced vector
     49    psPolynomial1D *poly; ///< Result of polynomial fit
     50    psSpline1D *spline;   ///< Result of spline fit
     51} pmOverscanStatOptions;
     52
     53/// Options for overscan subtraction
     54///
     55/// The overscan subtraction may be performed by reducing all overscan regions to a single value (e.g., if
     56/// there is no structure); or the overscan may be fit perpendicular to the read direction (usually the
     57/// columns) with a particular functional form; or a single value may be subtracted for each read/scan without
     58/// fitting (if the structure defies characterisation).  In any case, statistics are required to reduce
     59/// multiple values to a single value (either for the scan, or for the entire overscan regions).
     60typedef struct
     61{
     62    // Inputs
     63    bool single;             ///< Reduce all overscan regions to a single value?
     64    bool constant;           ///< use a supplied constant value (do not measure region)
     65    bool TwoD;               ///< use a supplied constant value (do not measure region)
     66    float value;             ///< supplied value if needed (per above)
     67    float minValid;          ///< if overscan is too low, readout is dead : mask
     68    float maxValid;          ///< if overscan is too high, readout is dead : mask
     69    psImageMaskType maskVal; ///< Mask value to give dead readouts
     70
     71    pmOverscanStatOptions *primary;
     72    pmOverscanStatOptions *secondary;
     73} pmOverscanOptions;
    5474
    5575/// Allocator for overscan options
    56 pmOverscanOptions *pmOverscanOptionsAlloc(bool single, ///< Reduce all overscan regions to a single value?
    57                                           pmFit fitType, ///< Type of fit to overscan
    58                                           unsigned int order, ///< Order of polynomial, or number of splines
    59                                           psStats *stat, ///< Statistic to use
    60                                           int boxcar, ///< Boxcar smoothing radius
    61                                           float gauss ///< Gaussian smoothing sigma
    62                                          );
     76pmOverscanOptions *pmOverscanOptionsAlloc(void);
    6377
    64 psVector *pmOverscanVector(float *chi2, // chi^2 from fit
    65                            pmOverscanOptions *overscanOpts, // Overscan options
    66                            const psArray *pixels, // Array of vectors containing the pixel values
    67                            psStats *myStats // Statistic to use in reducing the overscan
    68     );
     78pmOverscanStatOptions *pmOverscanStatOptionsAlloc(void);
    6979
    70 bool pmOverscanUpdateHeader (pmHDU *hdu, pmOverscanOptions *overscanOpts, float chi2);
     80psVector *pmOverscanVector(float *chi2,                         // chi^2 from fit
     81                           pmOverscanStatOptions *overscanOpts, // Overscan options
     82                           const psArray *pixels,               // Array of vectors containing the pixel values
     83                           bool robust                          // use robust statistics for boxcar smoothing
     84);
    7185
    72 bool pmOverscanSubtract (pmReadout *input, pmOverscanOptions *overscanOpts);
     86// bool pmOverscanUpdateHeader (pmHDU *hdu, pmOverscanOptions *overscanOpts, float chi2);
     87
     88bool pmOverscanSubtract(pmReadout *input, pmOverscanOptions *overscanOpts, bool doTwoDOverscan);
    7389
    7490/// @}
    7591#endif
    76 
  • trunk/psModules/src/detrend/pmPattern.c

    r42379 r42889  
    239239# define READNOISE 10 /* arbitrary number */
    240240    float sigma = sqrt(stats->robustMedian + PS_SQR(READNOISE));
     241    if(isnan(sigma)) sigma=20.;
    241242    float delta = PS_MIN (thresh * sigma, 2*nominalAmplitude);
    242243    float lower = stats->robustMedian - delta; // Lower bound for data
     
    246247    // if the noise from the background is too large
    247248    float significance = nominalAmplitude / sigma;
     249    significance = abs(significance);
    248250   
    249251    // XXX EAM : arbitrary number
    250252    if (isfinite(nominalAmplitude) && (significance < 1.0/6.0)) {
     253      psLogMsg("ppImage", PS_LOG_INFO, "Skipping row pattern correction for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance);
     254      return true;
     255    }
     256    //Add some safeties in case of bad fits
     257    if (isnan(significance) || (background < 0.0)) {
    251258      psLogMsg("ppImage", PS_LOG_INFO, "Skipping row pattern correction for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance);
    252259      return true;
     
    554561# define READNOISE 10 /* arbitrary number */
    555562    float sigma = sqrt(stats->robustMedian + PS_SQR(READNOISE));
     563    if(isnan(sigma)) sigma=20.; // XXX EAM : somewhat arbitrary number
    556564    float delta = PS_MIN (thresh * sigma, 5*nominalAmplitude);
    557565    float lower = stats->robustMedian - delta; // Lower bound for data
     
    561569    // if the noise from the background is too large
    562570    float significance = nominalAmplitude / sigma;
    563    
     571    significance = abs(significance);
     572     
    564573    // XXX EAM : arbitrary number
    565574    if (isfinite(nominalAmplitude) && (significance < 1.0/6.0)) {
     
    569578      return true;
    570579    }
     580    //Add some safeties in case of bad fits
     581    if (isnan(significance) || (background < 0.0)) {
     582      psLogMsg("ppImage", PS_LOG_INFO, "Skipping row pattern correction due to bad fit for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance);
     583      psFree(stats);
     584      psFree(rng);
     585      return true;
     586    }
     587   
    571588    psLogMsg("ppImage", PS_LOG_INFO, "Performing row pattern correction for %s, %s, stats: %f - %f - %f : %f %f %f\n", chipName, cellName, lower, background, upper, sigma, nominalAmplitude, significance);
    572589# endif   
Note: See TracChangeset for help on using the changeset viewer.