IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 26893


Ignore:
Timestamp:
Feb 10, 2010, 7:34:39 PM (16 years ago)
Author:
eugene
Message:

updates from eam_branches/20091201 (substantially changes to the kernel matching code; updates to pmDetection container, added pmPattern, etc)

Location:
trunk/psModules/src
Files:
2 deleted
53 edited
8 copied

Legend:

Unmodified
Added
Removed
  • trunk/psModules/src/astrom/pmAstrometryObjects.c

    r26359 r26893  
    108108        }
    109109
    110         if (found1->data.S8[i]) {
    111             i++;
    112             continue;
    113         }
    114         if (found2->data.S8[j]) {
    115             j++;
    116             continue;
    117         }
     110        if (found1->data.S8[i]) {
     111            i++;
     112            continue;
     113        }
     114        if (found2->data.S8[j]) {
     115            j++;
     116            continue;
     117        }
    118118
    119119        jStart = j;
     
    128128                continue;
    129129            }
    130             if (found2->data.S8[j]) {
    131                 j++;
    132                 continue;
    133             }
     130            if (found2->data.S8[j]) {
     131                j++;
     132                continue;
     133            }
    134134
    135135            // got a match; add to output list
     
    138138            psFree (match);
    139139
    140             found1->data.S8[i] = 1;
    141             found2->data.S8[j] = 1;
     140            found1->data.S8[i] = 1;
     141            found2->data.S8[j] = 1;
    142142
    143143            j++;
     
    196196    psArray *matches = match_lists(x1, y1, x2, y2, sorted1, sorted2, RADIUS); \
    197197    \
    198     psFree((void *)sorted1); \
    199     psFree((void *)sorted2); \
     198    psFree(sorted1); \
     199    psFree(sorted2); \
    200200    psFree(x1); \
    201201    psFree(y1); \
     
    314314    if (!xRes) abort();
    315315    psFree (xFit);
    316    
     316
    317317    psVector *yFit = psPolynomial2DEvalVector (map->y, X, Y);
    318318    if (!yFit) abort();
     
    322322
    323323    // extract a high-quality subset (unmasked, S/N > XXX) and position errors
    324     // XXX for now, generate a position error based on the magnitude error 
     324    // XXX for now, generate a position error based on the magnitude error
    325325    psVector *xErr     = psVectorAllocEmpty (match->n, PS_TYPE_F32);
    326326    psVector *yErr     = psVectorAllocEmpty (match->n, PS_TYPE_F32);
     
    329329
    330330    for (int i = 0; i < match->n; i++) {
    331         if (mask->data.PS_TYPE_VECTOR_MASK_DATA[i]) continue;
     331        if (mask->data.PS_TYPE_VECTOR_MASK_DATA[i]) continue;
    332332        pmAstromMatch *pair = match->data[i];
    333333        pmAstromObj *rawStar = raw->data[pair->raw];
     
    335335        if (rawStar->dMag > 0.02) continue;
    336336
    337         // two likely failure values: NAN or 0.0 --> use dMag in this case
    338         float xErrValue, yErrValue;
    339         if (isfinite(rawStar->chip->xErr) && (rawStar->chip->xErr > 0.0)) {
    340             xErrValue = rawStar->chip->xErr;
    341         } else {
    342             xErrValue = PS_MAX(0.005, rawStar->dMag);
    343         }
    344         if (isfinite(rawStar->chip->yErr) && (rawStar->chip->yErr > 0.0)) {
    345             yErrValue = rawStar->chip->yErr;
    346         } else {
    347             yErrValue = PS_MAX(0.005, rawStar->dMag);
    348         }
    349 
    350         psVectorAppend (xErr,     xErrValue);
    351         psVectorAppend (yErr,     yErrValue);
    352         psVectorAppend (xResGood, xRes->data.F32[i]);
    353         psVectorAppend (yResGood, yRes->data.F32[i]);
     337        // two likely failure values: NAN or 0.0 --> use dMag in this case
     338        float xErrValue, yErrValue;
     339        if (isfinite(rawStar->chip->xErr) && (rawStar->chip->xErr > 0.0)) {
     340            xErrValue = rawStar->chip->xErr;
     341        } else {
     342            xErrValue = PS_MAX(0.005, rawStar->dMag);
     343        }
     344        if (isfinite(rawStar->chip->yErr) && (rawStar->chip->yErr > 0.0)) {
     345            yErrValue = rawStar->chip->yErr;
     346        } else {
     347            yErrValue = PS_MAX(0.005, rawStar->dMag);
     348        }
     349
     350        psVectorAppend (xErr,     xErrValue);
     351        psVectorAppend (yErr,     yErrValue);
     352        psVectorAppend (xResGood, xRes->data.F32[i]);
     353        psVectorAppend (yResGood, yRes->data.F32[i]);
    354354    }
    355355
     
    434434    // Get the minimum and maximum values
    435435    if (!psVectorStats(stats, myVector, NULL, NULL, 0)) {
    436         psFree(stats);
    437         return NAN;
     436        psFree(stats);
     437        return NAN;
    438438    }
    439439    min = stats->min;
    440440    max = stats->max;
    441441    if (isnan(min) || isnan(max)) {
    442         psFree(stats);
    443         return NAN;
     442        psFree(stats);
     443        return NAN;
    444444    }
    445445
     
    448448    // If all data points have the same value, then we set the appropriate members of stats and return.
    449449    if (fabs(max - min) <= FLT_EPSILON) {
    450         psFree (stats);
    451         return 0.0;
    452     }
    453    
    454     // Define the histogram bin size. 
     450        psFree (stats);
     451        return 0.0;
     452    }
     453
     454    // Define the histogram bin size.
    455455    float binSize = 0.001;
    456456    long numBins = PS_MIN(100000, (max - min) / binSize); // Number of bins
     
    463463
    464464    if (!psVectorHistogram(histogram, myVector, NULL, NULL, 0)) {
    465         // if psVectorHistogram returns false, we have a programming error
    466         psAbort ("Unable to generate histogram");
     465        // if psVectorHistogram returns false, we have a programming error
     466        psAbort ("Unable to generate histogram");
    467467    }
    468468    if (psTraceGetLevel("psModules.astrom") >= 8) {
    469         PS_VECTOR_PRINT_F32(histogram->bounds);
    470         PS_VECTOR_PRINT_F32(histogram->nums);
     469        PS_VECTOR_PRINT_F32(histogram->bounds);
     470        PS_VECTOR_PRINT_F32(histogram->nums);
    471471    }
    472472
     
    475475    cumulative->nums->data.F32[0] = histogram->nums->data.F32[0];
    476476    for (long i = 1; i < histogram->nums->n; i++) {
    477         cumulative->nums->data.F32[i] = cumulative->nums->data.F32[i-1] + histogram->nums->data.F32[i];
    478         cumulative->bounds->data.F32[i-1] = histogram->bounds->data.F32[i];
     477        cumulative->nums->data.F32[i] = cumulative->nums->data.F32[i-1] + histogram->nums->data.F32[i];
     478        cumulative->bounds->data.F32[i-1] = histogram->bounds->data.F32[i];
    479479    }
    480480    if (psTraceGetLevel("psModules.astrom") >= 8) {
    481         PS_VECTOR_PRINT_F32(cumulative->bounds);
    482         PS_VECTOR_PRINT_F32(cumulative->nums);
     481        PS_VECTOR_PRINT_F32(cumulative->bounds);
     482        PS_VECTOR_PRINT_F32(cumulative->nums);
    483483    }
    484484
     
    837837        }
    838838
    839         if (psTraceGetLevel("psModules.astrom") >= 5) {
    840             char line[16];
    841             psFits *fits = psFitsOpen ("grid.image.fits", "w");
    842             psFitsWriteImage (fits, NULL, gridNP, 0, NULL);
    843             psFitsClose (fits);
    844             fprintf (stderr, "wrote grid image, press return to continue\n");
    845             if (!fgets(line, 15, stdin)) {
    846                 fprintf(stderr, "Something went wrong; continuing.");
     839        if (psTraceGetLevel("psModules.astrom") >= 5) {
     840            char line[16];
     841            psFits *fits = psFitsOpen ("grid.image.fits", "w");
     842            psFitsWriteImage (fits, NULL, gridNP, 0, NULL);
     843            psFitsClose (fits);
     844            fprintf (stderr, "wrote grid image, press return to continue\n");
     845            if (!fgets (line, 15, stdin)) {
     846                fprintf(stderr, "Error waiting for RETURN.");
    847847            }
    848         }
     848        }
    849849
    850850        // only check bins with at least 1/2 of max bin
     
    11911191psArray *pmAstromRadiusMatchUniq (psArray *rawstars, psArray *refstars, psArray *matches) {
    11921192
    1193     // I have the matches between the refstars and the rawstars. 
     1193    // I have the matches between the refstars and the rawstars.
    11941194    // For each refstar, find the single match which has the smallest radius and reject
    1195     // all others. 
     1195    // all others.
    11961196
    11971197    // create an array of refstars->n arrays, each containing all of the matches for the
    1198     // given refstar. 
     1198    // given refstar.
    11991199
    12001200    psArray *refstarMatches = psArrayAlloc (refstars->n);
     
    12021202    for (int i = 0; i < matches->n; i++) {
    12031203
    1204         pmAstromMatch *match = matches->data[i];
    1205        
    1206         // accumulate this refstar match on the array for this refstar (create if needed)
    1207         psArray *refSet = refstarMatches->data[match->ref];
    1208         if (!refSet) {
    1209             refstarMatches->data[match->ref] = psArrayAllocEmpty (8);
    1210             refSet = refstarMatches->data[match->ref];
    1211         }
    1212 
    1213         pmAstromMatchInfo *matchInfo = pmAstromMatchInfoAlloc();
    1214 
    1215         pmAstromObj *refStar = refstars->data[match->ref];
    1216         pmAstromObj *rawStar = rawstars->data[match->raw];
    1217        
    1218         matchInfo->match = match; // reference to the match of interest
    1219         matchInfo->radius = hypot (refStar->FP->x - rawStar->FP->x, refStar->FP->y - rawStar->FP->y);
    1220 
    1221         psArrayAdd (refSet, 8, matchInfo); // matchInfo->match is just a reference
    1222         psFree (matchInfo);
     1204        pmAstromMatch *match = matches->data[i];
     1205
     1206        // accumulate this refstar match on the array for this refstar (create if needed)
     1207        psArray *refSet = refstarMatches->data[match->ref];
     1208        if (!refSet) {
     1209            refstarMatches->data[match->ref] = psArrayAllocEmpty (8);
     1210            refSet = refstarMatches->data[match->ref];
     1211        }
     1212
     1213        pmAstromMatchInfo *matchInfo = pmAstromMatchInfoAlloc();
     1214
     1215        pmAstromObj *refStar = refstars->data[match->ref];
     1216        pmAstromObj *rawStar = rawstars->data[match->raw];
     1217
     1218        matchInfo->match = match; // reference to the match of interest
     1219        matchInfo->radius = hypot (refStar->FP->x - rawStar->FP->x, refStar->FP->y - rawStar->FP->y);
     1220
     1221        psArrayAdd (refSet, 8, matchInfo); // matchInfo->match is just a reference
     1222        psFree (matchInfo);
    12231223    }
    12241224
     
    12291229    for (int i = 0; i < refstars->n; i++) {
    12301230
    1231         psArray *refSet = refstarMatches->data[i];
    1232         if (!refSet) continue;
    1233         if (refSet->n == 0) continue; // not certain how this can happen...
    1234 
    1235         if (refSet->n == 1) {
    1236             pmAstromMatchInfo *matchInfo = refSet->data[0];
    1237             psArrayAdd (unique, 32, matchInfo->match);
    1238             continue;
    1239         }
    1240 
    1241         pmAstromMatchInfo *matchInfo = refSet->data[0];
    1242         float minRadius = matchInfo->radius;
    1243         pmAstromMatch *minMatch = matchInfo->match;
    1244         for (int j = 1; j < refSet->n; j++) {
    1245             pmAstromMatchInfo *matchInfo = refSet->data[j];
    1246             if (minRadius < matchInfo->radius) continue;
    1247             minMatch = matchInfo->match;
    1248             minRadius = matchInfo->radius;
    1249         }
    1250        
    1251         psArrayAdd (unique, 32, minMatch); // minMatch is just a reference to a match on matches,
     1231        psArray *refSet = refstarMatches->data[i];
     1232        if (!refSet) continue;
     1233        if (refSet->n == 0) continue; // not certain how this can happen...
     1234
     1235        if (refSet->n == 1) {
     1236            pmAstromMatchInfo *matchInfo = refSet->data[0];
     1237            psArrayAdd (unique, 32, matchInfo->match);
     1238            continue;
     1239        }
     1240
     1241        pmAstromMatchInfo *matchInfo = refSet->data[0];
     1242        float minRadius = matchInfo->radius;
     1243        pmAstromMatch *minMatch = matchInfo->match;
     1244        for (int j = 1; j < refSet->n; j++) {
     1245            pmAstromMatchInfo *matchInfo = refSet->data[j];
     1246            if (minRadius < matchInfo->radius) continue;
     1247            minMatch = matchInfo->match;
     1248            minRadius = matchInfo->radius;
     1249        }
     1250
     1251        psArrayAdd (unique, 32, minMatch); // minMatch is just a reference to a match on matches,
    12521252    }
    12531253
  • trunk/psModules/src/camera/pmFPA.c

    r26466 r26893  
    105105    psFree(fpa->concepts);
    106106    psFree(fpa->analysis);
    107     psFree((void *)fpa->camera);
     107    psFree(fpa->camera);
    108108    psFree(fpa->hdu);
    109109
  • trunk/psModules/src/camera/pmFPAMaskWeight.c

    r26076 r26893  
    370370
    371371    psImage *image = readout->image, *mask = readout->mask, *variance = readout->variance; // Readout parts
    372 
    373372    int numCols = image->numCols, numRows = image->numRows; // Size of image
    374     int numPix = numCols * numRows;                         // Number of pixels
    375     int num = PS_MAX(sample, numPix);                       // Number we care about
     373
     374    int xMin, xMax, yMin, yMax;         // Bounds of image
     375    if (mask) {
     376        xMin = numCols;
     377        xMax = 0;
     378        yMin = numRows;
     379        yMax = 0;
     380        for (int y = 0; y < numRows; y++) {
     381            for (int x = 0; x < numCols; x++) {
     382                if (mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) {
     383                    continue;
     384                }
     385                xMin = PS_MIN(xMin, x);
     386                xMax = PS_MAX(xMax, x);
     387                yMin = PS_MIN(yMin, y);
     388                yMax = PS_MAX(yMax, y);
     389            }
     390        }
     391    } else {
     392        xMin = 0;
     393        xMax = numCols;
     394        yMin = 0;
     395        yMax = numRows;
     396    }
     397
     398    int xNum = xMax - xMin, yNum = yMax - yMin; // Number of pixels
     399
     400    int numPix = xNum * yNum;                                  // Number of pixels
     401    int num = PS_MIN(sample, numPix);                          // Number we care about
    376402    psVector *signoise = psVectorAllocEmpty(num, PS_TYPE_F32);   // Signal-to-noise values
    377403
     
    379405        // We have an image smaller than Nsubset, so just loop over the image pixels
    380406        int index = 0;                  // Index for vector
    381         for (int y = 0; y < numRows; y++) {
    382             for (int x = 0; x < numCols; x++) {
     407        for (int y = yMin; y < yMax; y++) {
     408            for (int x = xMin; x < xMax; x++) {
    383409                if ((mask && mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) ||
    384410                    !isfinite(image->data.F32[y][x]) || !isfinite(variance->data.F32[y][x])) {
     
    397423            // Pixel coordinates
    398424            int pixel = numPix * psRandomUniform(rng);
    399             int x = pixel % numCols;
    400             int y = pixel / numCols;
     425            int x = xMin + pixel % xNum;
     426            int y = yMin + pixel / xNum;
    401427
    402428            if ((mask && mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) ||
     
    428454    // Check valid range of correction factor
    429455    if ((isfinite(minValid) && correction < minValid) || (isfinite(maxValid) && correction > maxValid)) {
    430         psWarning("Variance renormalisation is outside valid range: %f vs %f:%f --- no correction made",
    431                   correction, minValid, maxValid);
    432         return true;
    433     }
    434 
    435     psBinaryOp(variance, variance, "*", psScalarAlloc(PS_SQR(correction), PS_TYPE_F32));
     456        psError(PS_ERR_UNKNOWN, true, "Variance renormalisation is outside valid range: %f vs %f:%f --- no correction made", correction, minValid, maxValid);
     457        psMetadataAddF32(readout->analysis, PS_LIST_TAIL, PM_READOUT_ANALYSIS_RENORM, 0, "Renormalisation of variance", PS_SQR(correction));
     458        return false;
     459    }
     460
     461    psImage *subImage = psImageSubset(variance, psRegionSet(xMin, xMax, yMin, yMax)); // Smaller image
     462    psBinaryOp(subImage, subImage, "*", psScalarAlloc(PS_SQR(correction), PS_TYPE_F32));
     463    psFree(subImage);
    436464
    437465    pmHDU *hdu = pmHDUFromReadout(readout); // HDU for readout
  • trunk/psModules/src/camera/pmFPAMosaic.c

    r25383 r26893  
    626626    bool good = true;                   // Is everything good?
    627627
     628    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
     629    const char *chipName = psMetadataLookupStr(NULL, cell->parent->concepts, "CHIP.NAME"); // Name of chip
     630
    628631    // Offset of the cell on the chip
    629632    int x0Cell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0");
    630633    if (!mdok) {
    631         psError(PS_ERR_UNKNOWN, true, "CELL.X0 for cell is not set.\n");
     634        psError(PS_ERR_UNKNOWN, true, "CELL.X0 for cell %s,%s is not set.\n", chipName, cellName);
    632635        good = false;
    633636    }
    634637    int y0Cell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0");
    635638    if (!mdok) {
    636         psError(PS_ERR_UNKNOWN, true, "CELL.Y0 for cell is not set.\n");
     639        psError(PS_ERR_UNKNOWN, true, "CELL.Y0 for cell %s,%s is not set.\n", chipName, cellName);
    637640        good = false;
    638641    }
    639     psTrace("psModules.camera", 5, "Cell %ld: x0=%d y0=%d\n", index, x0Cell, y0Cell);
     642    psTrace("psModules.camera", 5, "Cell %s,%s (%ld): x0=%d y0=%d\n",
     643            chipName, cellName, index, x0Cell, y0Cell);
    640644
    641645    // Offset of the chip on the FPA
     
    649653        x0Chip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.X0");
    650654        if (!mdok) {
    651             psError(PS_ERR_UNKNOWN, true, "CHIP.X0 for chip is not set.\n");
     655            psError(PS_ERR_UNKNOWN, true, "CHIP.X0 for chip %s is not set.\n", chipName);
    652656            good = false;
    653657        }
    654658        y0Chip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.Y0");
    655659        if (!mdok) {
    656             psError(PS_ERR_UNKNOWN, true, "CHIP.Y0 for chip is not set.\n");
     660            psError(PS_ERR_UNKNOWN, true, "CHIP.Y0 for chip %s is not set.\n", chipName);
    657661            good = false;
    658662        }
     
    662666    xBin->data.S32[index] = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XBIN");
    663667    if (!mdok || xBin->data.S32[index] == 0) {
    664         psError(PS_ERR_UNKNOWN, true, "CELL.XBIN for cell is not set.\n");
     668        psError(PS_ERR_UNKNOWN, true, "CELL.XBIN for cell %s,%s is not set.\n", chipName, cellName);
    665669        return false;
    666670    } else if (xBin->data.S32[index] < *xBinMin) {
     
    669673    yBin->data.S32[index] = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YBIN");
    670674    if (!mdok || yBin->data.S32[index] == 0) {
    671         psError(PS_ERR_UNKNOWN, true, "CELL.YBIN for cell is not set.\n");
     675        psError(PS_ERR_UNKNOWN, true, "CELL.YBIN for cell %s,%s is not set.\n", chipName, cellName);
    672676        return false;
    673677    } else if (yBin->data.S32[index] < *yBinMin) {
     
    678682    int xParityCell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XPARITY");
    679683    if (!mdok || (xParityCell != 1 && xParityCell != -1)) {
    680         psError(PS_ERR_UNKNOWN, true, "CELL.XPARITY for cell is not set.\n");
     684        psError(PS_ERR_UNKNOWN, true, "CELL.XPARITY for cell %s,%s is not set.\n", chipName, cellName);
    681685        return false;
    682686    }
    683687    int yParityCell = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YPARITY");
    684688    if (!mdok || (yParityCell != 1 && yParityCell != -1)) {
    685         psError(PS_ERR_UNKNOWN, true, "CELL.YPARITY for cell is not set.\n");
     689        psError(PS_ERR_UNKNOWN, true, "CELL.YPARITY for cell %s,%s is not set.\n", chipName, cellName);
    686690        return false;
    687691    }
     
    693697        xParityChip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.XPARITY");
    694698        if (!mdok || (xParityChip != 1 && xParityChip != -1)) {
    695             psError(PS_ERR_UNKNOWN, true, "CHIP.XPARITY for chip is not set.\n");
     699            psError(PS_ERR_UNKNOWN, true, "CHIP.XPARITY for chip %s is not set.\n", chipName);
    696700            return false;
    697701        }
    698702        yParityChip = psMetadataLookupS32(&mdok, chip->concepts, "CHIP.YPARITY");
    699703        if (!mdok || (yParityChip != 1 && yParityChip != -1)) {
    700             psError(PS_ERR_UNKNOWN, true, "CHIP.YPARITY for chip is not set.\n");
     704            psError(PS_ERR_UNKNOWN, true, "CHIP.YPARITY for chip %s is not set.\n", chipName);
    701705            return false;
    702706        }
  • trunk/psModules/src/camera/pmFPAfile.c

    r23746 r26893  
    519519    if (!strcasecmp(type, "SUBKERNEL"))     {
    520520        return PM_FPA_FILE_SUBKERNEL;
     521    }
     522    if (!strcasecmp(type, "PATTERN")) {
     523        return PM_FPA_FILE_PATTERN;
    521524    }
    522525
     
    563566      case PM_FPA_FILE_SUBKERNEL:
    564567        return ("SUBKERNEL");
     568      case PM_FPA_FILE_PATTERN:
     569        return "PATTERN";
    565570      default:
    566571        return ("NONE");
  • trunk/psModules/src/camera/pmFPAfile.h

    r25979 r26893  
    4949    PM_FPA_FILE_SUBKERNEL,
    5050    PM_FPA_FILE_SRCTEXT,
     51    PM_FPA_FILE_PATTERN,
    5152} pmFPAfileType;
    5253
  • trunk/psModules/src/camera/pmFPAfileIO.c

    r25979 r26893  
    4343#include "pmFPAConstruct.h"
    4444#include "pmSubtractionIO.h"
     45#include "pmPatternIO.h"
    4546#include "pmConcepts.h"
    4647#include "pmConfigRun.h"
     
    6667
    6768        switch (place) {
    68           case PM_FPA_BEFORE:
     69          case PM_FPA_BEFORE:
    6970            if (!pmFPAfileRead (file, view, config)) {
    7071                psError(PS_ERR_IO, false, "failed READ in FPA_BEFORE block for %s", file->name);
     
    7677            }
    7778            break;
    78           case PM_FPA_AFTER:
    79             if (!pmFPAfileWrite (file, view, config)) {
     79          case PM_FPA_AFTER:
     80            if (!pmFPAfileWrite (file, view, config)) {
    8081                psError(PS_ERR_IO, false, "failed WRITE in FPA_AFTER block for %s", file->name);
    8182                goto failure;
     
    8687            }
    8788            break;
    88           default:
     89          default:
    8990            psAbort("You can't get here");
    9091        }
     
    202203        status = pmSubtractionReadKernels(view, file, config);
    203204        break;
     205      case PM_FPA_FILE_PATTERN:
     206        status = pmPatternRead(view, file, config);
     207        break;
    204208      case PM_FPA_FILE_SX:
    205209      case PM_FPA_FILE_RAW:
     
    273277      case PM_FPA_FILE_VARIANCE:
    274278      case PM_FPA_FILE_FRINGE:
    275       case PM_FPA_FILE_DARK: {
    276           // create FPA structure component based on view
    277           psMetadata *format = file->format; // Camera format configuration
    278           if (!format) {
    279               format = config->format;
    280           }
    281 
    282           pmFPA *nameSource = file->src; // Source of FPA.OBS
    283           if (!nameSource) {
    284               nameSource = file->fpa;
    285           }
    286           bool mdok;                  // Status of MD lookup
    287           const char *fpaObs = psMetadataLookupStr(&mdok, nameSource->concepts, "FPA.OBS"); // Obs. id
    288 
    289           pmFPAAddSourceFromView(file->fpa, fpaObs, view, format);
    290           psTrace ("psModules.camera", 5, "created fpa data elements for %s (%s) (%d:%d:%d)\n",
    291                    file->name, file->name, view->chip, view->cell, view->readout);
    292           break;
    293       }
     279      case PM_FPA_FILE_DARK:
     280      case PM_FPA_FILE_PATTERN:
     281        {
     282            // create FPA structure component based on view
     283            psMetadata *format = file->format; // Camera format configuration
     284            if (!format) {
     285                format = config->format;
     286            }
     287
     288            pmFPA *nameSource = file->src; // Source of FPA.OBS
     289            if (!nameSource) {
     290                nameSource = file->fpa;
     291            }
     292            bool mdok;                  // Status of MD lookup
     293            const char *fpaObs = psMetadataLookupStr(&mdok, nameSource->concepts, "FPA.OBS"); // Obs. id
     294
     295            pmFPAAddSourceFromView(file->fpa, fpaObs, view, format);
     296            psTrace ("psModules.camera", 5, "created fpa data elements for %s (%s) (%d:%d:%d)\n",
     297                     file->name, file->name, view->chip, view->cell, view->readout);
     298            break;
     299        }
    294300      case PM_FPA_FILE_HEADER:
    295301        psAbort ("Create not defined for HEADER");
     
    462468        status = pmSubtractionWriteKernels(view, file, config);
    463469        break;
     470      case PM_FPA_FILE_PATTERN:
     471        status = pmPatternWrite(view, file, config);
     472        break;
    464473      case PM_FPA_FILE_SX:
    465474      case PM_FPA_FILE_RAW:
     
    543552      case PM_FPA_FILE_DARK:
    544553      case PM_FPA_FILE_SUBKERNEL:
     554      case PM_FPA_FILE_PATTERN:
    545555      case PM_FPA_FILE_CMF:
    546556      case PM_FPA_FILE_WCS:
     
    610620        break;
    611621      case PM_FPA_FILE_SUBKERNEL:
     622      case PM_FPA_FILE_PATTERN:
    612623      case PM_FPA_FILE_SX:
    613624      case PM_FPA_FILE_RAW:
     
    771782      case PM_FPA_FILE_DARK:
    772783      case PM_FPA_FILE_SUBKERNEL:
     784      case PM_FPA_FILE_PATTERN:
    773785      case PM_FPA_FILE_CMF:
    774786      case PM_FPA_FILE_WCS:
     
    962974      case PM_FPA_FILE_SUBKERNEL:
    963975        status = pmSubtractionWritePHU(view, file, config);
     976        break;
     977      case PM_FPA_FILE_PATTERN:
     978        status = pmPatternWritePHU(view, file, config);
     979        break;
    964980      case PM_FPA_FILE_CMF:
    965981        status = pmSource_CMF_WritePHU (view, file, config);
  • trunk/psModules/src/config/pmConfigRecipeValue.c

    r26682 r26893  
    6868    }
    6969
     70    menu = chipItem->data.md;   // Menu with values by chip
     71
    7072    pmCell *cell = pmFPAviewThisCell(view, fpa);                                   // Cell of interest
    7173    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
  • trunk/psModules/src/detrend/Makefile.am

    r24891 r26893  
    1717        pmDark.c \
    1818        pmRemnance.c \
    19         pmPattern.c
     19        pmPattern.c \
     20        pmPatternIO.c
    2021
    2122#       pmSkySubtract.c
     
    3536        pmDark.h \
    3637        pmRemnance.h \
    37         pmPattern.h
     38        pmPattern.h \
     39        pmPatternIO.h
    3840
    3941#       pmSkySubtract.h
  • trunk/psModules/src/detrend/pmPattern.c

    r24905 r26893  
    11#include <stdio.h>
     2#include <string.h>
    23#include <pslib.h>
    34
     
    910// Mask a row as bad
    1011static void patternMaskRow(pmReadout *ro, // Readout to mask
    11                            int y,         // Row to mask
     12                           int y,       // Row to mask
    1213                           psImageMaskType bad // Mask value to give
    1314                           )
     
    1718    psAssert(y < image->numRows, "Row not in image");
    1819
    19     int numCols = image->numCols;       // Size of image
     20    int numCols = image->numCols;       // Number of columns
    2021    for (int x = 0; x < numCols; x++) {
    2122        image->data.F32[y][x] = NAN;
     
    2930    return;
    3031}
     32
     33//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     34// Measurement and application
     35//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    3136
    3237bool pmPatternRow(pmReadout *ro, int order, int iter, float rej, float thresh,
     
    6368    float lower = stats->robustMedian - thresh * stats->robustStdev; // Lower bound for data
    6469    float upper = stats->robustMedian + thresh * stats->robustStdev; // Upper bound for data
     70    float background = stats->robustMedian;
    6571    psFree(stats);
    6672    psFree(rng);
     
    7985    psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, order); // Polynomial to fit
    8086    psVector *data = psVectorAlloc(numCols, PS_TYPE_F32); // Data to fit
     87
     88    psImage *corr = psImageAlloc(order + 1, numRows, PS_TYPE_F64); // Corrections applied
     89    psImageInit(corr, NAN);
    8190
    8291    for (int y = 0; y < numRows; y++) {
     
    104113            continue;
    105114        }
     115
     116        poly->coeff[0] -= background;
     117        memcpy(corr->data.F64[y], poly->coeff, (order + 1) * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    106118        psVector *solution = psPolynomial1DEvalVector(poly, indices); // Solution vector
    107119        if (!solution) {
     
    117129        psFree(solution);
    118130    }
     131
     132    psMetadataAddImage(ro->analysis, PS_LIST_TAIL, PM_PATTERN_ROW_CORRECTION, PS_META_REPLACE,
     133                       "Pattern row correction", corr);
     134    psFree(corr);
    119135
    120136    psFree(indices);
     
    126142    return true;
    127143}
     144
     145bool pmPatternRowApply(pmReadout *ro, psImageMaskType maskBad)
     146{
     147    PM_ASSERT_READOUT_NON_NULL(ro, false);
     148    PM_ASSERT_READOUT_IMAGE(ro, false);
     149
     150    bool mdok;                          // Status of MD lookup
     151    psImage *corr = psMetadataLookupPtr(&mdok, ro->analysis, PM_PATTERN_ROW_CORRECTION); // Correction
     152    if (!mdok) {
     153        // No correction to apply
     154        return true;
     155    }
     156
     157    psImage *image = ro->image; // Image of interest
     158    int numCols = image->numCols, numRows = image->numRows; // Size of image
     159
     160    if (corr->numRows != numRows) {
     161        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
     162                "Number of rows of image (%d) does not match number of rows of pattern correction (%d)\n",
     163                numRows, corr->numRows);
     164        return false;
     165    }
     166
     167    int order = corr->numCols - 1;                                        // Polynomial order
     168    psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, order); // Polynomial to apply
     169    psVector *indices = psVectorAlloc(numCols, PS_TYPE_F32); // Indices for polynomial
     170    float norm = 2.0 / (float)numCols;  // Normalisation for indices
     171    for (int x = 0; x < numCols; x++) {
     172        indices->data.F32[x] = x * norm - 1.0;
     173    }
     174
     175    for (int y = 0; y < numRows; y++) {
     176        memcpy(poly->coeff, corr->data.F64[y], (order + 1) * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
     177        psVector *solution = psPolynomial1DEvalVector(poly, indices); // Solution vector
     178        if (!solution) {
     179            psWarning("Unable to evaluate polynomial for row %d", y);
     180            psErrorClear();
     181            patternMaskRow(ro, y, maskBad);
     182            continue;
     183        }
     184
     185        for (int x = 0; x < numCols; x++) {
     186            image->data.F32[y][x] -= solution->data.F32[x];
     187        }
     188        psFree(solution);
     189    }
     190
     191    psFree(poly);
     192    psFree(indices);
     193
     194    return true;
     195}
     196
     197
     198bool pmPatternCell(pmChip *chip, const psVector *tweak, psStatsOptions bgStat, psStatsOptions cellStat,
     199                   psImageMaskType maskVal, psImageMaskType maskBad)
     200{
     201    PS_ASSERT_PTR_NON_NULL(chip, false);
     202    PS_ASSERT_VECTOR_NON_NULL(tweak, false);
     203    PS_ASSERT_VECTOR_SIZE(tweak, chip->cells->n, false);
     204    PS_ASSERT_VECTOR_TYPE(tweak, PS_TYPE_U8, false);
     205
     206    int numCells = tweak->n;            // Number of cells
     207
     208    psVector *mean = psVectorAlloc(numCells, PS_TYPE_F32); // Mean for each cell
     209    psVector *meanMask = psVectorAlloc(numCells, PS_TYPE_VECTOR_MASK); // Mask for means
     210    psVectorInit(mean, NAN);
     211    psVectorInit(meanMask, 0);
     212
     213    // Mask bits
     214    enum {
     215        PM_PATTERN_IGNORE = 0x01,       // Ignore this cell
     216        PM_PATTERN_TWEAK  = 0x02,       // Tweak this cell
     217        PM_PATTERN_ERROR  = 0x04,       // Error in calculating background
     218        PM_PATTERN_ALL    = 0xFF,       // All causes
     219    };
     220
     221    // Count number of cells to tweak
     222    int numTweak = 0;                   // Number of cells to tweak
     223    int numIgnore = 0;                  // Number of cells to ignore
     224    for (int i = 0; i < numCells; i++) {
     225        pmCell *cell = chip->cells->data[i]; // Cell of interest
     226        if (!cell || !cell->data_exists || !cell->process ||
     227            cell->readouts->n == 0 || cell->readouts->n > 1 || !cell->readouts->data[0]) {
     228            numIgnore++;
     229            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PM_PATTERN_IGNORE;
     230            continue;
     231        }
     232        if (tweak->data.U8[i]) {
     233            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PM_PATTERN_TWEAK;
     234            numTweak++;
     235        }
     236    }
     237    if (numTweak == 0) {
     238        // Nothing to do
     239        psFree(mean);
     240        psFree(meanMask);
     241        return true;
     242    }
     243    if (numTweak >= numCells - numIgnore) {
     244        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot pattern-correct all cells within a chip.");
     245        psFree(mean);
     246        psFree(meanMask);
     247        return false;
     248    }
     249
     250    // Measure mean of each cell
     251    // This is not really the perfect thing to do, which would be to take a common mean for the set of cells
     252    // which aren't being tweaked (because some cells will be heavily masked, so shouldn't be weighted the
     253    // same as pure cells), but it's simple and fast.
     254    psStats *bgStats = psStatsAlloc(bgStat); // Statistics on background
     255    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
     256    for (int i = 0; i < numCells; i++) {
     257        if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_IGNORE) {
     258            continue;
     259        }
     260        pmCell *cell = chip->cells->data[i]; // Cell of interest
     261        pmReadout *ro = cell->readouts->data[0]; // Readout of interest
     262
     263        psStatsInit(bgStats);
     264#if 1
     265        if (!psImageBackground(bgStats, NULL, ro->image, ro->mask, maskVal, rng)) {
     266            psWarning("Unable to measure background for cell %d\n", i);
     267            psErrorClear();
     268            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PM_PATTERN_ERROR;
     269            continue;
     270        }
     271#else
     272        if (!psImageStats(bgStats, ro->image, ro->mask, maskVal)) {
     273            psWarning("Unable to measure background for cell %d\n", i);
     274            psErrorClear();
     275            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PM_PATTERN_ERROR;
     276            continue;
     277        }
     278#endif
     279        mean->data.F32[i] = psStatsGetValue(bgStats, bgStat);
     280        if (!isfinite(mean->data.F32[i])) {
     281            psWarning("Non-finite background for cell %d\n", i);
     282            psErrorClear();
     283            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PM_PATTERN_ERROR;
     284            continue;
     285        }
     286    }
     287    psFree(bgStats);
     288    psFree(rng);
     289
     290    psStats *cellStats = psStatsAlloc(cellStat); // Statistics on cells
     291    if (!psVectorStats(cellStats, mean, NULL, meanMask, PM_PATTERN_ALL)) {
     292        psError(PS_ERR_UNKNOWN, false, "Unable to calculate mean cell background.");
     293        psFree(mean);
     294        psFree(meanMask);
     295        psFree(cellStats);
     296        return false;
     297    }
     298
     299    float background = psStatsGetValue(cellStats, cellStat); // Background value for chip
     300    psFree(cellStats);
     301    if (!isfinite(background)) {
     302        psError(PS_ERR_UNKNOWN, false, "Non-finite mean cell background.");
     303        psFree(mean);
     304        psFree(meanMask);
     305        return false;
     306    }
     307
     308    psLogMsg("psModules.detrend", PS_LOG_DETAIL, "Mean chip background is %f", background);
     309
     310    for (int i = 0; i < numCells; i++) {
     311        if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_IGNORE) {
     312            continue;
     313        }
     314        if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_TWEAK)) {
     315            continue;
     316        }
     317        pmCell *cell = chip->cells->data[i]; // Cell of interest
     318        pmReadout *ro = cell->readouts->data[0]; // Readout of interest
     319        if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_ERROR) {
     320            psImageInit(ro->image, NAN);
     321            psBinaryOp(ro->mask, ro->mask, "|", psScalarAlloc(maskBad, PS_TYPE_IMAGE_MASK));
     322            psMetadataAddF32(ro->analysis, PS_LIST_TAIL, PM_PATTERN_CELL_CORRECTION, PS_META_REPLACE,
     323                             "Pattern cell correction solution", NAN);
     324            continue;
     325        }
     326        float correction = background - mean->data.F32[i]; // Correction to apply
     327        const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
     328        psLogMsg("psModules.detrend", PS_LOG_DETAIL, "Correcting background of cell %s by %f",
     329                 cellName, correction);
     330        psBinaryOp(ro->image, ro->image, "+", psScalarAlloc(correction, PS_TYPE_F32));
     331        psMetadataAddF32(ro->analysis, PS_LIST_TAIL, PM_PATTERN_CELL_CORRECTION, PS_META_REPLACE,
     332                         "Pattern cell correction solution", correction);
     333    }
     334
     335    psFree(mean);
     336    psFree(meanMask);
     337
     338    return true;
     339}
     340
     341bool pmPatternCellApply(pmReadout *ro, psImageMaskType maskBad)
     342{
     343    PM_ASSERT_READOUT_NON_NULL(ro, false);
     344    PM_ASSERT_READOUT_IMAGE(ro, false);
     345
     346    bool mdok;                          // Status of MD lookup
     347    float corr = psMetadataLookupF32(&mdok, ro->analysis, PM_PATTERN_CELL_CORRECTION); // Correction to apply
     348    if (!mdok) {
     349        // No correction to apply
     350        return true;
     351    }
     352
     353    psImage *image = ro->image, *mask = ro->mask; // Image and mask of interest
     354    int numCols = image->numCols, numRows = image->numRows; // Size of image
     355
     356    if (!isfinite(corr)) {
     357        for (int y = 0; y < numRows; y++) {
     358            for (int x = 0; x < numCols; x++) {
     359                image->data.F32[y][x] = NAN;
     360            }
     361        }
     362        if (mask) {
     363            for (int y = 0; y < numRows; y++) {
     364                for (int x = 0; x < numCols; x++) {
     365                    mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= maskBad;
     366                }
     367            }
     368        }
     369    } else {
     370        for (int y = 0; y < numRows; y++) {
     371            for (int x = 0; x < numCols; x++) {
     372                image->data.F32[y][x] += corr;
     373            }
     374        }
     375    }
     376
     377    return true;
     378}
     379
     380
  • trunk/psModules/src/detrend/pmPattern.h

    r24903 r26893  
    1818/// @{
    1919
     20#define PM_PATTERN_ROW_CORRECTION "PATTERN.ROW.CORRECTION" // Pattern row correction on analysis metadata
     21#define PM_PATTERN_CELL_CORRECTION "PATTERN.CELL.CORRECTION" // Pattern cell correction on analysis metadata
     22
    2023/// Fit and remove pattern noise over rows
    2124bool pmPatternRow(
     
    3134    );
    3235
     36/// Apply previously measured row pattern correction
     37bool pmPatternRowApply(pmReadout *ro,   ///< Readout to correct
     38                       psImageMaskType maskBad ///< Mask value to give bad pixels
     39                       );
     40
     41/// Fix the background on cells known to be troublesome
     42bool pmPatternCell(
     43    pmChip *chip,                       ///< Chip to correct
     44    const psVector *tweak,              ///< U8 vector indicating whether to tweak the corresponding cell
     45    psStatsOptions bgStat,              ///< Statistic to use for background measurement
     46    psStatsOptions cellStat,            ///< Statistic to use for combination of cell background measurements
     47    psImageMaskType maskVal,            ///< Mask value to use
     48    psImageMaskType maskBad             ///< Mask value to give bad pixels
     49    );
     50
     51/// Apply previously measured cell pattern correction
     52bool pmPatternCellApply(pmReadout *ro,          ///< Readout to correct
     53                        psImageMaskType maskBad ///< Mask value to give bad pixels
     54                        );
     55
     56
    3357/// @}
    3458#endif
  • trunk/psModules/src/imcombine/Makefile.am

    r23242 r26893  
    1313        pmSubtractionIO.c       \
    1414        pmSubtractionKernels.c  \
     15        pmSubtractionHermitian.c        \
     16        pmSubtractionDeconvolve.c       \
    1517        pmSubtractionMask.c     \
    1618        pmSubtractionMatch.c    \
     
    3234        pmSubtractionIO.h       \
    3335        pmSubtractionKernels.h  \
     36        pmSubtractionHermitian.h        \
     37        pmSubtractionDeconvolve.h       \
    3438        pmSubtractionMask.h     \
    3539        pmSubtractionMatch.h    \
  • trunk/psModules/src/imcombine/pmImageCombine.c

    r23989 r26893  
    118118            psImage *mask = masks->data[i]; // Mask of interest
    119119            pixelMasks->data.PS_TYPE_VECTOR_MASK_DATA[i] = (mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal);
    120         }       
    121         // Set the pixel error data, if necessary
     120        }
     121        // Set the pixel error data, if necessary
    122122        if (errors) {
    123123            psImage *error = errors->data[i]; // Error image of interest
     
    132132        // Combine all the pixels, using the specified stat.
    133133        if (!psVectorStats(stats, pixelData, pixelErrors, pixelMasks, 0xff)) {
    134             psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
    135             return false;
    136         }
    137         if (isnan(stats->sampleMean)) {
     134            psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
     135            return false;
     136        }
     137        if (isnan(stats->sampleMean)) {
    138138            combine->data.F32[y][x] = NAN;
    139139            psFree(buffer);
     
    141141        }
    142142        float combinedPixel = stats->sampleMean; // Value of the combination
    143        
     143
    144144        if (iter == 0) {
    145145            // Save the value produced with no rejection, since it may be useful later
     
    369369    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
    370370    if (!psVectorStats(stats, pixels, NULL, mask, 1)) {
    371         psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
     371        psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
    372372    }
    373373    float median = stats->sampleMedian;
     
    640640                                              (psPlaneTransform * )outToIn->data[otherImg],
    641641                                              outCoords);
    642                         psS32 xPix = (int)(inCoords->x + 0.5);
    643                         psS32 yPix = (int)(inCoords->y + 0.5);
     642                        psS32 xPix = (int)(inCoords->x - 0.5);
     643                        psS32 yPix = (int)(inCoords->y - 0.5);
    644644                        if ((xPix >= 0) && (xPix <= ((psImage*)(images->data[otherImg]))->numCols - 1) &&
    645645                                (yPix >= 0) && (yPix <= ((psImage*)(images->data[otherImg]))->numRows - 1)) {
  • trunk/psModules/src/imcombine/pmPSFEnvelope.c

    r25754 r26893  
    6565                     int radius,        // Radius of each PSF
    6666                     const char *modelName,// Name of PSF model to use
    67                      int xOrder, int yOrder // Order for PSF variation fit
     67                     int xOrder, int yOrder, // Order for PSF variation fit
     68                     psImageMaskType maskVal
    6869                     )
    6970{
     
    154155            pmModelClassSetLimits(PM_MODEL_LIMITS_IGNORE);
    155156            pmModel *model = pmModelFromPSFforXY(psf, numCols / 2.0, numRows / 2.0, PEAK_FLUX); // Test model
    156             model->modelSetLimits(PM_MODEL_LIMITS_STRICT);
    157             for (int j = 0; j < model->params->n && goodPSF; j++) {
    158                 if (!model->modelLimits(PS_MINIMIZE_PARAM_MIN, j, model->params->data.F32, NULL) ||
    159                     !model->modelLimits(PS_MINIMIZE_PARAM_MAX, j, model->params->data.F32, NULL)) {
    160                     goodPSF = false;
     157            if (!model) {
     158                goodPSF = false;
     159            } else {
     160                model->modelSetLimits(PM_MODEL_LIMITS_STRICT);
     161                for (int j = 0; j < model->params->n && goodPSF; j++) {
     162                    if (!model->modelLimits(PS_MINIMIZE_PARAM_MIN, j, model->params->data.F32, NULL) ||
     163                        !model->modelLimits(PS_MINIMIZE_PARAM_MAX, j, model->params->data.F32, NULL)) {
     164                        goodPSF = false;
     165                    }
    161166                }
    162             }
    163             psFree(model);
     167                psFree(model);
     168            }
    164169            if (!goodPSF) {
    165170                psWarning("PSF %d is bad --- not including in envelope calculation.", i);
     
    360365
    361366        // measure the source moments: tophat windowing, no pixel S/N cutoff
    362         if (!pmSourceMoments(source, maxRadius, 0.0, 1.0)) {
     367        // XXX probably should be passing the maskVal to this function so we can pass it along here...
     368        if (!pmSourceMoments(source, maxRadius, 0.0, 1.0, maskVal)) {
    363369            // Can't do anything about it; limp along as best we can
    364370            psErrorClear();
  • trunk/psModules/src/imcombine/pmPSFEnvelope.h

    r15837 r26893  
    1717                     int radius,        // Radius of each PSF
    1818                     const char *modelName, // Name of PSF model to use
    19                      int xOrder, int yOrder // Order for PSF variation
     19                     int xOrder, int yOrder, // Order for PSF variation
     20                     psImageMaskType maskVal
    2021    );
    2122
  • trunk/psModules/src/imcombine/pmStackReject.c

    r25468 r26893  
    3535{
    3636    int size = kernels->size;           // Half-size of convolution kernel
    37     psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, numCols, numRows,
    38                                                               xMin + size + 1, yMin + size + 1); // Polynomial
     37    int x = PS_MIN(xMin + size + 1, kernels->xMax); // x coordinate of interest
     38    int y = PS_MIN(yMin + size + 1, kernels->yMax); // y coordinate of interest
     39
     40    psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, x, y); // Polynomial
    3941    int box = p_pmSubtractionBadRadius(NULL, kernels, polyValues, false, poorFrac); // Radius of bad box
    4042    psTrace("psModules.imcombine", 10, "Growing by %d", box);
     
    150152    pmReadout *inRO = pmReadoutAlloc(NULL); // Readout with input image
    151153    inRO->image = image;
     154    convRO->image = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
    152155    for (int i = 0; i < numRegions; i++) {
    153156        psRegion *region = subRegions->data[i]; // Region of interest
    154157        pmSubtractionKernels *kernels = subKernels->data[i]; // Kernel of interest
    155         if (!pmSubtractionConvolve(NULL, convRO, NULL, inRO, NULL, stride, 0, 0, 1.0, 0.0,
     158        if (!pmSubtractionConvolve(NULL, convRO, NULL, inRO, NULL, stride, 0, 0, 1.0, 0.0, 0.0,
    156159                                   region, kernels, false, true)) {
    157160            psError(PS_ERR_UNKNOWN, false, "Unable to convolve mask image in region %d.", i);
     
    165168
    166169        // Image of the kernel at the centre of the region
    167         float xNorm = (region->x0 + 0.5 * (region->x1 - region->x0) - kernels->numCols/2.0) /
    168             (float)kernels->numCols;
    169         float yNorm = (region->y0 + 0.5 * (region->y1 - region->y0) - kernels->numRows/2.0) /
    170             (float)kernels->numRows;
    171         psImage *kernel = pmSubtractionKernelImage(kernels, xNorm, yNorm, false);
     170        psImage *kernel = pmSubtractionKernelImage(kernels, 0.5, 0.5, false);
    172171        if (!kernel) {
    173172            psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
  • trunk/psModules/src/imcombine/pmSubtraction.c

    r26584 r26893  
    2121#include "pmSubtractionStamps.h"
    2222#include "pmSubtractionEquation.h"
     23#include "pmSubtractionVisual.h"
    2324#include "pmSubtractionThreads.h"
    2425
     
    6364}
    6465
    65 // Contribute to an image of the solved kernel component for ISIS
    66 static void solvedKernelISIS(psKernel *kernel, // Kernel, updated
    67                              const pmSubtractionKernels *kernels, // Kernel basis functions
    68                              float value,                         // Normalisation value for basis function
    69                              int index                  // Index of basis function of interest
     66// Contribute to an image of the solved kernel component using the preCalculated image
     67static void solvedKernelPreCalc(psKernel *kernel, // Kernel, updated
     68                                const pmSubtractionKernels *kernels, // Kernel basis functions
     69                                float value,                         // Normalisation value for basis function
     70                                int index                  // Index of basis function of interest
    7071    )
    7172{
    7273    int size = kernels->size;           // Kernel half-size
    73     psArray *preCalc = kernels->preCalc->data[index]; // Precalculated values
     74    pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[index]; // Precalculated values
    7475#if 0
    75     psVector *xKernel = preCalc->data[0]; // Kernel in x
    76     psVector *yKernel = preCalc->data[1]; // Kernel in y
    7776    // Iterating over the kernel
    7877    for (int y = 0, v = -size; v <= size; y++, v++) {
    79         float yValue = value * yKernel->data.F32[y];
     78        float yValue = value * preCalc->yKernel->data.F32[y];
    8079        for (int x = 0, u = -size; u <= size; x++, u++) {
    81             kernel->kernel[v][u] +=  yValue * xKernel->data.F32[x];
     80            kernel->kernel[v][u] +=  yValue * preCalc->xKernel->data.F32[x];
    8281        }
    8382    }
     
    8786    }
    8887#else
    89     psKernel *k = preCalc->data[2]; // Kernel image
    9088    for (int v = -size; v <= size; v++) {
    9189        for (int u = -size; u <= size; u++) {
    92             kernel->kernel[v][u] +=  value * k->kernel[v][u];
     90            kernel->kernel[v][u] +=  value * preCalc->kernel->kernel[v][u];
    9391        }
    9492    }
     
    119117    for (int i = 0; i < numKernels; i++) {
    120118        double value = p_pmSubtractionSolutionCoeff(kernels, polyValues, i, wantDual); // Polynomial value
     119        if (wantDual) {
     120            // The model is built with the dual convolution terms added, so to produce zero residual the
     121            // equation results in negative coefficients which we must undo.
     122            value *= -1.0;
     123        }
    121124
    122125        switch (kernels->type) {
     
    149152          case PM_SUBTRACTION_KERNEL_GUNK: {
    150153              if (i < kernels->inner) {
    151                   solvedKernelISIS(kernel, kernels, value, i);
     154                  solvedKernelPreCalc(kernel, kernels, value, i);
    152155              } else {
    153156                  // Using delta function
     
    159162              break;
    160163          }
    161           case PM_SUBTRACTION_KERNEL_ISIS: {
    162               solvedKernelISIS(kernel, kernels, value, i);
     164          case PM_SUBTRACTION_KERNEL_ISIS:
     165          case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     166          case PM_SUBTRACTION_KERNEL_HERM:
     167          case PM_SUBTRACTION_KERNEL_DECONV_HERM: {
     168              solvedKernelPreCalc(kernel, kernels, value, i);
    163169              break;
    164170          }
    165171          case PM_SUBTRACTION_KERNEL_RINGS: {
    166               psArray *preCalc = kernels->preCalc->data[i]; // Precalculated data
    167               psVector *uCoords = preCalc->data[0]; // u coordinates
    168               psVector *vCoords = preCalc->data[1]; // v coordinates
    169               psVector *poly = preCalc->data[2]; // Polynomial values
    170               int num = uCoords->n;     // Number of pixels
     172              pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[i]; // Precalculated kernels
     173              int num = preCalc->uCoords->n;     // Number of pixels
    171174
    172175              for (int j = 0; j < num; j++) {
    173                   int u = uCoords->data.S32[j], v = vCoords->data.S32[j]; // Kernel coordinates
    174                   kernel->kernel[v][u] += poly->data.F32[j] * value;
     176                  int u = preCalc->uCoords->data.S32[j];
     177                  int v = preCalc->vCoords->data.S32[j]; // Kernel coordinates
     178                  kernel->kernel[v][u] += preCalc->poly->data.F32[j] * value;
    175179              }
    176180              // Photometric scaling is built into the kernel --- no subtraction!
     
    419423                    *target |= maskBad;
    420424                } else if (*source & subConvPoor) {
     425                    *target &= ~maskBad;
    421426                    *target |= maskPoor;
     427                } else {
     428                    *target &= ~maskBad & ~maskPoor;
    422429                }
    423430            }
     
    454461#endif
    455462
    456 // Convolve a stamp using an ISIS kernel basis function
    457 static psKernel *convolveStampISIS(const psKernel *image, // Image to convolve
    458                                    const pmSubtractionKernels *kernels, // Kernel basis functions
    459                                    int index,                            // Index of basis function of interest
    460                                    int footprint                         // Half-size of stamp
     463// Convolve a stamp using a pre-calculated kernel basis function
     464static psKernel *convolveStampPreCalc(const psKernel *image, // Image to convolve
     465                                      const pmSubtractionKernels *kernels, // Kernel basis functions
     466                                      int index,                            // Index of basis function of interest
     467                                      int footprint                         // Half-size of stamp
    461468    )
    462469{
    463     psArray *preCalc = kernels->preCalc->data[index]; // Precalculated data
     470    pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[index]; // Precalculated data
    464471#if 0
    465472    // Convolving using separable convolution
    466     psVector *xKernel = preCalc->data[0]; // Kernel in x
    467     psVector *yKernel = preCalc->data[1]; // Kernel in y
    468473    int size = kernels->size;     // Size of kernel
    469474
     
    477482            float value = 0.0;    // Value of convolved pixel
    478483            int uMin = x - size, uMax = x + size; // Range for u
    479             psF32 *xKernelData = &xKernel->data.F32[xKernel->n - 1]; // Kernel values
     484            psF32 *xKernelData = &preCalc->xKernel->data.F32[xKernel->n - 1]; // Kernel values
    480485            psF32 *imageData = &image->kernel[y][uMin]; // Image values
    481486            for (int u = uMin; u <= uMax; u++, xKernelData--, imageData++) {
     
    492497            float value = 0.0;    // Value of convolved pixel
    493498            int vMin = y - size, vMax = y + size; // Range for v
    494             psF32 *yKernelData = &yKernel->data.F32[yKernel->n - 1]; // Kernel values
     499            psF32 *yKernelData = &preCalc->yKernel->data.F32[yKernel->n - 1]; // Kernel values
    495500            psF32 *imageData = &temp->kernel[x][vMin]; // Image values; NOTE: wrong way!
    496501            for (int v = vMin; v <= vMax; v++, yKernelData--, imageData++) {
     
    509514#else
    510515    // Convolving using precalculated kernel
    511     return p_pmSubtractionConvolveStampPrecalc(image, preCalc->data[2]);
     516    return p_pmSubtractionConvolveStampPrecalc(image, preCalc->kernel);
    512517#endif
    513518}
     
    525530    int x0 = - image->xMin, y0 = - image->yMin; // Position of centre of convolved image
    526531    psKernel *convolved = psKernelAllocFromImage(conv, x0, y0); // Kernel version
     532
     533    // pmSubtractionVisualShowSubtraction(image->image, kernel->image, conv);
     534
    527535    psFree(conv);
    528536    return convolved;
     
    569577}
    570578
     579void p_pmSubtractionPolynomialNormCoords(float *xOut, float *yOut, float xIn, float yIn,
     580                                         int xMin, int xMax, int yMin, int yMax)
     581{
     582    float xNormSize = xMax - xMin, yNormSize = yMax - yMin; // Size to use for normalisation
     583    *xOut = 2.0 * (float)(xIn - xMin - xNormSize/2.0) / xNormSize;
     584    *yOut = 2.0 * (float)(yIn - yMin - yNormSize/2.0) / yNormSize;
     585    return;
     586}
     587
    571588psImage *p_pmSubtractionPolynomialFromCoords(psImage *output, const pmSubtractionKernels *kernels,
    572                                              int numCols, int numRows, int x, int y)
     589                                             int x, int y)
    573590{
    574591    assert(kernels);
    575     assert(numCols > 0 && numRows > 0);
    576 
    577     // Size to use when calculating normalised coordinates (different from actual size when convolving
    578     // subimage)
    579     int xNormSize = (kernels->numCols > 0 ? kernels->numCols : numCols);
    580     int yNormSize = (kernels->numRows > 0 ? kernels->numRows : numRows);
    581 
    582     // Normalised coordinates
    583     float yNorm = 2.0 * (float)(y - yNormSize/2.0) / (float)yNormSize;
    584     float xNorm = 2.0 * (float)(x - xNormSize/2.0) / (float)xNormSize;
    585 
     592
     593    float xNorm, yNorm;                 // Normalised coordinates
     594    p_pmSubtractionPolynomialNormCoords(&xNorm, &yNorm, x, y,
     595                                        kernels->xMin, kernels->xMax, kernels->yMin, kernels->yMax);
    586596    return p_pmSubtractionPolynomial(output, kernels->spatialOrder, xNorm, yNorm);
    587597}
     
    664674          if (index < kernels->inner) {
    665675              // Photometric scaling is already built in to the precalculated kernel
    666               return convolveStampISIS(image, kernels, index, footprint);
     676              return convolveStampPreCalc(image, kernels, index, footprint);
    667677          }
    668678          // Using delta function
     
    673683          return convolved;
    674684      }
    675       case PM_SUBTRACTION_KERNEL_ISIS: {
    676           return convolveStampISIS(image, kernels, index, footprint);
    677       }
     685      case PM_SUBTRACTION_KERNEL_ISIS:
     686      case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     687      case PM_SUBTRACTION_KERNEL_HERM:
     688      case PM_SUBTRACTION_KERNEL_DECONV_HERM: {
     689            return convolveStampPreCalc(image, kernels, index, footprint);
     690        }
    678691      case PM_SUBTRACTION_KERNEL_RINGS: {
    679           psKernel *convolved = psKernelAlloc(-footprint, footprint,
    680                                               -footprint, footprint); // Convolved image
    681           psArray *preCalc = kernels->preCalc->data[index]; // Precalculated data
    682           psVector *uCoords = preCalc->data[0]; // u coordinates
    683           psVector *vCoords = preCalc->data[1]; // v coordinates
    684           psVector *poly = preCalc->data[2]; // Polynomial values
    685           int num = uCoords->n;         // Number of pixels
    686           psS32 *uData = uCoords->data.S32, *vData = vCoords->data.S32; // Dereference u,v coordinates
    687           psF32 *polyData = poly->data.F32; // Dereference polynomial values
     692          psKernel *convolved = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Convolved image
     693          pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[index]; // Precalculated data
     694
     695          int num = preCalc->uCoords->n;         // Number of pixels
     696          psS32 *uData = preCalc->uCoords->data.S32; // Dereference v coordinate
     697          psS32 *vData = preCalc->vCoords->data.S32; // Dereference u coordinate
     698          psF32 *polyData = preCalc->poly->data.F32; // Dereference polynomial values
    688699          psF32 **imageData = image->kernel;  // Dereference image
    689700          psF32 **convData = convolved->kernel; // Dereference convolved image
     
    765776
    766777
    767 
    768 
    769778int pmSubtractionRejectStamps(pmSubtractionKernels *kernels, pmSubtractionStampList *stamps,
    770779                              const psVector *deviations, psImage *subMask, float sigmaRej)
     
    860869    int numGood = 0;                    // Number of good stamps
    861870    double newMean = 0.0;               // New mean
     871    psString log = NULL;                // Log message
     872    psStringAppend(&log, "Rejecting stamps, mean = %f, threshold = %f\n", mean, limit);
    862873    for (int i = 0; i < stamps->num; i++) {
    863874        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     
    872883                // Mask out the stamp in the image so you it's not found again
    873884                psTrace("psModules.imcombine", 3, "Rejecting stamp %d (%d,%d)\n", i,
    874                         (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
     885                        (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
     886                psStringAppend(&log, "Stamp %d (%d,%d): %f\n", i,
     887                               (int)(stamp->x - 0.5), (int)(stamp->y - 0.5),
     888                               fabsf(deviations->data.F32[i] - mean));
    875889                numRejected++;
    876890                for (int y = stamp->y - footprint; y <= stamp->y + footprint; y++) {
     
    895909                psFree(stamp->weight);
    896910                stamp->image1 = stamp->image2 = stamp->weight = NULL;
    897                 psFree(stamp->matrix1);
    898                 psFree(stamp->matrix2);
    899                 psFree(stamp->matrixX);
    900                 stamp->matrix1 = stamp->matrix2 = stamp->matrixX = NULL;
    901                 psFree(stamp->vector1);
    902                 psFree(stamp->vector2);
    903                 stamp->vector1 = stamp->vector2 = NULL;
     911                psFree(stamp->matrix);
     912                stamp->matrix = NULL;
     913                psFree(stamp->vector);
     914                stamp->vector = NULL;
    904915            } else {
    905916                numGood++;
     
    910921    }
    911922    newMean /= numGood;
     923
     924    if (numRejected == 0) {
     925        psStringAppend(&log, "<none>\n");
     926    }
     927    psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "%s", log);
     928    psFree(log);
    912929
    913930    if (ds9) {
     
    9951012    psVector *backup = psVectorCopy(NULL, solution, PS_TYPE_F64);  // Backup version
    9961013
    997     int num = wantDual ? solution->n - 1 : solution->n; // Number of kernel basis functions
     1014    int num = kernels->num;            // Number of kernel basis functions
    9981015
    9991016    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, x, y); // Solved polynomial
     
    10601077    // Only generate polynomial values every kernel footprint, since we have already assumed
    10611078    // (with the stamps) that it does not vary rapidly on this scale.
    1062     psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, numCols, numRows,
    1063                                                               xMin + x0 + size + 1,
    1064                                                               yMin + y0 + size + 1);
     1079    psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, xMin + x0 + size + 1,
     1080                                                              yMin + y0 + size + 1);        // Polynomial
    10651081    float background = doBG ? p_pmSubtractionSolutionBackground(kernels, polyValues) : 0.0; // Background term
    10661082
     
    11371153bool pmSubtractionConvolve(pmReadout *out1, pmReadout *out2, const pmReadout *ro1, const pmReadout *ro2,
    11381154                           psImage *subMask, int stride, psImageMaskType maskBad, psImageMaskType maskPoor,
    1139                            float poorFrac, float kernelError, const psRegion *region,
     1155                           float poorFrac, float kernelError, float covarFrac, const psRegion *region,
    11401156                           const pmSubtractionKernels *kernels, bool doBG, bool useFFT)
    11411157{
     
    11461162        PM_ASSERT_READOUT_NON_NULL(ro1, false);
    11471163        PM_ASSERT_READOUT_IMAGE(ro1, false);
     1164        PM_ASSERT_READOUT_IMAGE(out1, false);
    11481165        numCols = ro1->image->numCols;
    11491166        numRows = ro1->image->numRows;
     
    11551172        PM_ASSERT_READOUT_NON_NULL(ro2, false);
    11561173        PM_ASSERT_READOUT_IMAGE(ro2, false);
     1174        PM_ASSERT_READOUT_IMAGE(out2, false);
    11571175        if (numCols == 0 && numRows == 0) {
    11581176            numCols = ro2->image->numCols;
     
    11771195    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(kernelError, 0.0, false);
    11781196    PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(kernelError, 1.0, false);
     1197    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(covarFrac, 0.0, false);
     1198    PS_ASSERT_FLOAT_LESS_THAN(covarFrac, 1.0, false);
    11791199    if (region && psRegionIsNaN(*region)) {
    11801200        psString string = psRegionToString(*region);
     
    11881208    bool threaded = pmSubtractionThreaded(); // Running threaded?
    11891209
    1190     // Outputs
    1191     if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1192         if (!out1->image) {
    1193             out1->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1194         }
    1195         if (ro1->variance) {
    1196             if (!out1->variance) {
    1197                 out1->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1198             }
    1199             psImageInit(out1->variance, 0.0);
    1200         }
    1201     }
    1202     if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1203         if (!out2->image) {
    1204             out2->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1205         }
    1206         if (ro2->variance) {
    1207             if (!out2->variance) {
    1208                 out2->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1209             }
    1210             psImageInit(out2->variance, 0.0);
    1211         }
    1212     }
    12131210    psImage *convMask = NULL;           // Convolved mask image (common to inputs 1 and 2)
    12141211    if (subMask) {
    12151212        if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1216             if (!out1->mask) {
    1217                 out1->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    1218             }
    1219             psImageInit(out1->mask, 0);
    12201213            convMask = out1->mask;
    12211214        }
    12221215        if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1223             if (!out2->mask) {
    1224                 out2->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    1225             }
    1226             psImageInit(out2->mask, 0);
    12271216            if (!convMask) {
    12281217                convMask = out2->mask;
     
    12441233
    12451234    // Get region for convolution: [xMin:xMax,yMin:yMax]
    1246     int xMin = size, xMax = numCols - size;
    1247     int yMin = size, yMax = numRows - size;
     1235    int xMin = kernels->xMin + size, xMax = kernels->xMax - size;
     1236    int yMin = kernels->yMin + size, yMax = kernels->yMax - size;
    12481237    if (region) {
    12491238        xMin = PS_MAX(region->x0, xMin);
     
    13361325
    13371326    // Calculate covariances
    1338     // This can take a while, so we only do it for a single instance
    1339     // XXX psImageCovarianceCalculate could be multithreaded
     1327    // This can be fairly involved, so we only do it for a single instance
     1328    // Enable threads for covariance calculation, since we're not threading on top of it.
     1329    oldThreads = psImageCovarianceSetThreads(true);
    13401330    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    13411331        psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0, false); // Convolution kernel
     1332        psKernelTruncate(kernel, covarFrac);
    13421333        out1->covariance = psImageCovarianceCalculate(kernel, ro1->covariance);
    13431334        psFree(kernel);
     
    13461337        psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0,
    13471338                                               kernels->mode == PM_SUBTRACTION_MODE_DUAL); // Conv. kernel
     1339        psKernelTruncate(kernel, covarFrac);
    13481340        out2->covariance = psImageCovarianceCalculate(kernel, ro2->covariance);
    13491341        psFree(kernel);
    13501342    }
     1343    psImageCovarianceSetThreads(oldThreads);
    13511344
    13521345    // Copy anything that wasn't convolved
     
    13881381    }
    13891382
    1390 
    13911383    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolve image: %f sec",
    13921384             psTimerClear("pmSubtractionConvolve"));
  • trunk/psModules/src/imcombine/pmSubtraction.h

    r25279 r26893  
    113113                           psImageMaskType maskPoor, ///< Mask value to give poor pixels
    114114                           float poorFrac, ///< Fraction for "poor"
    115                            float sysError, ///< Relative systematic error
     115                           float kernelError, ///< Relative systematic error in kernel
     116                           float covarFrac,  ///< Truncation fraction for kernel before covariance calculation
    116117                           const psRegion *region, ///< Region to convolve (or NULL)
    117118                           const pmSubtractionKernels *kernels, ///< Kernel parameters
     
    127128    );
    128129
     130/// Return normalised coordinates
     131void p_pmSubtractionPolynomialNormCoords(
     132    float *xOut, float *yOut,           ///< Normalised coordinates, returned
     133    float xIn, float yIn,               ///< Input coordinates
     134    int xMin, int xMax, int yMin, int yMax ///< Bounds of validity
     135    );
     136
    129137/// Given (normalised) coordinates (x,y), generate a matrix where the elements (i,j) are x^i * y^j
    130138psImage *p_pmSubtractionPolynomial(psImage *output, ///< Output matrix, or NULL
     
    138146psImage *p_pmSubtractionPolynomialFromCoords(psImage *output, ///< Output matrix, or NULL
    139147                                             const pmSubtractionKernels *kernels, ///< Kernel parameters
    140                                              int numCols, int numRows, ///< Size of image of interest
    141148                                             int x, int y ///< Position of interest
    142149    );
  • trunk/psModules/src/imcombine/pmSubtractionAnalysis.c

    r25999 r26893  
    117117    }
    118118
     119    // sample difference images
     120    {
     121        psMetadataAddArray(analysis, PS_LIST_TAIL, "SUBTRACTION.SAMPLE.STAMP.SET", PS_META_DUPLICATE_OK, "Sample Difference Stamps", kernels->sampleStamps);
     122    }
    119123
    120124#ifdef TESTING
     
    269273    // Difference in background
    270274    {
    271         psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, numCols, numRows,
    272                                                                   numCols / 2.0, numRows / 2.0); // Polynomial
     275        psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, 0.0, 0.0); // Polynomial
    273276        float bg = p_pmSubtractionSolutionBackground(kernels, polyValues); // Background difference
    274277
     
    295298        psMetadataAddF32(header, PS_LIST_TAIL, PM_SUBTRACTION_ANALYSIS_DEV_RMS, 0, "RMS stamp deviation",
    296299                         kernels->rms);
     300
     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);
    297314    }
    298315
  • trunk/psModules/src/imcombine/pmSubtractionAnalysis.h

    r25060 r26893  
    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
     32
    2633// Derive QA information about the subtraction
    2734bool pmSubtractionAnalysis(
  • trunk/psModules/src/imcombine/pmSubtractionEquation.c

    r26035 r26893  
    1717//#define TESTING                         // TESTING output for debugging; may not work with threads!
    1818
    19 #define USE_WEIGHT                      // Include weight (1/variance) in equation?
     19//#define USE_WEIGHT                      // Include weight (1/variance) in equation?
     20//#define USE_WINDOW                      // Include weight (1/variance) in equation?
    2021
    2122
     
    2728static bool calculateMatrixVector(psImage *matrix, // Least-squares matrix, updated
    2829                                  psVector *vector, // Least-squares vector, updated
     30                                  double *norm,     // Normalisation, updated
    2931                                  const psKernel *input, // Input image (target)
    3032                                  const psKernel *reference, // Reference image (convolution source)
    3133                                  const psKernel *weight,  // Weight image
     34                                  const psKernel *window,  // Window image
    3235                                  const psArray *convolutions,         // Convolutions for each kernel
    3336                                  const pmSubtractionKernels *kernels, // Kernels
    3437                                  const psImage *polyValues, // Spatial polynomial values
    35                                   int footprint // (Half-)Size of stamp
     38                                  int footprint, // (Half-)Size of stamp
     39                                  int normWindow, // Window (half-)size for normalisation measurement
     40                                  const pmSubtractionEquationCalculationMode mode
    3641                                  )
    3742{
     
    5156
    5257    // Evaluate polynomial-polynomial terms
     58    // XXX we can skip this if we are not calculating kernel coeffs
    5359    for (int iyOrder = 0, iIndex = 0; iyOrder <= spatialOrder; iyOrder++) {
    5460        for (int ixOrder = 0; ixOrder <= spatialOrder - iyOrder; ixOrder++, iIndex++) {
     
    6470    }
    6571
     72    // initialize the matrix and vector for NOP on all coeffs.  we only fill in the coeffs we
     73    // choose to calculate
     74    psImageInit(matrix, 0.0);
     75    psVectorInit(vector, 1.0);
     76    for (int i = 0; i < matrix->numCols; i++) {
     77        matrix->data.F64[i][i] = 1.0;
     78    }
     79
     80    // the order of the elements in the matrix and vector is:
     81    // [kernel 0, x^0 y^0][kernel 1 x^0 y^0]...[kernel N, x^0 y^0]
     82    // [kernel 0, x^1 y^0][kernel 1 x^1 y^0]...[kernel N, x^1 y^0]
     83    // [kernel 0, x^n y^m][kernel 1 x^n y^m]...[kernel N, x^n y^m]
     84    // normalization
     85    // bg 0, bg 1, bg 2 (only 0 is currently used?)
    6686
    6787    for (int i = 0; i < numKernels; i++) {
     
    7494                for (int x = - footprint; x <= footprint; x++) {
    7595                    double cc = iConv->kernel[y][x] * jConv->kernel[y][x];
    76 #ifdef USE_WEIGHT
    77                     cc *= weight->kernel[y][x];
    78 #endif
     96                    if (weight) {
     97                        cc *= weight->kernel[y][x];
     98                    }
     99                    if (window) {
     100                        cc *= window->kernel[y][x];
     101                    }
    79102                    sumCC += cc;
    80103                }
    81104            }
    82105
    83             // Spatial variation
    84             for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    85                 for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    86                     double value = sumCC * poly2[iTerm][jTerm];
    87                     matrix->data.F64[iIndex][jIndex] = value;
    88                     matrix->data.F64[jIndex][iIndex] = value;
     106            // Spatial variation of kernel coeffs
     107            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     108                for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     109                    for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     110                        double value = sumCC * poly2[iTerm][jTerm];
     111                        matrix->data.F64[iIndex][jIndex] = value;
     112                        matrix->data.F64[jIndex][iIndex] = value;
     113                    }
    89114                }
    90115            }
     
    102127                double rc = ref * conv;
    103128                double c = conv;
    104 #ifdef USE_WEIGHT
    105                 float wtVal = weight->kernel[y][x];
    106                 ic *= wtVal;
    107                 rc *= wtVal;
    108                 c *= wtVal;
    109 #endif
     129                if (weight) {
     130                    float wtVal = weight->kernel[y][x];
     131                    ic *= wtVal;
     132                    rc *= wtVal;
     133                    c *= wtVal;
     134                }
     135                if (window) {
     136                    float winVal = window->kernel[y][x];
     137                    ic *= winVal;
     138                    rc *= winVal;
     139                    c  *= winVal;
     140                }
    110141                sumIC += ic;
    111142                sumRC += rc;
     
    117148            double normTerm = sumRC * poly[iTerm];
    118149            double bgTerm = sumC * poly[iTerm];
    119             matrix->data.F64[iIndex][normIndex] = normTerm;
    120             matrix->data.F64[normIndex][iIndex] = normTerm;
    121             matrix->data.F64[iIndex][bgIndex] = bgTerm;
    122             matrix->data.F64[bgIndex][iIndex] = bgTerm;
    123             vector->data.F64[iIndex] = sumIC * poly[iTerm];
     150            if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     151                matrix->data.F64[iIndex][normIndex] = normTerm;
     152                matrix->data.F64[normIndex][iIndex] = normTerm;
     153            }
     154            if ((mode & PM_SUBTRACTION_EQUATION_BG) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     155                matrix->data.F64[iIndex][bgIndex] = bgTerm;
     156                matrix->data.F64[bgIndex][iIndex] = bgTerm;
     157            }
     158            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     159                vector->data.F64[iIndex] = sumIC * poly[iTerm];
     160                if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) {
     161                    // subtract norm * sumRC * poly[iTerm]
     162                    psAssert (kernels->solution1, "programming error: define solution first!");
     163                    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     164                    double norm = fabs(kernels->solution1->data.F64[normIndex]);  // Normalisation
     165                    vector->data.F64[iIndex] -= norm * normTerm;
     166                }
     167            }
    124168        }
    125169    }
     
    130174    double sumR = 0.0;                  // Sum of the reference
    131175    double sumI = 0.0;                  // Sum of the input
     176    double normI1 = 0.0, normI2 = 0.0;  // Sum of I_1 and I_2 within the normalisation window
    132177    for (int y = - footprint; y <= footprint; y++) {
    133178        for (int x = - footprint; x <= footprint; x++) {
     
    137182            double rr = PS_SQR(ref);
    138183            double one = 1.0;
    139 #ifdef USE_WEIGHT
    140             float wtVal = weight->kernel[y][x];
    141             rr *= wtVal;
    142             ir *= wtVal;
    143             in *= wtVal;
    144             ref *= wtVal;
    145             one *= wtVal;
    146 #endif
     184
     185            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow)) {
     186                normI1 += ref;
     187                normI2 += in;
     188            }
     189
     190            if (weight) {
     191                float wtVal = weight->kernel[y][x];
     192                rr *= wtVal;
     193                ir *= wtVal;
     194                in *= wtVal;
     195                ref *= wtVal;
     196                one *= wtVal;
     197            }
     198            if (window) {
     199                float  winVal = window->kernel[y][x];
     200                rr      *= winVal;
     201                ir      *= winVal;
     202                in      *= winVal;
     203                ref *= winVal;
     204                one *= winVal;
     205            }
    147206            sumRR += rr;
    148207            sumIR += ir;
     
    152211        }
    153212    }
    154     matrix->data.F64[normIndex][normIndex] = sumRR;
    155     matrix->data.F64[bgIndex][bgIndex] = sum1;
    156     matrix->data.F64[normIndex][bgIndex] = matrix->data.F64[bgIndex][normIndex] = sumR;
    157     vector->data.F64[normIndex] = sumIR;
    158     vector->data.F64[bgIndex] = sumI;
     213
     214    *norm = normI2 / normI1;
     215
     216    if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     217        matrix->data.F64[normIndex][normIndex] = sumRR;
     218        vector->data.F64[normIndex] = sumIR;
     219        // subtract sum over kernels * kernel solution
     220    }
     221    if (mode & PM_SUBTRACTION_EQUATION_BG) {
     222        matrix->data.F64[bgIndex][bgIndex] = sum1;
     223        vector->data.F64[bgIndex] = sumI;
     224    }
     225    if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_BG)) {
     226        matrix->data.F64[normIndex][bgIndex] = sumR;
     227        matrix->data.F64[bgIndex][normIndex] = sumR;
     228    }
     229
     230    // check for any NAN values in the result, skip if found:
     231    for (int iy = 0; iy < matrix->numRows; iy++) {
     232        for (int ix = 0; ix < matrix->numCols; ix++) {
     233            if (!isfinite(matrix->data.F64[iy][ix])) {
     234                fprintf (stderr, "WARNING: NAN in matrix\n");
     235                return false;
     236            }
     237        }
     238    }
     239    for (int ix = 0; ix < vector->n; ix++) {
     240        if (!isfinite(vector->data.F64[ix])) {
     241            fprintf (stderr, "WARNING: NAN in vector\n");
     242            return false;
     243        }
     244    }
    159245
    160246    return true;
    161247}
    162248
     249
    163250// Calculate the least-squares matrix and vector for dual convolution
    164 static bool calculateDualMatrixVector(psImage *matrix1, // Least-squares matrix, updated
    165                                       psVector *vector1, // Least-squares vector, updated
    166                                       psImage *matrix2,  // Least-squares matrix, updated
    167                                       psVector *vector2, // Least-squares vector, updated
    168                                       psImage *matrixX,  // Cross-matrix
     251static bool calculateDualMatrixVector(psImage *matrix, // Least-squares matrix, updated
     252                                      psVector *vector, // Least-squares vector, updated
     253                                      double *norm,     // Normalisation, updated
    169254                                      const psKernel *image1, // Image 1
    170255                                      const psKernel *image2, // Image 2
    171256                                      const psKernel *weight,  // Weight image
     257                                      const psKernel *window,  // Window image
    172258                                      const psArray *convolutions1, // Convolutions of image 1 for each kernel
    173259                                      const psArray *convolutions2, // Convolutions of image 2 for each kernel
    174260                                      const pmSubtractionKernels *kernels, // Kernels
    175261                                      const psImage *polyValues, // Spatial polynomial values
    176                                       int footprint // (Half-)Size of stamp
     262                                      int footprint, // (Half-)Size of stamp
     263                                      int normWindow, // Window (half-)size for normalisation measurement
     264                                      const pmSubtractionEquationCalculationMode mode
    177265                                      )
    178266{
    179     // A_ij = A_i A_j
    180     // B_ij = B_i B_j
    181     // C_ij = A_i B_j
    182     // d_i = A_i I_2
    183     // e_i = B_i I_2
    184 
    185     // A_i = I_1 * k_i
    186     // B_i = I_2 * k_i
    187 
    188     // Background: A_i = 1.0
    189     // Normalisation: A_i = I_1
    190 
    191267    int numKernels = kernels->num;                      // Number of kernels
    192268    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     
    196272    double poly[numPoly];                                 // Polynomial terms
    197273    double poly2[numPoly][numPoly];                       // Polynomial-polynomial values
     274
     275    int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
     276    int numParams = numKernels * numPoly + 1 + numBackground;       // Number of regular parameters
     277    int numParams2 = numKernels * numPoly;                          // Number of additional parameters for dual
     278    int numDual = numParams + numParams2;                           // Total number of parameters for dual
     279
     280    psAssert(matrix &&
     281             matrix->type.type == PS_TYPE_F64 &&
     282             matrix->numCols == numDual &&
     283             matrix->numRows == numDual,
     284             "Least-squares matrix is bad.");
     285    psAssert(vector &&
     286             vector->type.type == PS_TYPE_F64 &&
     287             vector->n == numDual,
     288             "Least-squares vector is bad.");
    198289
    199290    // Evaluate polynomial-polynomial terms
     
    212303
    213304
     305    // initialize the matrix and vector for NOP on all coeffs.  we only fill in the coeffs we
     306    // choose to calculate
     307    psImageInit(matrix, 0.0);
     308    psVectorInit(vector, 1.0);
     309    for (int i = 0; i < matrix->numCols; i++) {
     310        matrix->data.F64[i][i] = 1.0;
     311    }
     312
    214313    for (int i = 0; i < numKernels; i++) {
    215314        psKernel *iConv1 = convolutions1->data[i]; // Convolution 1 for index i
     
    219318            psKernel *jConv2 = convolutions2->data[j]; // Convolution 2 for index j
    220319
    221             double sumAA = 0.0;         // Sum of convolution products for matrix A
    222             double sumBB = 0.0;         // Sum of convolution products for matrix B
    223             double sumAB = 0.0;         // Sum of convolution products for matrix C
     320            double sumAA = 0.0;         // Sum of convolution products between image 1
     321            double sumBB = 0.0;         // Sum of convolution products between image 2
     322            double sumAB = 0.0;         // Sum of convolution products across images 1 and 2
    224323            for (int y = - footprint; y <= footprint; y++) {
    225324                for (int x = - footprint; x <= footprint; x++) {
     
    227326                    double bb = iConv2->kernel[y][x] * jConv2->kernel[y][x];
    228327                    double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x];
    229 #ifdef USE_WEIGHT
    230                     float wtVal = weight->kernel[y][x];
    231                     aa *= wtVal;
    232                     bb *= wtVal;
    233                     ab *= wtVal;
    234 #endif
     328                    if (weight) {
     329                        float wtVal = weight->kernel[y][x];
     330                        aa *= wtVal;
     331                        bb *= wtVal;
     332                        ab *= wtVal;
     333                    }
     334                    if (window) {
     335                        float wtVal = window->kernel[y][x];
     336                        aa *= wtVal;
     337                        bb *= wtVal;
     338                        ab *= wtVal;
     339                    }
    235340                    sumAA += aa;
    236341                    sumBB += bb;
     
    239344            }
    240345
    241             // Spatial variation
    242             for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    243                 for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    244                     double aa = sumAA * poly2[iTerm][jTerm];
    245                     double bb = sumBB * poly2[iTerm][jTerm];
    246                     double ab = sumAB * poly2[iTerm][jTerm];
    247                     matrix1->data.F64[iIndex][jIndex] = aa;
    248                     matrix1->data.F64[jIndex][iIndex] = aa;
    249                     matrix2->data.F64[iIndex][jIndex] = bb;
    250                     matrix2->data.F64[jIndex][iIndex] = bb;
    251                     matrixX->data.F64[iIndex][jIndex] = ab;
     346            // Spatial variation of kernel coeffs
     347            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     348                for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     349                    for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     350                        double aa = sumAA * poly2[iTerm][jTerm];
     351                        double bb = sumBB * poly2[iTerm][jTerm];
     352                        double ab = sumAB * poly2[iTerm][jTerm];
     353
     354                        matrix->data.F64[iIndex][jIndex] = aa;
     355                        matrix->data.F64[jIndex][iIndex] = aa;
     356
     357                        matrix->data.F64[iIndex + numParams][jIndex + numParams] = bb;
     358                        matrix->data.F64[jIndex + numParams][iIndex + numParams] = bb;
     359
     360                        matrix->data.F64[iIndex][jIndex + numParams] = ab;
     361                        matrix->data.F64[jIndex + numParams][iIndex] = ab;
     362                    }
    252363                }
    253364            }
     
    259370                for (int x = - footprint; x <= footprint; x++) {
    260371                    double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x];
    261 #ifdef USE_WEIGHT
    262                     ab *= weight->kernel[y][x];
    263 #endif
     372                    if (weight) {
     373                        ab *= weight->kernel[y][x];
     374                    }
     375                    if (window) {
     376                        ab *= window->kernel[y][x];
     377                    }
    264378                    sumAB += ab;
    265379                }
    266380            }
    267381
    268             // Spatial variation
    269             for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    270                 for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    271                     double ab = sumAB * poly2[iTerm][jTerm];
    272                     matrixX->data.F64[iIndex][jIndex] = ab;
    273                 }
    274             }
    275         }
    276 
    277         double sumAI2 = 0.0;            // Sum of A.I_2 products (for vector 1)
    278         double sumBI2 = 0.0;            // Sum of B.I_2 products (for vector 2)
    279         double sumAI1 = 0.0;            // Sum of A.I_1 products (for matrix 1, normalisation)
    280         double sumA = 0.0;              // Sum of A (for matrix 1, background)
    281         double sumBI1 = 0.0;            // Sum of B.I_1 products (for matrix X, normalisation)
    282         double sumB = 0.0;              // Sum of B products (for matrix X, background)
    283         double sumI2 = 0.0;             // Sum of I_2 (for vector 1, background)
    284         double sumI1I2 = 0.0;           // Sum of I_1.I_2 (for vector 1, normalisation)
     382            // Spatial variation of kernel coeffs
     383            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     384                for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     385                    for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     386                        double ab = sumAB * poly2[iTerm][jTerm];
     387                        matrix->data.F64[iIndex][jIndex + numParams] = ab;
     388                        matrix->data.F64[jIndex + numParams][iIndex] = ab;
     389                    }
     390                }
     391            }
     392        }
     393
     394        double sumAI2 = 0.0;            // Sum of A.I_2 products (for vector)
     395        double sumBI2 = 0.0;            // Sum of B.I_2 products (for vector)
     396        double sumAI1 = 0.0;            // Sum of A.I_1 products (for matrix, normalisation)
     397        double sumA = 0.0;              // Sum of A (for matrix, background)
     398        double sumBI1 = 0.0;            // Sum of B.I_1 products (for matrix, normalisation)
     399        double sumB = 0.0;              // Sum of B products (for matrix, background)
     400        double sumI2 = 0.0;             // Sum of I_2 (for vector, background)
    285401        for (int y = - footprint; y <= footprint; y++) {
    286402            for (int x = - footprint; x <= footprint; x++) {
    287                 float a = iConv1->kernel[y][x];
    288                 float b = iConv2->kernel[y][x];
     403                double a = iConv1->kernel[y][x];
     404                double b = iConv2->kernel[y][x];
    289405                float i1 = image1->kernel[y][x];
    290406                float i2 = image2->kernel[y][x];
     
    294410                double ai1 = a * i1;
    295411                double bi1 = b * i1;
    296                 double i1i2 = i1 * i2;
    297 
    298 #ifdef USE_WEIGHT
    299                 float wtVal = weight->kernel[y][x];
    300                 ai2 *= wtVal;
    301                 bi2 *= wtVal;
    302                 ai1 *= wtVal;
    303                 bi1 *= wtVal;
    304                 i1i2 *= wtVal;
    305                 a *= wtVal;
    306                 b *= wtVal;
    307                 i2 *= wtVal;
    308 #endif
    309 
     412
     413                if (weight) {
     414                    float wtVal = weight->kernel[y][x];
     415                    ai2 *= wtVal;
     416                    bi2 *= wtVal;
     417                    ai1 *= wtVal;
     418                    bi1 *= wtVal;
     419                    a *= wtVal;
     420                    b *= wtVal;
     421                    i2 *= wtVal;
     422                }
     423                if (window) {
     424                    float wtVal = window->kernel[y][x];
     425                    ai2 *= wtVal;
     426                    bi2 *= wtVal;
     427                    ai1 *= wtVal;
     428                    bi1 *= wtVal;
     429                    a *= wtVal;
     430                    b *= wtVal;
     431                    i2 *= wtVal;
     432                }
    310433                sumAI2 += ai2;
    311434                sumBI2 += bi2;
     
    315438                sumB += b;
    316439                sumI2 += i2;
    317                 sumI1I2 += i1i2;
    318440            }
    319441        }
     
    323445            double bi2 = sumBI2 * poly[iTerm];
    324446            double ai1 = sumAI1 * poly[iTerm];
    325             double a = sumA * poly[iTerm];
     447            double a   = sumA * poly[iTerm];
    326448            double bi1 = sumBI1 * poly[iTerm];
    327             double b = sumB * poly[iTerm];
    328 
    329             matrix1->data.F64[iIndex][normIndex] = ai1;
    330             matrix1->data.F64[normIndex][iIndex] = ai1;
    331             matrix1->data.F64[iIndex][bgIndex] = a;
    332             matrix1->data.F64[bgIndex][iIndex] = a;
    333             vector1->data.F64[iIndex] = ai2;
    334             vector2->data.F64[iIndex] = bi2;
    335             matrixX->data.F64[iIndex][normIndex] = bi1;
    336             matrixX->data.F64[iIndex][bgIndex] = b;
    337         }
    338     }
    339 
    340     double sumI1 = 0.0;                 // Sum of I_1 (for matrix 1, background-normalisation)
    341     double sumI1I1 = 0.0;               // Sum of I_1^2 (for matrix 1, normalisation-normalisation)
    342     double sum1 = 0.0;                  // Sum of 1 (for matrix 1, background-background)
    343     double sumI2 = 0.0;                 // Sum of I_2 (for vector 1, background)
    344     double sumI1I2 = 0.0;               // Sum of I_1.I_2 (for vector 1, normalisation)
     449            double b   = sumB * poly[iTerm];
     450
     451            if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     452                matrix->data.F64[iIndex][normIndex] = ai1;
     453                matrix->data.F64[normIndex][iIndex] = ai1;
     454                matrix->data.F64[iIndex + numParams][normIndex] = bi1;
     455                matrix->data.F64[normIndex][iIndex + numParams] = bi1;
     456            }
     457            if ((mode & PM_SUBTRACTION_EQUATION_BG) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     458                matrix->data.F64[iIndex][bgIndex] = a;
     459                matrix->data.F64[bgIndex][iIndex] = a;
     460                matrix->data.F64[iIndex + numParams][bgIndex] = b;
     461                matrix->data.F64[bgIndex][iIndex + numParams] = b;
     462            }
     463            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     464                vector->data.F64[iIndex] = ai2;
     465                vector->data.F64[iIndex + numParams] = bi2;
     466                if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) {
     467                    // subtract norm * sumRC * poly[iTerm]
     468                    psAssert (kernels->solution1, "programming error: define solution first!");
     469                    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     470                    double norm = fabs(kernels->solution1->data.F64[normIndex]);  // Normalisation
     471                    vector->data.F64[iIndex] -= norm * ai1;
     472                    vector->data.F64[iIndex + numParams] -= norm * bi1;
     473                }
     474            }
     475        }
     476    }
     477
     478    double sumI1 = 0.0;                 // Sum of I_1 (for matrix, background-normalisation)
     479    double sumI1I1 = 0.0;               // Sum of I_1^2 (for matrix, normalisation-normalisation)
     480    double sum1 = 0.0;                  // Sum of 1 (for matrix, background-background)
     481    double sumI2 = 0.0;                 // Sum of I_2 (for vector, background)
     482    double sumI1I2 = 0.0;               // Sum of I_1.I_2 (for vector, normalisation)
     483    double normI1 = 0.0, normI2 = 0.0;  // Sum of I_1 and I_2 within the normalisation window
    345484    for (int y = - footprint; y <= footprint; y++) {
    346485        for (int x = - footprint; x <= footprint; x++) {
    347             float i1 = image1->kernel[y][x];
    348             float i2 = image2->kernel[y][x];
     486            double i1 = image1->kernel[y][x];
     487            double i2 = image2->kernel[y][x];
    349488
    350489            double i1i1 = i1 * i1;
     
    352491            double i1i2 = i1 * i2;
    353492
    354 #ifdef USE_WEIGHT
    355             float wtVal = weight->kernel[y][x];
    356             i1 *= wtVal;
    357             i1i1 *= wtVal;
    358             one *= wtVal;
    359             i2 *= wtVal;
    360             i1i2 *= wtVal;
    361 #endif
    362 
     493            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow)) {
     494                normI1 += i1;
     495                normI2 += i2;
     496            }
     497
     498            if (weight) {
     499                float wtVal = weight->kernel[y][x];
     500                i1 *= wtVal;
     501                i1i1 *= wtVal;
     502                one *= wtVal;
     503                i2 *= wtVal;
     504                i1i2 *= wtVal;
     505            }
     506            if (window) {
     507                float wtVal = window->kernel[y][x];
     508                i1 *= wtVal;
     509                i1i1 *= wtVal;
     510                one *= wtVal;
     511                i2 *= wtVal;
     512                i1i2 *= wtVal;
     513            }
    363514            sumI1 += i1;
    364515            sumI1I1 += i1i1;
     
    368519        }
    369520    }
    370     matrix1->data.F64[bgIndex][normIndex] = sumI1;
    371     matrix1->data.F64[normIndex][bgIndex] = sumI1;
    372     matrix1->data.F64[normIndex][normIndex] = sumI1I1;
    373     matrix1->data.F64[bgIndex][bgIndex] = sum1;
    374     vector1->data.F64[bgIndex] = sumI2;
    375     vector1->data.F64[normIndex] = sumI1I2;
     521
     522    *norm = normI2 / normI1;
     523
     524    if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     525        matrix->data.F64[normIndex][normIndex] = sumI1I1;
     526        vector->data.F64[normIndex] = sumI1I2;
     527    }
     528    if (mode & PM_SUBTRACTION_EQUATION_BG) {
     529        matrix->data.F64[bgIndex][bgIndex] = sum1;
     530        vector->data.F64[bgIndex] = sumI2;
     531    }
     532    if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_BG)) {
     533        matrix->data.F64[bgIndex][normIndex] = sumI1;
     534        matrix->data.F64[normIndex][bgIndex] = sumI1;
     535    }
     536
     537    // check for any NAN values in the result, skip if found:
     538    for (int iy = 0; iy < matrix->numRows; iy++) {
     539        for (int ix = 0; ix < matrix->numCols; ix++) {
     540            if (!isfinite(matrix->data.F64[iy][ix])) {
     541                fprintf (stderr, "WARNING: NAN in matrix\n");
     542                return false;
     543            }
     544        }
     545    }
     546    for (int ix = 0; ix < vector->n; ix++) {
     547        if (!isfinite(vector->data.F64[ix])) {
     548            fprintf (stderr, "WARNING: NAN in vector\n");
     549            return false;
     550        }
     551    }
     552
    376553
    377554    return true;
    378555}
    379556
    380 // Merge dual matrices and vectors into single matrix equation
    381 // Have: Aa = Ct.b + d
    382 // Have: Ca = Bb + e
    383 // Set: F = ( A -Ct ;  C -B )
    384 // Set: g = ( a ; b )
    385 // Set: h = ( d ; e )
    386 // So that we combine the above two equations: Fg = h
    387 static bool calculateEquationDual(psImage **outMatrix,
    388                                   psVector **outVector,
    389                                   const psImage *sumMatrix1,
    390                                   const psImage *sumMatrix2,
    391                                   const psImage *sumMatrixX,
    392                                   const psVector *sumVector1,
    393                                   const psVector *sumVector2
    394                                   )
    395 {
    396     psAssert(sumMatrix1 && sumMatrix2 && sumMatrixX, "Require input matrices");
    397     psAssert(sumVector1 && sumVector2, "Require input vectors");
    398     int num1 = sumVector1->n;   // Number of parameters in first set
    399     int num2 = sumVector2->n;   // Number of parameters in second set
    400     int num = num1 + num2;      // Number of parameters in new set
    401 
    402     psAssert(sumMatrix1->type.type == PS_TYPE_F64 &&
    403              sumMatrix2->type.type == PS_TYPE_F64 &&
    404              sumMatrixX->type.type == PS_TYPE_F64 &&
    405              sumVector1->type.type == PS_TYPE_F64 &&
    406              sumVector2->type.type == PS_TYPE_F64,
    407              "Require input type is F64");
    408 
    409     psAssert(outMatrix, "Require output matrix");
    410     psAssert(outVector, "Require output vector");
    411     if (!*outMatrix) {
    412         *outMatrix = psImageAlloc(num, num, PS_TYPE_F64);
    413     }
    414     if (!*outVector) {
    415         *outVector = psVectorAlloc(num, PS_TYPE_F64);
    416     }
    417     psImage *matrix = *outMatrix;
    418     psVector *vector = *outVector;
    419 
    420     psAssert(sumMatrix1->numCols == num1 && sumMatrix1->numRows == num1, "Require size NxN");
    421     psAssert(sumMatrix2->numCols == num2 && sumMatrix2->numRows == num2, "Require size MxM");
    422     psAssert(sumMatrixX->numCols == num1 && sumMatrixX->numRows == num2, "Require size MxN");
    423 
    424     memcpy(vector->data.F64, sumVector1->data.F64, num1 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    425     memcpy(&vector->data.F64[num1], sumVector2->data.F64, num2 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    426 
    427     for (int i = 0; i < num1; i++) {
    428         memcpy(matrix->data.F64[i], sumMatrix1->data.F64[i], num1 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    429         for (int j = 0, k = num1; j < num2; j++, k++) {
    430             matrix->data.F64[i][k] = - sumMatrixX->data.F64[j][i];
    431         }
    432     }
    433     for (int i1 = 0, i2 = num1; i1 < num2; i1++, i2++) {
    434         memcpy(matrix->data.F64[i2], sumMatrixX->data.F64[i1], num1 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    435         for (int j = 0, k = num1; j < num2; j++, k++) {
    436             matrix->data.F64[i2][k] = - sumMatrix2->data.F64[i1][j];
    437         }
    438     }
    439 
    440     return true;
    441 }
    442 
    443 
     557#if 1
    444558// Add in penalty term to least-squares vector
    445 static bool calculatePenalty(psImage *matrix,                     // Matrix to which to add in penalty term
     559bool calculatePenalty(psImage *matrix,                     // Matrix to which to add in penalty term
    446560                             psVector *vector,                    // Vector to which to add in penalty term
    447561                             const pmSubtractionKernels *kernels, // Kernel parameters
     
    456570    int spatialOrder = kernels->spatialOrder; // Order of spatial variations
    457571    int numKernels = kernels->num; // Number of kernel components
     572    int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial variations
     573    int numParams = numKernels * numSpatial;                 // Number of kernel parameters
     574
     575    // order is :
     576    // [p_0,x_0,y_0 p_1,x_0,y_0, p_2,x_0,y_0]
     577    // [p_0,x_1,y_0 p_1,x_1,y_0, p_2,x_1,y_0]
     578    // [p_0,x_0,y_1 p_1,x_0,y_1, p_2,x_0,y_1]
     579    // [norm]
     580    // [bg]
     581    // [q_0,x_0,y_0 q_1,x_0,y_0, q_2,x_0,y_0]
     582    // [q_0,x_1,y_0 q_1,x_1,y_0, q_2,x_1,y_0]
     583    // [q_0,x_0,y_1 q_1,x_0,y_1, q_2,x_0,y_1]
     584
    458585    for (int i = 0; i < numKernels; i++) {
    459586        for (int yOrder = 0, index = i; yOrder <= spatialOrder; yOrder++) {
    460587            for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
    461588                // Contribution to chi^2: a_i^2 P_i
    462                 matrix->data.F64[index][index] -= norm * penalties->data.F32[i];
     589                psAssert(isfinite(penalties->data.F32[i]), "Invalid penalty");
     590                matrix->data.F64[index][index] += norm * penalties->data.F32[i];
     591                if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
     592                    matrix->data.F64[index + numParams + 2][index + numParams + 2] += norm * penalties->data.F32[i];
     593                    // matrix[i][i] is ~ (k_i * I_1)(k_i * I_1)
     594                    // penalties scale with second moments
     595                    //
     596                }
    463597            }
    464598        }
     
    467601    return true;
    468602}
     603# endif
    469604
    470605//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    476611// Calculate the value of a polynomial, specified by coefficients and polynomial values
    477612double p_pmSubtractionCalculatePolynomial(const psVector *coeff, // Coefficients
    478                                                  const psImage *polyValues, // Polynomial values
    479                                                  int order, // Order of polynomials
    480                                                  int index, // Index at which to begin
    481                                                  int step // Step between subsequent indices
    482                                                  )
     613                                          const psImage *polyValues, // Polynomial values
     614                                          int order, // Order of polynomials
     615                                          int index, // Index at which to begin
     616                                          int step // Step between subsequent indices
     617                                          )
    483618{
    484619    double sum = 0.0;                   // Value of the polynomial sum
     
    495630
    496631double p_pmSubtractionSolutionCoeff(const pmSubtractionKernels *kernels, const psImage *polyValues,
    497                                            int index, bool wantDual)
     632                                    int index, bool wantDual)
    498633{
    499634#if 0
     
    548683    const pmSubtractionKernels *kernels = job->args->data[1]; // Kernels
    549684    int index = PS_SCALAR_VALUE(job->args->data[2], S32); // Stamp index
    550 
    551     return pmSubtractionCalculateEquationStamp(stamps, kernels, index);
     685    pmSubtractionEquationCalculationMode mode  = PS_SCALAR_VALUE(job->args->data[3], S32); // calculation model
     686
     687    return pmSubtractionCalculateEquationStamp(stamps, kernels, index, mode);
    552688}
    553689
    554690bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels,
    555                                          int index)
     691                                         int index, const pmSubtractionEquationCalculationMode mode)
    556692{
    557693    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    566702    int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
    567703
     704    // numKernels is the number of unique kernel images (one for each Gaussian modified by a specific polynomial).
     705    // = \sum_i^N_Gaussians [(order + 1) * (order + 2) / 2], eg for 1 Gauss and 1st order, numKernels = 3
     706
    568707    // Total number of parameters to solve for: coefficient of each kernel basis function, multipled by the
    569708    // number of coefficients for the spatial polynomial, normalisation and a constant background offset.
    570709    int numParams = numKernels * numSpatial + 1 + numBackground;
     710    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
     711        // An additional image is convolved
     712        numParams += numKernels * numSpatial;
     713    }
    571714
    572715    pmSubtractionStamp *stamp = stamps->stamps->data[index]; // Stamp of interest
    573716    psAssert(stamp->status == PM_SUBTRACTION_STAMP_CALCULATE, "We only operate on stamps with this state.");
    574717
    575     // Generate convolutions
     718    // Generate convolutions: these are generated once and saved
    576719    if (!pmSubtractionConvolveStamp(stamp, kernels, footprint)) {
    577720        psError(PS_ERR_UNKNOWN, false, "Unable to convolve stamp %d.", index);
     
    603746#endif
    604747
     748    // XXX visualize the set of convolved stamps
     749
    605750    psImage *polyValues = p_pmSubtractionPolynomial(NULL, spatialOrder,
    606751                                                    stamp->xNorm, stamp->yNorm); // Polynomial terms
    607752
    608     bool new = stamp->vector1 ? false : true; // Is this a new run?
     753    bool new = stamp->vector ? false : true; // Is this a new run?
    609754    if (new) {
    610         stamp->matrix1 = psImageAlloc(numParams, numParams, PS_TYPE_F64);
    611         stamp->vector1 = psVectorAlloc(numParams, PS_TYPE_F64);
     755        stamp->matrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
     756        stamp->vector = psVectorAlloc(numParams, PS_TYPE_F64);
    612757    }
    613758#ifdef TESTING
    614     psImageInit(stamp->matrix1, NAN);
    615     psVectorInit(stamp->vector1, NAN);
     759    psImageInit(stamp->matrix, NAN);
     760    psVectorInit(stamp->vector, NAN);
    616761#endif
    617762
    618763    bool status;                    // Status of least-squares matrix/vector calculation
     764
     765    psKernel *weight = NULL;
     766    psKernel *window = NULL;
     767
     768#ifdef USE_WEIGHT
     769    weight = stamp->weight;
     770#endif
     771#ifdef USE_WINDOW
     772    window = stamps->window;
     773#endif
     774
    619775    switch (kernels->mode) {
    620776      case PM_SUBTRACTION_MODE_1:
    621         status = calculateMatrixVector(stamp->matrix1, stamp->vector1, stamp->image2, stamp->image1,
    622                                        stamp->weight, stamp->convolutions1, kernels, polyValues,
    623                                        footprint);
     777        status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image2, stamp->image1,
     778                                       weight, window, stamp->convolutions1, kernels,
     779                                       polyValues, footprint, stamps->normWindow, mode);
    624780        break;
    625781      case PM_SUBTRACTION_MODE_2:
    626         status = calculateMatrixVector(stamp->matrix1, stamp->vector1, stamp->image1, stamp->image2,
    627                                        stamp->weight, stamp->convolutions2, kernels, polyValues,
    628                                        footprint);
     782        status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image1, stamp->image2,
     783                                       weight, window, stamp->convolutions2, kernels,
     784                                       polyValues, footprint, stamps->normWindow, mode);
    629785        break;
    630786      case PM_SUBTRACTION_MODE_DUAL:
    631         if (new) {
    632             stamp->matrix2 = psImageAlloc(numKernels * numSpatial, numKernels * numSpatial, PS_TYPE_F64);
    633             stamp->matrixX = psImageAlloc(numParams, numKernels * numSpatial, PS_TYPE_F64);
    634             stamp->vector2 = psVectorAlloc(numKernels * numSpatial, PS_TYPE_F64);
    635         }
    636 #ifdef TESTING
    637         psImageInit(stamp->matrix2, NAN);
    638         psImageInit(stamp->matrixX, NAN);
    639         psVectorInit(stamp->vector2, NAN);
    640 #endif
    641         status = calculateDualMatrixVector(stamp->matrix1, stamp->vector1, stamp->matrix2, stamp->vector2,
    642                                            stamp->matrixX, stamp->image1, stamp->image2, stamp->weight,
    643                                            stamp->convolutions1, stamp->convolutions2, kernels, polyValues,
    644                                            footprint);
     787        status = calculateDualMatrixVector(stamp->matrix, stamp->vector, &stamp->norm,
     788                                           stamp->image1, stamp->image2,
     789                                           weight, window, stamp->convolutions1, stamp->convolutions2,
     790                                           kernels, polyValues, footprint, stamps->normWindow, mode);
    645791        break;
    646792      default:
     
    651797        stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
    652798        psWarning("Rejecting stamp %d (%d,%d) because of bad equation",
    653                   index, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
     799                  index, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
    654800    } else {
    655801        stamp->status = PM_SUBTRACTION_STAMP_USED;
     
    659805    {
    660806        psString matrixName = NULL;
    661         psStringAppend(&matrixName, "matrix1_%d.fits", index);
     807        psStringAppend(&matrixName, "matrix_%d.fits", index);
    662808        psFits *matrixFile = psFitsOpen(matrixName, "w");
    663809        psFree(matrixName);
    664         psFitsWriteImage(matrixFile, NULL, stamp->matrix1, 0, NULL);
     810        psFitsWriteImage(matrixFile, NULL, stamp->matrix, 0, NULL);
    665811        psFitsClose(matrixFile);
    666812
    667813        matrixName = NULL;
    668         psStringAppend(&matrixName, "vector1_%d.fits", index);
    669         psImage *dummy = psImageAlloc(stamp->vector1->n, 1, PS_TYPE_F64);
    670         memcpy(dummy->data.F64[0], stamp->vector1->data.F64,
    671                PSELEMTYPE_SIZEOF(PS_TYPE_F64) * stamp->vector1->n);
     814        psStringAppend(&matrixName, "vector_%d.fits", index);
     815        psImage *dummy = psImageAlloc(stamp->vector->n, 1, PS_TYPE_F64);
     816        memcpy(dummy->data.F64[0], stamp->vector->data.F64,
     817               PSELEMTYPE_SIZEOF(PS_TYPE_F64) * stamp->vector->n);
    672818        matrixFile = psFitsOpen(matrixName, "w");
    673819        psFree(matrixName);
     
    675821        psFree(dummy);
    676822        psFitsClose(matrixFile);
    677 
    678         if (stamp->vector2) {
    679             matrixName = NULL;
    680             psStringAppend(&matrixName, "vector2_%d.fits", index);
    681             dummy = psImageAlloc(stamp->vector2->n, 1, PS_TYPE_F64);
    682             memcpy(dummy->data.F64[0], stamp->vector2->data.F64,
    683                    PSELEMTYPE_SIZEOF(PS_TYPE_F64) * stamp->vector2->n);
    684             matrixFile = psFitsOpen(matrixName, "w");
    685             psFree(matrixName);
    686             psFitsWriteImage(matrixFile, NULL, dummy, 0, NULL);
    687             psFree(dummy);
    688             psFitsClose(matrixFile);
    689         }
    690 
    691         if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    692             matrixName = NULL;
    693             psStringAppend(&matrixName, "matrix2_%d.fits", index);
    694             matrixFile = psFitsOpen(matrixName, "w");
    695             psFree(matrixName);
    696             psFitsWriteImage(matrixFile, NULL, stamp->matrix2, 0, NULL);
    697             psFitsClose(matrixFile);
    698 
    699             matrixName = NULL;
    700             psStringAppend(&matrixName, "matrixX_%d.fits", index);
    701             matrixFile = psFitsOpen(matrixName, "w");
    702             psFree(matrixName);
    703             psFitsWriteImage(matrixFile, NULL, stamp->matrixX, 0, NULL);
    704             psFitsClose(matrixFile);
    705         }
    706823    }
    707824#endif
     
    712829}
    713830
    714 bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels)
     831bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels,
     832                                    const pmSubtractionEquationCalculationMode mode)
    715833{
    716834    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    727845        }
    728846
     847        if ((stamp->x <= 0.0) && (stamp->y <= 0.0)) {
     848            psAbort ("bad stamp");
     849        }
     850        if (!isfinite(stamp->x) && !isfinite(stamp->y)) {
     851            psAbort ("bad stamp");
     852        }
     853
    729854        if (pmSubtractionThreaded()) {
    730855            psThreadJob *job = psThreadJobAlloc("PSMODULES_SUBTRACTION_CALCULATE_EQUATION");
     
    732857            psArrayAdd(job->args, 1, (pmSubtractionKernels*)kernels); // Casting away const to put on array
    733858            PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
     859            PS_ARRAY_ADD_SCALAR(job->args, mode, PS_TYPE_S32);
    734860            if (!psThreadJobAddPending(job)) {
    735861                psFree(job);
     
    738864            psFree(job);
    739865        } else {
    740             pmSubtractionCalculateEquationStamp(stamps, kernels, i);
     866            pmSubtractionCalculateEquationStamp(stamps, kernels, i, mode);
    741867        }
    742868    }
     
    748874
    749875    pmSubtractionVisualPlotLeastSquares(stamps);
     876    pmSubtractionVisualShowKernels((pmSubtractionKernels  *)kernels);
     877    pmSubtractionVisualShowBasis(stamps);
    750878
    751879    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Calculate equation: %f sec",
     
    756884}
    757885
    758 bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps)
     886// private functions used on pmSubtractionSolveEquation
     887bool psVectorWriteFile (char *filename, const psVector *vector);
     888bool psFitsWriteImageSimple (char *filename, psImage *image, psMetadata *header);
     889
     890psImage *p_pmSubSolve_wUt (psVector *w, psImage *U);
     891psImage *p_pmSubSolve_VwUt (psImage *V, psImage *wUt);
     892
     893bool p_pmSubSolve_SetWeights (psVector *wApply, psVector *w, psVector *wMask);
     894
     895bool p_pmSubSolve_UtB (psVector **UtB, psImage *U, psVector *B);
     896bool p_pmSubSolve_wUtB (psVector **wUtB, psVector *w, psVector *UtB);
     897bool p_pmSubSolve_VwUtB (psVector **VwUtB, psImage *V, psVector *wUtB);
     898
     899bool p_pmSubSolve_Ax (psVector **B, psImage *A, psVector *x);
     900bool p_pmSubSolve_VdV (double *value, psVector *x, psVector *y);
     901bool p_pmSubSolve_y2 (double *y2, pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps);
     902
     903psImage *p_pmSubSolve_Xvar (psImage *V, psVector *w);
     904
     905double p_pmSubSolve_ChiSquare (pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps);
     906
     907bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels,
     908                                const pmSubtractionStampList *stamps,
     909                                const pmSubtractionEquationCalculationMode mode)
    759910{
    760911    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
     
    762913
    763914    // Check inputs
    764     int numParams = -1;                // Number of parameters
    765     int numParams2 = 0;                // Number of parameters for part solution (DUAL mode)
     915    int numKernels = kernels->num;      // Number of kernel basis functions
     916    int numSpatial = PM_SUBTRACTION_POLYTERMS(kernels->spatialOrder); // Number of spatial variations
     917    int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
     918    int numParams = numKernels * numSpatial + 1 + numBackground;    // Number of parameters being solved for
     919    int numSolution1 = numParams, numSolution2 = 0;                 // Number of parameters for each solution
     920    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
     921        // An additional image is convolved
     922        numSolution2 = numKernels * numSpatial;
     923        numParams += numSolution2;
     924    }
     925
    766926    for (int i = 0; i < stamps->num; i++) {
    767927        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     
    771931        }
    772932
    773         PS_ASSERT_VECTOR_NON_NULL(stamp->vector1, false);
    774         if (numParams == -1) {
    775             numParams = stamp->vector1->n;
    776         }
    777         PS_ASSERT_VECTOR_SIZE(stamp->vector1, (long)numParams, false);
    778         PS_ASSERT_VECTOR_TYPE(stamp->vector1, PS_TYPE_F64, false);
    779         PS_ASSERT_IMAGE_NON_NULL(stamp->matrix1, false);
    780         PS_ASSERT_IMAGE_SIZE(stamp->matrix1, numParams, numParams, false);
    781         PS_ASSERT_IMAGE_TYPE(stamp->matrix1, PS_TYPE_F64, false);
    782 
    783         if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    784             PS_ASSERT_IMAGE_NON_NULL(stamp->matrix2, false);
    785             PS_ASSERT_IMAGE_NON_NULL(stamp->matrixX, false);
    786             if (numParams2 == 0) {
    787                 numParams2 = stamp->matrix2->numCols;
    788             }
    789             PS_ASSERT_IMAGE_SIZE(stamp->matrix2, numParams2, numParams2, false);
    790             PS_ASSERT_IMAGE_SIZE(stamp->matrixX, numParams, numParams2, false);
    791             PS_ASSERT_IMAGE_TYPE(stamp->matrix2, PS_TYPE_F64, false);
    792             PS_ASSERT_IMAGE_TYPE(stamp->matrixX, PS_TYPE_F64, false);
    793             PS_ASSERT_VECTOR_NON_NULL(stamp->vector2, false);
    794             PS_ASSERT_VECTOR_SIZE(stamp->vector2, (long)numParams2, false);
    795             PS_ASSERT_VECTOR_TYPE(stamp->vector2, PS_TYPE_F64, false);
    796         }
    797     }
    798     if (numParams == -1) {
    799         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "No suitable stamps found.");
    800         return NULL;
     933        PS_ASSERT_VECTOR_NON_NULL(stamp->vector, false);
     934        PS_ASSERT_VECTOR_SIZE(stamp->vector, (long)numParams, false);
     935        PS_ASSERT_VECTOR_TYPE(stamp->vector, PS_TYPE_F64, false);
     936        PS_ASSERT_IMAGE_NON_NULL(stamp->matrix, false);
     937        PS_ASSERT_IMAGE_SIZE(stamp->matrix, numParams, numParams, false);
     938        PS_ASSERT_IMAGE_TYPE(stamp->matrix, PS_TYPE_F64, false);
    801939    }
    802940
     
    814952        psVectorInit(sumVector, 0.0);
    815953        psImageInit(sumMatrix, 0.0);
     954
     955        psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
     956
    816957        int numStamps = 0;              // Number of good stamps
    817958        for (int i = 0; i < stamps->num; i++) {
    818959            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    819 
    820960            if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
    821 
    822 #ifdef TESTING
    823               // XXX double-check for NAN in data:
    824                 for (int iy = 0; iy < stamp->matrix1->numRows; iy++) {
    825                     for (int ix = 0; ix < stamp->matrix1->numCols; ix++) {
    826                         if (!isfinite(stamp->matrix1->data.F64[iy][ix])) {
    827                             fprintf (stderr, "WARNING: NAN in matrix1\n");
    828                         }
    829                     }
    830                 }
    831                 for (int ix = 0; ix < stamp->vector1->n; ix++) {
    832                     if (!isfinite(stamp->vector1->data.F64[ix])) {
    833                         fprintf (stderr, "WARNING: NAN in vector1\n");
    834                     }
    835                 }
    836 #endif
    837 
    838                 (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix1);
    839                 (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector1);
     961                (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
     962                (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
     963                psVectorAppend(norms, stamp->norm);
    840964                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
    841965                numStamps++;
     
    845969        }
    846970
    847 #ifdef TESTING
    848         for (int ix = 0; ix < sumVector->n; ix++) {
    849             if (!isfinite(sumVector->data.F64[ix])) {
    850                 fprintf (stderr, "WARNING: NAN in vector1\n");
    851             }
    852         }
    853 #endif
    854 
    855971#if 0
    856972        int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     
    858974#endif
    859975
    860 #ifdef TESTING
    861         for (int ix = 0; ix < sumVector->n; ix++) {
    862             if (!isfinite(sumVector->data.F64[ix])) {
    863                 fprintf (stderr, "WARNING: NAN in vector1\n");
    864             }
    865         }
     976        psVector *solution = NULL;                       // Solution to equation!
     977        solution = psVectorAlloc(numParams, PS_TYPE_F64);
     978        psVectorInit(solution, 0);
     979
     980#if 0
     981        // Regular, straight-forward solution
     982        solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     983#else
    866984        {
    867             psImage *inverse = psMatrixInvert(NULL, sumMatrix, NULL);
    868             psFits *fits = psFitsOpen("matrixInv.fits", "w");
    869             psFitsWriteImage(fits, NULL, inverse, 0, NULL);
    870             psFitsClose(fits);
    871             psFree(inverse);
    872         }
    873         {
    874             psImage *X = psMatrixInvert(NULL, sumMatrix, NULL);
    875             psImage *Xt = psMatrixTranspose(NULL, X);
    876             psImage *XtX = psMatrixMultiply(NULL, Xt, X);
    877             psFits *fits = psFitsOpen("matrixErr.fits", "w");
    878             psFitsWriteImage(fits, NULL, XtX, 0, NULL);
    879             psFitsClose(fits);
    880             psFree(X);
    881             psFree(Xt);
    882             psFree(XtX);
    883         }
    884 #endif
    885 
    886         psVector *permutation = NULL;       // Permutation vector, required for LU decomposition
    887         psImage *luMatrix = psMatrixLUDecomposition(NULL, &permutation, sumMatrix);
     985            // Solve normalisation and background separately
     986            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     987            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     988
     989            psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
     990            if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
     991                psError(PS_ERR_UNKNOWN, false, "Unable to determine median normalisation");
     992                psFree(stats);
     993                psFree(sumMatrix);
     994                psFree(sumVector);
     995                psFree(norms);
     996                return false;
     997            }
     998
     999            double normValue = stats->robustMedian;
     1000            // double bgValue = 0.0;
     1001
     1002            psFree(stats);
     1003
     1004            fprintf(stderr, "Norm: %lf\n", normValue);
     1005
     1006            // Solve kernel components
     1007            for (int i = 0; i < numSolution1; i++) {
     1008                sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i];
     1009
     1010                sumMatrix->data.F64[i][normIndex] = 0.0;
     1011                sumMatrix->data.F64[normIndex][i] = 0.0;
     1012            }
     1013            sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex];
     1014            sumMatrix->data.F64[bgIndex][normIndex] = 0.0;
     1015            sumMatrix->data.F64[normIndex][bgIndex] = 0.0;
     1016
     1017            sumMatrix->data.F64[normIndex][normIndex] = 1.0;
     1018            sumVector->data.F64[normIndex] = 0.0;
     1019
     1020            solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1021
     1022            solution->data.F64[normIndex] = normValue;
     1023        }
     1024# endif
     1025
     1026        if (!kernels->solution1) {
     1027            kernels->solution1 = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     1028            psVectorInit(kernels->solution1, 0.0);
     1029        }
     1030
     1031        // only update the solutions that we chose to calculate:
     1032        if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     1033            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1034            kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
     1035        }
     1036        if (mode & PM_SUBTRACTION_EQUATION_BG) {
     1037            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     1038            kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
     1039        }
     1040        if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     1041            int numKernels = kernels->num;
     1042            int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     1043            int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
     1044            for (int i = 0; i < numKernels * numPoly; i++) {
     1045                kernels->solution1->data.F64[i] = solution->data.F64[i];
     1046            }
     1047        }
     1048
     1049        psFree(solution);
     1050        psFree(sumVector);
    8881051        psFree(sumMatrix);
    889         if (!luMatrix) {
    890             psError(PS_ERR_UNKNOWN, true, "LU Decomposition of least-squares matrix failed.\n");
    891             psFree(sumVector);
    892             psFree(luMatrix);
    893             psFree(permutation);
    894             return NULL;
    895         }
    896         kernels->solution1 = psMatrixLUSolution(kernels->solution1, luMatrix, sumVector, permutation);
    8971052
    8981053#ifdef TESTING
     
    9001055        for (int ix = 0; ix < kernels->solution1->n; ix++) {
    9011056            if (!isfinite(kernels->solution1->data.F64[ix])) {
    902                 fprintf (stderr, "WARNING: NAN in vector1\n");
    903             }
    904         }
    905 #endif
    906 
    907         psFree(sumVector);
    908         psFree(luMatrix);
    909         psFree(permutation);
    910         if (!kernels->solution1) {
    911             psError(PS_ERR_UNKNOWN, true, "Failed to solve the least-squares system.\n");
    912             return NULL;
    913         }
     1057                fprintf (stderr, "WARNING: NAN in vector\n");
     1058            }
     1059        }
     1060#endif
     1061
    9141062    } else {
    9151063        // Dual convolution solution
    9161064
    9171065        // Accumulation of stamp matrices/vectors
    918         psImage *sumMatrix1 = psImageAlloc(numParams, numParams, PS_TYPE_F64);
    919         psImage *sumMatrix2 = psImageAlloc(numParams2, numParams2, PS_TYPE_F64);
    920         psImage *sumMatrixX = psImageAlloc(numParams, numParams2, PS_TYPE_F64);
    921         psVector *sumVector1 = psVectorAlloc(numParams, PS_TYPE_F64);
    922         psVector *sumVector2 = psVectorAlloc(numParams, PS_TYPE_F64);
    923         psImageInit(sumMatrix1, 0.0);
    924         psImageInit(sumMatrix2, 0.0);
    925         psImageInit(sumMatrixX, 0.0);
    926         psVectorInit(sumVector1, 0.0);
    927         psVectorInit(sumVector2, 0.0);
     1066        psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
     1067        psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64);
     1068        psImageInit(sumMatrix, 0.0);
     1069        psVectorInit(sumVector, 0.0);
     1070
     1071        psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
    9281072
    9291073        int numStamps = 0;              // Number of good stamps
     
    9311075            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    9321076            if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
    933                 (void)psBinaryOp(sumMatrix1, sumMatrix1, "+", stamp->matrix1);
    934                 (void)psBinaryOp(sumMatrix2, sumMatrix2, "+", stamp->matrix2);
    935                 (void)psBinaryOp(sumMatrixX, sumMatrixX, "+", stamp->matrixX);
    936                 (void)psBinaryOp(sumVector1, sumVector1, "+", stamp->vector1);
    937                 (void)psBinaryOp(sumVector2, sumVector2, "+", stamp->vector2);
     1077                (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
     1078                (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
     1079
     1080                psVectorAppend(norms, stamp->norm);
     1081
    9381082                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
    9391083                numStamps++;
     
    9411085        }
    9421086
    943         int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    944         calculatePenalty(sumMatrix1, sumVector1, kernels, sumMatrix1->data.F64[bgIndex][bgIndex]);
    945         calculatePenalty(sumMatrix2, sumVector2, kernels, -sumMatrix1->data.F64[bgIndex][bgIndex]);
    946 
    947         psImage *sumMatrix = NULL;      // Combined matrix
    948         psVector *sumVector = NULL;     // Combined vector
    949         calculateEquationDual(&sumMatrix, &sumVector, sumMatrix1, sumMatrix2,
    950                               sumMatrixX, sumVector1, sumVector2);
    951 
    9521087#ifdef TESTING
     1088        psFitsWriteImageSimple ("sumMatrix.fits", sumMatrix, NULL);
     1089        psVectorWriteFile("sumVector.dat", sumVector);
     1090#endif
     1091
     1092#if 1
     1093        // int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     1094        // calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]);
     1095
     1096        int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1097        calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[normIndex][normIndex] / 1000.0);
     1098#endif
     1099
     1100        psVector *solution = NULL;                       // Solution to equation!
     1101        solution = psVectorAlloc(numParams, PS_TYPE_F64);
     1102        psVectorInit(solution, 0);
     1103
     1104#if 0
     1105        // Regular, straight-forward solution
     1106        solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1107#else
    9531108        {
    954             psFits *fits = psFitsOpen("sumMatrix.fits", "w");
    955             psFitsWriteImage(fits, NULL, sumMatrix, 0, NULL);
    956             psFitsClose(fits);
    957         }
    958         {
    959             psImage *image = psImageAlloc(1, numParams + numParams2, PS_TYPE_F64);
    960             psFits *fits = psFitsOpen("sumVector.fits", "w");
    961             for (int i = 0; i < numParams + numParams2; i++) {
    962                 image->data.F64[0][i] = sumVector->data.F64[i];
    963             }
    964             psFitsWriteImage(fits, NULL, image, 0, NULL);
    965             psFree(image);
    966             psFitsClose(fits);
    967         }
    968 #endif
    969 
    970         psVector *solution = NULL;                       // Solution to equation!
    971         {
    972             solution = psMatrixSolveSVD(solution, sumMatrix, sumVector);
    973             if (!solution) {
    974                 psError(PS_ERR_UNKNOWN, false, "SVD solution of least-squares equation failed.\n");
     1109            // Solve normalisation and background separately
     1110            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1111            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     1112
     1113#if 0
     1114            psImage *normMatrix = psImageAlloc(2, 2, PS_TYPE_F64);
     1115            psVector *normVector = psVectorAlloc(2, PS_TYPE_F64);
     1116
     1117            normMatrix->data.F64[0][0] = sumMatrix->data.F64[normIndex][normIndex];
     1118            normMatrix->data.F64[1][1] = sumMatrix->data.F64[bgIndex][bgIndex];
     1119            normMatrix->data.F64[0][1] = normMatrix->data.F64[1][0] = sumMatrix->data.F64[normIndex][bgIndex];
     1120
     1121            normVector->data.F64[0] = sumVector->data.F64[normIndex];
     1122            normVector->data.F64[1] = sumVector->data.F64[bgIndex];
     1123
     1124            psVector *normSolution = psMatrixSolveSVD(NULL, normMatrix, normVector, NAN);
     1125
     1126            double normValue = normSolution->data.F64[0];
     1127            double bgValue = normSolution->data.F64[1];
     1128
     1129            psFree(normMatrix);
     1130            psFree(normVector);
     1131            psFree(normSolution);
     1132#endif
     1133
     1134            psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
     1135            if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
     1136                psError(PS_ERR_UNKNOWN, false, "Unable to determine median normalisation");
     1137                psFree(stats);
    9751138                psFree(sumMatrix);
    9761139                psFree(sumVector);
    977                 return NULL;
    978             }
    979 
    980             int numSpatial = PM_SUBTRACTION_POLYTERMS(kernels->spatialOrder); // Number of spatial variations
    981             int numKernels = kernels->num; // Number of kernel basis functions
    982 
    983             // Remove a kernel basis for image 1 from the equation
    984 #define MASK_BASIS_1(INDEX)                                             \
    985             {                                                           \
    986                 for (int j = 0, index = INDEX; j < numSpatial; j++, index += numKernels) { \
    987                     for (int k = 0; k < numParams2; k++) {              \
    988                         sumMatrix1->data.F64[k][index] = 0.0;           \
    989                         sumMatrix1->data.F64[index][k] = 0.0;           \
    990                         sumMatrixX->data.F64[k][index] = 0.0;           \
    991                     }                                                   \
    992                     sumMatrix1->data.F64[bgIndex][index] = 0.0;         \
    993                     sumMatrix1->data.F64[index][bgIndex] = 0.0;         \
    994                     sumMatrix1->data.F64[normIndex][index] = 0.0;       \
    995                     sumMatrix1->data.F64[index][normIndex] = 0.0;       \
    996                     sumMatrix1->data.F64[index][index] = 1.0;           \
    997                     sumVector1->data.F64[index] = 0.0;                  \
    998                 }                                                       \
    999             }
    1000 
    1001             // Remove a kernel basis for image 2 from the equation
    1002 #define MASK_BASIS_2(INDEX)                                             \
    1003             {                                                           \
    1004                 for (int j = 0, index = INDEX; j < numSpatial; j++, index += numKernels) { \
    1005                     for (int k = 0; k < numParams2; k++) {              \
    1006                         sumMatrix2->data.F64[k][index] = 0.0;           \
    1007                         sumMatrix2->data.F64[index][k] = 0.0;           \
    1008                         sumMatrixX->data.F64[index][k] = 0.0;           \
    1009                     }                                                   \
    1010                     sumMatrix2->data.F64[index][index] = 1.0;           \
    1011                     sumMatrixX->data.F64[index][normIndex] = 0.0;       \
    1012                     sumMatrixX->data.F64[index][bgIndex] = 0.0;         \
    1013                     sumVector2->data.F64[index] = 0.0;                  \
    1014                 }                                                       \
    1015             }
    1016 
    1017             #define TOL 1.0e-5
    1018             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1019             double norm = fabs(solution->data.F64[normIndex]);  // Normalisation
    1020             double thresh = norm * TOL;                         // Threshold for low parameters
    1021             for (int i = 0; i < numKernels; i++) {
    1022                 // Getting 0th order parameter value.  In the presence of spatial variation, the actual value
    1023                 // of the parameter will vary over the image.  We are in effect getting the value in the
    1024                 // centre of the image.  If we use different polynomial functions (e.g., Chebyshev), we may
    1025                 // have to change this to properly determine the value of the parameter at the centre.
    1026                 double param1 = solution->data.F64[i],
    1027                     param2 = solution->data.F64[numParams + i]; // Parameters of interest
    1028                 bool mask1 = false, mask2 = false;              // Masked the parameter?
    1029                 if (fabs(param1) < thresh) {
    1030                     psTrace("psModules.imcombine", 7, "Parameter %d: 1 below threshold\n", i);
    1031                     MASK_BASIS_1(i);
    1032                     mask1 = true;
    1033                 }
    1034                 if (fabs(param2) < thresh) {
    1035                     psTrace("psModules.imcombine", 7, "Parameter %d: 2 below threshold\n", i);
    1036                     MASK_BASIS_2(i);
    1037                     mask2 = true;
    1038                 }
    1039 
    1040                 if (!mask1 && !mask2) {
    1041                     if (fabs(param1) < fabs(param2)) {
    1042                         psTrace("psModules.imcombine", 7, "Parameter %d: 1 < 2\n", i);
    1043                         MASK_BASIS_1(i);
    1044                     } else {
    1045                         psTrace("psModules.imcombine", 7, "Parameter %d: 2 < 1\n", i);
    1046                         MASK_BASIS_2(i);
    1047                     }
    1048                 }
    1049             }
    1050         }
    1051 
    1052         calculateEquationDual(&sumMatrix, &sumVector, sumMatrix1, sumMatrix2,
    1053                               sumMatrixX, sumVector1, sumVector2);
     1140                psFree(norms);
     1141                return false;
     1142            }
     1143
     1144            double normValue = stats->robustMedian;
     1145
     1146            psFree(stats);
     1147
     1148            fprintf(stderr, "Norm: %lf\n", normValue);
     1149
     1150            // Solve kernel components
     1151            for (int i = 0; i < numSolution2; i++) {
     1152                sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i];
     1153                sumVector->data.F64[i + numSolution1] -= normValue * sumMatrix->data.F64[normIndex][i + numSolution1];
     1154
     1155                sumMatrix->data.F64[i][normIndex] = 0.0;
     1156                sumMatrix->data.F64[normIndex][i] = 0.0;
     1157
     1158                sumMatrix->data.F64[i + numSolution1][normIndex] = 0.0;
     1159                sumMatrix->data.F64[normIndex][i + numSolution1] = 0.0;
     1160            }
     1161            sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex];
     1162            sumMatrix->data.F64[bgIndex][normIndex] = 0.0;
     1163            sumMatrix->data.F64[normIndex][bgIndex] = 0.0;
     1164
     1165            sumMatrix->data.F64[normIndex][normIndex] = 1.0;
     1166
     1167            sumVector->data.F64[normIndex] = 0.0;
     1168
     1169            solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1170
     1171            solution->data.F64[normIndex] = normValue;
     1172        }
     1173#endif
     1174
    10541175
    10551176#ifdef TESTING
    1056         {
    1057             psFits *fits = psFitsOpen("sumMatrixFix.fits", "w");
    1058             psFitsWriteImage(fits, NULL, sumMatrix, 0, NULL);
    1059             psFitsClose(fits);
    1060         }
    1061         {
    1062             psImage *image = psImageAlloc(1, numParams + numParams2, PS_TYPE_F64);
    1063             psFits *fits = psFitsOpen("sumVectorFix.fits", "w");
    1064             for (int i = 0; i < numParams + numParams2; i++) {
    1065                 image->data.F64[0][i] = sumVector->data.F64[i];
    1066             }
    1067             psFitsWriteImage(fits, NULL, image, 0, NULL);
    1068             psFree(image);
    1069             psFitsClose(fits);
    1070         }
    1071 #endif
    1072 
    1073         solution = psMatrixSolveSVD(solution, sumMatrix, sumVector);
    1074         if (!solution) {
    1075             psError(PS_ERR_UNKNOWN, false, "SVD solution of least-squares equation failed.\n");
    1076             psFree(sumMatrix);
    1077             psFree(sumVector);
    1078             return NULL;
    1079         }
    1080 
    1081         psFree(sumMatrix1);
    1082         psFree(sumMatrix2);
    1083         psFree(sumMatrixX);
    1084         psFree(sumVector1);
    1085         psFree(sumVector2);
     1177        for (int i = 0; i < solution->n; i++) {
     1178            fprintf(stderr, "Dual solution %d: %lf\n", i, solution->data.F64[i]);
     1179        }
     1180#endif
    10861181
    10871182        psFree(sumMatrix);
    10881183        psFree(sumVector);
    10891184
    1090 #ifdef TESTING
    1091         {
    1092             psImage *image = psImageAlloc(1, numParams + numParams2, PS_TYPE_F64);
    1093             psFits *fits = psFitsOpen("solnVector.fits", "w");
    1094             for (int i = 0; i < numParams + numParams2; i++) {
    1095                 image->data.F64[0][i] = solution->data.F64[i];
    1096             }
    1097             psFitsWriteImage(fits, NULL, image, 0, NULL);
    1098             psFree(image);
    1099             psFitsClose(fits);
    1100         }
    1101 #endif
     1185        psFree(norms);
    11021186
    11031187        if (!kernels->solution1) {
    1104             kernels->solution1 = psVectorAlloc(numParams, PS_TYPE_F64);
     1188            kernels->solution1 = psVectorAlloc(numSolution1, PS_TYPE_F64);
     1189            psVectorInit (kernels->solution1, 0.0);
    11051190        }
    11061191        if (!kernels->solution2) {
    1107             kernels->solution2 = psVectorAlloc(numParams2, PS_TYPE_F64);
    1108         }
    1109 
    1110         memcpy(kernels->solution1->data.F64, solution->data.F64, numParams * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    1111         memcpy(kernels->solution2->data.F64, &solution->data.F64[numParams],
    1112                numParams2 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
     1192            kernels->solution2 = psVectorAlloc(numSolution2, PS_TYPE_F64);
     1193            psVectorInit (kernels->solution2, 0.0);
     1194        }
     1195
     1196        // only update the solutions that we chose to calculate:
     1197        if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     1198            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1199            kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
     1200        }
     1201        if (mode & PM_SUBTRACTION_EQUATION_BG) {
     1202            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     1203            kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
     1204        }
     1205        if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     1206            int numKernels = kernels->num;
     1207            for (int i = 0; i < numKernels * numSpatial; i++) {
     1208                // XXX fprintf (stderr, "keep\n");
     1209                kernels->solution1->data.F64[i] = solution->data.F64[i];
     1210                kernels->solution2->data.F64[i] = solution->data.F64[i + numSolution1];
     1211            }
     1212        }
     1213
     1214
     1215        memcpy(kernels->solution1->data.F64, solution->data.F64,
     1216               numSolution1 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
     1217        memcpy(kernels->solution2->data.F64, &solution->data.F64[numSolution1],
     1218               numSolution2 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    11131219
    11141220        psFree(solution);
     
    11311237     }
    11321238
    1133     pmSubtractionVisualPlotLeastSquares((pmSubtractionStampList *) stamps); //casting away const
     1239    // pmSubtractionVisualPlotLeastSquares((pmSubtractionStampList *) stamps); //casting away const
    11341240    return true;
    11351241}
    11361242
     1243bool pmSubtractionResidualStats(psVector *fSigRes, psVector *fMaxRes, psVector *fMinRes, psKernel *target, psKernel *source, psKernel *residual, double norm, int footprint) {
     1244
     1245    // XXX measure some useful stats on the residuals
     1246    float sum = 0.0;
     1247    float peak = 0.0;
     1248    for (int y = - footprint; y <= footprint; y++) {
     1249        for (int x = - footprint; x <= footprint; x++) {
     1250            sum += 0.5*(target->kernel[y][x] + source->kernel[y][x] * norm);
     1251            peak = PS_MAX(peak, 0.5*(target->kernel[y][x] + source->kernel[y][x] * norm));
     1252        }
     1253    }
     1254
     1255    // only count pixels with more than X% of the source flux
     1256    // calculate stdev(dflux)
     1257    float dflux1 = 0.0;
     1258    float dflux2 = 0.0;
     1259    int npix = 0;
     1260
     1261    float dmax = 0.0;
     1262    float dmin = 0.0;
     1263
     1264    for (int y = - footprint; y <= footprint; y++) {
     1265        for (int x = - footprint; x <= footprint; x++) {
     1266            float dflux = 0.5*(target->kernel[y][x] + source->kernel[y][x] * norm);
     1267            if (dflux < 0.02*sum) continue;
     1268            dflux1 += residual->kernel[y][x];
     1269            dflux2 += PS_SQR(residual->kernel[y][x]);
     1270            dmax = PS_MAX(residual->kernel[y][x], dmax);
     1271            dmin = PS_MIN(residual->kernel[y][x], dmin);
     1272            npix ++;
     1273        }
     1274    }
     1275    float sigma = sqrt(dflux2 / npix - PS_SQR(dflux1/npix));
     1276    if (!isfinite(sum))  return false;
     1277    if (!isfinite(dmax)) return false;
     1278    if (!isfinite(dmin)) return false;
     1279    if (!isfinite(peak)) return false;
     1280
     1281    // fprintf (stderr, "sum: %f, peak: %f, sigma: %f, fsigma: %f, fmax: %f, fmin: %f\n", sum, peak, sigma, sigma/sum, dmax/peak, dmin/peak);
     1282    psVectorAppend(fSigRes, sigma/sum);
     1283    psVectorAppend(fMaxRes, dmax/peak);
     1284    psVectorAppend(fMinRes, dmin/peak);
     1285    return true;
     1286}
     1287
    11371288psVector *pmSubtractionCalculateDeviations(pmSubtractionStampList *stamps,
    1138                                            const pmSubtractionKernels *kernels)
     1289                                           pmSubtractionKernels *kernels)
    11391290{
    11401291    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, NULL);
     
    11511302    psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
    11521303
     1304    // set up holding images for the visualization
     1305    pmSubtractionVisualShowFitInit (stamps);
     1306
     1307    psVector *fSigRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1308    psVector *fMinRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1309    psVector *fMaxRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1310
     1311    // we want to save the residual images for the 9 brightest stamps.
     1312    // identify the 9 brightest stamps
     1313    psVector *keepStamps  = psVectorAlloc(stamps->num, PS_TYPE_S32);
     1314    psVectorInit (keepStamps, 0);
     1315    {
     1316        psVector *flux  = psVectorAlloc(stamps->num, PS_TYPE_F32);
     1317        psVectorInit (flux, 0.0);
     1318
     1319        for (int i = 0; i < stamps->num; i++) {
     1320            pmSubtractionStamp *stamp = stamps->stamps->data[i];
     1321            if (!isfinite(stamp->flux)) continue;
     1322            flux->data.F32[i] = stamp->flux;
     1323        }
     1324
     1325        psVector *index = psVectorSortIndex(NULL, flux);
     1326        for (int i = 0; (i < stamps->num) && (i < 9); i++) {
     1327            int n = stamps->num - i - 1;
     1328            keepStamps->data.S32[index->data.S32[n]] = 1;
     1329            fprintf (stderr, "keeping %d (%d of %d)\n", index->data.S32[n], n, 9);
     1330        }
     1331        psFree (flux);
     1332        psFree (index);
     1333
     1334        // this function is called multiple times in the iteration, but
     1335        // we only know after the interation is done if we will try again.
     1336        // therefore we must save the sample each time, and blow away the old one
     1337        // if it exists.
     1338        psFree (kernels->sampleStamps);
     1339        kernels->sampleStamps = psArrayAllocEmpty(9);
     1340    }
     1341
     1342    psString log = psStringCopy("Deviations:\n");               // Log message with deviations
    11531343    for (int i = 0; i < stamps->num; i++) {
    11541344        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // The stamp of interest
     
    12101400                for (int y = - footprint; y <= footprint; y++) {
    12111401                    for (int x = - footprint; x <= footprint; x++) {
    1212                         residual->kernel[y][x] -= convolution->kernel[y][x] * coefficient;
     1402                        residual->kernel[y][x] += convolution->kernel[y][x] * coefficient;
    12131403                    }
    12141404                }
    12151405            }
     1406
     1407            // XXX visualize the target, source, convolution and residual
     1408            pmSubtractionVisualShowFitAddStamp (target, source, residual, background, norm, i);
     1409
    12161410            for (int y = - footprint; y <= footprint; y++) {
    12171411                for (int x = - footprint; x <= footprint; x++) {
    1218                     residual->kernel[y][x] += target->kernel[y][x] - background - source->kernel[y][x] * norm;
    1219                 }
    1220             }
     1412                    residual->kernel[y][x] += background + source->kernel[y][x] * norm - target->kernel[y][x];
     1413                }
     1414            }
     1415
     1416            if (keepStamps->data.S32[i]) {
     1417                psImage *sample = psImageCopy(NULL, residual->image, PS_TYPE_F32);
     1418                psArrayAdd (kernels->sampleStamps, 9, sample);
     1419                psFree (sample);
     1420            }
     1421
     1422            pmSubtractionResidualStats(fSigRes, fMaxRes, fMinRes, target, source, residual, norm, footprint);
     1423
    12211424        } else {
    12221425            // Dual convolution
     
    12341437                for (int y = - footprint; y <= footprint; y++) {
    12351438                    for (int x = - footprint; x <= footprint; x++) {
    1236                         residual->kernel[y][x] += conv2->kernel[y][x] * coeff2 - conv1->kernel[y][x] * coeff1;
     1439                        residual->kernel[y][x] += conv2->kernel[y][x] * coeff2 + conv1->kernel[y][x] * coeff1;
    12371440                    }
    12381441                }
    12391442            }
     1443
     1444            // XXX visualize the target, source, convolution and residual
     1445            pmSubtractionVisualShowFitAddStamp (image2, image1, residual, background, norm, i);
     1446
    12401447            for (int y = - footprint; y <= footprint; y++) {
    12411448                for (int x = - footprint; x <= footprint; x++) {
    1242                     residual->kernel[y][x] += image2->kernel[y][x] - background - image1->kernel[y][x] * norm;
    1243                 }
    1244             }
     1449                    residual->kernel[y][x] += background + image1->kernel[y][x] * norm - image2->kernel[y][x];
     1450                }
     1451            }
     1452            if (keepStamps->data.S32[i]) {
     1453                psImage *sample = psImageCopy(NULL, residual->image, PS_TYPE_F32);
     1454                psArrayAdd (kernels->sampleStamps, 9, sample);
     1455                psFree (sample);
     1456            }
     1457
     1458            pmSubtractionResidualStats(fSigRes, fMaxRes, fMinRes, image1, image2, residual, norm, footprint);
    12451459        }
    12461460
     
    12571471        deviations->data.F32[i] = devNorm * deviation;
    12581472        psTrace("psModules.imcombine", 5, "Deviation for stamp %d (%d,%d): %f\n",
    1259                 i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5), deviations->data.F32[i]);
     1473                i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5), deviations->data.F32[i]);
     1474        psStringAppend(&log, "Stamp %d (%d,%d): %f\n",
     1475                       i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5), deviations->data.F32[i]);
    12601476        if (!isfinite(deviations->data.F32[i])) {
    12611477            stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
    12621478            psTrace("psModules.imcombine", 5,
    12631479                    "Rejecting stamp %d (%d,%d) because of non-finite deviation\n",
    1264                     i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
     1480                    i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
    12651481            continue;
    12661482        }
     
    13021518
    13031519    }
     1520
     1521    psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "%s", log);
     1522    psFree(log);
     1523
     1524    // calculate and report the normalization and background for the image center
     1525    {
     1526        polyValues = p_pmSubtractionPolynomial(polyValues, kernels->spatialOrder, 0.0, 0.0);
     1527        double norm = p_pmSubtractionSolutionNorm(kernels); // Normalisation
     1528        double background = p_pmSubtractionSolutionBackground(kernels, polyValues);// Difference in background
     1529        psLogMsg("psModules.imcombine", PS_LOG_INFO, "normalization: %f, background: %f", norm, background);
     1530
     1531        pmSubtractionVisualShowFit(norm);
     1532        pmSubtractionVisualPlotFit(kernels);
     1533
     1534        psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
     1535        psVectorStats (stats, fSigRes, NULL, NULL, 0);
     1536        kernels->fSigResMean = stats->robustMedian;
     1537        kernels->fSigResStdev = stats->robustStdev;
     1538
     1539        psStatsInit (stats);
     1540        psVectorStats (stats, fMaxRes, NULL, NULL, 0);
     1541        kernels->fMaxResMean = stats->robustMedian;
     1542        kernels->fMaxResStdev = stats->robustStdev;
     1543
     1544        psStatsInit (stats);
     1545        psVectorStats (stats, fMinRes, NULL, NULL, 0);
     1546        kernels->fMinResMean = stats->robustMedian;
     1547        kernels->fMinResStdev = stats->robustStdev;
     1548
     1549        // XXX save these values somewhere
     1550        psLogMsg("psModules.imcombine", PS_LOG_INFO, "fSigma: %f +/- %f, fMaxRes: %f +/- %f, fMinRes: %f +/- %f",
     1551                 kernels->fSigResMean, kernels->fSigResStdev,
     1552                 kernels->fMaxResMean, kernels->fMaxResStdev,
     1553                 kernels->fMinResMean, kernels->fMinResStdev);
     1554
     1555        psFree (fSigRes);
     1556        psFree (fMaxRes);
     1557        psFree (fMinRes);
     1558        psFree (stats);
     1559    }
     1560
    13041561    psFree(residual);
    13051562    psFree(polyValues);
    13061563
     1564
    13071565    return deviations;
    13081566}
     1567
     1568// we are supplied U, not Ut; w represents a diagonal matrix (also, we apply 1/w instead of w)
     1569psImage *p_pmSubSolve_wUt (psVector *w, psImage *U) {
     1570
     1571    psAssert (w->n == U->numCols, "w and U dimensions do not match");
     1572
     1573    // wUt has dimensions transposed relative to Ut.
     1574    psImage *wUt = psImageAlloc (U->numRows, U->numCols, PS_TYPE_F64);
     1575    psImageInit (wUt, 0.0);
     1576
     1577    for (int i = 0; i < wUt->numCols; i++) {
     1578        for (int j = 0; j < wUt->numRows; j++) {
     1579            if (!isfinite(w->data.F64[j])) continue;
     1580            if (w->data.F64[j] == 0.0) continue;
     1581            wUt->data.F64[j][i] = U->data.F64[i][j] / w->data.F64[j];
     1582        }
     1583    }
     1584    return wUt;
     1585}
     1586
     1587// XXX this is just standard matrix multiplication: use psMatrixMultiply?
     1588psImage *p_pmSubSolve_VwUt (psImage *V, psImage *wUt) {
     1589
     1590    psAssert (V->numCols == wUt->numRows, "matrix dimensions do not match");
     1591
     1592    psImage *Ainv = psImageAlloc (wUt->numCols, V->numRows, PS_TYPE_F64);
     1593
     1594    for (int i = 0; i < Ainv->numCols; i++) {
     1595        for (int j = 0; j < Ainv->numRows; j++) {
     1596            double sum = 0.0;
     1597            for (int k = 0; k < V->numCols; k++) {
     1598                sum += V->data.F64[j][k] * wUt->data.F64[k][i];
     1599            }
     1600            Ainv->data.F64[j][i] = sum;
     1601        }
     1602    }
     1603    return Ainv;
     1604}
     1605
     1606// we are supplied U, not Ut
     1607bool p_pmSubSolve_UtB (psVector **UtB, psImage *U, psVector *B) {
     1608
     1609    psAssert (U->numRows == B->n, "U and B dimensions do not match");
     1610
     1611    UtB[0] = psVectorRecycle (UtB[0], U->numCols, PS_TYPE_F64);
     1612
     1613    for (int i = 0; i < U->numCols; i++) {
     1614        double sum = 0.0;
     1615        for (int j = 0; j < U->numRows; j++) {
     1616            sum += B->data.F64[j] * U->data.F64[j][i];
     1617        }
     1618        UtB[0]->data.F64[i] = sum;
     1619    }
     1620    return true;
     1621}
     1622
     1623// w is diagonal
     1624bool p_pmSubSolve_wUtB (psVector **wUtB, psVector *w, psVector *UtB) {
     1625
     1626    psAssert (w->n == UtB->n, "w and UtB dimensions do not match");
     1627
     1628    // wUt has dimensions transposed relative to Ut.
     1629    wUtB[0] = psVectorRecycle (wUtB[0], w->n, PS_TYPE_F64);
     1630    psVectorInit (wUtB[0], 0.0);
     1631
     1632    for (int i = 0; i < w->n; i++) {
     1633        if (!isfinite(w->data.F64[i])) continue;
     1634        if (w->data.F64[i] == 0.0) continue;
     1635        wUtB[0]->data.F64[i] = UtB->data.F64[i] / w->data.F64[i];
     1636    }
     1637    return true;
     1638}
     1639
     1640// this is basically matrix * vector
     1641bool p_pmSubSolve_VwUtB (psVector **VwUtB, psImage *V, psVector *wUtB) {
     1642
     1643    psAssert (V->numCols == wUtB->n, "V and wUtB dimensions do not match");
     1644
     1645    VwUtB[0] = psVectorRecycle (*VwUtB, V->numRows, PS_TYPE_F64);
     1646
     1647    for (int j = 0; j < V->numRows; j++) {
     1648        double sum = 0.0;
     1649        for (int i = 0; i < V->numCols; i++) {
     1650            sum += V->data.F64[j][i] * wUtB->data.F64[i];
     1651        }
     1652        VwUtB[0]->data.F64[j] = sum;
     1653    }
     1654    return true;
     1655}
     1656
     1657// this is basically matrix * vector
     1658bool p_pmSubSolve_Ax (psVector **B, psImage *A, psVector *x) {
     1659
     1660    psAssert (A->numCols == x->n, "A and x dimensions do not match");
     1661
     1662    B[0] = psVectorRecycle (*B, A->numRows, PS_TYPE_F64);
     1663
     1664    for (int j = 0; j < A->numRows; j++) {
     1665        double sum = 0.0;
     1666        for (int i = 0; i < A->numCols; i++) {
     1667            sum += A->data.F64[j][i] * x->data.F64[i];
     1668        }
     1669        B[0]->data.F64[j] = sum;
     1670    }
     1671    return true;
     1672}
     1673
     1674// this is basically Vector * vector
     1675bool p_pmSubSolve_VdV (double *value, psVector *x, psVector *y) {
     1676
     1677    psAssert (x->n == y->n, "x and y dimensions do not match");
     1678
     1679    double sum = 0.0;
     1680    for (int i = 0; i < x->n; i++) {
     1681        sum += x->data.F64[i] * y->data.F64[i];
     1682    }
     1683    *value = sum;
     1684    return true;
     1685}
     1686
     1687bool p_pmSubSolve_y2 (double *y2, pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps) {
     1688
     1689    int footprint = stamps->footprint; // Half-size of stamps
     1690
     1691    double sum = 0.0;
     1692    for (int i = 0; i < stamps->num; i++) {
     1693
     1694        pmSubtractionStamp *stamp = stamps->stamps->data[i];
     1695        if (stamp->status != PM_SUBTRACTION_STAMP_USED) continue;
     1696
     1697        psKernel *weight = NULL;
     1698        psKernel *window = NULL;
     1699        psKernel *input = NULL;
     1700
     1701#ifdef USE_WEIGHT
     1702        weight = stamp->weight;
     1703#endif
     1704#ifdef USE_WINDOW
     1705        window = stamps->window;
     1706#endif
     1707
     1708        switch (kernels->mode) {
     1709            // MODE_1 : convolve image 1 to match image 2 (and vice versa)
     1710          case PM_SUBTRACTION_MODE_1:
     1711            input = stamp->image2;
     1712            break;
     1713          case PM_SUBTRACTION_MODE_2:
     1714            input = stamp->image1;
     1715            break;
     1716          default:
     1717            psAbort ("programming error");
     1718        }
     1719
     1720        for (int y = - footprint; y <= footprint; y++) {
     1721            for (int x = - footprint; x <= footprint; x++) {
     1722                double in = input->kernel[y][x];
     1723                double value = in*in;
     1724                if (weight) {
     1725                    float wtVal = weight->kernel[y][x];
     1726                    value *= wtVal;
     1727                }
     1728                if (window) {
     1729                    float  winVal = window->kernel[y][x];
     1730                    value *= winVal;
     1731                }
     1732                sum += value;
     1733            }
     1734        }
     1735    }
     1736    *y2 = sum;
     1737    return true;
     1738}
     1739
     1740double p_pmSubSolve_ChiSquare (pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps) {
     1741
     1742    int footprint = stamps->footprint; // Half-size of stamps
     1743    int numKernels = kernels->num;      // Number of kernels
     1744
     1745    double sum = 0.0;
     1746
     1747    psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
     1748    psImageInit(residual->image, 0.0);
     1749
     1750    psImage *polyValues = NULL;         // Polynomial values
     1751
     1752    for (int i = 0; i < stamps->num; i++) {
     1753
     1754        pmSubtractionStamp *stamp = stamps->stamps->data[i];
     1755        if (stamp->status != PM_SUBTRACTION_STAMP_USED) continue;
     1756
     1757        psKernel *weight = NULL;
     1758        psKernel *window = NULL;
     1759        psKernel *target = NULL;
     1760        psKernel *source = NULL;
     1761
     1762        psArray *convolutions = NULL;
     1763
     1764#ifdef USE_WEIGHT
     1765        weight = stamp->weight;
     1766#endif
     1767#ifdef USE_WINDOW
     1768        window = stamps->window;
     1769#endif
     1770
     1771        switch (kernels->mode) {
     1772            // MODE_1 : convolve image 1 to match image 2 (and vice versa)
     1773          case PM_SUBTRACTION_MODE_1:
     1774            target = stamp->image2;
     1775            source = stamp->image1;
     1776            convolutions = stamp->convolutions1;
     1777            break;
     1778          case PM_SUBTRACTION_MODE_2:
     1779            target = stamp->image1;
     1780            source = stamp->image2;
     1781            convolutions = stamp->convolutions2;
     1782            break;
     1783          default:
     1784            psAbort ("programming error");
     1785        }
     1786
     1787        // Calculate coefficients of the kernel basis functions
     1788        polyValues = p_pmSubtractionPolynomial(polyValues, kernels->spatialOrder, stamp->xNorm, stamp->yNorm);
     1789        double norm = p_pmSubtractionSolutionNorm(kernels); // Normalisation
     1790        double background = p_pmSubtractionSolutionBackground(kernels, polyValues);// Difference in background
     1791
     1792        psImageInit(residual->image, 0.0);
     1793        for (int j = 0; j < numKernels; j++) {
     1794            psKernel *convolution = convolutions->data[j]; // Convolution
     1795            double coefficient = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, false); // Coefficient
     1796            for (int y = - footprint; y <= footprint; y++) {
     1797                for (int x = - footprint; x <= footprint; x++) {
     1798                    residual->kernel[y][x] -= convolution->kernel[y][x] * coefficient;
     1799                }
     1800            }
     1801        }
     1802
     1803        for (int y = - footprint; y <= footprint; y++) {
     1804            for (int x = - footprint; x <= footprint; x++) {
     1805                double resid = target->kernel[y][x] - background - source->kernel[y][x] * norm + residual->kernel[y][x];
     1806                double value = PS_SQR(resid);
     1807                if (weight) {
     1808                    float wtVal = weight->kernel[y][x];
     1809                    value *= wtVal;
     1810                }
     1811                if (window) {
     1812                    float  winVal = window->kernel[y][x];
     1813                    value *= winVal;
     1814                }
     1815                sum += value;
     1816            }
     1817        }
     1818    }
     1819    psFree (polyValues);
     1820    psFree (residual);
     1821
     1822    return sum;
     1823}
     1824
     1825bool p_pmSubSolve_SetWeights (psVector *wApply, psVector *w, psVector *wMask) {
     1826
     1827    for (int i = 0; i < w->n; i++) {
     1828        wApply->data.F64[i] = wMask->data.U8[i] ? 0.0 : w->data.F64[i];
     1829    }
     1830    return true;
     1831}
     1832
     1833// we are supplied V and w; w represents a diagonal matrix (also, we apply 1/w instead of w)
     1834psImage *p_pmSubSolve_Xvar (psImage *V, psVector *w) {
     1835
     1836    psAssert (w->n == V->numCols, "w and U dimensions do not match");
     1837
     1838    psImage *Vn = psImageAlloc (V->numCols, V->numRows, PS_TYPE_F64);
     1839    psImageInit (Vn, 0.0);
     1840
     1841    // generate Vn = V * w^{-1}
     1842    for (int j = 0; j < Vn->numRows; j++) {
     1843        for (int i = 0; i < Vn->numCols; i++) {
     1844            if (!isfinite(w->data.F64[i])) continue;
     1845            if (w->data.F64[i] == 0.0) continue;
     1846            Vn->data.F64[j][i] = V->data.F64[j][i] / w->data.F64[i];
     1847        }
     1848    }
     1849
     1850    psImage *Xvar = psImageAlloc (V->numCols, V->numRows, PS_TYPE_F64);
     1851    psImageInit (Xvar, 0.0);
     1852
     1853    // generate Xvar = Vn * Vn^T
     1854    for (int j = 0; j < Vn->numRows; j++) {
     1855        for (int i = 0; i < Vn->numCols; i++) {
     1856            double sum = 0.0;
     1857            for (int k = 0; k < Vn->numCols; k++) {
     1858                sum += Vn->data.F64[k][i]*Vn->data.F64[k][j];
     1859            }
     1860            Xvar->data.F64[j][i] = sum;
     1861        }
     1862    }
     1863    return Xvar;
     1864}
     1865
     1866// I get confused by the index values between the image vs matrix usage:  In terms
     1867// of the elements of an image A(x,y) = A->data.F64[y][x] = A_x,y, a matrix
     1868// multiplication is: A_k,j * B_i,k = C_i,j
     1869
     1870
     1871bool psFitsWriteImageSimple (char *filename, psImage *image, psMetadata *header) {
     1872
     1873    psFits *fits = psFitsOpen(filename, "w");
     1874    psFitsWriteImage(fits, header, image, 0, NULL);
     1875    psFitsClose(fits);
     1876
     1877    return true;
     1878}
     1879
     1880bool psVectorWriteFile (char *filename, const psVector *vector) {
     1881
     1882    FILE *f = fopen (filename, "w");
     1883    int fd = fileno(f);
     1884    p_psVectorPrint (fd, vector, "unnamed");
     1885    fclose (f);
     1886
     1887    return true;
     1888}
     1889
     1890
     1891# if 0
     1892
     1893#ifdef TESTING
     1894        psFitsWriteImageSimple("A.fits", sumMatrix, NULL);
     1895        psVectorWriteFile ("B.dat", sumVector);
     1896#endif
     1897
     1898# define SVD_ANALYSIS 0
     1899# define COEFF_SIG 0.0
     1900# define SVD_TOL 0.0
     1901
     1902        // Use SVD to determine the kernel coeffs (and validate)
     1903        if (SVD_ANALYSIS) {
     1904
     1905            // We have sumVector and sumMatrix.  we are trying to solve the following equation:
     1906            // sumMatrix * x = sumVector.
     1907
     1908            // we can use any standard matrix inversion to solve this.  However, the basis
     1909            // functions in general have substantial correlation, so that the solution may be
     1910            // somewhat poorly determined or unstable.  If not numerically ill-conditioned, the
     1911            // system of equations may be statistically ill-conditioned.  Noise in the image
     1912            // will drive insignificant, but correlated, terms in the solution.  To avoid these
     1913            // problems, we can use SVD to identify numerically unconstrained values and to
     1914            // avoid statistically badly determined value.
     1915
     1916            // A = sumMatrix, B = sumVector
     1917            // SVD: A = U w V^T  -> A^{-1} = V (1/w) U^T
     1918            // x = V (1/w) (U^T B)
     1919            // \sigma_x = sqrt(diag(A^{-1}))
     1920            // solve for x and A^{-1} to get x & dx
     1921            // identify the elements of (1/w) that are nan (1/0.0) -> set to 0.0
     1922            // identify the elements of x that are insignificant (x / dx < 1.0? < 0.5?) -> set to 0.0
     1923
     1924            // If I use the SVD trick to re-condition the matrix, I need to break out the
     1925            // kernel and normalization terms from the background term.
     1926            // XXX is this true?  or was this due to an error in the analysis?
     1927
     1928            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     1929
     1930            // now pull out the kernel elements into their own square matrix
     1931            psImage  *kernelMatrix = psImageAlloc  (sumMatrix->numCols - 1, sumMatrix->numRows - 1, PS_TYPE_F64);
     1932            psVector *kernelVector = psVectorAlloc (sumMatrix->numCols - 1, PS_TYPE_F64);
     1933
     1934            for (int ix = 0, kx = 0; ix < sumMatrix->numCols; ix++) {
     1935                if (ix == bgIndex) continue;
     1936                for (int iy = 0, ky = 0; iy < sumMatrix->numRows; iy++) {
     1937                    if (iy == bgIndex) continue;
     1938                    kernelMatrix->data.F64[ky][kx] = sumMatrix->data.F64[iy][ix];
     1939                    ky++;
     1940                }
     1941                kernelVector->data.F64[kx] = sumVector->data.F64[ix];
     1942                kx++;
     1943            }
     1944
     1945            psImage *U = NULL;
     1946            psImage *V = NULL;
     1947            psVector *w = NULL;
     1948            if (!psMatrixSVD (&U, &w, &V, kernelMatrix)) {
     1949                psError(PS_ERR_UNKNOWN, false, "failed to perform SVD on sumMatrix\n");
     1950                return NULL;
     1951            }
     1952
     1953            // calculate A_inverse:
     1954            // Ainv = V * w * U^T
     1955            psImage *wUt  = p_pmSubSolve_wUt (w, U);
     1956            psImage *Ainv = p_pmSubSolve_VwUt (V, wUt);
     1957            psImage *Xvar = NULL;
     1958            psFree (wUt);
     1959
     1960# ifdef TESTING
     1961            // kernel terms:
     1962            for (int i = 0; i < w->n; i++) {
     1963                fprintf (stderr, "w: %f\n", w->data.F64[i]);
     1964            }
     1965# endif
     1966            // loop over w adding in more and more of the values until chisquare is no longer
     1967            // dropping significantly.
     1968            // XXX this does not seem to work very well: we seem to need all terms even for
     1969            // simple cases...
     1970
     1971            psVector *Xsvd = NULL;
     1972            {
     1973                psVector *Ax = NULL;
     1974                psVector *UtB = NULL;
     1975                psVector *wUtB = NULL;
     1976
     1977                psVector *wApply = psVectorAlloc(w->n, PS_TYPE_F64);
     1978                psVector *wMask = psVectorAlloc(w->n, PS_TYPE_U8);
     1979                psVectorInit (wMask, 1); // start by masking everything
     1980
     1981                double chiSquareLast = NAN;
     1982                int maxWeight = 0;
     1983
     1984                double Axx, Bx, y2;
     1985
     1986                // XXX this is an attempt to exclude insignificant modes.
     1987                // it was not successful with the ISIS kernel set: removing even
     1988                // the least significant mode leaves additional ringing / noise
     1989                // because the terms are so coupled.
     1990                for (int k = 0; false && (k < w->n); k++) {
     1991
     1992                    // unmask the k-th weight
     1993                    wMask->data.U8[k] = 0;
     1994                    p_pmSubSolve_SetWeights(wApply, w, wMask);
     1995
     1996                    // solve for x:
     1997                    // x = V * w * (U^T * B)
     1998                    p_pmSubSolve_UtB (&UtB, U, kernelVector);
     1999                    p_pmSubSolve_wUtB (&wUtB, wApply, UtB);
     2000                    p_pmSubSolve_VwUtB (&Xsvd, V, wUtB);
     2001
     2002                    // chi-square for this system of equations:
     2003                    // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2
     2004                    // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2
     2005                    p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd);
     2006                    p_pmSubSolve_VdV (&Axx, Ax, Xsvd);
     2007                    p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd);
     2008                    p_pmSubSolve_y2 (&y2, kernels, stamps);
     2009
     2010                    // apparently, this works (compare with the brute force value below
     2011                    double chiSquare = Axx - 2.0*Bx + y2;
     2012                    double deltaChi = (k == 0) ? chiSquare : chiSquareLast - chiSquare;
     2013                    chiSquareLast = chiSquare;
     2014
     2015                    // fprintf (stderr, "chi square = %f, delta: %f\n", chiSquare, deltaChi);
     2016                    if (k && !maxWeight && (deltaChi < 1.0)) {
     2017                        maxWeight = k;
     2018                    }
     2019                }
     2020
     2021                // keep all terms or we get extra ringing
     2022                maxWeight = w->n;
     2023                psVectorInit (wMask, 1);
     2024                for (int k = 0; k < maxWeight; k++) {
     2025                    wMask->data.U8[k] = 0;
     2026                }
     2027                p_pmSubSolve_SetWeights(wApply, w, wMask);
     2028
     2029                // solve for x:
     2030                // x = V * w * (U^T * B)
     2031                p_pmSubSolve_UtB (&UtB, U, kernelVector);
     2032                p_pmSubSolve_wUtB (&wUtB, wApply, UtB);
     2033                p_pmSubSolve_VwUtB (&Xsvd, V, wUtB);
     2034
     2035                // chi-square for this system of equations:
     2036                // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2
     2037                // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2
     2038                p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd);
     2039                p_pmSubSolve_VdV (&Axx, Ax, Xsvd);
     2040                p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd);
     2041                p_pmSubSolve_y2 (&y2, kernels, stamps);
     2042
     2043                // apparently, this works (compare with the brute force value below
     2044                double chiSquare = Axx - 2.0*Bx + y2;
     2045                psLogMsg ("psModules.imcombine", PS_LOG_INFO, "model kernel with %d terms; chi square = %f\n", maxWeight, chiSquare);
     2046
     2047                // re-calculate A^{-1} to get new variances:
     2048                // Ainv = V * w * U^T
     2049                // XXX since we keep all terms, this is identical to Ainv
     2050                psImage *wUt  = p_pmSubSolve_wUt (wApply, U);
     2051                Xvar = p_pmSubSolve_VwUt (V, wUt);
     2052                psFree (wUt);
     2053
     2054                psFree (Ax);
     2055                psFree (UtB);
     2056                psFree (wUtB);
     2057                psFree (wApply);
     2058                psFree (wMask);
     2059            }
     2060
     2061            // copy the kernel solutions to the full solution vector:
     2062            solution = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     2063            solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     2064
     2065            for (int ix = 0, kx = 0; ix < sumVector->n; ix++) {
     2066                if (ix == bgIndex) {
     2067                    solution->data.F64[ix] = 0;
     2068                    solutionErr->data.F64[ix] = 0.001;
     2069                    continue;
     2070                }
     2071                solutionErr->data.F64[ix] = sqrt(Ainv->data.F64[kx][kx]);
     2072                solution->data.F64[ix] = Xsvd->data.F64[kx];
     2073                kx++;
     2074            }
     2075
     2076            psFree (kernelMatrix);
     2077            psFree (kernelVector);
     2078
     2079            psFree (U);
     2080            psFree (V);
     2081            psFree (w);
     2082
     2083            psFree (Ainv);
     2084            psFree (Xsvd);
     2085        } else {
     2086            psVector *permutation = NULL;       // Permutation vector, required for LU decomposition
     2087            psImage *luMatrix = psMatrixLUDecomposition(NULL, &permutation, sumMatrix);
     2088            if (!luMatrix) {
     2089                psError(PS_ERR_UNKNOWN, true, "LU Decomposition of least-squares matrix failed.\n");
     2090                psFree(solution);
     2091                psFree(sumVector);
     2092                psFree(sumMatrix);
     2093                psFree(luMatrix);
     2094                psFree(permutation);
     2095                return NULL;
     2096            }
     2097
     2098            solution = psMatrixLUSolution(NULL, luMatrix, sumVector, permutation);
     2099            psFree(luMatrix);
     2100            psFree(permutation);
     2101            if (!solution) {
     2102                psError(PS_ERR_UNKNOWN, true, "Failed to solve the least-squares system.\n");
     2103                psFree(solution);
     2104                psFree(sumVector);
     2105                psFree(sumMatrix);
     2106                return NULL;
     2107            }
     2108
     2109            // XXX LUD does not provide A^{-1}?  fake the error for now
     2110            solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     2111            for (int ix = 0; ix < sumVector->n; ix++) {
     2112                solutionErr->data.F64[ix] = 0.1*solution->data.F64[ix];
     2113            }
     2114        }
     2115
     2116        if (!kernels->solution1) {
     2117            kernels->solution1 = psVectorAlloc (sumVector->n, PS_TYPE_F64);
     2118            psVectorInit (kernels->solution1, 0.0);
     2119        }
     2120
     2121        // only update the solutions that we chose to calculate:
     2122        if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     2123            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     2124            kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
     2125        }
     2126        if (mode & PM_SUBTRACTION_EQUATION_BG) {
     2127            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     2128            kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
     2129        }
     2130        if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     2131            int numKernels = kernels->num;
     2132            int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     2133            int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
     2134            for (int i = 0; i < numKernels * numPoly; i++) {
     2135                // XXX fprintf (stderr, "%f +/- %f (%f) -> ", solution->data.F64[i], solutionErr->data.F64[i], fabs(solution->data.F64[i]/solutionErr->data.F64[i]));
     2136                if (fabs(solution->data.F64[i] / solutionErr->data.F64[i]) < COEFF_SIG) {
     2137                    // XXX fprintf (stderr, "drop\n");
     2138                    kernels->solution1->data.F64[i] = 0.0;
     2139                } else {
     2140                    // XXX fprintf (stderr, "keep\n");
     2141                    kernels->solution1->data.F64[i] = solution->data.F64[i];
     2142                }
     2143            }
     2144        }
     2145        // double chiSquare = p_pmSubSolve_ChiSquare (kernels, stamps);
     2146        // fprintf (stderr, "chi square Brute = %f\n", chiSquare);
     2147
     2148        psFree(solution);
     2149        psFree(sumVector);
     2150        psFree(sumMatrix);
     2151# endif
     2152
     2153#ifdef TESTING
     2154              // XXX double-check for NAN in data:
     2155                for (int iy = 0; iy < stamp->matrix->numRows; iy++) {
     2156                    for (int ix = 0; ix < stamp->matrix->numCols; ix++) {
     2157                        if (!isfinite(stamp->matrix->data.F64[iy][ix])) {
     2158                            fprintf (stderr, "WARNING: NAN in matrix\n");
     2159                        }
     2160                    }
     2161                }
     2162                for (int ix = 0; ix < stamp->vector->n; ix++) {
     2163                    if (!isfinite(stamp->vector->data.F64[ix])) {
     2164                        fprintf (stderr, "WARNING: NAN in vector\n");
     2165                    }
     2166                }
     2167#endif
     2168
     2169#ifdef TESTING
     2170        for (int ix = 0; ix < sumVector->n; ix++) {
     2171            if (!isfinite(sumVector->data.F64[ix])) {
     2172                fprintf (stderr, "WARNING: NAN in vector\n");
     2173            }
     2174        }
     2175#endif
     2176
     2177#ifdef TESTING
     2178        for (int ix = 0; ix < sumVector->n; ix++) {
     2179            if (!isfinite(sumVector->data.F64[ix])) {
     2180                fprintf (stderr, "WARNING: NAN in vector\n");
     2181            }
     2182        }
     2183        {
     2184            psImage *inverse = psMatrixInvert(NULL, sumMatrix, NULL);
     2185            psFitsWriteImageSimple("matrixInv.fits", inverse, NULL);
     2186            psFree(inverse);
     2187        }
     2188        {
     2189            psImage *X = psMatrixInvert(NULL, sumMatrix, NULL);
     2190            psImage *Xt = psMatrixTranspose(NULL, X);
     2191            psImage *XtX = psMatrixMultiply(NULL, Xt, X);
     2192            psFitsWriteImageSimple("matrixErr.fits", XtX, NULL);
     2193            psFree(X);
     2194            psFree(Xt);
     2195            psFree(XtX);
     2196        }
     2197#endif
     2198
  • trunk/psModules/src/imcombine/pmSubtractionEquation.h

    r19299 r26893  
    44#include "pmSubtractionStamps.h"
    55#include "pmSubtractionKernels.h"
     6
     7typedef enum {
     8    PM_SUBTRACTION_EQUATION_NONE    = 0x00,
     9    PM_SUBTRACTION_EQUATION_NORM    = 0x01,
     10    PM_SUBTRACTION_EQUATION_BG      = 0x02,
     11    PM_SUBTRACTION_EQUATION_KERNELS = 0x04,
     12    PM_SUBTRACTION_EQUATION_ALL     = 0x07, // value should be NORM | BG | KERNELS
     13} pmSubtractionEquationCalculationMode;
    614
    715/// Execute a thread job to calculate the least-squares equation for a stamp
     
    1220bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, ///< Stamps
    1321                                         const pmSubtractionKernels *kernels, ///< Kernel parameters
    14                                          int index ///< Index of stamp
    15                                     );
     22                                         int index, ///< Index of stamp
     23                                         const pmSubtractionEquationCalculationMode mode
     24    );
    1625
    1726/// Calculate the least-squares equation to match the image quality
    1827bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, ///< Stamps
    19                                     const pmSubtractionKernels *kernels ///< Kernel parameters
    20                                     );
     28                                    const pmSubtractionKernels *kernels, ///< Kernel parameters
     29                                    const pmSubtractionEquationCalculationMode mode
     30    );
    2131
    2232/// Solve the least-squares equation to match the image quality
    2333bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels, ///< Kernel parameters
    24                                 const pmSubtractionStampList *stamps ///< Stamps
     34                                const pmSubtractionStampList *stamps, ///< Stamps
     35                                const pmSubtractionEquationCalculationMode mode
    2536    );
    2637
    2738/// Calculate deviations
    2839psVector *pmSubtractionCalculateDeviations(pmSubtractionStampList *stamps, ///< Stamps
    29                                            const pmSubtractionKernels *kernels ///< Kernel parameters
     40                                           pmSubtractionKernels *kernels ///< Kernel parameters
    3041    );
    3142
  • trunk/psModules/src/imcombine/pmSubtractionIO.c

    r23378 r26893  
    8080        while ((item = psMetadataGetAndIncrement(iter))) {
    8181            assert(item->type == PS_DATA_UNKNOWN);
    82             // Set the normalisation dimensions, since these will be otherwise unavailable when reading the
    83             // images by scans.
    8482            pmSubtractionKernels *kernel = item->data.V; // Kernel used in subtraction
    85             kernel->numCols = ro->image->numCols;
    86             kernel->numRows = ro->image->numRows;
    87 
    8883            kernels = psArrayAdd(kernels, ARRAY_BUFFER, kernel);
    8984        }
     
    126121                         kernel->bgOrder);
    127122        psMetadataAddS32(row, PS_LIST_TAIL, NAME_MODE,  0, "Matching mode (enum)", kernel->mode);
    128         psMetadataAddS32(row, PS_LIST_TAIL, NAME_COLS,  0, "Number of columns", kernel->numCols);
    129         psMetadataAddS32(row, PS_LIST_TAIL, NAME_ROWS,  0, "Number of rows", kernel->numRows);
    130123        if (kernel->mode == PM_SUBTRACTION_MODE_1 || kernel->mode == PM_SUBTRACTION_MODE_2) {
    131124            psMetadataAddVector(row, PS_LIST_TAIL, NAME_SOL1, 0, "Solution vector 1", kernel->solution1);
     
    318311
    319312        TABLE_LOOKUP(int, S32, bg,      NAME_BG);
    320         TABLE_LOOKUP(int, S32, numCols, NAME_COLS);
    321         TABLE_LOOKUP(int, S32, numRows, NAME_ROWS);
    322313
    323314        TABLE_LOOKUP(float, F32, mean,      NAME_MEAN);
     
    325316        TABLE_LOOKUP(int,   S32, numStamps, NAME_NUMSTAMPS);
    326317
    327         pmSubtractionKernels *kernels = pmSubtractionKernelsFromDescription(description, bg, mode);
    328         kernels->numCols = numCols;
    329         kernels->numRows = numRows;
     318        pmSubtractionKernels *kernels = pmSubtractionKernelsFromDescription(description, bg, *region, mode);
    330319        kernels->mean = mean;
    331320        kernels->rms = rms;
  • trunk/psModules/src/imcombine/pmSubtractionKernels.c

    r26156 r26893  
    1010#include "pmSubtraction.h"
    1111#include "pmSubtractionKernels.h"
     12#include "pmSubtractionHermitian.h"
     13#include "pmSubtractionDeconvolve.h"
     14#include "pmSubtractionVisual.h"
    1215
    1316#define RINGS_BUFFER 10                 // Buffer size for RINGS data
    14 
    1517
    1618// Free function for pmSubtractionKernels
     
    2729    psFree(kernels->solution1);
    2830    psFree(kernels->solution2);
     31    psFree(kernels->sampleStamps);
     32}
     33
     34// Free function for pmSubtractionPreCalcKernel
     35static void pmSubtractionKernelPreCalcFree(pmSubtractionKernelPreCalc *kernel)
     36{
     37    psFree(kernel->xKernel);
     38    psFree(kernel->yKernel);
     39    psFree(kernel->kernel);
     40
     41    psFree(kernel->uCoords);
     42    psFree(kernel->vCoords);
     43    psFree(kernel->poly);
    2944}
    3045
     
    4560
    4661// Generate 1D convolution kernel for ISIS
    47 static psVector *subtractionKernelISIS(float sigma, // Gaussian width
     62psVector *pmSubtractionKernelISIS(float sigma, // Gaussian width
    4863                                       int order, // Polynomial order
    4964                                       int size // Kernel half-size
     
    5772    for (int i = 0, x = -size; x <= size; i++, x++) {
    5873        kernel->data.F32[i] = norm * power(x, order) * expf(expNorm * PS_SQR(x));
     74    }
     75
     76    return kernel;
     77}
     78
     79// Generate 1D convolution kernel for HERM (normalized for 2D)
     80psVector *pmSubtractionKernelHERM(float sigma, // Gaussian width
     81                                       int order, // Polynomial order
     82                                       int size // Kernel half-size
     83    )
     84{
     85    int fullSize = 2 * size + 1;        // Full size of kernel
     86    psVector *kernel = psVectorAlloc(fullSize, PS_TYPE_F32); // Kernel to return
     87
     88    // for now, we are only allowing equal orders and sigmas in X and Y
     89    float nf = exp(lgamma(order + 1));
     90    float norm = 1.0 / sqrt(nf*sigma*sqrt(M_2_PI));
     91
     92    for (int i = 0, x = -size; x <= size; i++, x++) {
     93        float xf = x / sigma;
     94        float z = -0.25*xf*xf;
     95        kernel->data.F32[i] = norm * p_pmSubtractionHermitianPolynomial(xf, order) * exp(z);
     96    }
     97
     98    return kernel;
     99}
     100
     101// Generate 1D convolution kernel for HERM (normalized for 2D)
     102psKernel *pmSubtractionKernelHERM_RADIAL(float sigma, // Gaussian width
     103                                         int order, // Polynomial order
     104                                         int size // Kernel half-size
     105    )
     106{
     107    psKernel *kernel = psKernelAlloc(-size, size, -size, size); // 2D Kernel
     108
     109    // for now, we are only allowing equal orders and sigmas in X and Y
     110    float nf = exp(lgamma(order + 1));
     111    float norm = 1.0 / sqrt(nf*sigma*sqrt(M_2_PI));
     112
     113    // generate 2D radial hermitian
     114    for (int v = -size; v <= size; v++) {
     115        for (int u = -size; u <= size; u++) {
     116            float r = hypot(u, v) / sigma;
     117            float z = -0.25*r*r;
     118            kernel->kernel[v][u] = norm * p_pmSubtractionHermitianPolynomial(r, order) * exp(z);
     119        }
    59120    }
    60121
     
    96157            kernels->preCalc->data[index] = NULL;
    97158            kernels->penalties->data.F32[index] = kernels->penalty * PS_SQR(PS_SQR(u) + PS_SQR(v));
    98 
     159            psAssert (isfinite(kernels->penalties->data.F32[index]), "invalid penalty");
    99160            psTrace("psModules.imcombine", 7, "Kernel %d: %d %d\n", index, u, v);
    100161        }
     
    110171}
    111172
     173bool pmSubtractionKernelPreCalcNormalize(pmSubtractionKernels *kernels, pmSubtractionKernelPreCalc *preCalc,
     174                                         int index, int size, int uOrder, int vOrder, float fwhm,
     175                                         bool AlardLuptonStyle, bool forceZeroNull)
     176{
     177    // we have 4 cases here:
     178    // 1) for odd functions, normalize the kernel by the maximum swing / Npix
     179    // 2) for even functions, normalize the kernel to unity
     180    // 3) for alard-lupton style normalization, subtract 1 from the 0,0 pixel for all even functions
     181    // 4) for deconvolved hermitians, subtract 1 from the 0,0 pixel for the 0,0 function(s)
     182
     183    // Calculate moments
     184    double penalty = 0.0;                   // Moment, for penalty
     185    double sum = 0.0, sum2 = 0.0;           // Sum of kernel component
     186    float min = INFINITY, max = -INFINITY;  // Minimum and maximum kernel value
     187    for (int v = -size; v <= size; v++) {
     188        for (int u = -size; u <= size; u++) {
     189            double value = preCalc->kernel->kernel[v][u];
     190            double value2 = PS_SQR(value);
     191            sum += value;
     192            sum2 += value2;
     193            penalty += value2 * PS_SQR((PS_SQR(u) + PS_SQR(v)));
     194            min = PS_MIN(value, min);
     195            max = PS_MAX(value, max);
     196        }
     197    }
     198
     199#if 0
     200    fprintf(stderr, "%d raw: %lf, null: %f, min: %lf, max: %lf, moment: %lf\n", index, sum, preCalc->kernel->kernel[0][0], min, max, penalty);
     201#endif
     202
     203    bool zeroNull = false;              // Zero out using the null position?
     204    float scale2D = NAN;                // Scaling for 2-D kernels
     205
     206    if (AlardLuptonStyle) {
     207        if (uOrder % 2 == 0 && vOrder % 2 == 0) {
     208            // Even functions: normalise to unit sum and subtract null pixel so that sum is zero
     209            scale2D = 1.0 / fabs(sum);
     210            zeroNull = true;
     211        } else {
     212            // Odd functions: choose normalisation so that parameters have about the same strength as for even
     213            // functions, no subtraction of null pixel because the sum is already (near) zero
     214            scale2D = 1.0 / sqrt(sum2);
     215            zeroNull = false;
     216        }
     217    }
     218
     219    if (!AlardLuptonStyle && (uOrder == 0 && vOrder == 0)) {
     220        zeroNull = true;
     221    }
     222    if (forceZeroNull) {
     223        // Force rescaling and subtraction of null pixel even though the order doesn't indicate it's even
     224        scale2D = 1.0 / fabs(sum);
     225        zeroNull = true;
     226    }
     227    if (!forceZeroNull && ((uOrder % 2) || (vOrder % 2))) {
     228        // Odd function
     229        scale2D = 1.0 / sqrt(sum2);
     230    }
     231
     232    float scale1D = sqrtf(scale2D);     // Scaling for 1-D kernels
     233    if (preCalc->xKernel) {
     234        psBinaryOp(preCalc->xKernel, preCalc->xKernel, "*", psScalarAlloc(scale1D, PS_TYPE_F32));
     235    }
     236    if (preCalc->yKernel) {
     237        psBinaryOp(preCalc->yKernel, preCalc->yKernel, "*", psScalarAlloc(scale1D, PS_TYPE_F32));
     238    }
     239
     240    psBinaryOp(preCalc->kernel->image, preCalc->kernel->image, "*", psScalarAlloc(scale2D, PS_TYPE_F32));
     241    penalty *= 1.0 / sum2;
     242
     243    if (zeroNull) {
     244        preCalc->kernel->kernel[0][0] -= 1.0;
     245    }
     246
     247#if 0
     248    {
     249        double sum = 0.0;   // Sum of kernel component
     250        float min = INFINITY, max = -INFINITY;  // Minimum and maximum kernel value
     251        for (int v = -size; v <= size; v++) {
     252            for (int u = -size; u <= size; u++) {
     253                sum += preCalc->kernel->kernel[v][u];
     254                min = PS_MIN(preCalc->kernel->kernel[v][u], min);
     255                max = PS_MAX(preCalc->kernel->kernel[v][u], max);
     256            }
     257        }
     258        fprintf(stderr, "%d mod: %lf, null: %f, min: %lf, max: %lf, scale: %f\n", index, sum, preCalc->kernel->kernel[0][0], min, max, scale2D);
     259    }
     260#endif
     261
     262    kernels->widths->data.F32[index] = fwhm;
     263    kernels->u->data.S32[index] = uOrder;
     264    kernels->v->data.S32[index] = vOrder;
     265    if (kernels->preCalc->data[index]) {
     266        psFree(kernels->preCalc->data[index]);
     267    }
     268    kernels->preCalc->data[index] = preCalc;
     269    kernels->penalties->data.F32[index] = kernels->penalty * penalty;
     270    psAssert (isfinite(kernels->penalties->data.F32[index]), "invalid penalty");
     271    psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d %f\n", index, fwhm, uOrder, vOrder, penalty);
     272
     273    return true;
     274}
     275
    112276pmSubtractionKernels *p_pmSubtractionKernelsRawISIS(int size, int spatialOrder,
    113                                                     const psVector *fwhms, const psVector *orders,
    114                                                     float penalty, pmSubtractionMode mode)
    115 {
    116     PS_ASSERT_VECTOR_NON_NULL(fwhms, NULL);
    117     PS_ASSERT_VECTOR_TYPE(fwhms, PS_TYPE_F32, NULL);
    118     PS_ASSERT_VECTOR_NON_NULL(orders, NULL);
    119     PS_ASSERT_VECTOR_TYPE(orders, PS_TYPE_S32, NULL);
    120     PS_ASSERT_VECTORS_SIZE_EQUAL(fwhms, orders, NULL);
     277                                                    const psVector *fwhmsIN, const psVector *ordersIN,
     278                                                    float penalty, psRegion bounds, pmSubtractionMode mode)
     279{
     280    PS_ASSERT_VECTOR_NON_NULL(fwhmsIN, NULL);
     281    PS_ASSERT_VECTOR_TYPE(fwhmsIN, PS_TYPE_F32, NULL);
     282    PS_ASSERT_VECTOR_NON_NULL(ordersIN, NULL);
     283    PS_ASSERT_VECTOR_TYPE(ordersIN, PS_TYPE_S32, NULL);
     284    PS_ASSERT_VECTORS_SIZE_EQUAL(fwhmsIN, ordersIN, NULL);
    121285    PS_ASSERT_INT_POSITIVE(size, NULL);
    122286    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
     287
     288    // check the requested fwhm values: any values <= 0.0 should be dropped
     289    psVector *fwhms  = psVectorAllocEmpty (fwhmsIN->n, PS_TYPE_F32);
     290    psVector *orders = psVectorAllocEmpty (ordersIN->n, PS_TYPE_S32);
     291    for (int i = 0; i < fwhmsIN->n; i++) {
     292        if (fwhmsIN->data.F32[i] <= FLT_EPSILON) continue;
     293        psVectorAppend(fwhms, fwhmsIN->data.F32[i]);
     294        psVectorAppend(orders, ordersIN->data.S32[i]);
     295    }
    123296
    124297    int numGaussians = fwhms->n;       // Number of Gaussians
     
    133306
    134307    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_ISIS, size,
    135                                                               spatialOrder, penalty, mode); // The kernels
     308                                                              spatialOrder, penalty, bounds, mode); // Kernels
    136309    psStringAppend(&kernels->description, "ISIS(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
    137310
     
    141314
    142315    // Set the kernel parameters
    143     int fullSize = 2 * size + 1;        // Full size of kernels
    144316    for (int i = 0, index = 0; i < numGaussians; i++) {
    145317        float sigma = fwhms->data.F32[i] / (2.0 * sqrtf(2.0 * logf(2.0))); // Gaussian sigma
     
    147319        for (int uOrder = 0; uOrder <= orders->data.S32[i]; uOrder++) {
    148320            for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++, index++) {
    149                 psArray *preCalc = psArrayAlloc(3); // Array to hold precalculated values
    150                 psVector *xKernel = preCalc->data[0] = subtractionKernelISIS(sigma, uOrder, size); // x Kernel
    151                 psVector *yKernel = preCalc->data[1] = subtractionKernelISIS(sigma, vOrder, size); // y Kernel
    152                 psKernel *kernel = preCalc->data[2] = psKernelAlloc(-size, size, -size, size);      // Kernel
    153 
    154                 // Calculate moments
    155                 double moment = 0.0;    // Moment, for penalty
    156                 for (int v = -size, y = 0; v <= size; v++, y++) {
    157                     for (int u = -size, x = 0; u <= size; u++, x++) {
    158                         double value = xKernel->data.F32[x] * yKernel->data.F32[y]; // Value of kernel
    159                         kernel->kernel[v][u] = value;
    160                         moment += value * PS_SQR((PS_SQR(u) + PS_SQR(v)));
    161                     }
     321
     322                pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_ISIS, uOrder, vOrder, size, sigma); // structure to hold precalculated values
     323                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
     324                // pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], false, false);
     325            }
     326        }
     327    }
     328
     329    return kernels;
     330}
     331
     332pmSubtractionKernels *pmSubtractionKernelsISIS_RADIAL(int size, int spatialOrder,
     333                                                      const psVector *fwhmsIN, const psVector *ordersIN,
     334                                                      float penalty, psRegion bounds, pmSubtractionMode mode)
     335{
     336    PS_ASSERT_VECTOR_NON_NULL(fwhmsIN, NULL);
     337    PS_ASSERT_VECTOR_TYPE(fwhmsIN, PS_TYPE_F32, NULL);
     338    PS_ASSERT_VECTOR_NON_NULL(ordersIN, NULL);
     339    PS_ASSERT_VECTOR_TYPE(ordersIN, PS_TYPE_S32, NULL);
     340    PS_ASSERT_VECTORS_SIZE_EQUAL(fwhmsIN, ordersIN, NULL);
     341    PS_ASSERT_INT_POSITIVE(size, NULL);
     342    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
     343
     344    // check the requested fwhm values: any values <= 0.0 should be dropped
     345    psVector *fwhms  = psVectorAllocEmpty (fwhmsIN->n, PS_TYPE_F32);
     346    psVector *orders = psVectorAllocEmpty (ordersIN->n, PS_TYPE_S32);
     347    for (int i = 0; i < fwhmsIN->n; i++) {
     348        if (fwhmsIN->data.F32[i] <= FLT_EPSILON) continue;
     349        psVectorAppend(fwhms, fwhmsIN->data.F32[i]);
     350        psVectorAppend(orders, ordersIN->data.S32[i]);
     351    }
     352
     353    int numGaussians = fwhms->n;       // Number of Gaussians
     354
     355    int num = 0;                        // Number of basis functions
     356    psString params = NULL;             // List of parameters
     357    for (int i = 0; i < numGaussians; i++) {
     358        int gaussOrder = orders->data.S32[i]; // Polynomial order to apply to Gaussian
     359        psStringAppend(&params, "(%.1f,%d)", fwhms->data.F32[i], orders->data.S32[i]);
     360        num += (gaussOrder + 1) * (gaussOrder + 2) / 2;
     361        num += (11 - gaussOrder - 1);   // include all higher order radial terms
     362    }
     363
     364    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_ISIS_RADIAL, size,
     365                                                              spatialOrder, penalty, bounds, mode); // Kernels
     366    psStringAppend(&kernels->description, "ISIS_RADIAL(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
     367
     368    psLogMsg("psModules.imcombine", PS_LOG_INFO, "ISIS_RADIAL kernel: %s,%d --> %d elements", params, spatialOrder, num);
     369    psFree(params);
     370
     371    // Set the kernel parameters
     372    for (int i = 0, index = 0; i < numGaussians; i++) {
     373        float sigma = fwhms->data.F32[i] / (2.0 * sqrtf(2.0 * logf(2.0))); // Gaussian sigma
     374        // Iterate over (u,v) order
     375        for (int uOrder = 0; uOrder <= orders->data.S32[i]; uOrder++) {
     376            for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++, index++) {
     377                pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_ISIS, uOrder, vOrder, size, sigma); // structure to hold precalculated values
     378                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
     379            }
     380        }
     381        for (int order = orders->data.S32[i] + 1; order < 11; order ++, index ++) {
     382            // XXX modify size for hermitians to account for sqrt(2) in Hermitian definition (relative to ISIS Gaussian)
     383            pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_ISIS_RADIAL, order, order, size, sigma / sqrt(2.0)); // structure to hold precalculated values
     384            pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, order, order, fwhms->data.F32[i], true, true);
     385        }
     386    }
     387    return kernels;
     388}
     389
     390pmSubtractionKernels *pmSubtractionKernelsHERM(int size, int spatialOrder,
     391                                               const psVector *fwhmsIN, const psVector *ordersIN,
     392                                               float penalty, psRegion bounds, pmSubtractionMode mode)
     393{
     394    PS_ASSERT_VECTOR_NON_NULL(fwhmsIN, NULL);
     395    PS_ASSERT_VECTOR_TYPE(fwhmsIN, PS_TYPE_F32, NULL);
     396    PS_ASSERT_VECTOR_NON_NULL(ordersIN, NULL);
     397    PS_ASSERT_VECTOR_TYPE(ordersIN, PS_TYPE_S32, NULL);
     398    PS_ASSERT_VECTORS_SIZE_EQUAL(fwhmsIN, ordersIN, NULL);
     399    PS_ASSERT_INT_POSITIVE(size, NULL);
     400    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
     401
     402    // check the requested fwhm values: any values <= 0.0 should be dropped
     403    psVector *fwhms  = psVectorAllocEmpty (fwhmsIN->n, PS_TYPE_F32);
     404    psVector *orders = psVectorAllocEmpty (ordersIN->n, PS_TYPE_S32);
     405    for (int i = 0; i < fwhmsIN->n; i++) {
     406        if (fwhmsIN->data.F32[i] <= FLT_EPSILON) continue;
     407        psVectorAppend(fwhms, fwhmsIN->data.F32[i]);
     408        psVectorAppend(orders, ordersIN->data.S32[i]);
     409    }
     410
     411    int numGaussians = fwhms->n;       // Number of Gaussians
     412
     413    int num = 0;                        // Number of basis functions
     414    psString params = NULL;             // List of parameters
     415    for (int i = 0; i < numGaussians; i++) {
     416        int gaussOrder = orders->data.S32[i]; // Polynomial order to apply to Gaussian
     417        psStringAppend(&params, "(%.1f,%d)", fwhms->data.F32[i], orders->data.S32[i]);
     418        num += (gaussOrder + 1) * (gaussOrder + 2) / 2;
     419    }
     420
     421    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_HERM, size,
     422                                                              spatialOrder, penalty, bounds, mode); // Kernels
     423    psStringAppend(&kernels->description, "HERM(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
     424
     425    psLogMsg("psModules.imcombine", PS_LOG_INFO, "HERM kernel: %s,%d --> %d elements",
     426             params, spatialOrder, num);
     427    psFree(params);
     428
     429    // Set the kernel parameters
     430    for (int i = 0, index = 0; i < numGaussians; i++) {
     431        float sigma = fwhms->data.F32[i] / (2.0 * sqrtf(2.0 * logf(2.0))); // Gaussian sigma
     432        // Iterate over (u,v) order
     433        for (int uOrder = 0; uOrder <= orders->data.S32[i]; uOrder++) {
     434            for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++, index++) {
     435                pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_HERM, uOrder, vOrder, size, sigma); // structure to hold precalculated values
     436                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
     437            }
     438        }
     439    }
     440
     441    return kernels;
     442}
     443
     444pmSubtractionKernels *pmSubtractionKernelsDECONV_HERM(int size, int spatialOrder,
     445                                                      const psVector *fwhmsIN, const psVector *ordersIN,
     446                                                      float penalty, psRegion bounds, pmSubtractionMode mode)
     447{
     448    PS_ASSERT_VECTOR_NON_NULL(fwhmsIN, NULL);
     449    PS_ASSERT_VECTOR_TYPE(fwhmsIN, PS_TYPE_F32, NULL);
     450    PS_ASSERT_VECTOR_NON_NULL(ordersIN, NULL);
     451    PS_ASSERT_VECTOR_TYPE(ordersIN, PS_TYPE_S32, NULL);
     452    PS_ASSERT_VECTORS_SIZE_EQUAL(fwhmsIN, ordersIN, NULL);
     453    PS_ASSERT_INT_POSITIVE(size, NULL);
     454    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
     455
     456    // check the requested fwhm values: any values <= 0.0 should be dropped
     457    psVector *fwhms  = psVectorAllocEmpty (fwhmsIN->n, PS_TYPE_F32);
     458    psVector *orders = psVectorAllocEmpty (ordersIN->n, PS_TYPE_S32);
     459    for (int i = 0; i < fwhmsIN->n; i++) {
     460        if (fwhmsIN->data.F32[i] <= FLT_EPSILON) continue;
     461        psVectorAppend(fwhms, fwhmsIN->data.F32[i]);
     462        psVectorAppend(orders, ordersIN->data.S32[i]);
     463    }
     464
     465    int numGaussians = fwhms->n;       // Number of Gaussians
     466
     467    int num = 0;                        // Number of basis functions
     468    psString params = NULL;             // List of parameters
     469    for (int i = 0; i < numGaussians; i++) {
     470        int gaussOrder = orders->data.S32[i]; // Polynomial order to apply to Gaussian
     471        psStringAppend(&params, "(%.1f,%d)", fwhms->data.F32[i], orders->data.S32[i]);
     472        num += PS_SQR(gaussOrder + 1);
     473    }
     474
     475    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_DECONV_HERM, size,
     476                                                              spatialOrder, penalty, bounds, mode); // Kernels
     477    psStringAppend(&kernels->description, "DECONV_HERM(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
     478
     479    psLogMsg("psModules.imcombine", PS_LOG_INFO, "DECONVOLVED HERM kernel: %s,%d --> %d elements", params, spatialOrder, num);
     480    psFree(params);
     481
     482    // XXXXX hard-wired reference sigma for now of 1.7 pix (== 4.0 pix fwhm == 1.0 arcsec in simtest)
     483    // generate the Gaussian deconvolution kernel
     484    # define DECONV_SIGMA 1.6
     485    psKernel *kernelGauss = pmSubtractionDeconvolveGauss (size, DECONV_SIGMA);
     486
     487# if 1
     488    psArray *deconKernels = psArrayAllocEmpty(100);
     489# endif
     490
     491    // Set the kernel parameters
     492    for (int i = 0, index = 0; i < numGaussians; i++) {
     493        float sigma = fwhms->data.F32[i] / (2.0 * sqrtf(2.0 * logf(2.0))); // Gaussian sigma
     494        // Iterate over (u,v) order
     495        for (int uOrder = 0; uOrder <= orders->data.S32[i]; uOrder++) {
     496            for (int vOrder = 0; vOrder <= orders->data.S32[i]; vOrder++, index++) {
     497
     498                pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc(PM_SUBTRACTION_KERNEL_HERM, uOrder, vOrder, size, sigma); // structure to hold precalculated values
     499
     500                // save the generated 2D kernel as the target, deconvolve it by Gaussian, replacing the generated 2D kernel
     501                psKernel *kernelTarget = preCalc->kernel;
     502                preCalc->kernel = pmSubtractionDeconvolveKernel(kernelTarget, kernelGauss); // Kernel
     503
     504                // XXX do we use Alard-Lupton normalization (last param true) or not?
     505                pmSubtractionKernelPreCalcNormalize (kernels, preCalc, index, size, uOrder, vOrder, fwhms->data.F32[i], true, false);
     506
     507                // XXXX test demo that deconvolved kernel is valid
     508# if 1
     509                psImage *kernelConv = psImageConvolveFFT(NULL, preCalc->kernel->image, NULL, 0, kernelGauss);
     510                psArrayAdd (deconKernels, 100, kernelConv);
     511                psFree (kernelConv);
     512
     513                if (!uOrder && !vOrder){
     514                    pmSubtractionVisualShowSubtraction (kernelTarget->image, preCalc->kernel->image, kernelConv);
    162515                }
    163 
    164                 // Normalise sum of kernel component to unity for even functions
    165                 if (uOrder % 2 == 0 && vOrder % 2 == 0) {
    166                     double sum = 0.0;   // Sum of kernel component
    167                     for (int v = 0; v < fullSize; v++) {
    168                         for (int u = 0; u < fullSize; u++) {
    169                             sum += xKernel->data.F32[u] * yKernel->data.F32[v];
    170                         }
    171                     }
    172                     sum = 1.0 / sqrt(sum);
    173                     psBinaryOp(xKernel, xKernel, "*", psScalarAlloc(sum, PS_TYPE_F32));
    174                     psBinaryOp(yKernel, yKernel, "*", psScalarAlloc(sum, PS_TYPE_F32));
    175                     psBinaryOp(kernel->image, kernel->image, "*", psScalarAlloc(PS_SQR(sum), PS_TYPE_F32));
    176                     kernel->kernel[0][0] -= 1.0;
    177                     moment *= PS_SQR(sum);
     516# endif
     517            }
     518        }
     519    }
     520
     521# if 1
     522    psImage *dot = psImageAlloc(deconKernels->n, deconKernels->n, PS_TYPE_F32);
     523    for (int i = 0; i < deconKernels->n; i++) {
     524        for (int j = 0; j <= i; j++) {
     525            psImage *t1 = deconKernels->data[i];
     526            psImage *t2 = deconKernels->data[j];
     527
     528            double sum = 0.0;
     529            for (int iy = 0; iy < t1->numRows; iy++) {
     530                for (int ix = 0; ix < t1->numCols; ix++) {
     531                    sum += t1->data.F32[iy][ix] * t2->data.F32[iy][ix];
    178532                }
    179 
    180 
    181 #if 0
    182                 double sum = 0.0;   // Sum of kernel component
    183                 for (int v = -size; v <= size; v++) {
    184                     for (int u = -size; u <= size; u++) {
    185                         sum += kernel->kernel[v][u];
    186                     }
    187                 }
    188                 fprintf(stderr, "%d sum: %lf\n", index, sum);
    189 #endif
    190 
    191                 kernels->widths->data.F32[index] = fwhms->data.F32[i];
    192                 kernels->u->data.S32[index] = uOrder;
    193                 kernels->v->data.S32[index] = vOrder;
    194                 if (kernels->preCalc->data[index]) {
    195                     psFree(kernels->preCalc->data[index]);
    196                 }
    197                 kernels->preCalc->data[index] = preCalc;
    198                 kernels->penalties->data.F32[index] = kernels->penalty * fabsf(moment);
    199 
    200                 psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d %f\n", index,
    201                         fwhms->data.F32[i], uOrder, vOrder, fabsf(moment));
    202533            }
    203         }
    204     }
     534            dot->data.F32[j][i] = sum;
     535            dot->data.F32[i][j] = sum;
     536        }
     537    }
     538    pmSubtractionVisualShowSubtraction (dot, NULL, NULL);
     539    psFree (dot);
     540    psFree (deconKernels);
     541# endif
    205542
    206543    return kernels;
     
    212549
    213550pmSubtractionKernels *pmSubtractionKernelsAlloc(int numBasisFunctions, pmSubtractionKernelsType type,
    214                                                 int size, int spatialOrder, float penalty,
     551                                                int size, int spatialOrder, float penalty, psRegion bounds,
    215552                                                pmSubtractionMode mode)
    216553{
     
    224561    kernels->v = psVectorAlloc(numBasisFunctions, PS_TYPE_S32);
    225562    kernels->widths = psVectorAlloc(numBasisFunctions, PS_TYPE_F32);
     563    kernels->uStop = NULL;
     564    kernels->vStop = NULL;
     565    kernels->xMin = bounds.x0;
     566    kernels->xMax = bounds.x1;
     567    kernels->yMin = bounds.y0;
     568    kernels->yMax = bounds.y1;
    226569    kernels->preCalc = psArrayAlloc(numBasisFunctions);
    227570    kernels->penalty = penalty;
    228571    kernels->penalties = psVectorAlloc(numBasisFunctions, PS_TYPE_F32);
    229     kernels->uStop = NULL;
    230     kernels->vStop = NULL;
    231572    kernels->size = size;
    232573    kernels->inner = 0;
     
    234575    kernels->bgOrder = 0;
    235576    kernels->mode = mode;
    236     kernels->numCols = 0;
    237     kernels->numRows = 0;
    238577    kernels->solution1 = NULL;
    239578    kernels->solution2 = NULL;
     579    kernels->mean = NAN;
     580    kernels->rms = NAN;
     581    kernels->numStamps = 0;
     582    kernels->sampleStamps = NULL;
     583
     584    kernels->fSigResMean  = NAN;
     585    kernels->fSigResStdev = NAN;
     586    kernels->fMaxResMean  = NAN;
     587    kernels->fMaxResStdev = NAN;
     588    kernels->fMinResMean  = NAN;
     589    kernels->fMinResStdev = NAN;
    240590
    241591    return kernels;
    242592}
    243593
    244 pmSubtractionKernels *pmSubtractionKernelsPOIS(int size, int spatialOrder, float penalty,
     594pmSubtractionKernelPreCalc *pmSubtractionKernelPreCalcAlloc(pmSubtractionKernelsType type, int uOrder, int vOrder, int size, float sigma) {
     595
     596    pmSubtractionKernelPreCalc *preCalc = psAlloc(sizeof(pmSubtractionKernelPreCalc)); // Kernels, to return
     597    psMemSetDeallocator(preCalc, (psFreeFunc)pmSubtractionKernelPreCalcFree);
     598
     599    // 1D kernel realizations:
     600    switch (type) {
     601      case PM_SUBTRACTION_KERNEL_ISIS:
     602        preCalc->xKernel = pmSubtractionKernelISIS(sigma, uOrder, size);
     603        preCalc->yKernel = pmSubtractionKernelISIS(sigma, vOrder, size);
     604        preCalc->uCoords = NULL;
     605        preCalc->vCoords = NULL;
     606        preCalc->poly    = NULL;
     607        break;
     608      case PM_SUBTRACTION_KERNEL_HERM:
     609        preCalc->xKernel = pmSubtractionKernelHERM(sigma, uOrder, size);
     610        preCalc->yKernel = pmSubtractionKernelHERM(sigma, vOrder, size);
     611        preCalc->uCoords = NULL;
     612        preCalc->vCoords = NULL;
     613        preCalc->poly    = NULL;
     614        break;
     615      case PM_SUBTRACTION_KERNEL_RINGS:
     616        // the RINGS kernel uses the uCoords, vCoords, and poly elements of the structure
     617        // we allocate these vectors here, but leave the kernel generation to the main function
     618        preCalc->xKernel = NULL;
     619        preCalc->yKernel = NULL;
     620        preCalc->kernel  = NULL;
     621        preCalc->uCoords = psVectorAllocEmpty(size, PS_TYPE_S32); // u coords
     622        preCalc->vCoords = psVectorAllocEmpty(size, PS_TYPE_S32); // v coords
     623        preCalc->poly    = psVectorAllocEmpty(size, PS_TYPE_F32); // Polynomial
     624        return preCalc;
     625      case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     626        preCalc->kernel  = pmSubtractionKernelHERM_RADIAL(sigma, uOrder, size);
     627        preCalc->xKernel = NULL;
     628        preCalc->yKernel = NULL;
     629        preCalc->uCoords = NULL;
     630        preCalc->vCoords = NULL;
     631        preCalc->poly    = NULL;
     632        return preCalc;
     633      default:
     634        psAbort("programming error: invalid type for PreCalc kernel");
     635    }
     636
     637    preCalc->kernel = psKernelAlloc(-size, size, -size, size); // 2D Kernel
     638
     639    // generate 2D kernel from 1D realizations
     640    for (int v = -size, y = 0; v <= size; v++, y++) {
     641        for (int u = -size, x = 0; u <= size; u++, x++) {
     642            preCalc->kernel->kernel[v][u] = preCalc->xKernel->data.F32[x] * preCalc->yKernel->data.F32[y]; // Value of kernel
     643        }
     644    }
     645
     646    return preCalc;
     647}
     648
     649pmSubtractionKernels *pmSubtractionKernelsPOIS(int size, int spatialOrder, float penalty, psRegion bounds,
    245650                                               pmSubtractionMode mode)
    246651{
     
    251656
    252657    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(0, PM_SUBTRACTION_KERNEL_POIS, size,
    253                                                               spatialOrder, penalty, mode); // The kernels
     658                                                              spatialOrder, penalty, bounds, mode); // Kernels
    254659    psStringAppend(&kernels->description, "POIS(%d,%d,%.2e)", size, spatialOrder, penalty);
    255660    psLogMsg("psModules.imcombine", PS_LOG_INFO, "POIS kernel: %d,%d --> %d elements",
     
    266671pmSubtractionKernels *pmSubtractionKernelsISIS(int size, int spatialOrder,
    267672                                               const psVector *fwhms, const psVector *orders,
    268                                                float penalty, pmSubtractionMode mode)
     673                                               float penalty, psRegion bounds, pmSubtractionMode mode)
    269674{
    270675    pmSubtractionKernels *kernels = p_pmSubtractionKernelsRawISIS(size, spatialOrder, fwhms, orders,
    271                                                                   penalty, mode); // Kernels
     676                                                                  penalty, bounds, mode); // Kernels
    272677    if (!kernels) {
    273678        return NULL;
     
    278683
    279684pmSubtractionKernels *pmSubtractionKernelsSPAM(int size, int spatialOrder, int inner, int binning,
    280                                                float penalty, pmSubtractionMode mode)
     685                                               float penalty, psRegion bounds, pmSubtractionMode mode)
    281686{
    282687    PS_ASSERT_INT_POSITIVE(size, NULL);
     
    299704
    300705    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_SPAM, size,
    301                                                               spatialOrder, penalty, mode); // The kernels
     706                                                              spatialOrder, penalty, bounds, mode); // Kernels
    302707    kernels->inner = inner;
    303708    psStringAppend(&kernels->description, "SPAM(%d,%d,%d,%d,%.2e)", size, inner, binning, spatialOrder,
     
    370775
    371776pmSubtractionKernels *pmSubtractionKernelsFRIES(int size, int spatialOrder, int inner, float penalty,
    372                                                 pmSubtractionMode mode)
     777                                                psRegion bounds, pmSubtractionMode mode)
    373778{
    374779    PS_ASSERT_INT_POSITIVE(size, NULL);
     
    397802
    398803    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_FRIES, size,
    399                                                               spatialOrder, penalty, mode); // The kernels
     804                                                              spatialOrder, penalty, bounds, mode); // Kernels
    400805    kernels->inner = inner;
    401806    psStringAppend(&kernels->description, "FRIES(%d,%d,%d,%.2e)", size, inner, spatialOrder, penalty);
     
    463868}
    464869
    465 // Grid United with Normal Kernel
     870// Grid United with Normal Kernel [description: GUNK=ISIS(...)+POIS(...)]
    466871pmSubtractionKernels *pmSubtractionKernelsGUNK(int size, int spatialOrder, const psVector *fwhms,
    467872                                               const psVector *orders, int inner, float penalty,
    468                                                pmSubtractionMode mode)
     873                                               psRegion bounds, pmSubtractionMode mode)
    469874{
    470875    PS_ASSERT_INT_POSITIVE(size, NULL);
     
    479884
    480885    pmSubtractionKernels *kernels = p_pmSubtractionKernelsRawISIS(size, spatialOrder, fwhms, orders,
    481                                                                   penalty, mode); // Kernels
     886                                                                  penalty, bounds, mode); // Kernels
    482887    kernels->type = PM_SUBTRACTION_KERNEL_GUNK;
    483888    psStringPrepend(&kernels->description, "GUNK=");
     
    495900// RINGS --- just what it says
    496901pmSubtractionKernels *pmSubtractionKernelsRINGS(int size, int spatialOrder, int inner, int ringsOrder,
    497                                                 float penalty, pmSubtractionMode mode)
     902                                                float penalty, psRegion bounds, pmSubtractionMode mode)
    498903{
    499904    PS_ASSERT_INT_POSITIVE(size, NULL);
     
    526931
    527932    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_RINGS, size,
    528                                                               spatialOrder, penalty, mode); // The kernels
     933                                                              spatialOrder, penalty, bounds, mode); // Kernels
    529934    kernels->inner = inner;
    530935    psStringAppend(&kernels->description, "RINGS(%d,%d,%d,%d,%.2e)", size, inner, ringsOrder, spatialOrder,
     
    566971            for (int vOrder = 0; vOrder <= (i == 0 ? 0 : ringsOrder - uOrder); vOrder++, index++) {
    567972
    568                 psArray *data = psArrayAlloc(3); // Container for data
    569                 psVector *uCoords = data->data[0] = psVectorAllocEmpty(RINGS_BUFFER, PS_TYPE_S32); // u coords
    570                 psVector *vCoords = data->data[1] = psVectorAllocEmpty(RINGS_BUFFER, PS_TYPE_S32); // v coords
    571                 psVector *poly = data->data[2] = psVectorAllocEmpty(RINGS_BUFFER, PS_TYPE_F32); // Polynomial
     973                pmSubtractionKernelPreCalc *preCalc = pmSubtractionKernelPreCalcAlloc (PM_SUBTRACTION_KERNEL_RINGS, 0, 0, RINGS_BUFFER, 0.0);
    572974                double moment = 0.0;    // Moment, for penalty
    573975
    574976                if (i == 0) {
    575977                    // Central pixel is easy
    576                     uCoords->data.S32[0] = vCoords->data.S32[0] = 0;
    577                     poly->data.F32[0] = 1.0;
    578                     uCoords->n = vCoords->n = poly->n = 1;
     978                    preCalc->uCoords->data.S32[0] = 0;
     979                    preCalc->vCoords->data.S32[0] = 0;
     980                    preCalc->poly->data.F32[0] = 1.0;
     981                    preCalc->uCoords->n = 1;
     982                    preCalc->vCoords->n = 1;
     983                    preCalc->poly->n = 1;
    579984                    radiusLast = 0;
    580985                    moment = 0.0;
     
    594999                                float polyVal = uPoly * vPoly; // Value of polynomial
    5951000                                if (polyVal != 0) { // No point adding it otherwise
    596                                     uCoords->data.S32[j] = u;
    597                                     vCoords->data.S32[j] = v;
    598                                     poly->data.F32[j] = polyVal;
     1001                                    preCalc->uCoords->data.S32[j] = u;
     1002                                    preCalc->vCoords->data.S32[j] = v;
     1003                                    preCalc->poly->data.F32[j] = polyVal;
    5991004                                    norm += polyVal;
    600                                     moment += polyVal * PS_SQR(PS_SQR(u) + PS_SQR(v));
    601 
    602                                     psVectorExtend(uCoords, RINGS_BUFFER, 1);
    603                                     psVectorExtend(vCoords, RINGS_BUFFER, 1);
    604                                     psVectorExtend(poly, RINGS_BUFFER, 1);
     1005                                    moment += PS_SQR(polyVal) * PS_SQR(PS_SQR(u) + PS_SQR(v));
     1006
     1007                                    psVectorExtend(preCalc->uCoords, RINGS_BUFFER, 1);
     1008                                    psVectorExtend(preCalc->vCoords, RINGS_BUFFER, 1);
     1009                                    psVectorExtend(preCalc->poly, RINGS_BUFFER, 1);
    6051010                                    psTrace("psModules.imcombine", 9, "u = %d, v = %d, poly = %f\n",
    606                                             u, v, poly->data.F32[j]);
     1011                                            u, v, preCalc->poly->data.F32[j]);
    6071012                                    j++;
    6081013                                }
     
    6121017                    // Normalise kernel component to unit sum
    6131018                    if (uOrder % 2 == 0 && vOrder % 2 == 0) {
    614                         psBinaryOp(poly, poly, "*", psScalarAlloc(1.0 / norm, PS_TYPE_F32));
     1019                        psBinaryOp(preCalc->poly, preCalc->poly, "*", psScalarAlloc(1.0 / norm, PS_TYPE_F32));
    6151020                        // Add subtraction of 0,0 component to preserve photometric scaling
    616                         uCoords->data.S32[j] = 0;
    617                         vCoords->data.S32[j] = 0;
    618                         poly->data.F32[j] = -1.0;
    619                         psVectorExtend(uCoords, RINGS_BUFFER, 1);
    620                         psVectorExtend(vCoords, RINGS_BUFFER, 1);
    621                         psVectorExtend(poly, RINGS_BUFFER, 1);
     1021                        preCalc->uCoords->data.S32[j] = 0;
     1022                        preCalc->vCoords->data.S32[j] = 0;
     1023                        preCalc->poly->data.F32[j] = -1.0;
     1024                        psVectorExtend(preCalc->uCoords, RINGS_BUFFER, 1);
     1025                        psVectorExtend(preCalc->vCoords, RINGS_BUFFER, 1);
     1026                        psVectorExtend(preCalc->poly, RINGS_BUFFER, 1);
    6221027                    } else {
    6231028                        norm = powf(size, uOrder) * powf(size, vOrder);
    624                         psBinaryOp(poly, poly, "*", psScalarAlloc(1.0 / norm, PS_TYPE_F32));
     1029                        psBinaryOp(preCalc->poly, preCalc->poly, "*", psScalarAlloc(1.0 / norm, PS_TYPE_F32));
    6251030                    }
    626                     moment /= norm;
     1031                    moment /= PS_SQR(norm);
    6271032                }
    6281033
    629                 psTrace("psModules.imcombine", 8, "%ld pixels in kernel\n", uCoords->n);
    630 
    631                 kernels->preCalc->data[index] = data;
     1034                psTrace("psModules.imcombine", 8, "%ld pixels in kernel\n", preCalc->uCoords->n);
     1035
     1036                kernels->preCalc->data[index] = preCalc;
    6321037                kernels->u->data.S32[index] = uOrder;
    6331038                kernels->v->data.S32[index] = vOrder;
    6341039                kernels->penalties->data.F32[index] = kernels->penalty * fabsf(moment);
     1040                if (!isfinite(kernels->penalties->data.F32[index])) {
     1041                    psAbort ("invalid penalty");
     1042                }
    6351043
    6361044                psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d\n", index,
     
    6451053pmSubtractionKernels *pmSubtractionKernelsGenerate(pmSubtractionKernelsType type, int size, int spatialOrder,
    6461054                                                   const psVector *fwhms, const psVector *orders, int inner,
    647                                                    int binning, int ringsOrder, float penalty,
     1055                                                   int binning, int ringsOrder, float penalty, psRegion bounds,
    6481056                                                   pmSubtractionMode mode)
    6491057{
    6501058    switch (type) {
    6511059      case PM_SUBTRACTION_KERNEL_POIS:
    652         return pmSubtractionKernelsPOIS(size, spatialOrder, penalty, mode);
     1060        return pmSubtractionKernelsPOIS(size, spatialOrder, penalty, bounds, mode);
    6531061      case PM_SUBTRACTION_KERNEL_ISIS:
    654         return pmSubtractionKernelsISIS(size, spatialOrder, fwhms, orders, penalty, mode);
     1062        return pmSubtractionKernelsISIS(size, spatialOrder, fwhms, orders, penalty, bounds, mode);
     1063      case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     1064        return pmSubtractionKernelsISIS_RADIAL(size, spatialOrder, fwhms, orders, penalty, bounds, mode);
     1065      case PM_SUBTRACTION_KERNEL_HERM:
     1066        return pmSubtractionKernelsHERM(size, spatialOrder, fwhms, orders, penalty, bounds, mode);
     1067      case PM_SUBTRACTION_KERNEL_DECONV_HERM:
     1068        return pmSubtractionKernelsDECONV_HERM(size, spatialOrder, fwhms, orders, penalty, bounds, mode);
    6551069      case PM_SUBTRACTION_KERNEL_SPAM:
    656         return pmSubtractionKernelsSPAM(size, spatialOrder, inner, binning, penalty, mode);
     1070        return pmSubtractionKernelsSPAM(size, spatialOrder, inner, binning, penalty, bounds, mode);
    6571071      case PM_SUBTRACTION_KERNEL_FRIES:
    658         return pmSubtractionKernelsFRIES(size, spatialOrder, inner, penalty, mode);
     1072        return pmSubtractionKernelsFRIES(size, spatialOrder, inner, penalty, bounds, mode);
    6591073      case PM_SUBTRACTION_KERNEL_GUNK:
    660         return pmSubtractionKernelsGUNK(size, spatialOrder, fwhms, orders, inner, penalty, mode);
     1074        return pmSubtractionKernelsGUNK(size, spatialOrder, fwhms, orders, inner, penalty, bounds, mode);
    6611075      case PM_SUBTRACTION_KERNEL_RINGS:
    662         return pmSubtractionKernelsRINGS(size, spatialOrder, inner, ringsOrder, penalty, mode);
     1076        return pmSubtractionKernelsRINGS(size, spatialOrder, inner, ringsOrder, penalty, bounds, mode);
    6631077      default:
    6641078        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unknown kernel type: %x", type);
     
    6961110
    6971111pmSubtractionKernels *pmSubtractionKernelsFromDescription(const char *description, int bgOrder,
    698                                                           pmSubtractionMode mode)
     1112                                                          psRegion bounds, pmSubtractionMode mode)
    6991113{
    7001114    PS_ASSERT_STRING_NON_EMPTY(description, NULL);
     
    7151129    float penalty = 0.0;                // Penalty for wideness
    7161130
    717     if (strncmp(description, "ISIS", 4) == 0) {
    718         // XXX Support for GUNK
    719         if (strstr(description, "+POIS")) {
    720             type = PM_SUBTRACTION_KERNEL_GUNK;
    721             psAbort("Deciphering GUNK kernels (%s) is not currently supported.", description);
    722         } else {
    723             type = PM_SUBTRACTION_KERNEL_ISIS;
    724             char *ptr = (char*)description + 5;    // Eat "ISIS("
    725             PARSE_STRING_NUMBER(size, ptr, ',', parseStringInt);
    726 
    727             // Count the number of Gaussians
    728             int numGauss = 0;
    729             for (char *string = ptr; string; string = strchr(string + 1, '(')) {
    730                 numGauss++;
    731             }
    732 
    733             fwhms = psVectorAlloc(numGauss, PS_TYPE_F32);
    734             orders = psVectorAlloc(numGauss, PS_TYPE_S32);
    735 
    736             for (int i = 0; i < numGauss; i++) {
    737                 ptr++;                  // Eat the '('
    738                 PARSE_STRING_NUMBER(fwhms->data.F32[i], ptr, ',', parseStringFloat); // Eat "1.234,"
    739                 PARSE_STRING_NUMBER(orders->data.S32[i], ptr, ')', parseStringInt); // Eat "3)"
    740             }
    741 
    742             ptr++;                      // Eat ','
    743             PARSE_STRING_NUMBER(spatialOrder, ptr, ',', parseStringInt);
    744             penalty = parseStringFloat(ptr);
    745         }
    746     } else if (strncmp(description, "RINGS", 5) == 0) {
    747         type = PM_SUBTRACTION_KERNEL_RINGS;
    748         char *ptr = (char*)description + 6;
     1131    // currently known descriptions:
     1132    // ISIS(...), ISIS_RADIAL(...), HERM(...), DECONV_HERM(...), POIS(...), SPAM(...),
     1133    // FRIES(...), GUNK=ISIS(...)+POIS(...), RINGS(...),
     1134    // the descriptive name is the set of characters before the (
     1135
     1136    type = pmSubtractionKernelsTypeFromString (description);
     1137    char *ptr = strchr(description, '(') + 1;
     1138    psAssert (ptr, "description is missing kernel parameters");
     1139
     1140    switch (type) {
     1141      case PM_SUBTRACTION_KERNEL_ISIS:
     1142      case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     1143      case PM_SUBTRACTION_KERNEL_HERM:
     1144      case PM_SUBTRACTION_KERNEL_DECONV_HERM:
     1145        PARSE_STRING_NUMBER(size, ptr, ',', parseStringInt);
     1146
     1147        // Count the number of Gaussians
     1148        int numGauss = 0;
     1149        for (char *string = ptr; string; string = strchr(string + 1, '(')) {
     1150            numGauss++;
     1151        }
     1152
     1153        fwhms = psVectorAlloc(numGauss, PS_TYPE_F32);
     1154        orders = psVectorAlloc(numGauss, PS_TYPE_S32);
     1155
     1156        for (int i = 0; i < numGauss; i++) {
     1157            ptr++;                                                               // Eat the '('
     1158            PARSE_STRING_NUMBER(fwhms->data.F32[i], ptr, ',', parseStringFloat); // Eat "1.234,"
     1159            PARSE_STRING_NUMBER(orders->data.S32[i], ptr, ')', parseStringInt);  // Eat "3)"
     1160        }
     1161
     1162        ptr++;                      // Eat ','
     1163        PARSE_STRING_NUMBER(spatialOrder, ptr, ',', parseStringInt);
     1164        penalty = parseStringFloat(ptr);
     1165        break;
     1166      case PM_SUBTRACTION_KERNEL_RINGS:
    7491167        PARSE_STRING_NUMBER(size, ptr, ',', parseStringInt);
    7501168        PARSE_STRING_NUMBER(inner, ptr, ',', parseStringInt);
     
    7521170        PARSE_STRING_NUMBER(spatialOrder, ptr, ',', parseStringInt);
    7531171        PARSE_STRING_NUMBER(penalty, ptr, ')', parseStringInt);
    754     } else {
    755         psAbort("Deciphering kernels other than ISIS and RINGS is not currently supported.");
    756     }
    757 
    758 
    759     return pmSubtractionKernelsGenerate(type, size, spatialOrder, fwhms, orders,
    760                                         inner, binning, ringsOrder, penalty, mode);
    761 }
    762 
    763 
     1172        break;
     1173      default:
     1174        psAbort("Deciphering kernels other than ISIS, HERM, DECONV_HERM or RINGS is not currently supported.");
     1175    }
     1176
     1177    return pmSubtractionKernelsGenerate(type, size, spatialOrder, fwhms, orders, inner, binning,
     1178                                        ringsOrder, penalty, bounds, mode);
     1179}
     1180
     1181
     1182// the input string can either be just the name or the description string.  Currently known
     1183// descriptions: ISIS(...), ISIS_RADIAL(...), HERM(...), DECONV_HERM(...), POIS(...),
     1184// SPAM(...), FRIES(...), GUNK=ISIS(...)+POIS(...), RINGS(...),
    7641185pmSubtractionKernelsType pmSubtractionKernelsTypeFromString(const char *type)
    7651186{
    766     if (strcasecmp(type, "POIS") == 0) {
     1187    // for a bare name (ISIS, HERM), use the full string length.
     1188    // otherwise, use the length up to the first '('
     1189    int nameLength = strlen(type);
     1190    char *ptr = strchr(type, '(');
     1191    if (ptr) {
     1192        nameLength = ptr - type;
     1193    }
     1194
     1195    if (strncasecmp(type, "POIS", nameLength) == 0) {
    7671196        return PM_SUBTRACTION_KERNEL_POIS;
    7681197    }
    769     if (strcasecmp(type, "ISIS") == 0) {
     1198    if (strncasecmp(type, "ISIS", nameLength) == 0) {
    7701199        return PM_SUBTRACTION_KERNEL_ISIS;
    7711200    }
    772     if (strcasecmp(type, "SPAM") == 0) {
     1201    if (strncasecmp(type, "ISIS_RADIAL", nameLength) == 0) {
     1202        return PM_SUBTRACTION_KERNEL_ISIS_RADIAL;
     1203    }
     1204    if (strncasecmp(type, "HERM", nameLength) == 0) {
     1205        return PM_SUBTRACTION_KERNEL_HERM;
     1206    }
     1207    if (strncasecmp(type, "DECONV_HERM", nameLength) == 0) {
     1208        return PM_SUBTRACTION_KERNEL_DECONV_HERM;
     1209    }
     1210    if (strncasecmp(type, "SPAM", nameLength) == 0) {
    7731211        return PM_SUBTRACTION_KERNEL_SPAM;
    7741212    }
    775     if (strcasecmp(type, "FRIES") == 0) {
     1213    if (strncasecmp(type, "FRIES", nameLength) == 0) {
    7761214        return PM_SUBTRACTION_KERNEL_FRIES;
    7771215    }
    778     if (strcasecmp(type, "GUNK") == 0) {
     1216    if (strncasecmp(type, "GUNK", nameLength) == 0) {
    7791217        return PM_SUBTRACTION_KERNEL_GUNK;
    7801218    }
    781     if (strcasecmp(type, "RINGS") == 0) {
     1219    // note that GUNK has a somewhat different description
     1220    if (strncasecmp(type, "GUNK=ISIS", nameLength) == 0) {
     1221        return PM_SUBTRACTION_KERNEL_GUNK;
     1222    }
     1223    if (strncasecmp(type, "RINGS", nameLength) == 0) {
    7821224        return PM_SUBTRACTION_KERNEL_RINGS;
    7831225    }
     
    8101252    out->bgOrder = in->bgOrder;
    8111253    out->mode = in->mode;
    812     out->numCols = in->numCols;
    813     out->numRows = in->numRows;
     1254    out->xMin = in->xMin;
     1255    out->xMax = in->xMax;
     1256    out->yMin = in->yMin;
     1257    out->yMax = in->yMax;
    8141258    out->solution1 = in->solution1 ? psVectorCopy(NULL, in->solution1, PS_TYPE_F64) : NULL;
    8151259    out->solution2 = in->solution2 ? psVectorCopy(NULL, in->solution2, PS_TYPE_F64) : NULL;
     1260    out->sampleStamps = psMemIncrRefCounter(in->sampleStamps);
    8161261
    8171262    return out;
  • trunk/psModules/src/imcombine/pmSubtractionKernels.h

    r26157 r26893  
    1010    PM_SUBTRACTION_KERNEL_POIS,         ///< Pan-STARRS Optimal Image Subtraction --- delta functions
    1111    PM_SUBTRACTION_KERNEL_ISIS,         ///< Traditional kernel --- gaussians modified by polynomials
     12    PM_SUBTRACTION_KERNEL_ISIS_RADIAL,  ///< ISIS + higher-order radial Hermitians
     13    PM_SUBTRACTION_KERNEL_HERM,         ///< Hermitian polynomial kernels
     14    PM_SUBTRACTION_KERNEL_DECONV_HERM,  ///< Deconvolved Hermitian polynomial kernels
    1215    PM_SUBTRACTION_KERNEL_SPAM,         ///< Summed Pixels for Advanced Matching --- summed delta functions
    1316    PM_SUBTRACTION_KERNEL_FRIES,        ///< Fibonacci Radius Increases Excellence of Subtraction
     
    2932    pmSubtractionKernelsType type;      ///< Type of kernels --- allowing the use of multiple kernels
    3033    psString description;               ///< Description of the kernel parameters
     34    int xMin, xMax, yMin, yMax;         ///< Bounds of image (for normalisation)
    3135    long num;                           ///< Number of kernel components (not including the spatial ones)
    32     psVector *u, *v;                    ///< Offset (for POIS) or polynomial order (for ISIS)
    33     psVector *widths;                   ///< Gaussian FWHMs (ISIS)
     36    psVector *u, *v;                    ///< Offset (for POIS) or polynomial order (for ISIS, HERM or DECONV_HERM)
     37    psVector *widths;                   ///< Gaussian FWHMs (ISIS, HERM or DECONV_HERM)
    3438    psVector *uStop, *vStop;            ///< Width of kernel element (SPAM,FRIES only)
    35     psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (for ISIS)
     39    psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (for ISIS, HERM or DECONV_HERM)
    3640    float penalty;                      ///< Penalty for wideness
    3741    psVector *penalties;                ///< Penalty for each kernel component
     
    4145    int bgOrder;                        ///< The order for the background fitting
    4246    pmSubtractionMode mode;             ///< Mode for subtraction
    43     int numCols, numRows;               ///< Size of image (for normalisation), or zero to use image provided
    4447    psVector *solution1, *solution2;    ///< Solution for the PSF matching
    4548    // Quality information
    4649    float mean, rms;                    ///< Mean and RMS of chi^2 from stamps
    4750    int numStamps;                      ///< Number of good stamps
     51    float fSigResMean;                  ///< mean fractional stdev of residuals
     52    float fSigResStdev;                 ///< stdev of fractional stdev of residuals
     53    float fMaxResMean;                  ///< mean fractional positive swing in residuals
     54    float fMaxResStdev;                 ///< stdev of fractional positive swing in residuals
     55    float fMinResMean;                  ///< mean fractional negative swing in residuals
     56    float fMinResStdev;                 ///< stdev of fractional negative swing in residuals
     57    psArray *sampleStamps;              ///< array of brightest set of stamps for output visualizations
    4858} pmSubtractionKernels;
     59
     60// pmSubtractionKernels->preCalc is an array of pmSubtractionKernelPreCalc structures
     61typedef struct {
     62    psVector *uCoords;                  // used by RINGS
     63    psVector *vCoords;                  // used by RINGS
     64    psVector *poly;                     // used by RINGS
     65
     66    psVector *xKernel;                  // used by ISIS, HERM, DECONV_HERM
     67    psVector *yKernel;                  // used by ISIS, HERM, DECONV_HERM
     68    psKernel *kernel;                   // used by ISIS, HERM, DECONV_HERM
     69} pmSubtractionKernelPreCalc;
    4970
    5071// Assertion to check pmSubtractionKernels
     
    6485        PS_ASSERT_VECTOR_SIZE((KERNELS)->widths, (KERNELS)->num, RETURNVALUE); \
    6586    } \
     87    if ((KERNELS)->type == PM_SUBTRACTION_KERNEL_ISIS_RADIAL) { \
     88        PS_ASSERT_VECTOR_NON_NULL((KERNELS)->widths, RETURNVALUE); \
     89        PS_ASSERT_VECTOR_TYPE((KERNELS)->widths, PS_TYPE_F32, RETURNVALUE); \
     90        PS_ASSERT_VECTOR_SIZE((KERNELS)->widths, (KERNELS)->num, RETURNVALUE); \
     91    } \
     92    if ((KERNELS)->type == PM_SUBTRACTION_KERNEL_HERM) { \
     93        PS_ASSERT_VECTOR_NON_NULL((KERNELS)->widths, RETURNVALUE); \
     94        PS_ASSERT_VECTOR_TYPE((KERNELS)->widths, PS_TYPE_F32, RETURNVALUE); \
     95        PS_ASSERT_VECTOR_SIZE((KERNELS)->widths, (KERNELS)->num, RETURNVALUE); \
     96    } \
     97    if ((KERNELS)->type == PM_SUBTRACTION_KERNEL_DECONV_HERM) { \
     98        PS_ASSERT_VECTOR_NON_NULL((KERNELS)->widths, RETURNVALUE); \
     99        PS_ASSERT_VECTOR_TYPE((KERNELS)->widths, PS_TYPE_F32, RETURNVALUE); \
     100        PS_ASSERT_VECTOR_SIZE((KERNELS)->widths, (KERNELS)->num, RETURNVALUE); \
     101    } \
    66102    if ((KERNELS)->uStop || (KERNELS)->vStop) { \
    67103        PS_ASSERT_VECTOR_NON_NULL((KERNELS)->uStop, RETURNVALUE); \
     
    99135}
    100136
     137// Generate 1D convolution kernel for ISIS
     138psVector *pmSubtractionKernelISIS(float sigma, // Gaussian width
     139                                       int order, // Polynomial order
     140                                       int size // Kernel half-size
     141    );
     142
     143// Generate 1D convolution kernel for HERM (normalized for 2D)
     144psVector *pmSubtractionKernelHERM(float sigma, // Gaussian width
     145                                       int order, // Polynomial order
     146                                       int size // Kernel half-size
     147    );
     148
    101149/// Generate a delta-function grid for subtraction kernels (like the POIS kernel)
    102150bool p_pmSubtractionKernelsAddGrid(pmSubtractionKernels *kernels, ///< The subtraction kernels to append to
     
    114162                                                int spatialOrder, ///< Order of spatial variations
    115163                                                float penalty, ///< Penalty for wideness
     164                                                psRegion bounds,       ///< Bounds for validity
    116165                                                pmSubtractionMode mode ///< Mode for subtraction
    117166    );
     167
     168/// Allocator for pre-calculated kernel data structure
     169pmSubtractionKernelPreCalc *pmSubtractionKernelPreCalcAlloc(
     170    pmSubtractionKernelsType type, ///< type of kernel to allocate (not all can be pre-calculated)
     171    int uOrder,                    ///< order in x-direction
     172    int vOrder,                    ///< order in x-direction
     173    int size,                      ///< Half-size of the kernel
     174    float sigma                    ///< sigma of gaussian kernel
     175    );
     176
    118177
    119178/// Generate POIS kernels
     
    121180                                               int spatialOrder, ///< Order of spatial variations
    122181                                               float penalty, ///< Penalty for wideness
     182                                               psRegion bounds,       ///< Bounds for validity
    123183                                               pmSubtractionMode mode ///< Mode for subtraction
    124184    );
     
    130190                                                    const psVector *orders, ///< Polynomial order of gaussians
    131191                                                    float penalty, ///< Penalty for wideness
     192                                                    psRegion bounds,       ///< Bounds for validity
    132193                                                    pmSubtractionMode mode ///< Mode for subtraction
    133194    );
     
    139200                                               const psVector *orders, ///< Polynomial order of gaussians
    140201                                               float penalty, ///< Penalty for wideness
     202                                               psRegion bounds,       ///< Bounds for validity
    141203                                               pmSubtractionMode mode ///< Mode for subtraction
    142204                                               );
     205
     206/// Generate ISIS + RADIAL_HERM kernels
     207pmSubtractionKernels *pmSubtractionKernelsISIS_RADIAL(int size, ///< Half-size of the kernel
     208                                                      int spatialOrder, ///< Order of spatial variations
     209                                                      const psVector *fwhms, ///< Gaussian FWHMs
     210                                                      const psVector *orders, ///< Polynomial order of gaussians
     211                                                      float penalty, ///< Penalty for wideness
     212                                                      psRegion bounds,       ///< Bounds for validity
     213                                                      pmSubtractionMode mode ///< Mode for subtraction
     214                                               );
     215
     216/// Generate HERM kernels
     217pmSubtractionKernels *pmSubtractionKernelsHERM(int size, ///< Half-size of the kernel
     218                                               int spatialOrder, ///< Order of spatial variations
     219                                               const psVector *fwhms, ///< Gaussian FWHMs
     220                                               const psVector *orders, ///< order of hermitian polynomials
     221                                               float penalty, ///< Penalty for wideness
     222                                               psRegion bounds,       ///< Bounds for validity
     223                                               pmSubtractionMode mode ///< Mode for subtraction
     224                                               );
     225
     226/// Generate DECONV_HERM kernels
     227pmSubtractionKernels *pmSubtractionKernelsDECONV_HERM(int size, ///< Half-size of the kernel
     228                                                      int spatialOrder, ///< Order of spatial variations
     229                                                      const psVector *fwhms, ///< Gaussian FWHMs
     230                                                      const psVector *orders, ///< order of hermitian polynomials
     231                                                      float penalty, ///< Penalty for wideness
     232                                                      psRegion bounds,       ///< Bounds for validity
     233                                                      pmSubtractionMode mode ///< Mode for subtraction
     234    );
    143235
    144236/// Generate SPAM kernels
     
    148240                                               int binning, ///< Kernel binning factor
    149241                                               float penalty, ///< Penalty for wideness
     242                                               psRegion bounds,       ///< Bounds for validity
    150243                                               pmSubtractionMode mode ///< Mode for subtraction
    151244    );
     
    156249                                                int inner, ///< Inner radius to preserve unbinned
    157250                                                float penalty, ///< Penalty for wideness
     251                                                psRegion bounds,       ///< Bounds for validity
    158252                                                pmSubtractionMode mode ///< Mode for subtraction
    159253    );
     
    166260                                               int inner, ///< Inner radius containing grid of delta functions
    167261                                               float penalty, ///< Penalty for wideness
     262                                               psRegion bounds,       ///< Bounds for validity
    168263                                               pmSubtractionMode mode ///< Mode for subtraction
    169264    );
     
    175270                                                int ringsOrder, ///< Polynomial order
    176271                                                float penalty, ///< Penalty for wideness
     272                                                psRegion bounds,       ///< Bounds for validity
    177273                                                pmSubtractionMode mode ///< Mode for subtraction
    178274    );
     
    189285                                                   int ringsOrder, ///< Polynomial order for RINGS
    190286                                                   float penalty, ///< Penalty for wideness
     287                                                   psRegion bounds,       ///< Bounds for validity
    191288                                                   pmSubtractionMode mode ///< Mode for subtraction
    192289    );
     
    196293    const char *description,            ///< Description of kernel
    197294    int bgOrder,                        ///< Polynomial order for background fitting
     295    psRegion bounds,                    ///< Bounds for validity
    198296    pmSubtractionMode mode              ///< Mode for subtraction
    199297    );
  • trunk/psModules/src/imcombine/pmSubtractionMask.c

    r24257 r26893  
    3838//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    3939
    40 psImage *pmSubtractionMask(const psImage *mask1, const psImage *mask2, psImageMaskType maskVal,
    41                            int size, int footprint, float badFrac, pmSubtractionMode mode)
    42 {
    43     PS_ASSERT_IMAGE_NON_NULL(mask1, NULL);
    44     PS_ASSERT_IMAGE_TYPE(mask1, PS_TYPE_IMAGE_MASK, NULL);
    45     if (mask2) {
    46         PS_ASSERT_IMAGE_NON_NULL(mask2, NULL);
    47         PS_ASSERT_IMAGE_TYPE(mask2, PS_TYPE_IMAGE_MASK, NULL);
    48         PS_ASSERT_IMAGES_SIZE_EQUAL(mask2, mask1, NULL);
    49     }
     40psImage *pmSubtractionMask(psRegion *bounds, const pmReadout *ro1, const pmReadout *ro2,
     41                           psImageMaskType maskVal, int size, int footprint, float badFrac,
     42                           pmSubtractionMode mode)
     43{
     44    PM_ASSERT_READOUT_NON_NULL(ro1, NULL);
     45    PM_ASSERT_READOUT_IMAGE(ro1, NULL);
     46    PM_ASSERT_READOUT_MASK(ro1, NULL);
     47    PM_ASSERT_READOUT_NON_NULL(ro2, NULL);
     48    PM_ASSERT_READOUT_IMAGE(ro2, NULL);
     49    PM_ASSERT_READOUT_MASK(ro2, NULL);
     50    PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->image, ro2->image, NULL);
    5051    PS_ASSERT_INT_NONNEGATIVE(size, NULL);
    5152    PS_ASSERT_INT_NONNEGATIVE(footprint, NULL);
     
    5556    }
    5657
    57     int numCols = mask1->numCols, numRows = mask1->numRows; // Size of the images
     58    int numCols = ro1->image->numCols, numRows = ro1->image->numRows; // Size of the images
    5859
    5960    // Dereference inputs for convenience
    60     psImageMaskType **data1 = mask1->data.PS_TYPE_IMAGE_MASK_DATA;
    61     psImageMaskType **data2 = NULL;
    62     if (mask2) {
    63         data2 = mask2->data.PS_TYPE_IMAGE_MASK_DATA;
    64     }
     61    psF32 **imageData1 = ro1->image->data.F32, **imageData2 = ro2->image->data.F32;
     62    psImageMaskType **maskData1 = ro1->mask->data.PS_TYPE_IMAGE_MASK_DATA,
     63        **maskData2 = ro2->mask->data.PS_TYPE_IMAGE_MASK_DATA;
    6564
    6665    // First, a pass through to determine the fraction of bad pixels
    67     if (isfinite(badFrac) && badFrac != 1.0) {
     66    if (bounds || (isfinite(badFrac) && badFrac != 1.0)) {
     67        int xMin = numCols, xMax = 0, yMin = numRows, yMax = 0; // Bounds of good pixels
    6868        int numBad = 0;                 // Number of bad pixels
    6969        for (int y = 0; y < numRows; y++) {
    7070            for (int x = 0; x < numCols; x++) {
    71                 if (data1[y][x] & maskVal) {
     71                if ((maskData1[y][x] & maskVal) || !isfinite(imageData1[y][x])) {
    7272                    numBad++;
    7373                    continue;
    7474                }
    75                 if (data2 && data2[y][x] & maskVal) {
     75                if ((maskData2[y][x] & maskVal) || !isfinite(imageData2[y][x])) {
    7676                    numBad++;
     77                    continue;
    7778                }
    78             }
    79         }
    80         if (numBad > badFrac * numCols * numRows) {
     79                xMin = PS_MIN(xMin, x);
     80                xMax = PS_MAX(xMax, x);
     81                yMin = PS_MIN(yMin, y);
     82                yMax = PS_MAX(yMax, y);
     83            }
     84        }
     85        if (bounds) {
     86            bounds->x0 = xMin;
     87            bounds->x1 = xMax;
     88            bounds->y0 = yMin;
     89            bounds->y1 = yMax;
     90        }
     91        if (isfinite(badFrac) && badFrac != 1.0 && numBad > badFrac * numCols * numRows) {
    8192            psError(PM_ERR_SMALL_AREA, true,
    8293                    "Fraction of bad pixels (%d/%d=%f) exceeds limit (%f)\n",
     
    117128    for (int y = 0; y < numRows; y++) {
    118129        for (int x = 0; x < numCols; x++) {
    119             if (data1[y][x] & maskVal) {
     130            if (maskData1[y][x] & maskVal) {
    120131                maskData[y][x] |= PM_SUBTRACTION_MASK_BAD_1;
    121132            }
    122             if (data2 && data2[y][x] & maskVal) {
     133            if (maskData2[y][x] & maskVal) {
    123134                maskData[y][x] |= PM_SUBTRACTION_MASK_BAD_2;
    124135            }
  • trunk/psModules/src/imcombine/pmSubtractionMask.h

    r23851 r26893  
    55
    66/// Generate a mask for use in the subtraction process
    7 psImage *pmSubtractionMask(const psImage *refMask, ///< Mask for the reference image (will be convolved)
    8                            const psImage *inMask, ///< Mask for the input image, or NULL
    9                            psImageMaskType maskVal, ///< Value to mask out
    10                            int size, ///< Half-size of the kernel (pmSubtractionKernels.size)
    11                            int footprint, ///< Half-size of the kernel footprint
    12                            float badFrac, ///< Maximum fraction of bad input pixels to accept
    13                            pmSubtractionMode mode  ///< Subtraction mode
     7psImage *pmSubtractionMask(
     8    psRegion *bounds,                   ///< Bounds of valid pixels (or NULL), returned
     9    const pmReadout *ro1,               ///< Readout 1
     10    const pmReadout *ro2,               ///< Readout 2
     11    psImageMaskType maskVal,            ///< Value to mask out
     12    int size,                           ///< Half-size of the kernel (pmSubtractionKernels.size)
     13    int footprint,                      ///< Half-size of the kernel footprint
     14    float badFrac,                      ///< Maximum fraction of bad input pixels to accept
     15    pmSubtractionMode mode              ///< Subtraction mode
    1416    );
    1517
  • trunk/psModules/src/imcombine/pmSubtractionMatch.c

    r26035 r26893  
    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
     34# define SUBMODE PM_SUBTRACTION_EQUATION_ALL
     35# endif
    3036
    3137//#define TESTING
     
    6470                                 const psImage *subMask, // Mask for subtraction, or NULL
    6571                                 psImage *variance,  // Variance map
    66                                  const psRegion *region, // Region of interest, or NULL
     72                                 const psRegion *region, // Region of interest
    6773                                 float thresh1,  // Threshold for stamp finding on readout 1
    6874                                 float thresh2,  // Threshold for stamp finding on readout 2
    6975                                 float stampSpacing, // Spacing between stamps
     76                                 float normFrac,     // Fraction of flux in window for normalisation window
    7077                                 float sysError,     // Relative systematic error in images
     78                                 float skyError,     // Relative systematic error in images
    7179                                 int size,         // Kernel half-size
    7280                                 int footprint,     // Convolution footprint for stamps
     
    7482    )
    7583{
     84    PS_ASSERT_PTR_NON_NULL(stamps, false);
     85    PM_ASSERT_READOUT_NON_NULL(ro1, false);
     86    PM_ASSERT_READOUT_NON_NULL(ro2, false);
     87    PS_ASSERT_IMAGE_NON_NULL(subMask, false);
     88    PS_ASSERT_IMAGE_NON_NULL(variance, false);
     89    PS_ASSERT_PTR_NON_NULL(region, false);
     90
    7691    psTrace("psModules.imcombine", 3, "Finding stamps...\n");
    7792
     
    7994
    8095    *stamps = pmSubtractionStampsFind(*stamps, image1, image2, subMask, region, thresh1, thresh2,
    81                                       size, footprint, stampSpacing, sysError, mode);
     96                                      size, footprint, stampSpacing, normFrac, sysError, skyError, mode);
    8297    if (!*stamps) {
    8398        psError(psErrorCodeLast(), false, "Unable to find stamps.");
     
    88103
    89104    psTrace("psModules.imcombine", 3, "Extracting stamps...\n");
    90     if (!pmSubtractionStampsExtract(*stamps, ro1->image, ro2 ? ro2->image : NULL, variance, size)) {
     105    if (!pmSubtractionStampsExtract(*stamps, ro1->image, ro2->image, variance, size, *region)) {
    91106        psError(PS_ERR_UNKNOWN, false, "Unable to extract stamps.");
    92107        return false;
     
    102117                                  const pmReadout *ro1, const pmReadout *ro2, // Input images
    103118                                  int stride, // Size for convolution patches
     119                                  float normFrac,           // Fraction of window for normalisation window
    104120                                  float sysError,           // Systematic error in images
     121                                  float skyError,           // Systematic error in images
    105122                                  float kernelError, // Systematic error in kernel
     123                                  float covarFrac,   // Fraction for kernel truncation before covariance
    106124                                  psImageMaskType maskVal, // Value to mask for input
    107125                                  psImageMaskType maskBad, // Mask for output bad pixels
     
    149167    PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->image, ro2->image, false);
    150168    PS_ASSERT_INT_NONNEGATIVE(stride, false);
     169    if (isfinite(normFrac)) {
     170        PS_ASSERT_FLOAT_LARGER_THAN(normFrac, 0.0, false);
     171        PS_ASSERT_FLOAT_LESS_THAN(normFrac, 1.0, false);
     172    }
    151173    if (isfinite(sysError)) {
    152174        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(sysError, 0.0, false);
    153175        PS_ASSERT_FLOAT_LESS_THAN(sysError, 1.0, false);
    154176    }
     177    if (isfinite(sysError)) {
     178        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(skyError, 0.0, false);
     179    }
    155180    if (isfinite(kernelError)) {
    156181        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(kernelError, 0.0, false);
    157182        PS_ASSERT_FLOAT_LESS_THAN(kernelError, 1.0, false);
    158183    }
     184    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(covarFrac, 0.0, false);
     185    PS_ASSERT_FLOAT_LESS_THAN(covarFrac, 1.0, false);
    159186    // Don't care about maskVal
    160187    // Don't care about maskBad
     
    198225}
    199226
     227bool pmSubtractionMaskInvalid (const pmReadout *readout, psImageMaskType maskVal) {
     228
     229    if (!readout) return true;
     230
     231    psImage *image = readout->image;
     232    psImage *mask  = readout->mask;
     233    psImage *variance = readout->variance;
     234    for (int y = 0; y < image->numRows; y++) {
     235        for (int x = 0; x < image->numCols; x++) {
     236            if (mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) continue;
     237            bool valid = false;
     238            valid = isfinite(image->data.F32[y][x]);
     239            if (variance) {
     240                valid &= isfinite(variance->data.F32[y][x]);
     241            }
     242            if (valid) continue;
     243            mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] = maskVal;
     244        }
     245    }
     246
     247    return true;
     248}
    200249
    201250bool pmSubtractionMatchPrecalc(pmReadout *conv1, pmReadout *conv2, const pmReadout *ro1, const pmReadout *ro2,
    202                                psMetadata *analysis, int stride, float kernelError,
     251                               psMetadata *analysis, int stride, float kernelError, float covarFrac,
    203252                               psImageMaskType maskVal, psImageMaskType maskBad, psImageMaskType maskPoor,
    204253                               float poorFrac, float badFrac)
     
    270319    }
    271320
    272     if (!subtractionMatchCheck(conv1, conv2, ro1, ro2, stride, NAN, kernelError,
     321    if (!subtractionMatchCheck(conv1, conv2, ro1, ro2, stride, NAN, NAN, NAN, kernelError, covarFrac,
    273322                               maskVal, maskBad, maskPoor, poorFrac, badFrac, mode)) {
    274323        psFree(kernels);
     
    277326    }
    278327
    279     psImage *subMask = pmSubtractionMask(ro1->mask, ro2 ? ro2->mask : NULL, maskVal, size, 0,
     328    pmSubtractionMaskInvalid(ro1, maskVal);
     329    pmSubtractionMaskInvalid(ro2, maskVal);
     330
     331    psRegion bounds = psRegionSet(NAN, NAN, NAN, NAN); // Bounds of valid pixels
     332
     333    psImage *subMask = pmSubtractionMask(&bounds, ro1, ro2, maskVal, size, 0,
    280334                                         badFrac, mode); // Subtraction mask
    281335    if (!subMask) {
     
    306360
    307361        if (!pmSubtractionConvolve(conv1, conv2, ro1, ro2, subMask, stride, maskBad, maskPoor, poorFrac,
    308                                    kernelError, region, kernel, true, useFFT)) {
     362                                   kernelError, covarFrac, region, kernel, true, useFFT)) {
    309363            psError(PS_ERR_UNKNOWN, false, "Unable to convolve image.");
    310364            psFree(outAnalysis);
     
    336390                        int inner, int ringsOrder, int binning, float penalty,
    337391                        bool optimum, const psVector *optFWHMs, int optOrder, float optThreshold,
    338                         int iter, float rej, float sysError, float kernelError, psImageMaskType maskVal,
    339                         psImageMaskType maskBad, psImageMaskType maskPoor, float poorFrac,
    340                         float badFrac, pmSubtractionMode subMode)
     392                        int iter, float rej, float normFrac, float sysError, float skyError,
     393                        float kernelError, float covarFrac, psImageMaskType maskVal, psImageMaskType maskBad,
     394                        psImageMaskType maskPoor, float poorFrac, float badFrac, pmSubtractionMode subMode)
    341395{
    342     if (!subtractionMatchCheck(conv1, conv2, ro1, ro2, stride, sysError, kernelError,
    343                                maskVal, maskBad, maskPoor, poorFrac, badFrac, subMode)) {
     396    if (!subtractionMatchCheck(conv1, conv2, ro1, ro2, stride, normFrac, sysError, skyError, kernelError,
     397                               covarFrac, maskVal, maskBad, maskPoor, poorFrac, badFrac, subMode)) {
    344398        return false;
    345399    }
     
    399453    // Putting important variable declarations here, since they are freed after a "goto" if there is an error.
    400454    psImage *subMask = NULL;            // Mask for subtraction
    401     psRegion *region = NULL;            // Iso-kernel region
     455    psRegion *region = psRegionAlloc(NAN, NAN, NAN, NAN); // Iso-kernel region
    402456    psString regionString = NULL;       // String for region
    403457    pmSubtractionStampList *stamps = NULL; // Stamps for matching PSF
     
    412466    memCheck("start");
    413467
    414     subMask = pmSubtractionMask(ro1->mask, ro2 ? ro2->mask : NULL, maskVal, size, footprint,
    415                                 badFrac, subMode);
     468    pmSubtractionMaskInvalid(ro1, maskVal);
     469    pmSubtractionMaskInvalid(ro2, maskVal);
     470
     471    psRegion bounds = psRegionSet(NAN, NAN, NAN, NAN); // Bounds of valid pixels
     472
     473    subMask = pmSubtractionMask(&bounds, ro1, ro2, maskVal, size, footprint, badFrac, subMode);
    416474    if (!subMask) {
    417475        psError(psErrorCodeLast(), false, "Unable to generate subtraction mask.");
     
    423481    // Get region of interest
    424482    int xRegions = 1, yRegions = 1;     // Number of iso-kernel regions
    425     float xRegionSize = 0, yRegionSize = 0; // Size of iso-kernel regions
     483    float xRegionSize = NAN, yRegionSize = NAN; // Size of iso-kernel regions
    426484    if (isfinite(regionSize) && regionSize != 0.0) {
    427         xRegions = numCols / regionSize + 1;
    428         yRegions = numRows / regionSize + 1;
    429         xRegionSize = (float)numCols / (float)xRegions;
    430         yRegionSize = (float)numRows / (float)yRegions;
    431         region = psRegionAlloc(NAN, NAN, NAN, NAN);
    432     }
    433 
     485        xRegions = (bounds.x1 - bounds.x0) / regionSize + 1;
     486        yRegions = (bounds.y1 - bounds.y0) / regionSize + 1;
     487        xRegionSize = (float)(bounds.x1 - bounds.x0) / (float)xRegions;
     488        yRegionSize = (float)(bounds.y1 - bounds.y0) / (float)yRegions;
     489    } else {
     490        xRegionSize = bounds.x1 - bounds.x0;
     491        yRegionSize = bounds.y1 - bounds.y0;
     492    }
     493
     494    // General background subtraction and measurement of stamp threshold
    434495    float stampThresh1 = NAN, stampThresh2 = NAN; // Stamp thresholds for images
    435496    {
    436         psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for backgroun
     497        psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for background
    437498        if (ro1) {
     499            psStatsInit(bg);
    438500            if (!psImageBackground(bg, NULL, ro1->image, ro1->mask, maskVal, rng)) {
    439501                psError(PS_ERR_UNKNOWN, false, "Unable to measure background statistics.");
     
    441503                goto MATCH_ERROR;
    442504            }
    443             stampThresh1 = bg->robustMedian + threshold * bg->robustStdev;
     505            stampThresh1 = threshold * bg->robustStdev;
     506            psBinaryOp(ro1->image, ro1->image, "-", psScalarAlloc((float)bg->robustMedian, PS_TYPE_F32));
    444507        }
    445508        if (ro2) {
     509            psStatsInit(bg);
    446510            if (!psImageBackground(bg, NULL, ro2->image, ro2->mask, maskVal, rng)) {
    447511                psError(PS_ERR_UNKNOWN, false, "Unable to measure background statistics.");
     
    449513                goto MATCH_ERROR;
    450514            }
    451             stampThresh2 = bg->robustMedian + threshold * bg->robustStdev;
    452         }
     515            stampThresh2 = threshold * bg->robustStdev;
     516            psBinaryOp(ro2->image, ro2->image, "-", psScalarAlloc((float)bg->robustMedian, PS_TYPE_F32));
     517       }
    453518        psFree(bg);
    454519    }
     520
     521    // Just in case the iso-kernel region doesn't cover the entire image...
     522    if (subMode == PM_SUBTRACTION_MODE_1 || subMode == PM_SUBTRACTION_MODE_UNSURE ||
     523        subMode == PM_SUBTRACTION_MODE_DUAL) {
     524        if (!conv1->image) {
     525            conv1->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     526        }
     527        psImageInit(conv1->image, NAN);
     528        if (ro1->variance) {
     529            if (!conv1->variance) {
     530                conv1->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     531            }
     532            psImageInit(conv1->variance, NAN);
     533        }
     534        if (subMask) {
     535            if (!conv1->mask) {
     536                conv1->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
     537            }
     538            psImageInit(conv1->mask, maskBad);
     539        }
     540    }
     541    if (subMode == PM_SUBTRACTION_MODE_2 || subMode == PM_SUBTRACTION_MODE_UNSURE ||
     542        subMode == PM_SUBTRACTION_MODE_DUAL) {
     543        if (!conv2->image) {
     544            conv2->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     545        }
     546        psImageInit(conv2->image, NAN);
     547        if (ro2->variance) {
     548            if (!conv2->variance) {
     549                conv2->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     550            }
     551            psImageInit(conv2->variance, NAN);
     552        }
     553        if (subMask) {
     554            if (!conv2->mask) {
     555                conv2->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
     556            }
     557            psImageInit(conv2->mask, maskBad);
     558        }
     559    }
     560
    455561
    456562    // Iterate over iso-kernel regions
     
    459565            psTrace("psModules.imcombine", 1, "Subtracting region %d of %d...\n",
    460566                    j * xRegions + i + 1, xRegions * yRegions);
    461             if (region) {
    462                 *region = psRegionSet((int)(i * xRegionSize), (int)((i + 1) * xRegionSize),
    463                                       (int)(j * yRegionSize), (int)((j + 1) * yRegionSize));
    464                 psFree(regionString);
    465                 regionString = psRegionToString(*region);
    466                 psTrace("psModules.imcombine", 3, "Iso-kernel region: %s out of %d,%d\n",
    467                         regionString, numCols, numRows);
    468             }
     567            *region = psRegionSet(bounds.x0 + (int)(i * xRegionSize),
     568                                  bounds.x0 + (int)((i + 1) * xRegionSize),
     569                                  bounds.y0 + (int)(j * yRegionSize),
     570                                  bounds.y0 + (int)((j + 1) * yRegionSize));
     571            psFree(regionString);
     572            regionString = psRegionToString(*region);
     573            psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "Iso-kernel region: %s out of %d,%d\n",
     574                    regionString, numCols, numRows);
    469575
    470576            if (stampsName && strlen(stampsName) > 0) {
    471577                stamps = pmSubtractionStampsSetFromFile(stampsName, ro1->image, subMask, region, size,
    472                                                         footprint, stampSpacing, sysError, subMode);
     578                                                        footprint, stampSpacing, normFrac,
     579                                                        sysError, skyError, subMode);
    473580            } else if (sources) {
    474581                stamps = pmSubtractionStampsSetFromSources(sources, ro1->image, subMask, region, size,
    475                                                            footprint, stampSpacing, sysError, subMode);
     582                                                           footprint, stampSpacing, normFrac,
     583                                                           sysError, skyError, subMode);
    476584            }
    477585
    478586            // We get the stamps here; we will also attempt to get stamps at the first iteration, but it
    479587            // doesn't matter.
    480             if (!subtractionGetStamps(&stamps, ro1, ro2, subMask, variance, NULL, stampThresh1, stampThresh2,
    481                                       stampSpacing, sysError, size, footprint, subMode)) {
     588            if (!subtractionGetStamps(&stamps, ro1, ro2, subMask, variance, region, stampThresh1, stampThresh2,
     589                                      stampSpacing, normFrac, sysError, skyError, size, footprint, subMode)) {
    482590                goto MATCH_ERROR;
    483591            }
    484592
     593
     594            // generate the window function from the set of stamps
     595            if (!pmSubtractionStampsGetWindow(stamps, size)) {
     596                psError(PS_ERR_UNKNOWN, false, "Unable to get stamp window.");
     597                goto MATCH_ERROR;
     598            }
    485599
    486600            // Define kernel basis functions
    487601            if (optimum && (type == PM_SUBTRACTION_KERNEL_ISIS || type == PM_SUBTRACTION_KERNEL_GUNK)) {
    488                 kernels = pmSubtractionKernelsOptimumISIS(type, size, inner, spatialOrder, optFWHMs, optOrder,
    489                                                           stamps, footprint, optThreshold, penalty, subMode);
     602                kernels = pmSubtractionKernelsOptimumISIS(type, size, inner, spatialOrder,
     603                                                          optFWHMs, optOrder, stamps, footprint,
     604                                                          optThreshold, penalty, bounds, subMode);
    490605                if (!kernels) {
    491606                    psErrorClear();
     
    496611                // Not an ISIS/GUNK kernel, or the optimum kernel search failed
    497612                kernels = pmSubtractionKernelsGenerate(type, size, spatialOrder, isisWidths, isisOrders,
    498                                                        inner, binning, ringsOrder, penalty, subMode);
     613                                                       inner, binning, ringsOrder, penalty, bounds, subMode);
     614                // pmSubtractionVisualShowKernels(kernels);
    499615            }
    500616
     
    545661
    546662                if (!subtractionGetStamps(&stamps, ro1, ro2, subMask, variance, region,
    547                                           stampThresh1, stampThresh2, stampSpacing, sysError,
    548                                           size, footprint, subMode)) {
    549                     goto MATCH_ERROR;
    550                 }
    551 
    552                 psTrace("psModules.imcombine", 3, "Calculating equation...\n");
    553                 if (!pmSubtractionCalculateEquation(stamps, kernels)) {
     663                                          stampThresh1, stampThresh2, stampSpacing, normFrac,
     664                                          sysError, skyError, size, footprint, subMode)) {
     665                    goto MATCH_ERROR;
     666                }
     667
     668                // generate the window function from the set of stamps
     669                if (!pmSubtractionStampsGetWindow(stamps, size)) {
     670                    psError(PS_ERR_UNKNOWN, false, "Unable to get stamps window.");
     671                    goto MATCH_ERROR;
     672                }
     673
     674                // XXX step 1: calculate normalization
     675                psTrace("psModules.imcombine", 3, "Calculating equation for normalization...\n");
     676                if (!pmSubtractionCalculateEquation(stamps, kernels, SUBMODE)) {
    554677                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    555678                    goto MATCH_ERROR;
    556679                }
    557680
     681                psTrace("psModules.imcombine", 3, "Solving equation for normalization...\n");
     682                if (!pmSubtractionSolveEquation(kernels, stamps, SUBMODE)) {
     683                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     684                    goto MATCH_ERROR;
     685                }
     686                memCheck("  solve equation");
     687
     688# if (SEPARATE)
     689                // set USED -> CALCULATE
     690                pmSubtractionStampsResetStatus (stamps);
     691
     692                // XXX step 2: calculate kernel parameters
     693                psTrace("psModules.imcombine", 3, "Calculating equation for kernels...\n");
     694                if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
     695                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     696                    goto MATCH_ERROR;
     697                }
    558698                memCheck("  calculate equation");
    559699
    560                 psTrace("psModules.imcombine", 3, "Solving equation...\n");
    561 
    562                 if (!pmSubtractionSolveEquation(kernels, stamps)) {
     700                psTrace("psModules.imcombine", 3, "Solving equation for kernels...\n");
     701                if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
    563702                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    564703                    goto MATCH_ERROR;
    565704                }
    566 
    567705                memCheck("  solve equation");
    568 
     706# endif
    569707                psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    570708                if (!deviations) {
     
    587725            }
    588726
     727            // if we hit the max number of iterations and we have rejected stamps, re-solve
    589728            if (numRejected > 0) {
    590                 psTrace("psModules.imcombine", 3, "Solving equation...\n");
    591                 if (!pmSubtractionSolveEquation(kernels, stamps)) {
     729                // XXX step 1: calculate normalization
     730                psTrace("psModules.imcombine", 3, "Calculating equation for normalization...\n");
     731                if (!pmSubtractionCalculateEquation(stamps, kernels, SUBMODE)) {
    592732                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    593733                    goto MATCH_ERROR;
    594734                }
     735
     736                // solve normalization
     737                psTrace("psModules.imcombine", 3, "Solving equation for kernels...\n");
     738                if (!pmSubtractionSolveEquation(kernels, stamps, SUBMODE)) {
     739                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     740                    goto MATCH_ERROR;
     741                }
     742
     743# if (SEPARATE)
     744                // set USED -> CALCULATE
     745                pmSubtractionStampsResetStatus (stamps);
     746
     747                // XXX step 2: calculate kernel parameters
     748                psTrace("psModules.imcombine", 3, "Calculating equation for normalization...\n");
     749                if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
     750                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     751                    goto MATCH_ERROR;
     752                }
     753
     754                // solve kernel parameters
     755                psTrace("psModules.imcombine", 3, "Solving equation for kernels...\n");
     756                if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
     757                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     758                    goto MATCH_ERROR;
     759                }
     760                memCheck("  solve equation");
     761# endif
    595762                psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    596763                if (!deviations) {
     
    615782            psTrace("psModules.imcombine", 2, "Convolving...\n");
    616783            if (!pmSubtractionConvolve(conv1, conv2, ro1, ro2, subMask, stride, maskBad, maskPoor, poorFrac,
    617                                        kernelError, region, kernels, true, useFFT)) {
     784                                       kernelError, covarFrac, region, kernels, true, useFFT)) {
    618785                psError(PS_ERR_UNKNOWN, false, "Unable to convolve image.");
    619786                goto MATCH_ERROR;
     
    8971064    assert(kernels);
    8981065
    899     psTrace("psModules.imcombine", 3, "Calculating %s equation...\n", description);
    900     if (!pmSubtractionCalculateEquation(stamps, kernels)) {
     1066    psTrace("psModules.imcombine", 3, "Calculating %s normalization equation...\n", description);
     1067    if (!pmSubtractionCalculateEquation(stamps, kernels, SUBMODE)) {
    9011068        psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    9021069        return false;
    9031070    }
    9041071
    905     psTrace("psModules.imcombine", 3, "Solving %s equation...\n", description);
    906     if (!pmSubtractionSolveEquation(kernels, stamps)) {
     1072    psTrace("psModules.imcombine", 3, "Solving %s normalization equation...\n", description);
     1073    if (!pmSubtractionSolveEquation(kernels, stamps, SUBMODE)) {
    9071074        psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    9081075        return false;
    9091076    }
     1077
     1078# if (SEPARATE)
     1079    // set USED -> CALCULATE
     1080    pmSubtractionStampsResetStatus (stamps);
     1081
     1082    psTrace("psModules.imcombine", 3, "Calculating %s kernel coeffs equation...\n", description);
     1083    if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
     1084        psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     1085        return false;
     1086    }
     1087
     1088    psTrace("psModules.imcombine", 3, "Solving %s kernel coeffs equation...\n", description);
     1089    if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
     1090        psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     1091        return false;
     1092    }
     1093# endif
    9101094
    9111095    psTrace("psModules.imcombine", 3, "Calculate %s deviations...\n", description);
     
    9271111    if (numRejected > 0) {
    9281112        // Allow re-fit with reduced stamps set
    929         psTrace("psModules.imcombine", 3, "Resolving %s equation...\n", description);
    930         if (!pmSubtractionSolveEquation(kernels, stamps)) {
     1113        psTrace("psModules.imcombine", 3, "Calculating %s normalization equation...\n", description);
     1114        if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_ALL)) {
    9311115            psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    9321116            return false;
    9331117        }
     1118
     1119        psTrace("psModules.imcombine", 3, "Resolving %s equation...\n", description);
     1120        if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_ALL)) {
     1121            psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     1122            return false;
     1123        }
    9341124        psTrace("psModules.imcombine", 3, "Recalculate %s deviations...\n", description);
     1125
     1126# if (SEPARATE)
     1127        // set USED -> CALCULATE
     1128        pmSubtractionStampsResetStatus (stamps);
     1129
     1130        psTrace("psModules.imcombine", 3, "Calculating %s normalization equation...\n", description);
     1131        if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_KERNELS)) {
     1132            psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     1133            return false;
     1134        }
     1135
     1136        psTrace("psModules.imcombine", 3, "Resolving %s equation...\n", description);
     1137        if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_KERNELS)) {
     1138            psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     1139            return false;
     1140        }
     1141        psTrace("psModules.imcombine", 3, "Recalculate %s deviations...\n", description);
     1142# endif
     1143
    9351144        psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    9361145        if (!deviations) {
     
    10191228}
    10201229
     1230
     1231bool pmSubtractionParamsScale(int *kernelSize, int *stampSize, psVector *widths,
     1232                              float fwhm1, float fwhm2, float scaleRef, float scaleMin, float scaleMax)
     1233{
     1234    PS_ASSERT_PTR_NON_NULL(kernelSize, false);
     1235    PS_ASSERT_PTR_NON_NULL(stampSize, false);
     1236    PS_ASSERT_VECTOR_NON_NULL(widths, false);
     1237    PS_ASSERT_VECTOR_TYPE(widths, PS_TYPE_F32, false);
     1238    PS_ASSERT_FLOAT_LARGER_THAN(fwhm1, 0.0, false);
     1239    PS_ASSERT_FLOAT_LARGER_THAN(fwhm2, 0.0, false);
     1240    PS_ASSERT_FLOAT_LARGER_THAN(scaleRef, 0.0, false);
     1241    PS_ASSERT_FLOAT_LARGER_THAN(scaleMin, 0.0, false);
     1242    PS_ASSERT_FLOAT_LARGER_THAN(scaleMax, 0.0, false);
     1243    PS_ASSERT_FLOAT_LARGER_THAN(scaleMax, scaleMin, false);
     1244
     1245//    float diff = sqrtf(PS_SQR(PS_MAX(fwhm1, fwhm2)) - PS_SQR(PS_MIN(fwhm1, fwhm2))); // Difference
     1246    float scale = PS_MAX(fwhm1, fwhm2) / scaleRef;      // Scaling factor
     1247
     1248    if (isfinite(scaleMin) && scale < scaleMin) {
     1249        scale = scaleMin;
     1250    }
     1251    if (isfinite(scaleMax) && scale > scaleMax) {
     1252        scale = scaleMax;
     1253    }
     1254
     1255    for (int i = 0; i < widths->n; i++) {
     1256        widths->data.F32[i] *= scale;
     1257    }
     1258    *kernelSize = *kernelSize * scale + 0.5;
     1259    *stampSize = *stampSize * scale + 0.5;
     1260
     1261    psLogMsg("psModules.imcombine", PS_LOG_INFO,
     1262             "Scaling kernel parameters by %f: %d %d", scale, *kernelSize, *stampSize);
     1263
     1264    return true;
     1265}
  • trunk/psModules/src/imcombine/pmSubtractionMatch.h

    r26035 r26893  
    3939                        int iter,       ///< Rejection iterations
    4040                        float rej,      ///< Rejection threshold
     41                        float normFrac, ///< Fraction of flux in window for normalisation window
    4142                        float sysError, ///< Relative systematic error in images
     43                        float skyError, ///< Relative systematic error in images
    4244                        float kernelError, ///< Relative systematic error in kernel
     45                        float covarFrac,   ///< Fraction for kernel truncation before covariance calculation
    4346                        psImageMaskType maskVal, ///< Value to mask for input
    4447                        psImageMaskType maskBad, ///< Mask for output bad pixels
     
    5760                               int stride, ///< Size for convolution patches
    5861                               float kernelError, ///< Relative systematic error in kernel
     62                               float covarFrac,   ///< Fraction for kernel truncation before covariance calc.
    5963                               psImageMaskType maskVal, ///< Value to mask for input
    6064                               psImageMaskType maskBad, ///< Mask for output bad pixels
     
    9498    );
    9599
     100
     101/// Scale subtraction parameters according to the FWHMs of the inputs
     102bool pmSubtractionParamsScale(
     103    int *kernelSize,                    ///< Half-size of the kernel
     104    int *stampSize,                     ///< Half-size of the stamp (footprint)
     105    psVector *widths,                   ///< ISIS widths
     106    float fwhm1, float fwhm2,           ///< FWHMs for inputs
     107    float scaleRef,                     ///< Reference width for scaling
     108    float scaleMin,                     ///< Minimum scaling ratio, or NAN
     109    float scaleMax                      ///< Maximum scaling ratio, or NAN
     110    );
     111
     112
    96113#endif
  • trunk/psModules/src/imcombine/pmSubtractionParams.c

    r26035 r26893  
    204204                                                      int spatialOrder, const psVector *fwhms, int maxOrder,
    205205                                                      const pmSubtractionStampList *stamps, int footprint,
    206                                                       float tolerance, float penalty, pmSubtractionMode mode)
     206                                                      float tolerance, float penalty, psRegion bounds,
     207                                                      pmSubtractionMode mode)
    207208{
    208209    if (type != PM_SUBTRACTION_KERNEL_ISIS && type != PM_SUBTRACTION_KERNEL_GUNK) {
     
    232233    psVectorInit(orders, maxOrder);
    233234    pmSubtractionKernels *kernels = p_pmSubtractionKernelsRawISIS(size, spatialOrder, fwhms, orders,
    234                                                                   penalty, mode); // Kernels
     235                                                                  penalty, bounds, mode); // Kernels
    235236    psFree(orders);
    236237    psFree(kernels->description);
     
    482483    // Maintain photometric scaling
    483484    if (type == PM_SUBTRACTION_KERNEL_ISIS) {
    484         psKernel *subtract = kernels->preCalc->data[0]; // Kernel to subtract from the rest
     485
     486        // XXX in r26035, this code was just wrong.  we had:
     487
     488        // psKernel *subtract = kernels->preCalc->data[0]
     489
     490        // but, kernels->preCalc was an array of psArray, not an array of kernels.  It is now
     491        // an array of pmSubtractionKernelPreCalc.
     492
     493        pmSubtractionKernelPreCalc *subtract = kernels->preCalc->data[0]; // Kernel to subtract from the rest
     494
    485495        for (int i = 1; i < newSize; i++) {
    486496            if (kernels->u->data.S32[i] % 2 == 0 && kernels->v->data.S32[i] % 2 == 0) {
    487                 psKernel *kernel = kernels->preCalc->data[i]; // Kernel of interest
    488                 psBinaryOp(kernel->image, kernel->image, "-", subtract->image);
     497                pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[i]; // Kernel of interest
     498                psBinaryOp(preCalc->kernel->image, preCalc->kernel->image, "-", subtract->kernel->image);
    489499            }
    490500        }
     
    495505        for (int i = 0; i < newSize; i++) {
    496506            if (kernels->u->data.S32[i] % 2 == 0 && kernels->v->data.S32[i] % 2 == 0) {
    497                 psKernel *kernel = kernels->preCalc->data[i]; // Kernel of interest
    498                 kernel->kernel[0][0] -= 1.0;
     507                pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[i]; // Kernel of interest
     508                preCalc->kernel->kernel[0][0] -= 1.0;
    499509            }
    500510        }
  • trunk/psModules/src/imcombine/pmSubtractionParams.h

    r18287 r26893  
    1717                                                      float tolerance, ///< Maximum difference in chi^2
    1818                                                      float penalty, ///< Penalty for wideness
    19                                                       pmSubtractionMode mode // Mode for subtraction
     19                                                      psRegion bounds,       ///< Bounds of validity
     20                                                      pmSubtractionMode mode ///< Mode for subtraction
    2021    );
    2122
  • trunk/psModules/src/imcombine/pmSubtractionStamps.c

    r26046 r26893  
    4646    psFree(list->y);
    4747    psFree(list->flux);
     48    psFree(list->window);
    4849}
    4950
     
    5758    psFree(stamp->convolutions1);
    5859    psFree(stamp->convolutions2);
    59 
    60     psFree(stamp->matrix1);
    61     psFree(stamp->matrix2);
    62     psFree(stamp->matrixX);
    63     psFree(stamp->vector1);
    64     psFree(stamp->vector2);
    65 
     60    psFree(stamp->matrix);
     61    psFree(stamp->vector);
    6662}
    6763
     
    8076
    8177// Search a region for a suitable stamp
    82 bool stampSearch(int *xStamp, int *yStamp, // Coordinates of stamp, to return
     78bool stampSearch(float *xStamp, float *yStamp, // Coordinates of stamp, to return
    8379                 float *fluxStamp, // Flux of stamp, to return
    8480                 const psImage *image1, const psImage *image2, // Images to search
     
    9389    *fluxStamp = -INFINITY;             // Flux of best stamp
    9490
     91    // fprintf (stderr, "xMin, xMax: %d, %d -> ", xMin, xMax);
     92
    9593    // Ensure we're not going to go outside the bounds of the image
    9694    xMin = PS_MAX(border, xMin);
     
    9997    yMax = PS_MIN(numRows - border - 1, yMax);
    10098
     99    if (xMax < xMin) return false;
     100    if (yMax < yMin) return false;
     101
     102    psAssert (xMin <= xMax, "x mismatch?");
     103    psAssert (yMin <= yMax, "y mismatch?");
     104
    101105    for (int y = yMin; y <= yMax; y++) {
    102106        for (int x = xMin; x <= xMax; x++) {
     
    115119                ((image1) ? image1->data.F32[y][x] : image2->data.F32[y][x]); // Flux at pixel
    116120            if (flux > *fluxStamp) {
    117                 *xStamp = x;
    118                 *yStamp = y;
     121                *xStamp = x + 0.5;
     122                *yStamp = y + 0.5;
    119123                *fluxStamp = flux;
    120124                found = true;
     
    180184
    181185pmSubtractionStampList *pmSubtractionStampListAlloc(int numCols, int numRows, const psRegion *region,
    182                                                     int footprint, float spacing, float sysErr)
     186                                                    int footprint, float spacing, float normFrac,
     187                                                    float sysErr, float skyErr)
    183188{
    184189    pmSubtractionStampList *list = psAlloc(sizeof(pmSubtractionStampList)); // Stamp list to return
     
    220225    list->y = NULL;
    221226    list->flux = NULL;
     227    list->window = NULL;
     228    list->normFrac = normFrac;
     229    list->normWindow = 0;
    222230    list->footprint = footprint;
    223231    list->sysErr = sysErr;
     232    list->skyErr = skyErr;
    224233
    225234    return list;
     
    239248    out->y = NULL;
    240249    out->flux = NULL;
     250    out->window = psMemIncrRefCounter(in->window);
    241251    out->footprint = in->footprint;
     252    out->normWindow = in->normWindow;
    242253
    243254    for (int i = 0; i < num; i++) {
     
    280291        }
    281292
    282         outStamp->matrix1 = inStamp->matrix1 ? psImageCopy(NULL, inStamp->matrix1, PS_TYPE_F64) : NULL;
    283         outStamp->matrix2 = inStamp->matrix2 ? psImageCopy(NULL, inStamp->matrix2, PS_TYPE_F64) : NULL;
    284         outStamp->matrixX = inStamp->matrixX ? psImageCopy(NULL, inStamp->matrixX, PS_TYPE_F64) : NULL;
    285         outStamp->vector1 = inStamp->vector1 ? psVectorCopy(NULL, inStamp->vector1, PS_TYPE_F64) : NULL;
    286         outStamp->vector2 = inStamp->vector2 ? psVectorCopy(NULL, inStamp->vector2, PS_TYPE_F64) : NULL;
     293        outStamp->matrix = inStamp->matrix ? psImageCopy(NULL, inStamp->matrix, PS_TYPE_F64) : NULL;
     294        outStamp->vector = inStamp->vector ? psVectorCopy(NULL, inStamp->vector, PS_TYPE_F64) : NULL;
    287295
    288296        out->stamps->data[i] = outStamp;
     
    310318    stamp->convolutions2 = NULL;
    311319
    312     stamp->matrix1 = NULL;
    313     stamp->matrix2 = NULL;
    314     stamp->matrixX = NULL;
    315     stamp->vector1 = NULL;
    316     stamp->vector2 = NULL;
     320    stamp->matrix = NULL;
     321    stamp->vector = NULL;
     322    stamp->norm = NAN;
    317323
    318324    return stamp;
     
    323329                                                const psImage *image2, const psImage *subMask,
    324330                                                const psRegion *region, float thresh1, float thresh2,
    325                                                 int size, int footprint, float spacing, float sysErr,
    326                                                 pmSubtractionMode mode)
     331                                                int size, int footprint, float spacing, float normFrac,
     332                                                float sysErr, float skyErr, pmSubtractionMode mode)
    327333{
    328334    if (!image1 && !image2) {
     
    380386
    381387    if (!stamps) {
    382         stamps = pmSubtractionStampListAlloc(numCols, numRows, region, footprint, spacing, sysErr);
     388        stamps = pmSubtractionStampListAlloc(numCols, numRows, region, footprint, spacing,
     389                                             normFrac, sysErr, skyErr);
    383390    }
    384391
     
    402409            numSearch++;
    403410
    404             int xStamp = 0, yStamp = 0; // Coordinates of stamp
     411            float xStamp = 0.0, yStamp = 0.0; // Coordinates of stamp
    405412            float fluxStamp = -INFINITY; // Flux of stamp
    406413            bool goodStamp = false;     // Found a good stamp?
     
    414421                // Take stamps off the top of the (sorted) list
    415422                for (int j = xList->n - 1; j >= 0 && !goodStamp; j--) {
    416                     int xCentre = xList->data.F32[j] + 0.5, yCentre = yList->data.F32[j] + 0.5;// Stamp centre
    417 
    418423                    // Chop off the top of the list
    419424                    xList->n = j;
     
    421426                    fluxList->n = j;
    422427
     428#if 0
    423429                    // Fish around a bit to see if we can find a pixel that isn't masked
     430                    // This is not a good idea if we're using the window feature
    424431                    psTrace("psModules.imcombine", 7, "Searching for stamp %d around %d,%d\n",
    425432                            i, xCentre, yCentre);
    426433
    427434                    // Search bounds
     435                    int xCentre = xList->data.F32[j] - 0.5, yCentre = yList->data.F32[j] - 0.5;// Stamp centre
    428436                    int search = footprint - size; // Search radius
    429437                    int xMin = PS_MAX(border, xCentre - search);
     
    434442                    goodStamp = stampSearch(&xStamp, &yStamp, &fluxStamp, image1, image2, thresh1, thresh2,
    435443                                            subMask, xMin, xMax, yMin, yMax, numCols, numRows, border);
     444                    // fprintf (stderr, "find: %d %d ==> %5.1f %5.1f (\n", xCentre, yCentre, xStamp, yStamp);
     445#else
     446                    // Only search the exact centre pixel
     447                    goodStamp = stampSearch(&xStamp, &yStamp, &fluxStamp, image1, image2, thresh1, thresh2,
     448                                            subMask, xList->data.F32[j], xList->data.F32[j],
     449                                            yList->data.F32[j], yList->data.F32[j], numCols, numRows, border);
     450#endif
     451                    if (0 && goodStamp) {
     452                        fprintf (stderr, "find: %6.1f < %6.1f < %6.1f, %6.1f < %6.1f %6.1f\n",
     453                                 region->x0 + size, xStamp, region->x1 - size,
     454                                 region->y0 + size, yStamp, region->y1 - size);
     455                    }
    436456                }
    437457            } else {
     
    488508                                               const psImage *image, const psImage *subMask,
    489509                                               const psRegion *region, int size, int footprint,
    490                                                float spacing, float sysErr, pmSubtractionMode mode)
     510                                               float spacing, float normFrac, float sysErr, float skyErr,
     511                                               pmSubtractionMode mode)
    491512
    492513{
     
    509530    pmSubtractionStampList *stamps = pmSubtractionStampListAlloc(subMask->numCols, subMask->numRows,
    510531                                                                 region, footprint, spacing,
    511                                                                  sysErr); // Stamp list
     532                                                                 normFrac, sysErr, skyErr); // Stamp list
    512533    int numStamps = stamps->num;        // Number of stamps
    513534
     
    531552    for (int i = 0; i < numStars; i++) {
    532553        float xStamp = x->data.F32[i], yStamp = y->data.F32[i]; // Coordinates of stamp
    533         int xPix = xStamp + 0.5, yPix = yStamp + 0.5; // Pixel coordinate of stamp
     554        int xPix = xStamp - 0.5, yPix = yStamp - 0.5; // Pixel coordinate of stamp
    534555        if (!checkStampRegion(xPix, yPix, region)) {
    535556            // It's not in the big region
     
    539560            continue;
    540561        }
     562
     563        // fprintf (stderr, "stamp: %5.1f %5.1f == %d %d\n", xStamp, yStamp, xPix, yPix);
    541564
    542565        bool found = false;
     
    607630
    608631
     632bool pmSubtractionStampsGetWindow(pmSubtractionStampList *stamps, int kernelSize)
     633{
     634    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     635    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
     636
     637    int size = stamps->footprint; // Size of postage stamps
     638
     639    psFree (stamps->window);
     640    stamps->window = psKernelAlloc(-size, size, -size, size);
     641    psImageInit(stamps->window->image, 0.0);
     642
     643    // generate normalizations for each stamp
     644    psVector *norm1 = psVectorAlloc(stamps->num, PS_TYPE_F32);
     645    psVector *norm2 = psVectorAlloc(stamps->num, PS_TYPE_F32);
     646    for (int i = 0; i < stamps->num; i++) {
     647        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     648        if (!stamp) continue;
     649        if (!stamp->image1) continue;
     650        if (!stamp->image2) continue;
     651
     652        float sum1 = 0.0;
     653        float sum2 = 0.0;
     654        for (int y = -size; y <= size; y++) {
     655            for (int x = -size; x <= size; x++) {
     656                sum1 += stamp->image1->kernel[y][x];
     657                sum2 += stamp->image2->kernel[y][x];
     658            }
     659        }
     660        norm1->data.F32[i] = sum1;
     661        norm2->data.F32[i] = sum2;
     662    }
     663
     664    // storage vector for flux data
     665    psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
     666    psVector *flux1 = psVectorAllocEmpty(2*stamps->num, PS_TYPE_F32);
     667    psVector *flux2 = psVectorAllocEmpty(2*stamps->num, PS_TYPE_F32);
     668
     669    // generate the window pixels
     670    double sum = 0.0;                   // Sum inside the window
     671    float maxValue = 0.0;               // Maximum value, for normalisation
     672    for (int y = -size; y <= size; y++) {
     673        for (int x = -size; x <= size; x++) {
     674
     675            flux1->n = 0;
     676            flux2->n = 0;
     677            for (int i = 0; i < stamps->num; i++) {
     678                pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     679                if (!stamp) continue;
     680                if (!stamp->image1) continue;
     681                if (!stamp->image2) continue;
     682
     683                psVectorAppend(flux1, stamp->image1->kernel[y][x] / norm1->data.F32[i]);
     684                psVectorAppend(flux2, stamp->image2->kernel[y][x] / norm2->data.F32[i]);
     685            }
     686
     687            psStatsInit (stats);
     688            if (!psVectorStats (stats, flux1, NULL, NULL, 0)) {
     689                psAbort ("failed to generate stats");
     690            }
     691            float f1 = stats->robustMedian;
     692            psStatsInit (stats);
     693            if (!psVectorStats (stats, flux2, NULL, NULL, 0)) {
     694                psAbort ("failed to generate stats");
     695            }
     696            float f2 = stats->robustMedian;
     697
     698            stamps->window->kernel[y][x] = f1 + f2;
     699            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(size)) {
     700                sum += stamps->window->kernel[y][x];
     701            }
     702            maxValue = PS_MAX(maxValue, stamps->window->kernel[y][x]);
     703        }
     704    }
     705
     706    psTrace("psModules.imcombine", 3, "Window total: %f, threshold: %f\n",
     707            sum, (1.0 - stamps->normFrac) * sum);
     708    bool done = false;
     709    for (int radius = 1; radius <= size && !done; radius++) {
     710        double within = 0.0;
     711        for (int y = -radius; y <= radius; y++) {
     712            for (int x = -radius; x <= radius; x++) {
     713                if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(radius)) {
     714                    within += stamps->window->kernel[y][x];
     715                }
     716            }
     717        }
     718        psTrace("psModules.imcombine", 5, "Radius %d: %f\n", radius, within);
     719        if (within > (1.0 - stamps->normFrac) * sum) {
     720            stamps->normWindow = radius;
     721            done = true;
     722        }
     723    }
     724
     725    psTrace("psModules.imcombine", 3, "Normalisation window radius set to %d\n", stamps->normWindow);
     726    if (stamps->normWindow == 0 || stamps->normWindow >= size) {
     727        psError(PS_ERR_UNKNOWN, true, "Unable to determine normalisation window size.");
     728        return false;
     729    }
     730
     731    // re-normalize so chisquare values are sensible
     732    for (int y = -size; y <= size; y++) {
     733        for (int x = -size; x <= size; x++) {
     734            stamps->window->kernel[y][x] /= maxValue;
     735        }
     736    }
     737
     738#if 0
     739    {
     740        psFits *fits = psFitsOpen ("window.fits", "w");
     741        psFitsWriteImage (fits, NULL, stamps->window->image, 0, NULL);
     742        psFitsClose (fits);
     743    }
     744#endif
     745
     746    psFree (stats);
     747    psFree (flux1);
     748    psFree(flux2);
     749    psFree (norm1);
     750    psFree (norm2);
     751    return true;
     752}
     753
    609754bool pmSubtractionStampsExtract(pmSubtractionStampList *stamps, psImage *image1, psImage *image2,
    610                                 psImage *variance, int kernelSize)
     755                                psImage *variance, int kernelSize, psRegion bounds)
    611756{
    612757    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    625770    }
    626771
    627     int numCols = image1->numCols, numRows = image1->numRows; // Size of images
    628772    int size = kernelSize + stamps->footprint; // Size of postage stamps
    629773
     
    634778        }
    635779
    636         if (isnan(stamp->xNorm)) {
    637             stamp->xNorm = 2.0 * (stamp->x - (float)numCols/2.0) / (float)numCols;
    638         }
    639         if (isnan(stamp->yNorm)) {
    640             stamp->yNorm = 2.0 * (stamp->y - (float)numRows/2.0) / (float)numRows;
    641         }
    642 
    643         int x = stamp->x + 0.5, y = stamp->y + 0.5; // Stamp coordinates
    644         if (x < size || x > numCols - size || y < size || y > numRows - size) {
    645             psError(PS_ERR_UNKNOWN, false, "Stamp %d (%d,%d) is within the image border.\n", i, x, y);
     780        p_pmSubtractionPolynomialNormCoords(&stamp->xNorm, &stamp->yNorm, stamp->x, stamp->y,
     781                                            bounds.x0, bounds.x1, bounds.y0, bounds.y1);
     782
     783        int x = stamp->x - 0.5, y = stamp->y - 0.5; // Stamp coordinates
     784        // fprintf (stderr, "stamp: %5.1f %5.1f == %d %d (size: %d)\n", stamp->x, stamp->y, x, y, size);
     785
     786        if (x < bounds.x0 + size || x > bounds.x1 - size || y < bounds.y0 + size || y > bounds.y1 - size) {
     787            psError(PS_ERR_UNKNOWN, false, "Stamp %d (%d,%d) is within the region border.\n", i, x, y);
    646788            return false;
    647789        }
     
    668810            psImage *varSub = psImageSubset(variance, region); // Subimage with stamp
    669811            psKernel *var = psKernelAllocFromImage(varSub, size, size); // Variance postage stamp
    670             if (isfinite(stamps->sysErr) && stamps->sysErr > 0) {
     812
     813            if ((isfinite(stamps->skyErr) && (stamps->skyErr > 0)) ||
     814                (isfinite(stamps->sysErr) && (stamps->sysErr > 0))) {
    671815                float sysErr = 0.25 * PS_SQR(stamps->sysErr); // Systematic error
     816                float skyErr = stamps->skyErr;
    672817                psKernel *image1 = stamp->image1, *image2 = stamp->image2; // Input images
    673818                for (int y = -size; y <= size; y++) {
    674819                    for (int x = -size; x <= size; x++) {
    675820                        float additional = image1->kernel[y][x] + image2->kernel[y][x];
    676                         weight->kernel[y][x] = 1.0 / (var->kernel[y][x] + sysErr * PS_SQR(additional));
     821                        weight->kernel[y][x] = 1.0 / (skyErr + var->kernel[y][x] + sysErr * PS_SQR(additional));
    677822                    }
    678823                }
     
    698843pmSubtractionStampList *pmSubtractionStampsSetFromSources(const psArray *sources, const psImage *image,
    699844                                                          const psImage *subMask, const psRegion *region,
    700                                                           int size, int footprint, float spacing, float sysErr,
     845                                                          int size, int footprint, float spacing,
     846                                                          float normFrac, float sysErr, float skyErr,
    701847                                                          pmSubtractionMode mode)
    702848{
     
    722868            y->data.F32[numOut] = source->peak->yf;
    723869        }
     870        // fprintf (stderr, "stamp: %5.1f %5.1f\n", x->data.F32[numOut], y->data.F32[numOut]);
    724871        numOut++;
    725872    }
     
    728875
    729876    pmSubtractionStampList *stamps = pmSubtractionStampsSet(x, y, image, subMask, region, size,
    730                                                             footprint, spacing, sysErr, mode); // Stamps
     877                                                            footprint, spacing, normFrac,
     878                                                            sysErr, skyErr, mode); // Stamps
    731879    psFree(x);
    732880    psFree(y);
     
    742890pmSubtractionStampList *pmSubtractionStampsSetFromFile(const char *filename, const psImage *image,
    743891                                                       const psImage *subMask, const psRegion *region,
    744                                                        int size, int footprint, float spacing, float sysErr,
    745                                                        pmSubtractionMode mode)
     892                                                       int size, int footprint, float spacing, float normFrac,
     893                                                       float sysErr, float skyErr, pmSubtractionMode mode)
    746894{
    747895    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
     
    760908
    761909    pmSubtractionStampList *stamps = pmSubtractionStampsSet(x, y, image, subMask, region, size, footprint,
    762                                                             spacing, sysErr, mode);
     910                                                            spacing, normFrac, sysErr, skyErr, mode);
    763911    psFree(data);
    764912
     
    766914
    767915}
     916
     917
     918bool pmSubtractionStampsResetStatus (pmSubtractionStampList *stamps) {
     919
     920    for (int i = 0; i < stamps->num; i++) {
     921        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     922        if (!stamp) continue;
     923        if (stamp->status != PM_SUBTRACTION_STAMP_USED) continue;
     924        stamp->status = PM_SUBTRACTION_STAMP_CALCULATE;
     925    }
     926    return true;
     927}
     928
  • trunk/psModules/src/imcombine/pmSubtractionStamps.h

    r26035 r26893  
    2424    psArray *flux;                      ///< Fluxes for possible stamps (or NULL)
    2525    int footprint;                      ///< Half-size of stamps
     26    psKernel *window;                   ///< window function generated from ensemble of stamps
     27    float normFrac;                     ///< Fraction of flux in window for normalisation window
     28    int normWindow;                     ///< Size of window for measuring normalisation
    2629    float sysErr;                       ///< Systematic error
     30    float skyErr;                       ///< increase effective readnoise
    2731} pmSubtractionStampList;
    2832
    2933/// Allocate a list of stamps
    30 pmSubtractionStampList *pmSubtractionStampListAlloc(int numCols, // Number of columns in image
    31                                                     int numRows, // Number of rows in image
    32                                                     const psRegion *region, // Region for stamps, or NULL
    33                                                     int footprint, // Half-size of stamps
    34                                                     float spacing, // Rough average spacing between stamps
    35                                                     float sysErr  // Relative systematic error or NAN
     34pmSubtractionStampList *pmSubtractionStampListAlloc(
     35    int numCols, // Number of columns in image
     36    int numRows, // Number of rows in image
     37    const psRegion *region, // Region for stamps, or NULL
     38    int footprint, // Half-size of stamps
     39    float spacing, // Rough average spacing between stamps
     40    float normFrac, // Fraction of flux in window for normalisation window
     41    float sysErr,  // Relative systematic error or NAN
     42    float skyErr  // Relative systematic error or NAN
    3643    );
    3744
     
    7380    psArray *convolutions1;             ///< Convolutions of image 1 for each kernel component, or NULL
    7481    psArray *convolutions2;             ///< Convolutions of image 2 for each kernel component, or NULL
    75     psImage *matrix1, *matrix2;         ///< Least-squares matrices for each image, or NULL
    76     psImage *matrixX;                   ///< Cross-matrix (for mode DUAL), or NULL
    77     psVector *vector1, *vector2;        ///< Least-squares vectors for each image, or NULL
     82    psImage *matrix;                    ///< Least-squares matrix, or NULL
     83    psVector *vector;                   ///< Least-squares vector, or NULL
     84    double norm;                        ///< Normalisation difference
    7885    pmSubtractionStampStatus status;    ///< Status of stamp
    7986} pmSubtractionStamp;
     
    8390
    8491/// Find stamps on an image
    85 pmSubtractionStampList *pmSubtractionStampsFind(pmSubtractionStampList *stamps, ///< Output stamps, or NULL
    86                                                 const psImage *image1, ///< Image for which to find stamps
    87                                                 const psImage *image2, ///< Image for which to find stamps
    88                                                 const psImage *mask, ///< Mask, or NULL
    89                                                 const psRegion *region, ///< Region to search, or NULL
    90                                                 float thresh1, ///< Threshold for stamps in image 1
    91                                                 float thresh2, ///< Threshold for stamps in image 2
    92                                                 int size, ///< Kernel half-size
    93                                                 int footprint, ///< Half-size for stamps
    94                                                 float spacing, ///< Rough spacing for stamps
    95                                                 float sysErr,  ///< Relative systematic error in images
    96                                                 pmSubtractionMode mode ///< Mode for subtraction
     92pmSubtractionStampList *pmSubtractionStampsFind(
     93    pmSubtractionStampList *stamps, ///< Output stamps, or NULL
     94    const psImage *image1, ///< Image for which to find stamps
     95    const psImage *image2, ///< Image for which to find stamps
     96    const psImage *mask, ///< Mask, or NULL
     97    const psRegion *region, ///< Region to search, or NULL
     98    float thresh1, ///< Threshold for stamps in image 1
     99    float thresh2, ///< Threshold for stamps in image 2
     100    int size, ///< Kernel half-size
     101    int footprint, ///< Half-size for stamps
     102    float spacing, ///< Rough spacing for stamps
     103    float normFrac, // Fraction of flux in window for normalisation window
     104    float sysErr,  ///< Relative systematic error in images
     105    float skyErr,  ///< Relative systematic error in images
     106    pmSubtractionMode mode ///< Mode for subtraction
    97107    );
    98108
    99109/// Set stamps based on a list of x,y
    100 pmSubtractionStampList *pmSubtractionStampsSet(const psVector *x, ///< x coordinates for each stamp
    101                                                const psVector *y, ///< y coordinates for each stamp
    102                                                const psImage *image, ///< Image for flux of stamp
    103                                                const psImage *mask, ///< Mask, or NULL
    104                                                const psRegion *region, ///< Region to search, or NULL
    105                                                int size, ///< Kernel half-size
    106                                                int footprint, ///< Half-size for stamps
    107                                                float spacing, ///< Rough spacing for stamps
    108                                                float sysErr,  ///< Systematic error in images
    109                                                pmSubtractionMode mode ///< Mode for subtraction
     110pmSubtractionStampList *pmSubtractionStampsSet(
     111    const psVector *x, ///< x coordinates for each stamp
     112    const psVector *y, ///< y coordinates for each stamp
     113    const psImage *image, ///< Image for flux of stamp
     114    const psImage *mask, ///< Mask, or NULL
     115    const psRegion *region, ///< Region to search, or NULL
     116    int size, ///< Kernel half-size
     117    int footprint, ///< Half-size for stamps
     118    float spacing, ///< Rough spacing for stamps
     119    float normFrac, // Fraction of flux in window for normalisation window
     120    float sysErr,  ///< Systematic error in images
     121    float skyErr,  ///< Systematic error in images
     122    pmSubtractionMode mode ///< Mode for subtraction
    110123    );
    111124
     
    119132    int footprint,                      ///< Half-size for stamps
    120133    float spacing,                      ///< Rough spacing for stamps
     134    float normFrac, // Fraction of flux in window for normalisation window
    121135    float sysErr,                       ///< Systematic error in images
     136    float skyErr,                       ///< Systematic error in images
    122137    pmSubtractionMode mode              ///< Mode for subtraction
    123138    );
     
    132147    int footprint,                      ///< Half-size for stamps
    133148    float spacing,                      ///< Rough spacing for stamps
     149    float normFrac, // Fraction of flux in window for normalisation window
    134150    float sysErr,                       ///< Systematic error in images
     151    float skyErr,                       ///< Systematic error in images
    135152    pmSubtractionMode mode              ///< Mode for subtraction
     153    );
     154
     155/// Calculate the window and normalisation window from the stamps
     156bool pmSubtractionStampsGetWindow(
     157    pmSubtractionStampList *stamps,     ///< List of stamps
     158    int kernelSize                      ///< Half-size of kernel
    136159    );
    137160
     
    141164                                psImage *image2, ///< Input image (or NULL)
    142165                                psImage *variance, ///< Variance map
    143                                 int kernelSize ///< Kernel half-size
     166                                int kernelSize, ///< Kernel half-size
     167                                psRegion bounds ///< Bounds of validity
    144168    );
    145169
     
    168192    );
    169193
     194
     195bool pmSubtractionStampsResetStatus (pmSubtractionStampList *stamps);
     196
    170197#endif
  • trunk/psModules/src/imcombine/pmSubtractionThreads.c

    r21363 r26893  
    6969
    7070    {
    71         psThreadTask *task = psThreadTaskAlloc("PSMODULES_SUBTRACTION_CALCULATE_EQUATION", 3);
     71        psThreadTask *task = psThreadTaskAlloc("PSMODULES_SUBTRACTION_CALCULATE_EQUATION", 4);
    7272        task->function = &pmSubtractionCalculateEquationThread;
    7373        psThreadTaskAdd(task);
  • trunk/psModules/src/imcombine/pmSubtractionVisual.c

    r23487 r26893  
    1616
    1717#include "pmKapaPlots.h"
     18#include "pmSubtraction.h"
    1819#include "pmSubtractionStamps.h"
     20#include "pmSubtractionEquation.h"
    1921
    2022#include "pmVisual.h"
     
    3234static bool plotLeastSquares     = true;
    3335static bool plotImage            = true;
     36
    3437// variables to store plotting window indices
    35 static int kapa  = -1;
     38static int kapa1 = -1;
    3639static int kapa2 = -1;
     40static int kapa3 = -1;
    3741
    3842/** function prototypes*/
     
    4650bool pmSubtractionVisualClose(void)
    4751{
    48     if(kapa != -1)
    49         KiiClose(kapa);
    50     if(kapa2 != -1)
    51         KiiClose(kapa2);
     52    if(kapa1 != -1) KiiClose(kapa1);
     53    if(kapa2 != -1) KiiClose(kapa2);
    5254    return true;
    5355}
     
    6062bool pmSubtractionVisualPlotConvKernels(psImage *convKernels) {
    6163    if (!pmVisualIsVisual() || !plotConvKernels) return true;
    62     if (!pmVisualInitWindow(&kapa, "ppSub:Images")) {
    63         return false;
    64     }
    65     pmVisualScaleImage(kapa, convKernels, "Convolution_Kernels", 0, true);
     64    if (!pmVisualInitWindow(&kapa1, "ppSub:Images")) {
     65        return false;
     66    }
     67    pmVisualScaleImage(kapa1, convKernels, "Convolution_Kernels", 0, true);
    6668    pmVisualAskUser(&plotConvKernels);
    6769    return true;
     
    7476bool pmSubtractionVisualPlotStamps(pmSubtractionStampList *stamps, pmReadout *ro) {
    7577    if (!pmVisualIsVisual() || !plotStamps) return true;
    76     if (!pmVisualInitWindow (&kapa, "ppSub:Images")) {
     78    if (!pmVisualInitWindow (&kapa1, "ppSub:Images")) {
    7779        return false;
    7880    }
     
    134136        stampNum++;
    135137    }
    136     pmVisualScaleImage(kapa, canvas, "Subtraction_Stamps", 0, true);
     138    pmVisualScaleImage(kapa1, canvas, "Subtraction_Stamps", 0, true);
    137139
    138140    pmVisualAskUser(&plotStamps);
     
    144146
    145147    if (!pmVisualIsVisual() || !plotLeastSquares) return true;
    146     if (!pmVisualInitWindow (&kapa, "PPSub:Images")) {
     148    if (!pmVisualInitWindow (&kapa1, "PPSub:Images")) {
    147149        return false;
    148150    }
     
    157159        if (stamp == NULL) continue;
    158160
    159         psImage *im = stamp->matrix1;
    160         if (im == NULL) im = stamp->matrix2;
    161         if (im == NULL) im = stamp->matrixX;
     161        psImage *im = stamp->matrix;
    162162        if (im == NULL) continue;
    163163
     
    187187        if (stamp == NULL) continue;
    188188
    189         psImage *im = stamp->matrix1;
    190         if (im == NULL) im = stamp->matrix2;
    191         if (im == NULL) im = stamp->matrixX;
     189        psImage *im = stamp->matrix;
    192190        if (im == NULL) continue;
    193191
     
    197195
    198196    psImage *canvas32 = pmVisualImageToFloat(canvas);
    199     pmVisualScaleImage(kapa, canvas32, "Least_Squares", 0, true);
     197    pmVisualScaleImage(kapa1, canvas32, "Least_Squares", 0, true);
    200198
    201199    pmVisualAskUser(&plotLeastSquares);
     
    207205bool pmSubtractionVisualShowSubtraction(psImage *image, psImage *ref, psImage *sub) {
    208206    if (!pmVisualIsVisual() || !plotImage) return true;
    209     if (!pmVisualInitWindow (&kapa, "PPSub:Images")) {
    210         return false;
    211     }
    212 
    213     pmVisualScaleImage(kapa, image, "Image", 0, true);
    214     pmVisualScaleImage(kapa, ref, "Reference", 1, true);
    215     pmVisualScaleImage(kapa, sub, "Subtraction", 2, true);
     207    if (!pmVisualInitWindow (&kapa1, "PPSub:Images")) {
     208        return false;
     209    }
     210
     211    pmVisualScaleImage(kapa1, image, "Image", 0, true);
     212    pmVisualScaleImage(kapa1, ref, "Reference", 1, true);
     213    pmVisualScaleImage(kapa1, sub, "Subtraction", 2, true);
     214    pmVisualAskUser(&plotImage);
     215    return true;
     216}
     217
     218bool pmSubtractionVisualShowKernels(pmSubtractionKernels *kernels) {
     219
     220    if (!pmVisualIsVisual()) return true;
     221    if (!pmVisualInitWindow (&kapa1, "PPSub:Images")) {
     222        return false;
     223    }
     224
     225    // get the kernel sizes
     226    int footprint = kernels->size;
     227
     228    // output image is a grid of NXsub by NYsub sub-images
     229    int NXsub = sqrt(kernels->num);
     230    int NYsub = kernels->num / NXsub;
     231    if (kernels->num % NXsub) NYsub++;
     232
     233    int NXpix = NXsub * (2*footprint + 1 + 3);
     234    int NYpix = NYsub * (2*footprint + 1 + 3);
     235
     236    psImage *output = psImageAlloc(NXpix, NYpix, PS_TYPE_F32);
     237    psImageInit (output, 0.0);
     238
     239    for (int i = 0; i < kernels->num; i++) {
     240        pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[i];
     241        psKernel *kernel = preCalc->kernel;
     242
     243        int xSub = i % NXsub;
     244        int ySub = i / NXsub;
     245
     246        int xPix = xSub * (2*footprint + 1 + 3) + footprint;
     247        int yPix = ySub * (2*footprint + 1 + 3) + footprint;
     248
     249        double sum = 0.0;
     250        for (int y = -footprint; y <= footprint; y++) {
     251            for (int x = -footprint; x <= footprint; x++) {
     252                output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x];
     253                sum += kernel->kernel[y][x];
     254            }
     255        }
     256        fprintf (stderr, "kernel %d, sum %f\n", i, sum);
     257    }                                                   
     258       
     259    pmVisualScaleImage(kapa1, output, "Image", 0, true);
     260    pmVisualAskUser(&plotImage);
     261    return true;
     262}
     263
     264bool pmSubtractionVisualShowBasis(pmSubtractionStampList *stamps) {
     265
     266    if (!pmVisualIsVisual()) return true;
     267    if (!pmVisualInitWindow (&kapa2, "ppSub:StampMasterImage")) {
     268        return false;
     269    }
     270
     271    // get the kernel sizes
     272    int footprint = stamps->footprint;
     273
     274    // choose the brightest stamp
     275    pmSubtractionStamp *maxStamp = NULL;
     276    float maxFlux = NAN;
     277    for (int i = 0; i < stamps->num; i++) {
     278        pmSubtractionStamp *stamp = stamps->stamps->data[i];
     279        if (!isfinite(stamp->flux)) continue;
     280        if (!stamp->convolutions1 && !stamp->convolutions2) continue;
     281        if (!maxStamp) {
     282            maxFlux = stamp->flux;
     283            maxStamp = stamp;
     284            continue;
     285        }
     286        if (stamp->flux > maxFlux) {
     287            maxFlux = stamp->flux;
     288            maxStamp = stamp;
     289        }
     290    }
     291
     292    if (!isfinite(maxStamp->flux)) {
     293        fprintf (stderr, "no valid stamps?\n");
     294    }
     295
     296    int nKernels = 0;
     297
     298    if (maxStamp->convolutions1) {
     299        // output image is a grid of NXsub by NYsub sub-images
     300        nKernels = maxStamp->convolutions1->n;
     301        int NXsub = sqrt(nKernels);
     302        int NYsub = nKernels / NXsub;
     303        if (nKernels % NXsub) NYsub++;
     304
     305        int NXpix = NXsub * (2*footprint + 1 + 3);
     306        int NYpix = NYsub * (2*footprint + 1 + 3);
     307
     308        psImage *output = psImageAlloc(NXpix, NYpix, PS_TYPE_F32);
     309        psImageInit (output, 0.0);
     310
     311        for (int i = 0; i < nKernels; i++) {
     312            psKernel *kernel = maxStamp->convolutions1->data[i];
     313           
     314            int xSub = i % NXsub;
     315            int ySub = i / NXsub;
     316           
     317            int xPix = xSub * (2*footprint + 1 + 3) + footprint;
     318            int yPix = ySub * (2*footprint + 1 + 3) + footprint;
     319           
     320            double sum = 0.0;
     321            for (int y = -footprint; y <= footprint; y++) {
     322                for (int x = -footprint; x <= footprint; x++) {
     323                    output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x];
     324                    sum += kernel->kernel[y][x];
     325                }
     326            }
     327            fprintf (stderr, "kernel %d, sum %f\n", i, sum);
     328        }               
     329        pmVisualScaleImage(kapa2, output, "Image", 0, true);
     330    }                                   
     331       
     332    if (maxStamp->convolutions2) {
     333        // output image is a grid of NXsub by NYsub sub-images
     334        nKernels = maxStamp->convolutions2->n;
     335        int NXsub = sqrt(nKernels);
     336        int NYsub = nKernels / NXsub;
     337        if (nKernels % NXsub) NYsub++;
     338
     339        int NXpix = NXsub * (2*footprint + 1 + 3);
     340        int NYpix = NYsub * (2*footprint + 1 + 3);
     341
     342        psImage *output = psImageAlloc(NXpix, NYpix, PS_TYPE_F32);
     343        psImageInit (output, 0.0);
     344
     345        for (int i = 0; i < nKernels; i++) {
     346            psKernel *kernel = maxStamp->convolutions2->data[i];
     347           
     348            int xSub = i % NXsub;
     349            int ySub = i / NXsub;
     350           
     351            int xPix = xSub * (2*footprint + 1 + 3) + footprint;
     352            int yPix = ySub * (2*footprint + 1 + 3) + footprint;
     353           
     354            double sum = 0.0;
     355            for (int y = -footprint; y <= footprint; y++) {
     356                for (int x = -footprint; x <= footprint; x++) {
     357                    output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x];
     358                    sum += kernel->kernel[y][x];
     359                }
     360            }
     361            fprintf (stderr, "kernel %d, sum %f\n", i, sum);
     362        }               
     363        pmVisualScaleImage(kapa2, output, "Image", 1, true);
     364    }                                   
     365       
    216366    pmVisualAskUser(&plotImage);
    217367    return true;
     
    240390
    241391        overlay[Noverlay].type = KII_OVERLAY_BOX;
     392        if ((stamp->x < 1.0) && (stamp->y < 1.0)) {
     393            // fprintf (stderr, "stamp zero: %f %f\n", stamp->x, stamp->y);
     394            continue;
     395        }
     396        if (!isfinite(stamp->x) && !isfinite(stamp->y)) {
     397            // fprintf (stderr, "stamp nan: %f %f\n", stamp->x, stamp->y);
     398            continue;
     399        }
    242400        overlay[Noverlay].x = stamp->x;
    243401        overlay[Noverlay].y = stamp->y;
     
    255413}
    256414
     415static int footprint = 0;
     416static int NX = 0;
     417static int NY = 0;
     418static psImage *sourceImage      = NULL;
     419static psImage *targetImage      = NULL;
     420static psImage *residualImage    = NULL;
     421static psImage *fresidualImage   = NULL;
     422static psImage *differenceImage  = NULL;
     423static psImage *convolutionImage = NULL;
     424
     425bool pmSubtractionVisualShowFitInit(pmSubtractionStampList *stamps) {
     426
     427    if (!pmVisualIsVisual()) return true;
     428
     429    // generate 4 storage images large enough to hold the N stamps:
     430
     431    footprint = stamps->footprint;
     432
     433    float NXf = sqrt(stamps->num);
     434    NX = (int) NXf == NXf ? NXf : NXf + 1.0;
     435   
     436    float NYf = stamps->num / NX;
     437    NY = (int) NYf == NY ? NYf : NYf + 1.0;
     438
     439    int NXpix = (2*footprint + 1) * NX;
     440    NXpix += (NX > 1) ? 3 * NX : 0;
     441
     442    int NYpix = (2*footprint + 1) * NY;
     443    NYpix += (NY > 1) ? 3 * NY : 0;
     444
     445    sourceImage      = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     446    targetImage      = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     447    residualImage    = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     448    fresidualImage   = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     449    differenceImage  = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     450    convolutionImage = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     451   
     452    psImageInit (sourceImage,      0.0);
     453    psImageInit (targetImage,      0.0);
     454    psImageInit (residualImage,    0.0);
     455    psImageInit (fresidualImage,   0.0);
     456    psImageInit (differenceImage,  0.0);
     457    psImageInit (convolutionImage, 0.0);
     458
     459    return true;
     460}
     461
     462bool pmSubtractionVisualShowFitAddStamp(psKernel *target, psKernel *source, psKernel *convolution, double background, double norm, int index) {
     463
     464    if (!pmVisualIsVisual()) return true;
     465
     466    double sum;
     467
     468    int NXoff = index % NX;
     469    int NYoff = index / NX;
     470
     471    int NXpix = NXoff * (2*footprint + 1 + 3) + footprint;
     472    int NYpix = NYoff * (2*footprint + 1 + 3) + footprint;
     473
     474    // insert the (target) kernel into the (target) image:
     475    sum = 0.0;
     476    for (int y = -footprint; y <= footprint; y++) {
     477        for (int x = -footprint; x <= footprint; x++) {
     478            targetImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x];
     479            sum += targetImage->data.F32[y + NYpix][x + NXpix];
     480        }
     481    }
     482    targetImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     483
     484    // insert the (source) kernel into the (source) image:
     485    sum = 0.0;
     486    for (int y = -footprint; y <= footprint; y++) {
     487        for (int x = -footprint; x <= footprint; x++) {
     488            sourceImage->data.F32[y + NYpix][x + NXpix] = source->kernel[y][x];
     489            sum += sourceImage->data.F32[y + NYpix][x + NXpix];
     490        }
     491    }
     492    sourceImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     493
     494    // insert the (convolution) kernel into the (convolution) image:
     495    sum = 0.0;
     496    for (int y = -footprint; y <= footprint; y++) {
     497        for (int x = -footprint; x <= footprint; x++) {
     498            convolutionImage->data.F32[y + NYpix][x + NXpix] = -convolution->kernel[y][x];
     499            sum += convolutionImage->data.F32[y + NYpix][x + NXpix];
     500        }
     501    }
     502    convolutionImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     503   
     504    // insert the (difference) kernel into the (difference) image:
     505    sum = 0.0;
     506    for (int y = -footprint; y <= footprint; y++) {
     507        for (int x = -footprint; x <= footprint; x++) {
     508            differenceImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x] - background - source->kernel[y][x] * norm;
     509            sum += differenceImage->data.F32[y + NYpix][x + NXpix];
     510        }
     511    }
     512    differenceImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     513
     514    // insert the (residual) kernel into the (residual) image:
     515    sum = 0.0;
     516    for (int y = -footprint; y <= footprint; y++) {
     517        for (int x = -footprint; x <= footprint; x++) {
     518            residualImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x] - background - source->kernel[y][x] * norm - convolution->kernel[y][x];
     519            sum += residualImage->data.F32[y + NYpix][x + NXpix];
     520        }
     521    }
     522    residualImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     523
     524    // insert the (fresidual) kernel into the (fresidual) image:
     525    for (int y = -footprint; y <= footprint; y++) {
     526        for (int x = -footprint; x <= footprint; x++) {
     527            fresidualImage->data.F32[y + NYpix][x + NXpix] = residualImage->data.F32[y + NYpix][x + NXpix] / sqrt(PS_MAX(target->kernel[y][x], 100.0));
     528        }
     529    }
     530    return true;
     531}
     532
     533bool pmSubtractionVisualShowFit(double norm) {
     534
     535    // for testing, dump the residual image and exit
     536    if (0) {
     537        psMetadata *header = psMetadataAlloc();
     538        psMetadataAddF32 (header, PS_LIST_TAIL, "NORM", 0, "Normalization", norm);
     539        psFits *fits = psFitsOpen("resid.fits", "w");
     540        psFitsWriteImage(fits, header, residualImage, 0, NULL);
     541        psFitsClose(fits);
     542        // exit (0);
     543    }
     544
     545    if (!pmVisualIsVisual()) return true;
     546    if (!pmVisualInitWindow(&kapa1, "ppSub:Images")) return false;
     547    if (!pmVisualInitWindow(&kapa2, "ppSub:Misc")) return false;
     548
     549    KiiEraseOverlay (kapa1, "red");
     550    KiiEraseOverlay (kapa2, "red");
     551
     552    pmVisualScaleImage(kapa1, targetImage, "Target Stamps", 0, true);
     553    pmVisualScaleImage(kapa1, sourceImage, "Source Stamps", 1, true);
     554    pmVisualScaleImage(kapa1, convolutionImage, "Convolution Stamps", 2, true);
     555    KiiCenter (kapa1, 0.5*targetImage->numCols, 0.5*targetImage->numRows, 1);
     556
     557    pmVisualScaleImage(kapa2, fresidualImage, "Frac Residual Stamps", 2, true);
     558    pmVisualScaleImage(kapa2, differenceImage, "Difference Stamps", 0, true);
     559
     560    if (1) {
     561        KiiImage image;
     562        KapaImageData data;
     563        Coords coords;
     564        strcpy (coords.ctype, "RA---TAN");
     565
     566        image.data2d = residualImage->data.F32;
     567        image.Nx = residualImage->numCols;
     568        image.Ny = residualImage->numRows;
     569        strcpy (data.name, "Residual Stamps");
     570        strcpy (data.file, "Residual Stamps");
     571
     572        data.zero  = -300.0;
     573        data.range = +600.0;
     574        data.logflux = 0;
     575
     576        KiiSetChannel (kapa2, 1);
     577        KiiNewPicture2D (kapa2, &image, &data, &coords);
     578    } else {
     579        pmVisualScaleImage(kapa2, residualImage, "Residual Stamps", 1, true);
     580    }
     581
     582    KiiCenter (kapa2, 0.5*residualImage->numCols, 0.5*residualImage->numRows, 1);
     583
     584    pmVisualAskUser(NULL);
     585
     586    psFree(targetImage);
     587    psFree(sourceImage);
     588    psFree(convolutionImage);
     589    psFree(differenceImage);
     590    psFree(residualImage);
     591    psFree(fresidualImage);
     592
     593    targetImage = NULL;
     594    sourceImage = NULL;
     595    convolutionImage = NULL;
     596    differenceImage = NULL;
     597    residualImage = NULL;
     598    fresidualImage = NULL;
     599
     600    return true;
     601}
     602
     603bool pmSubtractionVisualPlotFit(const pmSubtractionKernels *kernels) {
     604
     605    Graphdata graphdata;
     606
     607    if (!pmVisualIsVisual()) return true;
     608    if (!pmVisualInitWindow(&kapa3, "ppSub:plots")) return false;
     609
     610    KapaClearSections (kapa3);
     611    KapaInitGraph (&graphdata);
     612
     613    psVector *x = psVectorAllocEmpty (kernels->num, PS_TYPE_F32);
     614    psVector *y = psVectorAllocEmpty (kernels->num, PS_TYPE_F32);
     615
     616    graphdata.xmin = -1.0;
     617    graphdata.xmax = kernels->num + 1.0;
     618    graphdata.ymin = +32.0;
     619    graphdata.ymax = -32.0;
     620
     621    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, 0.0, 0.0);
     622
     623    // construct the plot vectors
     624    for (int i = 0; i < kernels->num; i++) {
     625        x->data.F32[i] = i;
     626        y->data.F32[i] = p_pmSubtractionSolutionCoeff(kernels, polyValues, i, false);
     627        graphdata.ymin = PS_MIN(graphdata.ymin, y->data.F32[i]);
     628        graphdata.ymax = PS_MAX(graphdata.ymax, y->data.F32[i]);
     629    }
     630    x->n = y->n = kernels->num;
     631
     632    float range;
     633    range = graphdata.xmax - graphdata.xmin;
     634    graphdata.xmax += 0.05*range;
     635    graphdata.xmin -= 0.05*range;
     636    range = graphdata.ymax - graphdata.ymin;
     637    graphdata.ymax += 0.05*range;
     638    graphdata.ymin -= 0.05*range;
     639
     640    KapaSetLimits (kapa3, &graphdata);
     641
     642    KapaSetFont (kapa3, "helvetica", 14);
     643    KapaBox (kapa3, &graphdata);
     644    KapaSendLabel (kapa3, "kernel number", KAPA_LABEL_XM);
     645    KapaSendLabel (kapa3, "coeff", KAPA_LABEL_YM);
     646
     647    graphdata.color = KapaColorByName ("black");
     648    graphdata.ptype = 2;
     649    graphdata.size = 0.5;
     650    graphdata.style = 2;
     651
     652    KapaPrepPlot   (kapa3, x->n, &graphdata);
     653    KapaPlotVector (kapa3, x->n, x->data.F32, "x");
     654    KapaPlotVector (kapa3, x->n, y->data.F32, "y");
     655
     656    psFree (x);
     657    psFree (y);
     658
     659    pmVisualAskUser(NULL);
     660    return true;
     661}
     662
    257663#else
    258664bool pmSubtractionVisualClose(void) {return true;}
     
    261667bool pmSubtractionVisualPlotLeastSquares(pmSubtractionStampList *stamps) {return true;}
    262668bool pmSubtractionVisualShowSubtraction(psImage *image, psImage *ref, psImage *sub) {return true;}
     669bool pmSubtractionVisualShowFitInit(pmSubtractionStampList *stamps) {return true;}
     670bool pmSubtractionVisualShowFitAddStamp(psKernel *target, psKernel *source, psKernel *convolution, double background, double norm, int index) {return true;}
     671bool pmSubtractionVisualShowFit() {return true;}
     672bool pmSubtractionVisualPlotFit(const pmSubtractionKernels *kernels);
    263673#endif
  • trunk/psModules/src/imcombine/pmSubtractionVisual.h

    r23487 r26893  
    77bool pmSubtractionVisualPlotLeastSquares(pmSubtractionStampList *stamps);
    88bool pmSubtractionVisualShowSubtraction(psImage *image, psImage *ref, psImage *sub);
     9bool pmSubtractionVisualShowFitInit(pmSubtractionStampList *stamps);
     10bool pmSubtractionVisualShowFitAddStamp(psKernel *target, psKernel *source, psKernel *convolution, double background, double norm, int index);
     11bool pmSubtractionVisualShowFit(double norm);
     12bool pmSubtractionVisualPlotFit(const pmSubtractionKernels *kernels);
     13bool pmSubtractionVisualShowKernels(pmSubtractionKernels *kernels);
     14bool pmSubtractionVisualShowBasis(pmSubtractionStampList *stamps);
    915
    1016#endif
  • trunk/psModules/src/objects/pmDetections.c

    r23487 r26893  
    2626  psFree (detections->peaks);
    2727  psFree (detections->oldPeaks);
     28  psFree (detections->oldFootprints);
     29
     30  psFree (detections->newSources);
     31  psFree (detections->allSources);
    2832  return;
    2933}
     
    3539    psMemSetDeallocator(detections, (psFreeFunc) pmDetectionsFree);
    3640
    37     detections->footprints = NULL;
    38     detections->peaks      = NULL;
    39     detections->oldPeaks   = NULL;
    40     detections->last       = 0;
     41    detections->footprints    = NULL;
     42    detections->peaks         = NULL;
     43    detections->oldPeaks      = NULL;
     44    detections->oldFootprints = NULL;
     45    detections->newSources    = NULL;
     46    detections->allSources    = NULL;
     47    detections->last          = 0;
    4148
    4249    return (detections);
  • trunk/psModules/src/objects/pmDetections.h

    r23487 r26893  
    2121typedef struct {
    2222  psArray *footprints;        // collection of footprints in the image
     23  psArray *oldFootprints;     // collection of footprints previously found
    2324  psArray *peaks;             // collection of all peaks contained by the footprints
    2425  psArray *oldPeaks;          // collection of all peaks previously found
     26  psArray *newSources;        // collection of sources
     27  psArray *allSources;        // collection of sources
    2528  int last;
    2629} pmDetections;
  • trunk/psModules/src/objects/pmFootprintCullPeaks.c

    r24888 r26893  
    2929  */
    3030
    31 # define IN_PEAK 1 
     31# define IN_PEAK 1
    3232psErrorCode pmFootprintCullPeaks(const psImage *img, // the image wherein lives the footprint
    33                                  const psImage *weight, // corresponding variance image
    34                                 pmFootprint *fp, // Footprint containing mortal peaks
    35                                 const float nsigma_delta, // how many sigma above local background a peak needs to be to survive
    36                                 const float fPadding, // fractional padding added to stdev since bright peaks have unreasonably high significance
    37                                 const float min_threshold) { // minimum permitted coll height
     33                                 const psImage *weight, // corresponding variance image
     34                                pmFootprint *fp, // Footprint containing mortal peaks
     35                                const float nsigma_delta, // how many sigma above local background a peak needs to be to survive
     36                                const float fPadding, // fractional padding added to stdev since bright peaks have unreasonably high significance
     37                                const float min_threshold) { // minimum permitted coll height
    3838    assert (img != NULL); assert (img->type.type == PS_TYPE_F32);
    3939    assert (weight != NULL); assert (weight->type.type == PS_TYPE_F32);
     
    4242
    4343    if (fp->peaks == NULL || fp->peaks->n == 0) { // nothing to do
    44         return PS_ERR_NONE;
    45     }
    46 
    47     psRegion subRegion;                 // desired subregion; 1 larger than bounding box (grr)
     44        return PS_ERR_NONE;
     45    }
     46
     47    psRegion subRegion;                 // desired subregion; 1 larger than bounding box (grr)
    4848    subRegion.x0 = fp->bbox.x0; subRegion.x1 = fp->bbox.x1 + 1;
    4949    subRegion.y0 = fp->bbox.y0; subRegion.y1 = fp->bbox.y1 + 1;
     
    5555    psImage *idImg = psImageAlloc(subImg->numCols, subImg->numRows, PS_TYPE_S32);
    5656
    57     // We need a psArray of peaks brighter than the current peak. 
     57    // We need a psArray of peaks brighter than the current peak.
    5858    // We reject peaks which either:
    5959    // 1) are below the local threshold
     
    6767    // The brightest peak is always safe; go through other peaks trying to cull them
    6868    for (int i = 1; i < fp->peaks->n; i++) { // n.b. fp->peaks->n can change within the loop
    69         const pmPeak *peak = fp->peaks->data[i];
    70         int x = peak->x - subImg->col0;
    71         int y = peak->y - subImg->row0;
    72         //
    73         // Find the level nsigma below the peak that must separate the peak
    74         // from any of its friends
    75         //
    76         assert (x >= 0 && x < subImg->numCols && y >= 0 && y < subImg->numRows);
    77 
    78         // const float stdev = sqrt(subWt->data.F32[y][x]);
    79         // float threshold = subImg->data.F32[y][x] - nsigma_delta*stdev;
    80 
    81         const float stdev = sqrt(subWt->data.F32[y][x]);
    82         const float flux = subImg->data.F32[y][x];
    83         const float fStdev = fabs(stdev/flux);
    84         const float stdev_pad = fabs(flux * hypot(fStdev, fPadding));
    85         // if flux is negative, careful with fStdev
    86 
    87         float threshold = flux - nsigma_delta*stdev_pad;
    88        
    89         if (isnan(threshold) || threshold < min_threshold) {
    90             // min_threshold is assumed to be below the detection threshold,
    91             // so all the peaks are pmFootprint, and this isn't the brightest
     69        const pmPeak *peak = fp->peaks->data[i];
     70        int x = peak->x - subImg->col0;
     71        int y = peak->y - subImg->row0;
     72        //
     73        // Find the level nsigma below the peak that must separate the peak
     74        // from any of its friends
     75        //
     76        assert (x >= 0 && x < subImg->numCols && y >= 0 && y < subImg->numRows);
     77
     78        // const float stdev = sqrt(subWt->data.F32[y][x]);
     79        // float threshold = subImg->data.F32[y][x] - nsigma_delta*stdev;
     80
     81        const float stdev = sqrt(subWt->data.F32[y][x]);
     82        const float flux = subImg->data.F32[y][x];
     83        const float fStdev = fabs(stdev/flux);
     84        const float stdev_pad = fabs(flux * hypot(fStdev, fPadding));
     85        // if flux is negative, careful with fStdev
     86
     87        float threshold = flux - nsigma_delta*stdev_pad;
     88
     89        if (isnan(threshold) || threshold < min_threshold) {
     90            // min_threshold is assumed to be below the detection threshold,
     91            // so all the peaks are pmFootprint, and this isn't the brightest
     92            continue;
     93        }
     94
     95        // XXX EAM : if stdev >= 0, i'm not sure how this can ever be true?
     96        if (threshold > subImg->data.F32[y][x]) {
     97            threshold = subImg->data.F32[y][x] - 10*FLT_EPSILON;
     98        }
     99
     100        // XXX this is a bit expensive: psImageAlloc for every peak contained in this footprint
     101        // perhaps this should alloc a single ID image above and pass it in to be set.
     102
     103        // XXX optionally use the faster pmFootprintsFind if the subimage size is large (eg, M31)
     104
     105        // XXX this is not quite there yet:
     106        // pmFootprint *peakFootprint = NULL;
     107        // int area = subImg->numCols * subImg->numRows;
     108        // if (area > 30000) {
     109
     110        pmFootprint *peakFootprint = pmFootprintsFindAtPoint(subImg, threshold, brightPeaks, peak->y, peak->x);
     111
     112        // at this point brightPeaks only has the peaks brighter than the current
     113
     114        // XXX need to supply the image here
     115        // we set the IDs to either 1 (in peak) or 0 (not in peak)
     116        pmSetFootprintID (idImg, peakFootprint, IN_PEAK);
     117        psFree(peakFootprint);
     118
     119        // If this peak has not already been assigned to a source, then we can look for any
     120        // brighter peaks within its footprint. Check if any of the previous (brighter) peaks
     121        // are within the footprint of this peak If so, the current peak is bogus; drop it.
     122        bool keep = true;
     123        for (int j = 0; keep && !peak->assigned && (j < brightPeaks->n); j++) {
     124            const pmPeak *peak2 = fp->peaks->data[j];
     125            int x2 = peak2->x - subImg->col0;
     126            int y2 = peak2->y - subImg->row0;
     127            if (idImg->data.S32[y2][x2] == IN_PEAK) {
     128                // There's a brighter peak within the footprint above threshold; so cull our initial peak
     129                keep = false;
     130            }
     131        }
     132        if (!keep) {
     133            psAssert (!peak->assigned, "logic error: trying to drop a previously-assigned peak");  // we should not drop any already assigned peaks.
    92134            continue;
    93135        }
    94136
    95         // XXX EAM : if stdev >= 0, i'm not sure how this can ever be true?
    96         if (threshold > subImg->data.F32[y][x]) {
    97             threshold = subImg->data.F32[y][x] - 10*FLT_EPSILON;
    98         }
    99 
    100         // XXX this is a bit expensive: psImageAlloc for every peak contained in this footprint
    101         // perhaps this should alloc a single ID image above and pass it in to be set.
    102 
    103         // XXX optionally use the faster pmFootprintsFind if the subimage size is large (eg, M31)
    104 
    105         // XXX this is not quite there yet:
    106         // pmFootprint *peakFootprint = NULL;
    107         // int area = subImg->numCols * subImg->numRows;
    108         // if (area > 30000) {
    109 
    110         pmFootprint *peakFootprint = pmFootprintsFindAtPoint(subImg, threshold, brightPeaks, peak->y, peak->x);
    111 
    112         // at this point brightPeaks only has the peaks brighter than the current
    113 
    114         // XXX need to supply the image here
    115         // we set the IDs to either 1 (in peak) or 0 (not in peak)
    116         pmSetFootprintID (idImg, peakFootprint, IN_PEAK);
    117         psFree(peakFootprint);
    118 
    119         // Check if any of the previous (brighter) peaks are within the footprint of this peak
    120         // If so, the current peak is bogus; drop it.
    121         bool keep = true;
    122         for (int j = 0; keep && (j < brightPeaks->n); j++) {
    123             const pmPeak *peak2 = fp->peaks->data[j];
    124             int x2 = peak2->x - subImg->col0;
    125             int y2 = peak2->y - subImg->row0;
    126             if (idImg->data.S32[y2][x2] == IN_PEAK)
    127                 // There's a brighter peak within the footprint above threshold; so cull our initial peak
    128                 keep = false;
    129         }
    130         if (!keep) continue;
    131 
    132         psArrayAdd (brightPeaks, 128, fp->peaks->data[i]);
     137        psArrayAdd (brightPeaks, 128, fp->peaks->data[i]);
    133138    }
    134139
     
    151156  */
    152157psErrorCode pmFootprintCullPeaks_OLD(const psImage *img, // the image wherein lives the footprint
    153                                  const psImage *weight, // corresponding variance image
    154                                 pmFootprint *fp, // Footprint containing mortal peaks
    155                                 const float nsigma_delta, // how many sigma above local background a peak needs to be to survive
    156                                 const float fPadding, // fractional padding added to stdev since bright peaks have unreasonably high significance
    157                                 const float min_threshold) { // minimum permitted coll height
     158                                 const psImage *weight, // corresponding variance image
     159                                pmFootprint *fp, // Footprint containing mortal peaks
     160                                const float nsigma_delta, // how many sigma above local background a peak needs to be to survive
     161                                const float fPadding, // fractional padding added to stdev since bright peaks have unreasonably high significance
     162                                const float min_threshold) { // minimum permitted coll height
    158163    assert (img != NULL); assert (img->type.type == PS_TYPE_F32);
    159164    assert (weight != NULL); assert (weight->type.type == PS_TYPE_F32);
     
    162167
    163168    if (fp->peaks == NULL || fp->peaks->n == 0) { // nothing to do
    164         return PS_ERR_NONE;
    165     }
    166 
    167     psRegion subRegion;                 // desired subregion; 1 larger than bounding box (grr)
     169        return PS_ERR_NONE;
     170    }
     171
     172    psRegion subRegion;                 // desired subregion; 1 larger than bounding box (grr)
    168173    subRegion.x0 = fp->bbox.x0; subRegion.x1 = fp->bbox.x1 + 1;
    169174    subRegion.y0 = fp->bbox.y0; subRegion.y1 = fp->bbox.y1 + 1;
     
    185190    //
    186191    for (int i = 1; i < fp->peaks->n; i++) { // n.b. fp->peaks->n can change within the loop
    187         const pmPeak *peak = fp->peaks->data[i];
    188         int x = peak->x - subImg->col0;
    189         int y = peak->y - subImg->row0;
    190         //
    191         // Find the level nsigma below the peak that must separate the peak
    192         // from any of its friends
    193         //
    194         assert (x >= 0 && x < subImg->numCols && y >= 0 && y < subImg->numRows);
    195 
    196         // stdev ~ sqrt(flux) : for bright regions (f ~ 1e6, sqrt(f) = 1e3 -- unrealistically small deviations)
    197         // add additional padding in quadrature:
    198 
    199         const float stdev = sqrt(subWt->data.F32[y][x]);
    200         const float flux = subImg->data.F32[y][x];
    201         const float fStdev = stdev/flux;
    202         const float stdev_pad = flux * hypot(fStdev, fPadding);
    203         float threshold = flux - nsigma_delta*stdev_pad;
    204 
    205         if (isnan(threshold) || threshold < min_threshold) {
    206 #if 1     // min_threshold is assumed to be below the detection threshold,
    207           // so all the peaks are pmFootprint, and this isn't the brightest
    208             // XXX mark peak to be dropped
    209             (void)psArrayRemoveIndex(fp->peaks, i);
    210             i--;                        // we moved everything down one
    211             continue;
     192        const pmPeak *peak = fp->peaks->data[i];
     193        int x = peak->x - subImg->col0;
     194        int y = peak->y - subImg->row0;
     195        //
     196        // Find the level nsigma below the peak that must separate the peak
     197        // from any of its friends
     198        //
     199        assert (x >= 0 && x < subImg->numCols && y >= 0 && y < subImg->numRows);
     200
     201        // stdev ~ sqrt(flux) : for bright regions (f ~ 1e6, sqrt(f) = 1e3 -- unrealistically small deviations)
     202        // add additional padding in quadrature:
     203
     204        const float stdev = sqrt(subWt->data.F32[y][x]);
     205        const float flux = subImg->data.F32[y][x];
     206        const float fStdev = stdev/flux;
     207        const float stdev_pad = flux * hypot(fStdev, fPadding);
     208        float threshold = flux - nsigma_delta*stdev_pad;
     209
     210        if (isnan(threshold) || threshold < min_threshold) {
     211#if 1     // min_threshold is assumed to be below the detection threshold,
     212          // so all the peaks are pmFootprint, and this isn't the brightest
     213            // XXX mark peak to be dropped
     214            (void)psArrayRemoveIndex(fp->peaks, i);
     215            i--;                        // we moved everything down one
     216            continue;
    212217#else
    213218#error n.b. We will be running LOTS of checks at this threshold, so only find the footprint once
    214             threshold = min_threshold;
     219            threshold = min_threshold;
    215220#endif
    216         }
    217 
    218         // XXX EAM : if stdev >= 0, i'm not sure how this can ever be true?
    219         if (threshold > subImg->data.F32[y][x]) {
    220             threshold = subImg->data.F32[y][x] - 10*FLT_EPSILON;
    221         }
    222 
    223         // XXX this is a bit expensive: psImageAlloc for every peak contained in this footprint
    224         // perhaps this should alloc a single ID image above and pass it in to be set.
    225 
    226         const int peak_id = 1;          // the ID for the peak of interest
    227         brightPeaks->n = i;             // only stop at a peak brighter than we are
    228 
    229         // XXX optionally use the faster pmFootprintsFind if the subimage size is large (eg, M31)
    230 
    231         pmFootprint *peakFootprint = pmFootprintsFindAtPoint(subImg, threshold, brightPeaks, peak->y, peak->x);
    232         brightPeaks->n = 0;             // don't double free
    233         psImage *idImg = pmSetFootprintID(NULL, peakFootprint, peak_id);
    234         psFree(peakFootprint);
    235 
    236         // Check if any of the previous (brighter) peaks are within the footprint of this peak
    237         // If so, the current peak is bogus; drop it.
    238         int j;
    239         for (j = 0; j < i; j++) {
    240             const pmPeak *peak2 = fp->peaks->data[j];
    241             int x2 = peak2->x - subImg->col0;
    242             int y2 = peak2->y - subImg->row0;
    243             const int peak2_id = idImg->data.S32[y2][x2]; // the ID for some other peak
    244 
    245             if (peak2_id == peak_id) {  // There's a brighter peak within the footprint above
    246                 ;                       // threshold; so cull our initial peak
    247                 (void)psArrayRemoveIndex(fp->peaks, i);
    248                 i--;                    // we moved everything down one
    249                 break;
    250             }
    251         }
    252         if (j == i) {
    253             j++;
    254         }
    255 
    256         psFree(idImg);
     221        }
     222
     223        // XXX EAM : if stdev >= 0, i'm not sure how this can ever be true?
     224        if (threshold > subImg->data.F32[y][x]) {
     225            threshold = subImg->data.F32[y][x] - 10*FLT_EPSILON;
     226        }
     227
     228        // XXX this is a bit expensive: psImageAlloc for every peak contained in this footprint
     229        // perhaps this should alloc a single ID image above and pass it in to be set.
     230
     231        const int peak_id = 1;          // the ID for the peak of interest
     232        brightPeaks->n = i;             // only stop at a peak brighter than we are
     233
     234        // XXX optionally use the faster pmFootprintsFind if the subimage size is large (eg, M31)
     235
     236        pmFootprint *peakFootprint = pmFootprintsFindAtPoint(subImg, threshold, brightPeaks, peak->y, peak->x);
     237        brightPeaks->n = 0;             // don't double free
     238        psImage *idImg = pmSetFootprintID(NULL, peakFootprint, peak_id);
     239        psFree(peakFootprint);
     240
     241        // Check if any of the previous (brighter) peaks are within the footprint of this peak
     242        // If so, the current peak is bogus; drop it.
     243        int j;
     244        for (j = 0; j < i; j++) {
     245            const pmPeak *peak2 = fp->peaks->data[j];
     246            int x2 = peak2->x - subImg->col0;
     247            int y2 = peak2->y - subImg->row0;
     248            const int peak2_id = idImg->data.S32[y2][x2]; // the ID for some other peak
     249
     250            if (peak2_id == peak_id) {  // There's a brighter peak within the footprint above
     251                ;                       // threshold; so cull our initial peak
     252                (void)psArrayRemoveIndex(fp->peaks, i);
     253                i--;                    // we moved everything down one
     254                break;
     255            }
     256        }
     257        if (j == i) {
     258            j++;
     259        }
     260
     261        psFree(idImg);
    257262    }
    258263
    259264    brightPeaks->n = 0; psFree(brightPeaks);
    260     psFree((psImage *)subImg);
    261     psFree((psImage *)subWt);
     265    psFree(subImg);
     266    psFree(subWt);
    262267
    263268    return PS_ERR_NONE;
  • trunk/psModules/src/objects/pmFootprintFindAtPoint.c

    r21183 r26893  
    2929 * so we set appropriate mask bits
    3030 *
    31  * EAM : these function were confusingly using "startspan" and "spartspan" 
     31 * EAM : these function were confusingly using "startspan" and "spartspan"
    3232 * I've rationalized them all to 'startspan'
    3333 */
     
    3636// An enum for what we should do with a Startspan
    3737//
    38 typedef enum {PM_SSPAN_DOWN = 0,        // scan down from this span
    39               PM_SSPAN_UP,              // scan up from this span
    40               PM_SSPAN_RESTART,         // restart scanning from this span
    41               PM_SSPAN_DONE             // this span is processed
    42 } PM_SSPAN_DIR;                         // How to continue searching
     38typedef enum {PM_SSPAN_DOWN = 0,        // scan down from this span
     39              PM_SSPAN_UP,              // scan up from this span
     40              PM_SSPAN_RESTART,         // restart scanning from this span
     41              PM_SSPAN_DONE             // this span is processed
     42} PM_SSPAN_DIR;                         // How to continue searching
    4343//
    4444// An enum for mask's pixel values.  We're looking for pixels that are above threshold, and
    4545// we keep extra book-keeping information in the PM_SSPAN_STOP plane.  It's simpler to be
    46 // able to check for 
     46// able to check for
    4747//
    4848enum {
    49     PM_SSPAN_INITIAL = 0x0,             // initial state of pixels.
    50     PM_SSPAN_DETECTED = 0x1,            // we've seen this pixel
    51     PM_SSPAN_STOP = 0x2                 // you may stop searching when you see this pixel
     49    PM_SSPAN_INITIAL = 0x0,             // initial state of pixels.
     50    PM_SSPAN_DETECTED = 0x1,            // we've seen this pixel
     51    PM_SSPAN_STOP = 0x2                 // you may stop searching when you see this pixel
    5252};
    5353//
     
    5555//
    5656typedef struct {
    57     const pmSpan *span;                 // save the pixel range
    58     PM_SSPAN_DIR direction;             // How to continue searching
    59     bool stop;                          // should we stop searching?
     57    const pmSpan *span;                 // save the pixel range
     58    PM_SSPAN_DIR direction;             // How to continue searching
     59    bool stop;                          // should we stop searching?
    6060} Startspan;
    6161
    6262static void startspanFree(Startspan *sspan) {
    63     psFree((void *)sspan->span);
     63    psFree(sspan->span);
    6464}
    6565
    6666static Startspan *
    67 StartspanAlloc(const pmSpan *span,      // The span in question
    68                psImage *mask,           // Pixels that we've already detected
    69                const PM_SSPAN_DIR dir   // Should we continue searching towards the top of the image?
     67StartspanAlloc(const pmSpan *span,      // The span in question
     68               psImage *mask,           // Pixels that we've already detected
     69               const PM_SSPAN_DIR dir   // Should we continue searching towards the top of the image?
    7070    ) {
    7171    Startspan *sspan = psAlloc(sizeof(Startspan));
     
    7575    sspan->direction = dir;
    7676    sspan->stop = false;
    77    
    78     if (mask != NULL) {                 // remember that we've detected these pixels
    79         psImageMaskType *mpix = &mask->data.PS_TYPE_IMAGE_MASK_DATA[span->y - mask->row0][span->x0 - mask->col0];
    80 
    81         for (int i = 0; i <= span->x1 - span->x0; i++) {
    82             mpix[i] |= PM_SSPAN_DETECTED;
    83             if (mpix[i] & PM_SSPAN_STOP) {
    84                 sspan->stop = true;
    85             }
    86         }
    87     }
    88    
     77
     78    if (mask != NULL) {                 // remember that we've detected these pixels
     79        psImageMaskType *mpix = &mask->data.PS_TYPE_IMAGE_MASK_DATA[span->y - mask->row0][span->x0 - mask->col0];
     80
     81        for (int i = 0; i <= span->x1 - span->x0; i++) {
     82            mpix[i] |= PM_SSPAN_DETECTED;
     83            if (mpix[i] & PM_SSPAN_STOP) {
     84                sspan->stop = true;
     85            }
     86        }
     87    }
     88
    8989    return sspan;
    9090}
     
    9494//
    9595static bool add_startspan(psArray *startspans, // the saved Startspans
    96                           const pmSpan *sp, // the span in question
    97                           psImage *mask, // mask of detected/stop pixels
    98                           const PM_SSPAN_DIR dir) { // the desired direction to search
     96                          const pmSpan *sp, // the span in question
     97                          psImage *mask, // mask of detected/stop pixels
     98                          const PM_SSPAN_DIR dir) { // the desired direction to search
    9999    if (dir == PM_SSPAN_RESTART) {
    100         if (add_startspan(startspans, sp, mask,  PM_SSPAN_UP) ||
    101             add_startspan(startspans, sp, NULL, PM_SSPAN_DOWN)) {
    102             return true;
    103         }
     100        if (add_startspan(startspans, sp, mask,  PM_SSPAN_UP) ||
     101            add_startspan(startspans, sp, NULL, PM_SSPAN_DOWN)) {
     102            return true;
     103        }
    104104    } else {
    105         Startspan *sspan = StartspanAlloc(sp, mask, dir);
    106         if (sspan->stop) {              // we detected a stop bit
    107             psFree(sspan);              // don't allocate new span
    108 
    109             return true;
    110         } else {
    111             psArrayAdd(startspans, 1, sspan);
    112             psFree(sspan);              // as it's now owned by startspans
    113         }
     105        Startspan *sspan = StartspanAlloc(sp, mask, dir);
     106        if (sspan->stop) {              // we detected a stop bit
     107            psFree(sspan);              // don't allocate new span
     108
     109            return true;
     110        } else {
     111            psArrayAdd(startspans, 1, sspan);
     112            psFree(sspan);              // as it's now owned by startspans
     113        }
    114114    }
    115115
     
    127127 */
    128128static bool do_startspan(pmFootprint *fp, // the footprint that we're building
    129                         const psImage *img, // the psImage we're working on
    130                         psImage *mask, // the associated masks
    131                          const float threshold, // Threshold
    132                          psArray *startspans) { // specify which span to process next
    133     bool F32 = false;                   // is this an F32 image?
     129                        const psImage *img, // the psImage we're working on
     130                        psImage *mask, // the associated masks
     131                         const float threshold, // Threshold
     132                         psArray *startspans) { // specify which span to process next
     133    bool F32 = false;                   // is this an F32 image?
    134134    if (img->type.type == PS_TYPE_F32) {
    135         F32 = true;
     135        F32 = true;
    136136    } else if (img->type.type == PS_TYPE_S32) {
    137         F32 = false;
    138     } else {                            // N.b. You can't trivially add more cases here; F32 is just a bool
    139         psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);
    140         return NULL;
    141     }
    142 
    143     psF32 *imgRowF32 = NULL;            // row pointer if F32
    144     psS32 *imgRowS32 = NULL;            //  "   "   "  "  !F32
    145     psImageMaskType *maskRow = NULL;            //  masks's row pointer
    146    
     137        F32 = false;
     138    } else {                            // N.b. You can't trivially add more cases here; F32 is just a bool
     139        psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);
     140        return NULL;
     141    }
     142
     143    psF32 *imgRowF32 = NULL;            // row pointer if F32
     144    psS32 *imgRowS32 = NULL;            //  "   "   "  "  !F32
     145    psImageMaskType *maskRow = NULL;            //  masks's row pointer
     146
    147147    const int row0 = img->row0;
    148148    const int col0 = img->col0;
    149149    const int numRows = img->numRows;
    150150    const int numCols = img->numCols;
    151    
     151
    152152    /********************************************************************************************************/
    153    
     153
    154154    Startspan *sspan = NULL;
    155155    for (int i = 0; i < startspans->n; i++) {
    156         sspan = startspans->data[i];
    157         if (sspan->direction != PM_SSPAN_DONE) {
    158             break;
    159         }
    160         if (sspan->stop) {
    161             break;
    162         }
     156        sspan = startspans->data[i];
     157        if (sspan->direction != PM_SSPAN_DONE) {
     158            break;
     159        }
     160        if (sspan->stop) {
     161            break;
     162        }
    163163    }
    164164    if (sspan == NULL || sspan->direction == PM_SSPAN_DONE) { // no more Startspans to process
    165         return false;
    166     }
    167     if (sspan->stop) {                  // they don't want any more spans processed
    168         return false;
     165        return false;
     166    }
     167    if (sspan->stop) {                  // they don't want any more spans processed
     168        return false;
    169169    }
    170170    /*
     
    179179     * Go through image identifying objects
    180180     */
    181     int nx0, nx1 = -1;                  // new values of x0, x1
     181    int nx0, nx1 = -1;                  // new values of x0, x1
    182182    const int di = (dir == PM_SSPAN_UP) ? 1 : -1; // how much i changes to get to the next row
    183     bool stop = false;                  // should I stop searching for spans?
     183    bool stop = false;                  // should I stop searching for spans?
    184184
    185185    for (int i = sspan->span->y -row0 + di; i < numRows && i >= 0; i += di) {
    186         imgRowF32 = img->data.F32[i];   // only one of
    187         imgRowS32 = img->data.S32[i];   //      these is valid!
    188         maskRow = mask->data.PS_TYPE_IMAGE_MASK_DATA[i];
    189         //
    190         // Search left from the pixel diagonally to the left of (i - di, x0). If there's
    191         // a connected span there it may need to grow up and/or down, so push it onto
    192         // the stack for later consideration
    193         //
    194         nx0 = -1;
    195         for (int j = x0 - 1; j >= -1; j--) {
    196             double pixVal = (j < 0) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
    197             if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) {
    198                 if (j < x0 - 1) {       // we found some pixels above threshold
    199                     nx0 = j + 1;
    200                 }
    201                 break;
    202             }
    203         }
    204 
    205         if (nx0 < 0) {                  // no span to the left
    206             nx1 = x0 - 1;               // we're going to resume searching at nx1 + 1
    207         } else {
    208             //
    209             // Search right in leftmost span
    210             //
    211             //nx1 = 0;                  // make gcc happy
    212             for (int j = nx0 + 1; j <= numCols; j++) {
    213                 double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
    214                 if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) {
    215                     nx1 = j - 1;
    216                     break;
    217                 }
    218             }
    219            
    220             const pmSpan *sp = pmFootprintAddSpan(fp, i + row0, nx0 + col0, nx1 + col0);
    221            
    222             if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
    223                 stop = true;
    224                 break;
    225             }
    226         }
    227         //
    228         // Now look for spans connected to the old span.  The first of these we'll
    229         // simply process, but others will have to be deferred for later consideration.
    230         //
    231         // In fact, if the span overhangs to the right we'll have to defer the overhang
    232         // until later too, as it too can grow in both directions
    233         //
    234         // Note that column numCols exists virtually, and always ends the last span; this
    235         // is why we claim below that sx1 is always set
    236         //
    237         bool first = false;             // is this the first new span detected?
    238         for (int j = nx1 + 1; j <= x1 + 1; j++) {
    239             double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
    240             if (!(maskRow[j] & PM_SSPAN_DETECTED) && pixVal >= threshold) {
    241                 int sx0 = j++;          // span that we're working on is sx0:sx1
    242                 int sx1 = -1;           // We know that if we got here, we'll also set sx1
    243                 for (; j <= numCols; j++) {
    244                     double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
    245                     if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) { // end of span
    246                         sx1 = j;
    247                         break;
    248                     }
    249                 }
    250                 assert (sx1 >= 0);
    251 
    252                 const pmSpan *sp;
    253                 if (first) {
    254                     if (sx1 <= x1) {
    255                         sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
    256                         if (add_startspan(startspans, sp, mask, PM_SSPAN_DONE)) {
    257                             stop = true;
    258                             break;
    259                         }
    260                     } else {            // overhangs to right
    261                         sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, x1 + col0);
    262                         if (add_startspan(startspans, sp, mask, PM_SSPAN_DONE)) {
    263                             stop = true;
    264                             break;
    265                         }
    266                         sp = pmFootprintAddSpan(fp, i + row0, x1 + 1 + col0, sx1 + col0 - 1);
    267                         if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
    268                             stop = true;
    269                             break;
    270                         }
    271                     }
    272                     first = false;
    273                 } else {
    274                     sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
    275                     if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
    276                         stop = true;
    277                         break;
    278                     }
    279                 }
    280             }
    281         }
    282 
    283         if (stop || first == false) {   // we're done
    284             break;
    285         }
    286 
    287         x0 = nx0; x1 = nx1;
     186        imgRowF32 = img->data.F32[i];   // only one of
     187        imgRowS32 = img->data.S32[i];   //      these is valid!
     188        maskRow = mask->data.PS_TYPE_IMAGE_MASK_DATA[i];
     189        //
     190        // Search left from the pixel diagonally to the left of (i - di, x0). If there's
     191        // a connected span there it may need to grow up and/or down, so push it onto
     192        // the stack for later consideration
     193        //
     194        nx0 = -1;
     195        for (int j = x0 - 1; j >= -1; j--) {
     196            double pixVal = (j < 0) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
     197            if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) {
     198                if (j < x0 - 1) {       // we found some pixels above threshold
     199                    nx0 = j + 1;
     200                }
     201                break;
     202            }
     203        }
     204
     205        if (nx0 < 0) {                  // no span to the left
     206            nx1 = x0 - 1;               // we're going to resume searching at nx1 + 1
     207        } else {
     208            //
     209            // Search right in leftmost span
     210            //
     211            //nx1 = 0;                  // make gcc happy
     212            for (int j = nx0 + 1; j <= numCols; j++) {
     213                double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
     214                if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) {
     215                    nx1 = j - 1;
     216                    break;
     217                }
     218            }
     219
     220            const pmSpan *sp = pmFootprintAddSpan(fp, i + row0, nx0 + col0, nx1 + col0);
     221
     222            if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
     223                stop = true;
     224                break;
     225            }
     226        }
     227        //
     228        // Now look for spans connected to the old span.  The first of these we'll
     229        // simply process, but others will have to be deferred for later consideration.
     230        //
     231        // In fact, if the span overhangs to the right we'll have to defer the overhang
     232        // until later too, as it too can grow in both directions
     233        //
     234        // Note that column numCols exists virtually, and always ends the last span; this
     235        // is why we claim below that sx1 is always set
     236        //
     237        bool first = false;             // is this the first new span detected?
     238        for (int j = nx1 + 1; j <= x1 + 1; j++) {
     239            double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
     240            if (!(maskRow[j] & PM_SSPAN_DETECTED) && pixVal >= threshold) {
     241                int sx0 = j++;          // span that we're working on is sx0:sx1
     242                int sx1 = -1;           // We know that if we got here, we'll also set sx1
     243                for (; j <= numCols; j++) {
     244                    double pixVal = (j >= numCols) ? threshold - 100 : (F32 ? imgRowF32[j] : imgRowS32[j]);
     245                    if ((maskRow[j] & PM_SSPAN_DETECTED) || pixVal < threshold) { // end of span
     246                        sx1 = j;
     247                        break;
     248                    }
     249                }
     250                assert (sx1 >= 0);
     251
     252                const pmSpan *sp;
     253                if (first) {
     254                    if (sx1 <= x1) {
     255                        sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
     256                        if (add_startspan(startspans, sp, mask, PM_SSPAN_DONE)) {
     257                            stop = true;
     258                            break;
     259                        }
     260                    } else {            // overhangs to right
     261                        sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, x1 + col0);
     262                        if (add_startspan(startspans, sp, mask, PM_SSPAN_DONE)) {
     263                            stop = true;
     264                            break;
     265                        }
     266                        sp = pmFootprintAddSpan(fp, i + row0, x1 + 1 + col0, sx1 + col0 - 1);
     267                        if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
     268                            stop = true;
     269                            break;
     270                        }
     271                    }
     272                    first = false;
     273                } else {
     274                    sp = pmFootprintAddSpan(fp, i + row0, sx0 + col0, sx1 + col0 - 1);
     275                    if (add_startspan(startspans, sp, mask, PM_SSPAN_RESTART)) {
     276                        stop = true;
     277                        break;
     278                    }
     279                }
     280            }
     281        }
     282
     283        if (stop || first == false) {   // we're done
     284            break;
     285        }
     286
     287        x0 = nx0; x1 = nx1;
    288288    }
    289289    /*
     
    309309 */
    310310pmFootprint *
    311 pmFootprintsFindAtPoint(const psImage *img,     // image to search
    312                        const float threshold,   // Threshold
    313                        const psArray *peaks, // array of peaks; finding one terminates search for footprint
    314                        int row, int col) { // starting position (in img's parent's coordinate system)
     311pmFootprintsFindAtPoint(const psImage *img,     // image to search
     312                       const float threshold,   // Threshold
     313                       const psArray *peaks, // array of peaks; finding one terminates search for footprint
     314                       int row, int col) { // starting position (in img's parent's coordinate system)
    315315   assert(img != NULL);
    316316
    317    bool F32 = false;                    // is this an F32 image?
     317   bool F32 = false;                    // is this an F32 image?
    318318   if (img->type.type == PS_TYPE_F32) {
    319319       F32 = true;
    320320   } else if (img->type.type == PS_TYPE_S32) {
    321321       F32 = false;
    322    } else {                             // N.b. You can't trivially add more cases here; F32 is just a bool
     322   } else {                             // N.b. You can't trivially add more cases here; F32 is just a bool
    323323       psError(PS_ERR_UNKNOWN, true, "Unsupported psImage type: %d", img->type.type);
    324324       return NULL;
    325325   }
    326    psF32 *imgRowF32 = NULL;             // row pointer if F32
    327    psS32 *imgRowS32 = NULL;             //  "   "   "  "  !F32
    328    
     326   psF32 *imgRowF32 = NULL;             // row pointer if F32
     327   psS32 *imgRowS32 = NULL;             //  "   "   "  "  !F32
     328
    329329   const int row0 = img->row0;
    330330   const int col0 = img->col0;
     
    339339        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
    340340                "row/col == (%d, %d) are out of bounds [%d--%d, %d--%d]",
    341                 row + row0, col + col0, row0, row0 + numRows - 1, col0, col0 + numCols - 1);
     341                row + row0, col + col0, row0, row0 + numRows - 1, col0, col0 + numCols - 1);
    342342       return NULL;
    343343   }
     
    347347       return pmFootprintAlloc(0, img);
    348348   }
    349    
     349
    350350   pmFootprint *fp = pmFootprintAlloc(1 + img->numRows/10, img);
    351351/*
     
    364364   if (peaks != NULL) {
    365365       for (int i = 0; i < peaks->n; i++) {
    366            pmPeak *peak = peaks->data[i];
    367            mask->data.PS_TYPE_IMAGE_MASK_DATA[peak->y - mask->row0][peak->x - mask->col0] |= PM_SSPAN_STOP;
     366           pmPeak *peak = peaks->data[i];
     367           mask->data.PS_TYPE_IMAGE_MASK_DATA[peak->y - mask->row0][peak->x - mask->col0] |= PM_SSPAN_STOP;
    368368       }
    369369   }
     
    372372 */
    373373   psArray *startspans = psArrayAllocEmpty(1); // spans where we have to restart the search
    374    
    375    imgRowF32 = img->data.F32[row];      // only one of
    376    imgRowS32 = img->data.S32[row];      //      these is valid!
     374
     375   imgRowF32 = img->data.F32[row];      // only one of
     376   imgRowS32 = img->data.S32[row];      //      these is valid!
    377377   psImageMaskType *maskRow = mask->data.PS_TYPE_IMAGE_MASK_DATA[row];
    378378   {
    379379       int i;
    380380       for (i = col; i >= 0; i--) {
    381            pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
    382            if ((maskRow[i] & PM_SSPAN_DETECTED) || pixVal < threshold) {
    383                break;
    384            }
     381           pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
     382           if ((maskRow[i] & PM_SSPAN_DETECTED) || pixVal < threshold) {
     383               break;
     384           }
    385385       }
    386386       int i0 = i;
    387387       for (i = col; i < numCols; i++) {
    388            pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
    389            if ((maskRow[i] & PM_SSPAN_DETECTED) || pixVal < threshold) {
    390                break;
    391            }
     388           pixVal = F32 ? imgRowF32[i] : imgRowS32[i];
     389           if ((maskRow[i] & PM_SSPAN_DETECTED) || pixVal < threshold) {
     390               break;
     391           }
    392392       }
    393393       int i1 = i;
     
    404404    */
    405405   psFree(mask);
    406    psFree(startspans);                  // restores the image pixel
    407 
    408    return fp;                           // pmFootprint really
     406   psFree(startspans);                  // restores the image pixel
     407
     408   return fp;                           // pmFootprint really
    409409}
  • trunk/psModules/src/objects/pmPSF.c

    r24206 r26893  
    4242#include "pmErrorCodes.h"
    4343
     44
     45#define MAX_AXIS_RATIO 20.0             // Maximum axis ratio for PSF model
     46
    4447/*****************************************************************************/
    4548/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
     
    405408    return psf;
    406409}
     410
     411
     412float pmPSFtoFWHM(const pmPSF *psf, float x, float y)
     413{
     414    PS_ASSERT_PTR_NON_NULL(psf, NAN);
     415
     416    pmModel *model = pmModelFromPSFforXY(psf, x, y, 1.0); // Model of source
     417    if (!model) {
     418        psError(PS_ERR_UNKNOWN, false, "Unable to determine PSF model at %f,%f\n", x, y);
     419        return NAN;
     420    }
     421    psF32 *params = model->params->data.F32; // Model parameters
     422    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO); // Ellipse axes
     423
     424    // Curiously, the minor axis can be larger than the major axis, so need to check.
     425    float fwhm = 2.355 * PS_MAX(axes.minor, axes.major); // FWHM, converted from sigma
     426
     427    psFree(model);
     428
     429    return fwhm;
     430}
  • trunk/psModules/src/objects/pmPSF.h

    r25754 r26893  
    111111psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR);
    112112
     113/// Calculate FWHM value from a PSF
     114float pmPSFtoFWHM(
     115    const pmPSF *psf,                   // PSF of interest
     116    float x, float y                    // Position of interest
     117    );
     118
     119
    113120/// @}
    114121# endif
  • trunk/psModules/src/objects/pmPeaks.c

    r26391 r26893  
    416416
    417417        } else {
    418             psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
     418            psLogMsg ("psModules.objects", 5, "peak specified outside valid column range.");
    419419        }
    420420    }
     
    501501                }
    502502            } else {
    503                 psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
     503                psLogMsg ("psModules.objects", 5, "peak specified outside valid column range.");
    504504            }
    505505
     
    545545            }
    546546        } else {
    547             psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
     547            psLogMsg ("psModules.objects", 5, "peak specified outside valid column range.");
    548548        }
    549549    }
  • trunk/psModules/src/objects/pmSource.c

    r26395 r26893  
    33 *  Functions to define and manipulate sources on images
    44 *
    5  *  @author GLG, MHPCC
    6  *  @author EAM, IfA: significant modifications.
     5 *  @author EAM, IfA
     6 *  @author GLG, MHPCC (initial code base)
    77 *
    88 *  @version $Revision: 1.70 $ $Name: not supported by cvs2svn $
    99 *  @date $Date: 2009-02-16 22:29:59 $
    10  *
    11  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
    12  *
     10 *  Copyright 2009 Institute for Astronomy, University of Hawaii
    1311 */
    1412
     
    277275// psphot-specific function which applies the recipe values
    278276// only apply selection to sources within specified region
    279 pmPSFClump pmSourcePSFClump(psRegion *region, psArray *sources, psMetadata *recipe)
     277pmPSFClump pmSourcePSFClump(psImage **savedImage, psRegion *region, psArray *sources, float PSF_SN_LIM, float PSF_CLUMP_GRID_SCALE, psF32 SX_MAX, psF32 SY_MAX, psF32 AR_MAX)
    280278{
    281279    psTrace("psModules.objects", 10, "---- begin ----\n");
     
    287285
    288286    PS_ASSERT_PTR_NON_NULL(sources, errorClump);
    289     PS_ASSERT_PTR_NON_NULL(recipe, errorClump);
    290 
    291     bool status = true;                 // Status of MD lookup
    292     float PSF_SN_LIM = psMetadataLookupF32(&status, recipe, "PSF_SN_LIM");
    293     if (!status) {
    294         PSF_SN_LIM = 0;
    295     }
    296     float PSF_CLUMP_GRID_SCALE = psMetadataLookupF32(&status, recipe, "PSF_CLUMP_GRID_SCALE");
    297     if (!status) {
    298         PSF_CLUMP_GRID_SCALE = 0.1;
    299     }
    300287
    301288    // find the sigmaX, sigmaY clump
    302289    {
    303         psF32 SX_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_SX_MAX");
    304         if (!status) {
    305             psWarning("MOMENTS_SX_MAX not set in recipe");
    306             SX_MAX = 10.0;
    307         }
    308         psF32 SY_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_SY_MAX");
    309         if (!status) {
    310             psWarning("MOMENTS_SY_MAX not set in recipe");
    311             SY_MAX = 10.0;
    312         }
    313         psF32 AR_MAX = psMetadataLookupF32(&status, recipe, "MOMENTS_AR_MAX");
    314         if (!status) {
    315             psWarning("MOMENTS_AR_MAX not set in recipe");
    316             AR_MAX =  3.0;
    317         }
    318290        psF32 AR_MIN = 1.0 / AR_MAX;
    319291
     
    401373        psfClump.nSigma = stats->sampleStdev;
    402374
    403         const bool keep_psf_clump = psMetadataLookupBool(NULL, recipe, "KEEP_PSF_CLUMP");
    404         if (keep_psf_clump)
    405         {
    406             psMetadataAdd(recipe, PS_LIST_TAIL,
    407                           "PSF_CLUMP", PS_DATA_IMAGE, "Image of PSF coefficients", splane);
     375        if (savedImage) {
     376            *savedImage = psMemIncrRefCounter(splane);
    408377        }
    409378        psFree (splane);
     
    411380
    412381        // if we failed to find a valid peak, return the empty clump (failure signal)
    413         if (!peaks || !peaks->n) {
     382        if (peaks == NULL) {
     383            psError(PS_ERR_UNKNOWN, false, "failure in peak analysis for PSF clump.\n");
     384            psFree (peaks);
     385            return emptyClump;
     386        }
     387
     388        if (peaks->n == 0)
     389        {
    414390            psLogMsg ("psphot", 3, "failed to find a peak in the PSF clump image\n");
    415391            if (nValid == 0) {
     
    418394                psLogMsg ("psphot", 3, "no significant peak\n");
    419395            }
     396            psFree (peaks);
    420397            return (emptyClump);
    421398        }
     
    524501*****************************************************************************/
    525502
    526 bool pmSourceRoughClass(psRegion *region, psArray *sources, psMetadata *recipe, pmPSFClump clump, psImageMaskType maskSat)
     503bool pmSourceRoughClass(psRegion *region, psArray *sources, float PSF_SN_LIM, float PSF_CLUMP_NSIGMA, pmPSFClump clump, psImageMaskType maskSat)
    527504{
    528505    psTrace("psModules.objects", 10, "---- begin ----");
    529506
    530507    PS_ASSERT_PTR_NON_NULL(sources, false);
    531     PS_ASSERT_PTR_NON_NULL(recipe, false);
    532508
    533509    int Nsat     = 0;
     
    542518    psVector *starsn_peaks = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
    543519    psVector *starsn_moments = psVectorAllocEmpty (sources->n, PS_TYPE_F32);
    544 
    545     // get basic parameters, or set defaults
    546     bool status;
    547     float PSF_SN_LIM = psMetadataLookupF32 (&status, recipe, "PSF_SN_LIM");
    548     if (!status) PSF_SN_LIM = 20.0;
    549     float PSF_CLUMP_NSIGMA = psMetadataLookupF32 (&status, recipe, "PSF_CLUMP_NSIGMA");
    550     if (!status) PSF_CLUMP_NSIGMA = 1.5;
    551 
    552     // float INNER_RADIUS = psMetadataLookupF32 (&status, recipe, "SKY_INNER_RADIUS");
    553520
    554521    pmSourceMode noMoments = PM_SOURCE_MODE_MOMENTS_FAILURE | PM_SOURCE_MODE_SKYVAR_FAILURE | PM_SOURCE_MODE_SKY_FAILURE | PM_SOURCE_MODE_BELOW_MOMENTS_SN;
  • trunk/psModules/src/objects/pmSource.h

    r25754 r26893  
    176176 *
    177177 * The return value indicates the success (TRUE) of the operation.
    178  *
    179  * XXX: Limit the S/N of the candidate sources (part of Metadata)? (TBD).
    180  * XXX: Save the clump parameters on the Metadata (TBD)
    181  *
    182  */
     178 */
     179
    183180pmPSFClump pmSourcePSFClump(
     181    psImage **savedImage,
    184182    psRegion *region,                   ///< restrict measurement to specified region
    185183    psArray *source,                    ///< The input pmSource
    186     psMetadata *metadata                ///< Contains classification parameters
     184    float PSF_SN_LIM,
     185    float PSF_CLUMP_GRID_SCALE,
     186    psF32 SX_MAX,
     187    psF32 SY_MAX,
     188    psF32 AR_MAX
    187189);
    188190
     
    200202    psRegion *region,                   ///< restrict measurement to specified region
    201203    psArray *sources,                    ///< The input pmSources
    202     psMetadata *metadata,               ///< Contains classification parameters
     204    float PSF_SN_LIM,                    ///< min S/N for source to be used for PSF model
     205    float PSF_CLUMP_NSIGMA,              ///< size of region around peak of clump for PSF stars
    203206    pmPSFClump clump,                   ///< Statistics about the PSF clump
    204207    psImageMaskType maskSat             ///< Mask value for saturated pixels
     
    220223    float radius,     ///< Use a circle of pixels around the peak
    221224    float sigma,      ///< size of Gaussian window function (<= 0.0 -> skip window)
    222     float minSN       ///< minimum pixel significance
     225    float minSN,              ///< minimum pixel significance
     226    psImageMaskType maskVal
    223227);
    224228
  • trunk/psModules/src/objects/pmSourceIO.c

    r26393 r26893  
    4040#include "pmPSF.h"
    4141#include "pmModel.h"
     42#include "pmDetections.h"
    4243#include "pmSource.h"
    4344#include "pmModelClass.h"
     
    344345
    345346    // if sources is NULL, write out an empty table
    346     // input / output sources are stored on the readout->analysis as "PSPHOT.SOURCES" -- a better name might be something like PM_SOURCE_DATA
    347     psArray *sources = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.SOURCES");
     347    // input / output sources are stored on the readout->analysis as "PSPHOT.DETECTIONS"
     348
     349    psArray *sources = NULL;
     350    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     351    if (detections) {
     352        sources = detections->allSources;
     353    }
    348354    if (!sources) {
     355        detections = pmDetectionsAlloc();
    349356        sources = psArrayAlloc(0);
    350         psMetadataAddArray(readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_META_REPLACE, "Blank array of sources", sources);
    351         psFree(sources); // Held onto by the metadata, so we can continue to use
     357        detections->allSources = sources;
     358        psMetadataAddPtr(readout->analysis, PS_LIST_TAIL, "PSPHOT.DETECTIONS", PS_DATA_UNKNOWN | PS_META_REPLACE, "Blank array of sources", detections);
     359        psFree(detections); // Held onto by the metadata, so we can continue to use
    352360    }
    353361
     
    10311039    }
    10321040    readout->data_exists = true;
    1033     status = psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.SOURCES", PS_DATA_ARRAY, "input sources", sources);
    1034     psFree (sources);
     1041
     1042    pmDetections *detections = pmDetectionsAlloc();
     1043    detections->allSources = sources;
     1044    status = psMetadataAdd (readout->analysis, PS_LIST_TAIL, "PSPHOT.DETECTIONS", PS_DATA_ARRAY, "input sources", detections);
     1045    psFree (detections);
    10351046    return true;
    10361047}
     
    11241135    bool status;
    11251136
    1126     // select the psf of interest
    1127     pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.SOURCES");
    1128     if (!psf) return false;
    1129     return true;
    1130 }
    1131 
    1132 
     1137    // select the detections of interest
     1138    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     1139    if (!detections) return false;
     1140    if (!detections->allSources) return false;
     1141    return true;
     1142}
     1143
     1144
  • trunk/psModules/src/objects/pmSourceIO_CMF_PS1_V2.c

    r25754 r26893  
    304304        source->peak->dx   = dPAR[PM_PAR_XPOS];
    305305        source->peak->dy   = dPAR[PM_PAR_YPOS];
     306        source->peak->SN   = sqrt(source->peak->flux); // XXX a proxy: various functions sort by peak S/N
    306307
    307308        source->pixWeight = psMetadataLookupF32 (&status, row, "PSF_QF");
  • trunk/psModules/src/objects/pmSourceIO_MatchedRefs.c

    r26392 r26893  
    163163
    164164    // try find the MATCHED_REFS extension.  if non-existent, note that we tried, and move on.
     165    // It is not an error to lack this entry -- psFitsMoveExtNameClean does not raise an error
    165166    if (!psFitsMoveExtNameClean (fits, "MATCHED_REFS")) {
    166167        psMetadataAddBool (fpa->analysis, PS_LIST_TAIL, "READ.REFMATCH", PS_META_REPLACE, "attempted to read MATCHED_REFS", true);
  • trunk/psModules/src/objects/pmSourceMoments.c

    r25979 r26893  
    5454# define VALID_RADIUS(X,Y,RAD2) (((RAD2) >= (PS_SQR(X) + PS_SQR(Y))) ? 1 : 0)
    5555
    56 bool pmSourceMoments(pmSource *source, psF32 radius, psF32 sigma, psF32 minSN)
     56bool pmSourceMoments(pmSource *source, psF32 radius, psF32 sigma, psF32 minSN, psImageMaskType maskVal)
    5757{
    5858    PS_ASSERT_PTR_NON_NULL(source, false);
     
    114114        for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
    115115            if (vMsk) {
    116                 if (*vMsk) {
     116                if (*vMsk & maskVal) {
    117117                    vMsk++;
    118118                    continue;
     
    135135            // stars.
    136136            if (PS_SQR(pDiff) < minSN2*wDiff) continue;
    137             if (pDiff < 0) continue; // XXX : MWV says I should include < 0.0 valued points...
     137            // if (pDiff < 0) continue; // XXX : MWV says I should include < 0.0 valued points...
    138138
    139139            // Apply a Gaussian window function.  Be careful with the window function.  S/N
     
    226226        for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
    227227            if (vMsk) {
    228                 if (*vMsk) {
     228                if (*vMsk & maskVal) {
    229229                    vMsk++;
    230230                    continue;
     
    249249            // stars.
    250250            if (PS_SQR(pDiff) < minSN2*wDiff) continue;
    251             if (pDiff < 0) continue;
     251            // if (pDiff < 0) continue;
    252252
    253253            // Apply a Gaussian window function.  Be careful with the window function.  S/N
     
    315315    source->moments->Myyyy = YYYY/Sum;
    316316
    317     if (source->moments->Mxx < 0) {
    318         fprintf (stderr, "error: neg second moment??\n");
    319     }
    320     if (source->moments->Myy < 0) {
    321         fprintf (stderr, "error: neg second moment??\n");
    322     }
     317    // if (source->moments->Mxx < 0) {
     318    // fprintf (stderr, "error: neg second moment??\n");
     319    // }
     320    // if (source->moments->Myy < 0) {
     321    // fprintf (stderr, "error: neg second moment??\n");
     322    // }
    323323
    324324    psTrace ("psModules.objects", 4, "Mxx: %f  Mxy: %f  Myy: %f  Mxxx: %f  Mxxy: %f  Mxyy: %f  Myyy: %f  Mxxxx: %f  Mxxxy: %f  Mxxyy: %f  Mxyyy: %f  Mxyyy: %f\n",
  • trunk/psModules/src/objects/pmSourcePlotApResid.c

    r20937 r26893  
    3434#include "pmPSF.h"
    3535#include "pmModel.h"
     36#include "pmDetections.h"
    3637#include "pmSource.h"
    3738#include "pmSourcePlots.h"
     
    5354    PS_ASSERT_PTR_NON_NULL(layout, false);
    5455
     56    bool status;
    5557    Graphdata graphdata;
    5658    KapaSection section;
     
    6163    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
    6264
    63     psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
    64     if (sources == NULL)
    65         return false;
     65    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     66    if (detections == NULL) return false;
     67
     68    psArray *sources = detections->allSources;
     69    if (sources == NULL) return false;
    6670
    6771    int kapa = pmKapaOpen (false);
  • trunk/psModules/src/objects/pmSourcePlotMoments.c

    r20937 r26893  
    3737#include "pmPSF.h"
    3838#include "pmModel.h"
     39#include "pmDetections.h"
    3940#include "pmSource.h"
    4041#include "pmSourcePlots.h"
     
    5455    PS_ASSERT_PTR_NON_NULL(layout, false);
    5556
     57    bool status;
    5658    Graphdata graphdata;
    5759    KapaSection section;
     
    6264    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
    6365
    64     psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
    65     if (sources == NULL)
    66         return false;
     66    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     67    if (detections == NULL) return false;
     68
     69    psArray *sources = detections->allSources;
     70    if (sources == NULL) return false;
    6771
    6872    int kapa = pmKapaOpen (false);
  • trunk/psModules/src/objects/pmSourcePlotPSFModel.c

    r20937 r26893  
    3737#include "pmPSF.h"
    3838#include "pmModel.h"
     39#include "pmDetections.h"
    3940#include "pmSource.h"
    4041#include "pmSourcePlots.h"
     
    5657    PS_ASSERT_PTR_NON_NULL(layout, false);
    5758
     59    bool status;
    5860    Graphdata graphdata;
    5961    KapaSection section;
     
    6466    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
    6567
    66     psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
    67     if (sources == NULL)
    68         return false;
     68    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     69    if (detections == NULL) return false;
     70
     71    psArray *sources = detections->allSources;
     72    if (sources == NULL) return false;
    6973
    7074    int kapa = pmKapaOpen (false);
  • trunk/psModules/src/psmodules.h

    r26682 r26893  
    7979#include <pmRemnance.h>
    8080#include <pmPattern.h>
     81#include <pmPatternIO.h>
    8182
    8283// the following headers are from psModule:astrom
     
    9798#include <pmSubtractionStamps.h>
    9899#include <pmSubtractionKernels.h>
     100#include <pmSubtractionDeconvolve.h>
    99101#include <pmSubtractionAnalysis.h>
    100102#include <pmSubtractionMatch.h>
Note: See TracChangeset for help on using the changeset viewer.