IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 26076


Ignore:
Timestamp:
Nov 9, 2009, 2:53:12 PM (17 years ago)
Author:
Paul Price
Message:

Merging branches/pap (unconvolved stacks, reworked combinePixels function into clearer roles, only throw out most variant pixel on each iteration, throw suspect pixels out as first rejection step) stack development. I think I've got everything, but not entirely sure, since I've already merged this branch once before (for dual convolution).

Location:
trunk
Files:
37 edited

Legend:

Unmodified
Added
Removed
  • trunk/ippconfig/recipes

  • trunk/ippconfig/recipes/filerules-mef.mdc

    r25997 r26076  
    263263PPSTACK.OUTPUT.MASK     OUTPUT {OUTPUT}.mk.fits                  MASK      COMP_MASK  FPA        TRUE      NONE
    264264PPSTACK.OUTPUT.VARIANCE OUTPUT {OUTPUT}.wt.fits                  VARIANCE  COMP_WT    FPA        TRUE      NONE
     265PPSTACK.UNCONV.MASK     OUTPUT {OUTPUT}.unconv.mask.fits         MASK      COMP_MASK  FPA        TRUE      NONE
     266PPSTACK.UNCONV.VARIANCE OUTPUT {OUTPUT}.unconv.wt.fits           VARIANCE  COMP_WT    FPA        TRUE      NONE
    265267PPSTACK.TARGET.PSF      OUTPUT {OUTPUT}.target.psf               PSF       NONE       CHIP       TRUE      NONE
    266268PPSTACK.CONV.KERNEL     OUTPUT {OUTPUT}.{FILE.INDEX}.kernel      SUBKERNEL NONE       FPA        TRUE      NONE
  • trunk/ippconfig/recipes/filerules-simple.mdc

    r25997 r26076  
    212212PPSTACK.OUTPUT.MASK     OUTPUT {OUTPUT}.mask.fits    MASK      NONE       FPA        TRUE      NONE
    213213PPSTACK.OUTPUT.VARIANCE OUTPUT {OUTPUT}.weight.fits  VARIANCE  NONE       FPA        TRUE      NONE
     214PPSTACK.UNCONV.MASK     OUTPUT {OUTPUT}.unconv.mask.fits MASK  COMP_MASK  FPA        TRUE      NONE
     215PPSTACK.UNCONV.VARIANCE OUTPUT {OUTPUT}.unconv.wt.fits VARIANCE COMP_WT   FPA        TRUE      NONE
    214216PPSTACK.TARGET.PSF      OUTPUT {OUTPUT}.target.psf   PSF       NONE       CHIP       TRUE      NONE
    215217PPSTACK.CONV.KERNEL     OUTPUT {OUTPUT}.{FILE.INDEX}.kernel SUBKERNEL NONE FPA       TRUE      NONE
  • trunk/ippconfig/recipes/filerules-split.mdc

    r25997 r26076  
    229229PPSTACK.OUTPUT.MASK     OUTPUT {OUTPUT}.mask.fits                MASK      COMP_MASK  FPA        TRUE      NONE
    230230PPSTACK.OUTPUT.VARIANCE OUTPUT {OUTPUT}.wt.fits                  VARIANCE  COMP_WT    FPA        TRUE      NONE
     231PPSTACK.UNCONV.MASK     OUTPUT {OUTPUT}.unconv.mask.fits         MASK      COMP_MASK  FPA        TRUE      NONE
     232PPSTACK.UNCONV.VARIANCE OUTPUT {OUTPUT}.unconv.wt.fits           VARIANCE  COMP_WT    FPA        TRUE      NONE
    231233PPSTACK.TARGET.PSF      OUTPUT {OUTPUT}.target.psf               PSF       NONE       CHIP       TRUE      NONE
    232234PPSTACK.CONV.KERNEL     OUTPUT {OUTPUT}.{FILE.INDEX}.kernel      SUBKERNEL NONE       FPA        TRUE      NONE
  • trunk/ippconfig/recipes/ppStack.config

    r26020 r26076  
    22
    33CONVOLVE        BOOL    TRUE            # Convolve images when stacking?
    4 ITER            S32     1               # Number of rejection iterations
     4COMBINE.ITER    F32     0.5             # Number of rejection iterations per input
    55COMBINE.REJ     F32     2.5             # Rejection threshold in combination (sigma)
    6 COMBINE.SYS     F32     0.03            # Relative systematic error in combination
     6COMBINE.SYS     F32     0.1             # Relative systematic error in combination
    77COMBINE.DISCARD F32     0.2             # Discard fraction for Olympic weighted mean
    8 MASK.VAL        STR     MASK.VALUE,CONV.BAD,BURNTOOL    # Mask value of input bad pixels
     8MASK.IN         STR     MASK.VALUE,CONV.BAD     # Mask value of input bad pixels
     9MASK.SUSPECT    STR     SUSPECT,CONV.POOR       # Mask value of suspect pixels
    910MASK.BAD        STR     BLANK           # Mask value to give bad pixels
    1011MASK.POOR       STR     CONV.POOR       # Mask value to give poor pixels
     
    3334PHOT.FRAC       F32     0.9             # Minimum fraction of good pixels
    3435
    35 ZP.RADIUS       F32     1.0             # Radius (pixels) for matching sources
     36ZP.RADIUS       F32     4.0             # Radius (pixels) for matching sources
    3637ZP.ITER.1       S32     5               # Iterations for zero point; pass 1
    3738ZP.ITER.2       S32     1000            # Iterations for zero point; pass 2
  • trunk/ippconfig/recipes/ppSub.config

    r26038 r26076  
    1515SYS.ERR         F32     0.1             # Relative systematic error in images
    1616
    17 MASK.IN         STR     MASK.VALUE,CONV.BAD     # Mask value for input
     17MASK.VAL        STR     MASK.VALUE,CONV.BAD     # Mask value for input
    1818MASK.BAD        STR     CONV.BAD        # Mask value to give bad pixels
    1919MASK.POOR       STR     CONV.POOR       # Mask value to give poor pixels
  • trunk/ppStack

  • trunk/ppStack/src/ppStack.h

    r23573 r26076  
    5959// Perform stacking on a readout
    6060//
    61 // Returns an array of pixels to inspect for each input image
     61// Returns two arrays: pixels to inspect for each input image, and pixels to reject for each input image.
    6262psArray *ppStackReadoutInitial(const pmConfig *config,   // Configuration
    6363                               pmReadout *outRO,   // Output readout
     
    8383                         const psArray *rejected, // Array with pixels rejected in each image
    8484                         const psVector *weightings, // Weighting factors for each image
    85                          const psVector *addVariance // Additional variance for rejection
     85                         const psVector *addVariance, // Additional variance for rejection
     86                         bool safety,                 // Enable safety switch?
     87                         const psVector *norm         // Normalisations to apply
    8688    );
    8789
  • trunk/ppStack/src/ppStackArguments.c

    r23841 r26076  
    148148    psMetadataAddStr(arguments, PS_LIST_TAIL, "-stamps", 0, "Stamps file with x,y,flux per line", NULL);
    149149    psMetadataAddStr(arguments, PS_LIST_TAIL, "-stats", 0, "Statistics file", NULL);
    150     psMetadataAddS32(arguments, PS_LIST_TAIL, "-iter", 0, "Number of rejection iterations", 0);
     150    psMetadataAddF32(arguments, PS_LIST_TAIL, "-combine-iter", 0, "Number of rejection iterations per input", NAN);
    151151    psMetadataAddF32(arguments, PS_LIST_TAIL, "-combine-rej", 0,
    152152                     "Combination rejection thresold (sigma)", NAN);
     
    250250    }
    251251
    252     VALUE_ARG_RECIPE_INT("-iter",              "ITER",            S32, 0);
     252    VALUE_ARG_RECIPE_FLOAT("-combine-iter",    "COMBINE.ITER",    F32);
    253253    VALUE_ARG_RECIPE_FLOAT("-combine-rej",     "COMBINE.REJ",     F32);
    254254    VALUE_ARG_RECIPE_FLOAT("-combine-sys",     "COMBINE.SYS",     F32);
     
    260260    VALUE_ARG_RECIPE_FLOAT("-poor-frac",       "POOR.FRACTION",   F32);
    261261
    262     valueArgRecipeStr(arguments, recipe, "-mask-val",  "MASK.VAL",  recipe);
     262    valueArgRecipeStr(arguments, recipe, "-mask-val",  "MASK.IN",   recipe);
    263263    valueArgRecipeStr(arguments, recipe, "-mask-bad",  "MASK.BAD",  recipe);
    264264    valueArgRecipeStr(arguments, recipe, "-mask-poor", "MASK.POOR", recipe);
  • trunk/ppStack/src/ppStackCamera.c

    r25519 r26076  
    233233
    234234    // Output image
    235     pmFPA *fpa = pmFPAConstruct(config->camera, config->cameraName); // FPA to contain the output
    236     if (!fpa) {
     235    pmFPA *outFPA = pmFPAConstruct(config->camera, config->cameraName); // FPA to contain the output
     236    if (!outFPA) {
    237237        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to construct an FPA from camera configuration.");
    238238        return false;
    239239    }
    240     pmFPAfile *output = pmFPAfileDefineOutput(config, fpa, "PPSTACK.OUTPUT");
    241     psFree(fpa);                        // Drop reference
     240    pmFPAfile *output = pmFPAfileDefineOutput(config, outFPA, "PPSTACK.OUTPUT");
     241    psFree(outFPA);                        // Drop reference
    242242    if (!output) {
    243243        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSTACK.OUTPUT"));
     
    250250    output->save = true;
    251251
    252     if (!pmFPAAddSourceFromFormat(fpa, "Stack", output->format)) {
     252    if (!pmFPAAddSourceFromFormat(outFPA, "Stack", output->format)) {
    253253        psError(PS_ERR_UNKNOWN, false, "Unable to generate output FPA.");
    254         psFree(fpa);
    255254        return false;
    256255    }
     
    294293        targetPSF->save = true;
    295294    }
     295
     296#if 1
     297    // Unconvolved stack
     298    pmFPA *unconvFPA = pmFPAConstruct(config->camera, config->cameraName); // FPA to contain unconvolved output
     299    if (!unconvFPA) {
     300        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to construct an FPA from camera configuration.");
     301        return false;
     302    }
     303    pmFPAfile *unConv = pmFPAfileDefineOutput(config, unconvFPA, "PPSTACK.UNCONV");
     304    psFree(unconvFPA);                  // Drop reference
     305    if (!unConv) {
     306        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSTACK.UNCONV"));
     307        return false;
     308    }
     309    if (unConv->type != PM_FPA_FILE_IMAGE) {
     310        psError(PS_ERR_IO, true, "PPSTACK.UNCONV is not of type IMAGE");
     311        return false;
     312    }
     313    unConv->save = true;
     314
     315    if (!pmFPAAddSourceFromFormat(unconvFPA, "Stack", unConv->format)) {
     316        psError(PS_ERR_UNKNOWN, false, "Unable to generate output FPA.");
     317        return false;
     318    }
     319
     320    // Unconvolved mask
     321    pmFPAfile *unconvMask = pmFPAfileDefineOutput(config, unconvFPA, "PPSTACK.UNCONV.MASK");
     322    if (!unconvMask) {
     323        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSTACK.UNCONV.MASK"));
     324        return false;
     325    }
     326    if (unconvMask->type != PM_FPA_FILE_MASK) {
     327        psError(PS_ERR_IO, true, "PPSTACK.UNCONV.MASK is not of type MASK");
     328        return false;
     329    }
     330    unconvMask->save = true;
     331
     332    // Unconvolved variance
     333    if (haveVariances) {
     334        pmFPAfile *unconvVariance = pmFPAfileDefineOutput(config, unconvFPA, "PPSTACK.UNCONV.VARIANCE");
     335        if (!unconvVariance) {
     336            psError(PS_ERR_IO, false, _("Unable to generate output file from PPSTACK.UNCONV.VARIANCE"));
     337            return false;
     338        }
     339        if (unconvVariance->type != PM_FPA_FILE_VARIANCE) {
     340            psError(PS_ERR_IO, true, "PPSTACK.UNCONV.VARIANCE is not of type VARIANCE");
     341            return false;
     342        }
     343        unconvVariance->save = true;
     344    }
     345#endif
    296346
    297347    // Output JPEGs
  • trunk/ppStack/src/ppStackCleanup.c

    r23341 r26076  
    7474
    7575        if (tempDelete) {
    76             psString imageResolved = pmConfigConvertFilename(options->imageNames->data[i],
     76            psString imageResolved = pmConfigConvertFilename(options->convImages->data[i],
    7777                                                             config, false, false);
    78             psString maskResolved = pmConfigConvertFilename(options->maskNames->data[i],
     78            psString maskResolved = pmConfigConvertFilename(options->convMasks->data[i],
    7979                                                            config, false, false);
    80             psString varianceResolved = pmConfigConvertFilename(options->varianceNames->data[i],
     80            psString varianceResolved = pmConfigConvertFilename(options->convVariances->data[i],
    8181                                                                config, false, false);
    8282            if (unlink(imageResolved) == -1 || unlink(maskResolved) == -1 ||
  • trunk/ppStack/src/ppStackCombineFinal.c

    r25096 r26076  
    1010#include "ppStackLoop.h"
    1111
    12 bool ppStackCombineFinal(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config)
     12//#define TESTING                         // Enable test output
     13
     14bool ppStackCombineFinal(pmReadout *target, ppStackThreadData *stack, psArray *covariances,
     15                         ppStackOptions *options, pmConfig *config, bool safe, bool normalise)
    1316{
    1417    psAssert(stack, "Require stack");
    1518    psAssert(options, "Require options");
    1619    psAssert(config, "Require configuration");
     20
     21    if (!target->mask) {
     22        target->mask = psImageAlloc(target->image->numCols, target->image->numRows, PS_TYPE_IMAGE_MASK);
     23    }
    1724
    1825    stack->lastScan = 0;            // Reset read
     
    3037        }
    3138
    32         // call: ppStackReadoutFinal(config, outRO, readouts, rejected)
     39        // call: ppStackReadoutFinal(config, target, readouts, rejected)
    3340        psThreadJob *job = psThreadJobAlloc("PPSTACK_FINAL_COMBINE"); // Job to start
     41        psArrayAdd(job->args, 1, target);
    3442        psArrayAdd(job->args, 1, thread);
    3543        psArrayAdd(job->args, 1, options);
    3644        psArrayAdd(job->args, 1, config);
     45        PS_ARRAY_ADD_SCALAR(job->args, safe, PS_TYPE_U8);
     46        PS_ARRAY_ADD_SCALAR(job->args, normalise, PS_TYPE_U8);
    3747        if (!psThreadJobAddPending(job)) {
    3848            psFree(job);
     
    4858
    4959    // Sum covariance matrices
    50     double sumWeights = 0.0;            // Sum of weights
    51     for (int i = 0; i < options->num; i++) {
    52         if (options->inputMask->data.U8[i]) {
    53             psFree(options->covariances->data[i]);
    54             options->covariances->data[i] = NULL;
    55             continue;
     60    if (covariances) {
     61        double sumWeights = 0.0;            // Sum of weights
     62        for (int i = 0; i < options->num; i++) {
     63            if (options->inputMask->data.U8[i]) {
     64                psFree(covariances->data[i]);
     65                covariances->data[i] = NULL;
     66                continue;
     67            }
     68            psKernel *covar = covariances->data[i]; // Covariance matrix
     69            if (!covar) {
     70                continue;
     71            }
     72            float weight = options->weightings->data.F32[i]; // Weight to apply
     73            psBinaryOp(covar->image, covar->image, "*", psScalarAlloc(weight, PS_TYPE_F32));
     74            sumWeights += weight;
    5675        }
    57         psKernel *covar = options->covariances->data[i]; // Covariance matrix
    58         if (!covar) {
    59             continue;
     76        if (sumWeights > 0.0) {
     77            target->covariance = psImageCovarianceSum(covariances);
     78            psBinaryOp(target->covariance->image, target->covariance->image, "/",
     79                       psScalarAlloc(sumWeights, PS_TYPE_F32));
     80            psImageCovarianceTransfer(target->variance, target->covariance);
    6081        }
    61         float weight = options->weightings->data.F32[i]; // Weight to apply
    62         psBinaryOp(covar->image, covar->image, "*", psScalarAlloc(weight, PS_TYPE_F32));
    63         sumWeights += weight;
    64     }
    65     if (sumWeights > 0.0) {
    66         pmReadout *outRO = options->outRO;  // Output readout
    67         outRO->covariance = psImageCovarianceSum(options->covariances);
    68         psBinaryOp(outRO->covariance->image, outRO->covariance->image, "/",
    69                    psScalarAlloc(sumWeights, PS_TYPE_F32));
    70         psFree(options->covariances); options->covariances = NULL;
    71         psImageCovarianceTransfer(outRO->variance, outRO->covariance);
     82    } else {
     83        target->covariance = psImageCovarianceNone();
    7284    }
    7385
    7486#ifdef TESTING
    75     pmStackVisualPlotTestImage(outRO->image, "combined_initial.fits");
    76     ppStackWriteImage("combined_final.fits", NULL, outRO->image, config);
     87    static int pass = 0;                // Pass through
     88    psString name = NULL;               // Name of file
     89    psStringAppend(&name, "combined_image_final_%d.fits", pass);
     90    pass++;
     91    ppStackWriteImage(name, NULL, target->image, config);
     92    psStringSubstitute(&name, "mask", "image");
     93    ppStackWriteImage(name, NULL, target->mask, config);
     94    psStringSubstitute(&name, "variance", "mask");
     95    ppStackWriteImage(name, NULL, target->variance, config);
     96    psFree(name);
     97
     98    pmStackVisualPlotTestImage(target->image, "combined_image_final.fits");
    7799#endif
    78100
  • trunk/ppStack/src/ppStackCombineInitial.c

    r23767 r26076  
    99#include "ppStack.h"
    1010#include "ppStackLoop.h"
     11
     12//#define TESTING                         // Enable test output
    1113
    1214bool ppStackCombineInitial(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config)
     
    6062    // Harvest the jobs, gathering the inspection lists
    6163    options->inspect = psArrayAlloc(options->num);
     64    options->rejected = psArrayAlloc(options->num);
    6265    for (int i = 0; i < options->num; i++) {
    6366        if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
     
    6568        }
    6669        options->inspect->data[i] = psArrayAllocEmpty(numChunk);
     70        options->rejected->data[i] = psArrayAllocEmpty(numChunk);
    6771    }
    6872    psThreadJob *job;               // Completed job
     
    7175                 "Job has incorrect type: %s", job->type);
    7276        psArray *results = job->results; // Results of job
     77        psAssert(results->n == 2, "Results array has wrong size!");
     78        psArray *inspect = results->data[0]; // Pixels to inspect
     79        psArray *reject = results->data[1];  // Pixels to reject
    7380        for (int i = 0; i < options->num; i++) {
    7481            if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    7582                continue;
    7683            }
    77             options->inspect->data[i] = psArrayAdd(options->inspect->data[i], 1, results->data[i]);
     84            options->inspect->data[i] = psArrayAdd(options->inspect->data[i], 1, inspect->data[i]);
     85            options->rejected->data[i] = psArrayAdd(options->rejected->data[i], 1, reject->data[i]);
    7886        }
    7987        psFree(job);
     
    8391
    8492#ifdef TESTING
    85     ppStackWriteImage("combined_initial.fits", NULL, outRO->image, config);
    86     pmStackVisualPlotTestImage(outRO->image, "combined_initial.fits");
     93    ppStackWriteImage("combined_image_initial.fits", NULL, options->outRO->image, config);
     94    ppStackWriteImage("combined_mask_initial.fits", NULL, options->outRO->mask, config);
     95    ppStackWriteImage("combined_variance_initial.fits", NULL, options->outRO->variance, config);
     96
     97    pmStackVisualPlotTestImage(options->outRO->image, "combined_image_initial.fits");
    8798#endif
    88 
    89     psFree(options->matchChi2); options->matchChi2 = NULL;
    90 
    9199
    92100    if (options->stats) {
  • trunk/ppStack/src/ppStackCombinePrepare.c

    r23576 r26076  
    3333    pmCell *outCell = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT"); // Output cell
    3434    options->outRO = pmReadoutAlloc(outCell); // Output readout
     35
     36    pmCell *unconvCell = pmFPAfileThisCell(config->files, view, "PPSTACK.UNCONV"); // Unconvolved cell
     37    options->unconvRO = pmReadoutAlloc(unconvCell); // Unconvolved readout
     38
    3539    psFree(view);
    3640
     
    3943    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
    4044    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
     45
    4146    if (!pmReadoutStackDefineOutput(options->outRO, col0, row0, numCols, numRows, true, true, maskBad)) {
    4247        psError(PS_ERR_UNKNOWN, false, "Unable to prepare output.");
     
    4449    }
    4550
     51    options->unconvRO->image = psImageCopy(NULL, options->outRO->image, PS_TYPE_F32);
     52//    options->unconvRO->mask = psImageCopy(NULL, options->outRO->mask, PS_TYPE_IMAGE_MASK);
     53    options->unconvRO->col0 = options->outRO->col0;
     54    options->unconvRO->row0 = options->outRO->row0;
     55
     56
     57
    4658    return true;
    4759}
  • trunk/ppStack/src/ppStackConvolve.c

    r23602 r26076  
    99#include "ppStack.h"
    1010#include "ppStackLoop.h"
     11
     12//#define TESTING
     13
    1114
    1215// Update the value of a concept
     
    3942    options->weightings = psVectorAlloc(num, PS_TYPE_F32); // Combination weightings for images (1/noise^2)
    4043    psVectorInit(options->weightings, 0.0);
    41     options->covariances = psArrayAlloc(num); // Covariance matrices
     44    options->origCovars = psArrayAlloc(num);
     45    options->convCovars = psArrayAlloc(num); // Covariance matrices
     46
     47    psVector *renorms = psVectorAlloc(num, PS_TYPE_F32); // Renormalisation values for variances
     48    psVectorInit(renorms, NAN);
    4249
    4350    psList *fpaList = psListAlloc(NULL); // List of input FPAs, for concept averaging
     
    7986        // Background subtraction, scaling and normalisation is performed automatically by the image matching
    8087        psTimerStart("PPSTACK_MATCH");
     88        options->origCovars->data[i] = psMemIncrRefCounter(readout->covariance);
    8189        if (!ppStackMatch(readout, options, i, config)) {
    8290            psErrorStackPrint(stderr, "Unable to match image %d --- ignoring.", i);
     
    8593            continue;
    8694        }
    87         options->covariances->data[i] = psMemIncrRefCounter(readout->covariance);
     95        options->convCovars->data[i] = psMemIncrRefCounter(readout->covariance);
     96
     97        float renorm = psMetadataLookupF32(NULL, readout->analysis, PM_READOUT_ANALYSIS_RENORM);
     98        if (!isfinite(renorm)) {
     99            renorm = 1.0;
     100        }
     101        renorms->data.F32[i] = renorm;
    88102
    89103        if (options->stats) {
     
    114128        pmHDU *hdu = readout->parent->parent->parent->hdu; // HDU for convolved image
    115129        assert(hdu);
    116         ppStackWriteImage(options->imageNames->data[i], hdu->header, readout->image, config);
     130        ppStackWriteImage(options->convImages->data[i], hdu->header, readout->image, config);
    117131        psMetadata *maskHeader = psMetadataCopy(NULL, hdu->header); // Copy of header, for mask
    118132        pmConfigMaskWriteHeader(config, maskHeader);
    119         ppStackWriteImage(options->maskNames->data[i], maskHeader, readout->mask, config);
     133        ppStackWriteImage(options->convMasks->data[i], maskHeader, readout->mask, config);
    120134        psFree(maskHeader);
    121         psImageCovarianceTransfer(readout->variance, readout->covariance);
    122         ppStackWriteImage(options->varianceNames->data[i], hdu->header, readout->variance, config);
     135        ppStackWriteImage(options->convVariances->data[i], hdu->header, readout->variance, config);
    123136#ifdef TESTING
    124137        {
     
    129142            psFree(name);
    130143        }
     144        {
     145            int numCols = readout->image->numCols, numRows = readout->image->numRows;
     146            psImage *sn = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     147            for (int y = 0; y < numRows; y++) {
     148                for (int x = 0; x < numCols; x++) {
     149                    sn->data.F32[y][x] = readout->image->data.F32[y][x] /
     150                        sqrtf(readout->variance->data.F32[y][x]);
     151                }
     152            }
     153            psString name = NULL;
     154            psStringAppend(&name, "signoise_%d.fits", i);
     155            ppStackWriteImage(name, hdu->header, sn, config);
     156            psFree(name);
     157            psFree(sn);
     158        }
    131159#endif
    132160
     
    149177    psFree(rng);
    150178
    151     psFree(options->norm); options->norm = NULL;
    152179    psFree(options->sourceLists); options->sourceLists = NULL;
    153180    psFree(options->psf); options->psf = NULL;
     
    211238            numGood = 0;                    // Number of good images
    212239            for (int i = 0; i < num; i++) {
    213               if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PPSTACK_MASK_ALL) {
     240                if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PPSTACK_MASK_ALL) {
    214241                    continue;
    215242                }
     
    233260    }
    234261
     262    // Correct chi^2 for renormalisation
     263    psBinaryOp(options->matchChi2, options->matchChi2, "/", renorms);
     264    for (int i = 0; i < num; i++) {
     265        psLogMsg("ppStack", PS_LOG_INFO, "Additional variance for image %d: %f\n",
     266                 i, options->matchChi2->data.F32[i]);
     267    }
     268    psFree(renorms);
     269
    235270    return true;
    236271}
  • trunk/ppStack/src/ppStackFiles.c

    r23357 r26076  
    2222/// Output files for the combination
    2323static char *filesCombine[] = { "PPSTACK.OUTPUT", "PPSTACK.OUTPUT.MASK", "PPSTACK.OUTPUT.VARIANCE",
     24                                "PPSTACK.UNCONV", "PPSTACK.UNCONV.MASK", "PPSTACK.UNCONV.VARIANCE",
    2425                                "PPSTACK.OUTPUT.JPEG1", "PPSTACK.OUTPUT.JPEG2", NULL };
    2526
  • trunk/ppStack/src/ppStackLoop.c

    r25738 r26076  
    5656    // Start threading
    5757    ppStackThreadInit();
    58     ppStackThreadData *stack = ppStackThreadDataSetup(options, config);
     58    ppStackThreadData *stack = ppStackThreadDataSetup(options, config, true);
    5959    if (!stack) {
    6060        psError(PS_ERR_IO, false, "Unable to initialise stack threads.");
     
    6262        return false;
    6363    }
    64     psFree(options->cells); options->cells = NULL;
    6564
    6665    // Prepare for combination
     
    9897    // Final combination
    9998    psTrace("ppStack", 2, "Final stack of convolved images....\n");
    100     if (!ppStackCombineFinal(stack, options, config)) {
     99    if (!ppStackCombineFinal(options->outRO, stack, options->convCovars, options, config, false, false)) {
    101100        psError(PS_ERR_UNKNOWN, false, "Unable to perform final combination.");
    102101        psFree(stack);
     
    106105    psLogMsg("ppStack", PS_LOG_INFO, "Stage 5: Final Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
    107106    ppStackMemDump("final");
    108 
    109107
    110108    // Clean up
     
    121119    psFree(stack);
    122120
     121#if 1
     122    // Unconvolved stack --- it's cheap to calculate, compared to everything else!
     123    if (options->convolve) {
     124        // Start threading
     125        ppStackThreadData *stack = ppStackThreadDataSetup(options, config, false);
     126        if (!stack) {
     127            psError(PS_ERR_IO, false, "Unable to initialise stack threads.");
     128            psFree(options);
     129            return false;
     130        }
     131        psTrace("ppStack", 2, "Stack of unconvolved images....\n");
     132        if (!ppStackCombineFinal(options->unconvRO, stack, options->origCovars, options, config, false, true)) {
     133            psError(PS_ERR_UNKNOWN, false, "Unable to perform unconvolved combination.");
     134            psFree(stack);
     135            psFree(options);
     136            return false;
     137        }
     138        psLogMsg("ppStack", PS_LOG_INFO, "Stage 7: Unconvolved Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
     139        ppStackMemDump("unconv");
     140
     141        psFree(stack);
     142    }
     143    psFree(options->cells); options->cells = NULL;
     144#endif
    123145
    124146    // Photometry
     
    129151        return false;
    130152    }
    131     psLogMsg("ppStack", PS_LOG_INFO, "Stage 7: Photometry Analysis: %f sec", psTimerClear("PPSTACK_STEPS"));
     153    psLogMsg("ppStack", PS_LOG_INFO, "Stage 8: Photometry Analysis: %f sec", psTimerClear("PPSTACK_STEPS"));
    132154    ppStackMemDump("photometry");
    133 
    134155
    135156    // Finish up
     
    140161        return false;
    141162    }
    142     psLogMsg("ppStack", PS_LOG_INFO, "Stage 8: Final output: %f sec", psTimerClear("PPSTACK_STEPS"));
     163    psLogMsg("ppStack", PS_LOG_INFO, "Stage 9: Final output: %f sec", psTimerClear("PPSTACK_STEPS"));
    143164    ppStackMemDump("finish");
    144165
    145166    psFree(options);
     167
    146168    return true;
    147169}
  • trunk/ppStack/src/ppStackLoop.h

    r23576 r26076  
    5656// Final combination
    5757bool ppStackCombineFinal(
     58    pmReadout *target,                  // Target readout
    5859    ppStackThreadData *stack,           // Stack
     60    psArray *covariances,               // Covariances
    5961    ppStackOptions *options,            // Options
    60     pmConfig *config                    // Configuration
     62    pmConfig *config,                   // Configuration
     63    bool safe,                          // Allow safe combination?
     64    bool norm                           // Normalise images?
    6165    );
    6266
  • trunk/ppStack/src/ppStackMatch.c

    r26074 r26076  
    167167    )
    168168{
     169#if 1
    169170    bool mdok; // Status of metadata lookups
    170171
     
    192193    psImageMaskType maskBad = pmConfigMaskGet("BLANK", config); // Bits to mask
    193194
     195    psImageCovarianceTransfer(readout->variance, readout->covariance);
    194196    return pmReadoutVarianceRenormalise(readout, maskBad, num, minValid, maxValid);
     197#else
     198    return true;
     199#endif
    195200}
    196201
     
    212217    int size = psMetadataLookupS32(NULL, ppsub, "KERNEL.SIZE"); // Kernel half-size
    213218
    214     psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of bits to mask going in
     219    psString maskValStr = psMetadataLookupStr(NULL, ppsub, "MASK.VAL"); // Name of bits to mask going in
    215220    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
    216221    psString maskPoorStr = psMetadataLookupStr(NULL, recipe, "MASK.POOR"); // Name of bits to mask for poor
     
    257262            psFitsClose(fits);
    258263
    259             if (!readImage(&readout->image, options->imageNames->data[index], config) ||
    260                 !readImage(&readout->mask, options->maskNames->data[index], config) ||
    261                 !readImage(&readout->variance, options->varianceNames->data[index], config)) {
     264            if (!readImage(&readout->image, options->convImages->data[index], config) ||
     265                !readImage(&readout->mask, options->convMasks->data[index], config) ||
     266                !readImage(&readout->variance, options->convVariances->data[index], config)) {
    262267                psError(PS_ERR_IO, false, "Unable to read previously produced image.");
    263268                return false;
     
    378383            }
    379384#endif
     385
     386            fprintf(stderr, "vf = %f\n", psImageCovarianceFactor(readout->covariance));
     387
    380388
    381389            if (threads > 0) {
     
    517525            psFree(iter);
    518526            options->matchChi2->data.F32[index] = sum / (psImageCovarianceFactor(readout->covariance) * num);
     527            fprintf(stderr, "chi2 = %f ; vf = %f\n", sum/num, psImageCovarianceFactor(readout->covariance));
    519528        }
    520529
     
    543552    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
    544553    if (!psImageBackground(bg, NULL, readout->image, readout->mask, maskVal | maskBad, rng)) {
    545       psWarning("Can't measure background for image.");
    546       psErrorClear();
     554        psWarning("Can't measure background for image.");
     555        psErrorClear();
    547556    } else {
    548       if (!psMetadataLookupBool(NULL, config->arguments, "PPSTACK.SKIP.BG.SUB")) {
    549         psLogMsg("ppStack", PS_LOG_INFO, "Correcting convolved image background by %lf (+/- %lf)",
    550                  psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN), psStatsGetValue(bg, PS_STAT_ROBUST_STDEV));
    551         (void)psBinaryOp(readout->image, readout->image, "-",
    552                          psScalarAlloc(psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN), PS_TYPE_F32));
    553       }
     557        if (!psMetadataLookupBool(NULL, config->arguments, "PPSTACK.SKIP.BG.SUB")) {
     558            psLogMsg("ppStack", PS_LOG_INFO, "Correcting convolved image background by %lf (+/- %lf)",
     559                     psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN), psStatsGetValue(bg, PS_STAT_ROBUST_STDEV));
     560            (void)psBinaryOp(readout->image, readout->image, "-",
     561                             psScalarAlloc(psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN), PS_TYPE_F32));
     562        }
    554563    }
    555564
     
    569578    options->weightings->data.F32[index] = 1.0 / (psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN) *
    570579                                                  psImageCovarianceFactor(readout->covariance));
     580    psLogMsg("ppStack", PS_LOG_INFO, "Weighting for image %d is %f\n",
     581             index, options->weightings->data.F32[index]);
    571582    psMetadataAddF32(readout->analysis, PS_LIST_TAIL, "PPSTACK.WEIGHTING", 0,
    572583                     "Weighting by 1/noise^2 for stack", options->weightings->data.F32[index]);
  • trunk/ppStack/src/ppStackOptions.c

    r23573 r26076  
    1212        fclose(options->statsFile);
    1313    }
    14     psFree(options->imageNames);
    15     psFree(options->maskNames);
    16     psFree(options->varianceNames);
     14    psFree(options->origImages);
     15    psFree(options->origMasks);
     16    psFree(options->origVariances);
     17    psFree(options->origCovars);
     18    psFree(options->convImages);
     19    psFree(options->convMasks);
     20    psFree(options->convVariances);
    1721    psFree(options->psf);
    1822    psFree(options->inputMask);
     
    2428    psFree(options->matchChi2);
    2529    psFree(options->weightings);
    26     psFree(options->covariances);
     30    psFree(options->convCovars);
    2731    psFree(options->outRO);
     32    psFree(options->unconvRO);
    2833    psFree(options->inspect);
    2934    psFree(options->rejected);
     
    3944    options->stats = NULL;
    4045    options->statsFile = NULL;
    41     options->imageNames = NULL;
    42     options->maskNames = NULL;
    43     options->varianceNames = NULL;
     46    options->origImages = NULL;
     47    options->origMasks = NULL;
     48    options->origVariances = NULL;
     49    options->origCovars = NULL;
     50    options->convImages = NULL;
     51    options->convMasks = NULL;
     52    options->convVariances = NULL;
    4453    options->num = 0;
    4554    options->psf = NULL;
     
    5463    options->matchChi2 = NULL;
    5564    options->weightings = NULL;
    56     options->covariances = NULL;
     65    options->convCovars = NULL;
    5766    options->outRO = NULL;
     67    options->unconvRO = NULL;
    5868    options->inspect = NULL;
    5969    options->rejected = NULL;
  • trunk/ppStack/src/ppStackOptions.h

    r23573 r26076  
    1111    psMetadata *stats;                  // Statistics for output
    1212    FILE *statsFile;                    // File to which to write statistics
    13     psArray *imageNames, *maskNames, *varianceNames; // Filenames for the temporary convolved images
     13    psArray *origImages, *origMasks, *origVariances; // Filenames of the original images
     14    psArray *convImages, *convMasks, *convVariances; // Filenames for the temporary convolved images
     15    psArray *origCovars;                // Original covariances matrices
    1416    int num;                            // Number of inputs
    1517    // Prepare
     
    2628    psVector *matchChi2;                // chi^2 for stamps from matching
    2729    psVector *weightings;               // Combination weightings for images (1/noise^2)
    28     psArray *covariances;               // Covariance matrices
     30    psArray *convCovars;                // Convolved covariance matrices
    2931    // Combine initial
    3032    pmReadout *outRO;                   // Output readout
     33    pmReadout *unconvRO;                // Unconvolved readout
    3134    psArray *inspect;                   // Array of arrays of pixels to inspect
    3235    // Rejection
  • trunk/ppStack/src/ppStackReadout.c

    r23577 r26076  
    2525    psVector *addVariance = options->matchChi2; // Additional variance when rejecting
    2626
    27     psArray *inspect = ppStackReadoutInitial(config, outRO, thread->readouts, mask,
    28                                              weightings, addVariance);
    29 
    30     job->results = inspect;
     27    job->results = ppStackReadoutInitial(config, outRO, thread->readouts, mask,
     28                                         weightings, addVariance);
    3129    thread->busy = false;
    3230
    33     return inspect ? true : false;
     31    return job->results ? true : false;
    3432}
    3533
     
    3937
    4038    psArray *args = job->args;          // Arguments
    41     ppStackThread *thread = args->data[0]; // Thread
    42     ppStackOptions *options = args->data[1]; // Options
    43     pmConfig *config = args->data[2];   // Configuration
    44 
    45     pmReadout *outRO = options->outRO;  // Output readout
     39    pmReadout *target = args->data[0];  // Output readout
     40    ppStackThread *thread = args->data[1]; // Thread
     41    ppStackOptions *options = args->data[2]; // Options
     42    pmConfig *config = args->data[3];   // Configuration
     43    bool safety = PS_SCALAR_VALUE(args->data[4], U8);    // Safety switch on?
     44    bool normalise = PS_SCALAR_VALUE(args->data[5], U8); // Normalise images?
     45
    4646    psVector *mask = options->inputMask; // Mask for inputs
    4747    psArray *rejected = options->rejected; // Rejected pixels
    4848    psVector *weightings = options->weightings; // Weightings (1/noise^2) for each image
    4949    psVector *addVariance = options->matchChi2; // Additional variance when rejecting
    50 
    51     bool status = ppStackReadoutFinal(config, outRO, thread->readouts, mask, rejected,
    52                                       weightings, addVariance); // Status of operation
     50    psVector *norm = normalise ? options->norm : NULL; // Normalisations to apply to images
     51
     52    bool status = ppStackReadoutFinal(config, target, thread->readouts, mask, rejected,
     53                                      weightings, addVariance, safety, norm); // Status of operation
    5354
    5455    thread->busy = false;
     
    6364
    6465    psArray *args = job->args;  // Input arguments
    65     psArray *inspect = args->data[0]; // Array of pixel arrays
    66     int index = PS_SCALAR_VALUE(args->data[1], S32); // Index of interest
    67 
    68     psArray *inputs = inspect->data[index]; // Array of interest
    69     psPixels *output = NULL;    // Output pixel list
    70     for (int i = 0; i < inputs->n; i++) {
    71         psPixels *input = inputs->data[i]; // Input pixel list
    72         if (!input || input->n == 0) {
    73             continue;
    74         }
    75         output = psPixelsConcatenate(output, input);
    76     }
    77 
    78     if (!output) {
    79         // If there are no pixels to inspect, then just fake it
    80         output = psPixelsAllocEmpty(0);
    81     }
    82 
    83     psFree(inputs);
    84     inspect->data[index] = output;
     66    psArray *inspects = args->data[0]; // Array of pixel arrays
     67    psArray *rejects = args->data[1];  // Array of pixel arrays
     68    int index = PS_SCALAR_VALUE(args->data[2], S32); // Index of interest
     69
     70    psArray *inInspects = inspects->data[index]; // Array of interest
     71    psArray *inRejects = rejects->data[index]; // Array of interest
     72    psAssert(inInspects->n == inRejects->n, "Size should be the same");
     73    psPixels *outInspect = NULL, *outReject = NULL; // Output pixel lists
     74    for (int i = 0; i < inInspects->n; i++) {
     75        psPixels *inInspect = inInspects->data[i]; // Input pixel list
     76        if (inInspect && inInspect->n > 0) {
     77            outInspect = psPixelsConcatenate(outInspect, inInspect);
     78        }
     79        psPixels *inReject = inRejects->data[i]; // Input pixel list
     80        if (inReject && inReject->n > 0) {
     81            outReject = psPixelsConcatenate(outReject, inReject);
     82        }
     83    }
     84
     85    // If there are no pixels to inspect, then just fake it
     86    if (!outInspect) {
     87        outInspect = psPixelsAllocEmpty(0);
     88    }
     89    if (!outReject) {
     90        outReject = psPixelsAllocEmpty(0);
     91    }
     92
     93    psFree(inspects->data[index]);
     94    inspects->data[index] = outInspect;
     95    psFree(rejects->data[index]);
     96    rejects->data[index] = outReject;
    8597
    8698    return true;
     
    104116
    105117    bool mdok;                          // Status of MD lookup
    106     int iter = psMetadataLookupS32(NULL, recipe, "ITER"); // Rejection iterations
     118    float iter = psMetadataLookupF32(NULL, recipe, "COMBINE.ITER"); // Rejection iterations
    107119    float combineRej = psMetadataLookupF32(NULL, recipe, "COMBINE.REJ"); // Combination threshold
    108120    float combineSys = psMetadataLookupF32(NULL, recipe, "COMBINE.SYS"); // Combination systematic error
     
    114126    int kernelSize = psMetadataLookupS32(NULL, ppsub, "KERNEL.SIZE"); // Kernel half-size
    115127
    116     psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of bits to mask going in
     128    psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.IN"); // Name of bits to mask going in
    117129    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
     130    psString maskSuspectStr = psMetadataLookupStr(NULL, recipe, "MASK.SUSPECT"); // Name of suspect mask bits
     131    psImageMaskType maskSuspect = pmConfigMaskGet(maskSuspectStr, config); // Suspect bits
    118132    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
    119133    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
     
    149163    }
    150164
    151     if (!pmStackCombine(outRO, stack, maskVal | maskBad, maskBad, kernelSize, iter,
    152                         combineRej, combineSys, combineDiscard, true, useVariance, safe, false)) {
     165    if (!pmStackCombine(outRO, stack, maskVal | maskBad, maskSuspect, maskBad, kernelSize, iter,
     166                        combineRej, combineSys, combineDiscard, useVariance, safe, false)) {
    153167        psError(PS_ERR_UNKNOWN, false, "Unable to combine input readouts with rejection.");
    154168        psFree(stack);
     
    156170    }
    157171
    158     // Save list of pixels to inspect
     172    // Save lists of pixels
    159173    psArray *inspect = psArrayAlloc(num); // List of pixels to inspect
     174    psArray *reject = psArrayAlloc(num);  // List of pixels rejected
    160175    for (int i = 0; i < num; i++) {
    161176        pmStackData *data = stack->data[i]; // Data for this image
     
    168183        }
    169184        inspect->data[i] = psMemIncrRefCounter(data->inspect);
     185        reject->data[i] = psMemIncrRefCounter(data->reject);
    170186    }
    171187    psFree(stack);
     
    173189    sectionNum++;
    174190
    175     return inspect;
     191    psArray *results = psArrayAlloc(2); // Array of results
     192    results->data[0] = inspect;
     193    results->data[1] = reject;
     194
     195    return results;
    176196}
    177197
     
    180200bool ppStackReadoutFinal(const pmConfig *config, pmReadout *outRO, const psArray *readouts,
    181201                         const psVector *mask, const psArray *rejected, const psVector *weightings,
    182                          const psVector *addVariance)
     202                         const psVector *addVariance, bool safety, const psVector *norm)
    183203{
    184204    assert(config);
     
    196216
    197217    bool mdok;                          // Status of MD lookup
    198     int iter = psMetadataLookupS32(NULL, recipe, "ITER"); // Rejection iterations
    199     float combineRej = psMetadataLookupF32(NULL, recipe, "COMBINE.REJ"); // Combination threshold
    200     float combineSys = psMetadataLookupF32(NULL, recipe, "COMBINE.SYS"); // Combination systematic error
    201     float combineDiscard = psMetadataLookupF32(NULL, recipe, "COMBINE.DISCARD"); // Olympic discard fraction
    202218    bool useVariance = psMetadataLookupBool(&mdok, recipe, "VARIANCE"); // Use variance for rejection?
    203219    bool safe = psMetadataLookupBool(&mdok, recipe, "SAFE"); // Be safe when combining small numbers of pixels
    204220
    205     psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.VAL"); // Name of bits to mask going in
     221    psString maskValStr = psMetadataLookupStr(NULL, recipe, "MASK.IN"); // Name of bits to mask going in
    206222    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
     223    psString maskSuspectStr = psMetadataLookupStr(NULL, recipe, "MASK.SUSPECT"); // Name of suspect mask bits
     224    psImageMaskType maskSuspect = pmConfigMaskGet(maskSuspectStr, config); // Suspect bits
    207225    psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits to mask for bad
    208226    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
     
    211229    psArray *stack = psArrayAlloc(num); // Array for stacking
    212230
    213     bool entire = true;                 // Combine entire image?
    214     if (rejected) {
    215         // We have rejection from a previous combination: combine without flagging pixels to inspect
    216         entire = false;
    217         safe = false;
    218         iter = 0;
    219         combineRej = NAN;
    220         combineSys = NAN;
    221     }
     231    // We have rejection from a previous combination: combine without flagging pixels to inspect
     232    safe &= safety;
     233    int iter = 0;
     234    float combineRej = NAN;
     235    float combineSys = NAN;
     236    float combineDiscard = NAN;
    222237
    223238    for (int i = 0; i < num; i++) {
    224239        pmReadout *ro = readouts->data[i];
    225         if (mask->data.U8[i] & (PPSTACK_MASK_REJECT | PPSTACK_MASK_BAD)) {
    226             // Image completely rejected since previous combination
    227             entire = true;
    228             continue;
    229         } else if (mask->data.U8[i]) {
    230             // Image completely rejected before original combination
     240        if (mask->data.U8[i]) {
     241            // Image completely rejected
    231242            continue;
    232243        }
     
    252263        data->reject = rejected ? psMemIncrRefCounter(rejected->data[i]) : NULL;
    253264        stack->data[i] = data;
    254     }
    255 
    256     if (!pmStackCombine(outRO, stack, maskVal | maskBad, maskBad, 0,
    257                         iter, combineRej, combineSys, combineDiscard,
    258                         entire, useVariance, safe, !rejected)) {
     265
     266        if (norm) {
     267            float normalise = powf(10.0, -0.4 * norm->data.F32[i]); // Normalisation
     268            psBinaryOp(ro->image, ro->image, "*", psScalarAlloc(normalise, PS_TYPE_F32));
     269            psBinaryOp(ro->variance, ro->variance, "*", psScalarAlloc(PS_SQR(normalise), PS_TYPE_F32));
     270        }
     271    }
     272
     273    if (!pmStackCombine(outRO, stack, maskVal | maskBad, maskSuspect, maskBad, 0, iter, combineRej,
     274                        combineSys, combineDiscard, useVariance, safe, true)) {
    259275        psError(PS_ERR_UNKNOWN, false, "Unable to combine input readouts.");
    260276        psFree(stack);
  • trunk/ppStack/src/ppStackReject.c

    r25466 r26076  
    2323
    2424    int num = options->num;             // Number of inputs
    25     options->rejected = psArrayAlloc(num);
    2625
    2726    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
     
    5352        psThreadJob *job = psThreadJobAlloc("PPSTACK_INSPECT"); // Job to start
    5453        psArrayAdd(job->args, 1, options->inspect);
     54        psArrayAdd(job->args, 1, options->rejected);
    5555        PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
    5656        if (!psThreadJobAddPending(job)) {
     
    9292#endif
    9393
    94         psPixels *reject = pmStackReject(options->inspect->data[i], options->numCols, options->numRows,
    95                                          threshold, poorFrac, stride, options->regions->data[i],
    96                                          options->kernels->data[i]); // Rejected pixels
    97 
    9894#ifdef TESTING
    9995        {
    100             psImage *mask = psPixelsToMask(NULL, reject,
     96            psImage *mask = psPixelsToMask(NULL, options->rejected->data[i],
    10197                                           psRegionSet(0, options->numCols - 1, 0, options->numRows - 1),
    10298                                           0xff); // Mask image
    10399            psString name = NULL;           // Name of image
    104             psStringAppend(&name, "reject_%03d.fits", i);
     100            psStringAppend(&name, "pre_reject_%03d.fits", i);
    105101            pmStackVisualPlotTestImage(mask, name);
    106102            psFits *fits = psFitsOpen(name, "w");
     
    111107        }
    112108#endif
     109
     110        psPixels *reject = pmStackReject(options->inspect->data[i], options->numCols, options->numRows,
     111                                         threshold, poorFrac, stride, options->regions->data[i],
     112                                         options->kernels->data[i]); // Rejected pixels
    113113
    114114        psFree(options->inspect->data[i]);
     
    127127                          "exceeds limit (%.3f)", i, frac, imageRej);
    128128                psFree(reject);
    129                 // reject == NULL means reject image completely
    130129                reject = NULL;
    131130                options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PPSTACK_MASK_BAD;
     
    134133        }
    135134
    136         // Images without a list of rejected pixels (the list may be empty) are rejected completely
    137         options->rejected->data[i] = reject;
     135        if (reject) {
     136            // Add to list of pixels already rejected
     137            reject = psPixelsConcatenate(reject, options->rejected->data[i]);
     138            options->rejected->data[i] = psPixelsDuplicates(options->rejected->data[i], reject);
     139        }
     140
     141#ifdef TESTING
     142        {
     143            psImage *mask = psPixelsToMask(NULL, options->rejected->data[i],
     144                                           psRegionSet(0, options->numCols - 1, 0, options->numRows - 1),
     145                                           0xff); // Mask image
     146            psString name = NULL;           // Name of image
     147            psStringAppend(&name, "reject_%03d.fits", i);
     148            pmStackVisualPlotTestImage(mask, name);
     149            psFits *fits = psFitsOpen(name, "w");
     150            psFree(name);
     151            psFitsWriteImage(fits, NULL, mask, 0, NULL);
     152            psFree(mask);
     153            psFitsClose(fits);
     154        }
     155#endif
    138156
    139157        if (options->stats) {
     
    143161                             "Number of pixels rejected", reject ? reject->n : 0);
    144162        }
     163
     164        psFree(reject);
    145165        psLogMsg("ppStack", PS_LOG_INFO, "Time to perform rejection on image %d: %f sec", i,
    146166                 psTimerClear("PPSTACK_REJECT"));
  • trunk/ppStack/src/ppStackSetup.c

    r26020 r26076  
    7272    }
    7373
    74     options->imageNames = psArrayAlloc(num);
    75     options->maskNames = psArrayAlloc(num);
    76     options->varianceNames = psArrayAlloc(num);
     74    options->convImages = psArrayAlloc(num);
     75    options->convMasks = psArrayAlloc(num);
     76    options->convVariances = psArrayAlloc(num);
    7777    for (int i = 0; i < num; i++) {
    7878        psString imageName = NULL, maskName = NULL, varianceName = NULL; // Names for convolved images
     
    8181        psStringAppend(&varianceName, "%s/%s.%d.%s", tempDir, tempName, i, tempVariance);
    8282        psTrace("ppStack", 5, "Temporary files: %s %s %s\n", imageName, maskName, varianceName);
    83         options->imageNames->data[i] = imageName;
    84         options->maskNames->data[i] = maskName;
    85         options->varianceNames->data[i] = varianceName;
     83        options->convImages->data[i] = imageName;
     84        options->convMasks->data[i] = maskName;
     85        options->convVariances->data[i] = varianceName;
    8686    }
    8787    psFree(outputName);
     88
     89    // Original images
     90    options->origImages = psArrayAlloc(num);
     91    options->origMasks = psArrayAlloc(num);
     92    options->origVariances = psArrayAlloc(num);
     93    pmFPAview *view = pmFPAviewAlloc(0);
     94    for (int i = 0; i < num; i++) {
     95        {
     96            pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.INPUT", i);
     97            options->origImages->data[i] = pmFPAfileName(file, view, config);
     98        }
     99        {
     100            // We want the convolved mask, since that defines the area that has been tested for outliers
     101            options->origMasks->data[i] = psMemIncrRefCounter(options->convMasks->data[i]);
     102        }
     103        {
     104            pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.INPUT.VARIANCE", i);
     105            options->origVariances->data[i] = pmFPAfileName(file, view, config);
     106        }
     107    }
     108    psFree(view);
    88109
    89110    if (!pmConfigMaskSetBits(NULL, NULL, config)) {
  • trunk/ppStack/src/ppStackSources.c

    r25313 r26076  
    188188    }
    189189
     190#if 0
     191    // Position offsets
     192    {
     193        psArray *offsets = pmSourceMatchRelastro(matches, num, tol, iter1, starRej1,
     194                                                  iter2, starRej2, starLimit); // Shifts for each image
     195        if (!offsets) {
     196            psError(PS_ERR_UNKNOWN, false, "Unable to measure offsets");
     197            return false;
     198        }
     199        for (int i = 0; i < num; i++) {
     200            psVector *offset = offsets->data[i];
     201            fprintf(stderr, "Offset %d: %f,%f\n", i, offset->data.F32[0], offset->data.F32[1]);
     202        }
     203        psFree(offsets);
     204    }
     205#endif
     206
    190207#ifdef TESTING
    191208    dumpMatches("source_mags.dat", num, matches, zp, trans);
  • trunk/ppStack/src/ppStackThread.c

    r24796 r26076  
    5656}
    5757
    58 ppStackThreadData *ppStackThreadDataSetup(const ppStackOptions *options, const pmConfig *config)
     58ppStackThreadData *ppStackThreadDataSetup(const ppStackOptions *options, const pmConfig *config, bool conv)
    5959{
    6060    psAssert(options, "Require options");
     
    6262
    6363    const psArray *cells = options->cells; // Array of input cells
    64     const psArray *imageNames = options->imageNames; // Names of images to read
    65     const psArray *maskNames = options->maskNames; // Names of masks to read
    66     const psArray *varianceNames = options->varianceNames; // Names of variance maps to read
    67     const psArray *covariances = options->covariances; // Covariance matrices (already read)
     64    const psArray *imageNames = conv ? options->convImages : options->origImages; // Names of images to read
     65    const psArray *maskNames = conv ? options->convMasks : options->origMasks; // Names of masks to read
     66    const psArray *varianceNames = conv ? options->convVariances : options->origVariances; // Variance names
     67    const psArray *covariances = conv ? options->convCovars : options->origCovars; // Covariance matrices
    6868
    6969    PS_ASSERT_ARRAY_NON_NULL(cells, NULL);
    70     PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, imageNames, NULL);
    71     PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, maskNames, NULL);
    72     PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, varianceNames, NULL);
    73     PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, covariances, NULL);
     70    if (imageNames) {
     71        PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, imageNames, NULL);
     72    }
     73    if (maskNames) {
     74        PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, maskNames, NULL);
     75    }
     76    if (varianceNames) {
     77        PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, varianceNames, NULL);
     78    }
     79    if (covariances) {
     80        PS_ASSERT_ARRAYS_SIZE_EQUAL(cells, covariances, NULL);
     81    }
    7482
    7583    ppStackThreadData *stack = psAlloc(sizeof(ppStackThreadData)); // Thread data, to return
     
    8795            continue;
    8896        }
    89         // Resolved names
    90         psString imageResolved = pmConfigConvertFilename(imageNames->data[i], config, false, false);
    91         psString maskResolved = pmConfigConvertFilename(maskNames->data[i], config, false, false);
    92         psString varianceResolved = pmConfigConvertFilename(varianceNames->data[i], config, false, false);
    93         stack->imageFits->data[i] = psFitsOpen(imageResolved, "r");
    94         stack->maskFits->data[i] = psFitsOpen(maskResolved, "r");
    95         stack->varianceFits->data[i] = psFitsOpen(varianceResolved, "r");
    96         psFree(imageResolved);
    97         psFree(maskResolved);
    98         psFree(varianceResolved);
    99         if (!stack->imageFits->data[i] || !stack->maskFits->data[i] || !stack->varianceFits->data[i]) {
    100             psError(PS_ERR_UNKNOWN, false, "Unable to open convolved files %s, %s, %s",
    101                     (char*)imageNames->data[i], (char*)maskNames->data[i], (char*)varianceNames->data[i]);
    102             return NULL;
    103         }
     97
     98// Open an image
     99#define IMAGE_OPEN(NAMES, FITS, INDEX)          \
     100        if (NAMES) { \
     101            psString resolved = pmConfigConvertFilename((NAMES)->data[INDEX], config, false, false); \
     102            (FITS)->data[INDEX] = psFitsOpen(resolved, "r");                            \
     103            if (!(FITS)->data[INDEX]) { \
     104                psError(PS_ERR_IO, false, "Unable to open file %s", (char*)(NAMES)->data[INDEX]); \
     105                psFree(resolved); \
     106                return NULL; \
     107            } \
     108            psFree(resolved); \
     109        }
     110
     111        IMAGE_OPEN(imageNames, stack->imageFits, i);
     112        IMAGE_OPEN(maskNames, stack->maskFits, i);
     113        IMAGE_OPEN(varianceNames, stack->varianceFits, i);
    104114    }
    105115
     
    116126            }
    117127            pmReadout *ro = pmReadoutAlloc(cell); // Readout for thread
    118             ro->covariance = psMemIncrRefCounter(covariances->data[j]);
     128            if (covariances) {
     129                ro->covariance = psMemIncrRefCounter(covariances->data[j]);
     130            }
    119131            readouts->data[j] = ro;
    120132        }
     
    186198                psFits *varianceFits = stack->varianceFits->data[i]; // FITS file for variance
    187199
    188 
    189                 int zMax = 0;
     200                int zMax = 0;
    190201                bool keepReading = false;
    191                 if (pmReadoutMore(ro, imageFits, 0, &zMax, rows, config)) {
     202
     203                if (imageFits && pmReadoutMore(ro, imageFits, 0, &zMax, rows, config)) {
    192204                    keepReading = true;
    193205                    if (!pmReadoutReadChunk(ro, imageFits, 0, NULL, rows, overlap, config)) {
     
    199211                }
    200212
    201                 if (pmReadoutMoreMask(ro, maskFits, 0, &zMax, rows, config)) {
     213                if (maskFits && pmReadoutMoreMask(ro, maskFits, 0, &zMax, rows, config)) {
    202214                    keepReading = true;
    203215                    if (!pmReadoutReadChunkMask(ro, maskFits, 0, NULL, rows, overlap, config)) {
     
    209221                }
    210222
    211                 if (pmReadoutMoreVariance(ro, varianceFits, 0, &zMax, rows, config)) {
     223                if (varianceFits && pmReadoutMoreVariance(ro, varianceFits, 0, &zMax, rows, config)) {
    212224                    keepReading = true;
    213225                    if (!pmReadoutReadChunkVariance(ro, varianceFits, 0, NULL, rows, overlap, config)) {
     
    263275
    264276    {
    265         psThreadTask *task = psThreadTaskAlloc("PPSTACK_INSPECT", 2);
     277        psThreadTask *task = psThreadTaskAlloc("PPSTACK_INSPECT", 3);
    266278        task->function = &ppStackInspect;
    267279        psThreadTaskAdd(task);
     
    270282
    271283    {
    272         psThreadTask *task = psThreadTaskAlloc("PPSTACK_FINAL_COMBINE", 3);
     284        psThreadTask *task = psThreadTaskAlloc("PPSTACK_FINAL_COMBINE", 6);
    273285        task->function = &ppStackReadoutFinalThread;
    274286        psThreadTaskAdd(task);
  • trunk/ppStack/src/ppStackThread.h

    r23341 r26076  
    3535ppStackThreadData *ppStackThreadDataSetup(
    3636    const ppStackOptions *options,      // Options
    37     const pmConfig *config              // Configuration
     37    const pmConfig *config,             // Configuration
     38    bool conv                           // Use convolved products?
    3839    );
    3940
  • trunk/ppSub/src/ppSubArguments.c

    r24833 r26076  
    8686    psMetadataAddS32(arguments, PS_LIST_TAIL, "-convolve", 0, "Image to convolve [1 or 2]", 0);
    8787    psMetadataAddBool(arguments, PS_LIST_TAIL, "-photometry", 0, "Perform photometry?", NULL);
     88    psMetadataAddF32(arguments, PS_LIST_TAIL, "-zp", 0, "Zero point for photometry", NAN);
    8889    psMetadataAddBool(arguments, PS_LIST_TAIL, "-inverse", 0, "Generate inverse subtractions?", NULL);
    8990    psMetadataAddBool(arguments, PS_LIST_TAIL, "-visual", 0, "Show diagnostic plots", NULL);
  • trunk/psModules/src/camera

  • trunk/psModules/src/camera/pmFPAMaskWeight.c

    r25379 r26076  
    444444    }
    445445
    446     return true;
     446    return psMetadataAddF32(readout->analysis, PS_LIST_TAIL, PM_READOUT_ANALYSIS_RENORM, 0,
     447                            "Renormalisation of variance", PS_SQR(correction));
    447448}
    448449
  • trunk/psModules/src/camera/pmFPAMaskWeight.h

    r25372 r26076  
    1515/// @addtogroup Camera Camera Layout
    1616/// @{
     17
     18#define PM_READOUT_ANALYSIS_RENORM "READOUT.RENORM" // Name on analysis metadata for renormalisation
    1719
    1820/// Set a temporary readout mask using CELL.SATURATION and CELL.BAD
  • trunk/psModules/src/camera/pmReadoutFake.c

    • Property svn:mergeinfo changed (with no actual effect on merging)
  • trunk/psModules/src/imcombine/pmStack.c

    r25380 r26076  
    3333#define NUM_DIRECT_STDEV 5              // For less than this number of values, measure stdev directly
    3434
     35
    3536//#define TESTING                         // Enable test output
    36 //#define TEST_X 1085                     // x coordinate to examine
    37 //#define TEST_Y 3371                     // y coordinate to examine
     37//#define TEST_X 3122-1                     // x coordinate to examine
     38//#define TEST_Y 1028-1                     // y coordinate to examine
    3839
    3940
     
    4243typedef struct {
    4344    psVector *pixels;                   // Pixel values
    44     psVector *masks;                    // Pixel masks
    4545    psVector *variances;                // Pixel variances
    4646    psVector *weights;                  // Pixel weightings
    4747    psVector *sources;                  // Pixel sources (which image did they come from?)
    4848    psVector *limits;                   // Rejection limits
     49    psVector *suspects;                 // Pixel is suspect?
    4950    psVector *sort;                     // Buffer for sorting (to get a robust estimator of the standard dev)
    5051} combineBuffer;
     
    5354{
    5455    psFree(buffer->pixels);
    55     psFree(buffer->masks);
    5656    psFree(buffer->variances);
    5757    psFree(buffer->weights);
    5858    psFree(buffer->sources);
    5959    psFree(buffer->limits);
     60    psFree(buffer->suspects);
    6061    psFree(buffer->sort);
    6162    return;
     
    6970
    7071    buffer->pixels = psVectorAlloc(numImages, PS_TYPE_F32);
    71     buffer->masks = psVectorAlloc(numImages, PS_TYPE_VECTOR_MASK);
    7272    buffer->variances = psVectorAlloc(numImages, PS_TYPE_F32);
    7373    buffer->weights = psVectorAlloc(numImages, PS_TYPE_F32);
    7474    buffer->sources = psVectorAlloc(numImages, PS_TYPE_U16);
    7575    buffer->limits = psVectorAlloc(numImages, PS_TYPE_F32);
     76    buffer->suspects = psVectorAlloc(numImages, PS_TYPE_U8);
    7677    buffer->sort = psVectorAlloc(numImages, PS_TYPE_F32);
    7778    return buffer;
     
    143144                                   float *stdev, // Standard deviation value, to return
    144145                                   const psVector *values, // Values to combine
    145                                    const psVector *masks, // Mask to apply
    146146                                   psVector *sortBuffer // Buffer for sorting
    147147                                   )
    148148{
    149149    assert(values);
    150     assert(!masks || values->n == masks->n);
    151150    assert(values->type.type == PS_TYPE_F32);
    152     assert(!masks || masks->type.type == PS_TYPE_VECTOR_MASK);
    153151    assert(sortBuffer && sortBuffer->nalloc >= values->n && sortBuffer->type.type == PS_TYPE_F32);
    154152
    155     // Need to filter out clipped values
    156     int num = 0;            // Number of valid values
    157     for (int i = 0; i < values->n; i++) {
    158         if (!masks || !masks->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    159             sortBuffer->data.F32[num++] = values->data.F32[i];
    160         }
    161     }
    162     sortBuffer->n = num;
    163     if (!psVectorSortInPlace(sortBuffer)) {
     153    int num = values->n;                // Number of values
     154    sortBuffer = psVectorSortIndex(sortBuffer, values);
     155    if (!sortBuffer) {
     156        *median = NAN;
     157        *stdev = NAN;
    164158        return false;
    165159    }
     
    167161    if (num == 3) {
    168162        // Attempt to measure standard deviation with only three values (and one of those possibly corrupted)
    169         *median = sortBuffer->data.F32[1];
     163        *median = values->data.F32[sortBuffer->data.S32[1]];
    170164        if (stdev) {
    171             float diff1 = sortBuffer->data.F32[0] - *median;
    172             float diff2 = sortBuffer->data.F32[2] - *median;
     165            float diff1 = values->data.F32[sortBuffer->data.S32[0]] - *median;
     166            float diff2 = values->data.F32[sortBuffer->data.S32[2]] - *median;
    173167            // This factor of sqrt(2) might not be exact, but it's about right
    174168            *stdev = M_SQRT2 * PS_MIN(fabsf(diff1), fabsf(diff2));
    175169        }
    176170    } else {
    177         *median = num % 2 ? sortBuffer->data.F32[num / 2] :
    178             (sortBuffer->data.F32[num / 2 - 1] + sortBuffer->data.F32[num / 2]) / 2.0;
     171        *median = num % 2 ? values->data.F32[sortBuffer->data.S32[num / 2]] :
     172            (values->data.F32[sortBuffer->data.S32[num / 2 - 1]] +
     173             values->data.F32[sortBuffer->data.S32[num / 2]]) / 2.0;
    179174        if (stdev) {
    180175            if (num <= NUM_DIRECT_STDEV) {
     
    182177                double sum = 0.0;
    183178                for (int i = 0; i < num; i++) {
    184                     sum += PS_SQR(sortBuffer->data.F32[i] - *median);
     179                    sum += PS_SQR(values->data.F32[sortBuffer->data.S32[i]] - *median);
    185180                }
    186181                *stdev = sqrt(sum / (double)(num - 1));
    187182            } else {
    188183                // Standard deviation from the interquartile range
    189                 *stdev = 0.74 * (sortBuffer->data.F32[(int)(0.75 * num)] -
    190                                  sortBuffer->data.F32[(int)(0.25 * num)]);
     184                *stdev = 0.74 * (values->data.F32[sortBuffer->data.S32[(int)(0.75 * num)]] -
     185                                 values->data.F32[sortBuffer->data.S32[(int)(0.25 * num)]]);
    191186            }
    192187        }
     
    195190    return true;
    196191}
    197 
    198 #if 0
    199 // Return the weighted median for the pixels
    200 // This does not appear to produce as clean images as the weighted Olympic mean
    201 static float combinationWeightedMedian(const psVector *values, // Values to combine
    202                                        const psVector *weights, // Weights to combine
    203                                        const psVector *masks, // Mask to apply
    204                                        psVector *sortBuffer // Buffer for sorting
    205                                        )
    206 {
    207     double sumWeight = 0.0;             // Sum of weights
    208     for (int j = 0; j < values->n; j++) {
    209         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
    210             continue;
    211         }
    212         sumWeight += weights->data.F32[j];
    213     }
    214 
    215     sortBuffer = psVectorSortIndex(sortBuffer, values);
    216     double target = sumWeight / 2.0;    // Target weight
    217 
    218     int dominant = -1;                  // Index of dominant value, if any
    219     double cumulativeWeight = 0.0;      // Sum of weights
    220     for (int j = 0; j < values->n; j++) {
    221         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
    222             continue;
    223         }
    224         int index = sortBuffer->data.S32[j];  // Index of value of interest
    225         float weight = weights->data.F32[index]; // Weight for value of interest
    226         if (weight >= target) {
    227             // Get the weighted median of the rest
    228             dominant = index;
    229             sumWeight -= weight;
    230             target = sumWeight / 2.0;
    231             continue;
    232         }
    233         cumulativeWeight += weight;
    234         if (cumulativeWeight >= target) {
    235             float median = values->data.F32[index]; // Weighted median median
    236             if (dominant != -1) {
    237                 // In the case that a single value contains a disproportionate weight compared to the rest,
    238                 // we use a weighted mean between that dominant value and the weighted median of the rest.
    239                 return (values->data.F32[dominant] * weights->data.F32[dominant] + median * sumWeight) /
    240                     (weights->data.F32[dominant] + sumWeight);
    241             }
    242             return median;
    243         }
    244     }
    245 
    246     return NAN;
    247 }
    248 #endif
    249192
    250193// Return the weighted Olympic mean for the pixels
    251194static float combinationWeightedOlympic(const psVector *values, // Values to combine
    252195                                        const psVector *weights, // Weights to combine
    253                                         const psVector *masks, // Mask to apply
    254196                                        float frac, // Fraction to discard
    255197                                        psVector *sortBuffer // Buffer for sorting
    256198                                        )
    257199{
    258     int numGood = 0;                    // Number of good values
    259     for (int i = 0; i < values->n; i++) {
    260         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    261             continue;
    262         }
    263         numGood++;
    264     }
     200    int numGood = values->n;            // Number of good values
    265201
    266202    int numBad = frac * numGood + 0.5;  // Number of bad values
     
    272208    for (int i = 0, j = 0; i < values->n; i++) {
    273209        int index = sortBuffer->data.S32[i]; // Index of interest
    274         if (masks->data.PS_TYPE_VECTOR_MASK_DATA[index]) {
    275             continue;
    276         }
    277210        j++;
    278211        if (j > high) {
     
    290223
    291224// Mark a pixel for inspection
    292 static inline void combineInspect(const psArray *inputs, // Stack data
    293                                   int x, int y, // Pixel
    294                                   int source // Source image index
    295                                   )
    296 {
     225// Value in pixel doesn't seem to agree with the stack, so need to look closer
     226static inline void combineMarkInspect(const psArray *inputs, // Stack data
     227                                      int x, int y, // Pixel
     228                                      int source // Source image index
     229                                      )
     230{
     231#ifdef TESTING
     232    if (x == TEST_X && y == TEST_Y) {
     233        fprintf(stderr, "Marking image %d for inspection\n", source);
     234    }
     235#endif
    297236    pmStackData *data = inputs->data[source]; // Stack data of interest
    298237    if (!data) {
     
    304243}
    305244
    306 // Given a stack of images, combine with optional rejection.
    307 // Pixels in the stack that are rejected are marked for subsequent inspection
    308 static void combinePixels(psImage *image, // Combined image, for output
    309                           psImage *mask, // Combined mask, for output
    310                           psImage *variance, // Combined variance map, for output
    311                           const psArray *inputs, // Stack data
    312                           const psVector *weights, // Global (single value) weights for data, or NULL
    313                           const psVector *addVariance, // Additional variance for data
    314                           const psVector *reject, // Indices of pixels to reject, or NULL
    315                           int x, int y, // Coordinates of interest; frame of output image
    316                           psImageMaskType maskVal, // Value to mask
    317                           psImageMaskType bad, // Value to give bad pixels
    318                           int numIter, // Number of rejection iterations
    319                           float rej, // Number of standard deviations at which to reject
    320                           float sys,    // Relative systematic error
    321                           float discard,// Fraction of values to discard (Olympic weighted mean)
    322                           bool useVariance, // Use variance for rejection when combining?
    323                           bool safe,    // Combine safely?
    324                           bool rejectInspect, // Reject values marked for inspection from combination?
    325                           combineBuffer *buffer // Buffer for combination; to avoid multiple allocations
    326                          )
     245// Mark a pixel for rejection
     246// Cannot possibly inspect this pixel and confirm that it's good.
     247// e.g., Only a single input
     248static inline void combineMarkReject(const psArray *inputs, // Stack data
     249                                     int x, int y, // Pixel
     250                                     int source // Source image index
     251                                     )
     252{
     253#ifdef TESTING
     254    if (x == TEST_X && y == TEST_Y) {
     255        fprintf(stderr, "Marking pixel image %d for rejection\n", source);
     256    }
     257#endif
     258    pmStackData *data = inputs->data[source]; // Stack data of interest
     259    if (!data) {
     260        psWarning("Can't find input data for source %d", source);
     261        return;
     262    }
     263    data->reject = psPixelsAdd(data->reject, data->reject->nalloc, x, y);
     264    return;
     265}
     266
     267
     268// Extract vectors for simple combination/rejection operations
     269static void combineExtract(int *num,                        // Number of good pixels
     270                           bool *suspect,                   // Any suspect pixels?
     271                           combineBuffer *buffer, // Buffer with vectors
     272                           psImage *image, // Combined image, for output
     273                           psImage *mask, // Combined mask, for output
     274                           psImage *variance, // Combined variance map, for output
     275                           const psArray *inputs, // Stack data
     276                           const psVector *weights, // Global (single value) weights for data, or NULL
     277                           const psVector *addVariance, // Additional variance for data
     278                           const psVector *reject, // Indices of pixels to reject, or NULL
     279                           int x, int y, // Coordinates of interest; frame of output image
     280                           psImageMaskType maskVal, // Value to mask
     281                           psImageMaskType maskSuspect // Value to suspect
     282                           )
    327283{
    328284    // Rudimentary error checking
     285    assert(buffer);
    329286    assert(image);
    330287    assert(mask);
    331288    assert(inputs);
    332     assert(numIter >= 0);
    333     assert(buffer);
    334     assert(addVariance);
    335     assert((useVariance && variance) || !useVariance);
    336289
    337290    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
    338     psVector *pixelMasks = buffer->masks; // Masks for the pixel of interest
    339291    psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest
    340292    psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest
    341293    psVector *pixelSources = buffer->sources; // Sources for the pixel of interest
    342294    psVector *pixelLimits = buffer->limits; // Limits for the pixel of interest
    343     psVector *sort = buffer->sort;      // Sort buffer
     295    psVector *pixelSuspects = buffer->suspects; // Is the pixel suspect?
     296
     297    if (suspect) {
     298        *suspect = false;
     299    }
    344300
    345301    // Extract the pixel and mask data
    346     int num = 0;                        // Number of good images
     302    int numGood = 0;                    // Number of good pixels
    347303    for (int i = 0, j = 0; i < inputs->n; i++) {
    348304        // Check if this pixel has been rejected.  Assumes that the rejection pixel list is sorted --- it
     
    364320        }
    365321
     322        pixelSuspects->data.U8[numGood] = mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & maskSuspect ?
     323            true : false;
     324
    366325        psImage *image = data->readout->image; // Image of interest
    367326        psImage *variance = data->readout->variance; // Variance map of interest
    368         pixelData->data.F32[num] = image->data.F32[yIn][xIn];
     327        pixelData->data.F32[numGood] = image->data.F32[yIn][xIn];
    369328        if (variance) {
    370             pixelVariances->data.F32[num] = variance->data.F32[yIn][xIn] * addVariance->data.F32[i];
    371         }
    372         pixelWeights->data.F32[num] = data->weight;
    373         pixelSources->data.U16[num] = i;
    374         num++;
    375     }
    376     pixelData->n = num;
     329            pixelVariances->data.F32[numGood] = variance->data.F32[yIn][xIn] * addVariance->data.F32[i];
     330        }
     331        pixelWeights->data.F32[numGood] = data->weight;
     332        pixelSources->data.U16[numGood] = i;
     333        numGood++;
     334    }
     335    pixelData->n = numGood;
    377336    if (variance) {
    378         pixelVariances->n = num;
    379     }
    380     pixelWeights->n = num;
    381     pixelSources->n = num;
    382     pixelLimits->n = num;
     337        pixelVariances->n = numGood;
     338    }
     339    pixelWeights->n = numGood;
     340    pixelSources->n = numGood;
     341    pixelLimits->n = numGood;
     342    pixelSuspects->n = numGood;
     343    *num = numGood;
    383344
    384345#ifdef TESTING
    385346    if (x == TEST_X && y == TEST_Y) {
    386         for (int i = 0; i < num; i++) {
    387             fprintf(stderr, "Input %d (%" PRIu16 "): %f %f %f\n",
     347        for (int i = 0; i < numGood; i++) {
     348            fprintf(stderr, "Input %d (%" PRIu16 "): %f %f (%f) %f %d\n",
    388349                    i, pixelSources->data.U16[i], pixelData->data.F32[i], pixelVariances->data.F32[i],
    389                     pixelWeights->data.F32[i]);
    390         }
    391     }
    392 #endif
    393 
    394     // The sensible thing to do varies according to how many good pixels there are.
     350                    addVariance->data.F32[i], pixelWeights->data.F32[i], pixelSuspects->data.U8[i]);
     351        }
     352    }
     353#endif
     354
     355    return;
     356}
     357
     358
     359// Combine pixels
     360static void combinePixels(psImage *image, // Combined image, for output
     361                          psImage *mask, // Combined mask, for output
     362                          psImage *variance, // Combined variance map, for output
     363                          int num,      // Number of good pixels
     364                          combineBuffer *buffer, // Buffer with vectors
     365                          int x, int y, // Coordinates of interest; frame of output image
     366                          psImageMaskType bad, // Value for bad pixels
     367                          bool safe             // Safe combination?
     368                          )
     369{
     370    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
     371    psVector *pixelVariances = variance ? buffer->variances : NULL; // Variances for the pixel of interest
     372    psVector *pixelWeights = buffer->weights; // Image weights for the pixel of interest
     373
    395374    // Default option is that the pixel is bad
    396375    float imageValue = NAN, varianceValue = NAN; // Value for combined image and variance map
    397376    psImageMaskType maskValue = bad;    // Value for combined mask
    398377    switch (num) {
    399       case 0:
    400         // Nothing to combine: it's bad
    401 #ifdef TESTING
    402     if (x == TEST_X && y == TEST_Y) {
    403         fprintf(stderr, "No inputs to combine, pixel is bad.\n");
    404     }
    405 #endif
    406         break;
     378      case 0: {
     379          // Nothing to combine: it's bad
     380#ifdef TESTING
     381          if (x == TEST_X && y == TEST_Y) {
     382              fprintf(stderr, "No inputs to combine, pixel is bad.\n");
     383          }
     384#endif
     385          break;
     386      }
    407387      case 1: {
    408388          // Accept the single pixel unless we have to be safe
     
    427407      }
    428408      case 2: {
    429           // Accept the mean of the pixels only if we're going to reject based on the variance, or we're not
    430           // playing safe
    431           if (useVariance || !safe) {
     409          // Automatically accept the mean of the pixels only if we're not playing safe
     410          if (!safe) {
    432411              float mean, var;   // Mean and variance from combination
    433412              if (combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
     
    437416#ifdef TESTING
    438417                  if (x == TEST_X && y == TEST_Y) {
    439                       fprintf(stderr, "Two inputs to combine using variance/unsafe --> %f %f\n",
    440                               mean, var);
     418                      fprintf(stderr, "Two inputs to combine using unsafe --> %f %f\n", mean, var);
    441419                  }
    442420#endif
    443421              }
    444422          }
    445           if (useVariance && numIter > 0) {
    446               // Use variance to check that the two are consistent
    447               float diff = 0.5 * (pixelData->data.F32[0] - pixelData->data.F32[1]); // Mean flux difference
    448               float var1 = pixelVariances->data.F32[0]; // Variance of first
    449               float var2 = pixelVariances->data.F32[1]; // Variance of second
    450               // Systematic error contributes to the rejection level
    451               var1 += PS_SQR(sys * pixelData->data.F32[0]);
    452               var2 += PS_SQR(sys * pixelData->data.F32[1]);
    453 
    454               float sigma2 = var1 + var2; // Combined variance
    455               if (PS_SQR(diff) > PS_SQR(rej) * sigma2) {
    456                   // Not consistent: mark both for inspection
    457                   if (rejectInspect) {
    458                       imageValue = NAN;
    459                       varianceValue = NAN;
    460                       maskValue = bad;
    461                   } else {
    462                       combineInspect(inputs, x, y, pixelSources->data.U16[0]);
    463                       combineInspect(inputs, x, y, pixelSources->data.U16[1]);
    464                   }
    465 #ifdef TESTING
    466                   if (x == TEST_X && y == TEST_Y) {
    467                       fprintf(stderr, "Both pixels marked for inspection (%f > %f x %f\n)",
    468                               diff, rej, sqrtf(sigma2));
    469                   }
    470 #endif
     423#ifdef TESTING
     424          else {
     425              if (x == TEST_X && y == TEST_Y) {
     426                  fprintf(stderr, "Two inputs to combine, safety on, pixel is bad\n");
    471427              }
    472428          }
     429#endif
    473430          break;
    474431      }
    475432      default: {
    476           // Record the value derived with no clipping, because pixels rejected using the harsh clipping
    477           // applied in the first pass might later be accepted.
     433          // Can combine without too much worrying
    478434          float mean, var;           // Mean and variance of the combination
    479435          if (!combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
     
    488444          }
    489445#endif
    490 
    491           // Prepare for clipping iteration
    492           if (numIter > 0) {
    493               pixelMasks->n = num;
    494               psVectorInit(pixelMasks, 0);
    495               if (useVariance) {
    496                   // Convert to rejection limits --- saves doing it later.
    497                   // Using squared rejection limit because it's cheaper than sqrts
    498                   float rej2 = PS_SQR(rej); // Rejection level squared
    499                   double sumWeights = 0.0;
    500                   for (int i = 0; i < num; i++) {
    501                       sumWeights += pixelWeights->data.F32[i];
    502                   }
    503                   for (int i = 0; i < num; i++) {
    504                       // Systematic error contributes to the rejection level
    505                       float sysVar = PS_SQR(sys * pixelData->data.F32[i]);
    506 #ifdef TESTING
    507                       // Correct variance for comparison against weighted mean including itself
    508                       float compare = 1.0 - pixelWeights->data.F32[i] / sumWeights;
    509                       if (x == TEST_X && y == TEST_Y) {
    510                           fprintf(stderr, "Variance %d (%d): %f %f %f\n", i, pixelSources->data.U16[i],
    511                                   pixelVariances->data.F32[i], sysVar, compare);
    512                       }
    513 #endif
    514 
    515                       pixelLimits->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar);
    516                   }
    517               }
    518           }
    519 
    520           // The clipping that follows is solely to identify suspect pixels.
    521           // These suspect pixels will be inspected in more detail by other functions.
    522           int numClipped = INT_MAX;     // Number of pixels clipped per iteration
    523           int totalClipped = 0;         // Total number of pixels clipped
    524           for (int i = 0; i < numIter && numClipped > 0 && num - totalClipped > 2; i++) {
    525               numClipped = 0;
    526               float median = NAN;       // Middle of distribution
    527               float limit = NAN;        // Rejection limit
    528               if (!useVariance) {
    529                   float stdev;  // Median and stdev of the combination, for rejection
    530                   if (!combinationMedianStdev(&median, useVariance ? NULL : &stdev,
    531                                               pixelData, pixelMasks, sort)) {
    532                       psWarning("Bad median/stdev at %d,%d", x, y);
    533                       break;
    534                   }
    535                   limit = rej * stdev;
    536 #ifdef TESTING
    537                   if (x == TEST_X && y == TEST_Y) {
    538                       fprintf(stderr, "Rejecting without variance; rejection limit: %f\n", limit);
    539                   }
    540 #endif
    541               } else {
    542 #ifdef TESTING
    543                   if (x == TEST_X && y == TEST_Y) {
    544                       fprintf(stderr, "Rejecting with variance...\n");
    545                   }
    546 #endif
    547                   median = combinationWeightedOlympic(pixelData, pixelWeights, pixelMasks, discard, sort);
    548               }
    549 
    550 #ifdef TESTING
    551               if (x == TEST_X && y == TEST_Y) {
    552                   fprintf(stderr, "Median: %f\n", median);
    553               }
    554 #endif
    555 
    556 
    557 // Mask a pixel for inspection
    558 #define MASK_PIXEL_FOR_INSPECTION() \
    559     pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[j] = 0xff; \
    560     if (!rejectInspect) { \
    561         combineInspect(inputs, x, y, pixelSources->data.U16[j]); \
    562     } \
    563     numClipped++; \
    564     totalClipped++;
    565 
    566               for (int j = 0; j < num; j++) {
    567                   if (pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
    568                       continue;
    569                   }
    570                   float diff = pixelData->data.F32[j] - median; // Difference from expected
    571                   if (useVariance) {
    572                       // Comparing squares --- cheaper than lots of sqrts
    573                       // pixelVariances includes the rejection limit, from above
    574                       if (PS_SQR(diff) > pixelLimits->data.F32[j]) {
    575                           MASK_PIXEL_FOR_INSPECTION();
    576 #ifdef TESTING
    577                           if (x == TEST_X && y == TEST_Y) {
    578                               fprintf(stderr, "Rejecting input %d based on variance: %f > %f\n",
    579                                       j, diff, sqrtf(pixelLimits->data.F32[j]));
    580                           }
    581 #endif
    582                       }
    583                   } else if (fabsf(diff) > limit) {
    584                       MASK_PIXEL_FOR_INSPECTION();
    585 #ifdef TESTING
    586                       if (x == TEST_X && y == TEST_Y) {
    587                           fprintf(stderr, "Rejecting input %d based on distribution: %f > %f\n",
    588                                   j, diff, limit);
    589                       }
    590 #endif
    591                   }
    592               }
    593           }
    594 
    595           if (rejectInspect && totalClipped > 0) {
    596               // Get rid of the masked values
    597               // The alternative to this is to make combinationMeanVariance() accept a mask
    598               int good = 0;            // Index of good value
    599               for (int i = 0; i < num; i++) {
    600                   if (pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    601                       continue;
    602                   }
    603                   if (i != good) {
    604                       pixelData->data.F32[good] = pixelData->data.F32[i];
    605                       pixelVariances->data.F32[good] = pixelVariances->data.F32[i];
    606                       pixelWeights->data.F32[good] = pixelWeights->data.F32[i];
    607                       pixelData->data.F32[good] = pixelData->data.F32[i];
    608                   }
    609                   good++;
    610               }
    611               pixelData->n = good;
    612               pixelVariances->n = good;
    613               pixelWeights->n = good;
    614               if (combinationMeanVariance(&mean, &var, pixelData, pixelVariances, pixelWeights)) {
    615                   imageValue = mean;
    616                   varianceValue = var;
    617                   maskValue = 0;
    618               } else {
    619                   imageValue = NAN;
    620                   varianceValue = NAN;
    621                   maskValue = bad;
    622               }
    623           }
    624 
    625446          break;
    626447      }
     
    633454    }
    634455
     456#ifdef TESTING
     457    mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = num;
     458#endif
     459
     460
    635461    return;
     462}
     463
     464
     465// Test pixels to be combined
     466// Returns false to repeat without suspect pixels
     467static bool combineTest(int num,      // Number of good pixels
     468                        bool suspect, // Does the stack contain suspect pixels?
     469                        psArray *inputs,       // Original inputs (for flagging)
     470                        combineBuffer *buffer, // Buffer with vectors
     471                        int x, int y, // Coordinates of interest; frame of output image
     472                        float iter, // Number of rejection iterations per input
     473                        float rej, // Number of standard deviations at which to reject
     474                        float sys,    // Relative systematic error
     475                        float olympic,// Fraction of values to discard (Olympic weighted mean)
     476                        bool useVariance, // Use variance for rejection when combining?
     477                        bool safe    // Combine safely?
     478                        )
     479{
     480    if (iter <= 0) {
     481        return true;
     482    }
     483
     484    int numIter = PS_MAX(iter * num, 1); // Number of iterations
     485
     486#ifdef TESTING
     487    if (x == TEST_X && y == TEST_Y) {
     488        fprintf(stderr, "Testing pixel %d,%d: %d %f %f %f %d %d\n",
     489                x, y, numIter, rej, sys, olympic, useVariance, safe);
     490    }
     491#endif
     492
     493    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
     494    psVector *pixelWeights = buffer->weights; // Is the pixel suspect?
     495    psVector *pixelVariances = buffer->variances; // Variances for the pixel of interest
     496    psVector *pixelSources = buffer->sources; // Sources for the pixel of interest
     497    psVector *pixelSuspects = buffer->suspects; // Is the pixel suspect?
     498    psVector *pixelLimits = buffer->limits; // Is the pixel suspect?
     499
     500    // Set up rejection limits
     501    float rej2 = PS_SQR(rej); // Rejection level squared
     502    if (num > 2 && useVariance) {
     503        // Convert rejection limits --- saves doing it later multiple times
     504        // Using squared rejection limit because it's cheaper than sqrts
     505        double sumWeights = 0.0;
     506        for (int i = 0; i < num; i++) {
     507            sumWeights += pixelWeights->data.F32[i];
     508        }
     509        for (int i = 0; i < num; i++) {
     510            // Systematic error contributes to the rejection level
     511            float sysVar = PS_SQR(sys * pixelData->data.F32[i]);
     512#ifdef TESTING
     513            // Correct variance for comparison against weighted mean including itself
     514            float compare = 1.0 - pixelWeights->data.F32[i] / sumWeights;
     515            if (x == TEST_X && y == TEST_Y) {
     516                fprintf(stderr, "Variance %d (%d): %f %f %f\n", i, pixelSources->data.U16[i],
     517                        pixelVariances->data.F32[i], sysVar, compare);
     518            }
     519#endif
     520            pixelLimits->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar);
     521        }
     522    }
     523
     524    int maskIndex = 0;                  // Index of pixel to mask
     525    int totalClipped = 0;               // Total number of pixels clipped
     526    for (int i = 0; i < numIter && maskIndex >= 0; i++) {
     527        maskIndex = -1;                 // Nothing to reject
     528
     529        switch (num) {
     530          case 0:
     531            break;
     532          case 1:
     533            if (i == 0 && safe) {
     534                combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     535            }
     536            break;
     537          case 2: {
     538              if (useVariance) {
     539                  // Use variance to check that the two are consistent
     540                  float diff = 0.5 * (pixelData->data.F32[0] - pixelData->data.F32[1]); // Mean flux difference
     541                  float var1 = pixelVariances->data.F32[0]; // Variance of first
     542                  float var2 = pixelVariances->data.F32[1]; // Variance of second
     543                  // Systematic error contributes to the rejection level
     544                  var1 += PS_SQR(sys * pixelData->data.F32[0]);
     545                  var2 += PS_SQR(sys * pixelData->data.F32[1]);
     546
     547                  float sigma2 = var1 + var2; // Combined variance
     548                  if (PS_SQR(diff) > rej2 * sigma2) {
     549                      // Not consistent: don't believe either!
     550                      if (i == 0 && suspect) {
     551                          combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     552                          combineMarkReject(inputs, x, y, pixelSources->data.U16[1]);
     553                      } else {
     554                          combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     555                          combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     556                      }
     557#ifdef TESTING
     558                      if (x == TEST_X && y == TEST_Y) {
     559                          fprintf(stderr, "Flagged both pixels (%f > %f x %f\n)",
     560                                  diff, rej, sqrtf(sigma2));
     561                      }
     562#endif
     563                  }
     564              } else if (i == 0 && safe) {
     565                  // Can't test them, and we want to be safe, so reject
     566                  combineMarkReject(inputs, x, y, pixelSources->data.U16[0]);
     567                  combineMarkReject(inputs, x, y, pixelSources->data.U16[1]);
     568              }
     569              break;
     570          }
     571#if 0
     572          case 3: {
     573              // Want to be a bit careful on the rejection than for a larger number of inputs
     574              if (!useVariance) {
     575                  return combineTestGeneral(num, suspect, inputs, buffer, x, y, numIter, rej, sys,
     576                                            olympic, useVariance, safe, allowSuspect);
     577              }
     578
     579              // Differences between pixel values
     580              float diff01 = pixelData->data.F32[0] - pixelData->data.F32[1];
     581              float diff12 = pixelData->data.F32[1] - pixelData->data.F32[2];
     582              float diff20 = pixelData->data.F32[2] - pixelData->data.F32[0];
     583              // Variance for each pixel
     584              float var0 = pixelVariances->data.F32[0] + PS_SQR(sys * pixelData->data.F32[0]);
     585              float var1 = pixelVariances->data.F32[1] + PS_SQR(sys * pixelData->data.F32[1]);
     586              float var2 = pixelVariances->data.F32[2] + PS_SQR(sys * pixelData->data.F32[2]);
     587              // Errors in pixel differences
     588              float err01 = var0 + var1;
     589              float err12 = var1 + var2;
     590              float err20 = var2 + var0;
     591
     592#ifdef TESTING
     593              if (x == TEST_X && y == TEST_Y) {
     594                  fprintf(stderr, "Diff 0-1: %f %f\n", diff01, err01);
     595                  fprintf(stderr, "Diff 1-2: %f %f\n", diff12, err12);
     596                  fprintf(stderr, "Diff 2-0: %f %f\n", diff20, err20);
     597              }
     598#endif
     599
     600              int badPairs = 0;         // Number of bad pairs
     601              bool bad01 = false, bad12 = false, bad20 = false; // Pair is bad?
     602              if (PS_SQR(diff01) > rej2 * err01) {
     603                  bad01 = true;
     604                  badPairs++;
     605              }
     606              if (PS_SQR(diff12) > rej2 * err12) {
     607                  bad12 = true;
     608                  badPairs++;
     609              }
     610              if (PS_SQR(diff20) > rej2 * err20) {
     611                  bad20 = true;
     612                  badPairs++;
     613              }
     614
     615              if (badPairs > 0 && allowSuspect && suspect) {
     616                  return false;
     617              }
     618
     619              switch (badPairs) {
     620                case 0:
     621                  // Nothing to worry about!
     622                  break;
     623                case 1:
     624                  // Can't tell which image is bad, so be sure to get it
     625                  if (bad01) {
     626                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     627                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     628                      break;
     629                  }
     630                  if (bad12) {
     631                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     632                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     633                      break;
     634                  }
     635                  if (bad20) {
     636                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     637                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     638                      break;
     639                  }
     640                  psAbort("Should never get here");
     641                case 2:
     642                  if (bad01 && bad12) {
     643                      // 2 and 0 are good
     644                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     645                      break;
     646                  }
     647                  if (bad12 && bad20) {
     648                      // 0 and 1 are good
     649                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     650                      break;
     651                  }
     652                  if (bad20 && bad01) {
     653                      // 1 and 2 are good
     654                      combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     655                      break;
     656                  }
     657                  psAbort("Should never get here");
     658                case 3:
     659                  // Everything's bad
     660                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[0]);
     661                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
     662                  combineMarkInspect(inputs, x, y, pixelSources->data.U16[2]);
     663                  break;
     664              }
     665              break;
     666          }
     667#endif
     668          default: {
     669              if (useVariance) {
     670                  float median = combinationWeightedOlympic(pixelData, pixelWeights,
     671                                                            olympic, buffer->sort); // Median for stack
     672#ifdef TESTING
     673                  if (x == TEST_X && y == TEST_Y) {
     674                      fprintf(stderr, "Flag with variance, median = %f\n", median);
     675                  }
     676#endif
     677                  float worst = -INFINITY; // Largest deviation
     678                  for (int j = 0; j < num; j++) {
     679                      float diff = pixelData->data.F32[j] - median; // Difference from expected
     680#ifdef TESTING
     681                      if (x == TEST_X && y == TEST_Y) {
     682                          fprintf(stderr, "Testing input %d: %f\n", j, diff);
     683                      }
     684#endif
     685
     686                      // Comparing squares --- cheaper than lots of sqrts
     687                      // pixelVariances includes the rejection limit, from above
     688                      float diff2 = PS_SQR(diff); // Square difference
     689                      if (diff2 > pixelLimits->data.F32[j]) {
     690                          float dev = diff2 / pixelLimits->data.F32[j]; // Deviation
     691                          if (dev > worst) {
     692                              worst = dev;
     693                              maskIndex = j;
     694                          }
     695                      }
     696                  }
     697              } else {
     698                  float median = NAN, stdev = NAN;  // Median and stdev of the combination, for rejection
     699                  combinationMedianStdev(&median, &stdev, pixelData, buffer->sort);
     700                  float limit = rej * stdev; // Rejection limit
     701#ifdef TESTING
     702                  if (x == TEST_X && y == TEST_Y) {
     703                      fprintf(stderr, "Flag without variance; median = %f, stdev = %f, limit = %f\n",
     704                              median, stdev, limit);
     705                  }
     706#endif
     707                  float worst = -INFINITY; // Largest deviation
     708                  for (int j = 0; j < num; j++) {
     709                      float diff = fabsf(pixelData->data.F32[j] - median); // Difference from expected
     710
     711                      if (diff > limit) {
     712                          float dev = diff / limit; // Deviation
     713                          if (dev > worst) {
     714                              worst = dev;
     715                              maskIndex = j;
     716                          }
     717                      }
     718                  }
     719              }
     720          }
     721        }
     722
     723        // Do the actual rejection of the pixel
     724        if (maskIndex >= 0) {
     725            if (suspect) {
     726#ifdef TESTING
     727                if (x == TEST_X && y == TEST_Y) {
     728                    fprintf(stderr, "Throwing out all suspect pixels\n");
     729                }
     730#endif
     731                // Throw out all suspect pixels
     732                int numGood = 0;        // Number of good pixels
     733                for (int j = 0; j < num; j++) {
     734                    if (pixelSuspects->data.U8[j]) {
     735                        combineMarkReject(inputs, x, y, pixelSources->data.U16[j]);
     736                        continue;
     737                    }
     738                    if (numGood == j) {
     739                        numGood++;
     740                        continue;
     741                    }
     742                    pixelData->data.F32[numGood] = pixelData->data.F32[j];
     743                    pixelWeights->data.F32[numGood] = pixelWeights->data.F32[j];
     744                    pixelSources->data.U16[numGood] = pixelSources->data.U16[j];
     745                    pixelLimits->data.F32[numGood] = pixelLimits->data.F32[j];
     746                    pixelVariances->data.F32[numGood] = pixelVariances->data.F32[j];
     747                    numGood++;
     748                }
     749                pixelData->n = numGood;
     750                pixelWeights->n = numGood;
     751                pixelSources->n = numGood;
     752                pixelLimits->n = numGood;
     753                pixelVariances->n = numGood;
     754                totalClipped += num - numGood;
     755                num = numGood;
     756                suspect = false;
     757            } else {
     758                // Throw out masked pixel
     759#ifdef TESTING
     760                if (x == TEST_X && y == TEST_Y) {
     761                    fprintf(stderr, "Throwing out input %d\n", maskIndex);
     762                }
     763#endif
     764                combineMarkInspect(inputs, x, y, pixelSources->data.U16[maskIndex]);
     765                int numGood = 0;        // Number of good pixels
     766                for (int j = 0; j < num; j++) {
     767                    if (j == maskIndex) {
     768                        continue;
     769                    }
     770                    if (numGood == j) {
     771                        numGood++;
     772                        continue;
     773                    }
     774                    pixelData->data.F32[numGood] = pixelData->data.F32[j];
     775                    pixelWeights->data.F32[numGood] = pixelWeights->data.F32[j];
     776                    pixelSources->data.U16[numGood] = pixelSources->data.U16[j];
     777                    pixelLimits->data.F32[numGood] = pixelLimits->data.F32[j];
     778                    pixelVariances->data.F32[numGood] = pixelVariances->data.F32[j];
     779                    numGood++;
     780                }
     781                pixelData->n = numGood;
     782                pixelWeights->n = numGood;
     783                pixelSources->n = numGood;
     784                pixelLimits->n = numGood;
     785                pixelVariances->n = numGood;
     786                totalClipped++;
     787                num--;
     788            }
     789        }
     790    }
     791
     792    return true;
    636793}
    637794
     
    639796// Ensure the input array of pmStackData is valid, and get some details out of it
    640797static bool validateInputData(bool *haveVariances, // Do we have variance maps in the sky images?
    641                               bool *haveRejects, // Do we have lists of rejected pixels?
    642798                              int *num,    // Number of inputs
    643799                              int *numCols, int *numRows, // Size of (sky) images
    644                               psArray *input // Input array of pmStackData to validate
     800                              const psArray *input, // Input array of pmStackData to validate
     801                              const pmReadout *output // Output readout
    645802    )
    646803{
     
    668825        PS_ASSERT_IMAGE_TYPE(data->readout->variance, PS_TYPE_F32, false);
    669826    }
    670     *haveRejects = (data->reject != NULL);
     827    bool haveRejects = (data->reject != NULL); // Do we have rejected pixels?
    671828
    672829    // Make sure the rest correspond with the first
     
    681838            return false;
    682839        }
    683         if ((*haveRejects && !data->reject) || (data->reject && !*haveRejects)) {
     840        if ((haveRejects && !data->reject) || (data->reject && !haveRejects)) {
    684841            psError(PS_ERR_UNEXPECTED_NULL, true,
    685842                    "The rejected pixels are specified in some but not all inputs.");
     
    697854            PS_ASSERT_IMAGE_TYPE(data->readout->variance, PS_TYPE_F32, false);
    698855        }
     856    }
     857
     858    PM_ASSERT_READOUT_NON_NULL(output, false);
     859    if (output->image) {
     860        PS_ASSERT_IMAGE_NON_NULL(output->image, false);
     861        PS_ASSERT_IMAGE_TYPE(output->image, PS_TYPE_F32, false);
     862        PS_ASSERT_IMAGE_NON_NULL(output->mask, false);
     863        PS_ASSERT_IMAGE_TYPE(output->mask, PS_TYPE_IMAGE_MASK, false);
     864        PS_ASSERT_IMAGES_SIZE_EQUAL(output->image, output->mask, false);
    699865    }
    700866
     
    782948
    783949/// Stack input images
    784 bool pmStackCombine(pmReadout *combined, psArray *input, psImageMaskType maskVal, psImageMaskType bad,
    785                     int kernelSize, int numIter, float rej, float sys, float discard,
    786                     bool entire, bool useVariance, bool safe, bool rejectInspect)
    787 {
    788     PS_ASSERT_PTR_NON_NULL(combined, false);
     950bool pmStackCombine(pmReadout *combined, psArray *input, psImageMaskType maskVal, psImageMaskType maskSuspect,
     951                    psImageMaskType bad, int kernelSize, float iter, float rej, float sys, float olympic,
     952                    bool useVariance, bool safe, bool rejection)
     953{
    789954    bool haveVariances;                 // Do we have the variance maps?
    790     bool haveRejects;                   // Do we have lists of rejected pixels?
    791955    int num;                            // Number of inputs
    792956    int numCols, numRows;               // Size of (sky) images
    793     if (!validateInputData(&haveVariances, &haveRejects, &num, &numCols, &numRows, input)) {
     957    if (!validateInputData(&haveVariances, &num, &numCols, &numRows, input, combined)) {
    794958        return false;
    795959    }
    796960    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
    797961    PS_ASSERT_INT_POSITIVE(bad, false);
    798     PS_ASSERT_INT_NONNEGATIVE(numIter, false);
    799962    if (isnan(rej)) {
    800         PS_ASSERT_INT_EQUAL(numIter, 0, false);
     963        PS_ASSERT_FLOAT_EQUAL(iter, 0, false);
    801964    } else {
     965        PS_ASSERT_FLOAT_LARGER_THAN(iter, 0, false);
    802966        PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
    803     }
    804     if (haveRejects) {
    805         // This is a subsequent combination, so expect that the image and mask already exist
    806         PS_ASSERT_IMAGE_NON_NULL(combined->image, false);
    807         PS_ASSERT_IMAGE_TYPE(combined->image, PS_TYPE_F32, false);
    808         PS_ASSERT_IMAGE_NON_NULL(combined->mask, false);
    809         PS_ASSERT_IMAGE_TYPE(combined->mask, PS_TYPE_IMAGE_MASK, false);
    810         PS_ASSERT_IMAGES_SIZE_EQUAL(combined->image, combined->mask, false);
    811967    }
    812968    if (useVariance && !haveVariances) {
     
    833989        }
    834990#endif
    835         if (!haveRejects && !data->inspect) {
    836             data->inspect = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
     991        if (!rejection) {
     992            // Ensure pixels can be put on the appropriate list
     993            if (!data->inspect) {
     994                data->inspect = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
     995            }
     996            if (!data->reject) {
     997                data->reject = psPixelsAllocEmpty(PIXEL_LIST_BUFFER);
     998            }
    837999        }
    8381000    }
     
    8631025    combineBuffer *buffer = combineBufferAlloc(num);
    8641026
    865     if (haveRejects) {
    866         psImage *combinedImage = combined->image; // Combined image
    867         psImage *combinedMask = combined->mask; // Combined mask
    868         psImage *combinedVariance = combined->variance; // Combined variance map
    869 
    870         psArray *pixelMap = pixelMapGenerate(input, minInputCols, maxInputCols,
    871                                              minInputRows, maxInputRows); // Map of pixels to source
    872         psPixels *pixels = NULL;            // Total list of pixels, with no duplicates
     1027    // Pull the products out, allocate if necessary
     1028    psImage *combinedImage = combined->image; // Combined image
     1029    if (!combinedImage) {
     1030        combined->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1031        combinedImage = combined->image;
     1032    }
     1033    psImage *combinedMask = combined->mask; // Combined mask
     1034    if (!combinedMask) {
     1035        combined->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
     1036        combinedMask = combined->mask;
     1037    }
     1038
     1039    psImage *combinedVariance = combined->variance; // Combined variance map
     1040    if (haveVariances && !combinedVariance) {
     1041        combined->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1042        combinedVariance = combined->variance;
     1043    }
     1044
     1045    // Set up rejection list
     1046    psArray *pixelMap = NULL;           // Map of pixels to source
     1047    if (rejection) {
     1048        pixelMap = pixelMapGenerate(input, minInputCols, maxInputCols, minInputRows, maxInputRows);
     1049    }
     1050
     1051    // Combine each pixel
     1052    for (int y = minInputRows; y < maxInputRows; y++) {
     1053        for (int x = minInputCols; x < maxInputCols; x++) {
     1054#ifdef TESTING
     1055            if (x == TEST_X && y == TEST_Y) {
     1056                fprintf(stderr, "Combining pixel %d,%d: %x %x %f %f %f %f %d %d %d\n",
     1057                        x, y, maskVal, bad, iter, rej, sys, olympic, useVariance, safe, rejection);
     1058            }
     1059#endif
     1060            psVector *reject = NULL; // Images to reject for this pixel
     1061            if (rejection) {
     1062                reject = pixelMapQuery(pixelMap, minInputCols, minInputRows, x, y);
     1063#ifdef TESTING
     1064                if (x == TEST_X && y == TEST_Y) {
     1065                    fprintf(stderr, "Rejected inputs: ");
     1066                    if (!reject) {
     1067                        fprintf(stderr, "<none>\n");
     1068                    } else {
     1069                        for (int i = 0; i < reject->n; i++) {
     1070                            fprintf(stderr, "%d ", reject->data.U16[i]);
     1071                        }
     1072                        fprintf(stderr, "\n");
     1073                    }
     1074                }
     1075#endif
     1076            }
     1077
     1078            int num;                    // Number of good pixels
     1079            bool suspect;               // Suspect pixels in stack?
     1080            combineExtract(&num, &suspect, buffer, combinedImage, combinedMask, combinedVariance,
     1081                           input, weights, addVariance, reject, x, y, maskVal, maskSuspect);
     1082            combinePixels(combinedImage, combinedMask, combinedVariance, num, buffer, x, y, bad, safe);
     1083
     1084            if (iter > 0) {
     1085                combineTest(num, suspect, input, buffer, x, y, iter, rej, sys, olympic,
     1086                            useVariance, safe);
     1087            }
     1088        }
     1089    }
     1090
     1091    psFree(pixelMap);
     1092    psFree(weights);
     1093    psFree(buffer);
     1094
     1095
     1096
     1097#ifndef PS_NO_TRACE
     1098    if (!rejection && psTraceGetLevel("psModules.imcombine") >= 5) {
    8731099        for (int i = 0; i < num; i++) {
    874             pmStackData *data = input->data[i]; // Stacking data; contains the list of pixels
    875             if (!data) {
     1100            pmStackData *data = input->data[i]; // Stack data for this input
     1101            if (!data || !data->inspect) {
    8761102                continue;
    8771103            }
    878             pixels = psPixelsConcatenate(pixels, data->reject);
    879         }
    880         pixels = psPixelsDuplicates(pixels, pixels);
    881 
    882         if (entire) {
    883             // Combine entire image
    884             for (int y = minInputRows; y < maxInputRows; y++) {
    885                 for (int x = minInputCols; x < maxInputCols; x++) {
    886                     psVector *reject = pixelMapQuery(pixelMap, minInputCols, minInputRows,
    887                                                      x, y); // Reject these images
    888                     combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
    889                                   addVariance, reject, x, y, maskVal, bad, numIter, rej, sys, discard,
    890                                   useVariance, safe, rejectInspect, buffer);
    891                 }
    892             }
    893         } else {
    894             // Only combine previously rejected pixels
    895             for (int i = 0; i < pixels->n; i++) {
    896                 // Pixel coordinates are in the frame of the original image
    897                 int x = pixels->data[i].x, y = pixels->data[i].y; // Coordinates of interest
    898                 if (x < minInputCols || x >= maxInputCols || y < minInputRows || y >= maxInputRows) {
    899                     continue;
    900                 }
    901                 psVector *reject = pixelMapQuery(pixelMap, minInputCols, minInputRows,
    902                                                  x, y); // Reject these images
    903                 combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
    904                               addVariance, reject, x, y, maskVal, bad, numIter, rej, sys, discard,
    905                               useVariance, safe, rejectInspect, buffer);
    906             }
    907         }
    908         psFree(pixels);
    909         psFree(pixelMap);
    910     } else {
    911         // Pull the products out, allocate if necessary
    912         psImage *combinedImage = combined->image; // Combined image
    913         if (!combinedImage) {
    914             combined->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    915             combinedImage = combined->image;
    916         }
    917         psImage *combinedMask = combined->mask; // Combined mask
    918         if (!combinedMask) {
    919             combined->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    920             combinedMask = combined->mask;
    921         }
    922 
    923         psImage *combinedVariance = combined->variance; // Combined variance map
    924         if (haveVariances && !combinedVariance) {
    925             combined->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    926             combinedVariance = combined->variance;
    927         }
    928 
    929         for (int y = minInputRows; y < maxInputRows; y++) {
    930             for (int x = minInputCols; x < maxInputCols; x++) {
    931                 combinePixels(combinedImage, combinedMask, combinedVariance, input, weights,
    932                               addVariance, NULL, x, y, maskVal, bad, numIter, rej, sys, discard,
    933                               useVariance, safe, rejectInspect, buffer);
    934             }
    935         }
    936 
    937 #ifndef PS_NO_TRACE
    938         if (psTraceGetLevel("psModules.imcombine") >= 5) {
    939             for (int i = 0; i < num; i++) {
    940                 pmStackData *data = input->data[i]; // Stack data for this input
    941                 if (!data || !data->inspect) {
    942                     continue;
    943                 }
    944                 psTrace("psModules.imcombine", 5, "Image %d: %ld pixels to inspect.\n", i, data->inspect->n);
    945             }
    946         }
    947 #endif
    948     }
    949 
    950     psFree(weights);
    951     psFree(buffer);
     1104            psTrace("psModules.imcombine", 5, "Image %d: %ld pixels to inspect.\n", i, data->inspect->n);
     1105        }
     1106    }
     1107#endif
    9521108
    9531109    return true;
  • trunk/psModules/src/imcombine/pmStack.h

    r23577 r26076  
    4444                    psArray *input,     ///< Input array of pmStackData
    4545                    psImageMaskType maskVal, ///< Mask value of bad pixels
     46                    psImageMaskType suspect, ///< Mask value of suspect pixels
    4647                    psImageMaskType bad,     ///< Mask value to give rejected pixels
    4748                    int kernelSize,     ///< Half-size of the convolution kernel
    48                     int numIter,        ///< Number of iterations
     49                    float iter,         ///< Number of iterations per input
    4950                    float rej,          ///< Rejection limit (standard deviations)
    5051                    float sys,          ///< Relative systematic error
    5152                    float discard,      ///< Fraction of values to discard for Olympic weighted mean
    52                     bool entire,        ///< Combine entire image even if rejection lists provided?
    5353                    bool useVariance,   ///< Use variance values for rejection?
    5454                    bool safe,          ///< Play safe with small numbers of input pixels (mask if N <= 2)?
  • trunk/psModules/src/objects

  • trunk/psModules/src/objects/pmSourceMatch.c

    r25256 r26076  
    1717#define SOURCES_MAX_LEAF 2              // Maximum number of points on a tree leaf
    1818#define ARRAY_BUFFER 16                 // Buffer for array
     19
    1920
    2021//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    124125    match->mag = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_F32);
    125126    match->magErr = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_F32);
     127    match->x = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_F32);
     128    match->y = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_F32);
    126129    match->image = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_U32);
    127130    match->index = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_U32);
     
    133136void pmSourceMatchAdd(pmSourceMatch *match, // Match data
    134137                      float mag, float magErr, // Magnitude and error
     138                      float x, float y,        // Position
    135139                      int image, // Image index
    136140                      int index // Source index
     
    141145    match->mag = psVectorExtend(match->mag, match->mag->nalloc, 1);
    142146    match->magErr = psVectorExtend(match->magErr, match->magErr->nalloc, 1);
     147    match->x = psVectorExtend(match->x, match->x->nalloc, 1);
     148    match->y = psVectorExtend(match->y, match->y->nalloc, 1);
    143149    match->image = psVectorExtend(match->image, match->image->nalloc, 1);
    144150    match->index = psVectorExtend(match->index, match->index->nalloc, 1);
     
    147153    match->mag->data.F32[num] = mag;
    148154    match->magErr->data.F32[num] = magErr;
     155    match->x->data.F32[num] = x;
     156    match->y->data.F32[num] = y;
    149157    match->image->data.S32[num] = image;
    150158    match->index->data.S32[num] = index;
     
    192200                pmSourceMatch *match = pmSourceMatchAlloc(); // Match data
    193201                pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j],
    194                                  i, indices->data.S32[j]);
     202                                 xImage->data.F32[j], yImage->data.F32[j], i, indices->data.S32[j]);
    195203                matches->data[j] = match;
    196204            }
     
    212220                pmSourceMatch *match = pmSourceMatchAlloc(); // Match data
    213221                pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j],
    214                                  i, indices->data.S32[j]);
     222                                 xImage->data.F32[j], yImage->data.F32[j], i, indices->data.S32[j]);
    215223                matches->data[k] = match;
    216224            }
     
    238246                    pmSourceMatch *match = matches->data[index]; // Match data
    239247                    pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j],
    240                                      i, indices->data.S32[j]);
     248                                     xImage->data.F32[j], yImage->data.F32[j], i, indices->data.S32[j]);
    241249                    numMatch++;
    242250                } else {
     
    244252                    pmSourceMatch *match = pmSourceMatchAlloc(); // Match data
    245253                    pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j],
    246                                      i, indices->data.S32[j]);
     254                                     xImage->data.F32[j], yImage->data.F32[j], i, indices->data.S32[j]);
    247255                    xMaster->data.F32[numMaster] = xImage->data.F32[j];
    248256                    yMaster->data.F32[numMaster] = yImage->data.F32[j];
     
    304312        pmSourceMatch *match = matches->data[i]; // Match of interest
    305313        for (int j = 0; j < match->num && !source; j++) {
    306             if (!isfinite(match->mag->data.F32[j]) || !isfinite(match->magErr->data.F32[j])) {
     314            if (!isfinite(match->mag->data.F32[j]) || !isfinite(match->magErr->data.F32[j]) ||
     315                !isfinite(match->x->data.F32[j]) || !isfinite(match->y->data.F32[j])) {
    307316                continue;
    308317            }
     
    365374        double star = 0.0, starErr = 0.0; // Accumulators for star
    366375        for (int j = 0; j < match->num; j++) {
    367             if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
     376            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_PHOT) {
    368377                continue;
    369378            }
     
    396405        pmSourceMatch *match = matches->data[i]; // Matched stars
    397406        for (int j = 0; j < match->num; j++) {
    398             if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
     407            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_PHOT) {
    399408                continue;
    400409            }
     
    424433        pmSourceMatch *match = matches->data[i]; // Matched stars
    425434        for (int j = 0; j < match->num; j++) {
    426             if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
     435            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_PHOT) {
    427436                continue;
    428437            }
     
    543552        pmSourceMatch *match = matches->data[i]; // Matched stars
    544553        for (int j = 0; j < match->num; j++) {
    545             if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
     554            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_PHOT) {
    546555                continue;
    547556            }
     
    564573                if (PS_SQR(dev) > starClip * (PS_SQR(magErr) + sysErr2)) {
    565574                    numRejected++;
    566                     match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] = 0xFF;
     575                    match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] |= PM_SOURCE_MATCH_MASK_PHOT;
    567576                }
    568577            }
     
    627636            return NULL;
    628637        }
    629         psTrace("psModules.objects", 3, "Pass 1: Determined %d/%d are photometric", numPhoto, numImages);
     638        psTrace("psModules.objects", 3, "Pass 1: Determined %d/%d are photometric\n", numPhoto, numImages);
    630639
    631640        fracRej = sourceMatchRelphotReject(trans, stars, matches, zp, photo, badImage, rej1, sys1);
    632         psTrace("psModules.objects", 3, "Pass 1: %f%% of measurements rejected", fracRej * 100);
     641        psTrace("psModules.objects", 3, "Pass 1: %f%% of measurements rejected\n", fracRej * 100);
    633642
    634643        chi2 = sourceMatchRelphotIterate(trans, stars, badImage, matches, zp, photo, sys1);
     
    649658            return NULL;
    650659        }
    651         psTrace("psModules.objects", 3, "Pass 2: Determined %d/%d are photometric", numPhoto, numImages);
     660        psTrace("psModules.objects", 3, "Pass 2: Determined %d/%d are photometric\n", numPhoto, numImages);
    652661
    653662        fracRej = sourceMatchRelphotReject(trans, stars, matches, zp, photo, badImage, rej2, sys2);
    654         psTrace("psModules.objects", 3, "Pass 2: %f%% of measurements rejected", fracRej * 100);
     663        psTrace("psModules.objects", 3, "Pass 2: %f%% of measurements rejected\n", fracRej * 100);
    655664
    656665        chi2 = sourceMatchRelphotIterate(trans, stars, badImage, matches, zp, photo, sys2);
     
    668677    return trans;
    669678}
     679
     680
     681// Iterate on the star positions and image shifts
     682// Returns the solution chi^2
     683static float sourceMatchRelastroIterate(psVector *xShift, psVector *yShift, // Shift for image
     684                                        psVector *xStar, psVector *yStar,   // Position for star
     685                                        const psArray *matches // Array of matches
     686    )
     687{
     688    psAssert(matches, "Need list of matches");
     689
     690    int numImages = xShift->n;          // Number of images
     691    int numStars = matches->n;          // Number of stars
     692
     693    psAssert(xShift && xShift->type.type == PS_TYPE_F32 && yShift && yShift->type.type == PS_TYPE_F32,
     694             "Need shifts");
     695    psAssert(yShift->n == numImages, "Not enough shifts: %ld\n", yShift->n);
     696    psAssert(xStar && xStar->type.type == PS_TYPE_F32 && yStar && yStar->type.type == PS_TYPE_F32,
     697             "Need star positions");
     698    psAssert(xStar->n == numStars && yStar->n == numStars, "Not enough stars\n");
     699
     700    // Solve the star positions
     701    psVectorInit(xStar, NAN);
     702    psVectorInit(yStar, NAN);
     703    psVector *starMask = psVectorAlloc(numStars, PS_TYPE_U8); // Mask for stars
     704    psVectorInit(starMask, 0xFF);
     705    int numGoodStars = 0;               // Number of stars with good measurements
     706    for (int i = 0; i < numStars; i++) {
     707        pmSourceMatch *match = matches->data[i]; // Matched stars
     708        int numMeasurements = 0;        // Number of unmasked measurements for star
     709        double xSum = 0.0, ySum = 0.0;  // Accumulators for star
     710        for (int j = 0; j < match->num; j++) {
     711            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_ASTRO) {
     712                continue;
     713            }
     714            numMeasurements++;
     715            int index = match->image->data.U32[j]; // Image index
     716
     717            xSum += match->x->data.F32[j] - xShift->data.F32[index];
     718            ySum += match->y->data.F32[j] - yShift->data.F32[index];
     719        }
     720        if (numMeasurements > 1) {
     721            // It's only a good star (contributing to the chi^2) if there's more than 1 measurement
     722            numGoodStars++;
     723            xStar->data.F32[i] = xSum / numMeasurements;
     724            yStar->data.F32[i] = ySum / numMeasurements;
     725            starMask->data.U8[i] = 0;
     726        }
     727    }
     728
     729    // Solve for the shifts
     730    psVectorInit(xShift, 0.0);
     731    psVectorInit(yShift, 0.0);
     732    psVector *num = psVectorAlloc(numImages, PS_TYPE_S32);    // Number of stars
     733    psVectorInit(num, 0);
     734    for (int i = 0; i < numStars; i++) {
     735        if (starMask->data.U8[i]) {
     736            continue;
     737        }
     738        pmSourceMatch *match = matches->data[i]; // Matched stars
     739        for (int j = 0; j < match->num; j++) {
     740            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_ASTRO) {
     741                continue;
     742            }
     743            int index = match->image->data.U32[j]; // Image index
     744
     745            xShift->data.F32[index] += match->x->data.F32[j] - xStar->data.F32[i];
     746            yShift->data.F32[index] += match->y->data.F32[j] - yStar->data.F32[i];
     747            num->data.S32[index]++;
     748        }
     749    }
     750    for (int i = 0; i < numImages; i++) {
     751        xShift->data.F32[i] /= num->data.S32[i];
     752        yShift->data.F32[i] /= num->data.S32[i];
     753        psTrace("psModules.objects", 3, "Shift for image %d: %f,%f\n",
     754                i, xShift->data.F32[i], yShift->data.F32[i]);
     755    }
     756    psFree(num);
     757
     758    // Once more through to evaluate chi^2
     759    float chi2 = 0.0;                   // chi^2 for iteration
     760    int dof = 0;                        // Degrees of freedom
     761    for (int i = 0; i < numStars; i++) {
     762        pmSourceMatch *match = matches->data[i]; // Matched stars
     763        if (starMask->data.U8[i]) {
     764            continue;
     765        }
     766        for (int j = 0; j < match->num; j++) {
     767            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j]) {
     768                continue;
     769            }
     770
     771            int index = match->image->data.U32[j]; // Image index
     772            float dx = match->x->data.F32[j] - xShift->data.F32[index] - xStar->data.F32[i];
     773            float dy = match->y->data.F32[j] - yShift->data.F32[index] - yStar->data.F32[i];
     774
     775            chi2 += PS_SQR(dx) + PS_SQR(dy);
     776            dof++;
     777        }
     778    }
     779    dof -= numGoodStars + numImages;
     780    chi2 /= dof;
     781
     782    return chi2;
     783}
     784
     785// Reject star measurements
     786// Returns the fraction of measurements that were rejected
     787static float sourceMatchRelastroReject(const psVector *xShift, const psVector *yShift, // Shifts for each image
     788                                       const psVector *xStar, const psVector *yStar, // Positions for each star
     789                                       const psArray *matches, // Array of matches
     790                                       float chi2,             // chi^2 from fit
     791                                       float rej               // Rejection threshold
     792                                )
     793{
     794    psAssert(matches, "Need list of matches");
     795
     796    int numImages = xShift->n;          // Number of images
     797    int numStars = matches->n;          // Number of stars
     798
     799    psAssert(xShift && xShift->type.type == PS_TYPE_F32 && yShift && yShift->type.type == PS_TYPE_F32,
     800             "Need shifts");
     801    psAssert(yShift->n == numImages, "Not enough shifts: %ld\n", yShift->n);
     802    psAssert(xStar && xStar->type.type == PS_TYPE_F32 && yStar && yStar->type.type == PS_TYPE_F32,
     803             "Need star positions");
     804    psAssert(xStar->n == numStars && yStar->n == numStars, "Not enough stars\n");
     805
     806    int numRejected = 0;                // Number rejected
     807    int numMeasurements = 0;            // Number of measurements
     808
     809    float thresh = PS_SQR(rej) * chi2;    // Threshold for rejection
     810
     811    for (int i = 0; i < numStars; i++) {
     812        pmSourceMatch *match = matches->data[i]; // Matched stars
     813        for (int j = 0; j < match->num; j++) {
     814            if (match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_SOURCE_MATCH_MASK_ASTRO) {
     815                continue;
     816            }
     817            numMeasurements++;
     818            int index = match->image->data.U32[j]; // Image index
     819
     820            float dx = match->x->data.F32[j] - xShift->data.F32[index] - xStar->data.F32[i];
     821            float dy = match->y->data.F32[j] - yShift->data.F32[index] - yStar->data.F32[i];
     822
     823            if (PS_SQR(dx) + PS_SQR(dy) > thresh) {
     824                numRejected++;
     825                match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] |= PM_SOURCE_MATCH_MASK_ASTRO;
     826            }
     827        }
     828    }
     829
     830    return (float)numRejected / (float)numMeasurements;
     831}
     832
     833psArray *pmSourceMatchRelastro(const psArray *matches, // Array of matches
     834                               int numImages,          // Number of images
     835                               float tol, // Relative tolerance for convergence
     836                               int iter1, // Number of iterations for pass 1
     837                               float rej1, // Limit on rejection between iterations for pass 1
     838                               int iter2, // Number of iterations for pass 2
     839                               float rej2, // Limit on rejection between iterations for pass 2
     840                               float rejLimit // Limit on rejection between iterations
     841    )
     842{
     843    PS_ASSERT_ARRAY_NON_NULL(matches, NULL);
     844
     845    int numStars = matches->n;          // Number of stars
     846    psVector *xShift = psVectorAlloc(numImages, PS_TYPE_F32); // x shift for each image
     847    psVector *yShift = psVectorAlloc(numImages, PS_TYPE_F32); // y shift for each image
     848    psVectorInit(xShift, 0.0);
     849    psVectorInit(yShift, 0.0);
     850    psVector *xStar = psVectorAlloc(numStars, PS_TYPE_F32); // x position for each star
     851    psVector *yStar = psVectorAlloc(numStars, PS_TYPE_F32); // y position for each star
     852
     853    float chi2 = sourceMatchRelastroIterate(xShift, yShift, xStar, yStar, matches); // chi^2 for solution
     854    psTrace("psModules.objects", 1, "Initial: chi^2 = %f\n", chi2);
     855    float lastChi2 = INFINITY;          // chi^2 on last iteration
     856    float fracRej = INFINITY;           // Fraction of measurements rejected
     857
     858    // In the first passes, the shifts are not well deteremined: use high systematic error and
     859    // rejection thresholds
     860    for (int i = 0; i < iter1; i++) {
     861        fracRej = sourceMatchRelastroReject(xShift, yShift, xStar, yStar, matches, chi2, rej1);
     862        psTrace("psModules.objects", 3, "Pass 1: %f%% of measurements rejected\n", fracRej * 100);
     863
     864        chi2 = sourceMatchRelastroIterate(xShift, yShift, xStar, yStar, matches);
     865        psTrace("psModules.objects", 1, "Pass 1: iter = %d: chi^2 = %f rejected = %f\n", i, chi2, fracRej);
     866    }
     867
     868    for (int i = 0; i < iter2 && (fabsf(lastChi2 - chi2) > tol * chi2 || fracRej > rejLimit); i++) {
     869        lastChi2 = chi2;
     870
     871        fracRej = sourceMatchRelastroReject(xShift, yShift, xStar, yStar, matches, chi2, rej2);
     872        psTrace("psModules.objects", 3, "Pass 2: %f%% of measurements rejected\n", fracRej * 100);
     873
     874        chi2 = sourceMatchRelastroIterate(xShift, yShift, xStar, yStar, matches);
     875        psTrace("psModules.objects", 1, "Pass 2: iter = %d: chi^2 = %f rejected = %f\n", i, chi2, fracRej);
     876    }
     877
     878    psFree(xStar);
     879    psFree(yStar);
     880
     881    if (fabsf(lastChi2 - chi2) > tol * chi2 || fracRej > rejLimit) {
     882        psWarning("Unable to converge to relphot solution (%f,%f)", (lastChi2 - chi2) / chi2, fracRej);
     883    }
     884
     885    psArray *results = psArrayAlloc(numImages); // Array of results
     886    for (int i = 0; i < numImages; i++) {
     887        psVector *offset = results->data[i] = psVectorAlloc(2, PS_TYPE_F32); // Offset for image
     888        offset->data.F32[0] = xShift->data.F32[i];
     889        offset->data.F32[1] = yShift->data.F32[i];
     890    }
     891    psFree(xShift);
     892    psFree(yShift);
     893
     894    return results;
     895}
     896
  • trunk/psModules/src/objects/pmSourceMatch.h

    r23241 r26076  
    11#ifndef PM_SOURCE_MATCH_H
    22#define PM_SOURCE_MATCH_H
     3
     4#include <pslib.h>
     5
     6/// Mask values for matched sources
     7typedef enum {
     8    PM_SOURCE_MATCH_MASK_PHOT = 0x01,   // Source was rejected from photometry fit
     9    PM_SOURCE_MATCH_MASK_ASTRO = 0x02,     // Source was rejected from astrometry fit
     10} pmSourceMatchMask;
    311
    412/// Matched sources
     
    1220    psVector *mag;                      // Magnitudes
    1321    psVector *magErr;                   // Magnitude errors
     22    psVector *x, *y;                    // Positions
    1423    psVector *mask;                     // Mask for measurements
    1524} pmSourceMatch;
     
    2635    PS_ASSERT_VECTOR_NON_NULL((MATCH)->mag, RVAL); \
    2736    PS_ASSERT_VECTOR_NON_NULL((MATCH)->magErr, RVAL); \
     37    PS_ASSERT_VECTOR_NON_NULL((MATCH)->x, RVAL); \
     38    PS_ASSERT_VECTOR_NON_NULL((MATCH)->y, RVAL); \
    2839    PS_ASSERT_VECTOR_SIZE((MATCH)->image, (MATCH)->num, RVAL); \
    2940    PS_ASSERT_VECTOR_SIZE((MATCH)->index, (MATCH)->num, RVAL); \
    3041    PS_ASSERT_VECTOR_SIZE((MATCH)->mag, (MATCH)->num, RVAL); \
    3142    PS_ASSERT_VECTOR_SIZE((MATCH)->magErr, (MATCH)->num, RVAL); \
     43    PS_ASSERT_VECTOR_SIZE((MATCH)->x, (MATCH)->num, RVAL); \
     44    PS_ASSERT_VECTOR_SIZE((MATCH)->y, (MATCH)->num, RVAL); \
    3245}
    3346
     
    3851void pmSourceMatchAdd(pmSourceMatch *match, // Match data
    3952                      float mag, float magErr, // Magnitude and error
     53                      float x, float y,        // Position
    4054                      int image, // Image index
    4155                      int index // Source index
     
    7589    );
    7690
     91/// Perform relative astrometry to calibrate images
     92psArray *pmSourceMatchRelastro(const psArray *matches, // Array of matches
     93                               int numImages,          // Number of images
     94                               float tol, // Relative tolerance for convergence
     95                               int iter1, // Number of iterations for pass 1
     96                               float rej1, // Limit on rejection between iterations for pass 1
     97                               int iter2, // Number of iterations for pass 2
     98                               float rej2, // Limit on rejection between iterations for pass 2
     99                               float rejLimit // Limit on rejection between iterations
     100    );
     101
    77102#endif
Note: See TracChangeset for help on using the changeset viewer.