IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 30622


Ignore:
Timestamp:
Feb 13, 2011, 12:19:53 PM (15 years ago)
Author:
eugene
Message:

some code reorganzation (move all types into pmSubtractionTypes.h); add stage to check on different mode and order options, choosing best based on chisq of subtraction; careful with the window and the kernel sizes: if the measured kron radius is too large, allow the window to grow as needed; use the 1st radial moment measured on the stamps to set the kernel scaling; remove some cruft from the code; calling program now passes in kernel scaling options to be used when 1st radial moment is measured; new function to update the kernel description string used for kernel I/O; update the kernel description after the spatial order and direction is chosen; need to carry the kernel fwhms and spatial order to allow for update; calculate the psf-match solution errors; psf solution now uses the weights to get sensible chisq values, window is used to calculate the moments; penalty scale is now adjusted to be consistent with sensible reduced chisq; improved visualizations; use range-limiter in SVD of 1e-10; calculate chisq and moments for the solution and use in the evaluation; split out the stamp selection / convolution steps; reject sources after fitting a model to the chisq assuming non-zero systematic error

Location:
trunk/psModules/src/imcombine
Files:
23 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/psModules/src/imcombine/Makefile.am

    r26893 r30622  
    3030        pmStackReject.h         \
    3131        pmSubtraction.h         \
     32        pmSubtractionTypes.h            \
    3233        pmSubtractionAnalysis.h \
    3334        pmSubtractionEquation.h \
  • trunk/psModules/src/imcombine/pmReadoutCombine.c

    r24907 r30622  
    3333    params->blank = 0;
    3434    params->nKeep = 0;
    35     params->fracHigh = 0.0;
     35    params->fracLow = 0.0;
    3636    params->fracHigh = 0.0;
    3737    params->iter = 1;
  • trunk/psModules/src/imcombine/pmStackReject.c

    r28405 r30622  
    77#include <pslib.h>
    88
     9#include "pmFPA.h"
     10#include "pmSubtractionTypes.h"
    911#include "pmSubtraction.h"
    1012#include "pmSubtractionThreads.h"
  • trunk/psModules/src/imcombine/pmSubtraction.c

    r29777 r30622  
    2020#include "pmHDU.h"                      // Required for pmFPA.h
    2121#include "pmFPA.h"
     22#include "pmSubtractionTypes.h"
    2223#include "pmSubtractionStamps.h"
    2324#include "pmSubtractionEquation.h"
    2425#include "pmSubtractionVisual.h"
    2526#include "pmSubtractionThreads.h"
     27
     28bool psFitsWriteImageSimple (char *filename, psImage *image, psMetadata *header);
    2629
    2730#include "pmSubtraction.h"
     
    773776
    774777    if (convolutions) {
    775         // Already done
    776778        return convolutions;
    777779    }
     
    787789}
    788790
     791
     792bool pmSubtractionConvolveStampThread(psThreadJob *job)
     793{
     794    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
     795
     796    pmSubtractionStamp *stamp = job->args->data[0]; // List of stamps
     797    pmSubtractionKernels *kernels = job->args->data[1]; // Kernels
     798    int footprint = PS_SCALAR_VALUE(job->args->data[2], S32); // Stamp index
     799
     800    return pmSubtractionConvolveStamp(stamp, kernels, footprint);
     801}
    789802
    790803bool pmSubtractionConvolveStamp (pmSubtractionStamp *stamp, pmSubtractionKernels *kernels, int footprint)
     
    818831    }
    819832
     833#ifdef TESTING
     834    for (int j = 0; j < kernels->num; j++) {
     835        if (stamp->convolutions1) {
     836            psString convName = NULL;
     837            psStringAppend(&convName, "conv1_%03d_%03d.fits", index, j);
     838            psFits *fits = psFitsOpen(convName, "w");
     839            psFree(convName);
     840            psKernel *conv = stamp->convolutions1->data[j];
     841            psFitsWriteImage(fits, NULL, conv->image, 0, NULL);
     842            psFitsClose(fits);
     843        }
     844
     845        if (stamp->convolutions2) {
     846            psString convName = NULL;
     847            psStringAppend(&convName, "conv2_%03d_%03d.fits", index, j);
     848            psFits *fits = psFitsOpen(convName, "w");
     849            psFree(convName);
     850            psKernel *conv = stamp->convolutions2->data[j];
     851            psFitsWriteImage(fits, NULL, conv->image, 0, NULL);
     852            psFitsClose(fits);
     853        }
     854    }
     855#endif
     856
    820857    return true;
    821858}
    822859
     860bool pmSubtractionConvolveStamps(pmSubtractionStampList *stamps, pmSubtractionKernels *kernels)
     861{
     862    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     863    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
     864
     865    psTimerStart("pmSubtractionConvolveStamps");
     866
     867    int footprint = stamps->footprint;  // Half-size of stamps
     868
     869    // We iterate over each stamp and generate the convolution if needed.  We do NOT need the
     870    // convolution if (a) it has already been calculated or (b) the stamp is not available for
     871    // use (available = USED or CALCULATE)
     872   
     873    for (int i = 0; i < stamps->num; i++) {
     874        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     875
     876        bool keep = false;
     877        keep |= (stamp->status == PM_SUBTRACTION_STAMP_USED);
     878        keep |= (stamp->status == PM_SUBTRACTION_STAMP_CALCULATE);
     879        if (!keep) continue;
     880
     881        bool haveConvolutions = false;
     882        if (kernels->mode == PM_SUBTRACTION_MODE_1) {
     883            haveConvolutions = (stamp->convolutions1 != NULL);
     884        }
     885        if (kernels->mode == PM_SUBTRACTION_MODE_2) {
     886            haveConvolutions = (stamp->convolutions2 != NULL);
     887        }
     888        if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
     889            haveConvolutions = (stamp->convolutions1 != NULL) && (stamp->convolutions2 != NULL);
     890        }
     891        if (haveConvolutions) {
     892            continue;
     893        }
     894
     895        if (pmSubtractionThreaded()) {
     896            psThreadJob *job = psThreadJobAlloc("PSMODULES_SUBTRACTION_CONVOLVE_STAMP");
     897            psArrayAdd(job->args, 1, stamp);
     898            psArrayAdd(job->args, 1, kernels);
     899            PS_ARRAY_ADD_SCALAR(job->args, footprint, PS_TYPE_S32);
     900            if (!psThreadJobAddPending(job)) {
     901                return false;
     902            }
     903        } else {
     904            pmSubtractionConvolveStamp(stamp, kernels, footprint);
     905        }
     906    }
     907    if (!psThreadPoolWait(true)) {
     908        psError(psErrorCodeLast(), false, "Error waiting for threads.");
     909        return false;
     910    }
     911    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolve stamps: %f sec", psTimerClear("pmSubtractionConvolveStamps"));
     912    return true;
     913}
    823914
    824915int pmSubtractionRejectStamps(pmSubtractionKernels *kernels, pmSubtractionStampList *stamps,
    825                               const psVector *deviations, psImage *subMask, float sigmaRej)
     916                              pmSubtractionQuality *match, psImage *subMask, float sigmaRej)
    826917{
    827918    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
    828919    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, -1);
    829     PS_ASSERT_VECTOR_NON_NULL(deviations, -1);
    830     PS_ASSERT_VECTOR_TYPE(deviations, PS_TYPE_F32, -1);
    831920    PS_ASSERT_IMAGE_NON_EMPTY(subMask, -1);
    832921    PS_ASSERT_IMAGE_TYPE(subMask, PS_TYPE_IMAGE_MASK, -1);
    833922
    834     // I used to measure the rms deviation about zero, and use that as the sigma against which to clip, but
    835     // the distribution is actually something like a chi^2 or Student's t, both of which become Gaussian-like
    836     // with large N.  Therefore, let's just treat this as a Gaussian distribution.
     923    // Comment from PAP (r18287): I used to measure the rms deviation about zero, and use that as the
     924    // sigma against which to clip, but the distribution is actually something like a chi^2 or
     925    // Student's t, both of which become Gaussian-like with large N.  Therefore, let's just
     926    // treat this as a Gaussian distribution.
     927
     928    // Comment from EAM (r29777): The residual distribution is only chisq-like if the model is
     929    // a good fit to the data.  In the (likely) case that there is a systematic difference
     930    // between the model and the data, the squared-residual distribution grows quadratically
     931    // with increasing flux: the systematic residual flux is a constant factor times the source
     932    // flux; the squared-residual is then of the form (k0 + k1*flux)^2, where k0 comes from the
     933    // Gaussian distributed residual and k1*flux is the systematic residual error.
     934
     935    // By rejecting sources with the largest squared-residuals, the rejection biases against
     936    // the brighter sources; in severe cases, this pushes the measurement to the weakest
     937    // sources with the most noise.  To account for this, let's fit a 2nd order polynomial to
     938    // the distribution of flux vs squared-residual, subtract that fit, and reject sources
     939    // which are significantly deviant from that distribution.
    837940
    838941    kernels->mean = NAN;
     
    840943    kernels->numStamps = -1;
    841944
    842     int numStamps = 0;                  // Number of used stamps
    843     psVector *mask = psVectorAlloc(stamps->num, PS_TYPE_VECTOR_MASK); // Mask, for statistics
    844     psVectorInit(mask, 0);
    845     for (int i = 0; i < stamps->num; i++) {
    846         pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    847         if (stamp->status != PM_SUBTRACTION_STAMP_USED) {
    848             mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0xff;
    849             continue;
    850         }
    851         numStamps++;
    852     }
    853     psTrace("psModules.imcombine", 1, "Number of good stamps: %d\n", numStamps);
    854 
    855     if (numStamps == 0) {
    856         psError(PM_ERR_STAMPS, true, "No good stamps found.");
    857         psFree(mask);
    858         return -1;
    859     }
    860 
    861     psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV |
    862                                   PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_QUARTILE); // Statistics for deviatns
    863     if (!psVectorStats(stats, deviations, NULL, mask, 0xff)) {
    864         psError(PM_ERR_DATA, false, "Unable to measure statistics for deviations.");
     945    psTrace("psModules.imcombine", 1, "Number of good stamps: %d\n", match->nGood);
     946
     947    // the chisq & flux vectors are calculated by pmSubtractionCalculateChisqAndMoments
     948
     949    // use 3hi/3lo sigma clipping on the chisq fit
     950    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
     951    stats->clipSigma = 5.0;
     952    stats->clipIter = 2;
     953    psPolynomial1D *model = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 2);
     954
     955    bool result = psVectorClipFitPolynomial1D(model, stats, match->stampMask, 0xff, match->chisq, NULL, match->fluxes);
     956    if (!result) {
     957        psError(PM_ERR_DATA, false, "Unable to measure statistics for deviations.");
     958        psFree(model);
    865959        psFree(stats);
    866         psFree(mask);
    867         return -1;
    868     }
    869     psFree(mask);
    870 
    871     // XXX raise an error?
     960        return -1;
     961    }
    872962    if (isnan(stats->sampleMean)) {
     963        psError(PM_ERR_DATA, false, "Unable to measure statistics for deviations.");
     964        psFree(model);
    873965        psFree(stats);
    874966        return -1;
    875967    }
    876968
    877     double mean, rms;                 // Mean and RMS of deviations
    878     if (numStamps < MIN_SAMPLE_STATS) {
    879         mean = stats->sampleMean;
    880         rms = stats->sampleStdev;
    881     } else {
    882         mean = stats->sampleMedian;
    883         rms = 0.74 * (stats->sampleUQ - stats->sampleLQ);
    884     }
    885     psFree(stats);
    886 
    887     psTrace("psModules.imcombine", 1, "Mean: %f\n", mean);
    888     psTrace("psModules.imcombine", 1, "RMS deviation: %f\n", rms);
    889 
    890     kernels->mean = mean;
    891     kernels->rms = rms;
    892     kernels->numStamps = numStamps;
    893 
    894     psLogMsg("psModules.imcombine", PS_LOG_INFO, "Mean deviation from %d stamps: %lf +/- %lf",
    895              numStamps, mean, rms);
    896 
    897     if (!isfinite(sigmaRej) || sigmaRej <= 0.0) {
    898         // User just wanted to calculate and record the deviation for posterity
    899         return 0;
    900     }
    901 
    902     float limit = sigmaRej * rms; // Limit on maximum deviation
    903     psTrace("psModules.imcombine", 1, "Deviation limit: %f\n", limit);
    904 
     969    kernels->mean = stats->sampleMean;
     970    kernels->rms = stats->sampleStdev;
     971    kernels->numStamps = stats->clippedNvalues;
     972
     973    psLogMsg ("pmPSFtry", 4, "chisq vs flux model: %e + %e flux + %e flux^2\n", model->coeff[0], model->coeff[1], model->coeff[2]);
     974    psLogMsg ("pmPSFtry", 4, "chisq vs flux resid: %f +/- %f\n", stats->sampleMean, stats->sampleStdev);
     975    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Mean deviation from %d stamps: %lf +/- %lf",  kernels->numStamps, kernels->mean, kernels->rms);
    905976
    906977    psString ds9name = NULL;            // Filename for ds9 region file
     
    914985    int numRejected = 0;                // Number of stamps rejected
    915986    int numGood = 0;                    // Number of good stamps
    916     double newMean = 0.0;               // New mean
    917987    psString log = NULL;                // Log message
    918     psStringAppend(&log, "Rejecting stamps, mean = %f, threshold = %f\n", mean, limit);
     988
     989    // save DS9 region files for the stamps and mark for rejection and replacement
    919990    for (int i = 0; i < stamps->num; i++) {
    920991        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    921         if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
     992        if (stamp->status  != PM_SUBTRACTION_STAMP_USED) { continue; }
     993        if (match->stampMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
    922994            // Should we reject stars with low deviation?  Well, if this is really a Gaussian-like
    923995            // distribution and they're low, then we have the right to ask why.  Isn't it suspicious that
     
    926998            // subtract well, in which case very few (if any) stars will be legitimately rejected for being
    927999            // low.
    928             if (fabsf(deviations->data.F32[i] - mean) > limit) {
    929                 // Mask out the stamp in the image so you it's not found again
    930                 psTrace("psModules.imcombine", 3, "Rejecting stamp %d (%d,%d)\n", i,
    931                         (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
    932                 psStringAppend(&log, "Stamp %d (%d,%d): %f\n", i,
    933                                (int)(stamp->x - 0.5), (int)(stamp->y - 0.5),
    934                                fabsf(deviations->data.F32[i] - mean));
    935                 numRejected++;
    936                 for (int y = stamp->y - footprint; y <= stamp->y + footprint; y++) {
    937                     for (int x = stamp->x - footprint; x <= stamp->x + footprint; x++) {
    938                         subMask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= PM_SUBTRACTION_MASK_REJ;
    939                     }
    940                 }
    941                 pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red");
    942 
    943                 // Set stamp for replacement
    944                 stamp->x = 0;
    945                 stamp->y = 0;
    946                 stamp->xNorm = NAN;
    947                 stamp->yNorm = NAN;
    948                 stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
    949                 // Recalculate convolutions
    950                 psFree(stamp->convolutions1);
    951                 psFree(stamp->convolutions2);
    952                 stamp->convolutions1 = stamp->convolutions2 = NULL;
    953                 psFree(stamp->image1);
    954                 psFree(stamp->image2);
    955                 psFree(stamp->weight);
    956                 stamp->image1 = stamp->image2 = stamp->weight = NULL;
    957                 psFree(stamp->matrix);
    958                 stamp->matrix = NULL;
    959                 psFree(stamp->vector);
    960                 stamp->vector = NULL;
    961             } else {
    962                 numGood++;
    963                 newMean += deviations->data.F32[i];
    964                 pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
    965             }
    966         }
    967     }
    968     newMean /= numGood;
     1000            psTrace("psModules.imcombine", 3, "Rejecting stamp %d (%d,%d)\n", i,
     1001                    (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
     1002            psStringAppend(&log, "Stamp %d (%d,%d): %f : %f : %f\n",
     1003                           i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5),
     1004                           match->chisq->data.F32[i], match->fluxes->data.F32[i], match->chisq->data.F32[i] - psPolynomial1DEval(model, match->fluxes->data.F32[i]));
     1005            numRejected++;
     1006            for (int y = stamp->y - footprint; y <= stamp->y + footprint; y++) {
     1007                for (int x = stamp->x - footprint; x <= stamp->x + footprint; x++) {
     1008                    subMask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= PM_SUBTRACTION_MASK_REJ;
     1009                }
     1010            }
     1011            pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red");
     1012
     1013            // Set stamp for replacement
     1014            stamp->x = 0;
     1015            stamp->y = 0;
     1016            stamp->xNorm = NAN;
     1017            stamp->yNorm = NAN;
     1018            stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
     1019            // Recalculate convolutions
     1020            psFree(stamp->convolutions1);
     1021            psFree(stamp->convolutions2);
     1022            stamp->convolutions1 = stamp->convolutions2 = NULL;
     1023            psFree(stamp->image1);
     1024            psFree(stamp->image2);
     1025            psFree(stamp->weight);
     1026            stamp->image1 = stamp->image2 = stamp->weight = NULL;
     1027            psFree(stamp->matrix);
     1028            stamp->matrix = NULL;
     1029            psFree(stamp->vector);
     1030            stamp->vector = NULL;
     1031        } else {
     1032            numGood++;
     1033            pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
     1034        }
     1035    }
    9691036
    9701037    if (numRejected == 0) {
     
    9781045    }
    9791046
     1047    psFree(model);
     1048    psFree(stats);
     1049
    9801050    if (numRejected > 0) {
    981         psLogMsg("psModules.imcombine", PS_LOG_INFO,
    982                  "%d good stamps; %d rejected.\nMean deviation: %lf --> %lf\n",
    983                  numGood, numRejected, mean, newMean);
     1051        psLogMsg("psModules.imcombine", PS_LOG_INFO, "%d good stamps; %d rejected.\n", numGood, numRejected);
    9841052    } else {
    985         psLogMsg("psModules.imcombine", PS_LOG_INFO,
    986                  "%d good stamps; 0 rejected.\nMean deviation: %lf\n",
    987                  numGood, mean);
     1053        psLogMsg("psModules.imcombine", PS_LOG_INFO, "%d good stamps; 0 rejected.\n", numGood);
    9881054    }
    9891055
     
    13731439    psFree(kernelErr2);
    13741440
     1441    static int nOut1 = 0;
     1442    static int nOut2 = 0;
     1443
    13751444    // Calculate covariances
    13761445    // This can be fairly involved, so we only do it for a small number of instances
     
    13861455                psKernelTruncate(kernel, covarFrac);
    13871456                covars->data[i] = psImageCovarianceCalculate(kernel, ro1->covariance);
     1457                if (0) {
     1458                    char name[128];
     1459                    snprintf (name, 128, "covar.sample1.%03d.fits", nOut1);
     1460                    psKernel *cov = covars->data[i];
     1461                    psFitsWriteImageSimple (name, cov->image, NULL);
     1462
     1463                    snprintf (name, 128, "incovar.sample1.%03d.fits", nOut1);
     1464                    psFitsWriteImageSimple (name, ro1->covariance->image, NULL);
     1465
     1466                    snprintf (name, 128, "conv.sample1.%03d.fits", nOut1);
     1467                    psFitsWriteImageSimple (name, kernel->image, NULL);
     1468
     1469                    fprintf (stderr, "incov: %d,%d; kern: %d,%d, outcov: %d,%d\n",
     1470                             ro1->covariance->image->numCols, ro1->covariance->image->numRows,
     1471                             kernel->image->numCols, kernel->image->numRows,
     1472                             cov->image->numCols, cov->image->numRows);
     1473
     1474                    nOut1 ++;
     1475                }
    13881476                psFree(kernel);
    13891477            }
     
    14071495                psKernelTruncate(kernel, covarFrac);
    14081496                covars->data[i] = psImageCovarianceCalculate(kernel, ro2->covariance);
     1497                if (0) {
     1498                    char name[128];
     1499                    snprintf (name, 128, "covar.sample2.%03d.fits", nOut2);
     1500                    psKernel *cov = covars->data[i];
     1501                    psFitsWriteImageSimple (name, cov->image, NULL);
     1502
     1503                    snprintf (name, 128, "incovar.sample2.%03d.fits", nOut2);
     1504                    psFitsWriteImageSimple (name, ro2->covariance->image, NULL);
     1505
     1506                    snprintf (name, 128, "conv.sample2.%03d.fits", nOut2);
     1507                    psFitsWriteImageSimple (name, kernel->image, NULL);
     1508
     1509                    fprintf (stderr, "incov: %d,%d; kern: %d,%d, outcov: %d,%d\n",
     1510                             ro2->covariance->image->numCols, ro2->covariance->image->numRows,
     1511                             kernel->image->numCols, kernel->image->numRows,
     1512                             cov->image->numCols, cov->image->numRows);
     1513
     1514                    nOut2 ++;
     1515                }
    14091516                psFree(kernel);
    14101517            }
     
    14221529    psImageCovarianceSetThreads(oldThreads);
    14231530
    1424     // Copy anything that wasn't convolved
     1531    // Copy anything that wasn't convolved (they may have been allocated though, so free them)
    14251532    switch (kernels->mode) {
    14261533      case PM_SUBTRACTION_MODE_1:
    14271534        if (out2) {
     1535            psFree(out2->image);
     1536            psFree(out2->variance);
     1537            psFree(out2->mask);
     1538            psFree(out2->covariance);
    14281539            out2->image = psMemIncrRefCounter(ro2->image);
    14291540            out2->variance = psMemIncrRefCounter(ro2->variance);
     
    14341545      case PM_SUBTRACTION_MODE_2:
    14351546        if (out1) {
     1547            psFree(out1->image);
     1548            psFree(out1->variance);
     1549            psFree(out1->mask);
     1550            psFree(out1->covariance);
    14361551            out1->image = psMemIncrRefCounter(ro1->image);
    14371552            out1->variance = psMemIncrRefCounter(ro1->variance);
     
    14791594  return true;
    14801595}
     1596
     1597static void pmSubtractionQualityFree(pmSubtractionQuality *quality) {
     1598
     1599    psFree (quality->fluxes);
     1600    psFree (quality->chisq);
     1601    psFree (quality->moments);
     1602    psFree (quality->stampMask);
     1603}   
     1604
     1605pmSubtractionQuality *pmSubtractionQualityAlloc() {
     1606
     1607    pmSubtractionQuality *quality = psAlloc(sizeof(pmSubtractionQuality)); // Stamp list to return
     1608    psMemSetDeallocator(quality, (psFreeFunc)pmSubtractionQualityFree);
     1609
     1610    quality->fluxes = NULL;
     1611    quality->chisq = NULL;
     1612    quality->moments = NULL;
     1613    quality->stampMask = NULL;
     1614
     1615    quality->score = NAN;
     1616    quality->mode = PM_SUBTRACTION_MODE_ERR;
     1617    quality->spatialOrder = -1;
     1618    quality->nGood = 0;
     1619   
     1620    return quality;
     1621}
  • trunk/psModules/src/imcombine/pmSubtraction.h

    r29601 r30622  
    1414#define PM_SUBTRACTION_H
    1515
    16 #include <pslib.h>
    17 
    18 #include <pmHDU.h>
    19 #include <pmFPA.h>
    20 #include <pmSubtractionKernels.h>
    21 #include <pmSubtractionStamps.h>
     16// #include <pslib.h>
     17// #include <pmHDU.h>
     18// #include <pmFPA.h>
     19// #include <pmSubtractionKernels.h>
     20// #include <pmSubtractionStamps.h>
    2221
    2322// if we use the original ppSub implementation, we subtract a central delta-function for all
     
    3029/// @addtogroup imcombine Image Combinations
    3130/// @{
    32 
    33 /// Mask values for the subtraction mask
    34 typedef enum {
    35     PM_SUBTRACTION_MASK_CLEAR          = 0x00, // No masking
    36     PM_SUBTRACTION_MASK_BAD_1          = 0x01, // Image 1 is bad
    37     PM_SUBTRACTION_MASK_BAD_2          = 0x02, // Image 2 is bad
    38     PM_SUBTRACTION_MASK_CONVOLVE_1     = 0x04, // If image 1 is convolved, would be poor or bad
    39     PM_SUBTRACTION_MASK_CONVOLVE_2     = 0x08, // If image 2 is convolved, would be poor or bad
    40     PM_SUBTRACTION_MASK_CONVOLVE_BAD_1 = 0x10, // If image 1 is convolved, would be bad
    41     PM_SUBTRACTION_MASK_CONVOLVE_BAD_2 = 0x20, // If image 2 is convolved, would be bad
    42     PM_SUBTRACTION_MASK_BORDER         = 0x40, // Image border
    43     PM_SUBTRACTION_MASK_REJ            = 0x80, // Previously tried as a stamp, and rejected
    44 } pmSubtractionMasks;
    45 
    4631
    4732/// Number of terms in a polynomial
     
    7055    );
    7156
     57bool pmSubtractionConvolveStamps(pmSubtractionStampList *stamps, pmSubtractionKernels *kernels);
     58
     59bool pmSubtractionConvolveStampThread(psThreadJob *job);
     60
    7261/// Reject stamps
    7362int pmSubtractionRejectStamps(pmSubtractionKernels *kernels, ///< Kernel parameters to update
    7463                              pmSubtractionStampList *stamps, ///< Stamps
    75                               const psVector *deviations, ///< Deviations for each stamp
     64                              pmSubtractionQuality *match, ///< data on the subtraction quality
    7665                              psImage *subMask, ///< Subtraction mask
    7766                              float sigmaRej ///< Number of RMS deviations above zero at which to reject
     
    167156bool pmSubtractionSetFWHMs(float fwhm1, float fwhm2);
    168157
     158pmSubtractionQuality *pmSubtractionQualityAlloc();
     159
    169160/// @}
    170161#endif
  • trunk/psModules/src/imcombine/pmSubtractionAnalysis.c

    r29595 r30622  
    77
    88#include "pmFPA.h"
     9#include "pmSubtractionTypes.h"
    910#include "pmSubtraction.h"
    1011#include "pmSubtractionKernels.h"
  • trunk/psModules/src/imcombine/pmSubtractionDeconvolve.c

    r26893 r30622  
    1010
    1111#include "pmFPA.h"
     12#include "pmSubtractionTypes.h"
    1213#include "pmSubtractionKernels.h"
    1314#include "pmSubtractionDeconvolve.h"
  • trunk/psModules/src/imcombine/pmSubtractionEquation.c

    r29598 r30622  
    88
    99#include "pmErrorCodes.h"
     10#include "pmVisual.h"
     11#include "pmFPA.h"
     12#include "pmSubtractionTypes.h"
    1013#include "pmSubtraction.h"
    1114#include "pmSubtractionKernels.h"
     
    1619#include "pmSubtractionVisual.h"
    1720
    18 //#define TESTING                         // TESTING output for debugging; may not work with threads!
    19 
    20 //#define USE_WEIGHT                      // Include weight (1/variance) in equation?
    21 
    22 // XXX TEST:
    23 //# define USE_WINDOW                      // window to avoid neighbor contamination
     21//# define TESTING                         // TESTING output for debugging; may not work with threads!
     22# define USE_WEIGHT                      // Include weight (1/variance) in equation?
     23# define USE_WINDOW                      // window to avoid neighbor contamination
     24
     25/* I believe we want to apply the WEIGHT to the chisq portions of the calculation (but not the WINDOW),
     26 * and the WINDOW to the moments portiosn of the calculations (but not the WEIGHT)
     27 *
     28 */
    2429
    2530# define PENALTY false
    2631# define MOMENTS (!PENALTY)
    27 # define MOMENTS_PENALTY_SCALE 2e-5 // XXX this value is not completely arbitrary, but I don't understand why it needs to be this value...
     32# define MOMENTS_PENALTY_SCALE 20 // up-weight the moments somewhat
    2833
    2934//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    107112                        cc *= weight->kernel[y][x];
    108113                    }
    109                     if (window) {
     114                    // XXX NOTE: do NOT apply the window to the chisq portions of the calculation
     115                    if (false && window) {
    110116                        cc *= window->kernel[y][x];
    111117                    }
     
    138144                    rc *= wtVal;
    139145                }
    140                 if (window) {
     146                // XXX NOTE: do NOT apply the window to the chisq portions of the calculation
     147                if (false && window) {
    141148                    float winVal = window->kernel[y][x];
    142149                    ic *= winVal;
     
    173180}
    174181
     182# define RENORM_BY_FLUX 0
    175183
    176184// Calculate the least-squares matrix and vector for dual convolution
     
    268276            for (int y = - footprint; y <= footprint; y++) {
    269277                for (int x = - footprint; x <= footprint; x++) {
     278
     279                    // XXX NOTE: clipping low S/N pixels does not seem to work very well
     280                    if (false && weight) {
     281                        float i1 = image1->kernel[y][x];
     282                        float i2 = image2->kernel[y][x];
     283                        float sn = (i1 + i2) / sqrt (weight->kernel[y][x]);
     284                        if (sn < 0.5) continue;
     285                    }
     286
    270287                    double aa = iConv1->kernel[y][x] * jConv1->kernel[y][x] * PS_SQR(normValue);
    271288                    double bb = iConv2->kernel[y][x] * jConv2->kernel[y][x];
    272289                    double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x] * normValue;
    273                     if (weight) {
    274                         float wtVal = weight->kernel[y][x];
    275                         aa *= wtVal;
    276                         bb *= wtVal;
    277                         ab *= wtVal;
    278                     }
    279                     if (window) {
    280                         float wtVal = window->kernel[y][x];
    281                         aa *= wtVal;
    282                         bb *= wtVal;
    283                         ab *= wtVal;
    284                     }
    285                     sumAA += aa;
    286                     sumBB += bb;
    287                     sumAB += ab;
     290
     291                    float wtVal = (weight) ? weight->kernel[y][x] : 1.0;
     292                    sumAA += wtVal*aa;
     293                    sumBB += wtVal*bb;
     294                    sumAB += wtVal*ab;
    288295
    289296                    if (MOMENTS) {
    290                         MxxAA += x*x*aa;
    291                         MyyAA += y*y*aa;
    292                         MxxBB += x*x*bb;
    293                         MyyBB += y*y*bb;
     297                        float winVal = (window) ? window->kernel[y][x] : 1.0;
     298                        MxxAA += winVal*x*x*aa;
     299                        MyyAA += winVal*y*y*aa;
     300                        MxxBB += winVal*x*x*bb;
     301                        MyyBB += winVal*y*y*bb;
    294302                    }
    295303                }
    296304            }
    297305
     306            // XXX does normSquare1,2 mess up the relative scaling?
     307            // XXX no: normSquare1,2 is the sum of the flux^2 for the source
    298308            if (MOMENTS) {
    299309                MxxAA /= stamp->normSquare1 * PS_SQR(normValue);
     
    305315            // XXX this makes the Chisq portion independent of the normalization and star flux
    306316            // but may be mis-scaling between stars of different fluxes
     317# if (RENORM_BY_FLUX)       
    307318            sumAA /= PS_SQR(stamp->normI2);
    308319            sumAB /= PS_SQR(stamp->normI2);
    309320            sumBB /= PS_SQR(stamp->normI2);
     321# endif
    310322
    311323            // fprintf (stderr, "i,j : %d %d : M(xx,yy)(AA,BB) : %f %f %f %f\n", i, j, MxxAA, MyyAA, MxxBB, MyyBB);
     
    328340                        matrix->data.F64[iIndex][jIndex] += kernels->penalty * MxxAA * MOMENTS_PENALTY_SCALE;
    329341                        matrix->data.F64[iIndex][jIndex] += kernels->penalty * MyyAA * MOMENTS_PENALTY_SCALE;
    330 
    331342                        matrix->data.F64[jIndex][iIndex] += kernels->penalty * MxxAA * MOMENTS_PENALTY_SCALE;
    332343                        matrix->data.F64[jIndex][iIndex] += kernels->penalty * MyyAA * MOMENTS_PENALTY_SCALE;
     
    334345                        matrix->data.F64[iIndex + numParams][jIndex + numParams] += kernels->penalty * MxxBB * MOMENTS_PENALTY_SCALE;
    335346                        matrix->data.F64[iIndex + numParams][jIndex + numParams] += kernels->penalty * MyyBB * MOMENTS_PENALTY_SCALE;
    336 
    337347                        matrix->data.F64[jIndex + numParams][iIndex + numParams] += kernels->penalty * MxxBB * MOMENTS_PENALTY_SCALE;
    338348                        matrix->data.F64[jIndex + numParams][iIndex + numParams] += kernels->penalty * MyyBB * MOMENTS_PENALTY_SCALE;
     
    354364                        ab *= weight->kernel[y][x];
    355365                    }
    356                     if (window) {
     366                    // XXX NOTE: do NOT apply the window to the chisq portions of the calculation
     367                    if (false && window) {
    357368                        ab *= window->kernel[y][x];
    358369                    }
     
    363374            // XXX this makes the Chisq portion independent of the normalization and star flux
    364375            // but may be mis-scaling between stars of different fluxes
     376# if (RENORM_BY_FLUX)
    365377            sumAB /= PS_SQR(stamp->normI2);
     378# endif
    366379
    367380            // Spatial variation of kernel coeffs
     
    391404                float i2 = image2->kernel[y][x];
    392405
     406                // XXX NOTE: clipping low S/N pixels does not seem to work very well
     407                if (false && weight) {
     408                    float sn = (i1 + i2) / sqrt (weight->kernel[y][x]);
     409                    if (sn < 0.5) continue;
     410                }
     411
    393412                double ai2 = a * i2 * normValue;
    394413                double bi2 = b * i2;
     
    396415                double bi1 = b * i1 * normValue;
    397416
    398                 if (weight) {
    399                     float wtVal = weight->kernel[y][x];
    400                     ai2 *= wtVal;
    401                     bi2 *= wtVal;
    402                     ai1 *= wtVal;
    403                     bi1 *= wtVal;
    404                 }
    405                 if (window) {
    406                     float wtVal = window->kernel[y][x];
    407                     ai2 *= wtVal;
    408                     bi2 *= wtVal;
    409                     ai1 *= wtVal;
    410                     bi1 *= wtVal;
    411                 }
    412                 sumAI2 += ai2;
    413                 sumBI2 += bi2;
    414                 sumAI1 += ai1;
    415                 sumBI1 += bi1;
     417                float wtVal = (weight) ? weight->kernel[y][x] : 1.0;
     418                sumAI2 += wtVal*ai2;
     419                sumBI2 += wtVal*bi2;
     420                sumAI1 += wtVal*ai1;
     421                sumBI1 += wtVal*bi1;
    416422
    417423                if (MOMENTS) {
    418                     MxxAI1 += x*x*ai1;
    419                     MyyAI1 += y*y*ai1;
    420                     MxxBI2 += x*x*bi2;
    421                     MyyBI2 += y*y*bi2;
     424                    float winVal = (window) ? window->kernel[y][x] : 1.0;
     425                    MxxAI1 += winVal*x*x*ai1;
     426                    MyyAI1 += winVal*y*y*ai1;
     427                    MxxBI2 += winVal*x*x*bi2;
     428                    MyyBI2 += winVal*y*y*bi2;
    422429                }
    423430            }
     
    435442        // XXX this makes the Chisq portion independent of the normalization and star flux
    436443        // but may be mis-scaling between stars of different fluxes
     444# if (RENORM_BY_FLUX)
    437445        sumAI1 /= PS_SQR(stamp->normI2);
    438446        sumBI1 /= PS_SQR(stamp->normI2);
    439447        sumAI2 /= PS_SQR(stamp->normI2);
    440448        sumBI2 /= PS_SQR(stamp->normI2);
     449# endif
    441450
    442451        // Spatial variation
     
    788797    stamp->normSquare2 = normSquare2;
    789798
    790     psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "normValue: %f %f %f  (%f %f)\n", normI1, normI2, stamp->norm, normSquare1, normSquare2);
     799    // psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "normValue: %f %f %f  (%f %f)\n", normI1, normI2, stamp->norm, normSquare1, normSquare2);
    791800
    792801    return true;
     
    800809    pmSubtractionKernels *kernels = job->args->data[1]; // Kernels
    801810    int index = PS_SCALAR_VALUE(job->args->data[2], S32); // Stamp index
    802     pmSubtractionEquationCalculationMode mode  = PS_SCALAR_VALUE(job->args->data[3], S32); // calculation model
    803 
    804     return pmSubtractionCalculateEquationStamp(stamps, kernels, index, mode);
    805 }
    806 
    807 bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, pmSubtractionKernels *kernels,
    808                                          int index, const pmSubtractionEquationCalculationMode mode)
     811
     812    return pmSubtractionCalculateEquationStamp(stamps, kernels, index);
     813}
     814
     815bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, pmSubtractionKernels *kernels, int index)
    809816{
    810817    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    833840    psAssert(stamp->status == PM_SUBTRACTION_STAMP_CALCULATE, "We only operate on stamps with this state.");
    834841
    835     // Generate convolutions: these are generated once and saved
    836     if (!pmSubtractionConvolveStamp(stamp, kernels, footprint)) {
    837         psError(psErrorCodeLast(), false, "Unable to convolve stamp %d.", index);
    838         return NULL;
    839     }
    840 
    841 #ifdef TESTING
    842     for (int j = 0; j < numKernels; j++) {
    843         if (stamp->convolutions1) {
    844             psString convName = NULL;
    845             psStringAppend(&convName, "conv1_%03d_%03d.fits", index, j);
    846             psFits *fits = psFitsOpen(convName, "w");
    847             psFree(convName);
    848             psKernel *conv = stamp->convolutions1->data[j];
    849             psFitsWriteImage(fits, NULL, conv->image, 0, NULL);
    850             psFitsClose(fits);
    851         }
    852 
    853         if (stamp->convolutions2) {
    854             psString convName = NULL;
    855             psStringAppend(&convName, "conv2_%03d_%03d.fits", index, j);
    856             psFits *fits = psFitsOpen(convName, "w");
    857             psFree(convName);
    858             psKernel *conv = stamp->convolutions2->data[j];
    859             psFitsWriteImage(fits, NULL, conv->image, 0, NULL);
    860             psFitsClose(fits);
    861         }
    862     }
    863 #endif
    864 
    865     // XXX visualize the set of convolved stamps
    866 
    867     psImage *polyValues = p_pmSubtractionPolynomial(NULL, spatialOrder,
    868                                                     stamp->xNorm, stamp->yNorm); // Polynomial terms
    869 
    870     bool new = stamp->vector ? false : true; // Is this a new run?
     842    psImage *polyValues = p_pmSubtractionPolynomial(NULL, spatialOrder, stamp->xNorm, stamp->yNorm); // Polynomial terms
     843
     844    // Is this a new run? Have we allocated the correct sized vector/matrix?
     845    bool new = stamp->vector ? false : true;
     846    if (!new && (stamp->vector->n != numParams)) {
     847        psFree (stamp->vector);
     848        psFree (stamp->matrix);
     849        new = true;
     850    }
     851
    871852    if (new) {
    872853        stamp->matrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
     
    941922}
    942923
    943 bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, pmSubtractionKernels *kernels,
    944                                     const pmSubtractionEquationCalculationMode mode)
     924bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, pmSubtractionKernels *kernels)
    945925{
    946926    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    953933    for (int i = 0; i < stamps->num; i++) {
    954934        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     935
    955936        if (stamp->status != PM_SUBTRACTION_STAMP_CALCULATE) {
    956937            continue;
     
    969950            psArrayAdd(job->args, 1, (pmSubtractionKernels*)kernels); // Casting away const to put on array
    970951            PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
    971             PS_ARRAY_ADD_SCALAR(job->args, mode, PS_TYPE_S32);
    972952            if (!psThreadJobAddPending(job)) {
    973953                return false;
    974954            }
    975955        } else {
    976             pmSubtractionCalculateEquationStamp(stamps, kernels, i, mode);
     956            pmSubtractionCalculateEquationStamp(stamps, kernels, i);
    977957        }
    978958    }
     
    983963    }
    984964
    985     pmSubtractionVisualPlotLeastSquares(stamps);
    986965    pmSubtractionVisualShowKernels((pmSubtractionKernels  *)kernels);
    987966    pmSubtractionVisualShowBasis(stamps);
    988 
    989     psLogMsg("psModules.imcombine", PS_LOG_INFO, "Calculate equation: %f sec",
    990              psTimerClear("pmSubtractionCalculateEquation"));
    991 
     967    pmSubtractionVisualPlotLeastSquares(stamps);
     968
     969    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Calculate equation: %f sec", psTimerClear("pmSubtractionCalculateEquation"));
    992970
    993971    return true;
     
    998976bool psFitsWriteImageSimple (char *filename, psImage *image, psMetadata *header);
    999977
    1000 psImage *p_pmSubSolve_wUt (psVector *w, psImage *U);
    1001 psImage *p_pmSubSolve_VwUt (psImage *V, psImage *wUt);
    1002 
    1003 bool p_pmSubSolve_SetWeights (psVector *wApply, psVector *w, psVector *wMask);
    1004 
    1005 bool p_pmSubSolve_UtB (psVector **UtB, psImage *U, psVector *B);
    1006 bool p_pmSubSolve_wUtB (psVector **wUtB, psVector *w, psVector *UtB);
    1007 bool p_pmSubSolve_VwUtB (psVector **VwUtB, psImage *V, psVector *wUtB);
    1008 
    1009 bool p_pmSubSolve_Ax (psVector **B, psImage *A, psVector *x);
    1010 bool p_pmSubSolve_VdV (double *value, psVector *x, psVector *y);
    1011 bool p_pmSubSolve_y2 (double *y2, pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps);
    1012 
    1013 psImage *p_pmSubSolve_Xvar (psImage *V, psVector *w);
    1014 
    1015 double p_pmSubSolve_ChiSquare (pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps);
    1016 
    1017 bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels,
    1018                                 const pmSubtractionStampList *stamps,
    1019                                 const pmSubtractionEquationCalculationMode mode)
     978bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps)
    1020979{
    1021980    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
    1022981    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     982
     983    psTimerStart("pmSubtractionSolveEquation");
    1023984
    1024985    // Check inputs
     
    10421003        }
    10431004
     1005        if (stamp->vector->n != numParams) {
     1006            fprintf (stderr, "mismatch length\n");
     1007        }
    10441008        PS_ASSERT_VECTOR_NON_NULL(stamp->vector, false);
    10451009        PS_ASSERT_VECTOR_SIZE(stamp->vector, (long)numParams, false);
     
    10801044        }
    10811045
     1046        pmSubtractionVisualPlotLeastSquaresResid(stamps, sumMatrix, numStamps);
     1047
    10821048#if 0
    10831049        psImage *save = psImageCopy(NULL, sumMatrix, PS_TYPE_F32);
     
    10871053#endif
    10881054
     1055        // XXX TEST : print the matrix & vector
     1056        if (0) {
     1057            for (int iy = 0; iy < sumMatrix->numRows; iy++) {
     1058                for (int ix = 0; ix < sumMatrix->numCols; ix++) {
     1059                    fprintf (stderr, "%e  ", sumMatrix->data.F64[iy][ix]);
     1060                }
     1061                fprintf (stderr, " : %e\n", sumVector->data.F64[iy]);
     1062            }
     1063        }
     1064
     1065        psImage *invMatrix = NULL;
    10891066        psVector *solution = NULL;                       // Solution to equation!
    10901067        solution = psVectorAlloc(numParams, PS_TYPE_F64);
     
    10941071        // solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
    10951072        // SINGLE solution
    1096         if (1) {
    1097             solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
    1098         } else {
    1099             psVector *PERM = NULL;
    1100             psImage *LU = psMatrixLUDecomposition(NULL, &PERM, sumMatrix);
    1101             solution = psMatrixLUSolution(solution, LU, sumVector, PERM);
    1102             psFree (LU);
    1103             psFree (PERM);
    1104         }
     1073# if (1)
     1074        solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, 1e-10);
     1075        invMatrix = psMatrixInvert(NULL, sumMatrix, NULL);
     1076# endif
     1077# if (0)
     1078        psMatrixLUSolve(sumMatrixLU, sumVector);
     1079        solution = psMemIncrRefCounter(sumVector);
     1080        invMatrix = psMemIncrRefCounter(sumMatrix);
     1081# endif
     1082# if (0)
     1083        psMatrixGJSolve(sumMatrix, sumVector);
     1084        invMatrix = psMemIncrRefCounter(sumMatrix);
     1085        solution = psMemIncrRefCounter(sumVector);
     1086# endif
    11051087
    11061088# if (0)
    11071089        for (int i = 0; i < solution->n; i++) {
    1108             psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "Single solution %d: %lf\n", i, solution->data.F64[i]);
     1090            psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "Single solution %d: %lf +/- %lf\n", i, solution->data.F64[i], sqrt(fabs(invMatrix->data.F64[i][i])));
    11091091        }
    11101092# endif
    11111093
    1112         if (!kernels->solution1) {
    1113             kernels->solution1 = psVectorAlloc(sumVector->n + 2, PS_TYPE_F64); // 1 for norm, 1 for bg
    1114             psVectorInit(kernels->solution1, 0.0);
    1115         }
     1094        // ensure we have a solution vector of the right size
     1095        kernels->solution1    = psVectorRecycle(kernels->solution1,    sumVector->n + 2, PS_TYPE_F64); // 1 for norm, 1 for bg
     1096        kernels->solution1err = psVectorRecycle(kernels->solution1err, sumVector->n + 2, PS_TYPE_F64); // 1 for norm, 1 for bg
     1097        psVectorInit(kernels->solution1, 0.0);
     1098        psVectorInit(kernels->solution1err, 0.0);
    11161099
    11171100        int numKernels = kernels->num;
    11181101        int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
    11191102        int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
     1103
    11201104        for (int i = 0; i < numKernels * numPoly; i++) {
    11211105            kernels->solution1->data.F64[i] = solution->data.F64[i];
     1106            kernels->solution1err->data.F64[i] = sqrt(invMatrix->data.F64[i][i]);
    11221107        }
    11231108
     
    11311116        psFree(sumVector);
    11321117        psFree(sumMatrix);
     1118        psFree(invMatrix);
    11331119
    11341120    } else {
     
    11601146        }
    11611147
     1148        pmSubtractionVisualPlotLeastSquaresResid(stamps, sumMatrix, numStamps);
     1149
    11621150#if 0
    11631151        psImage *save = psImageCopy(NULL, sumMatrix, PS_TYPE_F32);
     
    11711159        }
    11721160
     1161        // XXX TEST : print the matrix & vector
     1162        if (0) {
     1163            for (int iy = 0; iy < sumMatrix->numRows; iy++) {
     1164                for (int ix = 0; ix < sumMatrix->numCols; ix++) {
     1165                    fprintf (stderr, "%e  ", sumMatrix->data.F64[iy][ix]);
     1166                }
     1167                fprintf (stderr, " : %e\n", sumVector->data.F64[iy]);
     1168            }
     1169        }
     1170
     1171        psImage *invMatrix = NULL;
    11731172        psVector *solution = NULL;                       // Solution to equation!
    11741173        solution = psVectorAlloc(numParams, PS_TYPE_F64);
     
    11761175
    11771176        // DUAL solution
    1178         solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1177# if (1)
     1178        solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, 1e-10);
     1179        invMatrix = psMatrixInvert(NULL, sumMatrix, NULL);
     1180# endif
     1181# if (0)
     1182        psMatrixLUSolve(sumMatrix, sumVector);
     1183        solution = psMemIncrRefCounter(sumVector);
     1184        invMatrix = psMemIncrRefCounter(sumMatrix);
     1185# endif
    11791186
    11801187#if (0)
    11811188        for (int i = 0; i < solution->n; i++) {
    1182             fprintf(stderr, "Dual solution %d: %lf\n", i, solution->data.F64[i]);
     1189            fprintf(stderr, "Dual solution %d: %lf +/- %lf\n", i, solution->data.F64[i], sqrt(invMatrix->data.F64[i][i]));
    11831190        }
    11841191#endif
    11851192
    1186         if (!kernels->solution1) {
    1187             kernels->solution1 = psVectorAlloc(numSolution1 + 2, PS_TYPE_F64);
    1188             psVectorInit (kernels->solution1, 0.0);
    1189         }
    1190         if (!kernels->solution2) {
    1191             kernels->solution2 = psVectorAlloc(numSolution2, PS_TYPE_F64);
    1192             psVectorInit (kernels->solution2, 0.0);
    1193         }
     1193        // XXX TEST: manually set the coeffs to a desired solution
     1194        // solution->data.F64[0] = +1.826;
     1195        // solution->data.F64[1] = -0.115;
     1196        // solution->data.F64[2] =  0.0;
     1197        // solution->data.F64[3] =  0.0;
     1198
     1199        // ensure we have solution vectors of the right size
     1200        kernels->solution1    = psVectorRecycle(kernels->solution1,    numSolution1 + 2, PS_TYPE_F64); // 1 for norm, 1 for bg
     1201        kernels->solution1err = psVectorRecycle(kernels->solution1err, numSolution1 + 2, PS_TYPE_F64); // 1 for norm, 1 for bg
     1202        kernels->solution2    = psVectorRecycle(kernels->solution2,    numSolution2,     PS_TYPE_F64); // 1 for norm, 1 for bg
     1203        kernels->solution2err = psVectorRecycle(kernels->solution2err, numSolution2,     PS_TYPE_F64); // 1 for norm, 1 for bg
     1204
     1205        psVectorInit(kernels->solution1, 0.0);
     1206        psVectorInit(kernels->solution1err, 0.0);
     1207        psVectorInit(kernels->solution2, 0.0);
     1208        psVectorInit(kernels->solution2err, 0.0);
    11941209
    11951210        // for DUAL convolution analysis, we apply the normalization to I1 as follows:
     
    12051220            kernels->solution1->data.F64[i] = solution->data.F64[i] * stamps->normValue;
    12061221            kernels->solution2->data.F64[i] = solution->data.F64[i + numSolution1];
     1222
     1223            kernels->solution1err->data.F64[i] = sqrt(invMatrix->data.F64[i][i]) * stamps->normValue;
     1224            int i2 = i + numSolution1;
     1225            kernels->solution2err->data.F64[i] = sqrt(invMatrix->data.F64[i2][i2]);
    12071226        }
    12081227
     
    12131232        kernels->solution1->data.F64[bgIndex] = 0.0;
    12141233
     1234        psFree(solution);
     1235        psFree(sumVector);
    12151236        psFree(sumMatrix);
    1216         psFree(sumVector);
    1217         psFree(solution);
     1237        psFree(invMatrix);
    12181238    }
    12191239
     
    12241244    if (psTraceGetLevel("psModules.imcombine") >= 7) {
    12251245        for (int i = 0; i < kernels->solution1->n; i++) {
    1226             psTrace("psModules.imcombine", 7, "Solution 1 %d: %f\n", i, kernels->solution1->data.F64[i]);
     1246            psTrace("psModules.imcombine", 7, "Solution 1 %d: %f +/- %f\n", i, kernels->solution1->data.F64[i], kernels->solution1err->data.F64[i]);
    12271247        }
    12281248        if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    12291249            for (int i = 0; i < kernels->solution2->n; i++) {
    1230                 psTrace("psModules.imcombine", 7, "Solution 2 %d: %f\n", i, kernels->solution2->data.F64[i]);
     1250                psTrace("psModules.imcombine", 7, "Solution 2 %d: %f +/- %f\n", i, kernels->solution2->data.F64[i], kernels->solution2err->data.F64[i]);
    12311251            }
    12321252        }
    12331253     }
     1254
     1255    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Solve equation: %f sec", psTimerClear("pmSubtractionSolveEquation"));
    12341256
    12351257    // pmSubtractionVisualPlotLeastSquares((pmSubtractionStampList *) stamps); //casting away const
     
    12831305}
    12841306
    1285 psVector *pmSubtractionCalculateDeviations(pmSubtractionStampList *stamps,
    1286                                            pmSubtractionKernels *kernels)
     1307// given the convolved image(s) and the residual image, calculate the second moment(s) and the chisq
     1308bool pmSubtractionChisqStats(psVector *fluxesVector, psVector *chisqDVector, psVector *chisqRVector, psVector *momentVector, psVector *stampMask, psKernel *convolved1, psKernel *convolved2, psKernel *difference, psKernel *residual, psKernel *weight, psKernel *window) {
     1309
     1310# ifndef USE_WEIGHT
     1311    psAssert(weight == NULL, "impossible!");
     1312# endif
     1313# ifndef USE_WINDOW
     1314    psAssert(window == NULL, "impossible!");
     1315# endif
     1316
     1317    int npix = 0;
     1318    float chisqR = 0;
     1319    float chisqD = 0;
     1320
     1321    // get the chisq
     1322    for (int y = residual->yMin; y <= residual->yMax; y++) {
     1323        for (int x = residual->xMin; x <= residual->xMax; x++) {
     1324            float valueR = PS_SQR(residual->kernel[y][x]);
     1325            if (weight) {
     1326                valueR *= weight->kernel[y][x];
     1327            }
     1328            // XXX NOTE: do NOT apply the window to the chisq portions of the calculation (that would bias the chisq)
     1329            chisqR += valueR;
     1330
     1331            float valueD = PS_SQR(difference->kernel[y][x]);
     1332            if (weight) {
     1333                valueD *= weight->kernel[y][x];
     1334            }
     1335            chisqD += valueD;
     1336            npix ++;
     1337        }
     1338    }
     1339    psVectorAppend(chisqRVector, chisqR / npix);
     1340    psVectorAppend(chisqDVector, chisqD / npix);
     1341
     1342    float value1 = 0;
     1343    float value2 = 0;
     1344    float flux2 = 0;
     1345    float fluxX = 0;
     1346    float fluxY = 0;
     1347    float fluxX2 = 0;
     1348    float fluxY2 = 0;
     1349
     1350    float fluxC1 = 0;
     1351    float fluxC2 = 0;
     1352
     1353    float moment = 0;
     1354
     1355    // get the moments from convolved1
     1356    if (convolved1) {
     1357        for (int y = residual->yMin; y <= residual->yMax; y++) {
     1358            for (int x = residual->xMin; x <= residual->xMax; x++) {
     1359                value1  = convolved1->kernel[y][x];
     1360                value2  = PS_SQR(value1);
     1361
     1362                if (window) {
     1363                    value1 *= window->kernel[y][x];
     1364                    value2 *= window->kernel[y][x];
     1365                }
     1366
     1367                fluxC1 += value1;
     1368                flux2  += value2;
     1369                fluxX  += x * value2;
     1370                fluxY  += y * value2;
     1371                // fluxX2 += PS_SQR(x) * value2;
     1372                // fluxY2 += PS_SQR(y) * value2;
     1373                fluxX2 += PS_SQR(x) * value1;
     1374                fluxY2 += PS_SQR(y) * value1;
     1375            }
     1376        }
     1377        // float Mx = fluxX / flux2;
     1378        // float My = fluxY / flux2;
     1379        // float Mxx = fluxX2 / flux2;
     1380        // float Myy = fluxY2 / flux2;
     1381        float Mxx = fluxX2 / fluxC1;
     1382        float Myy = fluxY2 / fluxC1;
     1383
     1384        // fprintf (stderr, "conv1, flux2: %f, Mx: %f, My: %f, Mxx: %f, Myy: %f, chisq: %f, npix: %d\n", flux2, Mx, My, Mxx, Myy, chisq, npix);
     1385        moment += Mxx + Myy;
     1386    }
     1387
     1388    // get the moments from convolved1
     1389    if (convolved2) {
     1390        for (int y = residual->yMin; y <= residual->yMax; y++) {
     1391            for (int x = residual->xMin; x <= residual->xMax; x++) {
     1392                value1  = convolved2->kernel[y][x];
     1393                value2  = PS_SQR(value1);
     1394
     1395                // XXX NOTE: do NOT apply the weight to the moments calculation
     1396                if (false && weight) {
     1397                    value2 *= weight->kernel[y][x];
     1398                }
     1399                if (window) {
     1400                    value1 *= window->kernel[y][x];
     1401                    value2 *= window->kernel[y][x];
     1402                }
     1403
     1404                fluxC2 += value1;
     1405                flux2  += value2;
     1406                fluxX  += x * value2;
     1407                fluxY  += y * value2;
     1408                // fluxX2 += PS_SQR(x) * value2;
     1409                // fluxY2 += PS_SQR(y) * value2;
     1410                fluxX2 += PS_SQR(x) * value1;
     1411                fluxY2 += PS_SQR(y) * value1;
     1412            }
     1413        }
     1414        // float Mx = fluxX / flux2;
     1415        // float My = fluxY / flux2;
     1416        // float Mxx = fluxX2 / flux2;
     1417        // float Myy = fluxY2 / flux2;
     1418        float Mxx = fluxX2 / fluxC2;
     1419        float Myy = fluxY2 / fluxC2;
     1420
     1421        // fprintf (stderr, "conv2, flux2: %f, Mx: %f, My: %f, Mxx: %f, Myy: %f, chisq: %f, npix: %d\n", flux2, Mx, My, Mxx, Myy, chisq, npix);
     1422        moment += Mxx + Myy;
     1423    }
     1424
     1425    float flux = fluxC1 + fluxC2;
     1426   
     1427    if (convolved1 && convolved2) {
     1428        moment *= 0.5;
     1429        flux *= 0.5;
     1430    }
     1431    psVectorAppend(momentVector, moment);
     1432    psVectorAppend(fluxesVector, flux);
     1433
     1434    // check that the last appended values are ok:
     1435    int Nelem = fluxesVector->n - 1;
     1436    bool valid = true;
     1437    valid &= isfinite(chisqRVector->data.F32[Nelem]);
     1438    valid &= isfinite(fluxesVector->data.F32[Nelem]);
     1439    valid &= isfinite(momentVector->data.F32[Nelem]);
     1440    if (valid) {
     1441      psVectorAppend(stampMask, 0);
     1442    } else {
     1443      psVectorAppend(stampMask, 0x02);
     1444    }
     1445    return true;
     1446}
     1447
     1448bool pmSubtractionCalculateChisqAndMoments(pmSubtractionQuality **bestMatch,
     1449                                           pmSubtractionStampList *stamps,
     1450                                           pmSubtractionKernels *kernels)
    12871451{
    12881452    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, NULL);
    12891453    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NULL);
    12901454    PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NULL);
     1455
     1456    psTimerStart("pmSubtractionCalculateChisqAndMoments");
     1457
     1458    // XXX need to save these somewhere
     1459    psVector *fluxes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1460    psVector *chisqD = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1461    psVector *chisqR = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1462    psVector *moments = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1463    psVector *stampMask = psVectorAllocEmpty(stamps->num, PS_TYPE_VECTOR_MASK);
     1464
     1465    int footprint = stamps->footprint; // Half-size of stamps
     1466    int numKernels = kernels->num;      // Number of kernels
     1467
     1468    psImage *polyValues = NULL;         // Polynomial values
     1469
     1470    // storage for the image (convolved2 is not used in SINGLE mode)
     1471    psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
     1472    psKernel *difference = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
     1473    psKernel *convolved1 = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
     1474    psKernel *convolved2 = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
     1475
     1476    int nGood = 0;
     1477    for (int i = 0; i < stamps->num; i++) {
     1478        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // The stamp of interest
     1479        if (stamp->status != PM_SUBTRACTION_STAMP_USED) {
     1480            // mark this stamp as unused (note that we have to append NANs to the other vectors to keep the lengths in sync)
     1481            psVectorAppend(moments, NAN);
     1482            psVectorAppend(fluxes, NAN);
     1483            psVectorAppend(chisqD, NAN);
     1484            psVectorAppend(chisqR, NAN);
     1485            psVectorAppend(stampMask, 0x01);
     1486            continue;
     1487        }
     1488        nGood ++;
     1489
     1490        // Calculate coefficients of the kernel basis functions
     1491        polyValues = p_pmSubtractionPolynomial(polyValues, kernels->spatialOrder, stamp->xNorm, stamp->yNorm);
     1492        double norm = p_pmSubtractionSolutionNorm(kernels); // Normalisation
     1493        double background = p_pmSubtractionSolutionBackground(kernels, polyValues);// Difference in background
     1494
     1495        // Calculate residuals
     1496        psImageInit(residual->image, 0.0);
     1497        psImageInit(difference->image, 0.0);
     1498
     1499        psKernel *weight = NULL;
     1500        psKernel *window = NULL;
     1501   
     1502#ifdef USE_WEIGHT
     1503    weight = stamp->weight;
     1504#endif
     1505#ifdef USE_WINDOW
     1506    window = stamps->window;
     1507#endif
     1508
     1509        if (kernels->mode != PM_SUBTRACTION_MODE_DUAL) {
     1510
     1511            // the single-direction psf match code attempts to find the kernel such that:
     1512            // source * kernel = target.  we need to assign 'source' and 'target' correctly
     1513            // depending on which of image1 or image2 we asked to be convolved.
     1514
     1515            psKernel *target;           // Target postage stamp (convolve source to match the target)
     1516            psKernel *source;           // Source postage stamp (convolve source to match the target)
     1517            psArray *convolutions;      // Convolution postage stamps for each kernel basis function
     1518
     1519            // init the accumulation image
     1520            psImageInit(convolved1->image, 0.0);
     1521
     1522            switch (kernels->mode) {
     1523              case PM_SUBTRACTION_MODE_1:
     1524                target = stamp->image2;
     1525                source = stamp->image1;
     1526                convolutions = stamp->convolutions1;
     1527                break;
     1528              case PM_SUBTRACTION_MODE_2:
     1529                target = stamp->image1;
     1530                source = stamp->image2;
     1531                convolutions = stamp->convolutions2;
     1532                break;
     1533              default:
     1534                psAbort("Unsupported subtraction mode: %x", kernels->mode);
     1535            }
     1536
     1537            // generate the convolved source image (sum over kernels)
     1538            for (int j = 0; j < numKernels; j++) {
     1539                psKernel *convolution = convolutions->data[j]; // Convolution
     1540                double coefficient = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, false); // Coefficient
     1541                for (int y = - footprint; y <= footprint; y++) {
     1542                    for (int x = - footprint; x <= footprint; x++) {
     1543                        convolved1->kernel[y][x] += convolution->kernel[y][x] * coefficient;
     1544                    }
     1545                }
     1546            }
     1547
     1548            // Generate the difference, residual, and convolved source images.  Note the we
     1549            // accumulate the convolution of (A-B), so we need to replace it to generate the
     1550            // images of the convolved source image.
     1551            for (int y = - footprint; y <= footprint; y++) {
     1552                for (int x = - footprint; x <= footprint; x++) {
     1553                    difference->kernel[y][x] = target->kernel[y][x] - source->kernel[y][x] * norm - background;
     1554                    residual->kernel[y][x] = difference->kernel[y][x] - convolved1->kernel[y][x];
     1555                    convolved1->kernel[y][x] += source->kernel[y][x] * norm;
     1556                }
     1557            }
     1558
     1559            // XXX if we want to have a weight and window, we'll need to pass through to here
     1560            pmSubtractionChisqStats(fluxes, chisqD, chisqR, moments, stampMask, convolved1, NULL, difference, residual, weight, window);
     1561
     1562        } else {
     1563
     1564            // Dual convolution
     1565            psArray *convolutions1 = stamp->convolutions1; // Convolutions of the first image
     1566            psArray *convolutions2 = stamp->convolutions2; // Convolutions of the second image
     1567            psKernel *image1 = stamp->image1; // The first image
     1568            psKernel *image2 = stamp->image2; // The second image
     1569
     1570            // init the accumulation images
     1571            psImageInit(convolved1->image, 0.0);
     1572            psImageInit(convolved2->image, 0.0);
     1573
     1574            for (int j = 0; j < numKernels; j++) {
     1575                psKernel *conv1 = convolutions1->data[j]; // Convolution of first image
     1576                psKernel *conv2 = convolutions2->data[j]; // Convolution of second image
     1577                double coeff1 = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, false); // Coefficient 1
     1578                double coeff2 = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, true); // Coefficient 2
     1579
     1580                for (int y = - footprint; y <= footprint; y++) {
     1581                    for (int x = - footprint; x <= footprint; x++) {
     1582                        // NOTE sign for coeff2
     1583                        convolved1->kernel[y][x] += +conv1->kernel[y][x] * coeff1;
     1584                        convolved2->kernel[y][x] += -conv2->kernel[y][x] * coeff2;
     1585                    }
     1586                }
     1587            }
     1588
     1589            // Generate the difference, residual, and convolved source images.  Note the we
     1590            // accumulate the convolutions of (A-B), so we need to replace (A or B) to generate
     1591            // the images of the convolved source images.
     1592            for (int y = - footprint; y <= footprint; y++) {
     1593                for (int x = - footprint; x <= footprint; x++) {
     1594                    difference->kernel[y][x] = image2->kernel[y][x] - image1->kernel[y][x] * norm - background;
     1595                    residual->kernel[y][x] = difference->kernel[y][x] + convolved2->kernel[y][x] - convolved1->kernel[y][x];
     1596                    convolved1->kernel[y][x] += image1->kernel[y][x] * norm;
     1597                    convolved2->kernel[y][x] += image2->kernel[y][x];
     1598                }
     1599            }
     1600
     1601            if (0) {
     1602                psFitsWriteImageSimple("conv1.fits", convolved1->image, NULL);
     1603                psFitsWriteImageSimple("conv2.fits", convolved2->image, NULL);
     1604                psFitsWriteImageSimple("resid.fits", residual->image,   NULL);
     1605                pmVisualAskUser(NULL);
     1606            }
     1607
     1608            pmSubtractionChisqStats(fluxes, chisqD, chisqR, moments, stampMask, convolved1, convolved2, difference, residual, weight, window);
     1609        }
     1610    }
     1611
     1612    // find the mean chisq and mean moment
     1613    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
     1614    psVectorStats (stats, chisqD, NULL, stampMask, 0xff);
     1615    float chisqDValue = stats->sampleMean;
     1616
     1617    psStatsInit(stats);
     1618    psVectorStats (stats, chisqR, NULL, stampMask, 0xff);
     1619    float chisqRValue = stats->sampleMean;
     1620
     1621    psStatsInit(stats);
     1622    psVectorStats (stats, moments, NULL, stampMask, 0xff);
     1623    float momentValue = stats->sampleMean;
     1624
     1625    double sumKernel1 = 0.0, sumKernel2 = 0.0; // Sum of the kernel
     1626
     1627    // calculate the variance contribution from this smoothing kernel
     1628    psKernel *modelKernel = pmSubtractionKernel(kernels, 0.0, 0.0, false);
     1629    for (int y = modelKernel->yMin; y <= modelKernel->yMax; y++) {
     1630        for (int x = modelKernel->xMin; x <= modelKernel->xMax; x++) {
     1631            if (!isfinite(modelKernel->kernel[y][x])) {
     1632                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Non-finite covariance matrix element in kernel at %d,%d", x, y);
     1633                return NULL;
     1634            }
     1635            sumKernel1 += PS_SQR(modelKernel->kernel[y][x]);
     1636        }
     1637    }
     1638    psFree (modelKernel);
     1639
     1640    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
     1641        psKernel *modelKernel = pmSubtractionKernel(kernels, 0.0, 0.0, true);
     1642        for (int y = modelKernel->yMin; y <= modelKernel->yMax; y++) {
     1643            for (int x = modelKernel->xMin; x <= modelKernel->xMax; x++) {
     1644                if (!isfinite(modelKernel->kernel[y][x])) {
     1645                    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Non-finite covariance matrix element in kernel at %d,%d", x, y);
     1646                    return NULL;
     1647                }
     1648                sumKernel2 += PS_SQR(modelKernel->kernel[y][x]);
     1649            }
     1650        }
     1651        psFree (modelKernel);
     1652    } else {
     1653        sumKernel2 = 1.0;
     1654    }
     1655
     1656    // if we modify the chisq value by the (sumKernel1 + sumKernel2), we account for the
     1657    // smoothing coming from larger kernels adding additional spatial fit terms should be
     1658    // penalized by increasing the score somewhat.  the 0.01 value is not well-chosen.
     1659    float orderFactor = 0.01 * kernels->spatialOrder;
     1660    float score = 2.0 * chisqRValue / (sumKernel1 + sumKernel2) + orderFactor;
     1661    psLogMsg("psModules.imcombine", PS_LOG_INFO, "chisq: %6.3f, chisqD: %6.3f, moment: %6.3f, sumKernel_1: %6.3f, sumKernel_2, score: %6.3f: %6.3f\n", chisqRValue, chisqDValue, momentValue, sumKernel1, sumKernel2, score);
     1662
     1663    // save this result if it is the first or the best (skip if bestMatch is NULL)
     1664    if (bestMatch) {
     1665        pmSubtractionQuality *match = *bestMatch;
     1666        bool keep = false;
     1667        if (match == NULL) {
     1668            *bestMatch = match = pmSubtractionQualityAlloc();
     1669            keep = true;
     1670        } else {
     1671            if (score < match->score) {
     1672                psFree(match->fluxes);
     1673                psFree(match->chisq);
     1674                psFree(match->moments);
     1675                psFree(match->stampMask);
     1676                keep = true;
     1677            }
     1678        }
     1679        if (keep) {
     1680            psLogMsg("psModules.imcombine", PS_LOG_INFO, "keeping order: %d, mode: %d, score: %f\n", kernels->spatialOrder, kernels->mode, score);
     1681            match->score        = score;
     1682            match->spatialOrder = kernels->spatialOrder;
     1683            match->mode         = kernels->mode;
     1684            match->nGood        = nGood;
     1685            match->fluxes       = psMemIncrRefCounter(fluxes);
     1686            match->chisq        = psMemIncrRefCounter(chisqR);
     1687            match->moments      = psMemIncrRefCounter(moments);
     1688            match->stampMask    = psMemIncrRefCounter(stampMask);
     1689        }           
     1690    }
     1691
     1692    pmSubtractionVisualPlotChisqAndMoments(fluxes, chisqR, moments);
     1693
     1694    psFree(stats);
     1695    psFree(chisqR);
     1696    psFree(chisqD);
     1697    psFree(fluxes);
     1698    psFree(moments);
     1699    psFree(stampMask);
     1700
     1701    psFree(residual);
     1702    psFree(difference);
     1703    psFree(convolved1);
     1704    psFree(convolved2);
     1705    psFree(polyValues);
     1706
     1707    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Calculate Chisq and Moments: %f sec", psTimerClear("pmSubtractionCalculateChisqAndMoments"));
     1708
     1709    return true;
     1710}
     1711
     1712// XXX for now, let's not use this, and let's instead just use values from pmSubtractionCalculateChisqAndMoments
     1713psVector *pmSubtractionCalculateDeviations(pmSubtractionStampList *stamps, pmSubtractionKernels *kernels)
     1714{
     1715    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, NULL);
     1716    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NULL);
     1717    PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NULL);
     1718
     1719    psTimerStart("pmSubtractionCalculateDeviations");
    12911720
    12921721    psVector *deviations = psVectorAlloc(stamps->num, PS_TYPE_F32); // Mean deviation for stamps
     
    12981727    psImage *polyValues = NULL;         // Polynomial values
    12991728    psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
    1300 
    1301     // set up holding images for the visualization
    1302     pmSubtractionVisualShowFitInit (stamps);
    13031729
    13041730    psVector *fResSigma = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     
    14011827            }
    14021828
    1403             // XXX visualize the target, source, convolution and residual
    1404             pmSubtractionVisualShowFitAddStamp (target, source, residual, background, norm, i);
    1405 
    14061829            for (int y = - footprint; y <= footprint; y++) {
    14071830                for (int x = - footprint; x <= footprint; x++) {
     
    14381861            }
    14391862
    1440             // XXX visualize the target, source, convolution and residual
    1441             pmSubtractionVisualShowFitAddStamp (image2, image1, residual, background, norm, i);
    1442 
    14431863            for (int y = - footprint; y <= footprint; y++) {
    14441864                for (int x = - footprint; x <= footprint; x++) {
     
    14551875        }
    14561876
     1877        double flux = 0.0;
    14571878        double deviation = 0.0;         // Sum of differences
    14581879        for (int y = - footprint; y <= footprint; y++) {
     
    14601881                double dev = PS_SQR(residual->kernel[y][x]) * weight->kernel[y][x];
    14611882                deviation += dev;
    1462 #ifdef TESTING
    1463                 residual->kernel[y][x] = dev;
    1464 #endif
     1883                flux += stamp->image1->kernel[y][x] + stamp->image2->kernel[y][x];
    14651884            }
    14661885        }
    14671886        deviations->data.F32[i] = devNorm * deviation;
    1468         psTrace("psModules.imcombine", 5, "Deviation for stamp %d (%d,%d): %f\n",
    1469                 i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5), deviations->data.F32[i]);
     1887        psTrace("psModules.imcombine", 5, "Deviation and Flux for stamp %d (%d,%d): %f %f\n",
     1888                i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5), deviations->data.F32[i], flux);
    14701889        psStringAppend(&log, "Stamp %d (%d,%d): %f\n",
    14711890                       i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5), deviations->data.F32[i]);
    14721891        if (!isfinite(deviations->data.F32[i])) {
    14731892            stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
    1474             psTrace("psModules.imcombine", 5,
    1475                     "Rejecting stamp %d (%d,%d) because of non-finite deviation\n",
    1476                     i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
     1893            psTrace("psModules.imcombine", 5, "Rejecting stamp %d (%d,%d) because of non-finite deviation\n", i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
    14771894            continue;
    14781895        }
    1479 
    1480 #ifdef TESTING
    1481         {
    1482             psString filename = NULL;
    1483             psStringAppend(&filename, "resid_%03d.fits", i);
    1484             psFits *fits = psFitsOpen(filename, "w");
    1485             psFree(filename);
    1486             psFitsWriteImage(fits, NULL, residual->image, 0, NULL);
    1487             psFitsClose(fits);
    1488         }
    1489         if (stamp->image1) {
    1490             psString filename = NULL;
    1491             psStringAppend(&filename, "stamp_image1_%03d.fits", i);
    1492             psFits *fits = psFitsOpen(filename, "w");
    1493             psFree(filename);
    1494             psFitsWriteImage(fits, NULL, stamp->image1->image, 0, NULL);
    1495             psFitsClose(fits);
    1496         }
    1497         if (stamp->image2) {
    1498             psString filename = NULL;
    1499             psStringAppend(&filename, "stamp_image2_%03d.fits", i);
    1500             psFits *fits = psFitsOpen(filename, "w");
    1501             psFree(filename);
    1502             psFitsWriteImage(fits, NULL, stamp->image2->image, 0, NULL);
    1503             psFitsClose(fits);
    1504         }
    1505         if (stamp->weight) {
    1506             psString filename = NULL;
    1507             psStringAppend(&filename, "stamp_weight_%03d.fits", i);
    1508             psFits *fits = psFitsOpen(filename, "w");
    1509             psFree(filename);
    1510             psFitsWriteImage(fits, NULL, stamp->weight->image, 0, NULL);
    1511             psFitsClose(fits);
    1512         }
    1513 #endif
    1514 
    15151896    }
    15161897
    15171898    psFree(keepStamps);
    15181899
    1519     psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "%s", log);
     1900    psLogMsg("psModules.imcombine", PS_LOG_MINUTIA, "%s", log);
    15201901    psFree(log);
    15211902
     
    15271908        psLogMsg("psModules.imcombine", PS_LOG_INFO, "normalization: %f, background: %f", norm, background);
    15281909
    1529         pmSubtractionVisualShowFit(norm);
    1530         pmSubtractionVisualPlotFit(kernels);
    1531 
    15321910        psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
    15331911        psVectorStats (stats, fResSigma, NULL, NULL, 0);
     
    15601938    psFree(polyValues);
    15611939
     1940    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Calculate Deviations: %f sec", psTimerClear("pmSubtractionCalculateDeviations"));
     1941
    15621942    return deviations;
    1563 }
    1564 
    1565 // we are supplied U, not Ut; w represents a diagonal matrix (also, we apply 1/w instead of w)
    1566 psImage *p_pmSubSolve_wUt (psVector *w, psImage *U) {
    1567 
    1568     psAssert (w->n == U->numCols, "w and U dimensions do not match");
    1569 
    1570     // wUt has dimensions transposed relative to Ut.
    1571     psImage *wUt = psImageAlloc (U->numRows, U->numCols, PS_TYPE_F64);
    1572     psImageInit (wUt, 0.0);
    1573 
    1574     for (int i = 0; i < wUt->numCols; i++) {
    1575         for (int j = 0; j < wUt->numRows; j++) {
    1576             if (!isfinite(w->data.F64[j])) continue;
    1577             if (w->data.F64[j] == 0.0) continue;
    1578             wUt->data.F64[j][i] = U->data.F64[i][j] / w->data.F64[j];
    1579         }
    1580     }
    1581     return wUt;
    1582 }
    1583 
    1584 // XXX this is just standard matrix multiplication: use psMatrixMultiply?
    1585 psImage *p_pmSubSolve_VwUt (psImage *V, psImage *wUt) {
    1586 
    1587     psAssert (V->numCols == wUt->numRows, "matrix dimensions do not match");
    1588 
    1589     psImage *Ainv = psImageAlloc (wUt->numCols, V->numRows, PS_TYPE_F64);
    1590 
    1591     for (int i = 0; i < Ainv->numCols; i++) {
    1592         for (int j = 0; j < Ainv->numRows; j++) {
    1593             double sum = 0.0;
    1594             for (int k = 0; k < V->numCols; k++) {
    1595                 sum += V->data.F64[j][k] * wUt->data.F64[k][i];
    1596             }
    1597             Ainv->data.F64[j][i] = sum;
    1598         }
    1599     }
    1600     return Ainv;
    1601 }
    1602 
    1603 // we are supplied U, not Ut
    1604 bool p_pmSubSolve_UtB (psVector **UtB, psImage *U, psVector *B) {
    1605 
    1606     psAssert (U->numRows == B->n, "U and B dimensions do not match");
    1607 
    1608     UtB[0] = psVectorRecycle (UtB[0], U->numCols, PS_TYPE_F64);
    1609 
    1610     for (int i = 0; i < U->numCols; i++) {
    1611         double sum = 0.0;
    1612         for (int j = 0; j < U->numRows; j++) {
    1613             sum += B->data.F64[j] * U->data.F64[j][i];
    1614         }
    1615         UtB[0]->data.F64[i] = sum;
    1616     }
    1617     return true;
    1618 }
    1619 
    1620 // w is diagonal
    1621 bool p_pmSubSolve_wUtB (psVector **wUtB, psVector *w, psVector *UtB) {
    1622 
    1623     psAssert (w->n == UtB->n, "w and UtB dimensions do not match");
    1624 
    1625     // wUt has dimensions transposed relative to Ut.
    1626     wUtB[0] = psVectorRecycle (wUtB[0], w->n, PS_TYPE_F64);
    1627     psVectorInit (wUtB[0], 0.0);
    1628 
    1629     for (int i = 0; i < w->n; i++) {
    1630         if (!isfinite(w->data.F64[i])) continue;
    1631         if (w->data.F64[i] == 0.0) continue;
    1632         wUtB[0]->data.F64[i] = UtB->data.F64[i] / w->data.F64[i];
    1633     }
    1634     return true;
    1635 }
    1636 
    1637 // this is basically matrix * vector
    1638 bool p_pmSubSolve_VwUtB (psVector **VwUtB, psImage *V, psVector *wUtB) {
    1639 
    1640     psAssert (V->numCols == wUtB->n, "V and wUtB dimensions do not match");
    1641 
    1642     VwUtB[0] = psVectorRecycle (*VwUtB, V->numRows, PS_TYPE_F64);
    1643 
    1644     for (int j = 0; j < V->numRows; j++) {
    1645         double sum = 0.0;
    1646         for (int i = 0; i < V->numCols; i++) {
    1647             sum += V->data.F64[j][i] * wUtB->data.F64[i];
    1648         }
    1649         VwUtB[0]->data.F64[j] = sum;
    1650     }
    1651     return true;
    1652 }
    1653 
    1654 // this is basically matrix * vector
    1655 bool p_pmSubSolve_Ax (psVector **B, psImage *A, psVector *x) {
    1656 
    1657     psAssert (A->numCols == x->n, "A and x dimensions do not match");
    1658 
    1659     B[0] = psVectorRecycle (*B, A->numRows, PS_TYPE_F64);
    1660 
    1661     for (int j = 0; j < A->numRows; j++) {
    1662         double sum = 0.0;
    1663         for (int i = 0; i < A->numCols; i++) {
    1664             sum += A->data.F64[j][i] * x->data.F64[i];
    1665         }
    1666         B[0]->data.F64[j] = sum;
    1667     }
    1668     return true;
    1669 }
    1670 
    1671 // this is basically Vector * vector
    1672 bool p_pmSubSolve_VdV (double *value, psVector *x, psVector *y) {
    1673 
    1674     psAssert (x->n == y->n, "x and y dimensions do not match");
    1675 
    1676     double sum = 0.0;
    1677     for (int i = 0; i < x->n; i++) {
    1678         sum += x->data.F64[i] * y->data.F64[i];
    1679     }
    1680     *value = sum;
    1681     return true;
    1682 }
    1683 
    1684 bool p_pmSubSolve_y2 (double *y2, pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps) {
    1685 
    1686     int footprint = stamps->footprint; // Half-size of stamps
    1687 
    1688     double sum = 0.0;
    1689     for (int i = 0; i < stamps->num; i++) {
    1690 
    1691         pmSubtractionStamp *stamp = stamps->stamps->data[i];
    1692         if (stamp->status != PM_SUBTRACTION_STAMP_USED) continue;
    1693 
    1694         psKernel *weight = NULL;
    1695         psKernel *window = NULL;
    1696         psKernel *input = NULL;
    1697 
    1698 #ifdef USE_WEIGHT
    1699         weight = stamp->weight;
    1700 #endif
    1701 #ifdef USE_WINDOW
    1702         window = stamps->window;
    1703 #endif
    1704 
    1705         switch (kernels->mode) {
    1706             // MODE_1 : convolve image 1 to match image 2 (and vice versa)
    1707           case PM_SUBTRACTION_MODE_1:
    1708             input = stamp->image2;
    1709             break;
    1710           case PM_SUBTRACTION_MODE_2:
    1711             input = stamp->image1;
    1712             break;
    1713           default:
    1714             psAbort ("programming error");
    1715         }
    1716 
    1717         for (int y = - footprint; y <= footprint; y++) {
    1718             for (int x = - footprint; x <= footprint; x++) {
    1719                 double in = input->kernel[y][x];
    1720                 double value = in*in;
    1721                 if (weight) {
    1722                     float wtVal = weight->kernel[y][x];
    1723                     value *= wtVal;
    1724                 }
    1725                 if (window) {
    1726                     float  winVal = window->kernel[y][x];
    1727                     value *= winVal;
    1728                 }
    1729                 sum += value;
    1730             }
    1731         }
    1732     }
    1733     *y2 = sum;
    1734     return true;
    1735 }
    1736 
    1737 double p_pmSubSolve_ChiSquare (pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps) {
    1738 
    1739     int footprint = stamps->footprint; // Half-size of stamps
    1740     int numKernels = kernels->num;      // Number of kernels
    1741 
    1742     double sum = 0.0;
    1743 
    1744     psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
    1745     psImageInit(residual->image, 0.0);
    1746 
    1747     psImage *polyValues = NULL;         // Polynomial values
    1748 
    1749     for (int i = 0; i < stamps->num; i++) {
    1750 
    1751         pmSubtractionStamp *stamp = stamps->stamps->data[i];
    1752         if (stamp->status != PM_SUBTRACTION_STAMP_USED) continue;
    1753 
    1754         psKernel *weight = NULL;
    1755         psKernel *window = NULL;
    1756         psKernel *target = NULL;
    1757         psKernel *source = NULL;
    1758 
    1759         psArray *convolutions = NULL;
    1760 
    1761 #ifdef USE_WEIGHT
    1762         weight = stamp->weight;
    1763 #endif
    1764 #ifdef USE_WINDOW
    1765         window = stamps->window;
    1766 #endif
    1767 
    1768         switch (kernels->mode) {
    1769             // MODE_1 : convolve image 1 to match image 2 (and vice versa)
    1770           case PM_SUBTRACTION_MODE_1:
    1771             target = stamp->image2;
    1772             source = stamp->image1;
    1773             convolutions = stamp->convolutions1;
    1774             break;
    1775           case PM_SUBTRACTION_MODE_2:
    1776             target = stamp->image1;
    1777             source = stamp->image2;
    1778             convolutions = stamp->convolutions2;
    1779             break;
    1780           default:
    1781             psAbort ("programming error");
    1782         }
    1783 
    1784         // Calculate coefficients of the kernel basis functions
    1785         polyValues = p_pmSubtractionPolynomial(polyValues, kernels->spatialOrder, stamp->xNorm, stamp->yNorm);
    1786         double norm = p_pmSubtractionSolutionNorm(kernels); // Normalisation
    1787         double background = p_pmSubtractionSolutionBackground(kernels, polyValues);// Difference in background
    1788 
    1789         psImageInit(residual->image, 0.0);
    1790         for (int j = 0; j < numKernels; j++) {
    1791             psKernel *convolution = convolutions->data[j]; // Convolution
    1792             double coefficient = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, false); // Coefficient
    1793             for (int y = - footprint; y <= footprint; y++) {
    1794                 for (int x = - footprint; x <= footprint; x++) {
    1795                     residual->kernel[y][x] -= convolution->kernel[y][x] * coefficient;
    1796                 }
    1797             }
    1798         }
    1799 
    1800         for (int y = - footprint; y <= footprint; y++) {
    1801             for (int x = - footprint; x <= footprint; x++) {
    1802                 double resid = target->kernel[y][x] - background - source->kernel[y][x] * norm + residual->kernel[y][x];
    1803                 double value = PS_SQR(resid);
    1804                 if (weight) {
    1805                     float wtVal = weight->kernel[y][x];
    1806                     value *= wtVal;
    1807                 }
    1808                 if (window) {
    1809                     float  winVal = window->kernel[y][x];
    1810                     value *= winVal;
    1811                 }
    1812                 sum += value;
    1813             }
    1814         }
    1815     }
    1816     psFree (polyValues);
    1817     psFree (residual);
    1818 
    1819     return sum;
    1820 }
    1821 
    1822 bool p_pmSubSolve_SetWeights (psVector *wApply, psVector *w, psVector *wMask) {
    1823 
    1824     for (int i = 0; i < w->n; i++) {
    1825         wApply->data.F64[i] = wMask->data.U8[i] ? 0.0 : w->data.F64[i];
    1826     }
    1827     return true;
    1828 }
    1829 
    1830 // we are supplied V and w; w represents a diagonal matrix (also, we apply 1/w instead of w)
    1831 psImage *p_pmSubSolve_Xvar (psImage *V, psVector *w) {
    1832 
    1833     psAssert (w->n == V->numCols, "w and U dimensions do not match");
    1834 
    1835     psImage *Vn = psImageAlloc (V->numCols, V->numRows, PS_TYPE_F64);
    1836     psImageInit (Vn, 0.0);
    1837 
    1838     // generate Vn = V * w^{-1}
    1839     for (int j = 0; j < Vn->numRows; j++) {
    1840         for (int i = 0; i < Vn->numCols; i++) {
    1841             if (!isfinite(w->data.F64[i])) continue;
    1842             if (w->data.F64[i] == 0.0) continue;
    1843             Vn->data.F64[j][i] = V->data.F64[j][i] / w->data.F64[i];
    1844         }
    1845     }
    1846 
    1847     psImage *Xvar = psImageAlloc (V->numCols, V->numRows, PS_TYPE_F64);
    1848     psImageInit (Xvar, 0.0);
    1849 
    1850     // generate Xvar = Vn * Vn^T
    1851     for (int j = 0; j < Vn->numRows; j++) {
    1852         for (int i = 0; i < Vn->numCols; i++) {
    1853             double sum = 0.0;
    1854             for (int k = 0; k < Vn->numCols; k++) {
    1855                 sum += Vn->data.F64[k][i]*Vn->data.F64[k][j];
    1856             }
    1857             Xvar->data.F64[j][i] = sum;
    1858         }
    1859     }
    1860     return Xvar;
    18611943}
    18621944
     
    18641946// of the elements of an image A(x,y) = A->data.F64[y][x] = A_x,y, a matrix
    18651947// multiplication is: A_k,j * B_i,k = C_i,j
    1866 
    18671948
    18681949bool psFitsWriteImageSimple (char *filename, psImage *image, psMetadata *header) {
  • trunk/psModules/src/imcombine/pmSubtractionEquation.h

    r29543 r30622  
    44#include "pmSubtractionStamps.h"
    55#include "pmSubtractionKernels.h"
    6 
    7 typedef 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;
     6#include "pmSubtraction.h"
    147
    158/// Execute a thread job to calculate the least-squares equation for a stamp
     
    2013bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, ///< Stamps
    2114                                         pmSubtractionKernels *kernels, ///< Kernel parameters
    22                                          int index, ///< Index of stamp
    23                                          const pmSubtractionEquationCalculationMode mode
     15                                         int index ///< Index of stamp
    2416    );
    2517
    2618/// Calculate the least-squares equation to match the image quality
    2719bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, ///< Stamps
    28                                     pmSubtractionKernels *kernels, ///< Kernel parameters
    29                                     const pmSubtractionEquationCalculationMode mode
     20                                    pmSubtractionKernels *kernels ///< Kernel parameters
    3021    );
    3122
    3223/// Solve the least-squares equation to match the image quality
    3324bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels, ///< Kernel parameters
    34                                 const pmSubtractionStampList *stamps, ///< Stamps
    35                                 const pmSubtractionEquationCalculationMode mode
     25                                const pmSubtractionStampList *stamps ///< Stamps
    3626    );
    3727
     
    9282bool pmSubtractionCalculateMomentsKernel(double *Mxx, double *Myy, psKernel *image, int footprint, int window);
    9383
     84bool pmSubtractionChisqStats(psVector *fluxesVector, psVector *chisqDVector, psVector *chisqRVector, psVector *momentVector, psVector *stampMask, psKernel *convolved1, psKernel *convolved2, psKernel *difference, psKernel *residual, psKernel *weight, psKernel *window);
     85
     86bool pmSubtractionCalculateChisqAndMoments(pmSubtractionQuality **bestMatch, pmSubtractionStampList *stamps, pmSubtractionKernels *kernels);
    9487#endif
  • trunk/psModules/src/imcombine/pmSubtractionEquation.v0.c

    r29543 r30622  
    99#include "pmErrorCodes.h"
    1010#include "pmSubtraction.h"
     11#include "pmSubtractionTypes.h"
    1112#include "pmSubtractionKernels.h"
    1213#include "pmSubtractionStamps.h"
  • trunk/psModules/src/imcombine/pmSubtractionHermitian.c

    r26893 r30622  
    88#include <pslib.h>
    99
     10#include "pmSubtractionTypes.h"
    1011#include "pmSubtractionHermitian.h"
    1112
  • trunk/psModules/src/imcombine/pmSubtractionIO.c

    r27321 r30622  
    1212#include "pmConceptsRead.h"
    1313
     14#include "pmSubtractionTypes.h"
    1415#include "pmSubtraction.h"
    1516#include "pmSubtractionKernels.h"
  • trunk/psModules/src/imcombine/pmSubtractionKernels.c

    r29597 r30622  
    88#include <pslib.h>
    99
     10#include "pmFPA.h"
     11#include "pmSubtractionTypes.h"
    1012#include "pmSubtraction.h"
    1113#include "pmSubtractionKernels.h"
     
    2022{
    2123    psFree(kernels->description);
     24    psFree(kernels->fwhms);
     25    psFree(kernels->orders);
    2226    psFree(kernels->u);
    2327    psFree(kernels->v);
     
    3034    psFree(kernels->solution1);
    3135    psFree(kernels->solution2);
     36    psFree(kernels->solution1err);
     37    psFree(kernels->solution2err);
    3238    psFree(kernels->sampleStamps);
    3339}
     
    419425
    420426    int num = 0;                        // Number of basis functions
    421     psString params = NULL;             // List of parameters
    422427    for (int i = 0; i < numGaussians; i++) {
    423428        int gaussOrder = orders->data.S32[i]; // Polynomial order to apply to Gaussian
    424         psStringAppend(&params, "(%.1f,%d)", fwhms->data.F32[i], orders->data.S32[i]);
    425429        num += (gaussOrder + 1) * (gaussOrder + 2) / 2;
    426430    }
    427431
    428     pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_ISIS, size,
    429                                                               spatialOrder, penalty, bounds, mode); // Kernels
    430     psStringAppend(&kernels->description, "ISIS(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
    431 
    432     psLogMsg("psModules.imcombine", PS_LOG_INFO, "ISIS kernel: %s,%d --> %d elements",
    433              params, spatialOrder, num);
    434     psFree(params);
     432    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_ISIS, size, fwhms, orders, spatialOrder, penalty, bounds, mode); // Kernels
     433    pmSubtractionKernelsMakeDescription(kernels);
     434    psLogMsg("psModules.imcombine", PS_LOG_INFO, "kernel: %s --> %d elements", kernels->description, num);
    435435
    436436# if (!CENTRAL_DELTA && !ZERO_KERNEL_ZERO_FLUX)
     
    503503
    504504    int num = 0;                        // Number of basis functions
    505     psString params = NULL;             // List of parameters
    506505    for (int i = 0; i < numGaussians; i++) {
    507506        int gaussOrder = orders->data.S32[i]; // Polynomial order to apply to Gaussian
    508         psStringAppend(&params, "(%.1f,%d)", fwhms->data.F32[i], orders->data.S32[i]);
    509507        num += (gaussOrder + 1) * (gaussOrder + 2) / 2;
    510508        num += (11 - gaussOrder - 1);   // include all higher order radial terms
    511509    }
    512510
    513     pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_ISIS_RADIAL, size,
    514                                                               spatialOrder, penalty, bounds, mode); // Kernels
    515     psStringAppend(&kernels->description, "ISIS_RADIAL(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
    516 
    517     psLogMsg("psModules.imcombine", PS_LOG_INFO, "ISIS_RADIAL kernel: %s,%d --> %d elements", params, spatialOrder, num);
    518     psFree(params);
     511    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_ISIS_RADIAL, size, fwhms, orders, spatialOrder, penalty, bounds, mode); // Kernels
     512    pmSubtractionKernelsMakeDescription(kernels);
     513    psLogMsg("psModules.imcombine", PS_LOG_INFO, "kernel: %s --> %d elements", kernels->description, num);
    519514
    520515    // Set the kernel parameters
     
    569564
    570565    int num = 0;                        // Number of basis functions
    571     psString params = NULL;             // List of parameters
    572566    for (int i = 0; i < numGaussians; i++) {
    573567        int gaussOrder = orders->data.S32[i]; // Polynomial order to apply to Gaussian
    574         psStringAppend(&params, "(%.1f,%d)", fwhms->data.F32[i], orders->data.S32[i]);
    575568        num += (gaussOrder + 1) * (gaussOrder + 2) / 2;
    576569    }
    577570
    578     pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_HERM, size,
    579                                                               spatialOrder, penalty, bounds, mode); // Kernels
    580     psStringAppend(&kernels->description, "HERM(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
    581 
    582     psLogMsg("psModules.imcombine", PS_LOG_INFO, "HERM kernel: %s,%d --> %d elements",
    583              params, spatialOrder, num);
    584     psFree(params);
     571    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_HERM, size, fwhms, orders, spatialOrder, penalty, bounds, mode); // Kernels
     572    pmSubtractionKernelsMakeDescription(kernels);
     573    psLogMsg("psModules.imcombine", PS_LOG_INFO, "kernel: %s --> %d elements", kernels->description, num);
    585574
    586575    // Set the kernel parameters
     
    627616
    628617    int num = 0;                        // Number of basis functions
    629     psString params = NULL;             // List of parameters
    630618    for (int i = 0; i < numGaussians; i++) {
    631619        int gaussOrder = orders->data.S32[i]; // Polynomial order to apply to Gaussian
    632         psStringAppend(&params, "(%.1f,%d)", fwhms->data.F32[i], orders->data.S32[i]);
    633620        num += PS_SQR(gaussOrder + 1);
    634621    }
    635622
    636     pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_DECONV_HERM, size,
    637                                                               spatialOrder, penalty, bounds, mode); // Kernels
    638     psStringAppend(&kernels->description, "DECONV_HERM(%d,%s,%d,%.2e)", size, params, spatialOrder, penalty);
    639 
    640     psLogMsg("psModules.imcombine", PS_LOG_INFO, "DECONVOLVED HERM kernel: %s,%d --> %d elements", params, spatialOrder, num);
    641     psFree(params);
     623    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_DECONV_HERM, size, fwhms, orders, spatialOrder, penalty, bounds, mode); // Kernels
     624    pmSubtractionKernelsMakeDescription(kernels);
     625    psLogMsg("psModules.imcombine", PS_LOG_INFO, "kernel: %s --> %d elements", kernels->description, num);
    642626
    643627    // XXXXX hard-wired reference sigma for now of 1.7 pix (== 4.0 pix fwhm == 1.0 arcsec in simtest)
     
    713697
    714698pmSubtractionKernels *pmSubtractionKernelsAlloc(int numBasisFunctions, pmSubtractionKernelsType type,
    715                                                 int size, int spatialOrder, float penalty, psRegion bounds,
     699                                                int size, psVector *fwhms, psVector *orders, int spatialOrder, float penalty, psRegion bounds,
    716700                                                pmSubtractionMode mode)
    717701{
     
    722706    kernels->description = NULL;
    723707    kernels->num = numBasisFunctions;
     708    kernels->fwhms = psMemIncrRefCounter(fwhms);
     709    kernels->orders = psMemIncrRefCounter(orders);
    724710    kernels->u = psVectorAlloc(numBasisFunctions, PS_TYPE_S32);
    725711    kernels->v = psVectorAlloc(numBasisFunctions, PS_TYPE_S32);
     
    740726    kernels->size = size;
    741727    kernels->inner = 0;
     728    kernels->binning = 0;
     729    kernels->ringsOrder = 0;
    742730    kernels->spatialOrder = spatialOrder;
    743731    kernels->bgOrder = 0;
     
    745733    kernels->solution1 = NULL;
    746734    kernels->solution2 = NULL;
     735    kernels->solution1err = NULL;
     736    kernels->solution2err = NULL;
    747737    kernels->mean = NAN;
    748738    kernels->rms = NAN;
     
    823813    int num = PS_SQR(2 * size + 1) - 1; // Number of basis functions
    824814
    825     pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(0, PM_SUBTRACTION_KERNEL_POIS, size,
    826                                                               spatialOrder, penalty, bounds, mode); // Kernels
    827     psStringAppend(&kernels->description, "POIS(%d,%d,%.2e)", size, spatialOrder, penalty);
    828     psLogMsg("psModules.imcombine", PS_LOG_INFO, "POIS kernel: %d,%d --> %d elements",
    829              size, spatialOrder, num);
     815    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(0, PM_SUBTRACTION_KERNEL_POIS, size, NULL, NULL, spatialOrder, penalty, bounds, mode); // Kernels
     816    pmSubtractionKernelsMakeDescription(kernels);
     817    psLogMsg("psModules.imcombine", PS_LOG_INFO, "kernel: %s --> %d elements", kernels->description, num);
    830818
    831819    if (!p_pmSubtractionKernelsAddGrid(kernels, 0, size)) {
     
    871859    psTrace("psModules.imcombine", 3, "Number of basis functions: %d\n", num);
    872860
    873     pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_SPAM, size,
    874                                                               spatialOrder, penalty, bounds, mode); // Kernels
     861    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_SPAM, size, NULL, NULL, spatialOrder, penalty, bounds, mode); // Kernels
    875862    kernels->inner = inner;
    876     psStringAppend(&kernels->description, "SPAM(%d,%d,%d,%d,%.2e)", size, inner, binning, spatialOrder,
    877                    penalty);
    878 
    879     psLogMsg("psModules.imcombine", PS_LOG_INFO, "SPAM kernel: %d,%d,%d,%d --> %d elements",
    880              size, inner, binning, spatialOrder, num);
     863    kernels->binning = binning;
     864    pmSubtractionKernelsMakeDescription(kernels);
     865    psLogMsg("psModules.imcombine", PS_LOG_INFO, "kernel: %s --> %d elements", kernels->description, num);
    881866
    882867    kernels->uStop = psVectorAlloc(num, PS_TYPE_S32);
     
    970955    psTrace("psModules.imcombine", 3, "Number of basis functions: %d\n", num);
    971956
    972     pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_FRIES, size,
    973                                                               spatialOrder, penalty, bounds, mode); // Kernels
     957    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_FRIES, size, NULL, NULL, spatialOrder, penalty, bounds, mode); // Kernels
    974958    kernels->inner = inner;
    975     psStringAppend(&kernels->description, "FRIES(%d,%d,%d,%.2e)", size, inner, spatialOrder, penalty);
    976 
    977     psLogMsg("psModules.imcombine", PS_LOG_INFO, "FRIES kernel: %d,%d,%d --> %d elements",
    978              size, inner, spatialOrder, num);
     959    pmSubtractionKernelsMakeDescription(kernels);
     960    psLogMsg("psModules.imcombine", PS_LOG_INFO, "kernel: %s --> %d elements", kernels->description, num);
    979961
    980962    kernels->uStop = psVectorAlloc(num, PS_TYPE_S32);
     
    10531035    PS_ASSERT_INT_LESS_THAN(inner, size, NULL);
    10541036
    1055     pmSubtractionKernels *kernels = p_pmSubtractionKernelsRawISIS(size, spatialOrder, fwhms, orders,
    1056                                                                   penalty, bounds, mode); // Kernels
     1037    pmSubtractionKernels *kernels = p_pmSubtractionKernelsRawISIS(size, spatialOrder, fwhms, orders, penalty, bounds, mode); // Kernels
    10571038    kernels->type = PM_SUBTRACTION_KERNEL_GUNK;
    1058     psStringPrepend(&kernels->description, "GUNK=");
    1059     psStringAppend(&kernels->description, "+POIS(%d,%d)", inner, spatialOrder);
     1039    kernels->inner = inner;
     1040    pmSubtractionKernelsMakeDescription(kernels);
     1041    psLogMsg("psModules.imcombine", PS_LOG_INFO, "kernel: %s --> %d elements", kernels->description, (int) kernels->num);
    10601042
    10611043    int numISIS = kernels->num;         // Number of ISIS kernels
     
    11001082    int num = numRings * numPoly; // Total number of basis functions
    11011083
    1102     pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_RINGS, size,
    1103                                                               spatialOrder, penalty, bounds, mode); // Kernels
     1084    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_RINGS, size, NULL, NULL, spatialOrder, penalty, bounds, mode); // Kernels
    11041085    kernels->inner = inner;
    1105     psStringAppend(&kernels->description, "RINGS(%d,%d,%d,%d,%.2e)", size, inner, ringsOrder, spatialOrder,
    1106                    penalty);
    1107 
    1108     psLogMsg("psModules.imcombine", PS_LOG_INFO, "RINGS kernel: %d,%d,%d,%d --> %d elements",
    1109              size, inner, ringsOrder, spatialOrder, num);
     1086    kernels->ringsOrder = ringsOrder;
     1087    pmSubtractionKernelsMakeDescription(kernels);
     1088    psLogMsg("psModules.imcombine", PS_LOG_INFO, "kernel: %s --> %d elements", kernels->description, num);
    11101089
    11111090    // Set the Gaussian kernel parameters
     
    14051384}
    14061385
     1386bool pmSubtractionKernelsMakeDescription(pmSubtractionKernels *kernels) {
     1387
     1388    // free if it exists
     1389    psFree (kernels->description);
     1390
     1391    // generate the description parameter string
     1392    psString params = NULL;
     1393    if (kernels->fwhms) {
     1394        for (int i = 0; i < kernels->fwhms->n; i++) {
     1395            psStringAppend(&params, "(%.1f,%d)", kernels->fwhms->data.F32[i], kernels->orders->data.S32[i]);
     1396        }
     1397    }
     1398
     1399    switch (kernels->type) {
     1400      case PM_SUBTRACTION_KERNEL_ISIS:
     1401        psStringAppend (&kernels->description, "ISIS(%d,%s,%d,%.2e)", kernels->size, params, kernels->spatialOrder, kernels->penalty);
     1402        break;
     1403
     1404      case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     1405        psStringAppend(&kernels->description, "ISIS_RADIAL(%d,%s,%d,%.2e)", kernels->size, params, kernels->spatialOrder, kernels->penalty);
     1406        break;
     1407
     1408      case PM_SUBTRACTION_KERNEL_HERM:
     1409        psStringAppend(&kernels->description, "HERM(%d,%s,%d,%.2e)", kernels->size, params, kernels->spatialOrder, kernels->penalty);
     1410        break;
     1411
     1412      case PM_SUBTRACTION_KERNEL_DECONV_HERM:
     1413        psStringAppend(&kernels->description, "DECONV_HERM(%d,%s,%d,%.2e)", kernels->size, params, kernels->spatialOrder, kernels->penalty);
     1414        break;
     1415
     1416      case PM_SUBTRACTION_KERNEL_POIS:
     1417        psStringAppend(&kernels->description, "POIS(%d,%d,%.2e)", kernels->size, kernels->spatialOrder, kernels->penalty);
     1418        break;
     1419
     1420      case PM_SUBTRACTION_KERNEL_SPAM:
     1421        psStringAppend(&kernels->description, "SPAM(%d,%d,%d,%d,%.2e)", kernels->size, kernels->inner, kernels->binning, kernels->spatialOrder, kernels->penalty);
     1422        break;
     1423
     1424      case PM_SUBTRACTION_KERNEL_FRIES:
     1425        psStringAppend(&kernels->description, "FRIES(%d,%d,%d,%.2e)", kernels->size, kernels->inner, kernels->spatialOrder, kernels->penalty);
     1426        break;
     1427
     1428        // Grid United with Normal Kernel [description: GUNK=ISIS(...)+POIS(...)]
     1429      case PM_SUBTRACTION_KERNEL_GUNK:
     1430        psStringAppend(&kernels->description, "GUNK=ISIS(%d,%s,%d,%.2e)", kernels->size, params, kernels->spatialOrder, kernels->penalty);
     1431        psStringAppend(&kernels->description, "+POIS(%d,%d)", kernels->inner, kernels->spatialOrder);
     1432        break;
     1433       
     1434      case PM_SUBTRACTION_KERNEL_RINGS:
     1435        psStringAppend(&kernels->description, "RINGS(%d,%d,%d,%d,%.2e)", kernels->size, kernels->inner, kernels->ringsOrder, kernels->spatialOrder, kernels->penalty);
     1436        break;
     1437
     1438      default:
     1439        psAbort("unknown kernel");
     1440    }
     1441    psFree (params);
     1442    return true;
     1443}
     1444
    14071445pmSubtractionKernels *pmSubtractionKernelsCopy(const pmSubtractionKernels *in)
    14081446{
     
    14351473    out->solution1 = in->solution1 ? psVectorCopy(NULL, in->solution1, PS_TYPE_F64) : NULL;
    14361474    out->solution2 = in->solution2 ? psVectorCopy(NULL, in->solution2, PS_TYPE_F64) : NULL;
     1475    out->solution1err = in->solution1err ? psVectorCopy(NULL, in->solution1err, PS_TYPE_F64) : NULL;
     1476    out->solution2err = in->solution2err ? psVectorCopy(NULL, in->solution2err, PS_TYPE_F64) : NULL;
    14371477    out->sampleStamps = psMemIncrRefCounter(in->sampleStamps);
    14381478
  • trunk/psModules/src/imcombine/pmSubtractionKernels.h

    r29601 r30622  
    22#define PM_SUBTRACTION_KERNELS_H
    33
    4 #include <string.h>
    5 #include <pslib.h>
    6 
    7 /// Type of subtraction kernel
    8 typedef enum {
    9     PM_SUBTRACTION_KERNEL_NONE,         ///< Nothing --- an error
    10     PM_SUBTRACTION_KERNEL_POIS,         ///< Pan-STARRS Optimal Image Subtraction --- delta functions
    11     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
    15     PM_SUBTRACTION_KERNEL_SPAM,         ///< Summed Pixels for Advanced Matching --- summed delta functions
    16     PM_SUBTRACTION_KERNEL_FRIES,        ///< Fibonacci Radius Increases Excellence of Subtraction
    17     PM_SUBTRACTION_KERNEL_GUNK,         ///< Grid United with Normal Kernel --- POIS and ISIS hybrid
    18     PM_SUBTRACTION_KERNEL_RINGS,        ///< Rings Instead of the Normal Gaussian Subtraction
    19 } pmSubtractionKernelsType;
    20 
    21 /// Modes --- specifies which image to convolve
    22 typedef enum {
    23     PM_SUBTRACTION_MODE_ERR,            // Error in the mode
    24     PM_SUBTRACTION_MODE_1,              // Convolve image 1
    25     PM_SUBTRACTION_MODE_2,              // Convolve image 2
    26     PM_SUBTRACTION_MODE_UNSURE,         // Not sure yet which image to convolve so try to satisfy both
    27     PM_SUBTRACTION_MODE_DUAL,           // Dual convolution
    28 } pmSubtractionMode;
    29 
    30 /// Kernels specification
    31 typedef struct {
    32     pmSubtractionKernelsType type;      ///< Type of kernels --- allowing the use of multiple kernels
    33     psString description;               ///< Description of the kernel parameters
    34     int xMin, xMax, yMin, yMax;         ///< Bounds of image (for normalisation)
    35     long num;                           ///< Number of kernel components (not including the spatial ones)
    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)
    38     psVector *uStop, *vStop;            ///< Width of kernel element (SPAM,FRIES only)
    39     psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (for ISIS, HERM or DECONV_HERM)
    40     float penalty;                      ///< Penalty for wideness
    41     psVector *penalties1;               ///< Penalty for each kernel component
    42     psVector *penalties2;               ///< Penalty for each kernel component
    43     bool havePenalties;                 ///< flag to test if we have already calculated the penalties or not.
    44     int size;                           ///< The half-size of the kernel
    45     int inner;                          ///< The size of an inner region
    46     int spatialOrder;                   ///< The spatial order of the kernels
    47     int bgOrder;                        ///< The order for the background fitting
    48     pmSubtractionMode mode;             ///< Mode for subtraction
    49     psVector *solution1, *solution2;    ///< Solution for the PSF matching
    50     // Quality information
    51     float mean, rms;                    ///< Mean and RMS of chi^2 from stamps
    52     int numStamps;                      ///< Number of good stamps
    53     float fResSigmaMean;                ///< mean fractional stdev of residuals
    54     float fResSigmaStdev;               ///< stdev of fractional stdev of residuals
    55     float fResOuterMean;                ///< mean fractional positive swing in residuals
    56     float fResOuterStdev;               ///< stdev of fractional positive swing in residuals
    57     float fResTotalMean;                ///< mean fractional negative swing in residuals
    58     float fResTotalStdev;               ///< stdev of fractional negative swing in residuals
    59     psArray *sampleStamps;              ///< array of brightest set of stamps for output visualizations
    60 } pmSubtractionKernels;
    61 
    62 // pmSubtractionKernels->preCalc is an array of pmSubtractionKernelPreCalc structures
    63 typedef struct {
    64     psVector *uCoords;                  // used by RINGS
    65     psVector *vCoords;                  // used by RINGS
    66     psVector *poly;                     // used by RINGS
    67 
    68     psVector *xKernel;                  // used by ISIS, HERM, DECONV_HERM
    69     psVector *yKernel;                  // used by ISIS, HERM, DECONV_HERM
    70     psKernel *kernel;                   // used by ISIS, HERM, DECONV_HERM
    71 } pmSubtractionKernelPreCalc;
     4// #include <string.h>
     5// #include <pslib.h>
    726
    737// Assertion to check pmSubtractionKernels
     
    16296                                                pmSubtractionKernelsType type, ///< Kernel type
    16397                                                int size, ///< Half-size of kernel
     98                                                psVector *fwhms, ///< requested kernel basis function
     99                                                psVector *orders,
    164100                                                int spatialOrder, ///< Order of spatial variations
    165101                                                float penalty, ///< Penalty for wideness
     
    303239    );
    304240
     241bool pmSubtractionKernelsMakeDescription(pmSubtractionKernels *kernels);
     242
     243
    305244/// Copy kernels
    306245///
  • trunk/psModules/src/imcombine/pmSubtractionMask.c

    r27402 r30622  
    77
    88#include "pmErrorCodes.h"
     9#include "pmFPA.h"
     10#include "pmSubtractionTypes.h"
    911#include "pmSubtraction.h"
    1012#include "pmSubtractionKernels.h"
  • trunk/psModules/src/imcombine/pmSubtractionMatch.c

    r29596 r30622  
    1111#include "pmFPA.h"
    1212#include "pmHDUUtils.h"
     13#include "pmSubtractionTypes.h"
     14#include "pmSubtraction.h"
    1315#include "pmSubtractionParams.h"
    1416#include "pmSubtractionKernels.h"
    1517#include "pmSubtractionStamps.h"
    1618#include "pmSubtractionEquation.h"
    17 #include "pmSubtraction.h"
    1819#include "pmSubtractionAnalysis.h"
    1920#include "pmSubtractionMask.h"
     
    2728
    2829static bool useFFT = true;              // Do convolutions using FFT
    29 
    30 # define SUBMODE PM_SUBTRACTION_EQUATION_ALL
    3130
    3231//#define TESTING
     
    5958}
    6059
    61 
    62 static bool subtractionGetStamps(pmSubtractionStampList **stamps, // Stamps to read
    63                                  const pmReadout *ro1, // Readout 1
    64                                  const pmReadout *ro2, // Readout 2
    65                                  const psImage *subMask, // Mask for subtraction, or NULL
    66                                  psImage *variance,  // Variance map
    67                                  const psRegion *region, // Region of interest
    68                                  float thresh1,  // Threshold for stamp finding on readout 1
    69                                  float thresh2,  // Threshold for stamp finding on readout 2
    70                                  float stampSpacing, // Spacing between stamps
    71                                  float normFrac,     // Fraction of flux in window for normalisation window
    72                                  float sysError,     // Relative systematic error in images
    73                                  float skyError,     // Relative systematic error in images
    74                                  int size,         // Kernel half-size
    75                                  int footprint,     // Convolution footprint for stamps
    76                                  pmSubtractionMode mode // Mode for subtraction
    77     )
    78 {
    79     PS_ASSERT_PTR_NON_NULL(stamps, false);
    80     PM_ASSERT_READOUT_NON_NULL(ro1, false);
    81     PM_ASSERT_READOUT_NON_NULL(ro2, false);
    82     PS_ASSERT_IMAGE_NON_NULL(subMask, false);
    83     PS_ASSERT_IMAGE_NON_NULL(variance, false);
    84     PS_ASSERT_PTR_NON_NULL(region, false);
    85 
    86     psTrace("psModules.imcombine", 3, "Finding stamps...\n");
    87 
    88     psImage *image1 = ro1 ? ro1->image : NULL, *image2 = ro2 ? ro2->image : NULL; // Images of interest
    89 
    90     *stamps = pmSubtractionStampsFind(*stamps, image1, image2, subMask, region, thresh1, thresh2,
    91                                       size, footprint, stampSpacing, normFrac, sysError, skyError, mode);
    92     if (!*stamps) {
    93         psError(psErrorCodeLast(), false, "Unable to find stamps.");
    94         return false;
    95     }
    96 
    97     memCheck("  find stamps");
    98 
    99     psTrace("psModules.imcombine", 3, "Extracting stamps...\n");
    100     if (!pmSubtractionStampsExtract(*stamps, ro1->image, ro2->image, variance, size, *region)) {
    101         psError(psErrorCodeLast(), false, "Unable to extract stamps.");
    102         return false;
    103     }
    104 
    105     memCheck("   extract stamps");
    106     pmSubtractionVisualPlotStamps(*stamps, (pmReadout *) ro1);
    107     return true;
    108 }
    109 
    11060// Check input arguments
    11161static bool subtractionMatchCheck(pmReadout *conv1, pmReadout *conv2, // Convolved images
     
    12373                                  float badFrac,   // Maximum fraction of bad input pixels to accept
    12474                                  pmSubtractionMode subMode // Mode of subtraction
    125                                   )
     75    )
    12676{
    12777    if (subMode != PM_SUBTRACTION_MODE_2) {
     
    473423}
    474424
     425bool pmSubtractionMatchAttempt(pmSubtractionQuality **bestMatch, pmSubtractionKernels *kernels, pmSubtractionStampList *stamps, pmSubtractionMode mode, int spatialOrder, bool final) {
     426
     427    pmSubtractionMode nativeMode = kernels->mode;
     428    pmSubtractionMode nativeOrder = kernels->spatialOrder;
     429
     430    kernels->mode = mode;
     431    kernels->spatialOrder = spatialOrder;
     432
     433    // we always need to recalculate the matrix equation elements...
     434    pmSubtractionStampsResetStatus(stamps);
     435
     436    psTrace("psModules.imcombine", 3, "Convolving stamps as needed...\n");
     437    if (!pmSubtractionConvolveStamps(stamps, kernels)) {
     438        psError(psErrorCodeLast(), false, "Unable to convolve stamps.");
     439        return false;
     440    }
     441
     442    // step 1: generate the elements of the matrix equation Ax = B
     443    psTrace("psModules.imcombine", 3, "Calculating kernel equations...\n");
     444    if (!pmSubtractionCalculateEquation(stamps, kernels)) {
     445        psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     446        return false;
     447    }
     448                   
     449    // step 2: solve the matrix equation Ax = B
     450    psTrace("psModules.imcombine", 3, "Solving kernel equations...\n");
     451    if (!pmSubtractionSolveEquation(kernels, stamps)) {
     452        psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     453        return false;
     454    }
     455    memCheck("  solve equation");
     456
     457    // calculate the score for this model fit attempt
     458    // XXX store the chisq, flux and moments for stamp rejection
     459    pmSubtractionCalculateChisqAndMoments(bestMatch, stamps, kernels); // Stamp deviations
     460
     461    // display the input and model stamps
     462    pmSubtractionVisualShowFit(stamps, kernels);
     463    pmSubtractionVisualPlotFit(kernels);
     464    pmSubtractionVisualPlotConvKernels(kernels);
     465
     466    // reset the kernel if desired (on final pass, do not reset)
     467    if (!final) {
     468        kernels->mode = nativeMode;
     469        kernels->spatialOrder = nativeOrder;
     470    } else {
     471      pmSubtractionKernelsMakeDescription(kernels);
     472      psLogMsg("psModules.imcombine", PS_LOG_INFO, "final kernel: %s", kernels->description);
     473    }
     474    return true;
     475}
    475476
    476477bool pmSubtractionMatch(pmReadout *conv1, pmReadout *conv2, const pmReadout *ro1, const pmReadout *ro2,
     
    478479                        const psArray *sources, const char *stampsName,
    479480                        pmSubtractionKernelsType type, int size, int spatialOrder,
    480                         const psVector *isisWidths, const psVector *isisOrders,
     481                        psVector *isisWidths, const psVector *isisOrders,
    481482                        int inner, int ringsOrder, int binning, float penalty,
    482483                        bool optimum, const psVector *optFWHMs, int optOrder, float optThreshold,
     
    559560    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
    560561
     562    pmSubtractionQuality *bestMatch = NULL;
     563
     564    int N_TEST_MODES;
     565    int N_TEST_ORDER = spatialOrder;
     566
     567    pmSubtractionMode TestModes[3];
     568    switch (subMode) {
     569      case PM_SUBTRACTION_MODE_1:
     570        N_TEST_MODES = 1;
     571        TestModes[0] = PM_SUBTRACTION_MODE_1;
     572        break;
     573      case PM_SUBTRACTION_MODE_2:
     574        N_TEST_MODES = 1;
     575        TestModes[0] = PM_SUBTRACTION_MODE_2;
     576        break;
     577      case PM_SUBTRACTION_MODE_DUAL:
     578        N_TEST_MODES = 3;
     579        TestModes[0] = PM_SUBTRACTION_MODE_1;
     580        TestModes[1] = PM_SUBTRACTION_MODE_2;
     581        TestModes[2] = PM_SUBTRACTION_MODE_DUAL;
     582        break;
     583      default:
     584        psError(psErrorCodeLast(), false, "For now, only modes 1, 2, and DUAL are supported.");
     585        goto MATCH_ERROR;
     586    }
     587   
    561588    memCheck("start");
    562589
     
    628655            regionString = psRegionToString(*region);
    629656            psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "Iso-kernel region: %s out of %d,%d\n",
    630                     regionString, numCols, numRows);
     657                     regionString, numCols, numRows);
    631658
    632659            if (stampsName && strlen(stampsName) > 0) {
     
    640667            }
    641668
    642             // We get the stamps here; we will also attempt to get stamps at the first iteration, but it
    643             // doesn't matter.
    644             if (!subtractionGetStamps(&stamps, ro1, ro2, subMask, variance, region, stampThresh1, stampThresh2,
    645                                       stampSpacing, normFrac, sysError, skyError, size, footprint, subMode)) {
    646                 goto MATCH_ERROR;
    647             }
    648 
    649 
    650             // generate the window function from the set of stamps
    651             if (!pmSubtractionStampsGetWindow(stamps, size)) {
    652                 psError(psErrorCodeLast(), false, "Unable to get stamp window.");
    653                 goto MATCH_ERROR;
    654             }
    655 
    656             // Define kernel basis functions
    657             if (optimum && (type == PM_SUBTRACTION_KERNEL_ISIS || type == PM_SUBTRACTION_KERNEL_GUNK)) {
    658                 kernels = pmSubtractionKernelsOptimumISIS(type, size, inner, spatialOrder,
    659                                                           optFWHMs, optOrder, stamps, footprint,
    660                                                           optThreshold, penalty, bounds, subMode);
    661                 if (!kernels) {
    662                     psErrorClear();
    663                     psWarning("Unable to derive optimum ISIS kernel --- switching to default.");
    664                 }
    665             }
    666             if (kernels == NULL) {
    667                 // Not an ISIS/GUNK kernel, or the optimum kernel search failed
    668                 kernels = pmSubtractionKernelsGenerate(type, size, spatialOrder, isisWidths, isisOrders,
    669                                                        inner, binning, ringsOrder, penalty, bounds, subMode);
    670             }
    671 
    672             memCheck("kernels");
    673 
    674             if (subMode == PM_SUBTRACTION_MODE_UNSURE) {
    675                 pmSubtractionMode newMode = pmSubtractionBestMode(&stamps, &kernels, subMask, rej);
    676                 switch (newMode) {
    677                   case PM_SUBTRACTION_MODE_1:
    678                     psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolving image 1 to match image 2.");
    679                     break;
    680                   case PM_SUBTRACTION_MODE_2:
    681                     psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolving image 2 to match image 1.");
    682                     break;
    683                   default:
    684                     psError(psErrorCodeLast(), false, "Unable to determine subtraction order.");
    685                     goto MATCH_ERROR;
    686                 }
    687                 subMode = newMode;
    688             }
    689 
    690             int numRejected = -1;       // Number of rejected stamps in each iteration
    691             for (int k = 0; k < iter && numRejected != 0; k++) {
    692                 psLogMsg("psModules.imcombine", PS_LOG_INFO, "Iteration %d.", k);
    693 
    694                 if (!subtractionGetStamps(&stamps, ro1, ro2, subMask, variance, region,
    695                                           stampThresh1, stampThresh2, stampSpacing, normFrac,
    696                                           sysError, skyError, size, footprint, subMode)) {
    697                     goto MATCH_ERROR;
    698                 }
    699 
    700                 // generate the window function from the set of stamps
    701                 if (!pmSubtractionStampsGetWindow(stamps, size)) {
    702                     psError(psErrorCodeLast(), false, "Unable to get stamps window.");
    703                     goto MATCH_ERROR;
    704                 }
     669            bool tryAgain = true;
     670            while (tryAgain) {
     671                // We get the stamps here; we will also attempt to get stamps at the first iteration, but it
     672                // doesn't matter.
     673                if (!pmSubtractionStampsSelect(&stamps, ro1, ro2, subMask, variance, region, stampThresh1, stampThresh2,
     674                                               stampSpacing, normFrac, sysError, skyError, size, footprint, subMode)) {
     675                    goto MATCH_ERROR;
     676                }
     677
     678                // generate the window function from the set of stamps
     679                if (!pmSubtractionStampsGetWindow(&tryAgain, stamps, size)) {
     680                    // if we failed, it might be due to the desired normWindow being larger than the current footprint.
     681                    // in this case, just adjust the footprint and try again.
     682                    if (tryAgain) {
     683                        // keep the border constant
     684                        int border = footprint - size;
     685                        size = PS_MAX(stamps->normWindow1, stamps->normWindow2) + 2;
     686                        footprint = size + border;
     687
     688                        // we need to reconstruct everything, so just free the stamps here and retry
     689                        psFree(stamps);
     690                    } else {
     691                        // unrecoverable error
     692                        psError(psErrorCodeLast(), false, "Unable to get stamp window.");
     693                        goto MATCH_ERROR;
     694                    }
     695                }
     696            }
     697
     698            // check on the kernel scaling -- if the kron-based radial moments are very different, adjust to match them
     699            {
     700                // float fwhm1;
     701                // float fwhm2;
     702                // pmSubtractionGetFWHMs(&fwhm1, &fwhm2);
     703                // psAssert(isfinite(fwhm1), "fwhm 1 not set");
     704                // psAssert(isfinite(fwhm2), "fwhm 2 not set");
     705
     706                // XXX this is BAD: depends on the relationship below:
     707                // stamps->normWindow1 = 2.75*R1;
     708                // stamps->normWindow2 = 2.75*R2;
     709                float radMoment1 = stamps->normWindow1 / 2.75;
     710                float radMoment2 = stamps->normWindow2 / 2.75;
     711                pmSubtractionParamsScale(NULL, NULL, isisWidths, radMoment1, radMoment2);
     712
     713                // float maxFWHM = PS_MAX(fwhm1, fwhm2);
     714                // float maxRadial = PS_MAX(radMoment1, radMoment2);
     715               
     716                // if (fabs(2.0*(maxFWHM - maxRadial)/(maxFWHM + maxRadial)) > 0.25) {
     717                // if (1) {
     718                //
     719                //     float scale = maxRadial / maxFWHM;
     720                //     psLogMsg ("psModules.imcombine", PS_LOG_INFO, "Kron and FWHM scales are quite different, re-scale by %f to use Kron", scale);
     721                //     
     722                //     for (int i = 0; i < isisWidths->n; i++) {
     723                //      isisWidths->data.F32[i] *= scale;
     724                //     }
     725                // }
     726            }
     727
     728            // Define kernel basis functions
     729            if (optimum && (type == PM_SUBTRACTION_KERNEL_ISIS || type == PM_SUBTRACTION_KERNEL_GUNK)) {
     730                kernels = pmSubtractionKernelsOptimumISIS(type, size, inner, spatialOrder,
     731                                                          optFWHMs, optOrder, stamps, footprint,
     732                                                          optThreshold, penalty, bounds, subMode);
     733                if (!kernels) {
     734                    psErrorClear();
     735                    psWarning("Unable to derive optimum ISIS kernel --- switching to default.");
     736                }
     737            }
     738            if (kernels == NULL) {
     739                // Not an ISIS/GUNK kernel, or the optimum kernel search failed
     740                kernels = pmSubtractionKernelsGenerate(type, size, spatialOrder, isisWidths, isisOrders,
     741                                                       inner, binning, ringsOrder, penalty, bounds, subMode);
     742            }
     743
     744            memCheck("kernels");
     745
     746            if (subMode == PM_SUBTRACTION_MODE_UNSURE) {
     747                pmSubtractionMode newMode = pmSubtractionBestMode(&stamps, &kernels, subMask, rej);
     748                switch (newMode) {
     749                  case PM_SUBTRACTION_MODE_1:
     750                    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolving image 1 to match image 2.");
     751                    break;
     752                  case PM_SUBTRACTION_MODE_2:
     753                    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolving image 2 to match image 1.");
     754                    break;
     755                  default:
     756                    psError(psErrorCodeLast(), false, "Unable to determine subtraction order.");
     757                    goto MATCH_ERROR;
     758                }
     759                subMode = newMode;
     760            }
     761
     762            int numRejected = -1;       // Number of rejected stamps in each iteration
     763            for (int k = 0; (k < iter) && (numRejected != 0); k++) {
     764                psLogMsg("psModules.imcombine", PS_LOG_INFO, "Iteration %d.", k);
     765
     766                bool tryAgain = true;
     767                while (tryAgain) {
     768                    // We get the stamps here; we will also attempt to get stamps at the first iteration, but it
     769                    // doesn't matter.
     770                    if (!pmSubtractionStampsSelect(&stamps, ro1, ro2, subMask, variance, region, stampThresh1, stampThresh2,
     771                                                   stampSpacing, normFrac, sysError, skyError, size, footprint, subMode)) {
     772                        goto MATCH_ERROR;
     773                    }
     774
     775                    // generate the window function from the set of stamps
     776                    if (!pmSubtractionStampsGetWindow(&tryAgain, stamps, size)) {
     777                        // if we failed, it might be due to the desired normWindow being larger than the current footprint.
     778                        // in this case, just adjust the footprint and try again.
     779                        if (tryAgain) {
     780                            footprint = PS_MAX(stamps->normWindow1, stamps->normWindow2) + 2;
     781
     782                            // we need to reconstruct everything, so just free the stamps here and retry
     783                            psFree(stamps);
     784                        } else {
     785                            // unrecoverable error
     786                            psError(psErrorCodeLast(), false, "Unable to get stamp window.");
     787                            goto MATCH_ERROR;
     788                        }
     789                    }
     790                }
    705791
    706792                // step 0 : calculate the normalizations, pass along to the next steps via stamps->normValue
    707                 psTrace("psModules.imcombine", 3, "Calculating normalization...\n");
    708                 if (!pmSubtractionCalculateNormalization(stamps, kernels->mode)) {
    709                     psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    710                     goto MATCH_ERROR;
    711                 }
    712 
    713                 // step 0b : calculate the moments, pass along to the next steps via stamps->normValue
    714                 // XXX this step is now skipped -- delete
    715                 psTrace("psModules.imcombine", 3, "Calculating normalization...\n");
    716                 if (!pmSubtractionCalculateMoments(kernels, stamps)) {
    717                     psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    718                     goto MATCH_ERROR;
    719                 }
    720 
    721                 // step 1: generate the elements of the matrix equation Ax = B
    722                 psTrace("psModules.imcombine", 3, "Calculating kernel equations...\n");
    723                 if (!pmSubtractionCalculateEquation(stamps, kernels, SUBMODE)) {
    724                     psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    725                     goto MATCH_ERROR;
    726                 }
    727 
    728                 // step 2: solve the matrix equation Ax = B
    729                 psTrace("psModules.imcombine", 3, "Solving kernel equations...\n");
    730                 if (!pmSubtractionSolveEquation(kernels, stamps, SUBMODE)) {
    731                     psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    732                     goto MATCH_ERROR;
    733                 }
    734                 memCheck("  solve equation");
    735 
    736                 pmSubtractionVisualPlotConvKernels(kernels);
    737 
    738                 psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    739                 if (!deviations) {
    740                     psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
    741                     goto MATCH_ERROR;
    742                 }
    743                 memCheck("   calculate deviations");
    744 
    745                 psTrace("psModules.imcombine", 3, "Rejecting stamps...\n");
    746                 numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, rej);
    747                 if (numRejected < 0) {
    748                     psError(psErrorCodeLast(), false, "Unable to reject stamps.");
    749                     psFree(deviations);
    750                     goto MATCH_ERROR;
    751                 }
    752                 psFree(deviations);
    753 
    754                 memCheck("  reject stamps");
    755             }
    756 
    757             // if we hit the max number of iterations and we have rejected stamps, re-solve
    758             if (numRejected > 0) {
    759 
    760                 // step 1: generate the elements of the matrix equation Ax = B
    761                 psTrace("psModules.imcombine", 3, "Calculating equation for normalization...\n");
    762                 if (!pmSubtractionCalculateEquation(stamps, kernels, SUBMODE)) {
    763                     psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    764                     goto MATCH_ERROR;
    765                 }
    766 
    767                 // step 2: solve the matrix equation Ax = B
    768                 psTrace("psModules.imcombine", 3, "Solving equation for kernels...\n");
    769                 if (!pmSubtractionSolveEquation(kernels, stamps, SUBMODE)) {
    770                     psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    771                     goto MATCH_ERROR;
    772                 }
    773 
    774                 pmSubtractionVisualPlotConvKernels(kernels);
    775 
    776                 psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    777                 if (!deviations) {
    778                     psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
    779                     goto MATCH_ERROR;
    780                 }
    781                 pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, NAN);
    782                 psFree(deviations);
    783             }
    784             psFree(stamps);
    785             stamps = NULL;
    786 
    787             memCheck("solution");
    788 
    789             if (!pmSubtractionAnalysis(analysis, header, kernels, region, numCols, numRows)) {
    790                 psError(psErrorCodeLast(), false, "Unable to generate QA data");
    791                 goto MATCH_ERROR;
    792             }
    793             memCheck("diag outputs");
    794 
    795             psTrace("psModules.imcombine", 2, "Convolving...\n");
    796             if (!pmSubtractionConvolve(conv1, conv2, ro1, ro2, subMask, stride, maskBad, maskPoor, poorFrac,
    797                                        kernelError, covarFrac, region, kernels, true, useFFT)) {
    798                 psError(psErrorCodeLast(), false, "Unable to convolve image.");
    799                 goto MATCH_ERROR;
    800             }
    801 
    802             psFree(kernels);
    803             kernels = NULL;
    804         }
     793                psTrace("psModules.imcombine", 3, "Calculating normalization...\n");
     794                if (!pmSubtractionCalculateNormalization(stamps, kernels->mode)) {
     795                    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     796                    goto MATCH_ERROR;
     797                }
     798
     799                // on each iteration, we start from scratch
     800                psFree(bestMatch);
     801
     802                // choose the spatial order and subtraction direction (1, 2, dual)
     803                // XXX need to make these respect recipe somewhat
     804                for (int order = 0; order <= N_TEST_ORDER; order++) {
     805                    for (int j = 0; j < N_TEST_MODES; j++) {
     806                        if (!pmSubtractionMatchAttempt(&bestMatch, kernels, stamps, TestModes[j], order, false)) {
     807                            goto MATCH_ERROR;
     808                        }
     809                    }
     810                }
     811               
     812                // reject the deviant stamps based on the stats of the best match
     813                psTrace("psModules.imcombine", 3, "Rejecting stamps...\n");
     814                numRejected = pmSubtractionRejectStamps(kernels, stamps, bestMatch, subMask, rej);
     815                if (numRejected < 0) {
     816                    psError(psErrorCodeLast(), false, "Unable to reject stamps.");
     817                    goto MATCH_ERROR;
     818                }
     819                memCheck("  reject stamps");
     820            }
     821
     822            // apply the best fit so we are ready to roll
     823            psLogMsg("psModules.imcombine", PS_LOG_INFO, "applying order: %d, mode: %d\n", bestMatch->spatialOrder, bestMatch->mode);
     824            if (!pmSubtractionMatchAttempt(NULL, kernels, stamps, bestMatch->mode, bestMatch->spatialOrder, true)) {
     825                goto MATCH_ERROR;
     826            }
     827            psFree(stamps);
     828            psFree(bestMatch);
     829            memCheck("solution");
     830
     831            if (!pmSubtractionAnalysis(analysis, header, kernels, region, numCols, numRows)) {
     832                psError(psErrorCodeLast(), false, "Unable to generate QA data");
     833                goto MATCH_ERROR;
     834            }
     835            memCheck("diag outputs");
     836
     837            psTrace("psModules.imcombine", 2, "Convolving...\n");
     838            if (!pmSubtractionConvolve(conv1, conv2, ro1, ro2, subMask, stride, maskBad, maskPoor, poorFrac,
     839                                       kernelError, covarFrac, region, kernels, true, useFFT)) {
     840                psError(psErrorCodeLast(), false, "Unable to convolve image.");
     841                goto MATCH_ERROR;
     842            }
     843
     844            psFree(kernels);
     845            kernels = NULL;
     846        }
    805847    }
    806848    psFree(rng);
     
    816858
    817859    if (conv1 && !pmSubtractionBorder(conv1->image, conv1->variance, conv1->mask, size, maskBad)) {
    818         psError(psErrorCodeLast(), false, "Unable to set border of convolved image.");
    819         goto MATCH_ERROR;
     860        psError(psErrorCodeLast(), false, "Unable to set border of convolved image.");
     861        goto MATCH_ERROR;
    820862    }
    821863    if (conv2 && !pmSubtractionBorder(conv2->image, conv2->variance, conv2->mask, size, maskBad)) {
    822         psError(psErrorCodeLast(), false, "Unable to set border of convolved image.");
    823         goto MATCH_ERROR;
     864        psError(psErrorCodeLast(), false, "Unable to set border of convolved image.");
     865        goto MATCH_ERROR;
    824866    }
    825867
     
    832874#ifdef TESTING
    833875    {
    834         if (subMode == PM_SUBTRACTION_MODE_1 || subMode == PM_SUBTRACTION_MODE_DUAL) {
    835             psFits *fits = psFitsOpen("convolved1.fits", "w");
    836             psFitsWriteImage(fits, NULL, conv1->image, 0, NULL);
    837             psFitsClose(fits);
    838         }
    839 
    840         if (subMode == PM_SUBTRACTION_MODE_2 || subMode == PM_SUBTRACTION_MODE_DUAL) {
    841             psFits *fits = psFitsOpen("convolved2.fits", "w");
    842             psFitsWriteImage(fits, NULL, conv2->image, 0, NULL);
    843             psFitsClose(fits);
    844         }
     876        if (subMode == PM_SUBTRACTION_MODE_1 || subMode == PM_SUBTRACTION_MODE_DUAL) {
     877            psFits *fits = psFitsOpen("convolved1.fits", "w");
     878            psFitsWriteImage(fits, NULL, conv1->image, 0, NULL);
     879            psFitsClose(fits);
     880        }
     881
     882        if (subMode == PM_SUBTRACTION_MODE_2 || subMode == PM_SUBTRACTION_MODE_DUAL) {
     883            psFits *fits = psFitsOpen("convolved2.fits", "w");
     884            psFitsWriteImage(fits, NULL, conv2->image, 0, NULL);
     885            psFitsClose(fits);
     886        }
    845887    }
    846888#endif
     
    858900    psFree(variance);
    859901    psFree(rng);
     902    psFree(bestMatch);
    860903    return false;
    861904}
     
    866909// increment).
    867910static int subtractionOrderWidth(const psKernel *kernel, // Image
    868                                 float bg, // Background in image
    869                                 int size, // Maximum size
    870                                 const psArray *models, // Buffer of models
    871                                 const psVector *modelSums // Buffer of model sums
     911                                float bg, // Background in image
     912                                int size, // Maximum size
     913                                const psArray *models, // Buffer of models
     914                                const psVector *modelSums // Buffer of model sums
    872915    )
    873916{
     
    882925    psVector *chi2 = psVectorAlloc(size, PS_TYPE_F32); // chi^2 as a function of radius
    883926    for (int sigma = 0; sigma < size; sigma++) {
    884         double sumFG = 0.0; // Sum for calculating the normalisation of the Gaussian
    885         psKernel *model = models->data[sigma]; // Model of interest
    886         for (int y = yMin; y <= yMax; y++) {
    887             for (int x = xMin; x <= xMax; x++) {
    888                 sumFG += model->kernel[y][x] * (kernel->kernel[y][x] - bg);
    889             }
    890         }
    891         float norm = sumFG * modelSums->data.F64[sigma]; // Normalisation for Gaussian
    892         double sumDev2 = 0.0;           // Sum of square deviations
    893         for (int y = yMin; y <= yMax; y++) {
    894             for (int x = xMin; x <= xMax; x++) {
    895                 float dev = kernel->kernel[y][x] - bg - norm * model->kernel[y][x]; // Deviation
    896                 sumDev2 += PS_SQR(dev);
    897             }
    898         }
    899         chi2->data.F32[sigma] = sumDev2;
     927        double sumFG = 0.0; // Sum for calculating the normalisation of the Gaussian
     928        psKernel *model = models->data[sigma]; // Model of interest
     929        for (int y = yMin; y <= yMax; y++) {
     930            for (int x = xMin; x <= xMax; x++) {
     931                sumFG += model->kernel[y][x] * (kernel->kernel[y][x] - bg);
     932            }
     933        }
     934        float norm = sumFG * modelSums->data.F64[sigma]; // Normalisation for Gaussian
     935        double sumDev2 = 0.0;           // Sum of square deviations
     936        for (int y = yMin; y <= yMax; y++) {
     937            for (int x = xMin; x <= xMax; x++) {
     938                float dev = kernel->kernel[y][x] - bg - norm * model->kernel[y][x]; // Deviation
     939                sumDev2 += PS_SQR(dev);
     940            }
     941        }
     942        chi2->data.F32[sigma] = sumDev2;
    900943    }
    901944
     
    904947    float bestChi2 = INFINITY;          // Best chi^2
    905948    for (int i = 0; i < size; i++) {
    906         if (chi2->data.F32[i] < bestChi2) {
    907             bestChi2 = chi2->data.F32[i];
    908             bestIndex = i;
    909         }
     949        if (chi2->data.F32[i] < bestChi2) {
     950            bestChi2 = chi2->data.F32[i];
     951            bestIndex = i;
     952        }
    910953    }
    911954    psFree(chi2);
     
    916959
    917960bool pmSubtractionOrderStamp(psVector *ratios, psVector *mask, const pmSubtractionStampList *stamps,
    918                              const psArray *models, const psVector *modelSums,
    919                              int index, float bg1, float bg2)
     961                             const psArray *models, const psVector *modelSums,
     962                             int index, float bg1, float bg2)
    920963{
    921964    PS_ASSERT_VECTOR_NON_NULL(ratios, false);
     
    931974    pmSubtractionStamp *stamp = stamps->stamps->data[index]; // Stamp of interest
    932975    psAssert(stamp->status == PM_SUBTRACTION_STAMP_CALCULATE || stamp->status == PM_SUBTRACTION_STAMP_USED,
    933              "We checked this earlier.");
     976             "We checked this earlier.");
    934977
    935978    // Widths of stars
     
    938981
    939982    if (width1 == 0 || width2 == 0) {
    940         ratios->data.F32[index] = NAN;
    941         mask->data.PS_TYPE_VECTOR_MASK_DATA[index] = 0xff;
     983        ratios->data.F32[index] = NAN;
     984        mask->data.PS_TYPE_VECTOR_MASK_DATA[index] = 0xff;
    942985    } else {
    943         ratios->data.F32[index] = (float)width1 / (float)width2;
    944         mask->data.PS_TYPE_VECTOR_MASK_DATA[index] = 0;
    945         psTrace("psModules.imcombine", 3, "Stamp %d (%.1f,%.1f) widths: %d, %d --> %f\n",
    946                 index, stamp->x, stamp->y, width1, width2, ratios->data.F32[index]);
     986        ratios->data.F32[index] = (float)width1 / (float)width2;
     987        mask->data.PS_TYPE_VECTOR_MASK_DATA[index] = 0;
     988        psTrace("psModules.imcombine", 3, "Stamp %d (%.1f,%.1f) widths: %d, %d --> %f\n",
     989                index, stamp->x, stamp->y, width1, width2, ratios->data.F32[index]);
    947990    }
    948991
     
    9781021    psVector *modelSums = psVectorAlloc(size, PS_TYPE_F64); // Gaussian model sums
    9791022    for (int sigma = 0; sigma < size; sigma++) {
    980         psKernel *model = psKernelAlloc(-size, size, -size, size); // Gaussian model
    981         float invSigma2 = 1.0 / (float)PS_SQR(1 + sigma); // Inverse sigma squared
    982         double sumGG = 0.0;         // Sum of square of Gaussian
    983         for (int y = -size; y <= size; y++) {
    984             int y2 = PS_SQR(y);     // y squared
    985             for (int x = -size; x <= size; x++) {
    986                 float rad2 = PS_SQR(x) + y2; // Radius squared
    987                 float value = expf(-rad2 * invSigma2); // Model value
    988                 model->kernel[y][x] = value;
    989                 sumGG += PS_SQR(value);
    990             }
    991         }
    992         models->data[sigma] = model;
    993         modelSums->data.F64[sigma] = 1.0 / sumGG;
     1023        psKernel *model = psKernelAlloc(-size, size, -size, size); // Gaussian model
     1024        float invSigma2 = 1.0 / (float)PS_SQR(1 + sigma); // Inverse sigma squared
     1025        double sumGG = 0.0;         // Sum of square of Gaussian
     1026        for (int y = -size; y <= size; y++) {
     1027            int y2 = PS_SQR(y);     // y squared
     1028            for (int x = -size; x <= size; x++) {
     1029                float rad2 = PS_SQR(x) + y2; // Radius squared
     1030                float value = expf(-rad2 * invSigma2); // Model value
     1031                model->kernel[y][x] = value;
     1032                sumGG += PS_SQR(value);
     1033            }
     1034        }
     1035        models->data[sigma] = model;
     1036        modelSums->data.F64[sigma] = 1.0 / sumGG;
    9941037    }
    9951038
    9961039    // Fit models to stamps
    9971040    for (int i = 0; i < stamps->num; i++) {
    998         pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    999         if (stamp->status != PM_SUBTRACTION_STAMP_CALCULATE && stamp->status != PM_SUBTRACTION_STAMP_USED) {
    1000             mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0xff;
    1001             continue;
    1002         }
    1003 
    1004         if (pmSubtractionThreaded()) {
    1005             psThreadJob *job = psThreadJobAlloc("PSMODULES_SUBTRACTION_ORDER");
    1006             psArrayAdd(job->args, 1, ratios);
    1007             psArrayAdd(job->args, 1, mask);
    1008             psArrayAdd(job->args, 1, stamps);
    1009             psArrayAdd(job->args, 1, models);
    1010             psArrayAdd(job->args, 1, modelSums);
    1011             PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
    1012             PS_ARRAY_ADD_SCALAR(job->args, bg1, PS_TYPE_F32);
    1013             PS_ARRAY_ADD_SCALAR(job->args, bg2, PS_TYPE_F32);
    1014             if (!psThreadJobAddPending(job)) {
    1015                 return false;
    1016             }
    1017         } else {
    1018             if (!pmSubtractionOrderStamp(ratios, mask, stamps, models, modelSums, i, bg1, bg2)) {
    1019                 psError(psErrorCodeLast(), false, "Unable to measure PSF width for stamp %d", i);
    1020                 psFree(models);
    1021                 psFree(modelSums);
    1022                 psFree(ratios);
    1023                 psFree(mask);
    1024                 return false;
    1025             }
    1026         }
     1041        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     1042        if (stamp->status != PM_SUBTRACTION_STAMP_CALCULATE && stamp->status != PM_SUBTRACTION_STAMP_USED) {
     1043            mask->data.PS_TYPE_VECTOR_MASK_DATA[i] = 0xff;
     1044            continue;
     1045        }
     1046
     1047        if (pmSubtractionThreaded()) {
     1048            psThreadJob *job = psThreadJobAlloc("PSMODULES_SUBTRACTION_ORDER");
     1049            psArrayAdd(job->args, 1, ratios);
     1050            psArrayAdd(job->args, 1, mask);
     1051            psArrayAdd(job->args, 1, stamps);
     1052            psArrayAdd(job->args, 1, models);
     1053            psArrayAdd(job->args, 1, modelSums);
     1054            PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
     1055            PS_ARRAY_ADD_SCALAR(job->args, bg1, PS_TYPE_F32);
     1056            PS_ARRAY_ADD_SCALAR(job->args, bg2, PS_TYPE_F32);
     1057            if (!psThreadJobAddPending(job)) {
     1058                return false;
     1059            }
     1060        } else {
     1061            if (!pmSubtractionOrderStamp(ratios, mask, stamps, models, modelSums, i, bg1, bg2)) {
     1062                psError(psErrorCodeLast(), false, "Unable to measure PSF width for stamp %d", i);
     1063                psFree(models);
     1064                psFree(modelSums);
     1065                psFree(ratios);
     1066                psFree(mask);
     1067                return false;
     1068            }
     1069        }
    10271070    }
    10281071
    10291072    if (!psThreadPoolWait(true)) {
    1030         psError(psErrorCodeLast(), false, "Error waiting for threads.");
    1031         psFree(models);
    1032         psFree(modelSums);
    1033         psFree(ratios);
    1034         psFree(mask);
    1035             return false;
     1073        psError(psErrorCodeLast(), false, "Error waiting for threads.");
     1074        psFree(models);
     1075        psFree(modelSums);
     1076        psFree(ratios);
     1077        psFree(mask);
     1078        return false;
    10361079    }
    10371080
     
    10411084    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
    10421085    if (!psVectorStats(stats, ratios, NULL, mask, 0xff)) {
    1043         psError(psErrorCodeLast(), false, "Unable to calculate statistics for moments ratio.");
    1044         psFree(mask);
    1045         psFree(ratios);
    1046         psFree(stats);
    1047         return PM_SUBTRACTION_MODE_ERR;
     1086        psError(psErrorCodeLast(), false, "Unable to calculate statistics for moments ratio.");
     1087        psFree(mask);
     1088        psFree(ratios);
     1089        psFree(stats);
     1090        return PM_SUBTRACTION_MODE_ERR;
    10481091    }
    10491092    psFree(ratios);
     
    10521095    // XXX raise an error here or not?
    10531096    if (isnan(stats->robustMedian)) {
    1054         psFree(stats);
    1055         return PM_SUBTRACTION_MODE_ERR;
     1097        psFree(stats);
     1098        return PM_SUBTRACTION_MODE_ERR;
    10561099    }
    10571100
     
    10661109// Test a subtraction mode by performing a single iteration
    10671110static bool subtractionModeTest(pmSubtractionStampList *stamps, // Stamps to use to find best mode
    1068                                 pmSubtractionKernels *kernels, // Kernel description
    1069                                 const char *description, // Description for trace
    1070                                 psImage *subMask,  // Subtraction mask
    1071                                 float rej               // Rejection threshold
    1072                                 )
     1111                                pmSubtractionKernels *kernels, // Kernel description
     1112                                const char *description, // Description for trace
     1113                                psImage *subMask,  // Subtraction mask
     1114                                float rej               // Rejection threshold
     1115    )
    10731116{
    10741117    assert(stamps);
    10751118    assert(kernels);
    10761119
     1120    psAbort("this function is not working");
     1121# if (0)
     1122    psTrace("psModules.imcombine", 3, "Convolving stamps as needed...\n");
     1123    if (!pmSubtractionConvolveStamps(stamps, kernels)) {
     1124        psError(psErrorCodeLast(), false, "Unable to convolve stamps.");
     1125        return false;
     1126    }
     1127
    10771128    psTrace("psModules.imcombine", 3, "Calculating %s normalization equation...\n", description);
    1078     if (!pmSubtractionCalculateEquation(stamps, kernels, SUBMODE)) {
    1079         psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    1080         return false;
     1129    if (!pmSubtractionCalculateEquation(stamps, kernels)) {
     1130        psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1131        return false;
    10811132    }
    10821133
    10831134    psTrace("psModules.imcombine", 3, "Solving %s normalization equation...\n", description);
    1084     if (!pmSubtractionSolveEquation(kernels, stamps, SUBMODE)) {
    1085         psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    1086         return false;
     1135    if (!pmSubtractionSolveEquation(kernels, stamps)) {
     1136        psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1137        return false;
    10871138    }
    10881139
     
    10901141    psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    10911142    if (!deviations) {
    1092         psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
    1093         return false;
    1094     }
    1095 
     1143        psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
     1144        return false;
     1145    }
     1146
     1147    // XXX this needs to be made consistent with the modified 'reject stamps' function
    10961148    psTrace("psModules.imcombine", 3, "Rejecting %s stamps...\n", description);
    10971149    long numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, rej);
    10981150    if (numRejected < 0) {
    1099         psError(psErrorCodeLast(), false, "Unable to reject stamps.");
    1100         psFree(deviations);
    1101         return false;
     1151        psError(psErrorCodeLast(), false, "Unable to reject stamps.");
     1152        psFree(deviations);
     1153        return false;
    11021154    }
    11031155    psFree(deviations);
    11041156
    11051157    if (numRejected > 0) {
    1106         // Allow re-fit with reduced stamps set
    1107         psTrace("psModules.imcombine", 3, "Calculating %s normalization equation...\n", description);
    1108         if (!pmSubtractionCalculateEquation(stamps, kernels, PM_SUBTRACTION_EQUATION_ALL)) {
    1109             psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    1110             return false;
    1111         }
    1112 
    1113         psTrace("psModules.imcombine", 3, "Resolving %s equation...\n", description);
    1114         if (!pmSubtractionSolveEquation(kernels, stamps, PM_SUBTRACTION_EQUATION_ALL)) {
    1115             psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
    1116             return false;
    1117         }
    1118         psTrace("psModules.imcombine", 3, "Recalculate %s deviations...\n", description);
    1119 
    1120         psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
    1121         if (!deviations) {
    1122             psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
    1123             return false;
    1124         }
    1125         psTrace("psModules.imcombine", 3, "Measuring %s quality...\n", description);
    1126         long numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, NAN);
    1127         if (numRejected < 0) {
    1128             psError(psErrorCodeLast(), false, "Unable to reject stamps.");
    1129             psFree(deviations);
    1130             return false;
    1131         }
    1132         psFree(deviations);
    1133     }
    1134 
     1158        // Allow re-fit with reduced stamps set
     1159        psTrace("psModules.imcombine", 3, "Calculating %s normalization equation...\n", description);
     1160        if (!pmSubtractionCalculateEquation(stamps, kernels)) {
     1161            psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1162            return false;
     1163        }
     1164
     1165        psTrace("psModules.imcombine", 3, "Resolving %s equation...\n", description);
     1166        if (!pmSubtractionSolveEquation(kernels, stamps)) {
     1167            psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
     1168            return false;
     1169        }
     1170        psTrace("psModules.imcombine", 3, "Recalculate %s deviations...\n", description);
     1171
     1172        psVector *deviations = pmSubtractionCalculateDeviations(stamps, kernels); // Stamp deviations
     1173        if (!deviations) {
     1174            psError(psErrorCodeLast(), false, "Unable to calculate deviations.");
     1175            return false;
     1176        }
     1177        psTrace("psModules.imcombine", 3, "Measuring %s quality...\n", description);
     1178        long numRejected = pmSubtractionRejectStamps(kernels, stamps, deviations, subMask, NAN);
     1179        if (numRejected < 0) {
     1180            psError(psErrorCodeLast(), false, "Unable to reject stamps.");
     1181            psFree(deviations);
     1182            return false;
     1183        }
     1184        psFree(deviations);
     1185    }
     1186# endif
    11351187    return true;
    11361188}
     
    11381190
    11391191pmSubtractionMode pmSubtractionBestMode(pmSubtractionStampList **stamps, pmSubtractionKernels **kernels,
    1140                                         const psImage *subMask, float rej)
     1192                                        const psImage *subMask, float rej)
    11411193{
    11421194    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(*stamps, PM_SUBTRACTION_MODE_ERR);
     
    11501202
    11511203    if (!subtractionModeTest(stamps1, kernels1, "convolve 1", subMask1, rej)) {
    1152         psError(psErrorCodeLast(), false, "Unable to test subtraction with convolution of image 1");
    1153         psFree(stamps1);
    1154         psFree(kernels1);
    1155         psFree(subMask1);
    1156         return PM_SUBTRACTION_MODE_ERR;
     1204        psError(psErrorCodeLast(), false, "Unable to test subtraction with convolution of image 1");
     1205        psFree(stamps1);
     1206        psFree(kernels1);
     1207        psFree(subMask1);
     1208        return PM_SUBTRACTION_MODE_ERR;
    11571209    }
    11581210    psFree(subMask1);
     
    11651217
    11661218    if (!subtractionModeTest(stamps2, kernels2, "convolve 2", subMask2, rej)) {
    1167         psError(psErrorCodeLast(), false, "Unable to test subtraction with convolution of image 2");
    1168         psFree(stamps2);
    1169         psFree(kernels2);
    1170         psFree(subMask2);
    1171         psFree(stamps1);
    1172         psFree(kernels1);
    1173         return PM_SUBTRACTION_MODE_ERR;
     1219        psError(psErrorCodeLast(), false, "Unable to test subtraction with convolution of image 2");
     1220        psFree(stamps2);
     1221        psFree(kernels2);
     1222        psFree(subMask2);
     1223        psFree(stamps1);
     1224        psFree(kernels1);
     1225        return PM_SUBTRACTION_MODE_ERR;
    11741226    }
    11751227    psFree(subMask2);
     
    11791231    pmSubtractionKernels *bestKernels = NULL; // Best choice for kernels
    11801232    psLogMsg("psModules.imcombine", PS_LOG_INFO,
    1181              "Image 1: %f +/- %f from %d stamps\nImage 2: %f +/- %f from %d stamps\n",
    1182              kernels1->mean, kernels1->rms, kernels1->numStamps,
    1183              kernels2->mean, kernels2->rms, kernels2->numStamps);
     1233             "Image 1: %f +/- %f from %d stamps\nImage 2: %f +/- %f from %d stamps\n",
     1234             kernels1->mean, kernels1->rms, kernels1->numStamps,
     1235             kernels2->mean, kernels2->rms, kernels2->numStamps);
    11841236
    11851237    if (kernels1->mean < kernels2->mean) {
    1186         bestStamps = stamps1;
    1187         bestKernels = kernels1;
     1238        bestStamps = stamps1;
     1239        bestKernels = kernels1;
    11881240    } else {
    1189         bestStamps = stamps2;
    1190         bestKernels = kernels2;
     1241        bestStamps = stamps2;
     1242        bestKernels = kernels2;
    11911243    }
    11921244
     
    12041256}
    12051257
    1206 
    1207 bool pmSubtractionParamsScale(int *kernelSize, int *stampSize, psVector *widths,
    1208                               float scaleRef, float scaleMin, float scaleMax)
     1258static float scaleRefOption = NAN;
     1259static float scaleMinOption = NAN;
     1260static float scaleMaxOption = NAN;
     1261static bool  scaleOption = false;
     1262
     1263bool pmSubtractionParamScaleOptions(bool scale, float scaleRef, float scaleMin, float scaleMax) {
     1264
     1265    if (scale) {
     1266        PS_ASSERT_FLOAT_LARGER_THAN(scaleRef, 0.0, false);
     1267        PS_ASSERT_FLOAT_LARGER_THAN(scaleMin, 0.0, false);
     1268        PS_ASSERT_FLOAT_LARGER_THAN(scaleMax, 0.0, false);
     1269        PS_ASSERT_FLOAT_LARGER_THAN(scaleMax, scaleMin, false);
     1270    }
     1271
     1272    scaleRefOption = scaleRef;
     1273    scaleMinOption = scaleMin;
     1274    scaleMaxOption = scaleMax;
     1275    scaleOption = scale;
     1276   
     1277    return true;
     1278}
     1279
     1280bool pmSubtractionParamsScale(int *kernelSize, int *stampSize, psVector *widths, float fwhm1, float fwhm2)
    12091281{
    1210     PS_ASSERT_PTR_NON_NULL(kernelSize, false);
    1211     PS_ASSERT_PTR_NON_NULL(stampSize, false);
     1282    // PS_ASSERT_PTR_NON_NULL(kernelSize, false);
     1283    // PS_ASSERT_PTR_NON_NULL(stampSize, false);
    12121284    PS_ASSERT_VECTOR_NON_NULL(widths, false);
    12131285    PS_ASSERT_VECTOR_TYPE(widths, PS_TYPE_F32, false);
    1214     PS_ASSERT_FLOAT_LARGER_THAN(scaleRef, 0.0, false);
    1215     PS_ASSERT_FLOAT_LARGER_THAN(scaleMin, 0.0, false);
    1216     PS_ASSERT_FLOAT_LARGER_THAN(scaleMax, 0.0, false);
    1217     PS_ASSERT_FLOAT_LARGER_THAN(scaleMax, scaleMin, false);
    1218 
    1219     float fwhm1;
    1220     float fwhm2;
    1221 
    1222     pmSubtractionGetFWHMs(&fwhm1, &fwhm2);
    1223     psAssert(isfinite(fwhm1), "fwhm 1 not set");
    1224     psAssert(isfinite(fwhm2), "fwhm 2 not set");
     1286
     1287    if (!scaleOption) return true;
     1288
     1289    // pmSubtractionGetFWHMs(&fwhm1, &fwhm2);
     1290    // psAssert(isfinite(fwhm1), "fwhm 1 not set");
     1291    // psAssert(isfinite(fwhm2), "fwhm 2 not set");
    12251292   
    12261293    // float diff = sqrtf(PS_SQR(PS_MAX(fwhm1, fwhm2)) - PS_SQR(PS_MIN(fwhm1, fwhm2))); // Difference
    1227     float scale = PS_MAX(fwhm1, fwhm2) / scaleRef;      // Scaling factor
    1228 
    1229     if (isfinite(scaleMin) && scale < scaleMin) {
    1230         scale = scaleMin;
    1231     }
    1232     if (isfinite(scaleMax) && scale > scaleMax) {
    1233         scale = scaleMax;
     1294    float scale = PS_MAX(fwhm1, fwhm2) / scaleRefOption;      // Scaling factor
     1295
     1296    if (isfinite(scaleMinOption) && scale < scaleMinOption) {
     1297        scale = scaleMinOption;
     1298    }
     1299    if (isfinite(scaleMaxOption) && scale > scaleMaxOption) {
     1300        scale = scaleMaxOption;
    12341301    }
    12351302
    12361303    for (int i = 0; i < widths->n; i++) {
    1237         widths->data.F32[i] *= scale;
    1238     }
    1239     *kernelSize = *kernelSize * scale + 0.5;
    1240     *stampSize = *stampSize * scale + 0.5;
    1241 
    1242     psLogMsg("psModules.imcombine", PS_LOG_INFO,
    1243              "Scaling kernel parameters by %f: %d %d", scale, *kernelSize, *stampSize);
     1304        widths->data.F32[i] *= scale;
     1305    }
     1306    if (kernelSize) {
     1307        *kernelSize = *kernelSize * scale + 0.5;
     1308    }
     1309    if (stampSize) {
     1310        *stampSize = *stampSize * scale + 0.5;
     1311    }
     1312
     1313    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Scaling kernel parameters by %f", scale);
     1314    if (kernelSize) psLogMsg("psModules.imcombine", PS_LOG_INFO, " modified kernel size %d", *kernelSize);
     1315    if (stampSize) psLogMsg("psModules.imcombine", PS_LOG_INFO, " modified stamp size %d", *stampSize);
    12441316
    12451317    return true;
  • trunk/psModules/src/imcombine/pmSubtractionMatch.h

    r29543 r30622  
    88#include <pmSubtractionKernels.h>
    99#include <pmSubtractionStamps.h>
     10#include <pmSubtraction.h>
    1011
    1112/// Match two images
     
    2627                        int size,       ///< Kernel half-size
    2728                        int order,      ///< Spatial polynomial order
    28                         const psVector *widths, ///< ISIS Gaussian widths
     29                        psVector *widths, ///< ISIS Gaussian widths
    2930                        const psVector *orders, ///< ISIS Polynomial orders
    3031                        int inner,      ///< Inner radius for various kernel types
     
    100101
    101102/// Scale subtraction parameters according to the FWHMs of the inputs
    102 bool 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 scaleRef,                     ///< Reference width for scaling
    107     float scaleMin,                     ///< Minimum scaling ratio, or NAN
    108     float scaleMax                      ///< Maximum scaling ratio, or NAN
     103// bool pmSubtractionParamsScale(
     104//     int *kernelSize,                    ///< Half-size of the kernel
     105//     int *stampSize,                     ///< Half-size of the stamp (footprint)
     106//     psVector *widths,                   ///< ISIS widths
     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
     112bool pmSubtractionParamsScale(int *kernelSize, int *stampSize, psVector *widths, float fwhm1, float fwhm2);
     113
     114bool pmSubtractionParamScaleOptions(bool scale, float scaleRef, float scaleMin, float scaleMax);
     115
     116bool pmSubtractionMatchAttempt(
     117    pmSubtractionQuality **bestMatch,
     118    pmSubtractionKernels *kernels,
     119    pmSubtractionStampList *stamps,
     120    pmSubtractionMode mode,
     121    int spatialOrder,
     122    bool final
    109123    );
    110124
  • trunk/psModules/src/imcombine/pmSubtractionParams.c

    r27086 r30622  
    99
    1010#include "pmErrorCodes.h"
     11#include "pmFPA.h"
     12#include "pmSubtractionTypes.h"
     13#include "pmSubtraction.h"
    1114#include "pmSubtractionStamps.h"
    12 #include "pmSubtraction.h"
    1315#include "pmSubtractionParams.h"
    1416
     
    1618
    1719#if 0
     20// XXX this was moved to pmSubtraction.c in r15443 -- delete
    1821// Convolve the reference stamp by the kernel
    1922static psKernel *convolveStamp(const pmSubtractionStamp *stamp, // Stamp to be convolved
  • trunk/psModules/src/imcombine/pmSubtractionStamps.c

    r29543 r30622  
    2727#include "pmSource.h"
    2828
     29#include "pmSubtractionTypes.h"
    2930#include "pmSubtraction.h"
    3031#include "pmSubtractionStamps.h"
     32#include "pmSubtractionVisual.h"
    3133
    3234#define STAMP_LIST_BUFFER 20            // Number of stamps to add to list at a time
     
    122124            if ((image1 && image1->data.F32[y][x] < thresh1) ||
    123125                (image2 && image2->data.F32[y][x] < thresh2)) {
     126                // fprintf (stderr, "%f,%f : thresh\n", xRaw, yRaw);
    124127                continue;
    125128            }
     
    366369}
    367370
    368 
    369 pmSubtractionStampList *pmSubtractionStampsFind(pmSubtractionStampList *stamps, const psImage *image1,
    370                                                 const psImage *image2, const psImage *subMask,
    371                                                 const psRegion *region, float thresh1, float thresh2,
    372                                                 int size, int footprint, float spacing, float normFrac,
    373                                                 float sysErr, float skyErr, pmSubtractionMode mode)
     371bool pmSubtractionStampsSelect(pmSubtractionStampList **stamps, // Stamps to read
     372                               const pmReadout *ro1, // Readout 1
     373                               const pmReadout *ro2, // Readout 2
     374                               const psImage *subMask, // Mask for subtraction, or NULL
     375                               psImage *variance,  // Variance map
     376                               const psRegion *region, // Region of interest
     377                               float thresh1,  // Threshold for stamp finding on readout 1
     378                               float thresh2,  // Threshold for stamp finding on readout 2
     379                               float stampSpacing, // Spacing between stamps
     380                               float normFrac,     // Fraction of flux in window for normalisation window
     381                               float sysError,     // Relative systematic error in images
     382                               float skyError,     // Relative systematic error in images
     383                               int size,         // Kernel half-size
     384                               int footprint,     // Convolution footprint for stamps
     385                               pmSubtractionMode mode // Mode for subtraction
     386    )
     387{
     388    PS_ASSERT_PTR_NON_NULL(stamps, false);
     389    PM_ASSERT_READOUT_NON_NULL(ro1, false);
     390    PM_ASSERT_READOUT_NON_NULL(ro2, false);
     391    PS_ASSERT_IMAGE_NON_NULL(subMask, false);
     392    PS_ASSERT_IMAGE_NON_NULL(variance, false);
     393    PS_ASSERT_PTR_NON_NULL(region, false);
     394
     395    psTrace("psModules.imcombine", 3, "Finding stamps...\n");
     396
     397    psImage *image1 = ro1 ? ro1->image : NULL, *image2 = ro2 ? ro2->image : NULL; // Images of interest
     398
     399    *stamps = pmSubtractionStampsFind(*stamps, image1, image2, subMask, region, thresh1, thresh2,
     400                                      size, footprint, stampSpacing, normFrac, sysError, skyError, mode);
     401    if (!*stamps) {
     402        psError(psErrorCodeLast(), false, "Unable to find stamps.");
     403        return false;
     404    }
     405
     406    psTrace("psModules.imcombine", 3, "Extracting stamps...\n");
     407    if (!pmSubtractionStampsExtract(*stamps, ro1->image, ro2->image, variance, size, *region)) {
     408        psError(psErrorCodeLast(), false, "Unable to extract stamps.");
     409        return false;
     410    }
     411
     412    pmSubtractionVisualPlotStamps(*stamps, (pmReadout *) ro1);
     413    return true;
     414}
     415
     416pmSubtractionStampList *pmSubtractionStampsFind(pmSubtractionStampList *stamps,
     417                                                const psImage *image1,
     418                                                const psImage *image2,
     419                                                const psImage *subMask,
     420                                                const psRegion *region,
     421                                                float thresh1,
     422                                                float thresh2,
     423                                                int size,
     424                                                int footprint,
     425                                                float spacing,
     426                                                float normFrac,
     427                                                float sysErr,
     428                                                float skyErr,
     429                                                pmSubtractionMode mode)
    374430{
    375431    if (!image1 && !image2) {
     
    429485        stamps = pmSubtractionStampListAlloc(numCols, numRows, region, footprint, spacing,
    430486                                             normFrac, sysErr, skyErr);
     487    }
     488
     489    // XXX TEST : dump all stars in the stamps here
     490    if (0) {
     491        FILE *f = fopen ("stamp.dat", "w");
     492        for (int i = 0; i < stamps->num; i++) {
     493            psVector *xList = stamps->x->data[i];
     494            psVector *yList = stamps->y->data[i]; // Coordinate lists
     495            psVector *fluxList = stamps->flux->data[i]; // List of stamp fluxes
     496
     497            for (int j = 0; j < xList->n; j++) {
     498                fprintf (f, "%d %d  %f %f  %f\n", i, j, xList->data.F32[j], yList->data.F32[j], fluxList->data.F32[j]);
     499            }
     500        }
     501        fclose (f);
    431502    }
    432503
     
    636707    }
    637708
     709    int nTotal = 0;
     710
    638711    // Sort the list by flux, with the brightest last
    639712    for (int i = 0; i < numStamps; i++) {
     
    662735        stamps->y->data[i] = ySorted;
    663736        stamps->flux->data[i] = fluxSorted;
    664     }
    665 
     737        nTotal += num;
     738    }
     739    // fprintf (stderr, "nTotal %d\n", nTotal);
     740   
    666741    return stamps;
    667742}
    668743
    669 
    670 bool pmSubtractionStampsGetWindow(pmSubtractionStampList *stamps, int kernelSize)
     744// we are essentially using aperture photometry to determine the photometric match between the
     745// images.  we need to choose an appropriate-sized aperture for this analysis.  If it is too
     746// large, the measurement will be noisy (and possibly biased) due to the sky noise.  If it is
     747// too small, or inconsistent, the measurement will be biased.  We use Kron-mag like aperture
     748// scaled by the first radial moment.
     749bool pmSubtractionStampsGetWindow(bool *tryAgain, pmSubtractionStampList *stamps, int kernelSize)
    671750{
    672751    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
    673752    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
    674753
     754    // if we succeed, or fail with an unrecoverable error, do not try again
     755    if (tryAgain) {
     756        *tryAgain = false;
     757    }
     758
    675759    int size = stamps->footprint; // Size of postage stamps
    676760
     761    // window for moments calculations downstream
     762    psFree (stamps->window);
     763    stamps->window = psKernelAlloc(-size, size, -size, size);
     764    psImageInit(stamps->window->image, 0.0);
     765
     766    // window1 and window2 are mean stamp images used here to measure the
     767    // first radial moment, and thus the normalization window
    677768    psFree (stamps->window1);
    678769    stamps->window1 = psKernelAlloc(-size, size, -size, size);
     
    683774    psImageInit(stamps->window2->image, 0.0);
    684775
    685     // Generate a weighting window based on the fwhms (20% larger than the largest)
    686     {
    687         float fwhm1, fwhm2;
    688 
    689         // XXX this is annoyingly hack-ish
    690         pmSubtractionGetFWHMs(&fwhm1, &fwhm2);
    691 
    692         float sigma = 1.5 * PS_MAX(fwhm1, fwhm2) / 2.35;
    693 
    694         psFree (stamps->window);
    695         stamps->window = psKernelAlloc(-size, size, -size, size);
    696         psImageInit(stamps->window->image, 0.0);
    697 
    698         for (int y = -size; y <= size; y++) {
    699             for (int x = -size; x <= size; x++) {
    700                 stamps->window->kernel[y][x] = exp(-0.5*(x*x + y*y)/(sigma*sigma));
    701             }
    702         }
     776    // Generate an initial weighting window based on the fwhms (50% larger than the largest)
     777    float fwhm1, fwhm2;
     778
     779    // XXX this is annoyingly hack-ish
     780    pmSubtractionGetFWHMs(&fwhm1, &fwhm2);
     781   
     782    float sigma = 1.5 * PS_MAX(fwhm1, fwhm2) / 2.35;
     783   
     784    for (int y = -size; y <= size; y++) {
     785        for (int x = -size; x <= size; x++) {
     786            stamps->window->kernel[y][x] = exp(-0.5*(x*x + y*y)/(sigma*sigma));
     787        }
    703788    }
    704789
     
    790875    psTrace("psModules.imcombine", 3, "Window total (2): %f, threshold: %f\n", sum2, (1.0 - stamps->normFrac) * sum2);
    791876
    792 # if (1)
    793     // this block attempts to calculate the radius based on the first radial moment
     877    // attempt to calculate the normalization window based on the first radial moment
    794878    double Sr1 = 0.0;
    795879    double Sr2 = 0.0;
     
    809893    float R2 = Sr2 / Sf2;
    810894
    811     stamps->normWindow1 = 2.0*R1;
    812     stamps->normWindow2 = 2.0*R2;
    813     psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "Kron Radii: %f for 1, %f for 2\n", stamps->normWindow1, stamps->normWindow2);
    814 
    815 # else
    816     // XXX : this block attempts to calculate the radius by looking at the curve of growth (or something vaguely equivalent).
    817     // It did not do very well (though a true curve-of-growth analysis might be better...)
    818     bool done1 = false;
    819     bool done2 = false;
    820     double prior1 = 0.0;
    821     double prior2 = 0.0;
    822     double delta1o = 1.0;
    823     double delta2o = 1.0;
    824     for (int radius = 1; radius <= size && !(done1 && done2); radius++) {
    825         double within1 = 0.0;
    826         double within2 = 0.0;
    827         for (int y = -radius; y <= radius; y++) {
    828             for (int x = -radius; x <= radius; x++) {
    829                 if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(radius)) {
    830                     within1 += stamps->window1->kernel[y][x];
    831                 }
    832                 if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(radius)) {
    833                     within2 += stamps->window2->kernel[y][x];
    834                 }
    835             }
    836         }
    837         double delta1 = (within1 - prior1) / within1;
    838         if (!done1 && (fabs(delta1) < stamps->normFrac)) {
    839             // interpolate to the radius at which delta2 is normFrac:
    840             stamps->normWindow1 = radius - (stamps->normFrac - delta1) / (delta1o - delta1);
    841             psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "choosing %f (%d) for 1 (%f : %f)\n", stamps->normWindow1, radius, within1, delta1);
    842             done1 = true;
    843         }
    844         double delta2 = (within2 - prior2) / within2;
    845         if (!done2 && (fabs(delta2) < stamps->normFrac)) {
    846             // interpolate to the radius at which delta2 is normFrac:
    847             stamps->normWindow2 = radius - (stamps->normFrac - delta2) / (delta2o - delta2);
    848             psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "choosing %f (%d) for 2 (%f : %f)\n", stamps->normWindow2, radius, within2, delta2);
    849             done2 = true;
    850         }
    851         psTrace("psModules.imcombine", 5, "Radius %d: %f (%f) and %f (%f)\n", radius, within1, delta1, within2, delta2);
    852 
    853         prior1 = within1;
    854         prior2 = within2;
    855         delta1o = delta1;
    856         delta2o = delta2;
    857 
    858         // if (!done1 && (within1 > (1.0 - stamps->normFrac) * sum1)) {
    859         //     stamps->normWindow1 = radius;
    860         //     done1 = true;
    861         // }
    862         // if (!done2 && (within2 > (1.0 - stamps->normFrac) * sum2)) {
    863         //     stamps->normWindow2 = radius;
    864         //     done2 = true;
    865         // }
    866 
    867     }
    868 # endif
    869 
    870     psTrace("psModules.imcombine", 3, "Normalisation window radii set to %f and %f\n", stamps->normWindow1, stamps->normWindow2);
    871     if (stamps->normWindow1 == 0 || stamps->normWindow1 >= size) {
     895    // Compare the Kron Radii (R1 & R2) to above to the FWHMs : if they are too discrepant, we will need to rescale
     896    psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "Kron Radii vs FWHMs 1: fwhm: %f, kron %f\n", fwhm1, R1);
     897    psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "Kron Radii vs FWHMs 2: fwhm: %f, kron %f\n", fwhm2, R2);
     898
     899    // XXX CAREFUL : in pmSubtractionMatch.c:703, we rely on this factor of 2.75..
     900    stamps->normWindow1 = 2.75*R1;
     901    stamps->normWindow2 = 2.75*R2;
     902    psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "Windows from Kron Radii: %f for 1, %f for 2\n", stamps->normWindow1, stamps->normWindow2);
     903
     904    // if the calculated normWindows are too large, we will fall off the stamps.  In this case, we need to try again.
     905    if ((stamps->normWindow1 > size) || (stamps->normWindow2 > size)) {
     906        if (tryAgain) {
     907            *tryAgain = true;
     908        }
     909        psFree (stats);
     910        psFree (flux1);
     911        psFree (flux2);
     912        psFree (norm1);
     913        psFree (norm2);
     914        return false;
     915    }
     916
     917    // this is an unrecoverable error : something really bogus in the data
     918    if (stamps->normWindow1 == 0) {
    872919        psError(PM_ERR_STAMPS, true, "Unable to determine normalisation window size (1).");
     920        psFree (stats);
     921        psFree (flux1);
     922        psFree (flux2);
     923        psFree (norm1);
     924        psFree (norm2);
    873925        return false;
    874926    }
    875     if (stamps->normWindow2 == 0 || stamps->normWindow2 >= size) {
     927    if (stamps->normWindow2 == 0) {
    876928        psError(PM_ERR_STAMPS, true, "Unable to determine normalisation window size (2).");
     929        psFree (stats);
     930        psFree (flux1);
     931        psFree (flux2);
     932        psFree (norm1);
     933        psFree (norm2);
    877934        return false;
     935    }
     936
     937    // Generate a weighting window based on the kron radii
     938    float radius = 2.0 * PS_MAX(R1, R2);
     939    psImageInit(stamps->window->image, 0.0);
     940
     941    // we use a top-hat window for the moments analysis
     942    for (int y = -size; y <= size; y++) {
     943        for (int x = -size; x <= size; x++) {
     944            if (hypot(x,y) > radius) continue;
     945            stamps->window->kernel[y][x] = 1.0;
     946        }
    878947    }
    879948
     
    890959        }
    891960    }
    892 
    893 #if 0
    894     {
    895         psFits *fits = NULL;
    896         fits = psFitsOpen ("window1.norm.fits", "w");
    897         psFitsWriteImage (fits, NULL, stamps->window1->image, 0, NULL);
    898         psFitsClose (fits);
    899         fits = psFitsOpen ("window2.norm.fits", "w");
    900         psFitsWriteImage (fits, NULL, stamps->window2->image, 0, NULL);
    901         psFitsClose (fits);
    902     }
    903 #endif
    904961
    905962    psFree (stats);
     
    11281185            continue;
    11291186        }
     1187       
     1188        // XXX this is somewhat arbitrary...
     1189        if (source->errMag > 0.05) continue;
     1190        if (fabs(source->psfMag - source->apMag) > 0.5) continue;
     1191
    11301192        if (source->modelPSF) {
    11311193            x->data.F32[numOut] = source->modelPSF->params->data.F32[PM_PAR_XPOS];
     
    11351197            y->data.F32[numOut] = source->peak->yf;
    11361198        }
    1137         // fprintf (stderr, "stamp: %5.1f %5.1f\n", x->data.F32[numOut], y->data.F32[numOut]);
    11381199        numOut++;
    11391200    }
  • trunk/psModules/src/imcombine/pmSubtractionStamps.h

    r29543 r30622  
    11#ifndef PM_SUBTRACTION_STAMPS_H
    22#define PM_SUBTRACTION_STAMPS_H
    3 
    4 #include <pslib.h>
    5 
    6 #include "pmSubtractionKernels.h"
    7 
    8 /// Status of stamp
    9 typedef enum {
    10     PM_SUBTRACTION_STAMP_INIT,          ///< Initial state
    11     PM_SUBTRACTION_STAMP_FOUND,         ///< Found a suitable source for this stamp
    12     PM_SUBTRACTION_STAMP_CALCULATE,     ///< Calculate matrix and vector values for this stamp
    13     PM_SUBTRACTION_STAMP_USED,          ///< Use this stamp
    14     PM_SUBTRACTION_STAMP_REJECTED,      ///< This stamp has been rejected
    15     PM_SUBTRACTION_STAMP_NONE           ///< No stamp in this region
    16 } pmSubtractionStampStatus;
    17 
    18 /// A list of stamps
    19 typedef struct {
    20     long num;                           ///< Number of stamps
    21     psArray *stamps;                    ///< The stamps
    22     psArray *regions;                   ///< Regions for each stamp
    23     psArray *x, *y;                     ///< Coordinates for possible stamps (or NULL)
    24     psArray *flux;                      ///< Fluxes for possible stamps (or NULL)
    25     int footprint;                      ///< Half-size of stamps
    26     float normFrac;                     ///< Fraction of flux in window for normalisation window
    27     float normValue;                    ///< calculated normalization
    28     float normValue2;                   ///< calculated normalization
    29     psKernel *window1;                  ///< window function generated from ensemble of stamps (input 1)
    30     psKernel *window2;                  ///< window function generated from ensemble of stamps (input 2)
    31     psKernel *window;                   ///< weighting window function (sigma = 1.1 * MAX(fwhm))
    32     float normWindow1;                  ///< Size of window for measuring normalisation
    33     float normWindow2;                  ///< Size of window for measuring normalisation
    34     float sysErr;                       ///< Systematic error
    35     float skyErr;                       ///< increase effective readnoise
    36 } pmSubtractionStampList;
    373
    384/// Allocate a list of stamps
     
    7440    );
    7541
    76 
    77 /// A stamp for image subtraction
    78 typedef struct {
    79     float x, y;                         ///< Position
    80     float flux;                         ///< Flux
    81     float xNorm, yNorm;                 ///< Normalised position
    82     psKernel *image1;                   ///< Reference image postage stamp
    83     psKernel *image2;                   ///< Input image postage stamp
    84     psKernel *weight;                   ///< Weight image (1/variance) postage stamp, or NULL
    85     psArray *convolutions1;             ///< Convolutions of image 1 for each kernel component, or NULL
    86     psArray *convolutions2;             ///< Convolutions of image 2 for each kernel component, or NULL
    87     psImage *matrix;                    ///< Least-squares matrix, or NULL
    88     psVector *vector;                   ///< Least-squares vector, or NULL
    89     double norm;                        ///< Normalisation difference
    90     double normI1;                       ///< Sum(flux) for image 1
    91     double normI2;                       ///< Sum(flux) for image 2
    92     double normSquare1;                 ///< Sum(flux^2) for image 1 (used for penalty)
    93     double normSquare2;                 ///< Sum(flux^2) for image 2 (used for penalty)
    94     pmSubtractionStampStatus status;    ///< Status of stamp
    95     psVector *MxxI1;                    ///< second moments of convolution images
    96     psVector *MyyI1;                    ///< second moments of convolution images
    97     psVector *MxxI2;                    ///< second moments of convolution images
    98     psVector *MyyI2;                    ///< second moments of convolution images
    99     double MxxI1raw;
    100     double MyyI1raw;
    101     double MxxI2raw;
    102     double MyyI2raw;
    103 } pmSubtractionStamp;
    104 
    10542/// Allocate a stamp
    10643pmSubtractionStamp *pmSubtractionStampAlloc(void);
     44
     45// find and extract the stamps
     46bool pmSubtractionStampsSelect(pmSubtractionStampList **stamps, // Stamps to read
     47                               const pmReadout *ro1, // Readout 1
     48                               const pmReadout *ro2, // Readout 2
     49                               const psImage *subMask, // Mask for subtraction, or NULL
     50                               psImage *variance,  // Variance map
     51                               const psRegion *region, // Region of interest
     52                               float thresh1,  // Threshold for stamp finding on readout 1
     53                               float thresh2,  // Threshold for stamp finding on readout 2
     54                               float stampSpacing, // Spacing between stamps
     55                               float normFrac,     // Fraction of flux in window for normalisation window
     56                               float sysError,     // Relative systematic error in images
     57                               float skyError,     // Relative systematic error in images
     58                               int size,         // Kernel half-size
     59                               int footprint,     // Convolution footprint for stamps
     60                               pmSubtractionMode mode // Mode for subtraction
     61    );
     62
    10763
    10864/// Find stamps on an image
     
    172128/// Calculate the window and normalisation window from the stamps
    173129bool pmSubtractionStampsGetWindow(
     130    bool *tryAgain,                     ///< re-try with new stamp size?
    174131    pmSubtractionStampList *stamps,     ///< List of stamps
    175132    int kernelSize                      ///< Half-size of kernel
  • trunk/psModules/src/imcombine/pmSubtractionThreads.c

    r27365 r30622  
    66#include <pslib.h>
    77
     8#include "pmSubtractionTypes.h"
    89#include "pmSubtractionMatch.h"
    910#include "pmSubtractionEquation.h"
     
    3334
    3435    {
    35         psThreadTask *task = psThreadTaskAlloc("PSMODULES_SUBTRACTION_CALCULATE_EQUATION", 4);
     36        psThreadTask *task = psThreadTaskAlloc("PSMODULES_SUBTRACTION_CALCULATE_EQUATION", 3);
    3637        task->function = &pmSubtractionCalculateEquationThread;
     38        psThreadTaskAdd(task);
     39        psFree(task);
     40    }
     41
     42    {
     43        psThreadTask *task = psThreadTaskAlloc("PSMODULES_SUBTRACTION_CONVOLVE_STAMP", 3);
     44        task->function = &pmSubtractionConvolveStampThread;
    3745        psThreadTaskAdd(task);
    3846        psFree(task);
  • trunk/psModules/src/imcombine/pmSubtractionTypes.h

    r30585 r30622  
    8080    int xMin, xMax, yMin, yMax;         ///< Bounds of image (for normalisation)
    8181    long num;                           ///< Number of kernel components (not including the spatial ones)
     82    psVector *fwhms;                    ///< requested fwhms of the kernel Gaussians (ISIS, HERM or DECONV_HERM)
     83    psVector *orders;                   ///< polynomial orders for each Gaussian (ISIS, HERM or DECONV_HERM)
    8284    psVector *u, *v;                    ///< Offset (for POIS) or polynomial order (for ISIS, HERM or DECONV_HERM)
    83     psVector *widths;                   ///< Gaussian FWHMs (ISIS, HERM or DECONV_HERM)
     85    psVector *widths;                   ///< measured Gaussian FWHMs of Gauss*poly (ISIS, HERM or DECONV_HERM)
    8486    psVector *uStop, *vStop;            ///< Width of kernel element (SPAM,FRIES only)
    8587    psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (for ISIS, HERM or DECONV_HERM)
     
    9092    int size;                           ///< The half-size of the kernel
    9193    int inner;                          ///< The size of an inner region
     94    int binning;                        ///< Binning used for the SPAM kernels
     95    int ringsOrder;                     ///<
    9296    int spatialOrder;                   ///< The spatial order of the kernels
    9397    int bgOrder;                        ///< The order for the background fitting
  • trunk/psModules/src/imcombine/pmSubtractionVisual.c

    r29600 r30622  
    1616
    1717#include "pmKapaPlots.h"
     18#include "pmFPA.h"
     19#include "pmSubtractionTypes.h"
    1820#include "pmSubtraction.h"
    1921#include "pmSubtractionStamps.h"
    2022#include "pmSubtractionEquation.h"
    2123#include "pmSubtractionKernels.h"
     24#include "pmSubtractionVisual.h"
    2225
    2326#include "pmVisual.h"
     
    210213        psImageOverlaySection(canvas, im, x0, y0, "=");
    211214        stampNum++;
     215
     216        // renormalize the section
     217        float maxValue = 0;
     218        for (int iy = 0; iy < im->numRows; iy++) {
     219            for (int ix = 0; ix < im->numCols; ix++) {
     220                maxValue = PS_MAX(maxValue, im->data.F64[iy][ix]);
     221            }
     222        }
     223        if (maxValue == 0.0) continue;
     224        for (int iy = 0; iy < im->numRows; iy++) {
     225            for (int ix = 0; ix < im->numCols; ix++) {
     226                canvas->data.F64[y0 + iy][x0 + ix] /= maxValue;
     227            }
     228        }
    212229    }
    213230
     
    231248}
    232249
     250/** Plot the least-squares matrix of each stamp */
     251bool pmSubtractionVisualPlotLeastSquaresResid (const pmSubtractionStampList *stamps, psImage *matrixIn, int nUsed) {
     252
     253    if (!pmVisualTestLevel("ppsub.chisq", 1)) return true;
     254
     255    if (!plotLeastSquares) return true;
     256
     257    if (!pmVisualInitWindow (&kapa1, "PPSub:Images")) {
     258        return false;
     259    }
     260
     261    psImage *matrixNorm = psImageCopy(NULL, matrixIn, PS_TYPE_F64);
     262    {
     263        // renormalize the matrix
     264        float maxValue = 0;
     265        for (int iy = 0; iy < matrixNorm->numRows; iy++) {
     266            for (int ix = 0; ix < matrixNorm->numCols; ix++) {
     267                maxValue = PS_MAX(maxValue, matrixNorm->data.F64[iy][ix]);
     268            }
     269        }
     270        for (int iy = 0; iy < matrixNorm->numRows; iy++) {
     271            for (int ix = 0; ix < matrixNorm->numCols; ix++) {
     272                matrixNorm->data.F64[iy][ix] /= maxValue;
     273            }
     274        }
     275    }
     276
     277    // Find the stamp size
     278    int imageMax = -1;
     279    int numStamps = 0;
     280    psElemType type = PS_TYPE_F64;
     281
     282    for(int i = 0; i < stamps->num; i++) {
     283        pmSubtractionStamp *stamp = stamps->stamps->data[i];
     284        if (stamp == NULL) continue;
     285
     286        psImage *im = stamp->matrix;
     287        if (im == NULL) continue;
     288
     289        imageMax = PS_MAX(imageMax, im->numCols);
     290        imageMax = PS_MAX(imageMax, im->numRows);
     291        numStamps++;
     292        type = im->type.type;
     293    }
     294    if (imageMax == -1) return false;
     295
     296    int border = 15;
     297    imageMax += border;
     298    int tileRowCount = (int) ceil(sqrt(numStamps));
     299    int canvasX = tileRowCount * (imageMax);
     300    int canvasY = tileRowCount * (imageMax);
     301    psImage *canvas = psImageAlloc (canvasX, canvasY, type);
     302    psImageInit (canvas, NAN);
     303
     304    // overlay the images
     305    int stampNum = 0;
     306    int stampListNum = 0;
     307    while (stampNum < numStamps) {
     308        int x0 = (imageMax) * (stampNum % tileRowCount);
     309        int y0 = (imageMax) * (stampNum / tileRowCount);
     310
     311        pmSubtractionStamp *stamp = stamps->stamps->data[stampListNum++];
     312        if (stamp == NULL) continue;
     313
     314        psImage *im = stamp->matrix;
     315        if (im == NULL) continue;
     316
     317        stampNum++;
     318
     319        if (stamp->status != PM_SUBTRACTION_STAMP_USED) continue;
     320
     321        // renormalize the section
     322        float maxValue = 0;
     323        for (int iy = 0; iy < im->numRows; iy++) {
     324            for (int ix = 0; ix < im->numCols; ix++) {
     325                maxValue = PS_MAX(maxValue, im->data.F64[iy][ix]);
     326            }
     327        }
     328        if (maxValue == 0.0) continue;
     329        for (int iy = 0; iy < im->numRows; iy++) {
     330            for (int ix = 0; ix < im->numCols; ix++) {
     331                canvas->data.F64[y0 + iy][x0 + ix] = (im->data.F64[iy][ix] / maxValue - matrixNorm->data.F64[iy][ix]) / matrixNorm->data.F64[iy][ix];
     332            }
     333        }
     334    }
     335
     336    psImage *canvas32 = pmVisualImageToFloat(canvas);
     337    pmVisualRangeImage(kapa2, canvas32, "Least_Squares", 0, -100.0, 100.0);
     338
     339    if (0) {
     340        static int count = 0;
     341        char filename[64];
     342        sprintf (filename, "chisq.%02d.fits", count);
     343        count ++;
     344        psFits *fits = psFitsOpen (filename, "w");
     345        psFitsWriteImage (fits, NULL, canvas32, 0, NULL);
     346        psFitsClose (fits);
     347    }
     348
     349    pmVisualAskUser(&plotLeastSquares);
     350    psFree(canvas);
     351    psFree(canvas32);
     352    return true;
     353}
     354
    233355bool pmSubtractionVisualShowSubtraction(psImage *image, psImage *ref, psImage *sub) {
    234356
     
    304426    }
    305427
    306     // XXX clear the overlay(s) (red at least!)
     428    // clear the overlay (red at least!)
     429    KiiEraseOverlay (kapa2, "red");
    307430
    308431    // get the kernel sizes
     
    337460    int nKernels = 0;
    338461
     462    // paste in the kernel images, scaled by sum2
    339463    if (maxStamp->convolutions1) {
    340464        // output image is a grid of NXsub by NYsub sub-images
     
    359483            int yPix = ySub * (2*footprint + 1 + 3) + footprint;
    360484           
    361             double sum = 0.0;
    362485            double sum2 = 0.0;
    363486            for (int y = -footprint; y <= footprint; y++) {
    364487                for (int x = -footprint; x <= footprint; x++) {
    365                     output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x];
    366                     sum += kernel->kernel[y][x];
    367488                    sum2 += PS_SQR(kernel->kernel[y][x]);
    368489                }
    369490            }
    370             // fprintf (stderr, "kernel %d, sum %f, sum2: %e\n", i, sum, sum2);
     491            float scale = sqrt(sum2) / PS_SQR(2*footprint + 1);
     492            for (int y = -footprint; y <= footprint; y++) {
     493                for (int x = -footprint; x <= footprint; x++) {
     494                    output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x] / scale;
     495                }
     496            }
    371497        }               
    372498        pmVisualScaleImage(kapa2, output, "Image", 0, true);
     
    401527            int yPix = ySub * (2*footprint + 1 + 3) + footprint;
    402528           
    403             double sum = 0.0;
    404529            double sum2 = 0.0;
    405530            for (int y = -footprint; y <= footprint; y++) {
    406531                for (int x = -footprint; x <= footprint; x++) {
    407                     output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x];
    408                     sum += kernel->kernel[y][x];
    409532                    sum2 += PS_SQR(kernel->kernel[y][x]);
    410533                }
    411534            }
    412             // fprintf (stderr, "kernel %d, sum %f, sum2: %e\n", i, sum, sum2);
     535            float scale = sqrt(sum2) / PS_SQR(2*footprint + 1);
     536            for (int y = -footprint; y <= footprint; y++) {
     537                for (int x = -footprint; x <= footprint; x++) {
     538                    output->data.F32[y + yPix][x + xPix] = kernel->kernel[y][x] / scale;
     539                }
     540            }
    413541        }               
    414542        pmVisualScaleImage(kapa2, output, "Image", 1, true);
     
    468596    KiiLoadOverlay (kapa2, overlay, Noverlay, "red");
    469597    FREE (overlay);
    470     return true;
    471 }
    472 
    473 static int footprint = 0;
    474 static int NX = 0;
    475 static int NY = 0;
    476 static psImage *sourceImage      = NULL;
    477 static psImage *targetImage      = NULL;
    478 static psImage *residualImage    = NULL;
    479 static psImage *fresidualImage   = NULL;
    480 static psImage *differenceImage  = NULL;
    481 static psImage *convolutionImage = NULL;
    482 
    483 bool pmSubtractionVisualShowFitInit(pmSubtractionStampList *stamps) {
    484 
    485     if (!pmVisualTestLevel("ppsub.fit", 1)) return true;
    486 
    487     // generate 4 storage images large enough to hold the N stamps:
    488 
    489     footprint = stamps->footprint;
    490 
    491     float NXf = sqrt(stamps->num);
    492     NX = (int) NXf == NXf ? NXf : NXf + 1.0;
    493    
    494     float NYf = stamps->num / NX;
    495     NY = (int) NYf == NY ? NYf : NYf + 1.0;
    496 
    497     int NXpix = (2*footprint + 1) * NX;
    498     NXpix += (NX > 1) ? 3 * NX : 0;
    499 
    500     int NYpix = (2*footprint + 1) * NY;
    501     NYpix += (NY > 1) ? 3 * NY : 0;
    502 
    503     sourceImage      = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
    504     targetImage      = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
    505     residualImage    = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
    506     fresidualImage   = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
    507     differenceImage  = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
    508     convolutionImage = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
    509    
    510     psImageInit (sourceImage,      0.0);
    511     psImageInit (targetImage,      0.0);
    512     psImageInit (residualImage,    0.0);
    513     psImageInit (fresidualImage,   0.0);
    514     psImageInit (differenceImage,  0.0);
    515     psImageInit (convolutionImage, 0.0);
    516 
    517     return true;
    518 }
    519 
    520 bool pmSubtractionVisualShowFitAddStamp(psKernel *target, psKernel *source, psKernel *convolution, double background, double norm, int index) {
    521 
    522     if (!pmVisualTestLevel("ppsub.stamp", 1)) return true;
    523 
    524     double sum;
    525 
    526     int NXoff = index % NX;
    527     int NYoff = index / NX;
    528 
    529     int NXpix = NXoff * (2*footprint + 1 + 3) + footprint;
    530     int NYpix = NYoff * (2*footprint + 1 + 3) + footprint;
    531 
    532     // insert the (target) kernel into the (target) image:
    533     sum = 0.0;
    534     for (int y = -footprint; y <= footprint; y++) {
    535         for (int x = -footprint; x <= footprint; x++) {
    536             targetImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x];
    537             sum += targetImage->data.F32[y + NYpix][x + NXpix];
    538         }
    539     }
    540     targetImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
    541 
    542     // insert the (source) kernel into the (source) image:
    543     sum = 0.0;
    544     for (int y = -footprint; y <= footprint; y++) {
    545         for (int x = -footprint; x <= footprint; x++) {
    546             sourceImage->data.F32[y + NYpix][x + NXpix] = source->kernel[y][x];
    547             sum += sourceImage->data.F32[y + NYpix][x + NXpix];
    548         }
    549     }
    550     sourceImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
    551 
    552     // insert the (convolution) kernel into the (convolution) image:
    553     sum = 0.0;
    554     for (int y = -footprint; y <= footprint; y++) {
    555         for (int x = -footprint; x <= footprint; x++) {
    556             convolutionImage->data.F32[y + NYpix][x + NXpix] = convolution->kernel[y][x];
    557             sum += convolutionImage->data.F32[y + NYpix][x + NXpix];
    558         }
    559     }
    560     convolutionImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
    561    
    562     // insert the (difference) kernel into the (difference) image:
    563     sum = 0.0;
    564     for (int y = -footprint; y <= footprint; y++) {
    565         for (int x = -footprint; x <= footprint; x++) {
    566             differenceImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x] - background - source->kernel[y][x] * norm;
    567             sum += differenceImage->data.F32[y + NYpix][x + NXpix];
    568         }
    569     }
    570     differenceImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
    571 
    572     // insert the (residual) kernel into the (residual) image:
    573     sum = 0.0;
    574     for (int y = -footprint; y <= footprint; y++) {
    575         for (int x = -footprint; x <= footprint; x++) {
    576             residualImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x] - background - source->kernel[y][x] * norm - convolution->kernel[y][x];
    577             sum += residualImage->data.F32[y + NYpix][x + NXpix];
    578         }
    579     }
    580     residualImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
    581 
    582     // insert the (fresidual) kernel into the (fresidual) image:
    583     for (int y = -footprint; y <= footprint; y++) {
    584         for (int x = -footprint; x <= footprint; x++) {
    585             fresidualImage->data.F32[y + NYpix][x + NXpix] = residualImage->data.F32[y + NYpix][x + NXpix] / sqrt(PS_MAX(target->kernel[y][x], 100.0));
    586         }
    587     }
    588598    return true;
    589599}
     
    611621}
    612622
    613 bool pmSubtractionVisualShowFit(double norm) {
    614 
    615     // for testing, dump the residual image and exit
    616     if (0) {
    617         psMetadata *header = psMetadataAlloc();
    618         psMetadataAddF32 (header, PS_LIST_TAIL, "NORM", 0, "Normalization", norm);
    619         psFits *fits = psFitsOpen("resid.fits", "w");
    620         psFitsWriteImage(fits, header, residualImage, 0, NULL);
    621         psFitsClose(fits);
    622         // exit (0);
    623     }
     623static int footprint = 0;
     624static int NX = 0;
     625static int NY = 0;
     626static psImage *sourceImage      = NULL;
     627static psImage *targetImage      = NULL;
     628static psImage *residualImage    = NULL;
     629static psImage *fresidualImage   = NULL;
     630static psImage *differenceImage  = NULL;
     631static psImage *convolutionImage = NULL;
     632
     633bool pmSubtractionVisualShowFit(pmSubtractionStampList *stamps, pmSubtractionKernels *kernels) {
    624634
    625635    if (!pmVisualTestLevel("ppsub.fit", 1)) return true;
     
    627637    if (!pmVisualInitWindow(&kapa1, "ppSub:Images")) return false;
    628638    if (!pmVisualInitWindow(&kapa2, "ppSub:Misc")) return false;
     639
     640    // set up holding images for the visualization
     641    pmSubtractionVisualShowFitInit (stamps);
     642
     643    int numKernels = kernels->num;      // Number of kernels
     644
     645    psImage *polyValues = NULL;         // Polynomial values
     646    psKernel *residual = psKernelAlloc(-stamps->footprint, stamps->footprint, -stamps->footprint, stamps->footprint); // Residual image
     647
     648    double norm = p_pmSubtractionSolutionNorm(kernels); // Normalisation
     649
     650    for (int i = 0; i < stamps->num; i++) {
     651        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // The stamp of interest
     652        if (stamp->status != PM_SUBTRACTION_STAMP_USED) { continue; }
     653
     654        // Calculate coefficients of the kernel basis functions
     655        polyValues = p_pmSubtractionPolynomial(polyValues, kernels->spatialOrder, stamp->xNorm, stamp->yNorm);
     656        double background = p_pmSubtractionSolutionBackground(kernels, polyValues); // Difference in background
     657
     658        psImageInit(residual->image, 0.0);
     659
     660        if (kernels->mode != PM_SUBTRACTION_MODE_DUAL) {
     661            psKernel *target;           // Target postage stamp
     662            psKernel *source;           // Source postage stamp
     663            psArray *convolutions;      // Convolution postage stamps for each kernel basis function
     664            switch (kernels->mode) {
     665              case PM_SUBTRACTION_MODE_1:
     666                target = stamp->image2;
     667                source = stamp->image1;
     668                convolutions = stamp->convolutions1;
     669                break;
     670              case PM_SUBTRACTION_MODE_2:
     671                target = stamp->image1;
     672                source = stamp->image2;
     673                convolutions = stamp->convolutions2;
     674                break;
     675              default:
     676                psAbort("Unsupported subtraction mode: %x", kernels->mode);
     677            }
     678
     679            for (int j = 0; j < numKernels; j++) {
     680                psKernel *convolution = convolutions->data[j]; // Convolution
     681                double coefficient = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, false); // Coefficient
     682                for (int y = - footprint; y <= footprint; y++) {
     683                    for (int x = - footprint; x <= footprint; x++) {
     684                        residual->kernel[y][x] += convolution->kernel[y][x] * coefficient;
     685                    }
     686                }
     687            }
     688            // visualize the target, source, convolution and residual
     689            pmSubtractionVisualShowFitAddStamp (target, source, residual, background, norm, i);
     690        } else {
     691            // Dual convolution
     692            psArray *convolutions1 = stamp->convolutions1; // Convolutions of the first image
     693            psArray *convolutions2 = stamp->convolutions2; // Convolutions of the second image
     694            psKernel *image1 = stamp->image1; // The first image
     695            psKernel *image2 = stamp->image2; // The second image
     696
     697            for (int j = 0; j < numKernels; j++) {
     698                psKernel *conv1 = convolutions1->data[j]; // Convolution of first image
     699                psKernel *conv2 = convolutions2->data[j]; // Convolution of second image
     700                double coeff1 = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, false); // Coefficient 1
     701                double coeff2 = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, true); // Coefficient 2
     702
     703                for (int y = - footprint; y <= footprint; y++) {
     704                    for (int x = - footprint; x <= footprint; x++) {
     705                        residual->kernel[y][x] += conv2->kernel[y][x] * coeff2 + conv1->kernel[y][x] * coeff1;
     706                    }
     707                }
     708            }
     709            // visualize the target, source, convolution and residual
     710            pmSubtractionVisualShowFitAddStamp (image2, image1, residual, background, norm, i);
     711        }
     712    }
     713    pmSubtractionVisualShowFitImage(norm);
     714
     715    return true;
     716}
     717
     718// generate 4 storage images large enough to hold the stamps:
     719bool pmSubtractionVisualShowFitInit(pmSubtractionStampList *stamps) {
     720
     721    footprint = stamps->footprint;
     722
     723    float NXf = sqrt(stamps->num);
     724    NX = (int) NXf == NXf ? NXf : NXf + 1.0;
     725   
     726    float NYf = stamps->num / NX;
     727    NY = (int) NYf == NY ? NYf : NYf + 1.0;
     728
     729    int NXpix = (2*footprint + 1) * NX;
     730    NXpix += (NX > 1) ? 3 * NX : 0;
     731
     732    int NYpix = (2*footprint + 1) * NY;
     733    NYpix += (NY > 1) ? 3 * NY : 0;
     734
     735    sourceImage      = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     736    targetImage      = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     737    residualImage    = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     738    fresidualImage   = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     739    differenceImage  = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     740    convolutionImage = psImageAlloc (NXpix, NYpix, PS_TYPE_F32);
     741   
     742    psImageInit (sourceImage,      0.0);
     743    psImageInit (targetImage,      0.0);
     744    psImageInit (residualImage,    0.0);
     745    psImageInit (fresidualImage,   0.0);
     746    psImageInit (differenceImage,  0.0);
     747    psImageInit (convolutionImage, 0.0);
     748
     749    return true;
     750}
     751
     752bool pmSubtractionVisualShowFitAddStamp(psKernel *target, psKernel *source, psKernel *convolution, double background, double norm, int index) {
     753
     754    double sum;
     755
     756    int NXoff = index % NX;
     757    int NYoff = index / NX;
     758
     759    int NXpix = NXoff * (2*footprint + 1 + 3) + footprint;
     760    int NYpix = NYoff * (2*footprint + 1 + 3) + footprint;
     761
     762    // insert the (target) kernel into the (target) image:
     763    sum = 0.0;
     764    for (int y = -footprint; y <= footprint; y++) {
     765        for (int x = -footprint; x <= footprint; x++) {
     766            targetImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x];
     767            sum += targetImage->data.F32[y + NYpix][x + NXpix];
     768        }
     769    }
     770    targetImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     771
     772    // insert the (source) kernel into the (source) image:
     773    sum = 0.0;
     774    for (int y = -footprint; y <= footprint; y++) {
     775        for (int x = -footprint; x <= footprint; x++) {
     776            sourceImage->data.F32[y + NYpix][x + NXpix] = source->kernel[y][x];
     777            sum += sourceImage->data.F32[y + NYpix][x + NXpix];
     778        }
     779    }
     780    sourceImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     781
     782    // insert the (convolution) kernel into the (convolution) image:
     783    sum = 0.0;
     784    for (int y = -footprint; y <= footprint; y++) {
     785        for (int x = -footprint; x <= footprint; x++) {
     786            convolutionImage->data.F32[y + NYpix][x + NXpix] = convolution->kernel[y][x];
     787            sum += convolutionImage->data.F32[y + NYpix][x + NXpix];
     788        }
     789    }
     790    convolutionImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     791   
     792    // insert the (difference) kernel into the (difference) image:
     793    sum = 0.0;
     794    for (int y = -footprint; y <= footprint; y++) {
     795        for (int x = -footprint; x <= footprint; x++) {
     796            differenceImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x] - background - source->kernel[y][x] * norm;
     797            sum += differenceImage->data.F32[y + NYpix][x + NXpix];
     798        }
     799    }
     800    differenceImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     801
     802    // insert the (residual) kernel into the (residual) image:
     803    sum = 0.0;
     804    for (int y = -footprint; y <= footprint; y++) {
     805        for (int x = -footprint; x <= footprint; x++) {
     806            residualImage->data.F32[y + NYpix][x + NXpix] = target->kernel[y][x] - background - source->kernel[y][x] * norm - convolution->kernel[y][x];
     807            sum += residualImage->data.F32[y + NYpix][x + NXpix];
     808        }
     809    }
     810    residualImage->data.F32[footprint + 1 + NYpix][NXpix] = sum;
     811
     812    // insert the (fresidual) kernel into the (fresidual) image:
     813    for (int y = -footprint; y <= footprint; y++) {
     814        for (int x = -footprint; x <= footprint; x++) {
     815            fresidualImage->data.F32[y + NYpix][x + NXpix] = residualImage->data.F32[y + NYpix][x + NXpix] / sqrt(PS_MAX(target->kernel[y][x], 100.0));
     816        }
     817    }
     818    return true;
     819}
     820
     821bool pmSubtractionVisualShowFitImage(double norm) {
    629822
    630823    KiiEraseOverlay (kapa1, "red");
     
    673866    psVector *x = psVectorAllocEmpty (kernels->num, PS_TYPE_F32);
    674867    psVector *y = psVectorAllocEmpty (kernels->num, PS_TYPE_F32);
     868    psVector *dy = psVectorAllocEmpty (kernels->num, PS_TYPE_F32);
    675869
    676870    graphdata.xmin = -1.0;
     
    685879        x->data.F32[i] = i;
    686880        y->data.F32[i] = p_pmSubtractionSolutionCoeff(kernels, polyValues, i, false);
     881        dy->data.F32[i] = kernels->solution1err->data.F64[i];
    687882        graphdata.ymin = PS_MIN(graphdata.ymin, y->data.F32[i]);
    688883        graphdata.ymax = PS_MAX(graphdata.ymax, y->data.F32[i]);
    689884    }
    690     x->n = y->n = kernels->num;
     885    x->n = y->n = dy->n = kernels->num;
    691886
    692887    float range;
     
    709904    graphdata.size = 0.5;
    710905    graphdata.style = 2;
     906    graphdata.etype |= 0x01;
    711907
    712908    KapaPrepPlot   (kapa3, x->n, &graphdata);
    713909    KapaPlotVector (kapa3, x->n, x->data.F32, "x");
    714910    KapaPlotVector (kapa3, x->n, y->data.F32, "y");
     911    KapaPlotVector (kapa3, x->n, dy->data.F32, "dym");
     912    KapaPlotVector (kapa3, x->n, dy->data.F32, "dyp");
    715913
    716914    psFree (x);
    717915    psFree (y);
     916    psFree (dy);
    718917    psFree (polyValues);
     918
     919    pmVisualAskUser(NULL);
     920    return true;
     921}
     922
     923// plot log(flux) vs log(chisq), log(flux) vs log(moments), log(chisq) vs log(moments)
     924bool pmSubtractionVisualPlotChisqAndMoments(psVector *fluxes, psVector *chisq, psVector *moments) {
     925
     926    Graphdata graphdata;
     927    KapaSection section;
     928
     929    if (!pmVisualTestLevel("ppsub.fit", 1)) return true;
     930
     931    if (!pmVisualInitWindow(&kapa3, "ppSub:plots")) return false;
     932
     933    KapaClearSections (kapa3);
     934    KapaInitGraph (&graphdata);
     935    KiiResize(kapa3, 1500, 500);
     936
     937    psVector *lchi = psVectorAlloc (fluxes->n, PS_TYPE_F32);
     938    psVector *lflx = psVectorAlloc (fluxes->n, PS_TYPE_F32);
     939    psVector *lMxx = psVectorAlloc (fluxes->n, PS_TYPE_F32);
     940
     941    // construct the plot vectors
     942    for (int i = 0; i < fluxes->n; i++) {
     943        lchi->data.F32[i] = log10(chisq->data.F32[i]);
     944        lflx->data.F32[i] = log10(fluxes->data.F32[i]);
     945        lMxx->data.F32[i] = log10(moments->data.F32[i]);
     946    }
     947
     948    section.bg = KapaColorByName ("none"); // XXX probably should be 'none'
     949
     950    // section 1: lflux vs lchi
     951    section.dx = 0.33;
     952    section.dy = 1.00;
     953    section.x  = 0.00;
     954    section.y  = 0.00;
     955    section.name = psStringCopy ("flux.v.chi");
     956    KapaSetSection (kapa3, &section);
     957    psFree (section.name);
     958
     959    graphdata.color = KapaColorByName ("black");
     960    pmVisualScaleGraphdata(&graphdata, lflx, lchi, false);
     961    KapaSetLimits (kapa3, &graphdata);
     962
     963    KapaSetFont (kapa3, "helvetica", 14);
     964    KapaBox (kapa3, &graphdata);
     965    KapaSendLabel (kapa3, "log(flux)", KAPA_LABEL_XM);
     966    KapaSendLabel (kapa3, "log(chisq)", KAPA_LABEL_YM);
     967
     968    graphdata.color = KapaColorByName ("black");
     969    graphdata.ptype = 2;
     970    graphdata.size = 0.5;
     971    graphdata.style = 2;
     972
     973    KapaPrepPlot   (kapa3, lflx->n, &graphdata);
     974    KapaPlotVector (kapa3, lflx->n, lflx->data.F32, "x");
     975    KapaPlotVector (kapa3, lflx->n, lchi->data.F32, "y");
     976
     977    // section 2: lflux vs lMxx
     978    section.dx = 0.33;
     979    section.dy = 1.00;
     980    section.x  = 0.33;
     981    section.y  = 0.00;
     982    section.name = psStringCopy ("flux.v.mom");
     983    KapaSetSection (kapa3, &section);
     984    psFree (section.name);
     985
     986    graphdata.color = KapaColorByName ("black");
     987    pmVisualScaleGraphdata(&graphdata, lflx, lMxx, false);
     988    KapaSetLimits (kapa3, &graphdata);
     989
     990    KapaSetFont (kapa3, "helvetica", 14);
     991    KapaBox (kapa3, &graphdata);
     992    KapaSendLabel (kapa3, "log(flux)", KAPA_LABEL_XM);
     993    KapaSendLabel (kapa3, "log(moments)", KAPA_LABEL_YM);
     994
     995    graphdata.color = KapaColorByName ("black");
     996    graphdata.ptype = 2;
     997    graphdata.size = 0.5;
     998    graphdata.style = 2;
     999
     1000    KapaPrepPlot   (kapa3, lflx->n, &graphdata);
     1001    KapaPlotVector (kapa3, lflx->n, lflx->data.F32, "x");
     1002    KapaPlotVector (kapa3, lflx->n, lMxx->data.F32, "y");
     1003
     1004    // section 1: lflux vs lchi
     1005    section.dx = 0.33;
     1006    section.dy = 1.00;
     1007    section.x  = 0.66;
     1008    section.y  = 0.00;
     1009    section.name = psStringCopy ("chi.v.mom");
     1010    KapaSetSection (kapa3, &section);
     1011    psFree (section.name);
     1012
     1013    graphdata.color = KapaColorByName ("black");
     1014    pmVisualScaleGraphdata(&graphdata, lchi, lMxx, false);
     1015    KapaSetLimits (kapa3, &graphdata);
     1016
     1017    KapaSetFont (kapa3, "helvetica", 14);
     1018    KapaBox (kapa3, &graphdata);
     1019    KapaSendLabel (kapa3, "log(chisq)", KAPA_LABEL_XM);
     1020    KapaSendLabel (kapa3, "log(moments)", KAPA_LABEL_YM);
     1021
     1022    graphdata.color = KapaColorByName ("black");
     1023    graphdata.ptype = 2;
     1024    graphdata.size = 0.5;
     1025    graphdata.style = 2;
     1026
     1027    KapaPrepPlot   (kapa3, lflx->n, &graphdata);
     1028    KapaPlotVector (kapa3, lflx->n, lchi->data.F32, "x");
     1029    KapaPlotVector (kapa3, lflx->n, lMxx->data.F32, "y");
    7191030
    7201031    pmVisualAskUser(NULL);
  • trunk/psModules/src/imcombine/pmSubtractionVisual.h

    r29601 r30622  
    66bool pmSubtractionVisualPlotStamps(pmSubtractionStampList *stamps, pmReadout *ro);
    77bool pmSubtractionVisualPlotLeastSquares(pmSubtractionStampList *stamps);
     8bool pmSubtractionVisualPlotLeastSquaresResid (const pmSubtractionStampList *stamps, psImage *matrixIn, int nUsed);
    89bool pmSubtractionVisualShowSubtraction(psImage *image, psImage *ref, psImage *sub);
     10
     11bool pmSubtractionVisualShowFit(pmSubtractionStampList *stamps, pmSubtractionKernels *kernels);
    912bool pmSubtractionVisualShowFitInit(pmSubtractionStampList *stamps);
    1013bool pmSubtractionVisualShowFitAddStamp(psKernel *target, psKernel *source, psKernel *convolution, double background, double norm, int index);
    11 bool pmSubtractionVisualShowFit(double norm);
     14bool pmSubtractionVisualShowFitImage(double norm);
     15
    1216bool pmSubtractionVisualPlotFit(const pmSubtractionKernels *kernels);
    1317bool pmSubtractionVisualShowKernels(pmSubtractionKernels *kernels);
    1418bool pmSubtractionVisualShowBasis(pmSubtractionStampList *stamps);
     19bool pmSubtractionVisualPlotChisqAndMoments(psVector *fluxes, psVector *chisq, psVector *moments);
    1520
    1621#endif
Note: See TracChangeset for help on using the changeset viewer.