IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 29543


Ignore:
Timestamp:
Oct 25, 2010, 2:45:41 PM (16 years ago)
Author:
eugene
Message:

merge changes from eam_branches/ipp-20100823

Location:
trunk/psModules/src/imcombine
Files:
14 edited
2 copied

Legend:

Unmodified
Added
Removed
  • trunk/psModules/src/imcombine/pmPSFEnvelope.c

    r29004 r29543  
    380380
    381381        // measure the source moments: tophat windowing, no pixel S/N cutoff
    382         // XXX probably should be passing the maskVal to this function so we can pass it along here...
    383         if (!pmSourceMoments(source, maxRadius, 0.0, 1.0, maskVal)) {
     382        if (!pmSourceMoments(source, maxRadius, 0.0, 0.0, maskVal)) {
    384383            // Can't do anything about it; limp along as best we can
    385384            psErrorClear();
  • trunk/psModules/src/imcombine/pmStack.c

    r27427 r29543  
    1919
    2020#include <stdio.h>
     21#include <string.h> // for memset
    2122#include <pslib.h>
    2223
     
    3435
    3536
    36 //#define TESTING                         // Enable test output
    37 //#define TEST_X 843-1                     // x coordinate to examine
    38 //#define TEST_Y 813-1                     // y coordinate to examine
    39 //#define TEST_RADIUS 0                    // Radius to examine
    40 
     37# if (0)
     38#define TESTING                         // Enable test output
     39#define TEST_X 4963                       // x coordinate to examine
     40#define TEST_Y 5353                       // y coordinate to examine
     41#define TEST_X 40                       // x coordinate to examine
     42#define TEST_Y 40                       // y coordinate to examine
     43#define TEST_RADIUS 0.5                 // Radius to examine
     44# endif
     45
     46# ifdef TESTING
     47# define CHECKPIX(XPIX,YPIX,MSG,...) { if (PS_SQR(XPIX - TEST_X) + PS_SQR(YPIX - TEST_Y) <= PS_SQR(TEST_RADIUS)) { fprintf(stderr,MSG,__VA_ARGS__); } }
     48# else
     49# define CHECKPIX(XPIX,YPIX,MSG,...) { }
     50# endif   
    4151
    4252// Data structure for use as a buffer when combining pixels
     
    250260                                      )
    251261{
    252 #ifdef TESTING
    253     if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    254         fprintf(stderr, "Marking image %d, pixel %d,%d for inspection\n", source, x, y);
    255     }
    256 #endif
     262    CHECKPIX(x, y, "Marking image %d, pixel %d,%d for inspection\n", source, x, y);
    257263    pmStackData *data = inputs->data[source]; // Stack data of interest
    258264    if (!data) {
     
    272278                                     )
    273279{
    274 #ifdef TESTING
    275     if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    276         fprintf(stderr, "Marking pixel image %d, pixel %d,%d for rejection\n", source, x, y);
    277     }
    278 #endif
     280    CHECKPIX(x, y, "Marking pixel image %d, pixel %d,%d for rejection\n", source, x, y);
    279281    pmStackData *data = inputs->data[source]; // Stack data of interest
    280282    if (!data) {
     
    290292static void combineExtract(int *num,                        // Number of good pixels
    291293                           bool *suspect,                   // Any suspect pixels?
     294                           psImageMaskType *badMask,        // OR of all bad (masked) pixels
     295                           psImageMaskType *goodMask,       // OR of all good (unmasked) pixels
    292296                           combineBuffer *buffer, // Buffer with vectors
    293297                           psImage *image, // Combined image, for output
     
    300304                           const psVector *reject, // Indices of pixels to reject, or NULL
    301305                           int x, int y, // Coordinates of interest; frame of output image
    302                            psImageMaskType maskVal, // Value to mask
    303                            psImageMaskType maskSuspect // Value to suspect
     306                           psImageMaskType badMaskBits, // Value to mask as 'bad'
     307                           psImageMaskType suspectMaskBits // Value to mask as 'suspect'
    304308                           )
    305309{
     
    322326    }
    323327
     328    // mask values to store possible mask combinations
     329    *badMask = 0;
     330    *goodMask = 0xffff;
     331
     332    int nGoodBits[16]; // accumulate the good pixel bits here for fuzzy logic
     333    psAssert (sizeof(psImageMaskType) == 2, "psImageMaskType is not the expected size");
     334    memset (nGoodBits, 0, 16*sizeof(int));
     335
    324336    // Extract the pixel and mask data
    325337    int numGood = 0;                    // Number of good pixels
     
    328340        // should be because of how pixelMapGenerate works
    329341        if (reject && reject->data.U16[j] == i) {
     342            // pixels can be rejected because:
     343            // 1) only 1 input pixel (and 'safe' is true)
     344            // 2) only 2 input pixels were available and they were mutually inconsistent (or variance info was missing)
     345            // 3) NOTE : ifdef'ed out code for 3 inputs case
     346            // 4) outlier from sample of N pixels
     347            // 5) some of these may have been suspect.
     348            // XXX raise a specific mask bit for these (currently results in BLANK)
    330349            j++;
     350
     351            pmStackData *data = inputs->data[i]; // Stack data of interest
     352            if (data) {
     353                int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
     354                psImage *mask = data->readout->mask; // Mask of interest
     355                *badMask |= mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn]; // save the bad bits used
     356                CHECKPIX(x, y, "reject: adding bit to mask: %d : %x (badMask = %x)\n", i, mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn], *badMask);
     357            } else {
     358                CHECKPIX(x, y, "reject: no item in data (badMask = %x)\n", *badMask);
     359            }
    331360            continue;
    332361        }
     
    334363        pmStackData *data = inputs->data[i]; // Stack data of interest
    335364        if (!data) {
     365            CHECKPIX(x, y, "skip: no item in data (badMask = %x)\n", *badMask);
    336366            continue;
    337367        }
     
    339369        int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
    340370        psImage *mask = data->readout->mask; // Mask of interest
    341         if (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & maskVal) {
     371        if (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & badMaskBits) {
     372            *badMask |= (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & badMaskBits); // save the bad bits used
     373            CHECKPIX(x, y, "skip: adding bit to mask: %d : %x (badMask = %x)\n", i, mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn], *badMask);
    342374            continue;
    343375        }
    344376
    345         pixelSuspects->data.U8[numGood] = mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & maskSuspect ?
    346             true : false;
     377        pixelSuspects->data.U8[numGood] = mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & suspectMaskBits ? true : false;
     378
     379        // *goodMask &= mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn]; // save the mask bits still used
     380        // check for set bits and increment counter as appropriate
     381        {
     382            psImageMaskType value = 0x0001;
     383            for (int nbit = 0; nbit < 16; nbit ++) {
     384                if (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & value) {
     385                    nGoodBits[nbit] ++;
     386                }
     387                value <<= 1;
     388            }
     389        }
    347390
    348391        psImage *image = data->readout->image; // Image of interest
     
    356399        pixelSources->data.U16[numGood] = i;
    357400        numGood++;
     401
     402        CHECKPIX(x, y, "keep: %d : %x (badMask = %x)\n", i, mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn], *badMask);
    358403    }
    359404    pixelData->n = numGood;
     
    367412    *num = numGood;
    368413
     414    // set the mask bits if nGoodBits[i] > f*numGood
     415    {
     416        # define SUSPECT_FRACTION 0.65
     417        *goodMask = 0x0000;
     418        psImageMaskType value = 0x0001;
     419        for (int nbit = 0; nbit < 16; nbit ++) {
     420            if (nGoodBits[nbit] > SUSPECT_FRACTION*numGood) {
     421                *goodMask |= value;
     422            }
     423            value <<= 1;
     424        }
     425    }
     426
    369427#ifdef TESTING
    370428    if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    371429        for (int i = 0; i < numGood; i++) {
    372             fprintf(stderr, "Input %d, pixel %d,%d (%" PRIu16 "): %f %f (%f) %f %f %d\n",
     430            fprintf(stderr, "Input %d, pixel %d,%d (%" PRIu16 "): %f %f (%f) %f %f %d %x %x -> %x %x\n",
    373431                    i, x, y, pixelSources->data.U16[i], pixelData->data.F32[i], pixelVariances->data.F32[i],
    374432                    addVariance->data.F32[i], pixelWeights->data.F32[i], pixelExps->data.F32[i],
    375                     pixelSuspects->data.U8[i]);
     433                    pixelSuspects->data.U8[i], badMaskBits, suspectMaskBits, *badMask, *goodMask);
    376434        }
    377435    }
     
    380438    return;
    381439}
    382 
    383440
    384441// Combine pixels
     
    392449                          combineBuffer *buffer, // Buffer with vectors
    393450                          int x, int y, // Coordinates of interest; frame of output image
    394                           psImageMaskType bad, // Value for bad pixels
     451                          psImageMaskType blankMask, // Value for empty pixels
     452                          psImageMaskType badMask, // Value for bad pixels
     453                          psImageMaskType goodMask, // Value for good pixels
    395454                          bool safe,           // Safe combination?
    396455                          float invTotalWeight    // Inverse of total weight for all inputs
     
    404463    // Default option is that the pixel is bad
    405464    float imageValue = NAN, varianceValue = NAN; // Value for combined image and variance map
    406     psImageMaskType maskValue = bad;    // Value for combined mask
    407465    float expValue = 0.0, expWeightValue = NAN; // Exposure value (straight, and weighted)
     466
     467    // default output mask value.  badMask is the OR of all unused input pixels. 
     468    // if there are no input pixels, this value will be 0, in which case we want to set the output pixel to BLANK.
     469    // if there are only good input pixels, and they do not result in a valid pixel, we still want to set this to BLANK.
     470    psImageMaskType maskValue = badMask ? badMask : blankMask;    // Value for combined mask
     471
     472    CHECKPIX(x, y, "bad vs good : %x %x %x\n", maskValue, badMask, blankMask);
    408473
    409474    switch (num) {
    410475      case 0: {
    411476          // Nothing to combine: it's bad
    412 #ifdef TESTING
    413           if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    414               fprintf(stderr, "No inputs to combine, pixel %d,%d is bad.\n", x, y);
    415           }
    416 #endif
     477          CHECKPIX(x, y, "No inputs to combine, pixel %d,%d is bad.\n", x, y);
    417478          break;
    418479      }
     
    428489                  expWeightValue = pixelExps->data.F32[0];
    429490              }
    430               maskValue = 0;
    431 #ifdef TESTING
    432               if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    433                   fprintf(stderr, "Single input to combine, safety off, pixel %d,%d --> %f\n",
    434                           x, y, imageValue);
    435               }
    436 #endif
    437           }
    438 #ifdef TESTING
    439           else if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    440               fprintf(stderr, "Single input to combine, safety on, pixel %d,%d is bad.\n", x, y);
    441           }
    442 #endif
     491              maskValue = goodMask;
     492              CHECKPIX(x, y, "Single input to combine, safety off, pixel %d,%d --> %f\n", x, y, imageValue);
     493          } else {
     494              CHECKPIX(x, y, "Single input to combine, safety on, pixel %d,%d is bad.\n", x, y);
     495          }
    443496          break;
    444497      }
     
    446499          // Automatically accept the mean of the pixels only if we're not playing safe
    447500          if (!safe) {
    448               if (combinationMeanVariance(&imageValue, &varianceValue, &expValue, &expWeightValue,
    449                                           pixelData, pixelVariances, pixelExps, pixelWeights)) {
    450                   maskValue = 0;
    451 #ifdef TESTING
    452                   if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    453                       fprintf(stderr, "Two inputs to combine using unsafe, pixel %d,%d --> %f %f\n",
    454                               x, y, imageValue, varianceValue);
    455                   }
    456 #endif
     501              if (combinationMeanVariance(&imageValue, &varianceValue, &expValue, &expWeightValue, pixelData, pixelVariances, pixelExps, pixelWeights)) {
     502                  maskValue = goodMask;
     503                  CHECKPIX(x, y, "Two inputs to combine using unsafe, pixel %d,%d --> %f %f\n", x, y, imageValue, varianceValue);
    457504              }
     505          } else {
     506              CHECKPIX(x, y, "Two inputs to combine, safety on, pixel %d,%d is bad\n", x, y);
    458507          }
    459 #ifdef TESTING
    460           else {
    461               if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    462                   fprintf(stderr, "Two inputs to combine, safety on, pixel %d,%d is bad\n", x, y);
    463               }
    464           }
    465 #endif
    466508          break;
    467509      }
     
    472514              break;
    473515          }
    474           maskValue = 0;
    475 #ifdef TESTING
    476           if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    477               fprintf(stderr, "Combined inputs, pixel %d,%d --> %f %f\n", x, y, imageValue, varianceValue);
    478           }
    479 #endif
     516          maskValue = goodMask;
     517          CHECKPIX(x, y, "Combined inputs, pixel %d,%d --> %f %f\n", x, y, imageValue, varianceValue);
    480518          break;
    481519      }
     
    522560    int numIter = PS_MAX(iter * num, 1); // Number of iterations
    523561
    524 #ifdef TESTING
    525     if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    526         fprintf(stderr, "Testing pixel %d,%d: %d %f %f %f %d %d\n",
    527                 x, y, numIter, rej, sys, olympic, useVariance, safe);
    528     }
    529 #endif
     562    CHECKPIX(x, y, "Testing pixel %d,%d: %d %f %f %f %d %d\n", x, y, numIter, rej, sys, olympic, useVariance, safe);
    530563
    531564    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
     
    548581            // Systematic error contributes to the rejection level
    549582            float sysVar = PS_SQR(sys * pixelData->data.F32[i]);
    550 #ifdef TESTING
    551             // Correct variance for comparison against weighted mean including itself
    552             float compare = 1.0 - pixelWeights->data.F32[i] / sumWeights;
    553             if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    554                 fprintf(stderr, "Variance %d (%d), pixel %d,%d: %f %f %f\n", i, pixelSources->data.U16[i],
    555                         x, y, pixelVariances->data.F32[i], sysVar, compare);
    556             }
    557 #endif
     583            CHECKPIX(x, y, "Variance %d (%d), pixel %d,%d: %f %f %f\n",
     584                     i, pixelSources->data.U16[i], x, y,
     585                     pixelVariances->data.F32[i], sysVar, 1.0 - pixelWeights->data.F32[i] / sumWeights);
    558586            pixelLimits->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar);
    559587        }
     
    593621                          combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
    594622                      }
    595 #ifdef TESTING
    596                       if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    597                           fprintf(stderr, "Flagged both inputs for pixel %d,%d (%f > %f x %f\n)",
    598                                   x, y, diff, rej, sqrtf(sigma2));
    599                       }
    600 #endif
     623                      CHECKPIX(x, y, "Flagged both inputs for pixel %d,%d (%f > %f x %f\n)", x, y, diff, rej, sqrtf(sigma2));
    601624                  }
    602625              } else if (i == 0 && safe) {
     
    708731                  float median = combinationWeightedOlympic(pixelData, pixelWeights,
    709732                                                            olympic, buffer->sort); // Median for stack
    710 #ifdef TESTING
    711                   if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    712                       fprintf(stderr, "Flag with variance pixel %d,%d: median = %f\n", x, y, median);
    713                   }
    714 #endif
     733                  CHECKPIX(x, y, "Flag with variance pixel %d,%d: median = %f\n", x, y, median);
    715734                  float worst = -INFINITY; // Largest deviation
    716735                  for (int j = 0; j < num; j++) {
    717736                      float diff = pixelData->data.F32[j] - median; // Difference from expected
    718 #ifdef TESTING
    719                       if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    720                           fprintf(stderr, "Testing input %d for pixel %d,%d: %f\n", j, x, y, diff);
    721                       }
    722 #endif
     737                      CHECKPIX(x, y, "Testing input %d for pixel %d,%d: %f\n", j, x, y, diff);
    723738
    724739                      // Comparing squares --- cheaper than lots of sqrts
     
    737752                  combinationMedianStdev(&median, &stdev, pixelData, buffer->sort);
    738753                  float limit = rej * stdev; // Rejection limit
    739 #ifdef TESTING
    740                   if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    741                       fprintf(stderr,
    742                               "Flag without variance pixel %d,%d; median = %f, stdev = %f, limit = %f\n",
    743                               x, y, median, stdev, limit);
    744                   }
    745 #endif
     754                  CHECKPIX(x, y, "Flag without variance pixel %d,%d; median = %f, stdev = %f, limit = %f\n", x, y, median, stdev, limit);
    746755                  float worst = -INFINITY; // Largest deviation
    747756                  for (int j = 0; j < num; j++) {
     
    763772        if (maskIndex >= 0) {
    764773            if (suspect) {
    765 #ifdef TESTING
    766                 if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    767                     fprintf(stderr, "Throwing out all suspect pixels for %d,%d\n", x, y);
    768                 }
    769 #endif
     774                CHECKPIX(x, y, "Throwing out all suspect pixels for %d,%d\n", x, y);
    770775                // Throw out all suspect pixels
    771776                int numGood = 0;        // Number of good pixels
     
    796801            } else {
    797802                // Throw out masked pixel
    798 #ifdef TESTING
    799                 if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    800                     fprintf(stderr, "Throwing out input %d for pixel %d,%d\n", maskIndex, x, y);
    801                 }
    802 #endif
     803                CHECKPIX(x, y, "Throwing out input %d for pixel %d,%d\n", maskIndex, x, y);
    803804                combineMarkInspect(inputs, x, y, pixelSources->data.U16[maskIndex]);
    804805                int numGood = 0;        // Number of good pixels
     
    9991000
    10001001/// Stack input images
    1001 bool pmStackCombine(pmReadout *combined, pmReadout *expmaps, psArray *input,
    1002                     psImageMaskType maskVal, psImageMaskType maskSuspect,
    1003                     psImageMaskType bad, int kernelSize,
    1004                     float iter, float rej, float sys, float olympic,
    1005                     bool useVariance, bool safe, bool rejection)
     1002bool pmStackCombine(
     1003    pmReadout *combined,                // output stacked readout
     1004    pmReadout *expmaps,                 // output exposure map information
     1005    psArray *input,                     // input exposures
     1006    psImageMaskType badMaskBits,        // treat these bits as 'bad'
     1007    psImageMaskType suspectMaskBits,    // treat these bits as 'suspect'
     1008    psImageMaskType blankMaskBits,      // use this mask value for pixels missing input data (distinguish between Ninput = 0 and Ngood = 0?)
     1009    int kernelSize,
     1010    float iter,
     1011    float rej,
     1012    float sys,
     1013    float olympic,
     1014    bool useVariance,
     1015    bool safe,
     1016    bool rejection)
    10061017{
    10071018    bool haveVariances;                 // Do we have the variance maps?
     
    10121023    }
    10131024    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
    1014     PS_ASSERT_INT_POSITIVE(bad, false);
     1025    PS_ASSERT_INT_POSITIVE(blankMaskBits, false);
    10151026    if (isnan(rej)) {
    10161027        PS_ASSERT_FLOAT_EQUAL(iter, 0, false);
     
    10701081    }
    10711082    psFree(stack);
    1072     pmReadoutUpdateSize(combined, minInputCols, minInputRows, xSize, ySize, true, true, bad);
     1083    pmReadoutUpdateSize(combined, minInputCols, minInputRows, xSize, ySize, true, true, blankMaskBits);
    10731084    psTrace("psModules.imcombine", 1, "Have for combination [%d:%d,%d:%d] (%dx%d)\n",
    10741085            minInputCols, maxInputCols, minInputRows, maxInputRows, xSize, ySize);
     
    11311142    for (int y = minInputRows; y < maxInputRows; y++) {
    11321143        for (int x = minInputCols; x < maxInputCols; x++) {
     1144            CHECKPIX(x, y, "Combining pixel %d,%d: %x %x %f %f %f %f %d %d %d\n", x, y, badMaskBits, blankMaskBits, iter, rej, sys, olympic, useVariance, safe, rejection);
     1145
    11331146#ifdef TESTING
    1134             if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    1135                 fprintf(stderr, "Combining pixel %d,%d: %x %x %f %f %f %f %d %d %d\n",
    1136                         x, y, maskVal, bad, iter, rej, sys, olympic, useVariance, safe, rejection);
    1137             }
    1138 #endif
     1147            if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     1148                fprintf (stderr, "combine\n");
     1149            }
     1150# endif
     1151
    11391152            psVector *reject = NULL; // Images to reject for this pixel
    11401153            if (rejection) {
     
    11541167#endif
    11551168            }
    1156 
    1157             int num;                    // Number of good pixels
    1158             bool suspect;               // Suspect pixels in stack?
    1159             combineExtract(&num, &suspect, buffer, combinedImage, combinedMask, combinedVariance,
    1160                            input, weights, exps, addVariance, reject, x, y, maskVal, maskSuspect);
    1161             combinePixels(combinedImage, combinedMask, combinedVariance, exp, expnum, expweight,
    1162                           num, buffer, x, y, bad, safe, totalExpWeight);
     1169 
     1170            int num;                      // Number of good pixels
     1171            bool suspect;                 // Suspect pixels in stack?
     1172            psImageMaskType badMask = 0;  // OR of mask bits in all bad input pixels
     1173            psImageMaskType goodMask = 0; // OR of mask bits in all good input pixels
     1174            combineExtract(&num, &suspect, &badMask, &goodMask, buffer, combinedImage, combinedMask, combinedVariance, input, weights, exps, addVariance, reject, x, y, badMaskBits, suspectMaskBits);
     1175            combinePixels(combinedImage, combinedMask, combinedVariance, exp, expnum, expweight, num, buffer, x, y, blankMaskBits, badMask, goodMask, safe, totalExpWeight);
    11631176
    11641177            if (iter > 0) {
  • trunk/psModules/src/imcombine/pmSubtraction.c

    r29004 r29543  
    540540
    541541    psImage *conv = psImageConvolveFFT(NULL, image->image, NULL, 0, kernel); // Convolved image
     542
     543    // note: do not attempt to renormalize kernels here: cannot have different stars with
     544    // different kernel ratios
     545
    542546    int x0 = - image->xMin, y0 = - image->yMin; // Position of centre of convolved image
    543547    psKernel *convolved = psKernelAllocFromImage(conv, x0, y0); // Kernel version
  • trunk/psModules/src/imcombine/pmSubtractionAnalysis.c

    r27086 r29543  
    299299                         kernels->rms);
    300300
    301         psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
    302         psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
    303         psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMIN_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
    304         psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMIN_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
    305         psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMAX_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
    306         psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMAX_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
    307 
    308         psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
    309         psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
    310         psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMIN_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
    311         psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMIN_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
    312         psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMAX_RES_MEAN,  0, "Mean Fractional Sigma of Residuals", kernels->fSigResMean);
    313         psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FMAX_RES_STDEV, 0, "Mean Fractional Sigma of Residuals", kernels->fSigResStdev);
     301        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_SIGMA_MEAN,  0, "Fractional Sigma of Residuals (Mean)", kernels->fResSigmaMean);
     302        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_SIGMA_STDEV, 0, "Fractional Sigma of Residuals (Stdev)", kernels->fResSigmaStdev);
     303        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_OUTER_MEAN,  0, "Fractional Residual Flux (Mean, R > 2 pix)", kernels->fResOuterMean);
     304        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_OUTER_STDEV, 0, "Fractional Residual Flux (Stdev, R > 2 pix)", kernels->fResOuterStdev);
     305        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_TOTAL_MEAN,  0, "Fractional Residual Flux (Mean, R > 0 pix)", kernels->fResTotalMean);
     306        psMetadataAddF32(analysis, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_TOTAL_STDEV, 0, "Fractional Residual Flux (Stdev, R > 0 pix)", kernels->fResTotalStdev);
     307
     308        psMetadataAddF32(header,   PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_SIGMA_MEAN,  0, "Fractional Sigma of Residuals (Mean)", kernels->fResSigmaMean);
     309        psMetadataAddF32(header,   PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_SIGMA_STDEV, 0, "Fractional Sigma of Residuals (Stdev)", kernels->fResSigmaStdev);
     310        psMetadataAddF32(header,   PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_OUTER_MEAN,  0, "Fractional Residual Flux (Mean, R > 2 pix)", kernels->fResOuterMean);
     311        psMetadataAddF32(header,   PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_OUTER_STDEV, 0, "Fractional Residual Flux (Stdev, R > 2 pix)", kernels->fResOuterStdev);
     312        psMetadataAddF32(header,   PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_TOTAL_MEAN,  0, "Fractional Residual Flux (Mean, R > 0 pix)", kernels->fResTotalMean);
     313        psMetadataAddF32(header,   PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_FRES_TOTAL_STDEV, 0, "Fractional Residual Flux (Stdev, R > 0 pix)", kernels->fResTotalStdev);
    314314    }
    315315
  • trunk/psModules/src/imcombine/pmSubtractionAnalysis.h

    r26893 r29543  
    2424#define PM_SUBTRACTION_ANALYSIS_DECONV_MAX   "SUBTRACTION.DECONV.MAX"   // Maximum deconvolution fraction
    2525
    26 #define PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_MEAN  "SUBTRACTION.RES.FSIGMA.MEAN"      // RMS stamp deviation
    27 #define PM_SUBTRACTION_ANALYSIS_FSIGMA_RES_STDEV "SUBTRACTION.RES.FSIGMA.STDEV"      // RMS stamp deviation
    28 #define PM_SUBTRACTION_ANALYSIS_FMIN_RES_MEAN    "SUBTRACTION.RES.FMIN.MEAN"      // RMS stamp deviation
    29 #define PM_SUBTRACTION_ANALYSIS_FMIN_RES_STDEV   "SUBTRACTION.RES.FMIN.STDEV"      // RMS stamp deviation
    30 #define PM_SUBTRACTION_ANALYSIS_FMAX_RES_MEAN    "SUBTRACTION.RES.FMAX.MEAN"      // RMS stamp deviation
    31 #define PM_SUBTRACTION_ANALYSIS_FMAX_RES_STDEV   "SUBTRACTION.RES.FMAX.STDEV"      // RMS stamp deviation
     26#define PM_SUBTRACTION_ANALYSIS_FRES_SIGMA_MEAN  "SUBTRACTION.FRES.MEAN" // RMS stamp deviation
     27#define PM_SUBTRACTION_ANALYSIS_FRES_SIGMA_STDEV "SUBTRACTION.FRES.STDEV" // RMS stamp deviation
     28#define PM_SUBTRACTION_ANALYSIS_FRES_OUTER_MEAN  "SUBTRACTION.FRES.OUTER.MEAN" // RMS stamp deviation
     29#define PM_SUBTRACTION_ANALYSIS_FRES_OUTER_STDEV "SUBTRACTION.FRES.OUTER.STDEV" // RMS stamp deviation
     30#define PM_SUBTRACTION_ANALYSIS_FRES_TOTAL_MEAN  "SUBTRACTION.FRES.TOTAL.MEAN" // RMS stamp deviation
     31#define PM_SUBTRACTION_ANALYSIS_FRES_TOTAL_STDEV "SUBTRACTION.FRES.TOTAL.STDEV" // RMS stamp deviation
    3232
    3333// Derive QA information about the subtraction
  • trunk/psModules/src/imcombine/pmSubtractionEquation.c

    r29004 r29543  
    1919
    2020//#define USE_WEIGHT                      // Include weight (1/variance) in equation?
    21 //#define USE_WINDOW                      // Include weight (1/variance) in equation?
    22 
     21
     22// XXX TEST:
     23//# define USE_WINDOW                      // window to avoid neighbor contamination
     24
     25# define PENALTY false
     26# define MOMENTS (!PENALTY)
    2327
    2428//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    2731
    2832// Calculate the least-squares matrix and vector
    29 static bool calculateMatrixVector(psImage *matrix, // Least-squares matrix, updated
    30                                   psVector *vector, // Least-squares vector, updated
    31                                   double *norm,     // Normalisation, updated
     33static bool calculateMatrixVector(psImage *matrix,      // Least-squares matrix, updated
     34                                  psVector *vector,      // Least-squares vector, updated
     35                                  double normValue,      // Normalisation, supplied
    3236                                  const psKernel *input, // Input image (target)
    3337                                  const psKernel *reference, // Reference image (convolution source)
     
    3741                                  const pmSubtractionKernels *kernels, // Kernels
    3842                                  const psImage *polyValues, // Spatial polynomial values
    39                                   int footprint, // (Half-)Size of stamp
    40                                   int normWindow1, // Window (half-)size for normalisation measurement
    41                                   int normWindow2, // Window (half-)size for normalisation measurement
    42                                   const pmSubtractionEquationCalculationMode mode
     43                                  int footprint // (Half-)Size of stamp
    4344                                  )
    4445{
     
    5051
    5152    int numKernels = kernels->num;                      // Number of kernels
    52     int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    53     int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    5453    int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
    5554    int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
    5655    double poly[numPoly];                                 // Polynomial terms
    5756    double poly2[numPoly][numPoly];                       // Polynomial-polynomial values
     57    int numParams = numKernels * numPoly;
     58
     59    psAssert(matrix &&
     60             matrix->type.type == PS_TYPE_F64 &&
     61             matrix->numCols == numParams &&
     62             matrix->numRows == numParams,
     63             "Least-squares matrix is bad.");
     64    psAssert(vector &&
     65             vector->type.type == PS_TYPE_F64 &&
     66             vector->n == numParams,
     67             "Least-squares vector is bad.");
    5868
    5969    // Evaluate polynomial-polynomial terms
    60     // XXX we can skip this if we are not calculating kernel coeffs
    6170    for (int iyOrder = 0, iIndex = 0; iyOrder <= spatialOrder; iyOrder++) {
    6271        for (int ixOrder = 0; ixOrder <= spatialOrder - iyOrder; ixOrder++, iIndex++) {
     
    8493    // [kernel 0, x^1 y^0][kernel 1 x^1 y^0]...[kernel N, x^1 y^0]
    8594    // [kernel 0, x^n y^m][kernel 1 x^n y^m]...[kernel N, x^n y^m]
    86     // normalization
    87     // bg 0, bg 1, bg 2 (only 0 is currently used?)
    8895
    8996    for (int i = 0; i < numKernels; i++) {
     
    107114
    108115            // Spatial variation of kernel coeffs
    109             if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    110                 for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    111                     for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    112                         double value = sumCC * poly2[iTerm][jTerm];
    113                         matrix->data.F64[iIndex][jIndex] = value;
    114                         matrix->data.F64[jIndex][iIndex] = value;
    115                     }
    116                 }
    117             }
     116            for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     117                for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     118                    double value = sumCC * poly2[iTerm][jTerm];
     119                    matrix->data.F64[iIndex][jIndex] = value;
     120                    matrix->data.F64[jIndex][iIndex] = value;
     121                }
     122            }
    118123        }
    119124
    120125        double sumRC = 0.0;             // Sum of the reference-convolution products
    121126        double sumIC = 0.0;             // Sum of the input-convolution products
    122         double sumC = 0.0;              // Sum of the convolution
    123127        for (int y = - footprint; y <= footprint; y++) {
    124128            for (int x = - footprint; x <= footprint; x++) {
     
    128132                double ic = in * conv;
    129133                double rc = ref * conv;
    130                 double c = conv;
    131134                if (weight) {
    132135                    float wtVal = weight->kernel[y][x];
    133136                    ic *= wtVal;
    134137                    rc *= wtVal;
    135                     c *= wtVal;
    136138                }
    137139                if (window) {
     
    139141                    ic *= winVal;
    140142                    rc *= winVal;
    141                     c  *= winVal;
    142143                }
    143144                sumIC += ic;
    144145                sumRC += rc;
    145                 sumC += c;
    146             }
    147         }
     146            }
     147        }
     148
    148149        // Spatial variation
    149150        for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    150             double normTerm = sumRC * poly[iTerm];
    151             double bgTerm = sumC * poly[iTerm];
    152             if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
    153                 matrix->data.F64[iIndex][normIndex] = normTerm;
    154                 matrix->data.F64[normIndex][iIndex] = normTerm;
    155             }
    156             if ((mode & PM_SUBTRACTION_EQUATION_BG) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
    157                 matrix->data.F64[iIndex][bgIndex] = bgTerm;
    158                 matrix->data.F64[bgIndex][iIndex] = bgTerm;
    159             }
    160             if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    161                 vector->data.F64[iIndex] = sumIC * poly[iTerm];
    162                 if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) {
    163                     // subtract norm * sumRC * poly[iTerm]
    164                     psAssert (kernels->solution1, "programming error: define solution first!");
    165                     int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    166                     double norm = fabs(kernels->solution1->data.F64[normIndex]);  // Normalisation
    167                     vector->data.F64[iIndex] -= norm * normTerm;
    168                 }
    169             }
    170         }
    171     }
    172 
    173     double sumRR = 0.0;                 // Sum of the reference product
    174     double sumIR = 0.0;                 // Sum of the input-reference product
    175     double sum1 = 0.0;                  // Sum of the background
    176     double sumR = 0.0;                  // Sum of the reference
    177     double sumI = 0.0;                  // Sum of the input
    178     double normI1 = 0.0, normI2 = 0.0;  // Sum of I_1 and I_2 within the normalisation window
    179     for (int y = - footprint; y <= footprint; y++) {
    180         for (int x = - footprint; x <= footprint; x++) {
    181             double in = input->kernel[y][x];
    182             double ref = reference->kernel[y][x];
    183             double ir = in * ref;
    184             double rr = PS_SQR(ref);
    185             double one = 1.0;
    186 
    187             if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow1)) {
    188                 normI1 += ref;
    189             }
    190             if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow2)) {
    191                 normI2 += in;
    192             }
    193 
    194             if (weight) {
    195                 float wtVal = weight->kernel[y][x];
    196                 rr *= wtVal;
    197                 ir *= wtVal;
    198                 in *= wtVal;
    199                 ref *= wtVal;
    200                 one *= wtVal;
    201             }
    202             if (window) {
    203                 float  winVal = window->kernel[y][x];
    204                 rr      *= winVal;
    205                 ir      *= winVal;
    206                 in      *= winVal;
    207                 ref *= winVal;
    208                 one *= winVal;
    209             }
    210             sumRR += rr;
    211             sumIR += ir;
    212             sumR += ref;
    213             sumI += in;
    214             sum1 += one;
    215         }
    216     }
    217 
    218     *norm = normI2 / normI1;
    219 
    220     fprintf (stderr, "normValue: %f %f %f\n", normI1, normI2, *norm);
    221 
    222     if (mode & PM_SUBTRACTION_EQUATION_NORM) {
    223         matrix->data.F64[normIndex][normIndex] = sumRR;
    224         vector->data.F64[normIndex] = sumIR;
    225         // subtract sum over kernels * kernel solution
    226     }
    227     if (mode & PM_SUBTRACTION_EQUATION_BG) {
    228         matrix->data.F64[bgIndex][bgIndex] = sum1;
    229         vector->data.F64[bgIndex] = sumI;
    230     }
    231     if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_BG)) {
    232         matrix->data.F64[normIndex][bgIndex] = sumR;
    233         matrix->data.F64[bgIndex][normIndex] = sumR;
     151            vector->data.F64[iIndex] = (sumIC - normValue*sumRC) * poly[iTerm];
     152        }
    234153    }
    235154
     
    255174
    256175// Calculate the least-squares matrix and vector for dual convolution
    257 static bool calculateDualMatrixVector(psImage *matrix, // Least-squares matrix, updated
    258                                       psVector *vector, // Least-squares vector, updated
    259                                       double *norm,     // Normalisation, updated
    260                                       const psKernel *image1, // Image 1
    261                                       const psKernel *image2, // Image 2
     176// XXX we could avoid calculating these values on successive passes *if* the stamp has not changed.
     177static bool calculateDualMatrixVector(pmSubtractionStamp *stamp,              // stamp of interest
     178                                      double normValue,       // Normalisation, updated
     179                                      double normValue2,      // Normalisation, updated
    262180                                      const psKernel *weight,  // Weight image
    263181                                      const psKernel *window,  // Window image
    264                                       const psArray *convolutions1, // Convolutions of image 1 for each kernel
    265                                       const psArray *convolutions2, // Convolutions of image 2 for each kernel
    266182                                      const pmSubtractionKernels *kernels, // Kernels
    267183                                      const psImage *polyValues, // Spatial polynomial values
    268                                       int footprint, // (Half-)Size of stamp
    269                                       int normWindow1, // Window (half-)size for normalisation measurement
    270                                       int normWindow2, // Window (half-)size for normalisation measurement
    271                                       const pmSubtractionEquationCalculationMode mode
     184                                      int footprint // (Half-)Size of stamp
    272185                                      )
    273186{
    274     int numKernels = kernels->num;                      // Number of kernels
    275     int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    276     int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    277     int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     187    int numKernels = kernels->num;                        // Number of kernels
     188    int spatialOrder = kernels->spatialOrder;             // Order of spatial variation
    278189    int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
    279190    double poly[numPoly];                                 // Polynomial terms
    280191    double poly2[numPoly][numPoly];                       // Polynomial-polynomial values
    281192
    282     int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
    283     int numParams = numKernels * numPoly + 1 + numBackground;       // Number of regular parameters
    284     int numParams2 = numKernels * numPoly;                          // Number of additional parameters for dual
    285     int numDual = numParams + numParams2;                           // Total number of parameters for dual
     193    int numParams = numKernels * numPoly;                 // Number of regular parameters
     194    int numParams2 = numKernels * numPoly;         // Number of additional parameters for dual
     195    int numDual = numParams + numParams2;          // Total number of parameters for dual
     196
     197    psImage *matrix = stamp->matrix;               // Least-squares matrix, updated
     198    psVector *vector = stamp->vector;              // Least-squares vector, updated
     199    psKernel *image1 = stamp->image1;              // Image 1
     200    psKernel *image2 = stamp->image2;              // Image 2
     201    psArray *convolutions1 = stamp->convolutions1; // Convolutions of image 1 for each kernel
     202    psArray *convolutions2 = stamp->convolutions2; // Convolutions of image 2 for each kernel
    286203
    287204    psAssert(matrix &&
     
    290207             matrix->numRows == numDual,
    291208             "Least-squares matrix is bad.");
     209
    292210    psAssert(vector &&
    293211             vector->type.type == PS_TYPE_F64 &&
     
    318236    }
    319237
     238    // the order of the elements in the matrix and vector is:
     239    // [kernel 0, x^0 y^0][kernel 1 x^0 y^0]...[kernel N, x^0 y^0]
     240    // [kernel 0, x^1 y^0][kernel 1 x^1 y^0]...[kernel N, x^1 y^0]
     241    // [kernel 0, x^n y^m][kernel 1 x^n y^m]...[kernel N, x^n y^m]
     242
     243    // for DUAL convolution analysis, we apply the normalization to I1 as follows:
     244    // norm = I2 / I1
     245    //
     246    // I1c = norm I1 + \Sum_i a_i norm I1 \cross k_i
     247    // I2c =      I2 + \Sum_i b_i      I2 \cross k_i
     248
     249    // we cannot absorb the normalization into a_i until the analysis is complete, or the
     250    // second moment terms are incorrectly calculated.
     251
    320252    for (int i = 0; i < numKernels; i++) {
    321253        psKernel *iConv1 = convolutions1->data[i]; // Convolution 1 for index i
     
    328260            double sumBB = 0.0;         // Sum of convolution products between image 2
    329261            double sumAB = 0.0;         // Sum of convolution products across images 1 and 2
     262
     263            double MxxAA = 0.0;
     264            double MyyAA = 0.0;
     265            double MxxBB = 0.0;
     266            double MyyBB = 0.0;
    330267            for (int y = - footprint; y <= footprint; y++) {
    331268                for (int x = - footprint; x <= footprint; x++) {
    332                     double aa = iConv1->kernel[y][x] * jConv1->kernel[y][x];
     269                    double aa = iConv1->kernel[y][x] * jConv1->kernel[y][x] * PS_SQR(normValue);
    333270                    double bb = iConv2->kernel[y][x] * jConv2->kernel[y][x];
    334                     double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x];
     271                    double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x] * normValue;
    335272                    if (weight) {
    336273                        float wtVal = weight->kernel[y][x];
     
    348285                    sumBB += bb;
    349286                    sumAB += ab;
    350                 }
    351             }
     287
     288                    if (MOMENTS) {
     289                        MxxAA += x*x*aa;
     290                        MyyAA += y*y*aa;
     291                        MxxBB += x*x*bb;
     292                        MyyBB += y*y*bb;
     293                    }
     294                }
     295            }
     296
     297            if (MOMENTS) {
     298                MxxAA /= stamp->normSquare1 * PS_SQR(normValue);
     299                MyyAA /= stamp->normSquare1 * PS_SQR(normValue);
     300                MxxBB /= stamp->normSquare2;
     301                MyyBB /= stamp->normSquare2;
     302            }
     303
     304            // XXX this makes the Chisq portion independent of the normalization and star flux
     305            // but may be mis-scaling between stars of different fluxes
     306            sumAA /= PS_SQR(stamp->normI2);
     307            sumAB /= PS_SQR(stamp->normI2);
     308            sumBB /= PS_SQR(stamp->normI2);
     309
     310            // fprintf (stderr, "i,j : %d %d : M(xx,yy)(AA,BB) : %f %f %f %f\n", i, j, MxxAA, MyyAA, MxxBB, MyyBB);
    352311
    353312            // Spatial variation of kernel coeffs
    354             if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    355                 for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    356                     for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    357                         double aa = sumAA * poly2[iTerm][jTerm];
    358                         double bb = sumBB * poly2[iTerm][jTerm];
    359                         double ab = sumAB * poly2[iTerm][jTerm];
    360 
    361                         matrix->data.F64[iIndex][jIndex] = aa;
    362                         matrix->data.F64[jIndex][iIndex] = aa;
    363 
    364                         matrix->data.F64[iIndex + numParams][jIndex + numParams] = bb;
    365                         matrix->data.F64[jIndex + numParams][iIndex + numParams] = bb;
    366 
    367                         matrix->data.F64[iIndex][jIndex + numParams] = ab;
    368                         matrix->data.F64[jIndex + numParams][iIndex] = ab;
    369                     }
    370                 }
    371             }
    372         }
     313            for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     314                for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     315                    double aa = sumAA * poly2[iTerm][jTerm];
     316                    double bb = sumBB * poly2[iTerm][jTerm];
     317                    double ab = sumAB * poly2[iTerm][jTerm];
     318
     319                    matrix->data.F64[iIndex][jIndex] = aa;
     320                    matrix->data.F64[jIndex][iIndex] = aa;
     321
     322                    matrix->data.F64[iIndex + numParams][jIndex + numParams] = bb;
     323                    matrix->data.F64[jIndex + numParams][iIndex + numParams] = bb;
     324
     325                    // add in second moments
     326                    if (MOMENTS) {
     327                        matrix->data.F64[iIndex][jIndex] += kernels->penalty * MxxAA;
     328                        matrix->data.F64[iIndex][jIndex] += kernels->penalty * MyyAA;
     329
     330                        matrix->data.F64[jIndex][iIndex] += kernels->penalty * MxxAA;
     331                        matrix->data.F64[jIndex][iIndex] += kernels->penalty * MyyAA;
     332
     333                        matrix->data.F64[iIndex + numParams][jIndex + numParams] += kernels->penalty * MxxBB;
     334                        matrix->data.F64[iIndex + numParams][jIndex + numParams] += kernels->penalty * MyyBB;
     335
     336                        matrix->data.F64[jIndex + numParams][iIndex + numParams] += kernels->penalty * MxxBB;
     337                        matrix->data.F64[jIndex + numParams][iIndex + numParams] += kernels->penalty * MyyBB;
     338
     339                        // XXX this uses the single moments, first try
     340                        // matrix->data.F64[iIndex][jIndex] += kernels->penalty * stamp->MxxI1->data.F32[i]*stamp->MxxI1->data.F32[j];
     341                        // matrix->data.F64[iIndex][jIndex] += kernels->penalty * stamp->MyyI1->data.F32[i]*stamp->MyyI1->data.F32[j];
     342                        //
     343                        // matrix->data.F64[jIndex][iIndex] += kernels->penalty * stamp->MxxI1->data.F32[i]*stamp->MxxI1->data.F32[j];
     344                        // matrix->data.F64[jIndex][iIndex] += kernels->penalty * stamp->MyyI1->data.F32[i]*stamp->MyyI1->data.F32[j];
     345                        //
     346                        // matrix->data.F64[iIndex + numParams][jIndex + numParams] += kernels->penalty * stamp->MxxI2->data.F32[i]*stamp->MxxI2->data.F32[j];
     347                        // matrix->data.F64[iIndex + numParams][jIndex + numParams] += kernels->penalty * stamp->MyyI2->data.F32[i]*stamp->MyyI2->data.F32[j];
     348                        //
     349                        // matrix->data.F64[jIndex + numParams][iIndex + numParams] += kernels->penalty * stamp->MxxI2->data.F32[i]*stamp->MxxI2->data.F32[j];
     350                        // matrix->data.F64[jIndex + numParams][iIndex + numParams] += kernels->penalty * stamp->MyyI2->data.F32[i]*stamp->MyyI2->data.F32[j];
     351                    }
     352                    matrix->data.F64[iIndex][jIndex + numParams] = ab;
     353                    matrix->data.F64[jIndex + numParams][iIndex] = ab;
     354                }
     355            }
     356        }
     357
     358        // we need to calculate the lower-diagonal AB elements since they are not symmetric for A <-> B
    373359        for (int j = 0; j < i; j++) {
    374360            psKernel *jConv2 = convolutions2->data[j]; // Convolution 2 for index j
     
    376362            for (int y = - footprint; y <= footprint; y++) {
    377363                for (int x = - footprint; x <= footprint; x++) {
    378                     double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x];
     364                    double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x] * normValue;
    379365                    if (weight) {
    380366                        ab *= weight->kernel[y][x];
     
    387373            }
    388374
     375            // XXX this makes the Chisq portion independent of the normalization and star flux
     376            // but may be mis-scaling between stars of different fluxes
     377            sumAB /= PS_SQR(stamp->normI2);
     378
    389379            // Spatial variation of kernel coeffs
    390             if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    391                 for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    392                     for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    393                         double ab = sumAB * poly2[iTerm][jTerm];
    394                         matrix->data.F64[iIndex][jIndex + numParams] = ab;
    395                         matrix->data.F64[jIndex + numParams][iIndex] = ab;
    396                     }
    397                 }
     380            for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     381                for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     382                    double ab = sumAB * poly2[iTerm][jTerm];
     383                    matrix->data.F64[iIndex][jIndex + numParams] = ab;
     384                    matrix->data.F64[jIndex + numParams][iIndex] = ab;
     385                }
    398386            }
    399387        }
     
    402390        double sumBI2 = 0.0;            // Sum of B.I_2 products (for vector)
    403391        double sumAI1 = 0.0;            // Sum of A.I_1 products (for matrix, normalisation)
    404         double sumA = 0.0;              // Sum of A (for matrix, background)
    405392        double sumBI1 = 0.0;            // Sum of B.I_1 products (for matrix, normalisation)
    406         double sumB = 0.0;              // Sum of B products (for matrix, background)
    407         double sumI2 = 0.0;             // Sum of I_2 (for vector, background)
     393
     394        double MxxAI1 = 0.0;
     395        double MyyAI1 = 0.0;
     396        double MxxBI2 = 0.0;
     397        double MyyBI2 = 0.0;
    408398        for (int y = - footprint; y <= footprint; y++) {
    409399            for (int x = - footprint; x <= footprint; x++) {
     
    413403                float i2 = image2->kernel[y][x];
    414404
    415                 double ai2 = a * i2;
     405                double ai2 = a * i2 * normValue;
    416406                double bi2 = b * i2;
    417                 double ai1 = a * i1;
    418                 double bi1 = b * i1;
     407                double ai1 = a * i1 * PS_SQR(normValue);
     408                double bi1 = b * i1 * normValue;
    419409
    420410                if (weight) {
     
    424414                    ai1 *= wtVal;
    425415                    bi1 *= wtVal;
    426                     a *= wtVal;
    427                     b *= wtVal;
    428                     i2 *= wtVal;
    429416                }
    430417                if (window) {
     
    434421                    ai1 *= wtVal;
    435422                    bi1 *= wtVal;
    436                     a *= wtVal;
    437                     b *= wtVal;
    438                     i2 *= wtVal;
    439423                }
    440424                sumAI2 += ai2;
    441425                sumBI2 += bi2;
    442426                sumAI1 += ai1;
    443                 sumA += a;
    444427                sumBI1 += bi1;
    445                 sumB += b;
    446                 sumI2 += i2;
    447             }
    448         }
     428
     429                if (MOMENTS) {
     430                    MxxAI1 += x*x*ai1;
     431                    MyyAI1 += y*y*ai1;
     432                    MxxBI2 += x*x*bi2;
     433                    MyyBI2 += y*y*bi2;
     434                }
     435            }
     436        }
     437
     438        if (MOMENTS) {
     439            MxxAI1 /= stamp->normSquare1 * PS_SQR(normValue);
     440            MyyAI1 /= stamp->normSquare1 * PS_SQR(normValue);
     441            MxxBI2 /= stamp->normSquare2;
     442            MyyBI2 /= stamp->normSquare2;
     443        }
     444
     445        // fprintf (stderr, "i : %d : M(xx,yy)(AI1,BI2) : %f %f %f %f\n", i, MxxAI1, MyyAI1, MxxBI2, MyyBI2);
     446
     447        // XXX this makes the Chisq portion independent of the normalization and star flux
     448        // but may be mis-scaling between stars of different fluxes
     449        sumAI1 /= PS_SQR(stamp->normI2);
     450        sumBI1 /= PS_SQR(stamp->normI2);
     451        sumAI2 /= PS_SQR(stamp->normI2);
     452        sumBI2 /= PS_SQR(stamp->normI2);
     453
    449454        // Spatial variation
    450455        for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     
    452457            double bi2 = sumBI2 * poly[iTerm];
    453458            double ai1 = sumAI1 * poly[iTerm];
    454             double a   = sumA * poly[iTerm];
    455459            double bi1 = sumBI1 * poly[iTerm];
    456             double b   = sumB * poly[iTerm];
    457 
    458             if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
    459                 matrix->data.F64[iIndex][normIndex] = ai1;
    460                 matrix->data.F64[normIndex][iIndex] = ai1;
    461                 matrix->data.F64[iIndex + numParams][normIndex] = bi1;
    462                 matrix->data.F64[normIndex][iIndex + numParams] = bi1;
    463             }
    464             if ((mode & PM_SUBTRACTION_EQUATION_BG) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
    465                 matrix->data.F64[iIndex][bgIndex] = a;
    466                 matrix->data.F64[bgIndex][iIndex] = a;
    467                 matrix->data.F64[iIndex + numParams][bgIndex] = b;
    468                 matrix->data.F64[bgIndex][iIndex + numParams] = b;
    469             }
    470             if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    471                 vector->data.F64[iIndex] = ai2;
    472                 vector->data.F64[iIndex + numParams] = bi2;
    473                 if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) {
    474                     // subtract norm * sumRC * poly[iTerm]
    475                     psAssert (kernels->solution1, "programming error: define solution first!");
    476                     int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    477                     double norm = fabs(kernels->solution1->data.F64[normIndex]);  // Normalisation
    478                     vector->data.F64[iIndex] -= norm * ai1;
    479                     vector->data.F64[iIndex + numParams] -= norm * bi1;
    480                 }
    481             }
    482         }
    483     }
    484 
    485     double sumI1 = 0.0;                 // Sum of I_1 (for matrix, background-normalisation)
    486     double sumI1I1 = 0.0;               // Sum of I_1^2 (for matrix, normalisation-normalisation)
    487     double sum1 = 0.0;                  // Sum of 1 (for matrix, background-background)
    488     double sumI2 = 0.0;                 // Sum of I_2 (for vector, background)
    489     double sumI1I2 = 0.0;               // Sum of I_1.I_2 (for vector, normalisation)
    490     double normI1 = 0.0, normI2 = 0.0;  // Sum of I_1 and I_2 within the normalisation window
    491     for (int y = - footprint; y <= footprint; y++) {
    492         for (int x = - footprint; x <= footprint; x++) {
    493             double i1 = image1->kernel[y][x];
    494             double i2 = image2->kernel[y][x];
    495 
    496             double i1i1 = i1 * i1;
    497             double one = 1.0;
    498             double i1i2 = i1 * i2;
    499 
    500             if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow1)) {
    501                 normI1 += i1;
    502             }
    503             if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow2)) {
    504                 normI2 += i2;
    505             }
    506 
    507             if (weight) {
    508                 float wtVal = weight->kernel[y][x];
    509                 i1 *= wtVal;
    510                 i1i1 *= wtVal;
    511                 one *= wtVal;
    512                 i2 *= wtVal;
    513                 i1i2 *= wtVal;
    514             }
    515             if (window) {
    516                 float wtVal = window->kernel[y][x];
    517                 i1 *= wtVal;
    518                 i1i1 *= wtVal;
    519                 one *= wtVal;
    520                 i2 *= wtVal;
    521                 i1i2 *= wtVal;
    522             }
    523             sumI1 += i1;
    524             sumI1I1 += i1i1;
    525             sum1 += one;
    526             sumI2 += i2;
    527             sumI1I2 += i1i2;
    528         }
    529     }
    530 
    531     *norm = normI2 / normI1;
    532     fprintf (stderr, "normValue: %f %f %f\n", normI1, normI2, *norm);
    533 
    534     if (mode & PM_SUBTRACTION_EQUATION_NORM) {
    535         matrix->data.F64[normIndex][normIndex] = sumI1I1;
    536         vector->data.F64[normIndex] = sumI1I2;
    537     }
    538     if (mode & PM_SUBTRACTION_EQUATION_BG) {
    539         matrix->data.F64[bgIndex][bgIndex] = sum1;
    540         vector->data.F64[bgIndex] = sumI2;
    541     }
    542     if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_BG)) {
    543         matrix->data.F64[bgIndex][normIndex] = sumI1;
    544         matrix->data.F64[normIndex][bgIndex] = sumI1;
     460            vector->data.F64[iIndex]             = ai2 - ai1;
     461            vector->data.F64[iIndex + numParams] = bi2 - bi1;
     462
     463            // fprintf (stderr, "i : %d : V(I1,I2) : %f %f\n", i, vector->data.F64[iIndex], vector->data.F64[iIndex + numParams]);
     464
     465            // add in second moments
     466            if (MOMENTS) {
     467                vector->data.F64[iIndex]             -= kernels->penalty * MxxAI1;
     468                vector->data.F64[iIndex]             -= kernels->penalty * MyyAI1;
     469
     470                vector->data.F64[iIndex + numParams] -= kernels->penalty * MxxBI2;
     471                vector->data.F64[iIndex + numParams] -= kernels->penalty * MyyBI2;
     472            }
     473        }
    545474    }
    546475
     
    565494}
    566495
    567 #if 1
    568496// Add in penalty term to least-squares vector
    569497bool calculatePenalty(psImage *matrix,                     // Matrix to which to add in penalty term
    570498                      psVector *vector,                    // Vector to which to add in penalty term
    571499                      const pmSubtractionKernels *kernels, // Kernel parameters
    572                       float norm                           // Normalisation
     500                      float normSquare1,                   // Normalisation for image 1
     501                      float normSquare2            // Normalisation for image 2
    573502  )
    574503{
     504    psAssert (kernels->mode == PM_SUBTRACTION_MODE_DUAL, "only use penalties for dual convolution");
     505
    575506    if (kernels->penalty == 0.0) {
    576507        return true;
     
    589520    // [p_0,x_1,y_0 p_1,x_1,y_0, p_2,x_1,y_0]
    590521    // [p_0,x_0,y_1 p_1,x_0,y_1, p_2,x_0,y_1]
    591     // [norm]
    592     // [bg]
     522
    593523    // [q_0,x_0,y_0 q_1,x_0,y_0, q_2,x_0,y_0]
    594524    // [q_0,x_1,y_0 q_1,x_1,y_0, q_2,x_1,y_0]
     
    600530                // Contribution to chi^2: a_i^2 P_i
    601531                psAssert(isfinite(penalties1->data.F32[i]), "Invalid penalty");
    602                 fprintf (stderr, "penalty: %f + %f (%f * %f)\n", matrix->data.F64[index][index], norm * penalties1->data.F32[i], norm, penalties1->data.F32[i]);
    603                 matrix->data.F64[index][index] += norm * penalties1->data.F32[i];
    604                 if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    605                     fprintf (stderr, "penalty: (x^%d y^%d fwhm %f) : %f + %f (%f * %f)\n", kernels->u->data.S32[index], kernels->v->data.S32[index], kernels->widths->data.F32[index],
    606                              matrix->data.F64[index + numParams + 2][index + numParams + 2], norm * penalties2->data.F32[i], norm, penalties2->data.F32[i]);
    607                     matrix->data.F64[index + numParams + 2][index + numParams + 2] += norm * penalties2->data.F32[i];                       
    608                     // matrix[i][i] is ~ (k_i * I_1)(k_i * I_1)
    609                     // penalties scale with second moments
    610                     //
    611                 }
    612             }
    613         }
    614     }
    615 
    616     return true;
    617 }
    618 # endif
     532                // fprintf (stderr, "penalty: %f + %f (%f * %f)\n", matrix->data.F64[index][index], normSquare1 * penalties1->data.F32[i], normSquare1, penalties1->data.F32[i]);
     533                matrix->data.F64[index][index] += normSquare1 * penalties1->data.F32[i];
     534
     535                // fprintf (stderr, "penalty: (x^%d y^%d fwhm %f) : %f + %f (%f * %f)\n", kernels->u->data.S32[index], kernels->v->data.S32[index], kernels->widths->data.F32[index],
     536                // matrix->data.F64[index + numParams][index + numParams], normSquare2 * penalties2->data.F32[i], normSquare2, penalties2->data.F32[i]);
     537                matrix->data.F64[index + numParams][index + numParams] += normSquare2 * penalties2->data.F32[i];                             
     538                // matrix[i][i] is ~ (k_i * I_1)(k_i * I_1)
     539                // penalties scale with second moments
     540                //
     541            }
     542        }
     543    }
     544
     545    return true;
     546}
    619547
    620548//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    647575                                    int index, bool wantDual)
    648576{
    649 #if 0
    650577    // This is probably in a tight loop, so don't check inputs
    651     PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NAN);
    652     PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NAN);
    653     PS_ASSERT_IMAGE_NON_NULL(polyValues, NAN);
    654     PS_ASSERT_INT_POSITIVE(index, NAN);
    655 #endif
    656 
    657578    psVector *solution = wantDual ? kernels->solution2 : kernels->solution1; // Solution vector
    658579    return p_pmSubtractionCalculatePolynomial(solution, polyValues, kernels->spatialOrder, index,
     
    662583double p_pmSubtractionSolutionNorm(const pmSubtractionKernels *kernels)
    663584{
    664 #if 0
    665585    // This is probably in a tight loop, so don't check inputs
    666     PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NAN);
    667     PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NAN);
    668     PS_ASSERT_IMAGE_NON_NULL(polyValues, NAN);
    669 #endif
    670 
    671586    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    672587    return kernels->solution1->data.F64[normIndex];
     
    676591                                                const psImage *polyValues)
    677592{
    678 #if 0
    679593    // This is probably in a tight loop, so don't check inputs
    680     PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NAN);
    681     PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NAN);
    682     PS_ASSERT_IMAGE_NON_NULL(polyValues, NAN);
    683 #endif
    684 
    685594    int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    686595    return p_pmSubtractionCalculatePolynomial(kernels->solution1, polyValues, kernels->bgOrder, bgIndex, 1);
     
    690599// Public functions
    691600//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     601
     602bool pmSubtractionCalculateMoments(
     603    pmSubtractionKernels *kernels, // Kernels
     604    pmSubtractionStampList *stamps)
     605{
     606    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     607
     608    // XXX skip this, right?
     609    return true;
     610
     611    // these are only used by DUAL mode
     612    if (kernels->mode != PM_SUBTRACTION_MODE_DUAL) return true;
     613
     614    psTimerStart("pmSubtractionCalculateMoments");
     615
     616    int footprint = stamps->footprint;  // Half-size of stamps
     617
     618    // Loop over each stamp and calculate its normalization factor
     619    for (int i = 0; i < stamps->num; i++) {
     620        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     621        if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) continue;
     622        if (stamp->status == PM_SUBTRACTION_STAMP_NONE) continue;
     623
     624        pmSubtractionCalculateMomentsStamp(kernels, stamp, footprint, stamps->normWindow2, stamps->normWindow1);
     625    }
     626
     627    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Calculate moments: %f sec", psTimerClear("pmSubtractionCalculateMoments"));
     628
     629    return true;
     630}
     631
     632bool pmSubtractionCalculateMomentsStamp(
     633    pmSubtractionKernels *kernels, // Kernels
     634    pmSubtractionStamp *stamp,          // stamp on which to save normalization)
     635    int footprint,                      // (Half-)Size of stamp
     636    int normWindow1,                    // Window (half-)size for normalisation measurement
     637    int normWindow2                     // Window (half-)size for normalisation measurement
     638    )
     639{
     640    double Mxx, Myy;
     641
     642    int numKernels = kernels->num;
     643
     644    // Generate convolutions: these are generated once and saved
     645    if (!pmSubtractionConvolveStamp(stamp, kernels, footprint)) {
     646        psError(psErrorCodeLast(), false, "Unable to convolve stamp");
     647        return false;
     648    }
     649
     650    if (!stamp->MxxI1) {
     651        stamp->MxxI1 = psVectorAlloc (numKernels, PS_TYPE_F32);
     652    }
     653    if (!stamp->MyyI1) {
     654        stamp->MyyI1 = psVectorAlloc (numKernels, PS_TYPE_F32);
     655    }
     656    if (!stamp->MxxI2) {
     657        stamp->MxxI2 = psVectorAlloc (numKernels, PS_TYPE_F32);
     658    }
     659    if (!stamp->MyyI2) {
     660        stamp->MyyI2 = psVectorAlloc (numKernels, PS_TYPE_F32);
     661    }
     662
     663    for (int i = 0; i < numKernels; i++) {
     664        pmSubtractionCalculateMomentsKernel(&Mxx, &Myy, stamp->convolutions1->data[i], footprint, normWindow1);
     665        stamp->MxxI1->data.F32[i] = Mxx / stamp->normI1;
     666        stamp->MyyI1->data.F32[i] = Myy / stamp->normI1;
     667        pmSubtractionCalculateMomentsKernel(&Mxx, &Myy, stamp->convolutions2->data[i], footprint, normWindow2);
     668        stamp->MxxI2->data.F32[i] = Mxx / stamp->normI2;
     669        stamp->MyyI2->data.F32[i] = Myy / stamp->normI2;
     670    }
     671
     672    pmSubtractionCalculateMomentsKernel(&Mxx, &Myy, stamp->image1, footprint, normWindow1);
     673    stamp->MxxI1raw = Mxx / stamp->normI1;
     674    stamp->MyyI1raw = Myy / stamp->normI1;
     675
     676    pmSubtractionCalculateMomentsKernel(&Mxx, &Myy, stamp->image2, footprint, normWindow2);
     677    stamp->MxxI2raw = Mxx / stamp->normI2;
     678    stamp->MyyI2raw = Myy / stamp->normI2;
     679
     680    // fprintf (stderr, "Mxx I1: %f, Myy I1: %f, Mxx I2: %f, Myy I2: %f\n", stamp->MxxI1raw, stamp->MyyI1raw, stamp->MxxI2raw, stamp->MyyI2raw);
     681
     682    return true;
     683}
     684
     685bool pmSubtractionCalculateMomentsKernel(double *Mxx, double *Myy, psKernel *image, int footprint, int window) {
     686
     687    double Sxx = 0.0;
     688    double Syy = 0.0;
     689    for (int y = - footprint; y <= footprint; y++) {
     690        for (int x = - footprint; x <= footprint; x++) {
     691            if (PS_SQR(x) + PS_SQR(y) > PS_SQR(window)) continue;
     692
     693            double flux = image->kernel[y][x];
     694
     695            Sxx += PS_SQR(x) * flux;
     696            Syy += PS_SQR(y) * flux;
     697        }
     698    }
     699    *Mxx = Sxx;
     700    *Myy = Syy;
     701    return true;
     702}
     703
     704///---------
     705
     706bool pmSubtractionCalculateNormalization(
     707    pmSubtractionStampList *stamps,
     708    const pmSubtractionMode mode)
     709{
     710    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     711
     712    psTimerStart("pmSubtractionCalculateNormalization");
     713
     714    psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
     715    psVector *norm2 = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
     716
     717    int footprint = stamps->footprint;  // Half-size of stamps
     718
     719    // Loop over each stamp and calculate its normalization factor
     720    for (int i = 0; i < stamps->num; i++) {
     721        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     722        if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) continue;
     723        if (stamp->status == PM_SUBTRACTION_STAMP_NONE) continue;
     724
     725        // XXX skip this if we have already calculated it? (stamp->norm does not change, just the median statistic)
     726        // XXX maybe not: the star may have changed for a given stamp -- only if norm is reset to NAN can we do this
     727        if (mode == PM_SUBTRACTION_MODE_2) {
     728            pmSubtractionCalculateNormalizationStamp(stamp, stamp->image2, stamp->image1, footprint, stamps->normWindow2, stamps->normWindow1);
     729        } else {
     730            pmSubtractionCalculateNormalizationStamp(stamp, stamp->image1, stamp->image2, footprint, stamps->normWindow1, stamps->normWindow2);
     731        }
     732        psVectorAppend(norms, stamp->norm);
     733        psVectorAppend(norm2, stamp->normSquare2 / stamp->normSquare1);
     734    }
     735
     736    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
     737    if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
     738        psError(PM_ERR_DATA, false, "Unable to determine median normalisation");
     739        psFree(stats);
     740        psFree(norms);
     741        psFree(norm2);
     742        return false;
     743    }
     744    stamps->normValue = stats->robustMedian;
     745
     746    psStatsInit(stats);
     747    if (!psVectorStats(stats, norm2, NULL, NULL, 0)) {
     748        psError(PM_ERR_DATA, false, "Unable to determine median normalisation");
     749        psFree(stats);
     750        psFree(norms);
     751        psFree(norm2);
     752        return false;
     753    }
     754    stamps->normValue2 = stats->robustMedian;
     755
     756    psLogMsg ("psModules.imcombine", PS_LOG_INFO, "norm (1): %f (%f)\n", stamps->normValue, stamps->normValue2);
     757
     758    psFree(stats);
     759    psFree(norms);
     760    psFree(norm2);
     761
     762    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Calculate normalization: %f sec", psTimerClear("pmSubtractionCalculateNormalization"));
     763
     764    return true;
     765}
     766
     767bool pmSubtractionCalculateNormalizationStamp(
     768    pmSubtractionStamp *stamp,          // stamp on which to save normalization)
     769    const psKernel *image1,             // Input image (target)
     770    const psKernel *image2,             // Reference image (convolution source)
     771    int footprint,                      // (Half-)Size of stamp
     772    int normWindow1,                    // Window (half-)size for normalisation measurement
     773    int normWindow2                     // Window (half-)size for normalisation measurement
     774    )
     775{
     776    double normI1 = 0.0;  // Sum of I_1 within the normalisation window (aperture)
     777    double normI2 = 0.0;  // Sum of I_2 within the normalisation window (aperture)
     778    double normSquare1 = 0.0;  // Sum of (I_1)^2 within the normalisation window (aperture)
     779    double normSquare2 = 0.0;  // Sum of (I_2)^2 within the normalisation window (aperture)
     780    for (int y = - footprint; y <= footprint; y++) {
     781        for (int x = - footprint; x <= footprint; x++) {
     782            double im1 = image1->kernel[y][x];
     783            double im2 = image2->kernel[y][x];
     784
     785            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow1)) {
     786                normI1 += im1;
     787                normSquare1 += PS_SQR(im1);
     788            }
     789            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow2)) {
     790                normI2 += im2;
     791                normSquare2 += PS_SQR(im2);
     792            }
     793        }
     794    }
     795    stamp->norm = normI2 / normI1;
     796    stamp->normI1 = normI1;
     797    stamp->normI2 = normI2;
     798    stamp->normSquare1 = normSquare1;
     799    stamp->normSquare2 = normSquare2;
     800
     801    psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "normValue: %f %f %f  (%f %f)\n", normI1, normI2, stamp->norm, normSquare1, normSquare2);
     802
     803    return true;
     804}
    692805
    693806bool pmSubtractionCalculateEquationThread(psThreadJob *job)
     
    715828    int numKernels = kernels->num;      // Number of kernel basis functions
    716829    int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial variations
    717     int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
    718830
    719831    // numKernels is the number of unique kernel images (one for each Gaussian modified by a specific polynomial).
     
    722834    // Total number of parameters to solve for: coefficient of each kernel basis function, multipled by the
    723835    // number of coefficients for the spatial polynomial, normalisation and a constant background offset.
    724     int numParams = numKernels * numSpatial + 1 + numBackground;
     836    // XXX we no longer solve for the normalization and background in the matrix inversion
     837    int numParams = numKernels * numSpatial;
    725838    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    726839        // An additional image is convolved
     
    790903    switch (kernels->mode) {
    791904      case PM_SUBTRACTION_MODE_1:
    792         status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image2, stamp->image1,
    793                                        weight, window, stamp->convolutions1, kernels,
    794                                        polyValues, footprint, stamps->normWindow1, stamps->normWindow2, mode);
     905        status = calculateMatrixVector(stamp->matrix, stamp->vector, stamps->normValue, stamp->image2, stamp->image1,
     906                                       weight, window, stamp->convolutions1, kernels, polyValues, footprint);
    795907        break;
    796908      case PM_SUBTRACTION_MODE_2:
    797         status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image1, stamp->image2,
    798                                        weight, window, stamp->convolutions2, kernels,
    799                                        polyValues, footprint, stamps->normWindow2, stamps->normWindow1, mode);
     909        status = calculateMatrixVector(stamp->matrix, stamp->vector, stamps->normValue, stamp->image1, stamp->image2,
     910                                       weight, window, stamp->convolutions2, kernels, polyValues, footprint);
    800911        break;
    801912      case PM_SUBTRACTION_MODE_DUAL:
    802         status = calculateDualMatrixVector(stamp->matrix, stamp->vector, &stamp->norm,
    803                                            stamp->image1, stamp->image2,
    804                                            weight, window, stamp->convolutions1, stamp->convolutions2,
    805                                            kernels, polyValues, footprint, stamps->normWindow1, stamps->normWindow2, mode);
     913        status = calculateDualMatrixVector(stamp, stamps->normValue, stamps->normValue2, weight, window, kernels, polyValues, footprint);
    806914        break;
    807915      default:
     
    9281036    int numKernels = kernels->num;      // Number of kernel basis functions
    9291037    int numSpatial = PM_SUBTRACTION_POLYTERMS(kernels->spatialOrder); // Number of spatial variations
    930     int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
    931     int numParams = numKernels * numSpatial + 1 + numBackground;    // Number of parameters being solved for
    932     int numSolution1 = numParams, numSolution2 = 0;                 // Number of parameters for each solution
     1038    // XXX int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
     1039    // XXX int numParams = numKernels * numSpatial + 1 + numBackground;    // Number of parameters being solved for
     1040    int numParams = numKernels * numSpatial;        // Number of parameters being solved for
     1041    int numSolution1 = numParams, numSolution2 = 0; // Number of parameters for each solution
    9331042    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    9341043        // An additional image is convolved
     
    9601069
    9611070    if (kernels->mode != PM_SUBTRACTION_MODE_DUAL) {
    962         // Accumulate the least-squares matricies and vectors
     1071        // Accumulate the least-squares matrices and vectors.  These are generated for the
     1072        // kernel elements, excluding the background and normalization.
    9631073        psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64); // Combined matrix
    9641074        psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64); // Combined vector
     
    9661076        psImageInit(sumMatrix, 0.0);
    9671077
    968         psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
    969 
    9701078        int numStamps = 0;              // Number of good stamps
     1079        for (int i = 0; i < stamps->num; i++) {
     1080            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     1081            if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
     1082               
     1083                (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
     1084                (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
     1085
     1086                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
     1087                numStamps++;
     1088            } else if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) {
     1089                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red");
     1090            }
     1091        }
     1092
     1093#if 0
     1094        psImage *save = psImageCopy(NULL, sumMatrix, PS_TYPE_F32);
     1095        psFitsWriteImageSimple ("sumMatrix.fits", save, NULL);
     1096        psVectorWriteFile("sumVector.dat", sumVector);
     1097        psFree (save);
     1098#endif
     1099
     1100        psVector *solution = NULL;                       // Solution to equation!
     1101        solution = psVectorAlloc(numParams, PS_TYPE_F64);
     1102        psVectorInit(solution, 0);
     1103
     1104        // XXX TEST: try some constraint on the svd solution
     1105        // solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1106        // SINGLE solution
     1107        if (1) {
     1108            solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1109        } else {
     1110            psVector *PERM = NULL;
     1111            psImage *LU = psMatrixLUDecomposition(NULL, &PERM, sumMatrix);
     1112            solution = psMatrixLUSolution(solution, LU, sumVector, PERM);
     1113            psFree (LU);
     1114            psFree (PERM);
     1115        }
     1116
     1117# if (0)
     1118        for (int i = 0; i < solution->n; i++) {
     1119            psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "Single solution %d: %lf\n", i, solution->data.F64[i]);
     1120        }
     1121# endif
     1122
     1123        if (!kernels->solution1) {
     1124            kernels->solution1 = psVectorAlloc(sumVector->n + 2, PS_TYPE_F64); // 1 for norm, 1 for bg
     1125            psVectorInit(kernels->solution1, 0.0);
     1126        }
     1127
     1128        int numKernels = kernels->num;
     1129        int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     1130        int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
     1131        for (int i = 0; i < numKernels * numPoly; i++) {
     1132            kernels->solution1->data.F64[i] = solution->data.F64[i];
     1133        }
     1134
     1135        // Apply the normalisation and background separately
     1136        int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1137        int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     1138        kernels->solution1->data.F64[normIndex] = stamps->normValue;
     1139        kernels->solution1->data.F64[bgIndex] = 0.0;
     1140
     1141        psFree(solution);
     1142        psFree(sumVector);
     1143        psFree(sumMatrix);
     1144
     1145    } else {
     1146        // Dual convolution solution
     1147        // Accumulate the least-squares matrices and vectors.  These are generated for the
     1148        // kernel elements, excluding the background and normalization.
     1149        psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
     1150        psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64);
     1151        psImageInit(sumMatrix, 0.0);
     1152        psVectorInit(sumVector, 0.0);
     1153
     1154        int numStamps = 0;         // Number of good stamps
     1155        double normSquare1 = 0.0; // Sum of (I_1)^2 over stamps
     1156        double normSquare2 = 0.0; // Sum of (I_2)^2 over stamps
    9711157        for (int i = 0; i < stamps->num; i++) {
    9721158            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     
    9741160                (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
    9751161                (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
    976                 psVectorAppend(norms, stamp->norm);
     1162
     1163                normSquare1 += stamp->normSquare1;
     1164                normSquare2 += stamp->normSquare2;
     1165
    9771166                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
    9781167                numStamps++;
     
    9831172
    9841173#if 0
    985         int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    986         calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]);
     1174        psImage *save = psImageCopy(NULL, sumMatrix, PS_TYPE_F32);
     1175        psFitsWriteImageSimple ("sumMatrix.fits", save, NULL);
     1176        psVectorWriteFile("sumVector.dat", sumVector);
     1177        psFree (save);
    9871178#endif
     1179
     1180        if (PENALTY) {
     1181            calculatePenalty(sumMatrix, sumVector, kernels, normSquare1, normSquare2);
     1182        }
    9881183
    9891184        psVector *solution = NULL;                       // Solution to equation!
     
    9911186        psVectorInit(solution, 0);
    9921187
    993 #if 0
    994         // Regular, straight-forward solution
    995         solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
    996 #else
    997         {
    998             // Solve normalisation and background separately
    999             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1000             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    1001 
    1002             psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
    1003             if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
    1004                 psError(PM_ERR_DATA, false, "Unable to determine median normalisation");
    1005                 psFree(stats);
    1006                 psFree(sumMatrix);
    1007                 psFree(sumVector);
    1008                 psFree(norms);
    1009                 return false;
    1010             }
    1011 
    1012             // double normValue = 1.0;
    1013             double normValue = stats->robustMedian;
    1014             // double bgValue = 0.0;
    1015 
    1016             psFree(stats);
    1017 
    1018 #ifdef TESTING
    1019             fprintf(stderr, "Norm: %lf\n", normValue);
    1020 #endif
    1021             // Solve kernel components
    1022             for (int i = 0; i < numSolution1; i++) {
    1023                 sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i];
    1024 
    1025                 sumMatrix->data.F64[i][normIndex] = 0.0;
    1026                 sumMatrix->data.F64[normIndex][i] = 0.0;
    1027             }
    1028             sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex];
    1029             sumMatrix->data.F64[bgIndex][normIndex] = 0.0;
    1030             sumMatrix->data.F64[normIndex][bgIndex] = 0.0;
    1031 
    1032             sumMatrix->data.F64[normIndex][normIndex] = 1.0;
    1033             sumVector->data.F64[normIndex] = 0.0;
    1034 
    1035             solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
    1036 
    1037             solution->data.F64[normIndex] = normValue;
    1038         }
    1039 # endif
    1040 
    1041 #if (1)
    1042         for (int i = 0; i < solution->n; i++) {
    1043             fprintf(stderr, "Single solution %d: %lf\n", i, solution->data.F64[i]);
    1044         }
    1045 #endif
    1046 
    1047         if (!kernels->solution1) {
    1048             kernels->solution1 = psVectorAlloc(sumVector->n, PS_TYPE_F64);
    1049             psVectorInit(kernels->solution1, 0.0);
    1050         }
    1051 
    1052         // only update the solutions that we chose to calculate:
    1053         if (mode & PM_SUBTRACTION_EQUATION_NORM) {
    1054             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1055             kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
    1056         }
    1057         if (mode & PM_SUBTRACTION_EQUATION_BG) {
    1058             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    1059             kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
    1060         }
    1061         if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    1062             int numKernels = kernels->num;
    1063             int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
    1064             int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
    1065             for (int i = 0; i < numKernels * numPoly; i++) {
    1066                 kernels->solution1->data.F64[i] = solution->data.F64[i];
    1067             }
    1068         }
    1069 
    1070         psFree(norms);
    1071         psFree(solution);
    1072         psFree(sumVector);
    1073         psFree(sumMatrix);
    1074 
    1075 #ifdef TESTING
    1076         // XXX double-check for NAN in data:
    1077         for (int ix = 0; ix < kernels->solution1->n; ix++) {
    1078             if (!isfinite(kernels->solution1->data.F64[ix])) {
    1079                 fprintf (stderr, "WARNING: NAN in vector\n");
    1080             }
    1081         }
    1082 #endif
    1083 
    1084     } else {
    1085         // Dual convolution solution
    1086 
    1087         // Accumulation of stamp matrices/vectors
    1088         psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
    1089         psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64);
    1090         psImageInit(sumMatrix, 0.0);
    1091         psVectorInit(sumVector, 0.0);
    1092 
    1093         psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
    1094 
    1095         int numStamps = 0;              // Number of good stamps
    1096         for (int i = 0; i < stamps->num; i++) {
    1097             pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    1098             if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
    1099                 (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
    1100                 (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
    1101 
    1102                 psVectorAppend(norms, stamp->norm);
    1103 
    1104                 pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
    1105                 numStamps++;
    1106             }
    1107         }
    1108 
    1109 #ifdef TESTING
    1110         psFitsWriteImageSimple ("sumMatrix.fits", sumMatrix, NULL);
    1111         psVectorWriteFile("sumVector.dat", sumVector);
    1112 #endif
    1113 
    1114 #if 1
    1115         // int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    1116         // calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]);
    1117 
    1118         int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1119         calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[normIndex][normIndex] / 100.0);
    1120 #endif
    1121 
    1122         psVector *solution = NULL;                       // Solution to equation!
    1123         solution = psVectorAlloc(numParams, PS_TYPE_F64);
    1124         psVectorInit(solution, 0);
    1125 
    1126 #if 0
    1127         // Regular, straight-forward solution
    1128         solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
    1129 #else
    1130         {
    1131             // Solve normalisation and background separately
    1132             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1133             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    1134 
    1135 #if 0
    1136             psImage *normMatrix = psImageAlloc(2, 2, PS_TYPE_F64);
    1137             psVector *normVector = psVectorAlloc(2, PS_TYPE_F64);
    1138 
    1139             normMatrix->data.F64[0][0] = sumMatrix->data.F64[normIndex][normIndex];
    1140             normMatrix->data.F64[1][1] = sumMatrix->data.F64[bgIndex][bgIndex];
    1141             normMatrix->data.F64[0][1] = normMatrix->data.F64[1][0] = sumMatrix->data.F64[normIndex][bgIndex];
    1142 
    1143             normVector->data.F64[0] = sumVector->data.F64[normIndex];
    1144             normVector->data.F64[1] = sumVector->data.F64[bgIndex];
    1145 
    1146             psVector *normSolution = psMatrixSolveSVD(NULL, normMatrix, normVector, NAN);
    1147 
    1148             double normValue = normSolution->data.F64[0];
    1149             double bgValue = normSolution->data.F64[1];
    1150 
    1151             psFree(normMatrix);
    1152             psFree(normVector);
    1153             psFree(normSolution);
    1154 #endif
    1155 
    1156             psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
    1157             if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
    1158                 psError(PM_ERR_DATA, false, "Unable to determine median normalisation");
    1159                 psFree(stats);
    1160                 psFree(sumMatrix);
    1161                 psFree(sumVector);
    1162                 psFree(norms);
    1163                 return false;
    1164             }
    1165 
    1166             double normValue = stats->robustMedian;
    1167 
    1168             psFree(stats);
    1169 
    1170 #ifdef TESTING
    1171             fprintf(stderr, "Norm: %lf\n", normValue);
    1172 #endif
    1173 
    1174             // Solve kernel components
    1175             for (int i = 0; i < numSolution2; i++) {
    1176                 sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i];
    1177                 sumVector->data.F64[i + numSolution1] -= normValue * sumMatrix->data.F64[normIndex][i + numSolution1];
    1178 
    1179                 sumMatrix->data.F64[i][normIndex] = 0.0;
    1180                 sumMatrix->data.F64[normIndex][i] = 0.0;
    1181 
    1182                 sumMatrix->data.F64[i + numSolution1][normIndex] = 0.0;
    1183                 sumMatrix->data.F64[normIndex][i + numSolution1] = 0.0;
    1184             }
    1185             sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex];
    1186             sumMatrix->data.F64[bgIndex][normIndex] = 0.0;
    1187             sumMatrix->data.F64[normIndex][bgIndex] = 0.0;
    1188 
    1189             sumMatrix->data.F64[normIndex][normIndex] = 1.0;
    1190 
    1191             sumVector->data.F64[normIndex] = 0.0;
    1192 
    1193             solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
    1194 
    1195             solution->data.F64[normIndex] = normValue;
    1196         }
    1197 #endif
    1198 
    1199 
    1200 #if (1)
     1188        // DUAL solution
     1189        solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1190
     1191#if (0)
    12011192        for (int i = 0; i < solution->n; i++) {
    12021193            fprintf(stderr, "Dual solution %d: %lf\n", i, solution->data.F64[i]);
     
    12041195#endif
    12051196
    1206         psFree(sumMatrix);
    1207         psFree(sumVector);
    1208 
    1209         psFree(norms);
    1210 
    12111197        if (!kernels->solution1) {
    1212             kernels->solution1 = psVectorAlloc(numSolution1, PS_TYPE_F64);
     1198            kernels->solution1 = psVectorAlloc(numSolution1 + 2, PS_TYPE_F64);
    12131199            psVectorInit (kernels->solution1, 0.0);
    12141200        }
     
    12181204        }
    12191205
    1220         // only update the solutions that we chose to calculate:
    1221         if (mode & PM_SUBTRACTION_EQUATION_NORM) {
    1222             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1223             kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
    1224         }
    1225         if (mode & PM_SUBTRACTION_EQUATION_BG) {
    1226             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    1227             kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
    1228         }
    1229         if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    1230             int numKernels = kernels->num;
    1231             for (int i = 0; i < numKernels * numSpatial; i++) {
    1232                 // XXX fprintf (stderr, "keep\n");
    1233                 kernels->solution1->data.F64[i] = solution->data.F64[i];
    1234                 kernels->solution2->data.F64[i] = solution->data.F64[i + numSolution1];
    1235             }
    1236         }
    1237 
    1238 
    1239         memcpy(kernels->solution1->data.F64, solution->data.F64,
    1240                numSolution1 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    1241         memcpy(kernels->solution2->data.F64, &solution->data.F64[numSolution1],
    1242                numSolution2 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    1243 
     1206        // for DUAL convolution analysis, we apply the normalization to I1 as follows:
     1207        // I1c = norm I1 + \Sum_i a_i norm I1 \cross k_i
     1208        // I2c =      I2 + \Sum_i b_i      I2 \cross k_i
     1209
     1210        // We absorb the normalization into a_i after the analysis is complete to be consistent
     1211        // with the SINGLE definitions of the convolutions
     1212
     1213        int numKernels = kernels->num;
     1214        for (int i = 0; i < numKernels * numSpatial; i++) {
     1215            // we solve for coefficients
     1216            kernels->solution1->data.F64[i] = solution->data.F64[i] * stamps->normValue;
     1217            kernels->solution2->data.F64[i] = solution->data.F64[i + numSolution1];
     1218        }
     1219
     1220        // Apply the normalisation and background separately
     1221        int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1222        int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     1223        kernels->solution1->data.F64[normIndex] = stamps->normValue;
     1224        kernels->solution1->data.F64[bgIndex] = 0.0;
     1225
     1226        psFree(sumMatrix);
     1227        psFree(sumVector);
    12441228        psFree(solution);
    1245 
    12461229    }
    12471230
     
    12651248}
    12661249
    1267 bool pmSubtractionResidualStats(psVector *fSigRes, psVector *fMaxRes, psVector *fMinRes, psKernel *target, psKernel *source, psKernel *residual, double norm, int footprint) {
    1268 
    1269     // XXX measure some useful stats on the residuals
     1250// measure some useful stats on the stamp residuals:
     1251// fResSigma : the residual stdev / total flux
     1252// fResOuter : the residual fabs / total flux for R > 2 pix
     1253// fResTotal : the residual fabs / total flux for R > 0 pix
     1254bool pmSubtractionResidualStats(psVector *fResSigma, psVector *fResOuter, psVector *fResTotal, psKernel *target, psKernel *source, psKernel *residual, double norm, int footprint) {
     1255
    12701256    float sum = 0.0;
    12711257    float peak = 0.0;
     
    12771263    }
    12781264
    1279     // only count pixels with more than X% of the source flux
    1280     // calculate stdev(dflux)
     1265    // init counters
     1266    int npix = 0;
    12811267    float dflux1 = 0.0;
    12821268    float dflux2 = 0.0;
    1283     int npix = 0;
    1284 
    1285     float dmax = 0.0;
    1286     float dmin = 0.0;
     1269    float dOuter = 0.0;
     1270    float dTotal = 0.0;
    12871271
    12881272    for (int y = - footprint; y <= footprint; y++) {
    12891273        for (int x = - footprint; x <= footprint; x++) {
    1290             float dflux = 0.5*(target->kernel[y][x] + source->kernel[y][x] * norm);
    1291             if (dflux < 0.02*sum) continue;
    12921274            dflux1 += residual->kernel[y][x];
    12931275            dflux2 += PS_SQR(residual->kernel[y][x]);
    1294             dmax = PS_MAX(residual->kernel[y][x], dmax);
    1295             dmin = PS_MIN(residual->kernel[y][x], dmin);
     1276            dTotal += fabs(residual->kernel[y][x]);
     1277            if (hypot(x,y) > 2.0) {
     1278              dOuter += fabs(residual->kernel[y][x]);
     1279            }
    12961280            npix ++;
    12971281        }
     
    12991283    float sigma = sqrt(dflux2 / npix - PS_SQR(dflux1/npix));
    13001284    if (!isfinite(sum))  return false;
    1301     if (!isfinite(dmax)) return false;
    1302     if (!isfinite(dmin)) return false;
    13031285    if (!isfinite(peak)) return false;
    1304 
    1305     // fprintf (stderr, "sum: %f, peak: %f, sigma: %f, fsigma: %f, fmax: %f, fmin: %f\n", sum, peak, sigma, sigma/sum, dmax/peak, dmin/peak);
    1306     psVectorAppend(fSigRes, sigma/sum);
    1307     psVectorAppend(fMaxRes, dmax/peak);
    1308     psVectorAppend(fMinRes, dmin/peak);
     1286    if (!isfinite(dOuter)) return false;
     1287    if (!isfinite(dTotal)) return false;
     1288
     1289    // fprintf (stderr, "sum: %f, peak: %f, sigma: %f, fsigma: %f, fmax: %f, fmin: %f\n", sum, peak, sigma, sigma/sum, dOuter/sum, dTotal/sum);
     1290    psVectorAppend(fResSigma, sigma/sum);
     1291    psVectorAppend(fResOuter, dOuter/sum);
     1292    psVectorAppend(fResTotal, dTotal/sum);
    13091293    return true;
    13101294}
     
    13291313    pmSubtractionVisualShowFitInit (stamps);
    13301314
    1331     psVector *fSigRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
    1332     psVector *fMinRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
    1333     psVector *fMaxRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1315    psVector *fResSigma = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1316    psVector *fResOuter = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1317    psVector *fResTotal = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
    13341318
    13351319    // we want to save the residual images for the 9 brightest stamps.
     
    14431427            }
    14441428
    1445             pmSubtractionResidualStats(fSigRes, fMaxRes, fMinRes, target, source, residual, norm, footprint);
     1429            pmSubtractionResidualStats(fResSigma, fResOuter, fResTotal, target, source, residual, norm, footprint);
    14461430
    14471431        } else {
     
    14791463            }
    14801464
    1481             pmSubtractionResidualStats(fSigRes, fMaxRes, fMinRes, image1, image2, residual, norm, footprint);
     1465            pmSubtractionResidualStats(fResSigma, fResOuter, fResTotal, image1, image2, residual, norm, footprint);
    14821466        }
    14831467
     
    15581542
    15591543        psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
    1560         psVectorStats (stats, fSigRes, NULL, NULL, 0);
    1561         kernels->fSigResMean = stats->robustMedian;
    1562         kernels->fSigResStdev = stats->robustStdev;
     1544        psVectorStats (stats, fResSigma, NULL, NULL, 0);
     1545        kernels->fResSigmaMean = stats->robustMedian;
     1546        kernels->fResSigmaStdev = stats->robustStdev;
    15631547
    15641548        psStatsInit (stats);
    1565         psVectorStats (stats, fMaxRes, NULL, NULL, 0);
    1566         kernels->fMaxResMean = stats->robustMedian;
    1567         kernels->fMaxResStdev = stats->robustStdev;
     1549        psVectorStats (stats, fResOuter, NULL, NULL, 0);
     1550        kernels->fResOuterMean = stats->robustMedian;
     1551        kernels->fResOuterStdev = stats->robustStdev;
    15681552
    15691553        psStatsInit (stats);
    1570         psVectorStats (stats, fMinRes, NULL, NULL, 0);
    1571         kernels->fMinResMean = stats->robustMedian;
    1572         kernels->fMinResStdev = stats->robustStdev;
     1554        psVectorStats (stats, fResTotal, NULL, NULL, 0);
     1555        kernels->fResTotalMean = stats->robustMedian;
     1556        kernels->fResTotalStdev = stats->robustStdev;
    15731557
    15741558        // XXX save these values somewhere
    1575         psLogMsg("psModules.imcombine", PS_LOG_INFO, "fSigma: %f +/- %f, fMaxRes: %f +/- %f, fMinRes: %f +/- %f",
    1576                  kernels->fSigResMean, kernels->fSigResStdev,
    1577                  kernels->fMaxResMean, kernels->fMaxResStdev,
    1578                  kernels->fMinResMean, kernels->fMinResStdev);
    1579 
    1580         psFree (fSigRes);
    1581         psFree (fMaxRes);
    1582         psFree (fMinRes);
     1559        psLogMsg("psModules.imcombine", PS_LOG_INFO, "fResSigma: %f +/- %f, fResOuter: %f +/- %f, fResTotal: %f +/- %f",
     1560                 kernels->fResSigmaMean, kernels->fResSigmaStdev,
     1561                 kernels->fResOuterMean, kernels->fResOuterStdev,
     1562                 kernels->fResTotalMean, kernels->fResTotalStdev);
     1563
     1564        psFree (fResSigma);
     1565        psFree (fResOuter);
     1566        psFree (fResTotal);
    15831567        psFree (stats);
    15841568    }
     
    15861570    psFree(residual);
    15871571    psFree(polyValues);
    1588 
    15891572
    15901573    return deviations;
     
    19121895    return true;
    19131896}
    1914 
    1915 
    1916 # if 0
    1917 
    1918 #ifdef TESTING
    1919         psFitsWriteImageSimple("A.fits", sumMatrix, NULL);
    1920         psVectorWriteFile ("B.dat", sumVector);
    1921 #endif
    1922 
    1923 # define SVD_ANALYSIS 0
    1924 # define COEFF_SIG 0.0
    1925 # define SVD_TOL 0.0
    1926 
    1927         // Use SVD to determine the kernel coeffs (and validate)
    1928         if (SVD_ANALYSIS) {
    1929 
    1930             // We have sumVector and sumMatrix.  we are trying to solve the following equation:
    1931             // sumMatrix * x = sumVector.
    1932 
    1933             // we can use any standard matrix inversion to solve this.  However, the basis
    1934             // functions in general have substantial correlation, so that the solution may be
    1935             // somewhat poorly determined or unstable.  If not numerically ill-conditioned, the
    1936             // system of equations may be statistically ill-conditioned.  Noise in the image
    1937             // will drive insignificant, but correlated, terms in the solution.  To avoid these
    1938             // problems, we can use SVD to identify numerically unconstrained values and to
    1939             // avoid statistically badly determined value.
    1940 
    1941             // A = sumMatrix, B = sumVector
    1942             // SVD: A = U w V^T  -> A^{-1} = V (1/w) U^T
    1943             // x = V (1/w) (U^T B)
    1944             // \sigma_x = sqrt(diag(A^{-1}))
    1945             // solve for x and A^{-1} to get x & dx
    1946             // identify the elements of (1/w) that are nan (1/0.0) -> set to 0.0
    1947             // identify the elements of x that are insignificant (x / dx < 1.0? < 0.5?) -> set to 0.0
    1948 
    1949             // If I use the SVD trick to re-condition the matrix, I need to break out the
    1950             // kernel and normalization terms from the background term.
    1951             // XXX is this true?  or was this due to an error in the analysis?
    1952 
    1953             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    1954 
    1955             // now pull out the kernel elements into their own square matrix
    1956             psImage  *kernelMatrix = psImageAlloc  (sumMatrix->numCols - 1, sumMatrix->numRows - 1, PS_TYPE_F64);
    1957             psVector *kernelVector = psVectorAlloc (sumMatrix->numCols - 1, PS_TYPE_F64);
    1958 
    1959             for (int ix = 0, kx = 0; ix < sumMatrix->numCols; ix++) {
    1960                 if (ix == bgIndex) continue;
    1961                 for (int iy = 0, ky = 0; iy < sumMatrix->numRows; iy++) {
    1962                     if (iy == bgIndex) continue;
    1963                     kernelMatrix->data.F64[ky][kx] = sumMatrix->data.F64[iy][ix];
    1964                     ky++;
    1965                 }
    1966                 kernelVector->data.F64[kx] = sumVector->data.F64[ix];
    1967                 kx++;
    1968             }
    1969 
    1970             psImage *U = NULL;
    1971             psImage *V = NULL;
    1972             psVector *w = NULL;
    1973             if (!psMatrixSVD (&U, &w, &V, kernelMatrix)) {
    1974                 psError(psErrorCodeLast(), false, "failed to perform SVD on sumMatrix\n");
    1975                 return NULL;
    1976             }
    1977 
    1978             // calculate A_inverse:
    1979             // Ainv = V * w * U^T
    1980             psImage *wUt  = p_pmSubSolve_wUt (w, U);
    1981             psImage *Ainv = p_pmSubSolve_VwUt (V, wUt);
    1982             psImage *Xvar = NULL;
    1983             psFree (wUt);
    1984 
    1985 # ifdef TESTING
    1986             // kernel terms:
    1987             for (int i = 0; i < w->n; i++) {
    1988                 fprintf (stderr, "w: %f\n", w->data.F64[i]);
    1989             }
    1990 # endif
    1991             // loop over w adding in more and more of the values until chisquare is no longer
    1992             // dropping significantly.
    1993             // XXX this does not seem to work very well: we seem to need all terms even for
    1994             // simple cases...
    1995 
    1996             psVector *Xsvd = NULL;
    1997             {
    1998                 psVector *Ax = NULL;
    1999                 psVector *UtB = NULL;
    2000                 psVector *wUtB = NULL;
    2001 
    2002                 psVector *wApply = psVectorAlloc(w->n, PS_TYPE_F64);
    2003                 psVector *wMask = psVectorAlloc(w->n, PS_TYPE_U8);
    2004                 psVectorInit (wMask, 1); // start by masking everything
    2005 
    2006                 double chiSquareLast = NAN;
    2007                 int maxWeight = 0;
    2008 
    2009                 double Axx, Bx, y2;
    2010 
    2011                 // XXX this is an attempt to exclude insignificant modes.
    2012                 // it was not successful with the ISIS kernel set: removing even
    2013                 // the least significant mode leaves additional ringing / noise
    2014                 // because the terms are so coupled.
    2015                 for (int k = 0; false && (k < w->n); k++) {
    2016 
    2017                     // unmask the k-th weight
    2018                     wMask->data.U8[k] = 0;
    2019                     p_pmSubSolve_SetWeights(wApply, w, wMask);
    2020 
    2021                     // solve for x:
    2022                     // x = V * w * (U^T * B)
    2023                     p_pmSubSolve_UtB (&UtB, U, kernelVector);
    2024                     p_pmSubSolve_wUtB (&wUtB, wApply, UtB);
    2025                     p_pmSubSolve_VwUtB (&Xsvd, V, wUtB);
    2026 
    2027                     // chi-square for this system of equations:
    2028                     // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2
    2029                     // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2
    2030                     p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd);
    2031                     p_pmSubSolve_VdV (&Axx, Ax, Xsvd);
    2032                     p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd);
    2033                     p_pmSubSolve_y2 (&y2, kernels, stamps);
    2034 
    2035                     // apparently, this works (compare with the brute force value below
    2036                     double chiSquare = Axx - 2.0*Bx + y2;
    2037                     double deltaChi = (k == 0) ? chiSquare : chiSquareLast - chiSquare;
    2038                     chiSquareLast = chiSquare;
    2039 
    2040                     // fprintf (stderr, "chi square = %f, delta: %f\n", chiSquare, deltaChi);
    2041                     if (k && !maxWeight && (deltaChi < 1.0)) {
    2042                         maxWeight = k;
    2043                     }
    2044                 }
    2045 
    2046                 // keep all terms or we get extra ringing
    2047                 maxWeight = w->n;
    2048                 psVectorInit (wMask, 1);
    2049                 for (int k = 0; k < maxWeight; k++) {
    2050                     wMask->data.U8[k] = 0;
    2051                 }
    2052                 p_pmSubSolve_SetWeights(wApply, w, wMask);
    2053 
    2054                 // solve for x:
    2055                 // x = V * w * (U^T * B)
    2056                 p_pmSubSolve_UtB (&UtB, U, kernelVector);
    2057                 p_pmSubSolve_wUtB (&wUtB, wApply, UtB);
    2058                 p_pmSubSolve_VwUtB (&Xsvd, V, wUtB);
    2059 
    2060                 // chi-square for this system of equations:
    2061                 // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2
    2062                 // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2
    2063                 p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd);
    2064                 p_pmSubSolve_VdV (&Axx, Ax, Xsvd);
    2065                 p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd);
    2066                 p_pmSubSolve_y2 (&y2, kernels, stamps);
    2067 
    2068                 // apparently, this works (compare with the brute force value below
    2069                 double chiSquare = Axx - 2.0*Bx + y2;
    2070                 psLogMsg ("psModules.imcombine", PS_LOG_INFO, "model kernel with %d terms; chi square = %f\n", maxWeight, chiSquare);
    2071 
    2072                 // re-calculate A^{-1} to get new variances:
    2073                 // Ainv = V * w * U^T
    2074                 // XXX since we keep all terms, this is identical to Ainv
    2075                 psImage *wUt  = p_pmSubSolve_wUt (wApply, U);
    2076                 Xvar = p_pmSubSolve_VwUt (V, wUt);
    2077                 psFree (wUt);
    2078 
    2079                 psFree (Ax);
    2080                 psFree (UtB);
    2081                 psFree (wUtB);
    2082                 psFree (wApply);
    2083                 psFree (wMask);
    2084             }
    2085 
    2086             // copy the kernel solutions to the full solution vector:
    2087             solution = psVectorAlloc(sumVector->n, PS_TYPE_F64);
    2088             solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64);
    2089 
    2090             for (int ix = 0, kx = 0; ix < sumVector->n; ix++) {
    2091                 if (ix == bgIndex) {
    2092                     solution->data.F64[ix] = 0;
    2093                     solutionErr->data.F64[ix] = 0.001;
    2094                     continue;
    2095                 }
    2096                 solutionErr->data.F64[ix] = sqrt(Ainv->data.F64[kx][kx]);
    2097                 solution->data.F64[ix] = Xsvd->data.F64[kx];
    2098                 kx++;
    2099             }
    2100 
    2101             psFree (kernelMatrix);
    2102             psFree (kernelVector);
    2103 
    2104             psFree (U);
    2105             psFree (V);
    2106             psFree (w);
    2107 
    2108             psFree (Ainv);
    2109             psFree (Xsvd);
    2110         } else {
    2111             psVector *permutation = NULL;       // Permutation vector, required for LU decomposition
    2112             psImage *luMatrix = psMatrixLUDecomposition(NULL, &permutation, sumMatrix);
    2113             if (!luMatrix) {
    2114                 psError(PM_ERR_DATA, true, "LU Decomposition of least-squares matrix failed.\n");
    2115                 psFree(solution);
    2116                 psFree(sumVector);
    2117                 psFree(sumMatrix);
    2118                 psFree(luMatrix);
    2119                 psFree(permutation);
    2120                 return NULL;
    2121             }
    2122 
    2123             solution = psMatrixLUSolution(NULL, luMatrix, sumVector, permutation);
    2124             psFree(luMatrix);
    2125             psFree(permutation);
    2126             if (!solution) {
    2127                 psError(PM_ERR_DATA, true, "Failed to solve the least-squares system.\n");
    2128                 psFree(solution);
    2129                 psFree(sumVector);
    2130                 psFree(sumMatrix);
    2131                 return NULL;
    2132             }
    2133 
    2134             // XXX LUD does not provide A^{-1}?  fake the error for now
    2135             solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64);
    2136             for (int ix = 0; ix < sumVector->n; ix++) {
    2137                 solutionErr->data.F64[ix] = 0.1*solution->data.F64[ix];
    2138             }
    2139         }
    2140 
    2141         if (!kernels->solution1) {
    2142             kernels->solution1 = psVectorAlloc (sumVector->n, PS_TYPE_F64);
    2143             psVectorInit (kernels->solution1, 0.0);
    2144         }
    2145 
    2146         // only update the solutions that we chose to calculate:
    2147         if (mode & PM_SUBTRACTION_EQUATION_NORM) {
    2148             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    2149             kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
    2150         }
    2151         if (mode & PM_SUBTRACTION_EQUATION_BG) {
    2152             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    2153             kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
    2154         }
    2155         if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    2156             int numKernels = kernels->num;
    2157             int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
    2158             int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
    2159             for (int i = 0; i < numKernels * numPoly; i++) {
    2160                 // XXX fprintf (stderr, "%f +/- %f (%f) -> ", solution->data.F64[i], solutionErr->data.F64[i], fabs(solution->data.F64[i]/solutionErr->data.F64[i]));
    2161                 if (fabs(solution->data.F64[i] / solutionErr->data.F64[i]) < COEFF_SIG) {
    2162                     // XXX fprintf (stderr, "drop\n");
    2163                     kernels->solution1->data.F64[i] = 0.0;
    2164                 } else {
    2165                     // XXX fprintf (stderr, "keep\n");
    2166                     kernels->solution1->data.F64[i] = solution->data.F64[i];
    2167                 }
    2168             }
    2169         }
    2170         // double chiSquare = p_pmSubSolve_ChiSquare (kernels, stamps);
    2171         // fprintf (stderr, "chi square Brute = %f\n", chiSquare);
    2172 
    2173         psFree(solution);
    2174         psFree(sumVector);
    2175         psFree(sumMatrix);
    2176 # endif
    2177 
    2178 #ifdef TESTING
    2179               // XXX double-check for NAN in data:
    2180                 for (int iy = 0; iy < stamp->matrix->numRows; iy++) {
    2181                     for (int ix = 0; ix < stamp->matrix->numCols; ix++) {
    2182                         if (!isfinite(stamp->matrix->data.F64[iy][ix])) {
    2183                             fprintf (stderr, "WARNING: NAN in matrix\n");
    2184                         }
    2185                     }
    2186                 }
    2187                 for (int ix = 0; ix < stamp->vector->n; ix++) {
    2188                     if (!isfinite(stamp->vector->data.F64[ix])) {
    2189                         fprintf (stderr, "WARNING: NAN in vector\n");
    2190                     }
    2191                 }
    2192 #endif
    2193 
    2194 #ifdef TESTING
    2195         for (int ix = 0; ix < sumVector->n; ix++) {
    2196             if (!isfinite(sumVector->data.F64[ix])) {
    2197                 fprintf (stderr, "WARNING: NAN in vector\n");
    2198             }
    2199         }
    2200 #endif
    2201 
    2202 #ifdef TESTING
    2203         for (int ix = 0; ix < sumVector->n; ix++) {
    2204             if (!isfinite(sumVector->data.F64[ix])) {
    2205                 fprintf (stderr, "WARNING: NAN in vector\n");
    2206             }
    2207         }
    2208         {
    2209             psImage *inverse = psMatrixInvert(NULL, sumMatrix, NULL);
    2210             psFitsWriteImageSimple("matrixInv.fits", inverse, NULL);
    2211             psFree(inverse);
    2212         }
    2213         {
    2214             psImage *X = psMatrixInvert(NULL, sumMatrix, NULL);
    2215             psImage *Xt = psMatrixTranspose(NULL, X);
    2216             psImage *XtX = psMatrixMultiply(NULL, Xt, X);
    2217             psFitsWriteImageSimple("matrixErr.fits", XtX, NULL);
    2218             psFree(X);
    2219             psFree(Xt);
    2220             psFree(XtX);
    2221         }
    2222 #endif
    2223 
  • trunk/psModules/src/imcombine/pmSubtractionEquation.h

    r29004 r29543  
    6565    );
    6666
     67bool pmSubtractionCalculateNormalization(
     68  pmSubtractionStampList *stamps,
     69  const pmSubtractionMode mode);
     70
     71bool pmSubtractionCalculateNormalizationStamp(
     72    pmSubtractionStamp *stamp,          // stamp on which to save normalization)
     73    const psKernel *input,              // Input image (target)
     74    const psKernel *reference,          // Reference image (convolution source)
     75    int footprint,                      // (Half-)Size of stamp
     76    int normWindow1,                    // Window (half-)size for normalisation measurement
     77    int normWindow2                     // Window (half-)size for normalisation measurement
     78  );
     79
     80bool pmSubtractionCalculateMoments(
     81    pmSubtractionKernels *kernels, // Kernels
     82    pmSubtractionStampList *stamps);
     83
     84bool pmSubtractionCalculateMomentsStamp(
     85    pmSubtractionKernels *kernels, // Kernels
     86    pmSubtractionStamp *stamp,          // stamp on which to save normalization)
     87    int footprint,                      // (Half-)Size of stamp
     88    int normWindow1,                    // Window (half-)size for normalisation measurement
     89    int normWindow2                     // Window (half-)size for normalisation measurement
     90    );
     91
     92bool pmSubtractionCalculateMomentsKernel(double *Mxx, double *Myy, psKernel *image, int footprint, int window);
     93
    6794#endif
  • trunk/psModules/src/imcombine/pmSubtractionKernels.c

    r29004 r29543  
    183183}
    184184
     185# define CENTRAL_DELTA 0
     186
     187# if (CENTRAL_DELTA)
     188
     189// XXX *** this code used the central pixel to force zero net flux,
     190// Alard actually uses kernel(0) for this purpose (for even order, kernel[i] = kernel'[i] - kernel[0])
    185191static bool pmSubtractionKernelPreCalcNormalize(pmSubtractionKernels *kernels, pmSubtractionKernelPreCalc *preCalc,
    186192                                                int index, int uOrder, int vOrder, float fwhm,
     
    220226            // Re-normalize
    221227            // scale2D  = 1.0 / fabs(sum);
    222             scale2D  = 1.0 / sqrt(sum2);
     228            scale2D  = 1.0 / sqrt(sum2) / PS_SQR(fwhm);
    223229            zeroNull = true;
    224230        } else {
    225231            // Odd functions: choose normalisation so that parameters have about the same strength as for even
    226232            // functions, no subtraction of null pixel because the sum is already (near) zero
    227             scale2D = 1.0 / sqrt(sum2);
     233            scale2D = 1.0 / sqrt(sum2) / PS_SQR(fwhm);
    228234            zeroNull = false;
    229235        }
     
    235241    if (forceZeroNull) {
    236242        // Force rescaling and subtraction of null pixel even though the order doesn't indicate it's even
    237         scale2D = 1.0 / fabs(sum);
     243        scale2D = 1.0 / fabs(sum) / PS_SQR(fwhm);
    238244        zeroNull = true;
    239245    }
    240246    if (!forceZeroNull && ((uOrder % 2) || (vOrder % 2))) {
    241247        // Odd function
    242         scale2D = 1.0 / sqrt(sum2);
     248        scale2D = 1.0 / sqrt(sum2) / PS_SQR(fwhm);
    243249    }
    244250
     
    255261    if (zeroNull) {
    256262        // preCalc->kernel->kernel[0][0] -= 1.0;
    257         preCalc->kernel->kernel[0][0] -= sum / sqrt (sum2);
    258     }
    259 
    260 #if 1
     263        preCalc->kernel->kernel[0][0] -= sum * scale2D;
     264    }
     265
     266#if 0
    261267    {
    262268        double Sum = 0.0;   // Sum of kernel component
     
    287293    return true;
    288294}
     295
     296# else /* CENTRAL_DELTA */
     297
     298static bool zeroIsNormal = false;
     299
     300// this code uses kernel(0) to force zero flux, and is invalid for other kinds of normalizations
     301static bool pmSubtractionKernelPreCalcNormalize(pmSubtractionKernels *kernels, pmSubtractionKernelPreCalc *preCalc,
     302                                                int index, int uOrder, int vOrder, float fwhm,
     303                                                bool AlardLuptonStyle, bool forceZeroNull)
     304{
     305    // 1) for odd functions: no renormalization
     306    // 2) for even functions, normalize the kernel to unity
     307    // 3) for even functions & index > 0, subtract kernel(0)
     308
     309    // Calculate moments
     310    double sum = 0.0, sum2 = 0.0;           // Sum of kernel component
     311    float min = INFINITY, max = -INFINITY;  // Minimum and maximum kernel value
     312
     313    for (int v = preCalc->kernel->yMin; v <= preCalc->kernel->yMax; v++) {
     314        for (int u = preCalc->kernel->xMin; u <= preCalc->kernel->xMax; u++) {
     315            double value = preCalc->kernel->kernel[v][u];
     316            double value2 = PS_SQR(value);
     317            sum += value;
     318            sum2 += value2;
     319            min = PS_MIN(value, min);
     320            max = PS_MAX(value, max);
     321        }
     322    }
     323
     324#if 0
     325    fprintf(stderr, "%d raw: %lf, null: %f, min: %lf, max: %lf\n", index, sum, preCalc->kernel->kernel[0][0], min, max);
     326#endif
     327
     328    float scale2D = 1.0;                // Scaling for 2-D kernels
     329    float scale1D = 1.0;                // Scaling for 1-D kernels
     330
     331    if (uOrder % 2 == 0 && vOrder % 2 == 0) {
     332
     333        scale2D = 1.0 / sum;            // Scaling for 2-D kernels
     334        scale1D = sqrtf(scale2D);               // Scaling for 1-D kernels
     335
     336        for (int v = preCalc->kernel->yMin; v <= preCalc->kernel->yMax; v++) {
     337            for (int u = preCalc->kernel->xMin; u <= preCalc->kernel->xMax; u++) {
     338                preCalc->kernel->kernel[v][u] *= scale2D;
     339            }
     340        }
     341        if (index == 0) {
     342            zeroIsNormal = true;
     343        } else {
     344            psAssert(zeroIsNormal, "failed to normalize zero kernel first");
     345            pmSubtractionKernelPreCalc *zeroKernel = kernels->preCalc->data[0];
     346            psAssert(zeroKernel, "failed to supply zero kernel");
     347            for (int v = preCalc->kernel->yMin; v <= preCalc->kernel->yMax; v++) {
     348                for (int u = preCalc->kernel->xMin; u <= preCalc->kernel->xMax; u++) {
     349                    preCalc->kernel->kernel[v][u] -= zeroKernel->kernel->kernel[v][u];
     350                }
     351            }
     352        }
     353
     354        // XXX why do we bother renormlizing the 1D kernels?  I don't think we use them again...
     355        if (preCalc->xKernel) {
     356            psBinaryOp(preCalc->xKernel, preCalc->xKernel, "*", psScalarAlloc(scale1D, PS_TYPE_F32));
     357        }
     358        if (preCalc->yKernel) {
     359            psBinaryOp(preCalc->yKernel, preCalc->yKernel, "*", psScalarAlloc(scale1D, PS_TYPE_F32));
     360        }
     361    }
     362
     363#if 0
     364    {
     365        double Sum = 0.0;   // Sum of kernel component
     366        double Sum2 = 0.0;   // Sum of kernel component
     367        float min = INFINITY, max = -INFINITY;  // Minimum and maximum kernel value
     368        for (int v = preCalc->kernel->yMin; v <= preCalc->kernel->yMax; v++) {
     369            for (int u = preCalc->kernel->xMin; u <= preCalc->kernel->xMax; u++) {
     370                double value = preCalc->kernel->kernel[v][u];
     371                Sum += value;
     372                Sum2 += PS_SQR(value);
     373                min = PS_MIN(preCalc->kernel->kernel[v][u], min);
     374                max = PS_MAX(preCalc->kernel->kernel[v][u], max);
     375            }
     376        }
     377        fprintf(stderr, "%d sum: %lf, sum2: %lf, null: %f, min: %lf, max: %lf, scale: %f\n", index, Sum, Sum2, preCalc->kernel->kernel[0][0], min, max, scale2D);
     378    }
     379#endif
     380
     381    kernels->widths->data.F32[index] = fwhm;
     382    kernels->u->data.S32[index] = uOrder;
     383    kernels->v->data.S32[index] = vOrder;
     384    if (kernels->preCalc->data[index]) {
     385        psFree(kernels->preCalc->data[index]);
     386    }
     387    kernels->preCalc->data[index] = preCalc;
     388    psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d\n", index, fwhm, uOrder, vOrder);
     389
     390    return true;
     391}
     392# endif /* Central Delta */
    289393
    290394pmSubtractionKernels *p_pmSubtractionKernelsRawISIS(int size, int spatialOrder,
     
    603707    kernels->sampleStamps = NULL;
    604708
    605     kernels->fSigResMean  = NAN;
    606     kernels->fSigResStdev = NAN;
    607     kernels->fMaxResMean  = NAN;
    608     kernels->fMaxResStdev = NAN;
    609     kernels->fMinResMean  = NAN;
    610     kernels->fMinResStdev = NAN;
     709    kernels->fResSigmaMean  = NAN;
     710    kernels->fResSigmaStdev = NAN;
     711    kernels->fResOuterMean  = NAN;
     712    kernels->fResOuterStdev = NAN;
     713    kernels->fResTotalMean  = NAN;
     714    kernels->fResTotalStdev = NAN;
    611715
    612716    return kernels;
  • trunk/psModules/src/imcombine/pmSubtractionKernels.h

    r29004 r29543  
    5151    float mean, rms;                    ///< Mean and RMS of chi^2 from stamps
    5252    int numStamps;                      ///< Number of good stamps
    53     float fSigResMean;                  ///< mean fractional stdev of residuals
    54     float fSigResStdev;                 ///< stdev of fractional stdev of residuals
    55     float fMaxResMean;                  ///< mean fractional positive swing in residuals
    56     float fMaxResStdev;                 ///< stdev of fractional positive swing in residuals
    57     float fMinResMean;                  ///< mean fractional negative swing in residuals
    58     float fMinResStdev;                 ///< stdev of fractional negative swing in residuals
     53    float fResSigmaMean;                ///< mean fractional stdev of residuals
     54    float fResSigmaStdev;               ///< stdev of fractional stdev of residuals
     55    float fResOuterMean;                ///< mean fractional positive swing in residuals
     56    float fResOuterStdev;               ///< stdev of fractional positive swing in residuals
     57    float fResTotalMean;                ///< mean fractional negative swing in residuals
     58    float fResTotalStdev;               ///< stdev of fractional negative swing in residuals
    5959    psArray *sampleStamps;              ///< array of brightest set of stamps for output visualizations
    6060} pmSubtractionKernels;
  • trunk/psModules/src/imcombine/pmSubtractionMatch.c

    r29004 r29543  
    2828static bool useFFT = true;              // Do convolutions using FFT
    2929
    30 # define SEPARATE 0
    31 # if (SEPARATE)
    32 # define SUBMODE PM_SUBTRACTION_EQUATION_NORM
    33 # else
    3430# define SUBMODE PM_SUBTRACTION_EQUATION_ALL
    35 # endif
    3631
    3732//#define TESTING
     
    280275}
    281276
    282 bool pmSubtractionMaskInvalid (const pmReadout *readout, psImageMaskType maskVal) {
    283 
    284     if (!readout) return true;
    285 
    286     psImage *image = readout->image;
    287     psImage *mask  = readout->mask;
    288     psImage *variance = readout->variance;
    289     for (int y = 0; y < image->numRows; y++) {
    290         for (int x = 0; x < image->numCols; x++) {
    291             if (mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) continue;
    292             bool valid = false;
    293             valid = isfinite(image->data.F32[y][x]);
    294             if (variance) {
    295                 valid &= isfinite(variance->data.F32[y][x]);
    296             }
    297             if (valid) continue;
    298             mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = maskVal;
    299         }
    300     }
    301 
    302     return true;
    303 }
     277// bool pmSubtractionMaskInvalid (const pmReadout *readout, psImageMaskType maskVal) {
     278//
     279//     if (!readout) return true;
     280//
     281//     psImage *image = readout->image;
     282//     psImage *mask  = readout->mask;
     283//     psImage *variance = readout->variance;
     284//     for (int y = 0; y < image->numRows; y++) {
     285//         for (int x = 0; x < image->numCols; x++) {
     286//             if (mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) continue;
     287//             bool valid = false;
     288//             valid = isfinite(image->data.F32[y][x]);
     289//             if (variance) {
     290//                 valid &= isfinite(variance->data.F32[y][x]);
     291//             }
     292//             if (valid) continue;
     293//             mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = maskVal;
     294//         }
     295//     }
     296//
     297//     return true;
     298// }
    304299
    305300bool pmSubtractionMatchPrecalc(pmReadout *conv1, pmReadout *conv2, const pmReadout *ro1, const pmReadout *ro2,
     
    392387    }
    393388
    394     pmSubtractionMaskInvalid(ro1, maskVal);
    395     pmSubtractionMaskInvalid(ro2, maskVal);
     389    // XXX this is done before calling this function
     390    // pmSubtractionMaskInvalid(ro1, maskVal);
     391    // pmSubtractionMaskInvalid(ro2, maskVal);
    396392
    397393    // General background subtraction, since this is done in pmSubtractionMatch
     
    565561    memCheck("start");
    566562
    567     pmSubtractionMaskInvalid(ro1, maskVal);
    568     pmSubtractionMaskInvalid(ro2, maskVal);
     563    // pmSubtractionMaskInvalid(ro1, maskVal);
     564    // pmSubtractionMaskInvalid(ro2, maskVal);
    569565
    570566    psRegion bounds = psRegionSet(NAN, NAN, NAN, NAN); // Bounds of valid pixels
     
    672668                kernels = pmSubtractionKernelsGenerate(type, size, spatialOrder, isisWidths, isisOrders,
    673669                                                       inner, binning, ringsOrder, penalty, bounds, subMode);
    674                 // pmSubtractionVisualShowKernels(kernels);
    675670            }
    676671
     
    678673
    679674            if (subMode == PM_SUBTRACTION_MODE_UNSURE) {
    680 #if 0
    681                 // Get backgrounds
    682                 psStats *bgStats = psStatsAlloc(BG_STAT); // Statistics for background
    683                 psVector *buffer = NULL;// Buffer for stats
    684                 if (!psImageBackground(bgStats, &buffer, ro1->image, ro1->mask, maskVal, rng)) {
    685                     psError(PM_ERR_DATA, false, "Unable to measure background of image 1.");
    686                     psFree(bgStats);
    687                     psFree(buffer);
    688                     goto MATCH_ERROR;
    689                 }
    690                 float bg1 = psStatsGetValue(bgStats, BG_STAT); // Background for image 1
    691                 if (!psImageBackground(bgStats, &buffer, ro2->image, ro2->mask, maskVal, rng)) {
    692                     psError(PM_ERR_DATA, false, "Unable to measure background of image 2.");
    693                     psFree(bgStats);
    694                     psFree(buffer);
    695                     goto MATCH_ERROR;
    696                 }
    697                 float bg2 = psStatsGetValue(bgStats, BG_STAT); // Background for image 2
    698                 psFree(bgStats);
    699                 psFree(buffer);
    700 
    701                 pmSubtractionMode newMode = pmSubtractionOrder(stamps, bg1, bg2); // Subtraction mode to use
    702 #endif
    703675                pmSubtractionMode newMode = pmSubtractionBestMode(&stamps, &kernels, subMask, rej);
    704676                switch (newMode) {
     
    732704                }
    733705
    734                 // XXX step 1: calculate normalization
    735                 psTrace("psModules.imcombine", 3, "Calculating equation for normalization...\n");
     706                // step 0 : calculate the normalizations, pass along to the next steps via stamps->normValue
     707                psTrace("psModules.imcombine", 3, "Calculating normalization...\n");
     708                if (!pmSubtractionCalculateNormalization(stamps, kernels->mode)) {
     709                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     710                    goto MATCH_ERROR;
     711                }
     712
     713                // step 0b : calculate the moments, pass along to the next steps via stamps->normValue
     714                // XXX this step is now skipped -- delete
     715                psTrace("psModules.imcombine", 3, "Calculating normalization...\n");
     716                if (!pmSubtractionCalculateMoments(kernels, stamps)) {
     717                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     718                    goto MATCH_ERROR;
     719                }
     720
     721                // step 1: generate the elements of the matrix equation Ax = B
     722                psTrace("psModules.imcombine", 3, "Calculating kernel equations...\n");
    736723                if (!pmSubtractionCalculateEquation(stamps, kernels, SUBMODE)) {
    737724                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     
    739726                }
    740727
    741                 psTrace("psModules.imcombine", 3, "Solving equation for normalization...\n");
     728                // step 2: solve the matrix equation Ax = B
     729                psTrace("psModules.imcombine", 3, "Solving kernel equations...\n");
    742730                if (!pmSubtractionSolveEquation(kernels, stamps, SUBMODE)) {
    743731                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     
    746734                memCheck("  solve equation");
    747735
    748 # if (SEPARATE)
    749                 // set USED -> CALCULATE
    750                 pmSubtractionStampsResetStatus (stamps);
    751 
    752                 // XXX step 2: calculate kernel parameters
    753                 psTrace("psModules.imcombine", 3, "Calculating equation for kernels...\n");
    754                 if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
    755                     psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    756                     goto MATCH_ERROR;
    757                 }
    758                 memCheck("  calculate equation");
    759 
    760                 psTrace("psModules.imcombine", 3, "Solving equation for kernels...\n");
    761                 if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
    762                     psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    763                     goto MATCH_ERROR;
    764                 }
    765                 memCheck("  solve equation");
    766 # endif
    767736                psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    768737                if (!deviations) {
     
    770739                    goto MATCH_ERROR;
    771740                }
    772 
    773741                memCheck("   calculate deviations");
    774742
     
    787755            // if we hit the max number of iterations and we have rejected stamps, re-solve
    788756            if (numRejected > 0) {
    789                 // XXX step 1: calculate normalization
     757
     758                // step 1: generate the elements of the matrix equation Ax = B
    790759                psTrace("psModules.imcombine", 3, "Calculating equation for normalization...\n");
    791760                if (!pmSubtractionCalculateEquation(stamps, kernels, SUBMODE)) {
     
    794763                }
    795764
    796                 // solve normalization
     765                // step 2: solve the matrix equation Ax = B
    797766                psTrace("psModules.imcombine", 3, "Solving equation for kernels...\n");
    798767                if (!pmSubtractionSolveEquation(kernels, stamps, SUBMODE)) {
     
    801770                }
    802771
    803 # if (SEPARATE)
    804                 // set USED -> CALCULATE
    805                 pmSubtractionStampsResetStatus (stamps);
    806 
    807                 // XXX step 2: calculate kernel parameters
    808                 psTrace("psModules.imcombine", 3, "Calculating equation for normalization...\n");
    809                 if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
    810                     psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    811                     goto MATCH_ERROR;
    812                 }
    813 
    814                 // solve kernel parameters
    815                 psTrace("psModules.imcombine", 3, "Solving equation for kernels...\n");
    816                 if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
    817                     psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    818                     goto MATCH_ERROR;
    819                 }
    820                 memCheck("  solve equation");
    821 # endif
    822772                psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    823773                if (!deviations) {
     
    837787                goto MATCH_ERROR;
    838788            }
    839 
    840789            memCheck("diag outputs");
    841790
     
    11341083    }
    11351084
    1136 # if (SEPARATE)
    1137     // set USED -> CALCULATE
    1138     pmSubtractionStampsResetStatus (stamps);
    1139 
    1140     psTrace("psModules.imcombine", 3, "Calculating %s kernel coeffs equation...\n", description);
    1141     if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
    1142         psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    1143         return false;
    1144     }
    1145 
    1146     psTrace("psModules.imcombine", 3, "Solving %s kernel coeffs equation...\n", description);
    1147     if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
    1148         psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    1149         return false;
    1150     }
    1151 # endif
    1152 
    11531085    psTrace("psModules.imcombine", 3, "Calculate %s deviations...\n", description);
    11541086    psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
     
    11811113        }
    11821114        psTrace("psModules.imcombine", 3, "Recalculate %s deviations...\n", description);
    1183 
    1184 # if (SEPARATE)
    1185         // set USED -> CALCULATE
    1186         pmSubtractionStampsResetStatus (stamps);
    1187 
    1188         psTrace("psModules.imcombine", 3, "Calculating %s normalization equation...\n", description);
    1189         if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
    1190             psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    1191             return false;
    1192         }
    1193 
    1194         psTrace("psModules.imcombine", 3, "Resolving %s equation...\n", description);
    1195         if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
    1196             psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    1197             return false;
    1198         }
    1199         psTrace("psModules.imcombine", 3, "Recalculate %s deviations...\n", description);
    1200 # endif
    12011115
    12021116        psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
     
    12881202
    12891203bool pmSubtractionParamsScale(int *kernelSize, int *stampSize, psVector *widths,
    1290                               float fwhm1, float fwhm2, float scaleRef, float scaleMin, float scaleMax)
     1204                              float scaleRef, float scaleMin, float scaleMax)
    12911205{
    12921206    PS_ASSERT_PTR_NON_NULL(kernelSize, false);
     
    12941208    PS_ASSERT_VECTOR_NON_NULL(widths, false);
    12951209    PS_ASSERT_VECTOR_TYPE(widths, PS_TYPE_F32, false);
    1296     PS_ASSERT_FLOAT_LARGER_THAN(fwhm1, 0.0, false);
    1297     PS_ASSERT_FLOAT_LARGER_THAN(fwhm2, 0.0, false);
    12981210    PS_ASSERT_FLOAT_LARGER_THAN(scaleRef, 0.0, false);
    12991211    PS_ASSERT_FLOAT_LARGER_THAN(scaleMin, 0.0, false);
     
    13011213    PS_ASSERT_FLOAT_LARGER_THAN(scaleMax, scaleMin, false);
    13021214
    1303 //    float diff = sqrtf(PS_SQR(PS_MAX(fwhm1, fwhm2)) - PS_SQR(PS_MIN(fwhm1, fwhm2))); // Difference
     1215    float fwhm1;
     1216    float fwhm2;
     1217
     1218    pmSubtractionGetFWHMs(&fwhm1, &fwhm2);
     1219    psAssert(isfinite(fwhm1), "fwhm 1 not set");
     1220    psAssert(isfinite(fwhm2), "fwhm 2 not set");
     1221   
     1222    // float diff = sqrtf(PS_SQR(PS_MAX(fwhm1, fwhm2)) - PS_SQR(PS_MIN(fwhm1, fwhm2))); // Difference
    13041223    float scale = PS_MAX(fwhm1, fwhm2) / scaleRef;      // Scaling factor
    1305 
    1306     // XXX save these values in a static for later use
    1307     pmSubtractionSetFWHMs(fwhm1, fwhm2);
    13081224
    13091225    if (isfinite(scaleMin) && scale < scaleMin) {
  • trunk/psModules/src/imcombine/pmSubtractionMatch.h

    r29004 r29543  
    104104    int *stampSize,                     ///< Half-size of the stamp (footprint)
    105105    psVector *widths,                   ///< ISIS widths
    106     float fwhm1, float fwhm2,           ///< FWHMs for inputs
    107106    float scaleRef,                     ///< Reference width for scaling
    108107    float scaleMin,                     ///< Minimum scaling ratio, or NAN
  • trunk/psModules/src/imcombine/pmSubtractionStamps.c

    r29019 r29543  
    5151    psFree(list->y);
    5252    psFree(list->flux);
     53    psFree(list->window);
    5354    psFree(list->window1);
    5455    psFree(list->window2);
     
    9697
    9798    // fprintf (stderr, "xMin, xMax: %d, %d -> ", xMin, xMax);
     99    // float xRaw = xMin;
     100    // float yRaw = yMin;
    98101
    99102    // Ensure we're not going to go outside the bounds of the image
     
    103106    yMax = PS_MIN(numRows - border - 1, yMax);
    104107
    105     if (xMax < xMin) return false;
    106     if (yMax < yMin) return false;
     108    if (xMax < xMin) {
     109        // fprintf (stderr, "%f,%f : x-border\n", xRaw, yRaw);
     110        return false;
     111    }
     112    if (yMax < yMin) {
     113        // fprintf (stderr, "%f,%f : y-border\n", xRaw, yRaw);
     114        return false;
     115    }
    107116
    108117    psAssert (xMin <= xMax, "x mismatch?");
     
    118127            if (subMask && subMask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] &
    119128                (PM_SUBTRACTION_MASK_BORDER | PM_SUBTRACTION_MASK_REJ)) {
     129                // fprintf (stderr, "%f,%f : masked\n", xRaw, yRaw);
    120130                return false;
    121131            }
     
    133143    }
    134144
     145    // if (!found) {
     146    //  fprintf (stderr, "%f,%f : fails flux test\n", xRaw, yRaw);
     147    // }
     148
    135149    return found;
    136150}
     
    232246    list->flux = NULL;
    233247    list->normFrac = normFrac;
     248    list->normValue = NAN;
     249    list->window = NULL;
    234250    list->window1 = NULL;
    235251    list->window2 = NULL;
     
    256272    out->y = NULL;
    257273    out->flux = NULL;
     274    out->window = psMemIncrRefCounter(in->window);
    258275    out->window1 = psMemIncrRefCounter(in->window1);
    259276    out->window2 = psMemIncrRefCounter(in->window2);
     
    331348    stamp->vector = NULL;
    332349    stamp->norm = NAN;
     350    stamp->normI1 = NAN;
     351    stamp->normI2 = NAN;
     352    stamp->normSquare1 = NAN;
     353    stamp->normSquare2 = NAN;
     354
     355    stamp->MxxI1 = NULL;
     356    stamp->MyyI1 = NULL;
     357    stamp->MxxI2 = NULL;
     358    stamp->MyyI2 = NULL;
     359
     360    stamp->MxxI1raw = NAN;
     361    stamp->MyyI1raw = NAN;
     362    stamp->MxxI2raw = NAN;
     363    stamp->MyyI2raw = NAN;
    333364
    334365    return stamp;
     
    431462                // Take stamps off the top of the (sorted) list
    432463                for (int j = xList->n - 1; j >= 0 && !goodStamp; j--) {
     464                    // fprintf (stderr, "%d : xList: %ld elements\n", i, xList->n);
    433465                    // Chop off the top of the list
    434466                    xList->n = j;
     
    459491                                            yList->data.F32[j], yList->data.F32[j], numCols, numRows, border);
    460492#endif
    461                     if (0 && goodStamp) {
    462                         fprintf (stderr, "find: %6.1f < %6.1f < %6.1f, %6.1f < %6.1f %6.1f\n",
    463                                  region->x0 + size, xStamp, region->x1 - size,
    464                                  region->y0 + size, yStamp, region->y1 - size);
    465                     }
    466493                }
    467494            } else {
     
    656683    psImageInit(stamps->window2->image, 0.0);
    657684
     685    // Generate a weighting window based on the fwhms (20% larger than the largest)
     686    {
     687        float fwhm1, fwhm2;
     688
     689        // XXX this is annoyingly hack-ish
     690        pmSubtractionGetFWHMs(&fwhm1, &fwhm2);
     691
     692        float sigma = 1.5 * PS_MAX(fwhm1, fwhm2) / 2.35;
     693
     694        psFree (stamps->window);
     695        stamps->window = psKernelAlloc(-size, size, -size, size);
     696        psImageInit(stamps->window->image, 0.0);
     697
     698        for (int y = -size; y <= size; y++) {
     699            for (int x = -size; x <= size; x++) {
     700                stamps->window->kernel[y][x] = exp(-0.5*(x*x + y*y)/(sigma*sigma));
     701            }
     702        }
     703    }
     704
    658705    // generate normalizations for each stamp
    659706    psVector *norm1 = psVectorAlloc(stamps->num, PS_TYPE_F32);
     
    678725
    679726    // storage vector for flux data
    680     psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
     727    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
    681728    psVector *flux1 = psVectorAllocEmpty(2*stamps->num, PS_TYPE_F32);
    682729    psVector *flux2 = psVectorAllocEmpty(2*stamps->num, PS_TYPE_F32);
     
    706753                psAbort ("failed to generate stats");
    707754            }
    708             float f1 = stats->robustMedian;
     755            float f1 = stats->sampleMedian;
    709756
    710757            psStatsInit (stats);
     
    712759                psAbort ("failed to generate stats");
    713760            }
    714             float f2 = stats->robustMedian;
     761            float f2 = stats->sampleMedian;
    715762
    716763            stamps->window1->kernel[y][x] = f1;
     
    728775    }
    729776
     777#if 0
     778    {
     779        psFits *fits = NULL;
     780        fits = psFitsOpen ("window1.raw.fits", "w");
     781        psFitsWriteImage (fits, NULL, stamps->window1->image, 0, NULL);
     782        psFitsClose (fits);
     783        fits = psFitsOpen ("window2.raw.fits", "w");
     784        psFitsWriteImage (fits, NULL, stamps->window2->image, 0, NULL);
     785        psFitsClose (fits);
     786    }
     787#endif
     788
    730789    psTrace("psModules.imcombine", 3, "Window total (1): %f, threshold: %f\n", sum1, (1.0 - stamps->normFrac) * sum1);
    731790    psTrace("psModules.imcombine", 3, "Window total (2): %f, threshold: %f\n", sum2, (1.0 - stamps->normFrac) * sum2);
    732791
    733 # if (0)
     792# if (1)
    734793    // this block attempts to calculate the radius based on the first radial moment
    735     bool done1 = false;
    736     bool done2 = false;
    737     double prior1 = 0.0;
    738     double prior2 = 0.0;
     794    double Sr1 = 0.0;
     795    double Sr2 = 0.0;
     796    double Sf1 = 0.0;
     797    double Sf2 = 0.0;
    739798    for (int y = -size; y <= size; y++) {
    740799        for (int x = -size; x <= size; x++) {
     
    750809    float R2 = Sr2 / Sf2;
    751810
    752     stamps->normWindow1 = 2.5*R1;
    753     stamps->normWindow1 = 2.5*R2;
     811    stamps->normWindow1 = 2.0*R1;
     812    stamps->normWindow2 = 2.0*R2;
     813    psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "Kron Radii: %f for 1, %f for 2\n", stamps->normWindow1, stamps->normWindow2);
     814
    754815# else
    755816    // XXX : this block attempts to calculate the radius by looking at the curve of growth (or something vaguely equivalent).
     
    778839            // interpolate to the radius at which delta2 is normFrac:
    779840            stamps->normWindow1 = radius - (stamps->normFrac - delta1) / (delta1o - delta1);
    780             fprintf (stderr, "choosing %f (%d) for 1 (%f : %f)\n", stamps->normWindow1, radius, within1, delta1);
     841            psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "choosing %f (%d) for 1 (%f : %f)\n", stamps->normWindow1, radius, within1, delta1);
    781842            done1 = true;
    782843        }
     
    785846            // interpolate to the radius at which delta2 is normFrac:
    786847            stamps->normWindow2 = radius - (stamps->normFrac - delta2) / (delta2o - delta2);
    787             fprintf (stderr, "choosing %f (%d) for 2 (%f : %f)\n", stamps->normWindow2, radius, within2, delta2);
     848            psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "choosing %f (%d) for 2 (%f : %f)\n", stamps->normWindow2, radius, within2, delta2);
    788849            done2 = true;
    789850        }
     
    833894    {
    834895        psFits *fits = NULL;
    835         fits = psFitsOpen ("window1.fits", "w");
     896        fits = psFitsOpen ("window1.norm.fits", "w");
    836897        psFitsWriteImage (fits, NULL, stamps->window1->image, 0, NULL);
    837898        psFitsClose (fits);
    838         fits = psFitsOpen ("window2.fits", "w");
     899        fits = psFitsOpen ("window2.norm.fits", "w");
    839900        psFitsWriteImage (fits, NULL, stamps->window2->image, 0, NULL);
    840901        psFitsClose (fits);
     
    906967        float M2 = pmSubtractionKernelPenaltySingle(kernel->kernel, zeroNull);
    907968
    908         penalty1 = M2 + PS_SQR(fwhm1 / 2.35); // rescale the unconvolved second-moment by the image second moment
    909         penalty2 = M2 + PS_SQR(fwhm2 / 2.35); // rescale the unconvolved second-moment by the image second moment
     969        if (1) {
     970            penalty1 = M2 * PS_SQR(fwhm1 / 2.35); // rescale the unconvolved second-moment by the image second moment
     971            penalty2 = M2 * PS_SQR(fwhm2 / 2.35); // rescale the unconvolved second-moment by the image second moment
     972            // penalty1 = M2 + PS_SQR(fwhm1 / 2.35); // rescale the unconvolved second-moment by the image second moment
     973            // penalty2 = M2 + PS_SQR(fwhm2 / 2.35); // rescale the unconvolved second-moment by the image second moment
     974        } else {
     975            penalty1 = PS_SQR(fwhm1 / 2.35); // rescale the unconvolved second-moment by the image second moment
     976            penalty2 = PS_SQR(fwhm2 / 2.35); // rescale the unconvolved second-moment by the image second moment
     977        }
    910978    }
    911979    kernels->penalties1->data.F32[index] = kernels->penalty * penalty1;
     
    915983    psAssert (isfinite(kernels->penalties2->data.F32[index]), "invalid penalty");
    916984
    917     fprintf(stderr, "penalty1: %f, penalty2: %f\n", penalty1, penalty2);
     985    // fprintf(stderr, "penalty1: %f, penalty2: %f\n", penalty1, penalty2);
    918986
    919987    return true;
     
    9421010    penalty *= 1.0 / sum2;
    9431011
    944     if (1) {
    945         fprintf(stderr, "min: %lf, max: %lf, moment: %lf, flux^2: %lf\n", min, max, penalty, sum2);
     1012    if (0) {
     1013        // fprintf(stderr, "min: %lf, max: %lf, moment: %lf, flux^2: %lf\n", min, max, penalty, sum2);
    9461014        // psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d %f\n", index, fwhm, uOrder, vOrder, penalty);
    9471015    }
     
    9831051
    9841052        if (x < bounds.x0 + size || x > bounds.x1 - size || y < bounds.y0 + size || y > bounds.y1 - size) {
    985             psError(PM_ERR_PROG, false, "Stamp %d (%d,%d) is within the region border.\n", i, x, y);
    986             return false;
     1053          psLogMsg("psModules.imcombine", 3, "Stamp %d (%d,%d) is within the region border.\n", i, x, y);
     1054          stamp->status = PM_SUBTRACTION_STAMP_NONE;
     1055          continue;
    9871056        }
    9881057
  • trunk/psModules/src/imcombine/pmSubtractionStamps.h

    r29004 r29543  
    2525    int footprint;                      ///< Half-size of stamps
    2626    float normFrac;                     ///< Fraction of flux in window for normalisation window
     27    float normValue;                    ///< calculated normalization
     28    float normValue2;                   ///< calculated normalization
    2729    psKernel *window1;                  ///< window function generated from ensemble of stamps (input 1)
    2830    psKernel *window2;                  ///< window function generated from ensemble of stamps (input 2)
    29     float normWindow1;                    ///< Size of window for measuring normalisation
    30     float normWindow2;                    ///< Size of window for measuring normalisation
     31    psKernel *window;                   ///< weighting window function (sigma = 1.1 * MAX(fwhm))
     32    float normWindow1;                  ///< Size of window for measuring normalisation
     33    float normWindow2;                  ///< Size of window for measuring normalisation
    3134    float sysErr;                       ///< Systematic error
    3235    float skyErr;                       ///< increase effective readnoise
     
    8588    psVector *vector;                   ///< Least-squares vector, or NULL
    8689    double norm;                        ///< Normalisation difference
     90    double normI1;                       ///< Sum(flux) for image 1
     91    double normI2;                       ///< Sum(flux) for image 2
     92    double normSquare1;                 ///< Sum(flux^2) for image 1 (used for penalty)
     93    double normSquare2;                 ///< Sum(flux^2) for image 2 (used for penalty)
    8794    pmSubtractionStampStatus status;    ///< Status of stamp
     95    psVector *MxxI1;                    ///< second moments of convolution images
     96    psVector *MyyI1;                    ///< second moments of convolution images
     97    psVector *MxxI2;                    ///< second moments of convolution images
     98    psVector *MyyI2;                    ///< second moments of convolution images
     99    double MxxI1raw;
     100    double MyyI1raw;
     101    double MxxI2raw;
     102    double MyyI2raw;
    88103} pmSubtractionStamp;
    89104
  • trunk/psModules/src/imcombine/pmSubtractionVisual.c

    r29004 r29543  
    146146    }
    147147    pmVisualScaleImage(kapa1, canvas, "Subtraction_Stamps", 0, true);
     148    psFree(canvas);
    148149
    149150    pmVisualAskUser(&plotStamps);
     
    152153
    153154/** Plot the least-squares matrix of each stamp */
    154 bool pmSubtractionVisualPlotLeastSquares (pmSubtractionStampList *stamps, bool dual) {
     155bool pmSubtractionVisualPlotLeastSquares (pmSubtractionStampList *stamps) {
    155156
    156157    if (!pmVisualTestLevel("ppsub.chisq", 1)) return true;
     
    209210    pmVisualScaleImage(kapa1, canvas32, "Least_Squares", 0, true);
    210211
     212    if (0) {
     213        static int count = 0;
     214        char filename[64];
     215        sprintf (filename, "chisq.%02d.fits", count);
     216        count ++;
     217        psFits *fits = psFitsOpen (filename, "w");
     218        psFitsWriteImage (fits, NULL, canvas32, 0, NULL);
     219        psFitsClose (fits);
     220    }
     221
    211222    pmVisualAskUser(&plotLeastSquares);
    212223    psFree(canvas);
     
    271282            }
    272283        }
    273         fprintf (stderr, "kernel %d, sum %f\n", i, sum);
     284        // fprintf (stderr, "kernel %d, sum %f\n", i, sum);
    274285    }                                                   
    275286       
    276287    pmVisualScaleImage(kapa1, output, "Image", 0, true);
     288    psFree(output);
    277289    pmVisualAskUser(&plotImage);
    278290    return true;
     
    286298        return false;
    287299    }
     300
     301    // XXX clear the overlay(s) (red at least!)
    288302
    289303    // get the kernel sizes
     
    297311        if (!isfinite(stamp->flux)) continue;
    298312        if (!stamp->convolutions1 && !stamp->convolutions2) continue;
     313        // fprintf (stderr, "flux: %f, maxFlux: %f  ", stamp->flux, maxFlux);
    299314        if (!maxStamp) {
    300315            maxFlux = stamp->flux;
    301316            maxStamp = stamp;
     317            // fprintf (stderr, "maxStamp %d\n", i);
    302318            continue;
     319        } else {
     320            // fprintf (stderr, "\n");
    303321        }
    304322        if (stamp->flux > maxFlux) {
     
    337355           
    338356            double sum = 0.0;
     357            double sum2 = 0.0;
    339358            for (int y = -footprint; y <= footprint; y++) {
    340359                for (int x = -footprint; x <= footprint; x++) {
    341360                    output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x];
    342361                    sum += kernel->kernel[y][x];
     362                    sum2 += PS_SQR(kernel->kernel[y][x]);
    343363                }
    344364            }
    345             fprintf (stderr, "kernel %d, sum %f\n", i, sum);
     365            // fprintf (stderr, "kernel %d, sum %f, sum2: %e\n", i, sum, sum2);
    346366        }               
    347367        pmVisualScaleImage(kapa2, output, "Image", 0, true);
    348     }                                   
     368
     369        if (0) {
     370            psFits *fits = psFitsOpen("basis.1.fits", "w");
     371            psFitsWriteImage(fits, NULL, output, 0, NULL);
     372            psFitsClose(fits);
     373        }
     374    }
    349375       
    350376    if (maxStamp->convolutions2) {
     
    371397           
    372398            double sum = 0.0;
     399            double sum2 = 0.0;
    373400            for (int y = -footprint; y <= footprint; y++) {
    374401                for (int x = -footprint; x <= footprint; x++) {
    375402                    output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x];
    376403                    sum += kernel->kernel[y][x];
     404                    sum2 += PS_SQR(kernel->kernel[y][x]);
    377405                }
    378406            }
    379             fprintf (stderr, "kernel %d, sum %f\n", i, sum);
     407            // fprintf (stderr, "kernel %d, sum %f, sum2: %e\n", i, sum, sum2);
    380408        }               
    381409        pmVisualScaleImage(kapa2, output, "Image", 1, true);
     410
     411        if (0) {
     412            psFits *fits = psFitsOpen("basis.2.fits", "w");
     413            psFitsWriteImage(fits, NULL, output, 0, NULL);
     414            psFitsClose(fits);
     415        }
     416        psFree(output);
    382417    }                                   
    383418       
     
    676711    psFree (x);
    677712    psFree (y);
     713    psFree (polyValues);
    678714
    679715    pmVisualAskUser(NULL);
Note: See TracChangeset for help on using the changeset viewer.