IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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 
Note: See TracChangeset for help on using the changeset viewer.