IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Dec 16, 2005, 5:18:39 PM (21 years ago)
Author:
Paul Price
Message:

Importing PAP code from phase 2. NOTE: Reverted files in detrend/ and imsubtract/ to a previous version. DO NOT merge these files back!

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/pap_branch_051214/psModules/src/imsubtract/pmSubtractBias.c

    r5552 r5795  
     1//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     2// XXX WARNING: I have completely replaced this file with an OLD VERSION (that works) instead of the
     3// one that was being worked on.
     4//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     5
    16/** @file  pmSubtractBias.c
    27 *
     
    611 *  @author GLG, MHPCC
    712 *
    8  *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
    9  *  @date $Date: 2005-11-19 00:55:18 $
     13 *  @version $Revision: 1.6.8.1 $ $Name: not supported by cvs2svn $
     14 *  @date $Date: 2005-12-17 03:18:39 $
    1015 *
    1116 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
    1217 *
    1318 */
    14 /*****************************************************************************/
    15 /* INCLUDE FILES                                                             */
    16 /*****************************************************************************/
    17 #include <stdio.h>
    18 #include <math.h>
    19 #include <string.h>
    20 #include "pslib.h"
     19
    2120#if HAVE_CONFIG_H
    2221#include <config.h>
    2322#endif
     23
    2424#include "pmSubtractBias.h"
    2525
    26 /*****************************************************************************/
    27 /* DEFINE STATEMENTS                                                         */
    28 /*****************************************************************************/
     26#define PM_SUBTRACT_BIAS_POLYNOMIAL_ORDER 2
     27#define PM_SUBTRACT_BIAS_SPLINE_ORDER 3
     28
    2929// XXX: put these in psConstants.h
    30 void PS_POLY1D_PRINT(
    31     psPolynomial1D *poly)
     30void PS_POLY1D_PRINT(psPolynomial1D *poly)
    3231{
    3332    printf("-------------- PS_POLY1D_PRINT() --------------\n");
     
    5756}\
    5857
    59 /*****************************************************************************/
    60 /* TYPE DEFINITIONS                                                          */
    61 /*****************************************************************************/
    62 
    63 /*****************************************************************************/
    64 /* GLOBAL VARIABLES                                                          */
    65 /*****************************************************************************/
    66 psS32 currentId = 0;                // XXX: remove
    67 psS32 memLeaks = 0;                 // XXX: remove
    68 //PRINT_MEMLEAKS(8); XXX
    69 /*****************************************************************************/
    70 /* FILE STATIC VARIABLES                                                     */
    71 /*****************************************************************************/
    72 
    73 /*****************************************************************************/
    74 /* FUNCTION IMPLEMENTATION - LOCAL                                           */
    75 /*****************************************************************************/
    76 
    7758/******************************************************************************
    78 psSubtractFrame(): this routine will take as input the pmReadout for the input
    79 image and a pmReadout for the bias image.  The bias image is subtracted in
    80 place from the input image.  We assume that sizes and types are checked
    81 elsewhere.
    82  
    83 XXX: Verify that the image and readout offsets are being used the right way.
    84  
    85 XXX: Ensure that it does the correct thing with image size.
     59psSubtractFrame(): this routine will take as input a readout for the input
     60image and a readout for the bias image.  The bias image is subtracted in
     61place from the input image.
    8662*****************************************************************************/
    87 static pmReadout *SubtractFrame(
    88     pmReadout *in,
    89     const pmReadout *bias)
     63static pmReadout *SubtractFrame(pmReadout *in,
     64                                const pmReadout *bias)
    9065{
    91     // XXX: When did the ->row0 and ->col0 offsets get coded?
    92     for (psS32 i=0;i<in->image->numRows;i++) {
    93         for (psS32 j=0;j<in->image->numCols;j++) {
     66    psS32 i;
     67    psS32 j;
     68
     69    if (bias == NULL) {
     70        psLogMsg(__func__, PS_LOG_WARN,
     71                 "WARNING: pmSubtractBias.c: SubtractFrame(): bias frame is NULL.  Returning original image.\n");
     72        return(in);
     73    }
     74
     75
     76    if ((in->image->numRows + in->row0 - bias->row0) > bias->image->numRows) {
     77        psError(PS_ERR_UNKNOWN,true, "bias image does not have enough rows.  Returning in image\n");
     78        return(in);
     79    }
     80    if ((in->image->numCols + in->col0 - bias->col0) > bias->image->numCols) {
     81        psError(PS_ERR_UNKNOWN,true, "bias image does not have enough columns.  Returning in image\n");
     82        return(in);
     83    }
     84
     85    for (i=0;i<in->image->numRows;i++) {
     86        for (j=0;j<in->image->numCols;j++) {
    9487            in->image->data.F32[i][j]-=
    9588                bias->image->data.F32[i+in->row0-bias->row0][j+in->col0-bias->col0];
    96 
    9789            if ((in->mask != NULL) && (bias->mask != NULL)) {
    9890                (in->mask->data.U8[i][j])|=
     
    10597}
    10698
    107 
    108 /******************************************************************************
    109 psSubtractDarkFrame(): this routine will take as input the pmReadout for the
    110 input image and a pmReadout for the dark image.  The dark image is scaled and
    111 subtracted in place from the input image.
    112  
    113 XXX: Verify that the image and readout offsets are being used the right way.
    114  
    115 XXX: Ensure that it does the correct thing with image size.
    116 *****************************************************************************/
    117 static pmReadout *SubtractDarkFrame(
    118     pmReadout *in,
    119     const pmReadout *dark,
    120     psF32 scale)
    121 {
    122     // XXX: When did the ->row0 and ->col0 offsets get coded?
    123     if (fabs(scale) > FLT_EPSILON) {
    124         for (psS32 i=0;i<in->image->numRows;i++) {
    125             for (psS32 j=0;j<in->image->numCols;j++) {
    126                 in->image->data.F32[i][j]-=
    127                     (scale * dark->image->data.F32[i+in->row0-dark->row0][j+in->col0-dark->col0]);
    128 
    129                 if ((in->mask != NULL) && (dark->mask != NULL)) {
    130                     (in->mask->data.U8[i][j])|=
    131                         dark->mask->data.U8[i+in->row0-dark->row0][j+in->col0-dark->col0];
    132                 }
    133             }
    134         }
    135     } else {
    136         for (psS32 i=0;i<in->image->numRows;i++) {
    137             for (psS32 j=0;j<in->image->numCols;j++) {
    138                 in->image->data.F32[i][j]-=
    139                     dark->image->data.F32[i+in->row0-dark->row0][j+in->col0-dark->col0];
    140 
    141                 if ((in->mask != NULL) && (dark->mask != NULL)) {
    142                     (in->mask->data.U8[i][j])|=
    143                         dark->mask->data.U8[i+in->row0-dark->row0][j+in->col0-dark->col0];
    144                 }
    145             }
    146         }
    147     }
    148 
    149     return(in);
    150 }
    151 
    15299/******************************************************************************
    153100ImageSubtractScalar(): subtract a scalar from the input image.
    154101 
    155 XXX: Is there a psLib function for this?
     102XXX: Use a psLib function for this.
     103 
     104XXX: This should
    156105 *****************************************************************************/
    157 static psImage *ImageSubtractScalar(
    158     psImage *image,
    159     psF32 scalar)
     106static psImage *ImageSubtractScalar(psImage *image,
     107                                    psF32 scalar)
    160108{
    161109    for (psS32 i=0;i<image->numRows;i++) {
     
    221169
    222170    if (numOptions == 0) {
    223         psError(PS_ERR_UNKNOWN,true, "No allowable statistics options have been specified.\n");
     171        psError(PS_ERR_UNKNOWN,true, "No statistics options have been specified.\n");
    224172    }
    225173    if (numOptions != 1) {
     
    230178}
    231179
    232 /******************************************************************************
    233 Polynomial1DCopy(): This private function copies the members of the existing
    234 psPolynomial1D "in" into the existing psPolynomial1D "out".  The previous
    235 members of the existing psPolynomial1D "out" are psFree'ed.
    236  *****************************************************************************/
    237 static psBool Polynomial1DCopy(
    238     psPolynomial1D *out,
    239     psPolynomial1D *in)
    240 {
    241     psFree(out->coeff);
    242     psFree(out->coeffErr);
    243     psFree(out->mask);
    244 
    245     out->type = in->type;
    246     out->nX = in->nX;
    247 
    248     out->coeff = (psF64 *) psAlloc((in->nX + 1) * sizeof(psF64));
    249     // XXX: use memcpy
    250     for (psS32 i = 0 ; i < (in->nX + 1) ; i++) {
    251         out->coeff[i] = in->coeff[i];
    252     }
    253 
    254     out->coeffErr = (psF64 *) psAlloc((in->nX + 1) * sizeof(psF64));
    255     // XXX: use memcpy
    256     for (psS32 i = 0 ; i < (in->nX + 1) ; i++) {
    257         out->coeffErr[i] = in->coeffErr[i];
    258     }
    259 
    260     out->mask = (psMaskType *) psAlloc((in->nX + 1) * sizeof(psMaskType));
    261     // XXX: use memcpy
    262     for (psS32 i = 0 ; i < (in->nX + 1) ; i++) {
    263         out->mask[i] = in->mask[i];
    264     }
    265 
    266     return(true);
    267 }
    268 
    269 /******************************************************************************
    270 Polynomial1DDup(): This private function duplicates and then returns the input
    271 psPolynomial1D "in".
    272  *****************************************************************************/
    273 static psPolynomial1D *Polynomial1DDup(
    274     psPolynomial1D *in)
    275 {
    276     psPolynomial1D *out = psPolynomial1DAlloc(in->nX, in->type);
    277     Polynomial1DCopy(out, in);
    278     return(out);
    279 }
    280 
    281 
    282 /******************************************************************************
    283 SplineCopy(): This private function copies the members of the existing
    284 psSpline in into the existing psSpline out.
    285  *****************************************************************************/
    286 static psBool SplineCopy(
    287     psSpline1D *out,
    288     psSpline1D *in)
    289 {
    290     PS_ASSERT_PTR_NON_NULL(out, false);
    291     PS_ASSERT_PTR_NON_NULL(in, false);
    292 
    293     for (psS32 i = 0 ; i < out->n ; i++) {
    294         psFree(out->spline[i]);
    295     }
    296     psFree(out->spline);
    297     psFree(out->knots);
    298     psFree(out->p_psDeriv2);
    299 
    300     out->n = in->n;
    301     out->spline = (psPolynomial1D **) psAlloc(in->n * sizeof(psPolynomial1D *));
    302     for (psS32 i = 0 ; i < in->n ; i++) {
    303         out->spline[i] = Polynomial1DDup(in->spline[i]);
    304     }
    305 
    306     // XXX: use psVectorCopy if they get it working.
    307     out->knots = psVectorAlloc(in->knots->n, in->knots->type.type);
    308     for (psS32 i = 0 ; i < in->knots->n ; i++) {
    309         out->knots->data.F32[i] = in->knots->data.F32[i];
    310     }
    311     /*
    312         out->knots = psVectorCopy(out->knots, in->knots, in->knots->type.type);
    313     */
    314 
    315     out->p_psDeriv2 = (psF32 *) psAlloc((in->n + 1) * sizeof(psF32));
    316     // XXX: use memcpy
    317     for (psS32 i = 0 ; i < (in->n + 1) ; i++) {
    318         out->p_psDeriv2[i] = in->p_psDeriv2[i];
    319     }
    320 
    321     return(true);
    322 }
     180
    323181
    324182/******************************************************************************
     
    328186    PM_FIT_POLYNOMIAL: fit a polynomial to the entire input vector data.
    329187    PM_FIT_SPLINE: fit splines to the input vector data.
    330 The resulting spline or polynomial is set in the fitSpec argument.
     188XXX: Doesn't it make more sense to do polynomial interpolation on a few
     189elements of the input vector, rather than fit a polynomial to the entire
     190vector?
    331191 *****************************************************************************/
    332 static psVector *ScaleOverscanVector(
    333     psVector *overscanVector,
    334     psS32 n,
    335     void *fitSpec,
    336     pmFit fit)
     192static psVector *ScaleOverscanVector(psVector *overscanVector,
     193                                     psS32 n,
     194                                     void *fitSpec,
     195                                     pmFit fit)
    337196{
    338197    psTrace(".psModule.pmSubtracBias.ScaleOverscanVector", 4,
    339198            "---- ScaleOverscanVector() begin (%d -> %d) ----\n", overscanVector->n, n);
     199    //    PS_VECTOR_PRINT_F32(overscanVector);
    340200
    341201    if (NULL == overscanVector) {
     
    350210    //
    351211    if (n == overscanVector->n) {
    352         return(psVectorCopy(newVec, overscanVector, PS_TYPE_F32));
    353     }
     212        for (psS32 i = 0 ; i < n ; i++) {
     213            newVec->data.F32[i] = overscanVector->data.F32[i];
     214        }
     215        return(newVec);
     216    }
     217    psPolynomial1D *myPoly;
     218    psSpline1D *mySpline;
    354219    psF32 x;
    355 
     220    psS32 i;
    356221    if (fit == PM_FIT_POLYNOMIAL) {
    357222        // Fit a polynomial to the old overscan vector.
    358         psPolynomial1D *myPoly = (psPolynomial1D *) fitSpec;
     223        myPoly = (psPolynomial1D *) fitSpec;
    359224        PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
    360         PS_ASSERT_POLY1D(myPoly, NULL);
    361225        myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
    362226        if (myPoly == NULL) {
    363             psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the psVector.\n");
     227            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(1): Could not fit a polynomial to the psVector.\n");
    364228            return(NULL);
    365229        }
     
    368232        // of the old vector, use the fitted polynomial to determine the
    369233        // interpolated value at that point, and set the new vector.
    370         for (psS32 i=0;i<n;i++) {
     234        for (i=0;i<n;i++) {
    371235            x = ((psF32) i) * ((psF32) overscanVector->n) / ((psF32) n);
    372236            newVec->data.F32[i] = psPolynomial1DEval(myPoly, x);
    373237        }
    374238    } else if (fit == PM_FIT_SPLINE) {
     239        psS32 mustFreeSpline = 0;
     240        // Fit a spline to the old overscan vector.
     241        mySpline = (psSpline1D *) fitSpec;
     242        // XXX: Does it make any sense to have a psSpline argument?
     243        if (mySpline == NULL) {
     244            mustFreeSpline = 1;
     245        }
     246
    375247        //
    376248        // NOTE: Since the X arg in the psVectorFitSpline1D() function is NULL,
     
    378250        // properly when doing the spline eval.
    379251        //
    380         psSpline1D *mySpline = psVectorFitSpline1D(NULL, overscanVector);
     252        //        mySpline = psVectorFitSpline1D(mySpline, NULL, overscanVector, NULL);
     253        mySpline = psVectorFitSpline1D(NULL, overscanVector);
    381254        if (mySpline == NULL) {
    382             psError(PS_ERR_UNKNOWN, false, "Could not fit a spline to the psVector.\n");
     255            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(2): Could not fit a spline to the psVector.\n");
    383256            return(NULL);
    384257        }
     258        //        PS_PRINT_SPLINE(mySpline);
    385259
    386260        // For each element of the new vector, convert the x-ordinate to that
    387         // of the old vector, use the fitted spline to determine the
     261        // of the old vector, use the fitted polynomial to determine the
    388262        // interpolated value at that point, and set the new vector.
    389         for (psS32 i=0;i<n;i++) {
     263        for (i=0;i<n;i++) {
    390264            // Scale to [0 : overscanVector->n - 1]
    391265            x = ((psF32) i) * ((psF32) (overscanVector->n-1)) / ((psF32) n);
    392266            newVec->data.F32[i] = psSpline1DEval(mySpline, x);
    393267        }
    394 
    395         psSpline1D *ptrSpline = (psSpline1D *) fitSpec;
    396         if (ptrSpline != NULL) {
    397             // Copy the resulting spline fit into ptrSpline.
    398             PS_ASSERT_SPLINE(ptrSpline, NULL);
    399             SplineCopy(ptrSpline, mySpline);
    400         }
    401         psFree(mySpline);
     268        if (mustFreeSpline ==1) {
     269            psFree(mySpline);
     270        }
     271        //        PS_VECTOR_PRINT_F32(newVec);
     272
     273
    402274    } else {
    403275        psError(PS_ERR_UNKNOWN, true, "unknown fit type.  Returning NULL.\n");
     
    412284
    413285/******************************************************************************
     286XXX: The SDRS does not specify type support.  F32 is implemented here.
    414287 *****************************************************************************/
    415 static psS32 GetOverscanSize(
    416     psImage *inImg,
    417     pmOverscanAxis overScanAxis)
    418 {
    419     if (overScanAxis == PM_OVERSCAN_ROWS) {
    420         return(inImg->numCols);
    421     } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
    422         return(inImg->numRows);
    423     } else if (overScanAxis == PM_OVERSCAN_ALL) {
    424         return(1);
    425     }
    426     return(0);
    427 }
    428 
    429 /******************************************************************************
    430 GetOverscanAxis(in) this private routine determines the appropiate overscan
    431 axis from the parent cell metadata.
    432  
    433 XXX: Verify the READDIR corresponds with my overscan axis.
    434  *****************************************************************************/
    435 static pmOverscanAxis GetOverscanAxis(pmReadout *in)
    436 {
    437     psBool rc;
    438     if ((in->parent != NULL) && (in->parent->concepts)) {
    439         psS32 dir = psMetadataLookupS32(&rc, in->parent->concepts, "CELL.READDIR");
    440         if (rc == true) {
    441             if (dir == 1) {
    442                 return(PM_OVERSCAN_ROWS);
    443             } else if (dir == 2) {
    444                 return(PM_OVERSCAN_COLUMNS);
    445             } else if (dir == 3) {
    446                 return(PM_OVERSCAN_ALL);
    447             }
    448         }
    449     }
    450 
    451     psLogMsg(__func__, PS_LOG_WARN,
    452              "WARNING: pmSubtractBias.(): could not determine CELL.READDIR from in->parent metadata.  Setting overscan axis to PM_OVERSCAN_NONE.\n");
    453     return(PM_OVERSCAN_NONE);
    454 }
    455 
    456 /******************************************************************************
    457 psListLength(list): determine the length of a psList.
    458  
    459 XXX: Put this elsewhere.
    460  *****************************************************************************/
    461 static psS32 psListLength(
    462     psList *list)
    463 {
    464     psS32 length = 0;
    465     psListElem *tmpElem = (psListElem *) list->head;
    466     while (NULL != tmpElem) {
    467         tmpElem = tmpElem->next;
    468         length++;
    469     }
    470     return(length);
    471 }
    472 
    473 /******************************************************************************
    474 Note: this isn't needed anymore as of psModule SDRS 12-09.
    475  *****************************************************************************/
    476 static psBool OverscanReducePixel(
    477     psImage *in,
    478     psList *bias,
    479     psStats *myStats)
    480 {
    481     PS_ASSERT_PTR_NON_NULL(in, NULL);
    482     PS_ASSERT_PTR_NON_NULL(bias, NULL);
    483     PS_ASSERT_PTR_NON_NULL(bias->head, NULL);
    484     PS_ASSERT_PTR_NON_NULL(myStats, NULL);
    485 
    486     // Allocate a psVector with one element per overscan image.
    487     psS32 numOverscanImages = psListLength(bias);
    488     psVector *statsAll = psVectorAlloc(numOverscanImages, PS_TYPE_F32);
    489     psListElem *tmpOverscan = (psListElem *) bias->head;
    490     psS32 i = 0;
    491     psF64 statValue;
    492     //
    493     // We loop through each overscan image, calculating the specified
    494     // statistic on that image.
    495     //
    496     while (NULL != tmpOverscan) {
    497         psImage *myOverscanImage = (psImage *) tmpOverscan->data;
    498 
    499         PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL);
    500         myStats = psImageStats(myStats, myOverscanImage, NULL, (psMaskType)0xffffffff);
    501         if (myStats == NULL) {
    502             psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
    503             psFree(statsAll);
    504             return(false);
    505         }
    506         if (false == p_psGetStatValue(myStats, &statValue)) {
    507             psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
    508             psFree(statsAll);
    509             return(false);
    510         }
    511         statsAll->data.F32[i] = statValue;
    512         i++;
    513         tmpOverscan = tmpOverscan->next;
    514     }
    515 
    516     //
    517     // We reduce the individual stats for each overscan image to
    518     // a single psF32.
    519     //
    520     myStats = psVectorStats(myStats, statsAll, NULL, NULL, 0);
    521     if (myStats == NULL) {
    522         psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
    523         psFree(statsAll);
    524         return(false);
    525     }
    526     if (false == p_psGetStatValue(myStats, &statValue)) {
    527         psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
    528         psFree(statsAll);
    529         return(false);
    530     }
    531 
    532     //
    533     // Subtract the result and return.
    534     //
    535     ImageSubtractScalar(in, statValue);
    536     psFree(statsAll);
    537     return(in);
    538 }
    539 
    540 /******************************************************************************
    541 ReduceOverscanImageToCol(overscanImage, myStats): This private routine reduces
    542 a single psImage to a column by combining all pixels from each row into a
    543 single pixel via requested statistic in myStats.
    544  *****************************************************************************/
    545 static psVector *ReduceOverscanImageToCol(
    546     psImage *overscanImage,
    547     psStats *myStats)
    548 {
    549     psF64 statValue;
    550     psVector *tmpRow = psVectorAlloc(overscanImage->numCols, PS_TYPE_F32);
    551     psVector *tmpCol = psVectorAlloc(overscanImage->numRows, PS_TYPE_F32);
    552 
    553     //
    554     // For each row, we store all pixels in that row into a temporary psVector,
    555     // then we run psVectorStats() on that vector.
    556     //
    557     for (psS32 i=0;i<overscanImage->numRows;i++) {
    558         for (psS32 j=0;j<overscanImage->numCols;j++) {
    559             tmpRow->data.F32[j] = overscanImage->data.F32[i][j];
    560         }
    561 
    562         psStats *rc = psVectorStats(myStats, tmpRow, NULL, NULL, 0);
    563         if (rc == NULL) {
    564             psError(PS_ERR_UNKNOWN, true, "psVectorStats() could not perform requested statistical operation.  Returning in image.\n");
    565             return(NULL);
    566         }
    567 
    568         if (false ==  p_psGetStatValue(rc, &statValue)) {
    569             psError(PS_ERR_UNKNOWN, true, "p_psGetStatValue() could not determine result from requested statistical operation.  Returning in image.\n");
    570             return(NULL);
    571         }
    572 
    573         tmpCol->data.F32[i] = (psF32) statValue;
    574     }
    575     psFree(tmpRow);
    576 
    577     return(tmpCol);
    578 }
    579 
    580 /******************************************************************************
    581 ReduceOverscanImageToCol(overscanImage, myStats): This private routine reduces
    582 a single psImage to a row by combining all pixels from each column into a
    583 single pixel via requested statistic in myStats.
    584  *****************************************************************************/
    585 static psVector *ReduceOverscanImageToRow(
    586     psImage *overscanImage,
    587     psStats *myStats)
    588 {
    589     psF64 statValue;
    590     psVector *tmpRow = psVectorAlloc(overscanImage->numCols, PS_TYPE_F32);
    591     psVector *tmpCol = psVectorAlloc(overscanImage->numRows, PS_TYPE_F32);
    592 
    593     //
    594     // For each column, we store all pixels in that column into a temporary psVector,
    595     // then we run psVectorStats() on that vector.
    596     //
    597     for (psS32 i=0;i<overscanImage->numCols;i++) {
    598         for (psS32 j=0;j<overscanImage->numRows;j++) {
    599             tmpCol->data.F32[j] = overscanImage->data.F32[j][i];
    600         }
    601 
    602         psStats *rc = psVectorStats(myStats, tmpCol, NULL, NULL, 0);
    603         if (rc == NULL) {
    604             psError(PS_ERR_UNKNOWN, true, "psVectorStats() could not perform requested statistical operation.  Returning in image.\n");
    605             return(NULL);
    606         }
    607 
    608         if (false ==  p_psGetStatValue(rc, &statValue)) {
    609             psError(PS_ERR_UNKNOWN, true, "p_psGetStatValue() could not determine result from requested statistical operation.  Returning in image.\n");
    610             return(NULL);
    611         }
    612 
    613         tmpRow->data.F32[i] = (psF32) statValue;
    614     }
    615     psFree(tmpCol);
    616 
    617     return(tmpRow);
    618 }
    619 
    620 /******************************************************************************
    621 OverscanReduce(vecSize, bias, myStats): This private routine takes a psList of
    622 overscan images (in bias) and reduces them to a single psVector via the
    623 specified psStats struct.  The vector is then scaled to the length or the
    624 row/column in inImg.
    625  *****************************************************************************/
    626 static psVector* OverscanReduce(
    627     psImage *inImg,
    628     pmOverscanAxis overScanAxis,
    629     psList *bias,
    630     void *fitSpec,
    631     pmFit fit,
    632     psStats *myStats)
    633 {
    634     if ((overScanAxis != PM_OVERSCAN_ROWS) && (overScanAxis != PM_OVERSCAN_COLUMNS)) {
    635         psError(PS_ERR_UNKNOWN, true, "overScanAxis must be PM_OVERSCAN_ROWS or PM_OVERSCAN_COLUMNS\n");
    636         return(NULL);
    637     }
    638     PS_ASSERT_PTR_NON_NULL(inImg, NULL);
    639     PS_ASSERT_PTR_NON_NULL(bias, NULL);
    640     PS_ASSERT_PTR_NON_NULL(bias->head, NULL);
    641     PS_ASSERT_PTR_NON_NULL(myStats, NULL);
    642     //
    643     // Allocate a psVector for the output of this routine.
    644     //
    645     psS32 vecSize = GetOverscanSize(inImg, overScanAxis);
    646     psVector *overscanVector = psVectorAlloc(vecSize, PS_TYPE_F32);
    647 
    648     //
    649     // Allocate an array of psVectors with one psVector per element of the
    650     // final oversan column vector.  These psVectors will be used with
    651     // psStats to reduce the multiple elements from each overscan column
    652     // vector to a single final column vector.
    653     //
    654     psS32 numOverscanImages = psListLength(bias);
    655     psVector **overscanVectors = (psVector **) psAlloc(numOverscanImages * sizeof(psVector *));
    656     for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    657         overscanVectors[i] = NULL;
    658     }
    659 
    660     //
    661     // We iterate through the list of overscan images.  For each image,
    662     // we reduce it to a single column or row.  Save the overscan vector
    663     // in overscanVectors[].
    664     //
    665     psListElem *tmpOverscan = (psListElem *) bias->head;
    666     psS32 overscanID = 0;
    667     while (tmpOverscan != NULL) {
    668         psImage *tmpOverscanImage = (psImage *) tmpOverscan->data;
    669         if (overScanAxis == PM_OVERSCAN_ROWS) {
    670             overscanVectors[overscanID] = ReduceOverscanImageToRow(tmpOverscanImage, myStats);
    671         } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
    672             overscanVectors[overscanID] = ReduceOverscanImageToCol(tmpOverscanImage, myStats);
    673         }
    674 
    675         tmpOverscan = tmpOverscan->next;
    676         overscanID++;
    677     }
    678 
    679     //
    680     // For each overscan vector, if necessary, we scale that column or
    681     // row to vecSize.  Note: we should have already ensured that the
    682     // fit is poly or spline.
    683     //
    684     for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    685         psVector *tmpOverscanVector = overscanVectors[i];
    686 
    687         if (tmpOverscanVector->n != vecSize) {
    688             overscanVectors[i] = ScaleOverscanVector(tmpOverscanVector, vecSize, fitSpec, fit);
    689             if (overscanVectors[i] == NULL) {
    690                 psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.\n");
    691                 for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    692                     psFree(overscanVectors[i]);
    693                 }
    694                 psFree(overscanVectors);
    695                 psFree(tmpOverscanVector);
    696                 return(NULL);
    697             }
    698             psFree(tmpOverscanVector);
    699         }
    700     }
    701 
    702     //
    703     // We collect all elements in the overscan vectors for the various
    704     // overscan images into a single psVector (tmpVec).  Then we call
    705     // psStats on that vector to determine the final values for the
    706     // overscan vector.
    707     //
    708     psVector *tmpVec = psVectorAlloc(numOverscanImages, PS_TYPE_F32);
    709     psF64 statValue;
    710     for (psS32 i = 0 ; i < vecSize ; i++) {
    711         // Collect the i-th elements from each overscan vector into a single vector.
    712         for (psS32 j = 0 ; j < numOverscanImages ; j++) {
    713             tmpVec->data.F32[j] = overscanVectors[j]->data.F32[i];
    714         }
    715 
    716         if (NULL == psVectorStats(myStats, tmpVec, NULL, NULL, 0)) {
    717             psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
    718             for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    719                 psFree(overscanVectors[i]);
    720             }
    721             psFree(overscanVectors);
    722             psFree(tmpVec);
    723             return(NULL);
    724         }
    725         if (false == p_psGetStatValue(myStats, &statValue)) {
    726             psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
    727             for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    728                 psFree(overscanVectors[i]);
    729             }
    730             psFree(overscanVectors);
    731             psFree(tmpVec);
    732             return(NULL);
    733         }
    734 
    735         overscanVector->data.F32[i] = (psF32) statValue;
    736     }
    737 
    738     //
    739     // We're done.  Free the intermediate overscan vectors.
    740     //
    741     psFree(tmpVec);
    742     for (psS32 i = 0 ; i < numOverscanImages ; i++) {
    743         psFree(overscanVectors[i]);
    744     }
    745     psFree(overscanVectors);
    746 
    747     //
    748     // Return the computed overscanVector
    749     //
    750     return(overscanVector);
    751 }
    752 
    753 /******************************************************************************
    754 RebinOverscanVector(overscanVector, nBinOrig, myStats): this private routine
    755 takes groups of nBinOrig elements in the input vector, combines them into a
    756 single pixel via myStats and psVectorStats(), and then outputs a vector of
    757 those pixels.
    758  *****************************************************************************/
    759 static psS32 RebinOverscanVector(
    760     psVector *overscanVector,
    761     psS32 nBinOrig,
    762     psStats *myStats)
    763 {
    764     psF64 statValue;
    765     psS32 nBin;
    766     if ((nBinOrig > 1) && (nBinOrig < overscanVector->n)) {
    767         psS32 numBins = 1+((overscanVector->n)/nBinOrig);
    768         psVector *myBin = psVectorAlloc(numBins, PS_TYPE_F32);
    769         psVector *binVec = psVectorAlloc(nBinOrig, PS_TYPE_F32);
    770 
    771         for (psS32 i=0;i<numBins;i++) {
    772             for(psS32 j=0;j<nBinOrig;j++) {
    773                 if (overscanVector->n > ((i*nBinOrig)+j)) {
    774                     binVec->data.F32[j] = overscanVector->data.F32[(i*nBinOrig)+j];
    775                 } else {
    776                     // XXX: we get here if nBinOrig does not evenly divide
    777                     // the overscanVector vector.  This is the last bin.  Should
    778                     // we change the binVec->n to acknowledge that?
    779                     binVec->n = j;
    780                 }
    781             }
    782             psStats *rc = psVectorStats(myStats, binVec, NULL, NULL, 0);
    783             if (rc == NULL) {
    784                 psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
    785                 return(-1);
    786             }
    787             if (false ==  p_psGetStatValue(rc, &statValue)) {
    788                 psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
    789                 return(-1);
    790             }
    791             myBin->data.F32[i] = statValue;
    792         }
    793 
    794         // Change the effective size of overscanVector.
    795         overscanVector->n = numBins;
    796         for (psS32 i=0;i<numBins;i++) {
    797             overscanVector->data.F32[i] = myBin->data.F32[i];
    798         }
    799         psFree(binVec);
    800         psFree(myBin);
    801         nBin = nBinOrig;
    802     } else {
    803         nBin = 1;
    804     }
    805 
    806     return(nBin);
    807 }
    808 
    809 /******************************************************************************
    810 FitOverscanVectorAndUnbin(inImg, overscanVector, overScanAxis, fitSpec, fit,
    811 nBin):  this private routine fits a psPolynomial or psSpline to the overscan
    812 vector.  It then creates a new vector, with a size determined by the input
    813 image, evaluates the psPolynomial or psSpline at each element in that vector,
    814 then returns that vector.
    815  *****************************************************************************/
    816 static psVector *FitOverscanVectorAndUnbin(
    817     psImage *inImg,
    818     psVector *overscanVector,
    819     pmOverscanAxis overScanAxis,
    820     void *fitSpec,
    821     pmFit fit,
    822     psS32 nBin)
    823 {
    824     psPolynomial1D* myPoly = NULL;
    825     psSpline1D *mySpline = NULL;
    826     //
    827     // Fit a polynomial or spline to the overscan vector.
    828     //
    829     if (fit == PM_FIT_POLYNOMIAL) {
    830         myPoly = (psPolynomial1D *) fitSpec;
    831         PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
    832         PS_ASSERT_POLY1D(myPoly, NULL);
    833         myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
    834         if (myPoly == NULL) {
    835             psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to overscan vector.  Returning NULL.\n");
    836             return(NULL);
    837         }
    838     } else if (fit == PM_FIT_SPLINE) {
    839         mySpline = psVectorFitSpline1D(NULL, overscanVector);
    840         if (mySpline == NULL) {
    841             psError(PS_ERR_UNKNOWN, false, "Could not fit a spline to overscan vector.  Returning NULL.\n");
    842             return(NULL);
    843         }
    844         if (fitSpec != NULL) {
    845             // Copy the resulting spline fit into fitSpec.
    846             psSpline1D *ptrSpline = (psSpline1D *) fitSpec;
    847             PS_ASSERT_SPLINE(ptrSpline, NULL);
    848             SplineCopy(ptrSpline, mySpline);
    849         }
    850     }
    851 
    852     //
    853     // Evaluate the poly/spline at each pixel in the overscan row/column.
    854     //
    855     psS32 vecSize = GetOverscanSize(inImg, overScanAxis);
    856     psVector *newVec = psVectorAlloc(vecSize, PS_TYPE_F32);
    857     if ((nBin > 1) && (nBin < overscanVector->n)) {
    858         for (psS32 i = 0 ; i < vecSize ; i++) {
    859             if (fit == PM_FIT_POLYNOMIAL) {
    860                 newVec->data.F32[i] = psPolynomial1DEval(myPoly, ((psF32) i) / ((psF32) nBin));
    861             } else if (fit == PM_FIT_SPLINE) {
    862                 newVec->data.F32[i] = psSpline1DEval(mySpline, ((psF32) i) / ((psF32) nBin));
    863             }
    864         }
    865     } else {
    866         for (psS32 i = 0 ; i < vecSize ; i++) {
    867             if (fit == PM_FIT_POLYNOMIAL) {
    868                 newVec->data.F32[i] = psPolynomial1DEval(myPoly, (psF32) i);
    869             } else if (fit == PM_FIT_SPLINE) {
    870                 newVec->data.F32[i] = psSpline1DEval(mySpline, (psF32) i);
    871             }
    872         }
    873     }
    874 
    875     psFree(mySpline);
    876     psFree(overscanVector);
    877     return(newVec);
    878 }
    879 
    880 
    881 
    882 /******************************************************************************
    883 UnbinOverscanVector(inImg, overscanVector, overScanAxis, nBin):  this private
    884 routine takes a psVector overscanVector that was previously binned by a factor
    885 of nBin, and then expands it to its original size, duplicated elements nBin
    886 times for each element in the input vector overscanVector.
    887  *****************************************************************************/
    888 static psVector *UnbinOverscanVector(
    889     psImage *inImg,
    890     psVector *overscanVector,
    891     pmOverscanAxis overScanAxis,
    892     psS32 nBin)
    893 {
    894     psS32 vecSize;
    895 
    896     if (overScanAxis == PM_OVERSCAN_ROWS) {
    897         vecSize = inImg->numCols;
    898     } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
    899         vecSize = inImg->numRows;
    900     }
    901 
    902     psVector *newVec = psVectorAlloc(vecSize, PS_TYPE_F32);
    903     for (psS32 i = 0 ; i < vecSize ; i++) {
    904         newVec->data.F32[i] = overscanVector->data.F32[i/nBin];
    905     }
    906 
    907     psFree(overscanVector);
    908     return(newVec);
    909 }
    910 
    911 
    912 /******************************************************************************
    913 SubtractVectorFromImage(inImg, overscanVector, overScanAxis):  this private
    914 routine subtracts the overscanVector column-wise or row-wise from inImg.
    915  *****************************************************************************/
    916 static psImage *SubtractVectorFromImage(
    917     psImage *inImg,
    918     psVector *overscanVector,
    919     pmOverscanAxis overScanAxis)
    920 {
    921     //
    922     // Subtract overscan vector row-wise from the image.
    923     //
    924     if (overScanAxis == PM_OVERSCAN_ROWS) {
    925         for (psS32 i=0;i<inImg->numCols;i++) {
    926             for (psS32 j=0;j<inImg->numRows;j++) {
    927                 inImg->data.F32[j][i]-= overscanVector->data.F32[i];
    928             }
    929         }
    930     }
    931 
    932     //
    933     // Subtract overscan vector column-wise from the image.
    934     //
    935     if (overScanAxis == PM_OVERSCAN_COLUMNS) {
    936         for (psS32 i=0;i<inImg->numRows;i++) {
    937             for (psS32 j=0;j<inImg->numCols;j++) {
    938                 inImg->data.F32[i][j]-= overscanVector->data.F32[i];
    939             }
    940         }
    941     }
    942 
    943     return(inImg);
    944 }
    945 
    946 
    947 
    948 typedef enum {
    949     PM_ERROR_NO_SUBTRACTION,
    950     PM_WARNING_NO_SUBTRACTION,
    951     PM_ERROR_NO_BIAS_SUBTRACT,
    952     PM_WARNING_NO_BIAS_SUBTRACT,
    953     PM_ERROR_NO_DARK_SUBTRACT,
    954     PM_WARNING_NO_DARK_SUBTRACT,
    955     PM_OKAY
    956 } pmSubtractBiasAssertStatus;
    957 /******************************************************************************
    958 AssertCodeOverscan(....) this private routine verifies that the various input
    959 parameters to pmSubtractBias() are correct for overscan subtraction.
    960  *****************************************************************************/
    961 pmSubtractBiasAssertStatus AssertCodeOverscan(
    962     pmReadout *in,
    963     void *fitSpec,
    964     pmFit fit,
    965     bool overscan,
    966     psStats *stat,
    967     int nBinOrig,
    968     const pmReadout *bias,
    969     const pmReadout *dark)
    970 {
    971 
    972     PS_ASSERT_READOUT_NON_NULL(in, PM_ERROR_NO_SUBTRACTION);
    973     PS_ASSERT_READOUT_NON_EMPTY(in, PM_ERROR_NO_SUBTRACTION);
    974     PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, PM_ERROR_NO_SUBTRACTION);
    975     PS_WARN_PTR_NON_NULL(in->parent);
    976     if (in->parent != NULL) {
    977         PS_WARN_PTR_NON_NULL(in->parent->concepts);
    978     }
    979 
    980     if (overscan == true) {
    981         pmOverscanAxis overScanAxis = GetOverscanAxis(in);
    982         PS_ASSERT_PTR_NON_NULL(stat, PM_ERROR_NO_SUBTRACTION);
    983         PS_ASSERT_PTR_NON_NULL(in->bias, PM_ERROR_NO_SUBTRACTION);
    984         PS_ASSERT_PTR_NON_NULL(in->bias->head, PM_ERROR_NO_SUBTRACTION);
    985         //
    986         // Check the type, size of each bias image.
    987         //
    988         psListElem *tmpOverscan = (psListElem *) in->bias->head;
    989         psS32 numOverscans = 0;
    990         while (NULL != tmpOverscan) {
    991             numOverscans++;
    992             psImage *myOverscanImage = (psImage *) tmpOverscan->data;
    993             PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, PM_ERROR_NO_SUBTRACTION);
    994             // XXX: Get this right with the rows and columns.
    995             if (overScanAxis == PM_OVERSCAN_ROWS) {
    996                 if (myOverscanImage->numRows != in->image->numRows) {
    997                     psLogMsg(__func__, PS_LOG_WARN,
    998                              "WARNING: pmSubtractBias.(): overscan image (# %d) has %d rows, input image has %d rows\n",
    999                              numOverscans, myOverscanImage->numCols, in->image->numRows);
    1000                     if (fit == PM_FIT_NONE) {
    1001                         psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vectors.  Set fit to PM_FIT_POLYNOMIAL or PM_FIT_SPLINE.\n");
    1002                         return(PM_ERROR_NO_SUBTRACTION);
    1003                     }
    1004                 }
    1005             } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
    1006                 if (myOverscanImage->numCols != in->image->numCols) {
    1007                     psLogMsg(__func__, PS_LOG_WARN,
    1008                              "WARNING: pmSubtractBias.(): overscan image (# %d) has %d columns, input image has %d columns\n",
    1009                              numOverscans, myOverscanImage->numCols, in->image->numCols);
    1010                     if (fit == PM_FIT_NONE) {
    1011                         psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vectors.  Set fit to PM_FIT_POLYNOMIAL or PM_FIT_SPLINE.\n");
    1012                         return(PM_ERROR_NO_SUBTRACTION);
    1013                     }
    1014                 }
    1015             } else if (overScanAxis != PM_OVERSCAN_ALL) {
    1016                 psError(PS_ERR_UNKNOWN, true, "Must specify and overscan axis.\n");
    1017                 return(PM_ERROR_NO_SUBTRACTION);
    1018             }
    1019             tmpOverscan = tmpOverscan->next;
    1020         }
    1021     } else {
    1022         if (fit != PM_FIT_NONE) {
    1023             psLogMsg(__func__, PS_LOG_WARN,
    1024                      "WARNING: pmSubtractBias.(): overscan is FALSE and fit is not PM_FIT_NONE.\n");
    1025             return(PM_WARNING_NO_SUBTRACTION);
    1026         }
    1027     }
    1028 
    1029     // XXX: I do not like the following spec since it's useless to specify
    1030     // a psSpline as the fitSpec.
    1031     if (0) {
    1032         if ((fitSpec == NULL) &&
    1033                 ((fit != PM_FIT_NONE) || (overscan == true))) {
    1034             psError(PS_ERR_UNKNOWN, true, "fitSpec is NULL and fit is not PM_FIT_NONE or overscan is TRUE.\n");
    1035             return(PM_ERROR_NO_SUBTRACTION);
    1036         }
    1037     }
    1038 
    1039     return(PM_OKAY);
    1040 }
    1041 
    1042 /******************************************************************************
    1043 AssertCodeBias(....) this private routine verifies that the various input
    1044 parameters to pmSubtractBias() are correct for bias subtraction.
    1045  *****************************************************************************/
    1046 static pmSubtractBiasAssertStatus AssertCodeBias(
    1047     pmReadout *in,
    1048     void *fitSpec,
    1049     pmFit fit,
    1050     bool overscan,
    1051     psStats *stat,
    1052     int nBinOrig,
    1053     const pmReadout *bias,
    1054     const pmReadout *dark)
    1055 {
    1056     if ((in->image->numRows + in->row0 - bias->row0) > bias->image->numRows) {
    1057         psError(PS_ERR_UNKNOWN,true, "bias image does not have enough rows.  Returning in image\n");
    1058         return(PM_ERROR_NO_BIAS_SUBTRACT);
    1059     }
    1060     if ((in->image->numCols + in->col0 - bias->col0) > bias->image->numCols) {
    1061         psError(PS_ERR_UNKNOWN,true, "bias image does not have enough columns.  Returning in image\n");
    1062         return(PM_ERROR_NO_BIAS_SUBTRACT);
    1063     }
    1064 
    1065     if (bias != NULL) {
    1066         PS_ASSERT_READOUT_NON_EMPTY(bias, PM_ERROR_NO_BIAS_SUBTRACT);
    1067         PS_ASSERT_READOUT_TYPE(bias, PS_TYPE_F32, PM_ERROR_NO_DARK_SUBTRACT);
    1068     }
    1069     return(PM_OKAY);
    1070 }
    1071 
    1072 /******************************************************************************
    1073 AssertCodeDark(....) this private routine verifies that the various input
    1074 parameters to pmSubtractBias() are correct for dark subtraction.
    1075  *****************************************************************************/
    1076 pmSubtractBiasAssertStatus AssertCodeDark(
    1077     pmReadout *in,
    1078     void *fitSpec,
    1079     pmFit fit,
    1080     bool overscan,
    1081     psStats *stat,
    1082     int nBinOrig,
    1083     const pmReadout *bias,
    1084     const pmReadout *dark)
    1085 {
    1086     if ((in->image->numRows + in->row0 - dark->row0) > dark->image->numRows) {
    1087         psError(PS_ERR_UNKNOWN, true, "dark image does not have enough rows.  Returning in image\n");
    1088         return(PM_ERROR_NO_DARK_SUBTRACT);
    1089     }
    1090     if ((in->image->numCols + in->col0 - dark->col0) > dark->image->numCols) {
    1091         psError(PS_ERR_UNKNOWN, true, "dark image does not have enough columns.  Returning in image\n");
    1092         return(PM_ERROR_NO_DARK_SUBTRACT);
    1093     }
    1094 
    1095     if (dark != NULL) {
    1096         PS_ASSERT_READOUT_NON_EMPTY(dark, PM_ERROR_NO_DARK_SUBTRACT);
    1097         PS_ASSERT_READOUT_TYPE(dark, PS_TYPE_F32, PM_ERROR_NO_DARK_SUBTRACT);
    1098     }
    1099     return(PM_OKAY);
    1100 }
    1101 
    1102 /******************************************************************************
    1103 p_psDetermineTrimmedImage(): global routine: determines the region of the
    1104 input pmReadout which will be operated on by the various detrend modules.  It
    1105 does a metadata fetch on "CELL.TRIMSEC" for the parent cell of the pmReadout.
    1106  
    1107 Use it this way:
    1108     PS_WARN_PTR_NON_NULL(in->parent);
    1109     if (in->parent != NULL) {
    1110         PS_WARN_PTR_NON_NULL(in->parent->concepts);
    1111     }
    1112     //
    1113     // Determine trimmed image from metadata.
    1114     //
    1115     psImage *trimmedImg = p_psDetermineTrimmedImage(in);
    1116  
    1117 XXX: Create a pmUtils.c file and put this routine there.
    1118  *****************************************************************************/
    1119 psImage *p_psDetermineTrimmedImage(pmReadout *in)
    1120 {
    1121     if ((in->parent == NULL) || (in->parent->concepts == NULL)) {
    1122         psLogMsg(__func__, PS_LOG_WARN,
    1123                  "WARNING: could not determine CELL.TRIMSEC from parent cell Metadata (NULL).\n");
    1124         return(in->image);
    1125     }
    1126 
    1127     psBool rc = false;
    1128     psImage *trimmedImg = NULL;
    1129     psRegion *trimRegion = psMetadataLookupPtr(&rc, in->parent->concepts,
    1130                            "CELL.TRIMSEC");
    1131     if (rc == false) {
    1132         psLogMsg(__func__, PS_LOG_WARN,
    1133                  "WARNING: could not determine CELL.TRIMSEC from parent cell Metadata.\n");
    1134         trimmedImg = in->image;
    1135     } else {
    1136         trimmedImg = psImageSubset(in->image, *trimRegion);
    1137     }
    1138 
    1139     return(trimmedImg);
    1140 }
    1141 
    1142 
    1143 
    1144 
    1145 
    1146 
    1147 
    1148 
    1149 
    1150 
    1151 
    1152 
    1153 
    1154 
    1155 
    1156 
    1157 
    1158 
    1159 /******************************************************************************
    1160 pmSubtractBias(....): see SDRS for complete specification.
    1161  
    1162 XXX: Code and assert type support: U16, S32, F32.
    1163 XXX: Add trace messages.
    1164  *****************************************************************************/
    1165 pmReadout *pmSubtractBias(
    1166     pmReadout *in,
    1167     void *fitSpec,
    1168     pmFit fit,
    1169     bool overscan,
    1170     psStats *stat,
    1171     int nBin,
    1172     const pmReadout *bias,
    1173     const pmReadout *dark)
     288pmReadout *pmSubtractBias(pmReadout *in,
     289                          void *fitSpec,
     290                          const psList *overscans,
     291                          pmOverscanAxis overScanAxis,
     292                          psStats *stat,
     293                          psS32 nBinOrig,
     294                          pmFit fit,
     295                          const pmReadout *bias)
    1174296{
    1175297    psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4,
    1176298            "---- pmSubtractBias() begin ----\n");
    1177     //
    1178     // Check input parameters, generate warnings and errors.
    1179     //
    1180     if (PM_OKAY != AssertCodeOverscan(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) {
    1181         return(in);
    1182     }
    1183     //
    1184     // Determine trimmed image from metadata.
    1185     //
    1186     psImage *trimmedImg = p_psDetermineTrimmedImage(in);
    1187 
    1188     //
    1189     // Subtract overscan frames if necessary.
    1190     //
    1191     if (overscan == true) {
    1192         pmOverscanAxis overScanAxis = GetOverscanAxis(in);
     299    PS_ASSERT_READOUT_NON_NULL(in, NULL);
     300    PS_ASSERT_READOUT_NON_EMPTY(in, NULL);
     301    PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL);
     302
     303    //
     304    // If the overscans != NULL, then check the type of each image.
     305    //
     306    if (overscans != NULL) {
     307        psListElem *tmpOverscan = (psListElem *) overscans->head;
     308        while (NULL != tmpOverscan) {
     309            psImage *myOverscanImage = (psImage *) tmpOverscan->data;
     310            PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL);
     311            tmpOverscan = tmpOverscan->next;
     312        }
     313    }
     314
     315    if ((overscans == NULL) && (overScanAxis != PM_OVERSCAN_NONE)) {
     316        psError(PS_ERR_UNKNOWN,true, "(overscans == NULL) && (overScanAxis != PM_OVERSCAN_NONE).  Returning in image\n");
     317        return(in);
     318    }
     319
     320    // Check for an unallowable pmFit.
     321    if ((fit != PM_OVERSCAN_NONE) &&
     322            (fit != PM_OVERSCAN_ROWS) &&
     323            (fit != PM_OVERSCAN_COLUMNS) &&
     324            (fit != PM_OVERSCAN_ALL)) {
     325        psError(PS_ERR_UNKNOWN, true, "fit is unallowable (%d).  Returning in image.\n", fit);
     326        return(in);
     327    }
     328    // Check for an unallowable pmOverscanAxis.
     329    if ((overScanAxis != PM_OVERSCAN_NONE) &&
     330            (overScanAxis != PM_OVERSCAN_ROWS) &&
     331            (overScanAxis != PM_OVERSCAN_COLUMNS) &&
     332            (overScanAxis != PM_OVERSCAN_ALL)) {
     333        psError(PS_ERR_UNKNOWN, true, "overScanAxis is unallowable (%d).  Returning in image.\n", overScanAxis);
     334        return(in);
     335    }
     336    psS32 i;
     337    psS32 j;
     338    psS32 numBins = 0;
     339    static psVector *overscanVector = NULL;
     340    psVector *tmpRow = NULL;
     341    psVector *tmpCol = NULL;
     342    psVector *myBin = NULL;
     343    psVector *binVec = NULL;
     344    psListElem *tmpOverscan = NULL;
     345    double statValue;
     346    psImage *myOverscanImage = NULL;
     347    psPolynomial1D *myPoly = NULL;
     348    psSpline1D *mySpline = NULL;
     349    psS32 nBin;
     350
     351    //
     352    //  Create a static stats data structure and determine the highest
     353    //  priority stats option.
     354    //
     355    static psStats *myStats = NULL;
     356    if (myStats == NULL) {
     357        myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
     358        p_psMemSetPersistent(myStats, true);
     359    }
     360    if (stat != NULL) {
     361        myStats->options = GenNewStatOptions(stat);
     362    }
     363
     364
     365    if (overScanAxis == PM_OVERSCAN_NONE) {
     366        if (fit != PM_FIT_NONE) {
     367            psLogMsg(__func__, PS_LOG_WARN,
     368                     "WARNING: pmSubtractBias.(): overScanAxis equals NONE, and fit does not equal NONE.  Proceeding to full fram subtraction.\n");
     369        }
     370
     371        if (overscans != NULL) {
     372            psLogMsg(__func__, PS_LOG_WARN,
     373                     "WARNING: pmSubtractBias.(): overScanAxis equals NONE and overscans does not equal NULL.  Proceeding to full fram subtraction.\n");
     374        }
     375        return(SubtractFrame(in, bias));
     376    }
     377
     378    if ((overScanAxis == PM_OVERSCAN_ALL) && (fit != PM_FIT_NONE)) {
     379        psLogMsg(__func__, PS_LOG_WARN,
     380                 "WARNING: pmSubtractBias.(): overScanAxis equals ALL, and fit does not equal NONE.  Proceeding with the rest of the module.\n");
     381    }
     382
     383
     384    //
     385    // We subtract each overscan region from the image data.
     386    // If we get here we know that overscans != NULL.
     387    //
     388
     389    if (overScanAxis == PM_OVERSCAN_ALL) {
     390        tmpOverscan = (psListElem *) overscans->head;
     391        while (NULL != tmpOverscan) {
     392            myOverscanImage = (psImage *) tmpOverscan->data;
     393
     394            PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL);
     395            psStats *rc = psImageStats(myStats, myOverscanImage, NULL, (psMaskType)0xffffffff);
     396            if (rc == NULL) {
     397                psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
     398                return(in);
     399            }
     400            if (false == p_psGetStatValue(myStats, &statValue)) {
     401                psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
     402                return(in);
     403            }
     404            ImageSubtractScalar(in->image, statValue);
     405
     406            tmpOverscan = tmpOverscan->next;
     407        }
     408        return(in);
     409    }
     410
     411    // This check is redundant with above code.
     412    if (!((overScanAxis == PM_OVERSCAN_ROWS) || (overScanAxis == PM_OVERSCAN_COLUMNS))) {
     413        psError(PS_ERR_UNKNOWN, true, "overScanAxis is unallowable (%d).\nReturning in image.\n", overScanAxis);
     414        return(in);
     415    }
     416
     417    tmpOverscan = (psListElem *) overscans->head;
     418    while (NULL != tmpOverscan) {
     419        //        PS_IMAGE_PRINT_F32_HIDEF(in->image);
     420        myOverscanImage = (psImage *) tmpOverscan->data;
     421
     422        if (overScanAxis == PM_OVERSCAN_ROWS) {
     423            if (myOverscanImage->numCols != (in->image)->numCols) {
     424                psLogMsg(__func__, PS_LOG_WARN,
     425                         "WARNING: pmSubtractBias.(): overscan image has %d columns, input image has %d columns\n",
     426                         myOverscanImage->numCols, in->image->numCols);
     427            }
     428
     429            // We create a row vector and subtract this vector from image.
     430            // XXX: Is there a better way to extract a psVector from a psImage without
     431            // having to copy every element in that vector?
     432            overscanVector = psVectorAlloc(myOverscanImage->numCols, PS_TYPE_F32);
     433            for (i=0;i<overscanVector->n;i++) {
     434                overscanVector->data.F32[i] = 0.0;
     435            }
     436            tmpRow = psVectorAlloc(myOverscanImage->numRows, PS_TYPE_F32);
     437
     438            // For each column of the input image, loop through every row,
     439            // collect the pixel in that row, then performed the specified
     440            // statistical op on those pixels.  Store this in overscanVector.
     441            for (i=0;i<myOverscanImage->numCols;i++) {
     442                for (j=0;j<myOverscanImage->numRows;j++) {
     443                    tmpRow->data.F32[j] = myOverscanImage->data.F32[j][i];
     444                }
     445                psStats *rc = psVectorStats(myStats, tmpRow, NULL, NULL, 0);
     446                if (rc == NULL) {
     447                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
     448                    return(in);
     449                }
     450                if (false ==  p_psGetStatValue(rc, &statValue)) {
     451                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
     452                    return(in);
     453                }
     454                overscanVector->data.F32[i] = statValue;
     455            }
     456            psFree(tmpRow);
     457
     458            // Scale the overscan vector to the size of the input image.
     459            if (overscanVector->n != in->image->numCols) {
     460                if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) {
     461                    psVector *newVec = ScaleOverscanVector(overscanVector,
     462                                                           in->image->numCols,
     463                                                           fitSpec, fit);
     464                    if (newVec == NULL) {
     465                        psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.  Returning in image.\n");
     466                        return(in);
     467                    }
     468                    psFree(overscanVector);
     469                    overscanVector = newVec;
     470                } else {
     471                    psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vector.  Set fit to PM_FIT_SPLINE or PM_FIT_POLYNOMIAL.  Returning in image.\n");
     472                    psFree(overscanVector);
     473                    return(in);
     474                }
     475            }
     476        }
     477
     478        if (overScanAxis == PM_OVERSCAN_COLUMNS) {
     479            if (myOverscanImage->numRows != (in->image)->numRows) {
     480                psLogMsg(__func__, PS_LOG_WARN,
     481                         "WARNING: pmSubtractBias.(): overscan image has %d rows, input image has %d rows\n",
     482                         myOverscanImage->numRows, in->image->numRows);
     483            }
     484
     485            // We create a column vector and subtract this vector from image.
     486            overscanVector = psVectorAlloc(myOverscanImage->numRows, PS_TYPE_F32);
     487            for (i=0;i<overscanVector->n;i++) {
     488                overscanVector->data.F32[i] = 0.0;
     489            }
     490            tmpCol = psVectorAlloc(myOverscanImage->numCols, PS_TYPE_F32);
     491
     492            // For each row of the input image, loop through every column,
     493            // collect the pixel in that row, then performed the specified
     494            // statistical op on those pixels.  Store this in overscanVector.
     495            for (i=0;i<myOverscanImage->numRows;i++) {
     496                for (j=0;j<myOverscanImage->numCols;j++) {
     497                    tmpCol->data.F32[j] = myOverscanImage->data.F32[i][j];
     498                }
     499                psStats *rc = psVectorStats(myStats, tmpCol, NULL, NULL, 0);
     500                if (rc == NULL) {
     501                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
     502                    return(in);
     503                }
     504                if (false ==  p_psGetStatValue(rc, &statValue)) {
     505                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
     506                    return(in);
     507                }
     508                overscanVector->data.F32[i] = statValue;
     509            }
     510            psFree(tmpCol);
     511
     512            // Scale the overscan vector to the size of the input image.
     513            if (overscanVector->n != in->image->numRows) {
     514                if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) {
     515                    psVector *newVec = ScaleOverscanVector(overscanVector,
     516                                                           in->image->numRows,
     517                                                           fitSpec, fit);
     518                    if (newVec == NULL) {
     519                        psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.  Returning in image.\n");
     520                        return(in);
     521                    }
     522                    psFree(overscanVector);
     523                    overscanVector = newVec;
     524                } else {
     525                    psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vector.  Set fit to PM_FIT_SPLINE or PM_FIT_POLYNOMIAL.  Returning in image.\n");
     526                    psFree(overscanVector);
     527                    return(in);
     528                }
     529            }
     530        }
     531
    1193532        //
    1194         //  Create a psStats data structure and determine the highest
    1195         //  priority stats option.
     533        // Re-bin the overscan vector (change its length).
    1196534        //
    1197         psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
    1198         if (stat != NULL) {
    1199             myStats->options = GenNewStatOptions(stat);
    1200         }
    1201 
     535        // Only if nBinOrig > 1.
     536        if ((nBinOrig > 1) && (nBinOrig < overscanVector->n)) {
     537            numBins = 1+((overscanVector->n)/nBinOrig);
     538            myBin = psVectorAlloc(numBins, PS_TYPE_F32);
     539            binVec = psVectorAlloc(nBinOrig, PS_TYPE_F32);
     540
     541            for (i=0;i<numBins;i++) {
     542                for(j=0;j<nBinOrig;j++) {
     543                    if (overscanVector->n > ((i*nBinOrig)+j)) {
     544                        binVec->data.F32[j] = overscanVector->data.F32[(i*nBinOrig)+j];
     545                    } else {
     546                        // XXX: we get here if nBinOrig does not evenly divide
     547                        // the overscanVector vector.  This is the last bin.  Should
     548                        // we change the binVec->n to acknowledge that?
     549                        binVec->n = j;
     550                    }
     551                }
     552                psStats *rc = psVectorStats(myStats, binVec, NULL, NULL, 0);
     553                if (rc == NULL) {
     554                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
     555                    return(in);
     556                }
     557                if (false ==  p_psGetStatValue(rc, &statValue)) {
     558                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
     559                    return(in);
     560                }
     561                myBin->data.F32[i] = statValue;
     562            }
     563
     564            // Change the effective size of overscanVector.
     565            overscanVector->n = numBins;
     566            for (i=0;i<numBins;i++) {
     567                overscanVector->data.F32[i] = myBin->data.F32[i];
     568            }
     569            psFree(binVec);
     570            psFree(myBin);
     571            nBin = nBinOrig;
     572        } else {
     573            nBin = 1;
     574        }
     575
     576        // At this point the number of data points in overscanVector should be
     577        // equal to the number of rows/columns (whatever is appropriate) in the
     578        // image divided by numBins.
    1202579        //
    1203         // Reduce overscan images to a single pixel, then subtract.
    1204         // This code is no longer required as of SDRS 12-09.
     580
     581
    1205582        //
    1206         if (overScanAxis == PM_OVERSCAN_ALL) {
    1207             if (false == OverscanReducePixel(trimmedImg, in->bias, myStats)) {
    1208                 return(in);
    1209             }
    1210             psFree(myStats);
     583        // This doesn't seem right.  The only way to do a spline fit is if,
     584        // by SDRS requirements, fitSpec is not-NULL>  But in order for it
     585        // to be non-NULL, someone must have called psSpline1DAlloc() with
     586        // the min, max, and number of splines.
     587        //
     588        if (!((fitSpec == NULL) || (fit == PM_FIT_NONE))) {
     589            //
     590            // Fit a polynomial or spline to the overscan vector.
     591            //
     592            if (fit == PM_FIT_POLYNOMIAL) {
     593                myPoly = (psPolynomial1D *) fitSpec;
     594                myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
     595                if (myPoly == NULL) {
     596                    psError(PS_ERR_UNKNOWN, false, "(3) Could not fit a polynomial to overscan vector.  Returning in image.\n");
     597                    psFree(overscanVector);
     598                    return(in);
     599                }
     600            } else if (fit == PM_FIT_SPLINE) {
     601                // XXX: This makes no sense
     602                // XXX: must free mySpline?
     603                mySpline = (psSpline1D *) fitSpec;
     604                mySpline = psVectorFitSpline1D(NULL, overscanVector);
     605                if (mySpline == NULL) {
     606                    psError(PS_ERR_UNKNOWN, false, "Could not fit a spline to overscan vector.  Returning in image.\n");
     607                    psFree(overscanVector);
     608                    return(in);
     609                }
     610            }
     611
     612            //
     613            // Subtract fitted overscan vector row-wise from the image.
     614            //
     615            if (overScanAxis == PM_OVERSCAN_ROWS) {
     616                for (i=0;i<(in->image)->numCols;i++) {
     617                    psF32 tmpF32 = 0.0;
     618                    if (fit == PM_FIT_POLYNOMIAL) {
     619                        tmpF32 = psPolynomial1DEval(myPoly, ((psF32) i) / ((psF32) nBin));
     620                    } else if (fit == PM_FIT_SPLINE) {
     621                        tmpF32 = psSpline1DEval(mySpline, ((psF32) i) / ((psF32) nBin));
     622                    }
     623                    for (j=0;j<(in->image)->numRows;j++) {
     624                        (in->image)->data.F32[j][i]-= tmpF32;
     625                    }
     626                }
     627            }
     628
     629            //
     630            // Subtract fitted overscan vector column-wise from the image.
     631            //
     632            if (overScanAxis == PM_OVERSCAN_COLUMNS) {
     633                for (i=0;i<(in->image)->numRows;i++) {
     634                    psF32 tmpF32 = 0.0;
     635                    if (fit == PM_FIT_POLYNOMIAL) {
     636                        tmpF32 = psPolynomial1DEval(myPoly, ((psF32) i) / ((psF32) nBin));
     637                    } else if (fit == PM_FIT_SPLINE) {
     638                        tmpF32 = psSpline1DEval(mySpline, ((psF32) i) / ((psF32) nBin));
     639                    }
     640
     641                    for (j=0;j<(in->image)->numCols;j++) {
     642                        (in->image)->data.F32[i][j]-= tmpF32;
     643                    }
     644                }
     645            }
    1211646        } else {
    1212647            //
    1213             // Reduce the overscan images to a single overscan vector.
    1214             //
    1215             psVector *overscanVector = OverscanReduce(in->image, overScanAxis,
    1216                                        in->bias, fitSpec,
    1217                                        fit, myStats);
    1218             if (overscanVector == NULL) {
    1219                 psError(PS_ERR_UNKNOWN, false, "Could not reduce overscan images to a single overscan vector.  Returning in image\n");
    1220                 psFree(myStats);
    1221                 return(in);
    1222             }
    1223 
    1224             //
    1225             // Rebin the overscan vector if necessary.
    1226             //
    1227             psS32 newBin = RebinOverscanVector(overscanVector, nBin, myStats);
    1228             if (newBin < 0) {
    1229                 psError(PS_ERR_UNKNOWN, false, "Could rebin the overscan vector.  Returning in image\n");
    1230                 psFree(myStats);
    1231                 return(in);
    1232             }
    1233 
    1234             //
    1235             // If necessary, fit a psPolynomial or psSpline to the overscan vector.
    1236             // Then, unbin the overscan vector to appropriate length for the in image.
    1237             //
    1238             if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) {
    1239                 overscanVector = FitOverscanVectorAndUnbin(trimmedImg, overscanVector, overScanAxis, fitSpec, fit, newBin);
    1240                 if (overscanVector == NULL) {
    1241                     psError(PS_ERR_UNKNOWN, false, "Could not fit the polynomial or spline to the overscan vector.  Returning in image\n");
    1242                     psFree(myStats);
    1243                     return(in);
    1244                 }
    1245             } else {
    1246                 overscanVector = UnbinOverscanVector(trimmedImg, overscanVector, overScanAxis, newBin);
    1247             }
    1248 
    1249             //
    1250             // Subtract the overscan vector from the input image.
    1251             //
    1252             SubtractVectorFromImage(trimmedImg, overscanVector, overScanAxis);
    1253             psFree(myStats);
    1254             psFree(overscanVector);
    1255         }
    1256     }
    1257 
    1258     //
    1259     // Perform bias subtraction if necessary.
    1260     //
    1261     if (bias != NULL) {
    1262         if (PM_OKAY == AssertCodeBias(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) {
    1263             SubtractFrame(in, bias);
    1264         }
    1265     }
    1266 
    1267     //
    1268     // Perform dark subtraction if necessary.
    1269     //
    1270     if (dark != NULL) {
    1271         if (PM_OKAY == AssertCodeDark(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) {
    1272             psBool rc;
    1273             psF32 scale = 0.0;
    1274             if (in->parent != NULL) {
    1275                 scale = psMetadataLookupS32(&rc, in->parent->concepts, "CELL.DARKTIME");
    1276                 if (rc == false) {
    1277                     psLogMsg(__func__, PS_LOG_WARN,
    1278                              "WARNING: pmSubtractBias.(): could not determine CELL.FARKTIME from in->parent metadata.\n");
    1279                 }
    1280             }
    1281             SubtractDarkFrame(in, dark, scale);
    1282         }
    1283     }
    1284 
    1285     //
    1286     // All done.
    1287     //
     648            // If we get here, then no polynomials were fit to the overscan
     649            // vector.  We simply subtract it, taking into account binning,
     650            // from the image.
     651            //
     652
     653            //
     654            // Subtract overscan vector row-wise from the image.
     655            //
     656            if (overScanAxis == PM_OVERSCAN_ROWS) {
     657                for (i=0;i<(in->image)->numCols;i++) {
     658                    for (j=0;j<(in->image)->numRows;j++) {
     659                        (in->image)->data.F32[j][i]-= overscanVector->data.F32[i/nBin];
     660                    }
     661                }
     662            }
     663
     664            //
     665            // Subtract overscan vector column-wise from the image.
     666            //
     667            if (overScanAxis == PM_OVERSCAN_COLUMNS) {
     668                for (i=0;i<(in->image)->numRows;i++) {
     669                    for (j=0;j<(in->image)->numCols;j++) {
     670                        (in->image)->data.F32[i][j]-= overscanVector->data.F32[i/nBin];
     671                    }
     672                }
     673            }
     674        }
     675
     676        psFree(overscanVector);
     677
     678        tmpOverscan = tmpOverscan->next;
     679    }
     680
    1288681    psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4,
    1289682            "---- pmSubtractBias() exit ----\n");
     683
     684    if (bias != NULL) {
     685        return(SubtractFrame(in, bias));
     686    }
    1290687    return(in);
    1291688}
Note: See TracChangeset for help on using the changeset viewer.