IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 15443


Ignore:
Timestamp:
Nov 2, 2007, 4:28:24 PM (19 years ago)
Author:
Paul Price
Message:

Adding function pmSubtractionOrder (choice of name is probably not the best) to determine which of the two images under consideration should be convolved to match the other. Originally was doing this by solving for a RING kernel of width 1, going both ways, and comparing the deviations for each. However, when doing stacks this didn't work (convolving the wider fake Gaussian image gave smaller deviations than convolving the narrower input image), so have settled on measuring the second moments for each stamp, and using the ratio of moments between the two images to determine which is wider; this seems to work for both subtracting and stacking images. In the process, plugged a few memory leaks, and added code to support convolving either way (or both ways; this will be useful when it comes time to code the dual convolution algorithm).

Location:
trunk/psModules/src/imcombine
Files:
10 edited

Legend:

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

    r14701 r15443  
    5252
    5353    // Convolve the image with the kernel --- we're basically applying a matched filter and then thresholding
    54     psImage *convolved = NULL;          // Convolved image
     54    pmReadout *convRO = pmReadoutAlloc(NULL); // Readout with convolved image
     55    pmReadout *inRO = pmReadoutAlloc(NULL); // Readout with input image
     56    inRO->image = image;
    5557    for (int i = 0; i < numRegions; i++) {
    5658        psRegion *region = regions->data[i]; // Region of interest
    5759        psVector *solution = solutions->data[i]; // Solution of interest
    58         if (!pmSubtractionConvolve(&convolved, NULL, NULL, image, NULL, NULL, 0,
    59                                    region, solution, kernels, true)) {
     60        if (!pmSubtractionConvolve(convRO, inRO, NULL, NULL, 0, region, solution, kernels, true,
     61                                   PM_SUBTRACTION_MODE_1)) {
    6062            psError(PS_ERR_UNKNOWN, false, "Unable to convolve mask image in region %d.", i);
    61             psFree(convolved);
    62             psFree(image);
     63            psFree(convRO);
     64            psFree(inRO);
    6365            return NULL;
    6466        }
     
    7375        if (!kernel) {
    7476            psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
    75             psFree(convolved);
    76             psFree(image);
     77            psFree(convRO);
     78            psFree(inRO);
    7779            return NULL;
    7880        }
     
    8587        psFree(kernel);
    8688
    87         psImage *subConv = psImageSubset(convolved, *region); // Sub-image of convolved image
     89        psImage *subConv = psImageSubset(convRO->image, *region); // Sub-image of convolved image
    8890        psBinaryOp(subConv, subConv, "*", psScalarAlloc(1.0 / sum, PS_TYPE_F32));
    8991    }
    90     psFree(image);
     92    psFree(inRO);
     93    psImage *convolved = psMemIncrRefCounter(convRO->image);
     94    psFree(convRO);
    9195
    9296    // Threshold the convolved image
  • trunk/psModules/src/imcombine/pmSubtraction.c

    r15427 r15443  
    44 *  @author GLG, MHPCC
    55 *
    6  *  @version $Revision: 1.71 $ $Name: not supported by cvs2svn $
    7  *  @date $Date: 2007-11-01 01:11:03 $
     6 *  @version $Revision: 1.72 $ $Name: not supported by cvs2svn $
     7 *  @date $Date: 2007-11-03 02:28:24 $
    88 *
    99 *  Copyright 2004-2007 Institute for Astronomy, University of Hawaii
     
    7676
    7777// Generate the kernel to apply to the variance from the normal kernel
    78 static psKernel *varianceKernel(psKernel *normalKernel // Normal kernel
     78static psKernel *varianceKernel(psKernel *out, // Output kernel
     79                                psKernel *normalKernel // Normal kernel
    7980                                )
    8081{
     
    8384    int yMin = normalKernel->yMin, yMax = normalKernel->yMax;
    8485
    85     psKernel *kernel = psKernelAlloc(xMin, xMax, yMin, yMax); // Kernel to return
     86    if (!out) {
     87        out = psKernelAlloc(xMin, xMax, yMin, yMax);
     88    }
    8689
    8790    // Take the square of the normal kernel
     
    9396            sumNormal += value;
    9497            sumVariance += value2;
    95             kernel->kernel[v][u] = value2;
     98            out->kernel[v][u] = value2;
    9699        }
    97100    }
     
    99102    // Normalise so that the sum of the variance kernel is the square of the sum of the normal kernel
    100103    // This is required to keep the relative scaling between the image and the weight map
    101     psBinaryOp(kernel->image, kernel->image, "*",
    102                psScalarAlloc(PS_SQR(sumNormal) / sumVariance, PS_TYPE_F32));
    103 
    104     return kernel;
     104    psBinaryOp(out->image, out->image, "*", psScalarAlloc(PS_SQR(sumNormal) / sumVariance, PS_TYPE_F32));
     105
     106    return out;
    105107}
    106108
     
    264266}
    265267
    266 // Generate the convolved reference image
    267 static psKernel *convolveRef(const pmSubtractionKernels *kernels, // Kernel basis functions
    268                              int index, // Kernel basis function index
    269                              const psKernel *image, // Image to convolve (a kernel for convenience)
    270                              int footprint // Size of region of interest
    271                              )
     268
     269// Convolve an image using FFT
     270static void convolveFFT(psImage *target,// Place the result in here
     271                        const psImage *image, // Image to convolve
     272                        const psImage *mask, // Mask image
     273                        psMaskType maskVal, // Value to mask
     274                        const psKernel *kernel, // Kernel by which to convolve
     275                        psRegion region,// Region of interest
     276                        float background, // Background to add
     277                        int size        // Size of (square) kernel
     278                        )
     279{
     280    psRegion border = psRegionSet(region.x0 - size, region.x1 + size,
     281                                  region.y0 - size, region.y1 + size); // Add a border
     282
     283    // Casting away const so psImageSubset can add the child
     284    psImage *subImage = psImageSubset((psImage*)image, border); // Subimage to convolve
     285    psImage *subMask = mask ? psImageSubset((psImage*)mask, border) : NULL; // Subimage mask
     286    psImage *convolved = psImageConvolveFFT(subImage, subMask, maskVal, kernel, 0.0); // Convolution
     287    psFree(subImage);
     288    psFree(subMask);
     289
     290    // Now, we have to chop off the borders, and stick it in where it belongs
     291    psImage *subConv = psImageSubset(convolved, psRegionSet(size, -size, size, -size)); // Cut off the edges
     292    psImage *subTarget = psImageSubset(target, region); // Target for this subregion
     293    if (background != 0.0) {
     294        psBinaryOp(subTarget, subConv, "+", psScalarAlloc(background, PS_TYPE_F32));
     295    } else {
     296        int numBytes = subTarget->numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes to copy
     297        for (int y = 0; y < subTarget->numRows; y++) {
     298            memcpy(subTarget->data.F32[y], subConv->data.F32[y], numBytes);
     299        }
     300    }
     301    psFree(subConv);
     302    psFree(convolved);
     303    psFree(subTarget);
     304    return;
     305}
     306
     307// Convolve an image directly
     308static void convolveDirect(psImage *target, // Put the result here
     309                           const psImage *image, // Image to convolve
     310                           const psKernel *kernel, // Kernel by which to convolve
     311                           psRegion region,// Region of interest
     312                           float background, // Background to add
     313                           int size        // Size of (square) kernel
     314                           )
     315{
     316    for (int y = region.y0; y < region.y1; y++) {
     317        for (int x = region.x0; x < region.x1; x++) {
     318            target->data.F32[y][x] = background;
     319            for (int v = -size; v <= size; v++) {
     320                for (int u = -size; u <= size; u++) {
     321                    target->data.F32[y][x] += kernel->kernel[v][u] *
     322                        image->data.F32[y - v][x - u];
     323                }
     324            }
     325        }
     326    }
     327    return;
     328}
     329
     330// Mark a pixel as blank in the image, mask and weight
     331static inline void markBlank(psImage *image, // Image to mark as blank
     332                             psImage *mask, // Mask to mark as blank (or NULL)
     333                             psImage *weight, // Weight map to mark as blank (or NULL)
     334                             int x, int y, // Coordinates to mark blank
     335                             psMaskType blank // Blank mask value
     336    )
     337{
     338    image->data.F32[y][x] = NAN;
     339    if (mask) {
     340        mask->data.PS_TYPE_MASK_DATA[y][x] |= blank;
     341    }
     342    if (weight) {
     343        weight->data.F32[y][x] = NAN;
     344    }
     345    return;
     346}
     347
     348//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     349// Semi-public functions
     350//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     351
     352// Generate the convolution given a precalculated kernel
     353psKernel *p_pmSubtractionConvolveStampPrecalc(const psKernel *image, const psKernel *kernel)
     354{
     355    PS_ASSERT_KERNEL_NON_NULL(image, NULL);
     356    PS_ASSERT_KERNEL_NON_NULL(kernel, NULL);
     357
     358    psImage *conv = psImageConvolveFFT(image->image, NULL, 0, kernel, 0.0); // Convolved image
     359    int x0 = - image->xMin, y0 = - image->yMin; // Position of centre of convolved image
     360    psKernel *convolved = psKernelAllocFromImage(conv, x0, y0); // Kernel version
     361    psFree(conv);
     362    return convolved;
     363}
     364
     365
     366//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     367// Public functions
     368//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     369
     370psImage *pmSubtractionMask(const psImage *mask1, const psImage *mask2, psMaskType maskVal,
     371                           int size, int footprint, float badFrac, bool useFFT)
     372{
     373    PS_ASSERT_IMAGE_NON_NULL(mask1, NULL);
     374    PS_ASSERT_IMAGE_TYPE(mask1, PS_TYPE_MASK, NULL);
     375    if (mask2) {
     376        PS_ASSERT_IMAGE_NON_NULL(mask2, NULL);
     377        PS_ASSERT_IMAGE_TYPE(mask2, PS_TYPE_MASK, NULL);
     378        PS_ASSERT_IMAGES_SIZE_EQUAL(mask2, mask1, NULL);
     379    }
     380    PS_ASSERT_INT_NONNEGATIVE(size, NULL);
     381    PS_ASSERT_INT_NONNEGATIVE(footprint, NULL);
     382    if (isfinite(badFrac)) {
     383        PS_ASSERT_FLOAT_LARGER_THAN(badFrac, 0.0, NULL);
     384        PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(badFrac, 1.0, NULL);
     385    }
     386
     387    int numCols = mask1->numCols, numRows = mask1->numRows; // Size of the images
     388
     389    // Dereference inputs for convenience
     390    psMaskType **data1 = mask1->data.PS_TYPE_MASK_DATA;
     391    psMaskType **data2 = NULL;
     392    if (mask2) {
     393        data2 = mask2->data.PS_TYPE_MASK_DATA;
     394    }
     395
     396    // First, a pass through to determine the fraction of bad pixels
     397    if (isfinite(badFrac) && badFrac != 1.0) {
     398        int numBad = 0;                 // Number of bad pixels
     399        for (int y = 0; y < numRows; y++) {
     400            for (int x = 0; x < numCols; x++) {
     401                if (data1[y][x] & maskVal) {
     402                    numBad++;
     403                    continue;
     404                }
     405                if (data2 && data2[y][x] & maskVal) {
     406                    numBad++;
     407                }
     408            }
     409        }
     410        if (numBad > badFrac * numCols * numRows) {
     411            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
     412                    "Fraction of bad pixels (%d/%d=%f) exceeds limit (%f)\n",
     413                    numBad, numCols * numRows, (float)numBad/(float)(numCols * numRows), badFrac);
     414            return NULL;
     415        }
     416    }
     417
     418    // Worried about the masks for bad pixels and bad stamps colliding, so make our own mask
     419    psImage *mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK); // The global mask
     420    psImageInit(mask, 0);
     421    psMaskType **maskData = mask->data.PS_TYPE_MASK_DATA; // Dereference for convenience
     422
     423    // Block out a border around the edge of the image
     424
     425    // Bottom stripe
     426    for (int y = 0; y < PS_MIN(size + footprint, numRows); y++) {
     427        for (int x = 0; x < numCols; x++) {
     428            maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
     429        }
     430    }
     431    // Either side
     432    for (int y = PS_MIN(size + footprint, numRows); y < numRows - size - footprint; y++) {
     433        for (int x = 0; x < PS_MIN(size + footprint, numCols); x++) {
     434            maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
     435        }
     436        for (int x = PS_MAX(numCols - size - footprint, 0); x < numCols; x++) {
     437            maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
     438        }
     439    }
     440    // Top stripe
     441    for (int y = PS_MAX(numRows - size - footprint, 0); y < numRows; y++) {
     442        for (int x = 0; x < numCols; x++) {
     443            maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
     444        }
     445    }
     446
     447    // XXX Could do something smarter here --- we will get images that are predominantly masked (where the
     448    // skycell isn't overlapped by a large fraction by the observation), so that convolving around every bad
     449    // pixel is wasting time.  As a first cut, I've put in a check on the fraction of bad pixels, but we could
     450    // imagine looking for the edge of big regions and convolving just at the edge.  As a second cut, allow
     451    // use of FFT convolution.
     452
     453    for (int y = 0; y < numRows; y++) {
     454        for (int x = 0; x < numCols; x++) {
     455            if (data1[y][x] & maskVal) {
     456                maskData[y][x] |= PM_SUBTRACTION_MASK_BAD_1;
     457            }
     458            if (data2 && data2[y][x] & maskVal) {
     459                maskData[y][x] |= PM_SUBTRACTION_MASK_BAD_2;
     460            }
     461        }
     462    }
     463
     464    // Block out the entire stamp footprint around bad input pixels.
     465
     466    // We want to block out with the CONVOLVE mask anything that would be bad if we convolved with a bad
     467    // reference pixel (within 'size').  Then we want to block out with the FOOTPRINT mask everything within a
     468    // footprint's distance of those (within 'footprint').
     469
     470    if (useFFT) {
     471        if (!psImageConvolveMaskFFT(mask, mask, PM_SUBTRACTION_MASK_BAD_1 | PM_SUBTRACTION_MASK_BAD_2,
     472                                    PM_SUBTRACTION_MASK_CONVOLVE_1 | PM_SUBTRACTION_MASK_FOOTPRINT_1,
     473                                    -size, size, -size, size, 0.5)) {
     474            psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad pixels from mask 1.");
     475            psFree(mask);
     476            return NULL;
     477        }
     478        if (!psImageConvolveMaskFFT(mask, mask, PM_SUBTRACTION_MASK_BAD_2,
     479                                    PM_SUBTRACTION_MASK_CONVOLVE_2 | PM_SUBTRACTION_MASK_FOOTPRINT_2,
     480                                    -size, size, -size, size, 0.5)) {
     481            psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad pixels from mask 2.");
     482            psFree(mask);
     483            return NULL;
     484        }
     485        if (!psImageConvolveMaskFFT(mask, mask, PM_SUBTRACTION_MASK_CONVOLVE_1,
     486                                    PM_SUBTRACTION_MASK_FOOTPRINT_1,
     487                                    -footprint, footprint, -footprint, footprint, 0.5)) {
     488            psError(PS_ERR_UNKNOWN, false, "Unable to reconvolve bad pixels from mask 1.");
     489            psFree(mask);
     490            return NULL;
     491        }
     492        if (!psImageConvolveMaskFFT(mask, mask, PM_SUBTRACTION_MASK_CONVOLVE_2,
     493                                    PM_SUBTRACTION_MASK_FOOTPRINT_2,
     494                                    -footprint, footprint, -footprint, footprint, 0.5)) {
     495            psError(PS_ERR_UNKNOWN, false, "Unable to reconvolve bad pixels from mask 2.");
     496            psFree(mask);
     497            return NULL;
     498        }
     499    } else {
     500        if (!psImageConvolveMaskDirect(mask, mask, PM_SUBTRACTION_MASK_BAD_1,
     501                                       PM_SUBTRACTION_MASK_CONVOLVE_1 | PM_SUBTRACTION_MASK_FOOTPRINT_1,
     502                                       -size, size, -size, size)) {
     503            psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad pixels from mask 1.");
     504            psFree(mask);
     505            return NULL;
     506        }
     507        if (!psImageConvolveMaskDirect(mask, mask, PM_SUBTRACTION_MASK_BAD_2,
     508                                       PM_SUBTRACTION_MASK_CONVOLVE_2 | PM_SUBTRACTION_MASK_FOOTPRINT_2,
     509                                       -size, size, -size, size)) {
     510            psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad pixels from mask 2.");
     511            psFree(mask);
     512            return NULL;
     513        }
     514        if (!psImageConvolveMaskDirect(mask, mask, PM_SUBTRACTION_MASK_CONVOLVE_1,
     515                                       PM_SUBTRACTION_MASK_FOOTPRINT_1,
     516                                       -footprint, footprint, -footprint, footprint)) {
     517            psError(PS_ERR_UNKNOWN, false, "Unable to reconvolve bad pixels from mask 1.");
     518            psFree(mask);
     519            return NULL;
     520        }
     521        if (!psImageConvolveMaskDirect(mask, mask, PM_SUBTRACTION_MASK_CONVOLVE_2,
     522                                       PM_SUBTRACTION_MASK_FOOTPRINT_2,
     523                                       -footprint, footprint, -footprint, footprint)) {
     524            psError(PS_ERR_UNKNOWN, false, "Unable to reconvolve bad pixels from mask 2.");
     525            psFree(mask);
     526            return NULL;
     527        }
     528    }
     529
     530    return mask;
     531}
     532
     533// Convolve a stamp by a single kernel basis function
     534static psKernel *convolveStampSingle(const pmSubtractionKernels *kernels, // Kernel basis functions
     535                                     int index, // Kernel basis function index
     536                                     const psKernel *image, // Image to convolve (a kernel for convenience)
     537                                     int footprint // Size of region of interest
     538    )
    272539{
    273540    switch (kernels->type) {
     
    326593      }
    327594      case PM_SUBTRACTION_KERNEL_RINGS: {
    328           psKernel *convolved = psKernelAlloc(-footprint, footprint,
    329                                               -footprint, footprint); // Convolved image
    330595          if (index == kernels->subIndex) {
    331596              // Simply copying over the image
    332597              return convolveOffset(image, 0, 0, footprint);
    333598          }
    334 
     599          psKernel *convolved = psKernelAlloc(-footprint, footprint,
     600                                              -footprint, footprint); // Convolved image
    335601          psArray *preCalc = kernels->preCalc->data[index]; // Precalculated data
    336602          psVector *uCoords = preCalc->data[0]; // u coordinates
     
    357623}
    358624
    359 // Convolve an image using FFT
    360 static void convolveFFT(psImage *target,// Place the result in here
    361                         const psImage *image, // Image to convolve
    362                         const psImage *mask, // Mask image
    363                         const psKernel *kernel, // Kernel by which to convolve
    364                         psRegion region,// Region of interest
    365                         float background, // Background to add
    366                         int size        // Size of (square) kernel
    367                         )
    368 {
    369     psRegion border = psRegionSet(region.x0 - size, region.x1 + size,
    370                                   region.y0 - size, region.y1 + size); // Add a border
    371 
    372     // Casting away const so psImageSubset can add the child
    373     psImage *subImage = psImageSubset((psImage*)image, border); // Subimage to convolve
    374     psImage *subMask = mask ? psImageSubset((psImage*)mask, border) : NULL; // Subimage mask
    375     psImage *convolved = psImageConvolveFFT(subImage, subMask, PM_SUBTRACTION_MASK_REF,
    376                                             kernel, 0.0); // Convolution
    377     psFree(subImage);
    378     psFree(subMask);
    379 
    380     // Now, we have to chop off the borders, and stick it in where it belongs
    381     psImage *subConv = psImageSubset(convolved, psRegionSet(size, -size, size, -size)); // Cut off the edges
    382     psImage *subTarget = psImageSubset(target, region); // Target for this subregion
    383     if (background != 0.0) {
    384         psBinaryOp(subTarget, subConv, "+", psScalarAlloc(background, PS_TYPE_F32));
    385     } else {
    386         int numBytes = subTarget->numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes to copy
    387         for (int y = 0; y < subTarget->numRows; y++) {
    388             memcpy(subTarget->data.F32[y], subConv->data.F32[y], numBytes);
    389         }
    390     }
    391     psFree(subConv);
    392     psFree(convolved);
    393     psFree(subTarget);
    394     return;
    395 }
    396 
    397 // Convolve an image directly
    398 static void convolveDirect(psImage *target, // Put the result here
    399                            const psImage *image, // Image to convolve
    400                            const psKernel *kernel, // Kernel by which to convolve
    401                            psRegion region,// Region of interest
    402                            float background, // Background to add
    403                            int size        // Size of (square) kernel
    404                            )
    405 {
    406     for (int y = region.y0; y < region.y1; y++) {
    407         for (int x = region.x0; x < region.x1; x++) {
    408             target->data.F32[y][x] = background;
    409             for (int v = -size; v <= size; v++) {
    410                 for (int u = -size; u <= size; u++) {
    411                     target->data.F32[y][x] += kernel->kernel[v][u] *
    412                         image->data.F32[y - v][x - u];
    413                 }
    414             }
    415         }
    416     }
    417     return;
    418 }
    419 
    420 // Mark a pixel as blank in the image, mask and weight
    421 static inline void markBlank(psImage *image, // Image to mark as blank
    422                              psImage *mask, // Mask to mark as blank (or NULL)
    423                              psImage *weight, // Weight map to mark as blank (or NULL)
    424                              int x, int y, // Coordinates to mark blank
    425                              psMaskType blank // Blank mask value
     625// Convolve the stamp by each of the kernel basis functions
     626static psArray *convolveStamp(psArray *convolutions, // The convolutions
     627                              const psKernel *image, // Image to convolve
     628                              const pmSubtractionKernels *kernels, // Kernel basis functions
     629                              int footprint // Stamp half-size
    426630    )
    427631{
    428     image->data.F32[y][x] = NAN;
    429     if (mask) {
    430         mask->data.PS_TYPE_MASK_DATA[y][x] |= blank;
    431     }
    432     if (weight) {
    433         weight->data.F32[y][x] = NAN;
    434     }
    435     return;
    436 }
    437 
    438 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    439 // Semi-public functions
    440 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    441 
    442 // Generate the convolution given a precalculated kernel
    443 psKernel *p_pmSubtractionConvolveStampPrecalc(const psKernel *image, const psKernel *kernel)
    444 {
    445     PS_ASSERT_KERNEL_NON_NULL(image, NULL);
    446     PS_ASSERT_KERNEL_NON_NULL(kernel, NULL);
    447 
    448     psImage *conv = psImageConvolveFFT(image->image, NULL, 0, kernel, 0.0); // Convolved image
    449     int x0 = - image->xMin, y0 = - image->yMin; // Position of centre of convolved image
    450     psKernel *convolved = psKernelAllocFromImage(conv, x0, y0); // Kernel version
    451     psFree(conv);
    452     return convolved;
    453 }
    454 
    455 
    456 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    457 // Public functions
    458 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    459 
    460 psImage *pmSubtractionMask(const psImage *refMask, const psImage *inMask, psMaskType maskVal,
    461                            int size, int footprint, float badFrac, bool useFFT)
    462 {
    463     PS_ASSERT_IMAGE_NON_NULL(refMask, NULL);
    464     PS_ASSERT_IMAGE_TYPE(refMask, PS_TYPE_MASK, NULL);
    465     if (inMask) {
    466         PS_ASSERT_IMAGE_NON_NULL(inMask, NULL);
    467         PS_ASSERT_IMAGE_TYPE(inMask, PS_TYPE_MASK, NULL);
    468         PS_ASSERT_IMAGES_SIZE_EQUAL(inMask, refMask, NULL);
    469     }
    470     PS_ASSERT_INT_NONNEGATIVE(size, NULL);
    471     PS_ASSERT_INT_NONNEGATIVE(footprint, NULL);
    472     if (isfinite(badFrac)) {
    473         PS_ASSERT_FLOAT_LARGER_THAN(badFrac, 0.0, NULL);
    474         PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(badFrac, 1.0, NULL);
    475     }
    476 
    477     // Size of the images
    478     int numCols = refMask->numCols;
    479     int numRows = refMask->numRows;
    480 
    481     // Dereference inputs for convenience
    482     psMaskType **inData = NULL;
    483     if (inMask) {
    484         inData = inMask->data.PS_TYPE_MASK_DATA;
    485     }
    486     psMaskType **refData = refMask->data.PS_TYPE_MASK_DATA;
    487 
    488     // First, a pass through to determine the fraction of bad pixels
    489     if (isfinite(badFrac) && badFrac != 1.0) {
    490         int numBad = 0;                 // Number of bad pixels
    491         for (int y = 0; y < numRows; y++) {
    492             for (int x = 0; x < numCols; x++) {
    493                 if (inData && inData[y][x] & maskVal) {
    494                     numBad++;
    495                     continue;
    496                 }
    497                 if (refData[y][x] & maskVal) {
    498                     numBad++;
    499                 }
    500             }
    501         }
    502         if (numBad > badFrac * numCols * numRows) {
    503             psError(PS_ERR_BAD_PARAMETER_VALUE, true,
    504                     "Fraction of bad pixels (%d/%d=%f) exceeds limit (%f)\n",
    505                     numBad, numCols * numRows, (float)numBad/(float)(numCols * numRows), badFrac);
    506             return NULL;
    507         }
    508     }
    509 
    510     // Worried about the masks for bad pixels and bad stamps colliding, so make our own mask
    511     psImage *mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK); // The global mask
    512     psImageInit(mask, 0);
    513     psMaskType **maskData = mask->data.PS_TYPE_MASK_DATA; // Dereference for convenience
    514 
    515     // Block out a border around the edge of the image
    516 
    517     // Bottom stripe
    518     for (int y = 0; y < PS_MIN(size + footprint, numRows); y++) {
    519         for (int x = 0; x < numCols; x++) {
    520             maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
    521         }
    522     }
    523     // Either side
    524     for (int y = PS_MIN(size + footprint, numRows); y < numRows - size - footprint; y++) {
    525         for (int x = 0; x < PS_MIN(size + footprint, numCols); x++) {
    526             maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
    527         }
    528         for (int x = PS_MAX(numCols - size - footprint, 0); x < numCols; x++) {
    529             maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
    530         }
    531     }
    532     // Top stripe
    533     for (int y = PS_MAX(numRows - size - footprint, 0); y < numRows; y++) {
    534         for (int x = 0; x < numCols; x++) {
    535             maskData[y][x] |= PM_SUBTRACTION_MASK_BORDER;
    536         }
    537     }
    538 
    539     // XXX Could do something smarter here --- we will get images that are predominantly masked (where the
    540     // skycell isn't overlapped by a large fraction by the observation), so that convolving around every bad
    541     // pixel is wasting time.  As a first cut, I've put in a check on the fraction of bad pixels, but we could
    542     // imagine looking for the edge of big regions and convolving just at the edge.  As a second cut, allow
    543     // use of FFT convolution.
    544 
    545     for (int y = 0; y < numRows; y++) {
    546         for (int x = 0; x < numCols; x++) {
    547             if (inData && inData[y][x] & maskVal) {
    548                 maskData[y][x] |= PM_SUBTRACTION_MASK_INPUT;
    549             }
    550             if (refData[y][x] & maskVal) {
    551                 maskData[y][x] |= PM_SUBTRACTION_MASK_REF;
    552             }
    553         }
    554     }
    555 
    556     // Block out the entire stamp footprint around bad input pixels.
    557 
    558     // We want to block out with the CONVOLVE mask anything that would be bad if we convolved with a bad
    559     // reference pixel (within 'size').  Then we want to block out with the FOOTPRINT mask everything within a
    560     // footprint's distance of those (within 'footprint').
    561 
    562     if (useFFT) {
    563         if (!psImageConvolveMaskFFT(mask, mask, PM_SUBTRACTION_MASK_INPUT, PM_SUBTRACTION_MASK_FOOTPRINT,
    564                                     -footprint, footprint, -footprint, footprint, 0.5)) {
    565             psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad input pixels.");
    566             psFree(mask);
    567             return NULL;
    568         }
    569         if (!psImageConvolveMaskFFT(mask, mask, PM_SUBTRACTION_MASK_REF, PM_SUBTRACTION_MASK_CONVOLVE,
    570                                     -size, size, -size, size, 0.5)) {
    571             psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad reference pixels.");
    572             psFree(mask);
    573             return NULL;
    574         }
    575         if (!psImageConvolveMaskFFT(mask, mask, PM_SUBTRACTION_MASK_CONVOLVE, PM_SUBTRACTION_MASK_FOOTPRINT,
    576                                     -footprint, footprint, -footprint, footprint, 0.5)) {
    577             psError(PS_ERR_UNKNOWN, false, "Unable to reconvolve bad reference pixels.");
    578             psFree(mask);
    579             return NULL;
    580         }
    581     } else {
    582         if (!psImageConvolveMaskDirect(mask, mask, PM_SUBTRACTION_MASK_INPUT, PM_SUBTRACTION_MASK_FOOTPRINT,
    583                                        -footprint, footprint, -footprint, footprint)) {
    584             psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad input pixels.");
    585             psFree(mask);
    586             return NULL;
    587         }
    588         if (!psImageConvolveMaskDirect(mask, mask, PM_SUBTRACTION_MASK_REF, PM_SUBTRACTION_MASK_CONVOLVE,
    589                                    -size, size, -size, size)) {
    590             psError(PS_ERR_UNKNOWN, false, "Unable to convolve bad reference pixels.");
    591             psFree(mask);
    592             return NULL;
    593         }
    594         if (!psImageConvolveMaskDirect(mask, mask, PM_SUBTRACTION_MASK_CONVOLVE,
    595                                        PM_SUBTRACTION_MASK_FOOTPRINT,
    596                                        -footprint, footprint, -footprint, footprint)) {
    597             psError(PS_ERR_UNKNOWN, false, "Unable to reconvolve bad reference pixels.");
    598             psFree(mask);
    599             return NULL;
    600         }
    601     }
    602 
    603     return mask;
    604 }
    605 
    606 bool pmSubtractionConvolveStamp(pmSubtractionStamp *stamp, const pmSubtractionKernels *kernels, int footprint)
     632    assert(image);
     633    assert(kernels);
     634    assert(footprint >= 0);
     635
     636    if (convolutions) {
     637        // Already done
     638        return convolutions;
     639    }
     640
     641    int numKernels = kernels->num;      // Number of kernels
     642    convolutions = psArrayAlloc(numKernels);
     643
     644    for (int i = 0; i < numKernels; i++) {
     645        convolutions->data[i] = convolveStampSingle(kernels, i, image, footprint);
     646    }
     647
     648    return convolutions;
     649}
     650
     651
     652bool pmSubtractionConvolveStamp(pmSubtractionStamp *stamp, const pmSubtractionKernels *kernels, int footprint,
     653                                pmSubtractionMode mode)
    607654{
    608655    PS_ASSERT_PTR_NON_NULL(stamp, false);
    609     PS_ASSERT_KERNEL_NON_NULL(stamp->reference, false);
    610656    PS_ASSERT_PTR_NON_NULL(kernels, false);
    611657    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, false);
     
    617663    }
    618664
    619     if (stamp->convolutions) {
    620         // Already done
    621         return true;
    622     }
    623 
    624     stamp->convolutions = psArrayAlloc(kernels->num);
    625     psKernel *reference = stamp->reference; // Reference postage stamp
    626 
    627     for (int i = 0; i < kernels->num; i++) {
    628         stamp->convolutions->data[i] = convolveRef(kernels, i, reference, footprint);
     665    switch (mode) {
     666      case PM_SUBTRACTION_MODE_TARGET:
     667      case PM_SUBTRACTION_MODE_1:
     668        stamp->convolutions1 = convolveStamp(stamp->convolutions1, stamp->image1, kernels, footprint);
     669        break;
     670      case PM_SUBTRACTION_MODE_2:
     671        stamp->convolutions2 = convolveStamp(stamp->convolutions2, stamp->image2, kernels, footprint);
     672        break;
     673      case PM_SUBTRACTION_MODE_UNSURE:
     674      case PM_SUBTRACTION_MODE_DUAL:
     675        stamp->convolutions1 = convolveStamp(stamp->convolutions1, stamp->image1, kernels, footprint);
     676        stamp->convolutions2 = convolveStamp(stamp->convolutions2, stamp->image2, kernels, footprint);
     677        break;
     678      default:
     679        psAbort("Unsupported subtraction mode: %x", mode);
    629680    }
    630681
     
    634685
    635686bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels,
    636                                     int footprint)
     687                                    int footprint, pmSubtractionMode mode)
    637688{
    638689    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    676727        psVectorInit(stampVector, 0.0);
    677728
    678         psKernel *input = stamp->input; // Input postage stamp
    679         psKernel *weight = stamp->weight; // Weight map postage stamp
    680 
    681         // Generate convolutions of the reference
    682         if (!pmSubtractionConvolveStamp(stamp, kernels, footprint)) {
     729        // Generate convolutions
     730        if (!pmSubtractionConvolveStamp(stamp, kernels, footprint, mode)) {
    683731            psError(PS_ERR_UNKNOWN, false, "Unable to convolve stamp %d.", i);
    684732            return NULL;
    685733        }
    686734
    687         psArray *convolutions = stamp->convolutions; // Convolutions of the reference for each kernel
     735        psKernel *weight = stamp->weight; // Weight map postage stamp
     736        psKernel *target;               // Target postage stamp
     737        psArray *convolutions;          // Convolutions for each kernel
     738
     739        switch (mode) {
     740          case PM_SUBTRACTION_MODE_TARGET:
     741          case PM_SUBTRACTION_MODE_1:
     742            target = stamp->image2;
     743            convolutions = stamp->convolutions1;
     744            break;
     745          case PM_SUBTRACTION_MODE_2:
     746            target = stamp->image1;
     747            convolutions = stamp->convolutions2;
     748            break;
     749          default:
     750            psAbort("Unsupported subtraction mode: %x", mode);
     751        }
     752
    688753        psImage *polyValues = spatialPolyValues(spatialOrder, stamp->xNorm, stamp->yNorm); // Polynomial terms
    689754
     
    739804            // Vector and background term for matrix
    740805            double sumC = 0.0;      // Sum of the convolution
    741             double sumIC = 0.0;     // Sum of the convolution/input product
     806            double sumTC = 0.0;     // Sum of the convolution/target product
    742807            for (int y = - footprint; y <= footprint; y++) {
    743808                for (int x = - footprint; x <= footprint; x++) {
    744809                    double convProduct = jConv->kernel[y][x] / weight->kernel[y][x]; // Convolution / noise^2
    745810                    sumC += convProduct;
    746                     sumIC += input->kernel[y][x] * convProduct;
     811                    sumTC += target->kernel[y][x] * convProduct;
    747812                }
    748813            }
     
    752817                continue;
    753818            }
    754             if (!isfinite(sumIC)) {
     819            if (!isfinite(sumTC)) {
    755820                psStringAppend(&badString, "\nBad sumIC detected at %d", j);
    756821                bad = true;
     
    761826                for (int jxOrder = 0; jxOrder <= spatialOrder - jyOrder;
    762827                     jxOrder++, jIndex += numKernels) {
    763                     stampVector->data.F64[jIndex] = sumIC * polyValues->data.F64[jyOrder][jxOrder];
     828                    stampVector->data.F64[jIndex] = sumTC * polyValues->data.F64[jyOrder][jxOrder];
    764829
    765830                    double convPoly = sumC * polyValues->data.F64[jyOrder][jxOrder]; // Value
     
    774839        // Background only terms
    775840        double sum1 = 0.0;              // Sum of the weighting
    776         double sumI = 0.0;              // Sum of the input
     841        double sumT = 0.0;              // Sum of the target
    777842        for (int y = - footprint; y <= footprint; y++) {
    778843            for (int x = - footprint; x <= footprint; x++) {
    779844                double invNoise2 = 1.0 / weight->kernel[y][x]; // Inverse noise, squared
    780845                sum1 += invNoise2;
    781                 sumI += input->kernel[y][x] * invNoise2;
     846                sumT += target->kernel[y][x] * invNoise2;
    782847            }
    783848        }
     
    785850            psStringAppend(&badString, "\nBad sum1 detected");
    786851            bad = true;
    787         } else if (!isfinite(sumI)) {
     852        } else if (!isfinite(sumT)) {
    788853            psStringAppend(&badString, "\nBad sumI detected");
    789854            bad = true;
     
    791856
    792857        stampMatrix->data.F64[bgIndex][bgIndex] = sum1;
    793         stampVector->data.F64[bgIndex] = sumI;
     858        stampVector->data.F64[bgIndex] = sumT;
    794859
    795860        if (bad) {
     
    890955}
    891956
    892 
    893 int pmSubtractionRejectStamps(pmSubtractionStampList *stamps, psImage *subMask, const psVector *solution,
    894                               int footprint, float sigmaRej, const pmSubtractionKernels *kernels)
     957psVector *pmSubtractionCalculateDeviations(pmSubtractionStampList *stamps, const psVector *solution,
     958                                           int footprint, const pmSubtractionKernels *kernels,
     959                                           pmSubtractionMode mode)
     960{
     961    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, NULL);
     962    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
     963    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
     964    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
     965    PS_ASSERT_VECTOR_SIZE(solution, kernels->num * polyTerms(kernels->spatialOrder) +
     966                          polyTerms(kernels->bgOrder), NULL);
     967    PS_ASSERT_VECTOR_SIZE(kernels->u, kernels->num, NULL);
     968    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
     969
     970    psVector *deviations = psVectorAlloc(stamps->num, PS_TYPE_F32); // Mean deviation for stamps
     971    double devNorm = 1.0 / PS_SQR(2 * footprint + 1); // Normalisation for deviations
     972    int numKernels = kernels->num;      // Number of kernels
     973    int spatialOrder = kernels->spatialOrder; // Order of kernel spatial variations
     974    float background = solution->data.F64[solution->n-1]; // The difference in background
     975
     976    psVector *coeff = psVectorAlloc(numKernels, PS_TYPE_F64); // Coefficients for the kernel basis functions
     977
     978    for (int i = 0; i < stamps->num; i++) {
     979        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // The stamp of interest
     980        if (stamp->status != PM_SUBTRACTION_STAMP_USED) {
     981            deviations->data.F32[i] = NAN;
     982            continue;
     983        }
     984
     985        // Calculate coefficients of the kernel basis functions
     986        psImage *polyValues = spatialPolyValues(kernels->spatialOrder,
     987                                                stamp->xNorm, stamp->yNorm); // Polynomial terms
     988        for (int j = 0; j < numKernels; j++) {
     989            double polynomial = 0.0; // Value of the polynomial
     990            for (int yOrder = 0, index = j; yOrder <= spatialOrder; yOrder++) {
     991                for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
     992                    polynomial += solution->data.F64[index] * polyValues->data.F64[yOrder][xOrder];
     993                }
     994            }
     995            coeff->data.F64[j] = polynomial;
     996        }
     997        psFree(polyValues);
     998
     999        // Calculate residuals
     1000        psKernel *weight = stamp->weight; // Weight postage stamp
     1001        psKernel *target;               // Target postage stamp
     1002        psArray *convolutions;          // Convolution postage stamps for each kernel basis function
     1003        switch (mode) {
     1004          case PM_SUBTRACTION_MODE_TARGET:
     1005          case PM_SUBTRACTION_MODE_1:
     1006            target = stamp->image2;
     1007            convolutions = stamp->convolutions1;
     1008            break;
     1009          case PM_SUBTRACTION_MODE_2:
     1010            target = stamp->image1;
     1011            convolutions = stamp->convolutions2;
     1012            break;
     1013          default:
     1014            psAbort("Unsupported subtraction mode: %x", mode);
     1015        }
     1016
     1017#ifdef TESTING
     1018        psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint);
     1019#endif
     1020        float deviation = 0.0;      // Deviation for this stamp
     1021        for (int y = - footprint; y <= footprint; y++) {
     1022            for (int x = - footprint; x <= footprint; x++) {
     1023                float conv = background; // The value of the convolution
     1024                for (int j = 0; j < numKernels; j++) {
     1025                    psKernel *convolution = convolutions->data[j]; // Convolution
     1026                    conv += convolution->kernel[y][x] * coeff->data.F64[j];
     1027                }
     1028                float diff = target->kernel[y][x] - conv;
     1029                deviation += PS_SQR(diff) / weight->kernel[y][x];
     1030#ifdef TESTING
     1031                residual->kernel[y][x] = diff;
     1032#endif
     1033            }
     1034        }
     1035
     1036        deviations->data.F32[i] = sqrtf(devNorm * deviation);
     1037        psTrace("psModules.imcombine", 5, "Deviation for stamp %d (%d,%d): %f\n",
     1038                i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5), deviations->data.F32[i]);
     1039        if (!isfinite(deviations->data.F32[i])) {
     1040            stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
     1041            psTrace("psModules.imcombine", 5,
     1042                    "Rejecting stamp %d (%d,%d) because of non-finite deviation\n",
     1043                    i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
     1044            continue;
     1045        }
     1046
     1047#ifdef TESTING
     1048        {
     1049            psString filename = NULL;
     1050            psStringAppend(&filename, "resid_%03d.fits", i);
     1051            psFits *fits = psFitsOpen(filename, "w");
     1052            psFree(filename);
     1053            psFitsWriteImage(fits, NULL, residual->image, 0, NULL);
     1054            psFitsClose(fits);
     1055        }
     1056        psFree(residual);
     1057#endif
     1058
     1059    }
     1060    psFree(coeff);
     1061
     1062    return deviations;
     1063}
     1064
     1065
     1066int pmSubtractionRejectStamps(pmSubtractionStampList *stamps, const psVector *deviations,
     1067                              psImage *subMask, float sigmaRej, int footprint)
    8951068{
    8961069    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, -1);
     1070    PS_ASSERT_VECTOR_NON_NULL(deviations, -1);
     1071    PS_ASSERT_VECTOR_TYPE(deviations, PS_TYPE_F32, -1);
    8971072    PS_ASSERT_IMAGE_NON_EMPTY(subMask, -1);
    8981073    PS_ASSERT_IMAGE_TYPE(subMask, PS_TYPE_MASK, -1);
    899     PS_ASSERT_VECTOR_NON_NULL(solution, -1);
    900     PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, -1);
    901     PS_ASSERT_PTR_NON_NULL(kernels, -1);
    902     PS_ASSERT_VECTOR_SIZE(solution, kernels->num * polyTerms(kernels->spatialOrder) +
    903                           polyTerms(kernels->bgOrder), -1);
    904     PS_ASSERT_VECTOR_SIZE(kernels->u, kernels->num, -1);
    905     PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, -1);
    906 
    907     // Measure deviations
    908     psVector *deviations = psVectorAlloc(stamps->num, PS_TYPE_F32); // Mean deviation for stamps
     1074
    9091075    double totalSquareDev = 0.0;        // Total square deviation from zero
    9101076    int numStamps = 0;                  // Number of used stamps
    911     int numKernels = kernels->num;      // Number of kernels
    912     int spatialOrder = kernels->spatialOrder; // Order of kernel spatial variations
    913     double devNorm = 1.0 / PS_SQR(2 * footprint + 1); // Normalisation for deviations
    914     {
    915         float background = solution->data.F64[solution->n-1]; // The difference in background
    916 
    917         for (int i = 0; i < stamps->num; i++) {
    918             pmSubtractionStamp *stamp = stamps->stamps->data[i]; // The stamp of interest
    919             if (stamp->status != PM_SUBTRACTION_STAMP_USED) {
    920                 continue;
    921             }
    922 
    923             psImage *polyValues = spatialPolyValues(kernels->spatialOrder, stamp->xNorm,
    924                                                     stamp->yNorm); // Polynomial terms
    925 
    926             psKernel *input = stamp->input; // Input postage stamp
    927             psKernel *weight = stamp->weight; // Weight postage stamp
    928             psArray *convolutions = stamp->convolutions; // Convolutions of reference image for each kernel
    929 #ifdef TESTING
    930             psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint);
    931 #endif
    932             float deviation = 0.0;      // Deviation for this stamp
    933             for (int y = - footprint; y <= footprint; y++) {
    934                 for (int x = - footprint; x <= footprint; x++) {
    935                     float conv = background; // The value of the convolution
    936                     for (int j = 0; j < numKernels; j++) {
    937                         psKernel *convolution = convolutions->data[j]; // Convolution of reference
    938 
    939                         // XXX Precalculate these values, so that we're not calculating the same thing for
    940                         // every pixel.
    941                         double polynomial = 0.0; // Value of the polynomial
    942                         for (int yOrder = 0, index = j; yOrder <= spatialOrder; yOrder++) {
    943                             for (int xOrder = 0; xOrder <= spatialOrder - yOrder;
    944                                  xOrder++, index += numKernels) {
    945                                 polynomial += solution->data.F64[index] *
    946                                     polyValues->data.F64[yOrder][xOrder];
    947                             }
    948                         }
    949                         conv += convolution->kernel[y][x] * polynomial;
    950                     }
    951                     float diff = input->kernel[y][x] - conv;
    952                     deviation += PS_SQR(diff) / weight->kernel[y][x];
    953 #ifdef TESTING
    954                     residual->kernel[y][x] = diff;
    955 #endif
    956                 }
    957             }
    958             psFree(polyValues);
    959 
    960             deviations->data.F32[i] = devNorm * deviation;
    961             if (!isfinite(deviations->data.F32[i])) {
    962                 stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
    963                 psTrace("psModules.imcombine", 5,
    964                         "Rejecting stamp %d (%d,%d) because of non-finite deviation\n",
    965                         i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
    966                 continue;
    967             }
    968             psTrace("psModules.imcombine", 5, "Deviation for stamp %d (%d,%d): %f\n",
    969                     i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5), deviations->data.F32[i]);
    970             totalSquareDev += PS_SQR(deviations->data.F32[i]);
    971             numStamps++;
    972 
    973 #ifdef TESTING
    974             {
    975                 psString filename = NULL;
    976                 psStringAppend(&filename, "resid_%03d.fits", i);
    977                 psFits *fits = psFitsOpen(filename, "w");
    978                 psFree(filename);
    979                 psFitsWriteImage(fits, NULL, residual->image, 0, NULL);
    980                 psFitsClose(fits);
    981             }
    982             psFree(residual);
    983 #endif
    984 
    985         }
     1077    for (int i = 0; i < stamps->num; i++) {
     1078        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     1079        if (stamp->status != PM_SUBTRACTION_STAMP_USED) {
     1080            continue;
     1081        }
     1082        totalSquareDev += PS_SQR(deviations->data.F32[i]);
     1083        numStamps++;
    9861084    }
    9871085
    9881086    if (numStamps == 0) {
    9891087        psError(PS_ERR_UNKNOWN, true, "No good stamps found.");
    990         psFree(deviations);
    9911088        return -1;
    9921089    }
    9931090
    9941091    if (!isfinite(sigmaRej) || sigmaRej <= 0.0) {
     1092        // User just wanted to calculate and record the RMS for posterity
    9951093        psLogMsg("psModules.imcombine", PS_LOG_INFO, "RMS deviation: %f", sqrt(totalSquareDev / numStamps));
    996         psFree(deviations);
    9971094        return 0;
    9981095    }
     1096
     1097    float limit = sigmaRej * sqrt(totalSquareDev / (double)numStamps); // Limit on maximum deviation
     1098    psTrace("psModules.imcombine", 1, "Deviation limit: %f\n", limit);
    9991099
    10001100    int numRejected = 0;                // Number of stamps rejected
    10011101    int numGood = 0;                    // Number of good stamps
    10021102    double newSquareDev = 0.0;          // New square deviation
    1003 
    1004     float limit = sigmaRej * sqrt(totalSquareDev / numStamps); // Limit on maximum deviation
    1005     psTrace("psModules.imcombine", 1, "Deviation limit: %f\n", limit);
    1006 
    10071103    for (int i = 0; i < stamps->num; i++) {
    10081104        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     
    10241120                stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
    10251121                // Recalculate convolutions
    1026                 psFree(stamp->convolutions);
    1027                 stamp->convolutions = NULL;
     1122                psFree(stamp->convolutions1);
     1123                psFree(stamp->convolutions2);
     1124                stamp->convolutions1 = stamp->convolutions2 = NULL;
     1125                psFree(stamp->image1);
     1126                psFree(stamp->image2);
     1127                psFree(stamp->weight);
     1128                stamp->image1 = stamp->image2 = stamp->weight = NULL;
    10281129            } else {
    10291130                numGood++;
     
    10331134    }
    10341135
    1035     psLogMsg("psModules.imcombine", PS_LOG_INFO, "%d good stamps; %d rejected.\nRMS deviation: %f --> %f\n",
    1036              numGood, numRejected, sqrt(totalSquareDev / numStamps), sqrt(newSquareDev / numGood));
    1037 
    1038     psFree(deviations);
     1136    if (numRejected > 0) {
     1137        psLogMsg("psModules.imcombine", PS_LOG_INFO,
     1138                 "%d good stamps; %d rejected.\nRMS deviation: %f --> %f\n",
     1139                 numGood, numRejected, sqrt(totalSquareDev / numStamps),
     1140                 sqrt(newSquareDev / (double)numGood));
     1141    } else {
     1142        psLogMsg("psModules.imcombine", PS_LOG_INFO,
     1143                 "%d good stamps; 0 rejected.\nRMS deviation: %f\n",
     1144                 numGood, sqrt(totalSquareDev / numStamps));
     1145    }
    10391146
    10401147    return numRejected;
     
    10961203}
    10971204
    1098 bool pmSubtractionConvolve(psImage **outImage, psImage **outWeight, psImage **outMask,
    1099                            const psImage *inImage, const psImage *inWeight, const psImage *subMask,
    1100                            psMaskType blank, const psRegion *region, const psVector *solution,
    1101                            const pmSubtractionKernels *kernels, bool useFFT)
    1102 {
    1103     PS_ASSERT_IMAGE_NON_NULL(inImage, false);
    1104     PS_ASSERT_IMAGE_TYPE(inImage, PS_TYPE_F32, false);
     1205bool pmSubtractionConvolve(pmReadout *out, const pmReadout *ro1, const pmReadout *ro2,
     1206                           const psImage *subMask, psMaskType blank, const psRegion *region,
     1207                           const psVector *solution, const pmSubtractionKernels *kernels,
     1208                           pmSubtractionMode mode, bool useFFT)
     1209{
     1210    PS_ASSERT_PTR_NON_NULL(out, false);
     1211    PS_ASSERT_PTR_NON_NULL(ro1, false);
     1212    PS_ASSERT_IMAGE_NON_NULL(ro1->image, false);
     1213    PS_ASSERT_IMAGE_TYPE(ro1->image, PS_TYPE_F32, false);
     1214    if (ro1->weight) {
     1215        PS_ASSERT_IMAGE_NON_NULL(ro1->weight, false);
     1216        PS_ASSERT_IMAGE_TYPE(ro1->weight, PS_TYPE_F32, false);
     1217        PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->weight, ro1->image, false);
     1218    }
     1219    PS_ASSERT_PTR_NON_NULL(ro2, false);
     1220    PS_ASSERT_IMAGE_NON_NULL(ro2->image, false);
     1221    PS_ASSERT_IMAGE_TYPE(ro2->image, PS_TYPE_F32, false);
     1222    if (ro2->weight) {
     1223        PS_ASSERT_IMAGE_NON_NULL(ro2->weight, false);
     1224        PS_ASSERT_IMAGE_TYPE(ro2->weight, PS_TYPE_F32, false);
     1225        PS_ASSERT_IMAGES_SIZE_EQUAL(ro2->weight, ro1->image, false);
     1226    }
    11051227    if (subMask) {
    11061228        PS_ASSERT_IMAGE_NON_NULL(subMask, false);
    11071229        PS_ASSERT_IMAGE_TYPE(subMask, PS_TYPE_MASK, false);
    1108         PS_ASSERT_IMAGES_SIZE_EQUAL(subMask, inImage, false);
    1109     }
    1110     if (inWeight) {
    1111         PS_ASSERT_IMAGE_NON_NULL(inWeight, false);
    1112         PS_ASSERT_IMAGE_TYPE(inWeight, PS_TYPE_F32, false);
    1113         PS_ASSERT_IMAGES_SIZE_EQUAL(inWeight, inImage, false);
    1114     }
    1115     PS_ASSERT_PTR_NON_NULL(outImage, false);
    1116     if (*outImage) {
    1117         PS_ASSERT_IMAGE_NON_NULL(*outImage, false);
    1118         PS_ASSERT_IMAGES_SIZE_EQUAL(*outImage, inImage, false);
    1119         PS_ASSERT_IMAGE_TYPE(*outImage, PS_TYPE_F32, false);
    1120     }
    1121     if (outMask && *outMask) {
    1122         PS_ASSERT_IMAGE_NON_NULL(*outMask, false);
    1123         PS_ASSERT_IMAGES_SIZE_EQUAL(*outMask, inImage, false);
    1124         PS_ASSERT_IMAGE_TYPE(*outMask, PS_TYPE_MASK, false);
     1230        PS_ASSERT_IMAGES_SIZE_EQUAL(subMask, ro1->image, false);
     1231    }
     1232    if (out->image) {
     1233        PS_ASSERT_IMAGE_NON_NULL(out->image, false);
     1234        PS_ASSERT_IMAGES_SIZE_EQUAL(out->image, ro1->image, false);
     1235        PS_ASSERT_IMAGE_TYPE(out->image, PS_TYPE_F32, false);
     1236    }
     1237    if (out->mask) {
     1238        PS_ASSERT_IMAGE_NON_NULL(out->mask, false);
     1239        PS_ASSERT_IMAGES_SIZE_EQUAL(out->mask, ro1->image, false);
     1240        PS_ASSERT_IMAGE_TYPE(out->mask, PS_TYPE_MASK, false);
    11251241        PS_ASSERT_IMAGE_NON_NULL(subMask, false);
    11261242    }
    1127     if (outWeight && *outWeight) {
    1128         PS_ASSERT_IMAGE_NON_NULL(*outWeight, false);
    1129         PS_ASSERT_IMAGES_SIZE_EQUAL(*outWeight, inImage, false);
    1130         PS_ASSERT_IMAGE_TYPE(*outWeight, PS_TYPE_F32, false);
    1131         PS_ASSERT_IMAGE_NON_NULL(inWeight, false);
     1243    if (out->weight) {
     1244        PS_ASSERT_IMAGE_NON_NULL(out->weight, false);
     1245        PS_ASSERT_IMAGES_SIZE_EQUAL(out->weight, ro1->image, false);
     1246        PS_ASSERT_IMAGE_TYPE(out->weight, PS_TYPE_F32, false);
    11321247    }
    11331248    PS_ASSERT_VECTOR_NON_NULL(solution, false);
     
    11451260            return false;
    11461261        }
    1147         if (region->x0 < 0 || region->x1 > inImage->numCols ||
    1148             region->y0 < 0 || region->y1 > inImage->numRows) {
     1262        if (region->x0 < 0 || region->x1 > ro1->image->numCols ||
     1263            region->y0 < 0 || region->y1 > ro1->image->numRows) {
    11491264            psString string = psRegionToString(*region);
    11501265            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Input region (%s) does not fit in image (%dx%d)",
    1151                     string, inImage->numCols, inImage->numRows);
     1266                    string, ro1->image->numCols, ro1->image->numRows);
    11521267            psFree(string);
    11531268            return false;
     
    11551270    }
    11561271
     1272    psImage *image, *weight;            // Image and weight map to convolve
     1273    switch (mode) {
     1274      case PM_SUBTRACTION_MODE_TARGET:
     1275      case PM_SUBTRACTION_MODE_1:
     1276        image = ro1->image;
     1277        weight = ro1->weight;
     1278        break;
     1279      case PM_SUBTRACTION_MODE_2:
     1280        image = ro2->image;
     1281        weight = ro2->image;
     1282        break;
     1283      default:
     1284        psAbort("Unsupported subtraction mode: %x", mode);
     1285    }
     1286
    11571287    int numBackground = polyTerms(kernels->bgOrder); // Number of background terms
    11581288    float background = solution->data.F64[solution->n - numBackground]; // The difference in background
    1159     int numCols = inImage->numCols, numRows = inImage->numRows; // Image dimensions
    1160 
    1161     psImage *convImage = *outImage;    // Convolved image
     1289    int numCols = ro1->image->numCols, numRows = ro1->image->numRows; // Image dimensions
     1290
     1291    psImage *convImage = out->image;   // Convolved image
    11621292    if (!convImage) {
    1163         *outImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1164         convImage = *outImage;
     1293        out->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1294        convImage = out->image;
    11651295    }
    11661296    psImage *convMask = NULL;           // Convolved mask image
    1167     if (outMask && subMask) {
    1168         if (!*outMask) {
    1169             *outMask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
    1170         }
    1171         convMask = *outMask;
     1297    if (subMask) {
     1298        if (!out->mask) {
     1299            out->mask = psImageAlloc(numCols, numRows, PS_TYPE_MASK);
     1300        }
     1301        convMask = out->mask;
    11721302        psImageInit(convMask, 0);
    11731303    }
    11741304    psImage *convWeight = NULL;         // Convolved weight (variance) image
    1175     if (outWeight && inWeight) {
    1176         if (!*outWeight) {
    1177             *outWeight = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1178         }
    1179         convWeight = *outWeight;
     1305    if (weight) {
     1306        if (!out->weight) {
     1307            out->weight = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     1308        }
     1309        convWeight = out->weight;
    11801310        psImageInit(convWeight, 0.0);
    11811311    }
     
    11981328    }
    11991329
     1330    psMaskType maskSource, maskTarget;  // Mask values for source and target
     1331    switch (mode) {
     1332      case PM_SUBTRACTION_MODE_TARGET:
     1333      case PM_SUBTRACTION_MODE_1:
     1334        maskSource = PM_SUBTRACTION_MASK_BAD_1;
     1335        maskTarget = PM_SUBTRACTION_MASK_BAD_2 | PM_SUBTRACTION_MASK_CONVOLVE_1;
     1336        break;
     1337      case PM_SUBTRACTION_MODE_2:
     1338        maskSource = PM_SUBTRACTION_MASK_BAD_2;
     1339        maskTarget = PM_SUBTRACTION_MASK_BAD_1 | PM_SUBTRACTION_MASK_CONVOLVE_2;
     1340        break;
     1341      default:
     1342        psAbort("Unsupported subtraction mode: %x", mode);
     1343    }
     1344
    12001345    for (int j = yMin; j < yMax; j += fullSize) {
    12011346        int ySubMax = PS_MIN(j + fullSize, yMax); // Range for subregion of interest
     
    12111356
    12121357            kernelImage = solvedKernel(kernelImage, solution, kernels, polyValues);
    1213             if (inWeight) {
    1214                 kernelWeight = varianceKernel(kernelImage);
    1215             }
    1216 
    1217 
     1358            if (weight) {
     1359                kernelWeight = varianceKernel(kernelWeight, kernelImage);
     1360            }
     1361
     1362            psRegion subRegion = psRegionSet(i, xSubMax, j, ySubMax); // Sub-region to convolve
    12181363            if (useFFT) {
    12191364                // Use Fast Fourier Transform to do the convolution
    12201365                // This provides a big speed-up for large kernels
    1221                 convolveFFT(convImage, inImage, subMask, kernelImage, psRegionSet(i, xSubMax, j, ySubMax),
     1366                convolveFFT(convImage, image, subMask, maskSource, kernelImage, subRegion,
    12221367                            background, size);
    1223                 if (inWeight) {
    1224                     convolveFFT(convWeight, inWeight, subMask, kernelWeight,
    1225                                 psRegionSet(i, xSubMax, j, ySubMax), 0.0, size);
     1368                if (weight) {
     1369                    convolveFFT(convWeight, weight, subMask, maskSource, kernelWeight, subRegion,
     1370                                0.0, size);
    12261371                }
    12271372            } else {
    1228                 convolveDirect(convImage, inImage, kernelImage, psRegionSet(i, xSubMax, j, ySubMax),
    1229                                background, size);
    1230                 if (inWeight) {
    1231                     convolveDirect(convWeight, inWeight, kernelWeight, psRegionSet(i, xSubMax, j, ySubMax),
    1232                                    0.0, size);
     1373                convolveDirect(convImage, image, kernelImage, subRegion, background, size);
     1374                if (weight) {
     1375                    convolveDirect(convWeight, weight, kernelWeight, subRegion, 0.0, size);
    12331376                }
    12341377            }
    12351378
    12361379            // Propagate the mask
    1237             for (int y = j; y < ySubMax; y++) {
    1238                 for (int x = i; x < xSubMax; x++) {
    1239                     if (subMask && (subMask->data.PS_TYPE_MASK_DATA[y][x] &
    1240                                     (PM_SUBTRACTION_MASK_INPUT | PM_SUBTRACTION_MASK_CONVOLVE))) {
    1241                         convMask->data.PS_TYPE_MASK_DATA[y][x] |= blank;
    1242                         convImage->data.F32[y][x] = NAN;
    1243                         if (inWeight) {
    1244                             convWeight->data.F32[y][x] = NAN;
     1380            if (subMask) {
     1381                for (int y = j; y < ySubMax; y++) {
     1382                    for (int x = i; x < xSubMax; x++) {
     1383                        if (subMask->data.PS_TYPE_MASK_DATA[y][x] & maskTarget) {
     1384                            convMask->data.PS_TYPE_MASK_DATA[y][x] |= blank;
     1385                            convImage->data.F32[y][x] = NAN;
     1386                            if (weight) {
     1387                                convWeight->data.F32[y][x] = NAN;
     1388                            }
    12451389                        }
    12461390                    }
  • trunk/psModules/src/imcombine/pmSubtraction.h

    r15325 r15443  
    66 * @author GLG, MHPCC
    77 *
    8  * @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
    9  * @date $Date: 2007-10-17 02:45:40 $
     8 * @version $Revision: 1.20 $ $Name: not supported by cvs2svn $
     9 * @date $Date: 2007-11-03 02:28:24 $
    1010 *
    1111 * Copyright 2004-207 Institute for Astronomy, University of Hawaii
     
    1616
    1717#include <pslib.h>
    18 #include "pmSubtractionKernels.h"
    19 #include "pmSubtractionStamps.h"
     18
     19#include <pmHDU.h>
     20#include <pmFPA.h>
     21#include <pmSubtractionKernels.h>
     22#include <pmSubtractionStamps.h>
    2023
    2124/// @addtogroup imcombine Image Combinations
    2225/// @{
    2326
     27/// Mask values for the subtraction mask
    2428typedef enum {
    25     PM_SUBTRACTION_MASK_CLEAR     = 0x00, // No masking
    26     PM_SUBTRACTION_MASK_REF       = 0x01, // Reference image is bad
    27     PM_SUBTRACTION_MASK_INPUT     = 0x02, // Input image is bad
    28     PM_SUBTRACTION_MASK_CONVOLVE  = 0x04, // If convolved, would be bad
    29     PM_SUBTRACTION_MASK_FOOTPRINT = 0x08, // Bad pixel within the stamp footprint
    30     PM_SUBTRACTION_MASK_BORDER    = 0x10, // Image border
    31     PM_SUBTRACTION_MASK_REJ       = 0x20, // Previously tried as a stamp, and rejected
     29    PM_SUBTRACTION_MASK_CLEAR       = 0x00, // No masking
     30    PM_SUBTRACTION_MASK_BAD_1       = 0x01, // Image 1 is bad
     31    PM_SUBTRACTION_MASK_BAD_2       = 0x02, // Image 2 is bad
     32    PM_SUBTRACTION_MASK_CONVOLVE_1  = 0x04, // If image 1 is convolved, would be bad
     33    PM_SUBTRACTION_MASK_CONVOLVE_2  = 0x08, // If image 2 is convolved, would be bad
     34    PM_SUBTRACTION_MASK_FOOTPRINT_1 = 0x10, // Bad pixel within the stamp footprint of image 1
     35    PM_SUBTRACTION_MASK_FOOTPRINT_2 = 0x20, // Bad pixel within the stamp footprint of image 2
     36    PM_SUBTRACTION_MASK_BORDER      = 0x40, // Image border
     37    PM_SUBTRACTION_MASK_REJ         = 0x80, // Previously tried as a stamp, and rejected
    3238} pmSubtractionMasks;
    3339
     
    4551bool pmSubtractionConvolveStamp(pmSubtractionStamp *stamp, ///< Stamp to convolve
    4652                                const pmSubtractionKernels *kernels, ///< Kernel parameters
    47                                 int footprint ///< Half-size of region over which to calculate equation
     53                                int footprint, ///< Half-size of region over which to calculate equation
     54                                pmSubtractionMode mode ///< Mode for subtraction (which to convolve)
    4855    );
    4956
     
    5158bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, ///< Stamps
    5259                                    const pmSubtractionKernels *kernels, ///< Kernel parameters
    53                                     int footprint ///< Half-size of region over which to calculate equation
     60                                    int footprint, ///< Half-size of region over which to calculate equation
     61                                    pmSubtractionMode mode ///< Mode for subtraction (which to convolve)
    5462                                    );
    5563
     
    5967                                     );
    6068
     69/// Calculate deviations
     70psVector *pmSubtractionCalculateDeviations(pmSubtractionStampList *stamps, ///< Stamps
     71                                           const psVector *solution, ///< Solution vector
     72                                           int footprint, ///< Half-size of stamp
     73                                           const pmSubtractionKernels *kernels, ///< Kernel parameters
     74                                           pmSubtractionMode mode ///< Mode for subtraction
     75    );
     76
    6177/// Reject stamps
    6278int pmSubtractionRejectStamps(pmSubtractionStampList *stamps, ///< Stamps
     79                              const psVector *deviations, ///< Deviations for each stamp
    6380                              psImage *subMask, ///< Subtraction mask
    64                               const psVector *solution, ///< Solution vector
    65                               int footprint, ///< Region to mask if stamp is bad
    6681                              float sigmaRej, ///< Number of RMS deviations above zero at which to reject
    67                               const pmSubtractionKernels *kernels ///< Kernel parameters
     82                              int footprint ///< Half-size of stamp
    6883    );
    6984
     
    8196
    8297/// Convolve image in preparation for subtraction
    83 bool pmSubtractionConvolve(psImage **outImage, ///< Output image
    84                            psImage **outWeight, ///< Output weight map (or NULL)
    85                            psImage **outMask, ///< Output mask (or NULL)
    86                            const psImage *inImage, ///< Input image
    87                            const psImage *inWeight, ///< Input weight map (or NULL)
     98bool pmSubtractionConvolve(pmReadout *out, ///< Output image
     99                           const pmReadout *ro1, // Input image 1
     100                           const pmReadout *ro2, // Input image 2
    88101                           const psImage *subMask, ///< Subtraction mask (or NULL)
    89102                           psMaskType blank, ///< Mask value for blank regions
     
    91104                           const psVector *solution, ///< The solution vector
    92105                           const pmSubtractionKernels *kernels, ///< Kernel parameters
     106                           pmSubtractionMode mode, ///< Mode for subtraction
    93107                           bool useFFT  ///< Use Fast Fourier Transform for the convolution?
    94108    );
  • trunk/psModules/src/imcombine/pmSubtractionKernels.h

    r14671 r15443  
    1414    PM_SUBTRACTION_KERNEL_RINGS,        ///< Rings Instead of the Normal Gaussian Subtraction
    1515} pmSubtractionKernelsType;
     16
     17/// Modes --- specifies which image to convolve
     18typedef enum {
     19    PM_SUBTRACTION_MODE_ERR,            // Error in the mode
     20    PM_SUBTRACTION_MODE_TARGET,         // Convolve image 1 to match target PSF
     21    PM_SUBTRACTION_MODE_1,              // Convolve image 1
     22    PM_SUBTRACTION_MODE_2,              // Convolve image 2
     23    PM_SUBTRACTION_MODE_UNSURE,         // Not sure yet which image to convolve so try to satisfy both
     24    PM_SUBTRACTION_MODE_DUAL,           // Dual convolution
     25} pmSubtractionMode;
    1626
    1727/// Kernels specification
  • trunk/psModules/src/imcombine/pmSubtractionMatch.c

    r15329 r15443  
    5050
    5151static bool getStamps(pmSubtractionStampList **stamps, // Stamps to read
    52                       const pmReadout *reference, // Reference readout
    53                       const pmReadout *input, // Input readout, or NULL to generate fake stamps
     52                      const pmReadout *ro1, // Readout 1
     53                      const pmReadout *ro2, // Readout 2
    5454                      const psImage *subMask, // Mask for subtraction, or NULL
    5555                      psImage *weight,  // Weight map
     
    5757                      float threshold,  // Threshold for stamp finding
    5858                      float stampSpacing, // Spacing between stamps
    59                       float targetWidth,// Target width for fake stamps
    6059                      int size,         // Kernel half-size
    61                       int footprint     // Convolution footprint for stamps
     60                      int footprint,     // Convolution footprint for stamps
     61                      pmSubtractionMode mode // Mode for subtraction
    6262    )
    6363{
    6464    psTrace("psModules.imcombine", 3, "Finding stamps...\n");
    65     *stamps = pmSubtractionStampsFind(*stamps, reference->image, subMask, region, threshold, stampSpacing);
     65    *stamps = pmSubtractionStampsFind(*stamps, ro1->image, subMask, region, threshold, stampSpacing, mode);
    6666    if (!*stamps) {
    6767        psError(PS_ERR_UNKNOWN, false, "Unable to find stamps.");
     
    7171    memCheck("  find stamps");
    7272
    73     if (!input && !pmSubtractionStampsGenerate(*stamps, targetWidth, footprint, size)) {
    74         psError(PS_ERR_UNKNOWN, false, "Unable to generate target stamps.");
    75         return false;
    76     }
    77 
    78     memCheck("   generate stamps");
    79 
    8073    psTrace("psModules.imcombine", 3, "Extracting stamps...\n");
    81     if (!pmSubtractionStampsExtract(*stamps, reference->image, input ? input->image : NULL,
    82                                     weight, footprint, size)) {
     74    if (!pmSubtractionStampsExtract(*stamps, ro1->image, ro2 ? ro2->image : NULL, weight, footprint, size)) {
    8375        psError(PS_ERR_UNKNOWN, false, "Unable to extract stamps.");
    8476        return false;
     
    9486
    9587
    96 bool pmSubtractionMatch(pmReadout *convolved, const pmReadout *reference, const pmReadout *input,
     88bool pmSubtractionMatch(pmReadout *convolved, const pmReadout *ro1, const pmReadout *ro2,
    9789                        int footprint, float regionSize, float stampSpacing, float threshold,
    98                         const psArray *sources, const char *stampsName, float targetWidth,
     90                        const psArray *sources, const char *stampsName,
    9991                        pmSubtractionKernelsType type, int size, int spatialOrder,
    10092                        const psVector *isisWidths, const psVector *isisOrders,
    10193                        int inner, int ringsOrder, int binning, bool optimum, const psVector *optFWHMs,
    10294                        int optOrder, float optThreshold, int iter, float rej, psMaskType maskBad,
    103                         psMaskType maskBlank, float badFrac)
     95                        psMaskType maskBlank, float badFrac, pmSubtractionMode mode)
    10496{
    10597    PS_ASSERT_PTR_NON_NULL(convolved, false);
    106     PS_ASSERT_PTR_NON_NULL(reference, false);
    107     PS_ASSERT_IMAGE_NON_NULL(reference->image, false);
    108     PS_ASSERT_IMAGE_TYPE(reference->image, PS_TYPE_F32, false);
    109     if (reference->mask) {
    110         PS_ASSERT_IMAGE_NON_NULL(reference->mask, false);
    111         PS_ASSERT_IMAGE_TYPE(reference->mask, PS_TYPE_MASK, false);
    112         PS_ASSERT_IMAGES_SIZE_EQUAL(reference->mask, reference->image, false);
    113     }
    114     if (reference->weight) {
    115         PS_ASSERT_IMAGE_NON_NULL(reference->weight, false);
    116         PS_ASSERT_IMAGE_TYPE(reference->weight, PS_TYPE_F32, false);
    117         PS_ASSERT_IMAGES_SIZE_EQUAL(reference->weight, reference->image, false);
    118     }
    119     if (input) {
    120         PS_ASSERT_IMAGE_NON_NULL(input->image, false);
    121         PS_ASSERT_IMAGE_TYPE(input->image, PS_TYPE_F32, false);
    122         PS_ASSERT_IMAGES_SIZE_EQUAL(input->image, reference->image, false);
    123         if (input->mask) {
    124             PS_ASSERT_IMAGE_NON_NULL(input->mask, false);
    125             PS_ASSERT_IMAGE_TYPE(input->mask, PS_TYPE_MASK, false);
    126             PS_ASSERT_IMAGES_SIZE_EQUAL(input->mask, reference->image, false);
    127         }
    128         if (input->weight) {
    129             PS_ASSERT_IMAGE_NON_NULL(input->weight, false);
    130             PS_ASSERT_IMAGE_TYPE(input->weight, PS_TYPE_F32, false);
    131             PS_ASSERT_IMAGES_SIZE_EQUAL(input->weight, reference->image, false);
     98    PS_ASSERT_PTR_NON_NULL(ro1, false);
     99    PS_ASSERT_IMAGE_NON_NULL(ro1->image, false);
     100    PS_ASSERT_IMAGE_TYPE(ro1->image, PS_TYPE_F32, false);
     101    if (ro1->mask) {
     102        PS_ASSERT_IMAGE_NON_NULL(ro1->mask, false);
     103        PS_ASSERT_IMAGE_TYPE(ro1->mask, PS_TYPE_MASK, false);
     104        PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->mask, ro1->image, false);
     105    }
     106    if (ro1->weight) {
     107        PS_ASSERT_IMAGE_NON_NULL(ro1->weight, false);
     108        PS_ASSERT_IMAGE_TYPE(ro1->weight, PS_TYPE_F32, false);
     109        PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->weight, ro1->image, false);
     110    }
     111    if (ro2) {
     112        PS_ASSERT_IMAGE_NON_NULL(ro2->image, false);
     113        PS_ASSERT_IMAGE_TYPE(ro2->image, PS_TYPE_F32, false);
     114        PS_ASSERT_IMAGES_SIZE_EQUAL(ro2->image, ro1->image, false);
     115        if (ro2->mask) {
     116            PS_ASSERT_IMAGE_NON_NULL(ro2->mask, false);
     117            PS_ASSERT_IMAGE_TYPE(ro2->mask, PS_TYPE_MASK, false);
     118            PS_ASSERT_IMAGES_SIZE_EQUAL(ro2->mask, ro1->image, false);
     119        }
     120        if (ro2->weight) {
     121            PS_ASSERT_IMAGE_NON_NULL(ro2->weight, false);
     122            PS_ASSERT_IMAGE_TYPE(ro2->weight, PS_TYPE_F32, false);
     123            PS_ASSERT_IMAGES_SIZE_EQUAL(ro2->weight, ro1->image, false);
    132124        }
    133125    } else if (!stampsName && !sources) {
     
    144136    }
    145137    // stampsName may be anything
    146     // targetWidth can be just about anything (except maybe negative, but it can be NAN)
    147138    // We'll check kernel type when we allocate the kernels
    148139    PS_ASSERT_INT_POSITIVE(size, false);
     
    196187    }
    197188
    198     psImage *inImage = NULL, *inMask = NULL; // Input image, mask, weight
    199     if (input) {
    200         inImage = input->image;
    201         inMask = input->mask;
    202     }
    203 
    204189    // Where does our weight map come from?
    205190    psImage *weight = NULL;             // Weight image to use
    206     if (input && input->weight) {
    207         weight = input->weight;
    208     } else if (reference->weight) {
    209         weight = reference->weight;
    210     } else if (input) {
    211         weight = input->image;
     191    if (ro1->weight && ro2 && ro2->weight) {
     192        weight = (psImage*)psBinaryOp(NULL, ro1->weight, "+", ro2->weight);
     193    } else if (ro1->weight) {
     194        weight = psMemIncrRefCounter(ro1->weight);
     195    } else if (ro2) {
     196        if (ro2->weight) {
     197            weight = psMemIncrRefCounter(ro2->weight);
     198        } else {
     199            weight = (psImage*)psBinaryOp(NULL, ro1->image, "+", ro2->image);
     200        }
    212201    } else {
    213         weight = reference->image;
     202        weight = psMemIncrRefCounter(ro1->image);
    214203    }
    215204
     
    221210    psVector *solution = NULL;          // Solution to match PSF
    222211    pmSubtractionKernels *kernels = NULL; // Kernel basis functions
    223 
    224     int numCols = reference->image->numCols, numRows = reference->image->numRows; // Image dimensions
     212    int numCols = ro1->image->numCols, numRows = ro1->image->numRows; // Image dimensions
    225213
    226214    memCheck("start");
    227215
    228     subMask = pmSubtractionMask(reference->mask, inMask, maskBad, size, footprint, badFrac, useFFT);
     216    subMask = pmSubtractionMask(ro1->mask, ro2 ? ro2->mask : NULL, maskBad, size, footprint,
     217                                badFrac, useFFT);
    229218    if (!subMask) {
    230219        psError(PS_ERR_UNKNOWN, false, "Unable to generate subtraction mask.");
     220        psFree(weight);
    231221        return false;
    232222    }
     
    260250
    261251            if (sources) {
    262                 stamps = pmSubtractionStampsSetFromSources(sources, subMask, region, stampSpacing,
    263                                                            input ? 0 : 2 * footprint);
     252                stamps = pmSubtractionStampsSetFromSources(sources, subMask, region, stampSpacing, mode);
    264253            } else if (stampsName && strlen(stampsName) > 0) {
    265                 // Read stamps from file
    266                 psTrace("psModules.imcombine", 3, "Reading stamps from %s...\n", stampsName);
    267                 const char *stampFormat = input ? "%f %f" : "%f %f %f"; // Format for reading stamp file
    268                 psArray *stampsData = stampsData = psVectorsReadFromFile(stampsName, stampFormat);
    269                 psVector *xStamp = NULL, *yStamp = NULL, *fluxStamp = NULL; // Stamp positions and fluxes
    270                 if (!stampsData) {
    271                     psError(PS_ERR_IO, false, "Unable to read stamps file %s", stampsName);
    272                     goto ERROR;
    273                 }
    274                 xStamp = stampsData->data[0];
    275                 yStamp = stampsData->data[1];
    276                 if (!input) {
    277                     fluxStamp = stampsData->data[2];
    278                 }
    279 
    280                 // Correct for IRAF/FITS (unit-offset) positions to C (zero-offset) positions
    281                 psBinaryOp(xStamp, xStamp, "-", psScalarAlloc(1.0, PS_TYPE_F32));
    282                 psBinaryOp(yStamp, yStamp, "-", psScalarAlloc(1.0, PS_TYPE_F32));
    283 
    284                 stamps = pmSubtractionStampsSet(xStamp, yStamp, fluxStamp, reference->image, subMask,
    285                                                 region, stampSpacing, input ? 0 : footprint + size);
    286                 psFree(stampsData);
     254                stamps = pmSubtractionStampsSetFromFile(stampsName, subMask, region, stampSpacing, mode);
     255            }
     256
     257            // We get the stamps here; we will also attempt to get stamps at the first iteration, but it
     258            // doesn't matter.
     259            if (!getStamps(&stamps, ro1, ro2, subMask, weight, NULL, threshold, stampSpacing,
     260                           size, footprint, mode)) {
     261                goto MATCH_ERROR;
     262            }
     263
     264            if (mode == PM_SUBTRACTION_MODE_UNSURE || mode == PM_SUBTRACTION_MODE_TARGET) {
     265                pmSubtractionMode newMode = pmSubtractionOrder(stamps, footprint); // Subtraction mode
     266                switch (newMode) {
     267                  case PM_SUBTRACTION_MODE_1:
     268                    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolving image 1 to match image 2.");
     269                    break;
     270                  case PM_SUBTRACTION_MODE_2:
     271                    if (mode == PM_SUBTRACTION_MODE_TARGET) {
     272                        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
     273                                "Input PSF is larger than target PSF --- can't match image.");
     274                        goto MATCH_ERROR;
     275                    }
     276                    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolving image 2 to match image 1.");
     277                    break;
     278                  default:
     279                    psError(PS_ERR_UNKNOWN, false, "Unable to determine subtraction order.");
     280                    goto MATCH_ERROR;
     281                }
     282                mode = newMode;
    287283            }
    288284
    289285            // Define kernel basis functions
    290286            if (optimum && (type == PM_SUBTRACTION_KERNEL_ISIS || type == PM_SUBTRACTION_KERNEL_GUNK)) {
    291                 if (!getStamps(&stamps, reference, input, subMask, weight, NULL,
    292                                threshold, stampSpacing, targetWidth, size, footprint)) {
    293                     goto ERROR;
    294                 }
    295287                kernels = pmSubtractionKernelsOptimumISIS(type, size, inner, spatialOrder, optFWHMs, optOrder,
    296                                                           stamps, footprint, optThreshold);
     288                                                          stamps, footprint, optThreshold, mode);
    297289                if (!kernels) {
    298290                    psErrorClear();
     
    314306                psLogMsg("psModules.imcombine", PS_LOG_INFO, "Iteration %d.", k);
    315307
    316                 if (!getStamps(&stamps, reference, input, subMask, weight, region,
    317                                threshold, stampSpacing, targetWidth, size, footprint)) {
    318                     goto ERROR;
     308                if (!getStamps(&stamps, ro1, ro2, subMask, weight, region, threshold, stampSpacing,
     309                               size, footprint, mode)) {
     310                    goto MATCH_ERROR;
    319311                }
    320312
    321313                psTrace("psModules.imcombine", 3, "Calculating equation...\n");
    322                 if (!pmSubtractionCalculateEquation(stamps, kernels, footprint)) {
     314                if (!pmSubtractionCalculateEquation(stamps, kernels, footprint, mode)) {
    323315                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    324                     goto ERROR;
     316                    goto MATCH_ERROR;
    325317                }
    326318
     
    331323                if (!solution) {
    332324                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    333                     goto ERROR;
     325                    goto MATCH_ERROR;
    334326                }
    335327
    336328                memCheck("  solve equation");
    337329
     330                psVector *deviations = pmSubtractionCalculateDeviations(stamps, solution, footprint, kernels,
     331                                                                        mode); // Deviations for each stamp
     332                if (!deviations) {
     333                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate deviations.");
     334                    goto MATCH_ERROR;
     335                }
     336
     337                memCheck("   calculate deviations");
     338
    338339                psTrace("psModules.imcombine", 3, "Rejecting stamps...\n");
    339                 numRejected = pmSubtractionRejectStamps(stamps, subMask, solution, footprint, rej, kernels);
     340                numRejected = pmSubtractionRejectStamps(stamps, deviations, subMask, rej, footprint);
    340341                if (numRejected < 0) {
    341342                    psError(PS_ERR_UNKNOWN, false, "Unable to reject stamps.");
    342                     goto ERROR;
    343                 }
     343                    psFree(deviations);
     344                    goto MATCH_ERROR;
     345                }
     346                psFree(deviations);
     347
    344348                memCheck("  reject stamps");
    345349            }
     
    350354                if (!solution) {
    351355                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
    352                     goto ERROR;
    353                 }
    354                 (void)pmSubtractionRejectStamps(stamps, subMask, solution, footprint, NAN, kernels);
     356                    goto MATCH_ERROR;
     357                }
     358                psVector *deviations = pmSubtractionCalculateDeviations(stamps, solution, footprint, kernels,
     359                                                                        mode); // Deviations for each stamp
     360                if (!deviations) {
     361                    psError(PS_ERR_UNKNOWN, false, "Unable to calculate deviations.");
     362                    goto MATCH_ERROR;
     363                }
     364                (void)pmSubtractionRejectStamps(stamps, deviations, subMask, footprint, NAN);
     365                psFree(deviations);
    355366            }
    356367            psFree(stamps);
     
    372383                            psError(PS_ERR_UNKNOWN, false, "Unable to generate kernel image.");
    373384                            psFree(convKernels);
    374                             goto ERROR;
     385                            goto MATCH_ERROR;
    375386                        }
    376387
     
    380391                            psFree(kernel);
    381392                            psFree(convKernels);
    382                             goto ERROR;
     393                            goto MATCH_ERROR;
    383394                        }
    384395                        psFree(kernel);
     
    416427
    417428            psTrace("psModules.imcombine", 2, "Convolving...\n");
    418             if (!pmSubtractionConvolve(&convolved->image, &convolved->weight, &convolved->mask,
    419                                        reference->image, reference->weight, subMask, maskBlank, region,
    420                                        solution, kernels, useFFT)) {
    421                 psError(PS_ERR_UNKNOWN, false, "Unable to convolve reference image.");
    422                 goto ERROR;
     429            if (!pmSubtractionConvolve(convolved, ro1, ro2, subMask, maskBlank, region,
     430                                       solution, kernels, mode, useFFT)) {
     431                psError(PS_ERR_UNKNOWN, false, "Unable to convolve image.");
     432                goto MATCH_ERROR;
    423433            }
    424434            psFree(kernels);
     
    461471    psFree(subMask);
    462472    subMask = NULL;
     473    psFree(weight);
     474    weight = NULL;
    463475
    464476    if (!pmSubtractionBorder(convolved->image, convolved->weight, convolved->mask, size, maskBlank)) {
    465477        psError(PS_ERR_UNKNOWN, false, "Unable to set border of convolved image.");
    466         goto ERROR;
     478        goto MATCH_ERROR;
    467479    }
    468480
     
    472484    return true;
    473485
    474 ERROR:
     486MATCH_ERROR:
    475487    psFree(region);
    476488    psFree(regionString);
     
    479491    psFree(stamps);
    480492    psFree(solution);
     493    psFree(weight);
    481494    return false;
    482495}
     496
     497// Calculate the second order moments for an image
     498static float subtractionOrderMoment(const psKernel *kernel, // Image for which to measure moments
     499                                    int radius   // Maximum radius
     500                                    )
     501{
     502    assert(kernel && kernel->kernel);
     503
     504    int xMin = PS_MAX(kernel->xMin, -radius), xMax = PS_MIN(kernel->xMax, radius); // Bounds in x
     505    int yMin = PS_MAX(kernel->yMin, -radius), yMax = PS_MIN(kernel->yMax, radius); // Bounds in y
     506
     507    float xCentroid = 0.0, yCentroid = 0.0; // Centroid (first moment)
     508    float sum = 0.0;       // Sum (zero-th moment)
     509    for (int y = yMin; y <= yMax; y++) {
     510        for (int x = xMin; x <= xMax; x++) {
     511            xCentroid += kernel->kernel[y][x] * x;
     512            yCentroid += kernel->kernel[y][x] * y;
     513            sum += kernel->kernel[y][x];
     514        }
     515    }
     516    xCentroid /= sum;
     517    yCentroid /= sum;
     518
     519    float eta20 = 0.0, eta02 = 0.0;     // Second moments
     520    for (int y = yMin; y <= yMax; y++) {
     521        float yDiff = y - yCentroid;
     522        for (int x = xMin; x <= xMax; x++) {
     523            float xDiff = x - xCentroid;
     524            eta20 += PS_SQR(xDiff) * kernel->kernel[y][x];
     525            eta02 += PS_SQR(yDiff) * kernel->kernel[y][x];
     526        }
     527    }
     528
     529    // Normalise to calculate the scale-invariant
     530    float sum2 = PS_SQR(sum);
     531    eta20 /= sum2;
     532    eta02 /= sum2;
     533    // eta11 /= sum2;
     534
     535    return eta20 + eta02;
     536}
     537
     538#if 0
     539// Calculate the deviations for a particular subtraction order
     540static psVector *subtractionOrderDeviation(float *sumKernel, // Sum of the kernel
     541                                           pmSubtractionStampList *stamps, // Stamps to convolve
     542                                           const pmSubtractionKernels *kernels, // Kernel basis functions
     543                                           int footprint, // Stamp footprint
     544                                           pmSubtractionMode mode // Mode of subtraction
     545                                           )
     546{
     547    assert(stamps);
     548    assert(footprint >= 0);
     549    assert(mode == PM_SUBTRACTION_MODE_1 || mode == PM_SUBTRACTION_MODE_2);
     550
     551    if (!pmSubtractionCalculateEquation(stamps, kernels, footprint, mode)) {
     552        psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     553        return NULL;
     554    }
     555
     556    psVector *solution = pmSubtractionSolveEquation(NULL, stamps);
     557    if (!solution) {
     558        psError(PS_ERR_UNKNOWN, false, "Unable to calculate least-squares equation.");
     559        return NULL;
     560    }
     561
     562    if (sumKernel) {
     563        float sum = 0.0;                // Sum of the kernel
     564        psImage *image = pmSubtractionKernelImage(solution, kernels, 0.0, 0.0); // Image of kernel
     565        for (int y = 0; y < image->numRows; y++) {
     566            for (int x = 0; x < image->numCols; x++) {
     567                sum += image->data.F32[y][x];
     568            }
     569        }
     570        psFree(image);
     571        *sumKernel = sum;
     572    }
     573
     574    psVector *deviations = pmSubtractionCalculateDeviations(stamps, solution, footprint, kernels, mode);
     575    psFree(solution);
     576    if (!deviations) {
     577        psError(PS_ERR_UNKNOWN, false, "Unable to calculate deviations.");
     578        return NULL;
     579    }
     580
     581    return deviations;
     582}
     583#endif
     584
     585pmSubtractionMode pmSubtractionOrder(pmSubtractionStampList *stamps, int radius)
     586{
     587    PS_ASSERT_INT_POSITIVE(radius, PM_SUBTRACTION_MODE_ERR);
     588
     589    psVector *mask = psVectorAlloc(stamps->num, PS_TYPE_MASK); // Mask for stamps
     590    psVector *moments = psVectorAlloc(stamps->num, PS_TYPE_F32); // Moments
     591    for (int i = 0; i < stamps->num; i++) {
     592        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     593        if (stamp->status != PM_SUBTRACTION_STAMP_CALCULATE && stamp->status != PM_SUBTRACTION_STAMP_USED) {
     594            mask->data.PS_TYPE_MASK_DATA[i] = 0xff;
     595            continue;
     596        }
     597        mask->data.PS_TYPE_MASK_DATA[i] = 0;
     598        moments->data.F32[i] = subtractionOrderMoment(stamp->image1, radius) /
     599            subtractionOrderMoment(stamp->image2, radius);
     600    }
     601
     602    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
     603    if (!psVectorStats(stats, moments, NULL, mask, 0xff)) {
     604        psError(PS_ERR_UNKNOWN, false, "Unable to calculate statistics for moments ratio.");
     605        psFree(mask);
     606        psFree(moments);
     607        psFree(stats);
     608        return PM_SUBTRACTION_MODE_ERR;
     609    }
     610    psFree(moments);
     611    psFree(mask);
     612
     613    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Median ratio of second moments: %lf", stats->robustMedian);
     614    pmSubtractionMode mode = (stats->robustMedian <= 1.0 ? PM_SUBTRACTION_MODE_1 : PM_SUBTRACTION_MODE_2);
     615    psFree(stats);
     616
     617    return mode;
     618}
     619
     620
     621
  • trunk/psModules/src/imcombine/pmSubtractionMatch.h

    r15305 r15443  
    44#include <pslib.h>
    55
    6 #include "pmHDU.h"
    7 #include "pmFPA.h"
    8 #include "pmSubtractionKernels.h"
     6#include <pmHDU.h>
     7#include <pmFPA.h>
     8#include <pmSubtractionKernels.h>
     9#include <pmSubtractionStamps.h>
    910
    10 
     11/// Match two images
    1112bool pmSubtractionMatch(pmReadout *convolved, ///< Output convolved data
    12                         const pmReadout *reference, ///< Reference data
    13                         const pmReadout *input, ///< Input data
     13                        const pmReadout *ro1, ///< Image 1
     14                        const pmReadout *ro2, ///< Image 2
    1415                        // Stamp parameters
    1516                        int footprint,  ///< Stamp half-size
     
    1920                        const psArray *sources, ///< Sources for stamps
    2021                        const char *stampsName, ///< Filename for stamps
    21                         float targetWidth, ///< Width of PSF for simulated target
    2222                        // Kernel parameters
    2323                        pmSubtractionKernelsType type, ///< Kernel type
     
    3838                        psMaskType maskBad, ///< Value to mask
    3939                        psMaskType maskBlank, ///< Mask for blank region
    40                         float badFrac   ///< Maximum fraction of bad input pixels to accept
     40                        float badFrac,   ///< Maximum fraction of bad input pixels to accept
     41                        pmSubtractionMode mode ///< Mode of subtraction
    4142    );
    4243
     44/// Determine which image to convolve
     45pmSubtractionMode pmSubtractionOrder(pmSubtractionStampList *stamps, ///< Stamps that have been extracted
     46                                     int footprint ///< Stamp half-size
     47    );
     48
     49
    4350#endif
  • trunk/psModules/src/imcombine/pmSubtractionParams.c

    r15247 r15443  
    5050#endif
    5151
     52/// Select the appropriate convolution, given the kernel basis function and subtraction mode
     53static inline psKernel *selectConvolution(const pmSubtractionStamp *stamp, // Stamp
     54                                          int kernelIndex, // Index for kernel component
     55                                          pmSubtractionMode mode // Mode of subtraction
     56    )
     57{
     58    switch (mode) {
     59      case PM_SUBTRACTION_MODE_1:
     60        return stamp->convolutions1->data[kernelIndex];
     61      case PM_SUBTRACTION_MODE_2:
     62        return stamp->convolutions2->data[kernelIndex];
     63      default:
     64        psAbort("Unsupported subtraction mode: %x", mode);
     65    }
     66    return NULL;                        // Unreached
     67}
     68
    5269// Accumulate cross-term sums for a stamp
    5370static void accumulateCross(double *sumI, // Sum of I(x)/sigma(x)^2
     
    5572                            double *sumIC, // Sum of I(x)conv(x)/sigma(x)^2
    5673                            const pmSubtractionStamp *stamp, // Stamp with weight
    57                             const psKernel *input, // Input image, I(x)
     74                            const psKernel *target, // Target stamp
    5875                            int kernelIndex, // Index for kernel component
    59                             int footprint // Size of region of interest
     76                            int footprint, // Size of region of interest
     77                            pmSubtractionMode mode // Mode of subtraction
    6078    )
    6179{
    6280    psKernel *weight = stamp->weight;   // Weight, sigma(x)^2
    63     psKernel *convolution = stamp->convolutions->data[kernelIndex]; // Convolution of interest
     81    psKernel *convolution = selectConvolution(stamp, kernelIndex, mode); // Convolution of interest
    6482
    6583    for (int y = -footprint; y <= footprint; y++) {
    66         psF32 *in = &input->kernel[y][-footprint]; // Dereference input
     84        psF32 *in = &target->kernel[y][-footprint]; // Dereference input
    6785        psF32 *wt = &weight->kernel[y][-footprint]; // Dereference weight
    6886        psF32 *conv = &convolution->kernel[y][-footprint]; // Dereference convolution
     
    82100                                   const pmSubtractionStamp *stamp, // Stamp with input and weight
    83101                                   int kernelIndex, // Index for kernel component
    84                                    int footprint // Size of region of interest
     102                                   int footprint, // Size of region of interest
     103                                   pmSubtractionMode mode // Mode of subtraction
    85104    )
    86105{
    87106    psKernel *weight = stamp->weight;   // Weight, sigma(x)^2
    88     psKernel *convolution = stamp->convolutions->data[kernelIndex]; // Convolution of interest
     107    psKernel *convolution = selectConvolution(stamp, kernelIndex, mode); // Convolution of interest
    89108
    90109    for (int y = -footprint; y <= footprint; y++) {
     
    100119}
    101120
    102 static double accumulateChi2(psKernel *input, // Input stamp
     121static double accumulateChi2(const psKernel *target, // Target stamp
    103122                             pmSubtractionStamp *stamp, // Stamp with weight
    104123                             int kernelIndex, // Index for kernel component
    105124                             double coeff, // Coefficient of convolution
    106125                             double bg,  // Background term
    107                              int footprint // Size of region of interest
     126                             int footprint, // Size of region of interest
     127                             pmSubtractionMode mode // Mode of subtraction
    108128    )
    109129{
    110130    double chi2 = 0.0;
    111131    psKernel *weight = stamp->weight;   // Weight, sigma(x)^2
    112     psKernel *convolution = stamp->convolutions->data[kernelIndex]; // Convolution of interest
     132    psKernel *convolution = selectConvolution(stamp, kernelIndex, mode); // Convolution of interest
    113133
    114134    for (int y = -footprint; y <= footprint; y++) {
    115         psF32 *in = &input->kernel[y][-footprint]; // Dereference input
     135        psF32 *in = &target->kernel[y][-footprint]; // Dereference input
    116136        psF32 *wt = &weight->kernel[y][-footprint]; // Dereference weight
    117137        psF32 *conv = &convolution->kernel[y][-footprint]; // Dereference convolution
     
    125145
    126146// Return the initial value of chi^2
    127 static double initialChi2(psKernel *input, // Input stamp
     147static double initialChi2(const psKernel *target, // Target stamp
    128148                          const pmSubtractionStamp *stamp, // Stamp with weight
    129                           int footprint // Size of convolution
     149                          int footprint, // Size of convolution
     150                          pmSubtractionMode mode // Mode of subtraction
    130151    )
    131152{
    132153    psKernel *weight = stamp->weight;   // Weight map
    133     psKernel *reference = stamp->reference; // Reference stamp
     154    psKernel *source;                   // Source stamp
     155    switch (mode) {
     156      case PM_SUBTRACTION_MODE_1:
     157        source = stamp->image1;
     158        break;
     159      case PM_SUBTRACTION_MODE_2:
     160        source = stamp->image2;
     161        break;
     162      default:
     163        psAbort("Unsupported subtraction mode: %x", mode);
     164    }
    134165
    135166    double chi2 = 0.0;                  // Chi^2
    136167    for (int y = -footprint; y <= footprint; y++) {
    137         psF32 *in = &input->kernel[y][-footprint]; // Dereference input
     168        psF32 *in = &target->kernel[y][-footprint]; // Dereference input
    138169        psF32 *wt = &weight->kernel[y][-footprint]; // Dereference weight
    139         psF32 *ref = &reference->kernel[y][-footprint]; // Derference reference
     170        psF32 *ref = &source->kernel[y][-footprint]; // Derference reference
    140171        for (int x = -footprint; x <= footprint; x++, in++, wt++, ref++) {
    141172            float diff = *in - *ref;    // Temporary value
     
    148179
    149180// Subtract a convolution from the input
    150 static void subtractConvolution(psKernel *input, // Input stamp
     181static void subtractConvolution(psKernel *target, // Target stamp
    151182                                const pmSubtractionStamp *stamp, // Stamp with weight
    152183                                int kernelIndex, // Index for kernel component
    153184                                float coeff, // Coefficient of subtraction
    154185                                float bg, // Background term
    155                                 int footprint // Size of region of interest
    156     )
    157 {
    158     psKernel *convolution = stamp->convolutions->data[kernelIndex]; // Convolution of interest
    159 
     186                                int footprint, // Size of region of interest
     187                                pmSubtractionMode mode // Mode of subtraction
     188    )
     189{
     190    psKernel *convolution = selectConvolution(stamp, kernelIndex, mode); // Convolution of interest
    160191    for (int y = -footprint; y <= footprint; y++) {
    161         psF32 *in = &input->kernel[y][-footprint]; // Dereference input
     192        psF32 *in = &target->kernel[y][-footprint]; // Dereference input
    162193        psF32 *conv = &convolution->kernel[y][-footprint]; // Dereference convolution
    163194        for (int x = -footprint; x <= footprint; x++, in++, conv++) {
     
    173204                                                      int spatialOrder, const psVector *fwhms, int maxOrder,
    174205                                                      const pmSubtractionStampList *stamps, int footprint,
    175                                                       float tolerance)
     206                                                      float tolerance, pmSubtractionMode mode)
    176207{
    177208    if (type != PM_SUBTRACTION_KERNEL_ISIS && type != PM_SUBTRACTION_KERNEL_GUNK) {
     
    210241    // Need to save the stamp inputs --- we're changing the values!
    211242    int numStamps = stamps->num;        // Number of stamps
    212     psArray *inputs = psArrayAlloc(numStamps); // Deep copies of the inputs
     243    psArray *targets = psArrayAlloc(numStamps); // Deep copies of the targets
    213244    psVector *badStamps = psVectorAlloc(numStamps, PS_TYPE_U8); // Mark the bad stamps
    214245    psVectorInit(badStamps, 0);
     
    219250            continue;
    220251        }
    221         psKernel *input = stamp->input; // Input image of interest
    222         psImage *copy = psImageCopy(NULL, input->image, PS_TYPE_F32); // Copy of the image
    223         inputs->data[i] = psKernelAllocFromImage(copy, size + footprint, size + footprint);
     252        psKernel *target;               // Target image of interest
     253        switch (mode) {
     254          case PM_SUBTRACTION_MODE_1:
     255            target = stamp->image2;
     256            break;
     257          case PM_SUBTRACTION_MODE_2:
     258            target = stamp->image1;
     259            break;
     260          default:
     261            psAbort("Unsupported subtraction mode: %x", mode);
     262        }
     263        psImage *copy = psImageCopy(NULL, target->image, PS_TYPE_F32); // Copy of the image
     264        targets->data[i] = psKernelAllocFromImage(copy, size + footprint, size + footprint);
    224265        psFree(copy);                   // Drop reference
    225266    }
     
    238279            continue;
    239280        }
    240         if (!pmSubtractionConvolveStamp(stamp, kernels, footprint)) {
     281        if (!pmSubtractionConvolveStamp(stamp, kernels, footprint, mode)) {
    241282            psError(PS_ERR_UNKNOWN, false, "Unable to convolve stamp %d.", i);
    242             psFree(inputs);
     283            psFree(targets);
    243284            psFree(kernels);
    244285            psFree(badStamps);
     
    258299                    "Sum of 1/sigma^2 is non-finite for stamp %d (%d,%d)\n",
    259300                    i, (int)stamp->x, (int)stamp->y);
    260             psFree(inputs);
     301            psFree(targets);
    261302            psFree(kernels);
    262303            psFree(badStamps);
     
    265306
    266307        for (int j = 0; j < numKernels; j++) {
    267             accumulateConvolutions(&sumC->data.F64[j], &sumCC->data.F64[j], stamp, j, footprint);
    268         }
    269 
    270         lastChi2 += initialChi2(inputs->data[i], stamp, footprint);
     308            accumulateConvolutions(&sumC->data.F64[j], &sumCC->data.F64[j], stamp, j, footprint, mode);
     309        }
     310
     311        lastChi2 += initialChi2(targets->data[i], stamp, footprint, mode);
    271312        numPixels += PS_SQR(2 * footprint + 1);
    272313    }
     
    297338                }
    298339                pmSubtractionStamp *stamp = stamps->stamps->data[j]; // Stamp of interest
    299                 accumulateCross(&sumI, &sumII, &sumIC, stamp, inputs->data[j], i, footprint);
     340                accumulateCross(&sumI, &sumII, &sumIC, stamp, targets->data[j], i, footprint, mode);
    300341            }
    301342
     
    310351                }
    311352                pmSubtractionStamp *stamp = stamps->stamps->data[j]; // Stamp of interest
    312                 chi2 += accumulateChi2(inputs->data[j], stamp, i, coeff, bg, footprint);
     353                chi2 += accumulateChi2(targets->data[j], stamp, i, coeff, bg, footprint, mode);
    313354            }
    314355
     
    328369        if (bestIndex == -1) {
    329370            psError(PS_ERR_UNKNOWN, false, "Unable to find best kernel component in round %d.", iter);
    330             psFree(inputs);
     371            psFree(targets);
    331372            psFree(sumC);
    332373            psFree(sumCC);
     
    345386            }
    346387            pmSubtractionStamp *stamp = stamps->stamps->data[j]; // Stamp of interest
    347             subtractConvolution(inputs->data[j], stamp, bestIndex, bestCoeff, bestBG, footprint);
     388            subtractConvolution(targets->data[j], stamp, bestIndex, bestCoeff, bestBG, footprint, mode);
    348389        }
    349390
     
    361402        lastChi2 = bestChi2;
    362403    }
    363     psFree(inputs);
     404    psFree(targets);
    364405    psFree(sumC);
    365406    psFree(sumCC);
     
    401442                pmSubtractionStamp *stamp = stamps->stamps->data[j]; // Stamp of interest
    402443                psArray *convolutions = convNew->data[j]; // Convolutions for this stamp
    403                 convolutions->data[rank] = psMemIncrRefCounter(stamp->convolutions->data[i]);
     444                convolutions->data[rank] = psMemIncrRefCounter(selectConvolution(stamp, i, mode));
    404445            }
    405446        }
     
    420461        }
    421462        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    422         psFree(stamp->convolutions);
    423         stamp->convolutions = convNew->data[i];
     463        psFree(stamp->convolutions1);
     464        psFree(stamp->convolutions2);
     465        switch (mode) {
     466          case PM_SUBTRACTION_MODE_1:
     467            stamp->convolutions1 = convNew->data[i];
     468            stamp->convolutions2 = NULL;
     469            break;
     470          case PM_SUBTRACTION_MODE_2:
     471            stamp->convolutions1 = NULL;
     472            stamp->convolutions2 = convNew->data[i];
     473            break;
     474          default:
     475            psAbort("Unsupported subtraction mode: %x", mode);
     476        }
    424477    }
    425478
  • trunk/psModules/src/imcombine/pmSubtractionParams.h

    r14804 r15443  
    1515                                                      const pmSubtractionStampList *stamps, ///< Stamps
    1616                                                      int footprint, ///< Convolution footprint for stamps
    17                                                       float tolerance ///< Maximum difference in chi^2
     17                                                      float tolerance, ///< Maximum difference in chi^2
     18                                                      pmSubtractionMode mode // Mode for subtraction
    1819    );
    1920
  • trunk/psModules/src/imcombine/pmSubtractionStamps.c

    r15286 r15443  
    44
    55#include <stdio.h>
     6#include <string.h>
    67#include <pslib.h>
    78
     
    4546    psFree(stamp->matrix);
    4647    psFree(stamp->vector);
    47     psFree(stamp->reference);
    48     psFree(stamp->input);
     48    psFree(stamp->image1);
     49    psFree(stamp->image2);
    4950    psFree(stamp->weight);
    50     psFree(stamp->convolutions);
     51    psFree(stamp->convolutions1);
     52    psFree(stamp->convolutions2);
    5153}
    5254
     
    6567// Is this position unmasked?
    6668static bool checkStampMask(int x, int y, // Coordinates of stamp
    67                            const psImage *mask
     69                           const psImage *mask, // Mask
     70                           pmSubtractionMode mode // Mode for subtraction
    6871                           )
    6972{
     
    7477        return false;
    7578    }
    76     return (mask->data.PS_TYPE_MASK_DATA[y][x] & (PM_SUBTRACTION_MASK_BORDER |
    77                                                   PM_SUBTRACTION_MASK_FOOTPRINT | PM_SUBTRACTION_MASK_REJ)) ?
    78         false : true;
     79
     80    psMaskType maskVal = PM_SUBTRACTION_MASK_BORDER | PM_SUBTRACTION_MASK_REJ; // Mask value
     81    switch (mode) {
     82      case PM_SUBTRACTION_MODE_1:
     83      case PM_SUBTRACTION_MODE_TARGET:
     84        maskVal |= PM_SUBTRACTION_MASK_FOOTPRINT_1;
     85        break;
     86      case PM_SUBTRACTION_MODE_2:
     87        maskVal |= PM_SUBTRACTION_MASK_FOOTPRINT_2;
     88        break;
     89      case PM_SUBTRACTION_MODE_UNSURE:
     90      case PM_SUBTRACTION_MODE_DUAL:
     91        maskVal |= PM_SUBTRACTION_MASK_FOOTPRINT_1 | PM_SUBTRACTION_MASK_FOOTPRINT_2;
     92        break;
     93      default:
     94        psAbort("Unsupported subtraction mode: %x", mode);
     95    }
     96
     97    return (mask->data.PS_TYPE_MASK_DATA[y][x] & maskVal) ? false : true;
    7998}
    8099
     
    140159    stamp->status = PM_SUBTRACTION_STAMP_INIT;
    141160
    142     stamp->reference = NULL;
    143     stamp->input = NULL;
     161    stamp->image1 = NULL;
     162    stamp->image2 = NULL;
    144163    stamp->weight = NULL;
    145     stamp->convolutions = NULL;
     164    stamp->convolutions1 = NULL;
     165    stamp->convolutions2 = NULL;
    146166
    147167    return stamp;
     
    151171pmSubtractionStampList *pmSubtractionStampsFind(pmSubtractionStampList *stamps, const psImage *image,
    152172                                                const psImage *subMask, const psRegion *region,
    153                                                 float threshold, float spacing)
     173                                                float threshold, float spacing, pmSubtractionMode mode)
    154174{
    155175    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
     
    226246            for (int y = subRegion->y0; y <= subRegion->y1 ; y++) {
    227247                for (int x = subRegion->x0; x <= subRegion->y1 ; x++) {
    228                     if (checkStampMask(x, y, subMask) && image->data.F32[y][x] > fluxStamp) {
     248                    if (checkStampMask(x, y, subMask, mode) && image->data.F32[y][x] > fluxStamp) {
    229249                        fluxStamp = image->data.F32[y][x];
    230250                        xStamp = x;
     
    242262
    243263            // Reset the postage stamps since we're making a new stamp
    244             psFree(stamp->reference);
    245             psFree(stamp->input);
     264            psFree(stamp->image1);
     265            psFree(stamp->image2);
    246266            psFree(stamp->weight);
    247             stamp->reference = stamp->input = stamp->weight = NULL;
    248 
    249             stamp->status = PM_SUBTRACTION_STAMP_CALCULATE;
     267            psFree(stamp->convolutions1);
     268            psFree(stamp->convolutions2);
     269            stamp->image1 = stamp->image2 = stamp->weight = NULL;
     270            stamp->convolutions1 = stamp->convolutions2 = NULL;
     271
     272            stamp->status = PM_SUBTRACTION_STAMP_FOUND;
    250273            numFound++;
    251274            psTrace("psModules.imcombine", 5, "Found stamp in subregion %d: %d,%d\n",
     
    266289pmSubtractionStampList *pmSubtractionStampsSet(const psVector *x, const psVector *y, const psVector *flux,
    267290                                               const psImage *image, const psImage *subMask,
    268                                                const psRegion *region, float spacing, int exclusionZone)
     291                                               const psRegion *region, float spacing, pmSubtractionMode mode)
     292
    269293{
    270294    PS_ASSERT_VECTOR_NON_NULL(x, NULL);
     
    289313    }
    290314    PS_ASSERT_FLOAT_LARGER_THAN(spacing, 0.0, NULL);
    291     PS_ASSERT_INT_NONNEGATIVE(exclusionZone, NULL);
    292315
    293316    int numStars = x->n;                // Number of stars
    294     psVector *exclude = psVectorAlloc(numStars, PS_TYPE_U8); // Exclude a star?
    295     psVectorInit(exclude, 0);
    296 
    297     // Apply exclusion zone around each star --- important when we're convolving to a specified PSF; less so
    298     // when we're trying to get a good subtraction.
    299     if (exclusionZone > 0) {
    300         psTrace("psModules.imcombine", 2, "Applying exclusion zone of %d pixels for stamps\n", exclusionZone);
    301         // We use something resembling a binary search --- coordinates are sorted in the x dimension, and then
    302         // to exclude stars within a nominated distance, we need only examine (i.e., calculate the
    303         // 2-dimensional distance to) other stars in the list that are within that distance of the x
    304         // coordinate of the star of interest.  By marking both stars when we find a violation of the
    305         // exclusion zone, we need only search upwards from the x coordinate of the star of interest.
    306 
    307         int minDistance2 = PS_SQR(exclusionZone); // Minimum square distance for other stars
    308         psVector *indexes = psVectorSortIndex(NULL, x); // Indices for sorting in x
    309         for (int i = 0; i < numStars - 1; i++) {
    310             int iIndex = indexes->data.S32[i]; // Index for star i
    311             if (exclude->data.U8[iIndex]) {
    312                 continue;
    313             }
    314             float ix = x->data.F32[iIndex], iy = y->data.F32[iIndex]; // Coordinates for star i
    315             float jx, jy;                   // Coordinates for star j
    316             for (int j = i + 1, jIndex = indexes->data.S32[j];
    317                  j < numStars && (jx = x->data.F32[jIndex]) < ix + exclusionZone;
    318                  j++, jIndex = indexes->data.S32[j]) {
    319                 jy = y->data.F32[jIndex];
    320                 if (PS_SQR(ix - jx) + PS_SQR(iy - jy) < minDistance2) {
    321                     exclude->data.U8[iIndex] = 0xff;
    322                     exclude->data.U8[jIndex] = 0xff;
    323                     psTrace("psModules.imcombine", 5, "Excluding stamps %d,%d and %d,%d\n",
    324                             (int)x->data.F32[iIndex], (int)y->data.F32[iIndex],
    325                             (int)x->data.F32[jIndex], (int)y->data.F32[jIndex]);
    326                     // Would 'break' here, but there might be more stamps within the exclusion zone.
    327                 }
    328             }
    329         }
    330         psFree(indexes);
    331     }
    332 
    333317    pmSubtractionStampList *stamps = pmSubtractionStampListAlloc(subMask->numCols, subMask->numRows,
    334318                                                                 region, spacing); // Stamp list
     
    347331    // Put the stars into their appropriate subregions
    348332    for (int i = 0; i < numStars; i++) {
    349         if (exclude->data.U8[i]) {
    350             // Star has been excluded
    351             continue;
    352         }
    353333        float xStamp = x->data.F32[i], yStamp = y->data.F32[i]; // Coordinates of stamp
    354334        int xPix = xStamp + 0.5, yPix = yStamp + 0.5; // Pixel coordinate of stamp
     
    357337            continue;
    358338        }
    359         if (!checkStampMask(xPix, yPix, subMask)) {
     339        if (!checkStampMask(xPix, yPix, subMask, mode)) {
    360340            // Not a good stamp
    361341            continue;
     
    386366        }
    387367    }
    388     psFree(exclude);
    389368
    390369    // Sort the list by flux, with the brightest last
     
    421400
    422401
    423 bool pmSubtractionStampsExtract(pmSubtractionStampList *stamps, psImage *reference, psImage *input,
     402bool pmSubtractionStampsExtract(pmSubtractionStampList *stamps, psImage *image1, psImage *image2,
    424403                                psImage *weight, int footprint, int kernelSize)
    425404{
    426405    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
    427     PS_ASSERT_IMAGE_NON_NULL(reference, false);
    428     PS_ASSERT_IMAGE_TYPE(reference, PS_TYPE_F32, false);
    429     if (input) {
    430         PS_ASSERT_IMAGE_NON_NULL(input, false);
    431         PS_ASSERT_IMAGES_SIZE_EQUAL(input, reference, false);
    432         PS_ASSERT_IMAGE_TYPE(input, PS_TYPE_F32, false);
    433     }
    434     if (weight) {
    435         PS_ASSERT_IMAGE_NON_NULL(weight, false);
    436         PS_ASSERT_IMAGES_SIZE_EQUAL(weight, reference, false);
    437         PS_ASSERT_IMAGE_TYPE(weight, PS_TYPE_F32, false);
    438     }
     406    PS_ASSERT_IMAGE_NON_NULL(image1, false);
     407    PS_ASSERT_IMAGE_TYPE(image1, PS_TYPE_F32, false);
     408    if (image2) {
     409        PS_ASSERT_IMAGE_NON_NULL(image2, false);
     410        PS_ASSERT_IMAGES_SIZE_EQUAL(image2, image1, false);
     411        PS_ASSERT_IMAGE_TYPE(image2, PS_TYPE_F32, false);
     412    }
     413    PS_ASSERT_IMAGE_NON_NULL(weight, false);
     414    PS_ASSERT_IMAGES_SIZE_EQUAL(weight, image1, false);
     415    PS_ASSERT_IMAGE_TYPE(weight, PS_TYPE_F32, false);
    439416    PS_ASSERT_INT_NONNEGATIVE(footprint, false);
    440417    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
    441418
    442     int numCols = reference->numCols, numRows = reference->numRows; // Size of images
     419    int numCols = image1->numCols, numRows = image1->numRows; // Size of images
    443420    int size = kernelSize + footprint; // Size of postage stamps
    444 
    445     if (!weight) {
    446         // Use the input (or if I must, the reference) as a rough approximation to the variance map, and HOPE
    447         // that it's positive.
    448         weight = input ? input : reference;
    449     }
    450421
    451422    for (int i = 0; i < stamps->num; i++) {
    452423        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    453         if (!stamp || stamp->status != PM_SUBTRACTION_STAMP_CALCULATE) {
     424        if (!stamp || stamp->status != PM_SUBTRACTION_STAMP_FOUND) {
    454425            continue;
    455426        }
     
    468439        }
    469440
     441        // Catch memory leaks --- these should have been freed and NULLed before
     442        assert(stamp->image1 == NULL);
     443        assert(stamp->image2 == NULL);
     444        assert(stamp->weight == NULL);
     445
    470446        psRegion region = psRegionSet(x - size, x + size + 1, y - size, y + size + 1); // Region of interest
    471447
    472         psImage *refSub = psImageSubset(reference, region); // Subimage with stamp
    473         stamp->reference = psKernelAllocFromImage(refSub, size, size);
    474         psFree(refSub);                 // Drop reference
    475 
    476         if (input) {
    477             psImage *inSub = psImageSubset(input, region); // Subimage with stamp
    478             stamp->input = psKernelAllocFromImage(inSub, size, size);
    479             psFree(inSub);              // Drop reference
     448        psImage *sub1 = psImageSubset(image1, region); // Subimage with stamp
     449        stamp->image1 = psKernelAllocFromImage(sub1, size, size);
     450        psFree(sub1);                   // Drop reference
     451
     452        if (image2) {
     453            psImage *sub2 = psImageSubset(image2, region); // Subimage with stamp
     454            stamp->image2 = psKernelAllocFromImage(sub2, size, size);
     455            psFree(sub2);               // Drop reference
    480456        }
    481457
    482458        psImage *wtSub = psImageSubset(weight, region); // Subimage with stamp
    483459        stamp->weight = psKernelAllocFromImage(wtSub, size, size);
    484         psFree(wtSub);                 // Drop reference
     460        psFree(wtSub);                  // Drop reference
     461
     462        stamp->status = PM_SUBTRACTION_STAMP_CALCULATE;
    485463    }
    486464
     
    488466}
    489467
    490 
     468#if 0
    491469bool pmSubtractionStampsGenerate(pmSubtractionStampList *stamps, float fwhm, int footprint, int kernelSize)
    492470{
     
    517495        float yStamp = y - (int)(y + 0.5); // y coordinate of star in stamp frame
    518496
    519         psFree(stamp->input);
    520         stamp->input = psKernelAlloc(-size, size, -size, size);
    521         psKernel *input = stamp->input; // Target stamp
     497        psFree(stamp->image2);
     498        stamp->image2 = psKernelAlloc(-size, size, -size, size);
     499        psKernel *target = stamp->image2; // Target stamp
    522500
    523501        // Put in a Waussian, just for fun!
     
    525503            for (int u = -size; u <= size; u++) {
    526504                float z = (PS_SQR(u + xStamp) + PS_SQR(v + yStamp)) / (2.0 * PS_SQR(sigma));
    527                 input->kernel[v][u] = flux / sigma * 0.5 * M_2_SQRTPI * M_SQRT1_2 / (1.0 + z + PS_SQR(z));
     505                target->kernel[v][u] = flux / sigma * 0.5 * M_2_SQRTPI * M_SQRT1_2 / (1.0 + z + PS_SQR(z));
    528506            }
    529507        }
     
    533511    return true;
    534512}
    535 
     513#endif
    536514
    537515pmSubtractionStampList *pmSubtractionStampsSetFromSources(const psArray *sources, const psImage *subMask,
    538516                                                          const psRegion *region, float spacing,
    539                                                           int exclusionZone)
     517                                                          pmSubtractionMode mode)
    540518{
    541519    PS_ASSERT_ARRAY_NON_NULL(sources, NULL);
     
    561539
    562540    pmSubtractionStampList *stamps = pmSubtractionStampsSet(x, y, flux, NULL, subMask, region,
    563                                                             spacing, exclusionZone); // Stamps to return
     541                                                            spacing, mode); // Stamps to return
    564542    psFree(x);
    565543    psFree(y);
     
    572550    return stamps;
    573551}
     552
     553
     554pmSubtractionStampList *pmSubtractionStampsSetFromFile(const char *filename, const psImage *subMask,
     555                                                       const psRegion *region, float spacing,
     556                                                       pmSubtractionMode mode)
     557{
     558    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
     559    // Let pmSubtractionStampsSet take care of the rest of the assertions
     560
     561    const char *format = (mode == PM_SUBTRACTION_MODE_TARGET ? "%f %f %f" : "%f %f"); // Format of file
     562    psArray *data = psVectorsReadFromFile(filename, format);
     563    if (!data) {
     564        psError(PS_ERR_IO, false, "Unable to read stamps file %s", filename);
     565        return NULL;
     566    }
     567    psVector *x = data->data[0], *y = data->data[1]; // Stamp positions
     568    psVector *flux = (mode == PM_SUBTRACTION_MODE_TARGET ? data->data[2] : NULL); // Stamp fluxes
     569
     570    // Correct for IRAF/FITS (unit-offset) positions to C (zero-offset) positions
     571    psBinaryOp(x, x, "-", psScalarAlloc(1.0, PS_TYPE_F32));
     572    psBinaryOp(y, y, "-", psScalarAlloc(1.0, PS_TYPE_F32));
     573
     574    pmSubtractionStampList *stamps = pmSubtractionStampsSet(x, y, flux, NULL, subMask, region, spacing,
     575                                                            mode);
     576    psFree(data);
     577
     578    return stamps;
     579
     580}
  • trunk/psModules/src/imcombine/pmSubtractionStamps.h

    r14802 r15443  
    99typedef enum {
    1010    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
    1113    PM_SUBTRACTION_STAMP_USED,          ///< Use this stamp
    1214    PM_SUBTRACTION_STAMP_REJECTED,      ///< This stamp has been rejected
    13     PM_SUBTRACTION_STAMP_CALCULATE,     ///< Calculate matrix and vector values for this stamp
    1415    PM_SUBTRACTION_STAMP_NONE           ///< No stamp in this region
    1516} pmSubtractionStampStatus;
     
    5455    float flux;                         ///< Flux
    5556    float xNorm, yNorm;                 ///< Normalised position
    56     psKernel *reference;                ///< Reference image postage stamp
    57     psKernel *input;                    ///< Input image postage stamp
     57    psKernel *image1;                   ///< Reference image postage stamp
     58    psKernel *image2;                   ///< Input image postage stamp
    5859    psKernel *weight;                   ///< Weight image postage stamp
    59     psArray *convolutions;              ///< Convolutions of the reference for each kernel component
     60    psArray *convolutions1;             ///< Convolutions of image 1 for each kernel component
     61    psArray *convolutions2;             ///< Convolutions of image 2 for each kernel component
    6062    psImage *matrix;                    ///< Associated matrix
    6163    psVector *vector;                   ///< Assoicated vector
     
    7274                                                const psRegion *region, ///< Region to search, or NULL
    7375                                                float threshold, ///< Threshold for stamps in the image
    74                                                 float spacing ///< Rough spacing for stamps
     76                                                float spacing, ///< Rough spacing for stamps
     77                                                pmSubtractionMode mode ///< Mode for subtraction
    7578    );
    7679
    7780/// Set stamps based on a list of x,y
    78 ///
    79 /// We may optionally apply an exclusion zone around each star --- this is important when we're convolving to
    80 /// a specified PSF; less so when we're only trying to get a good subtraction.
    8181pmSubtractionStampList *pmSubtractionStampsSet(const psVector *x, ///< x coordinates for each stamp
    8282                                               const psVector *y, ///< y coordinates for each stamp
     
    8686                                               const psRegion *region, ///< Region to search, or NULL
    8787                                               float spacing, ///< Rough spacing for stamps
    88                                                int exclusionZone ///< Exclusion zone around each star
     88                                               pmSubtractionMode mode ///< Mode for subtraction
    8989    );
    9090
    91 ///
     91/// Set stamps based on a list of sources
    9292pmSubtractionStampList *pmSubtractionStampsSetFromSources(
    93     const psArray *sources, ///< Sources for each stamp
    94     const psImage *subMask, ///< Mask, or NULL
    95     const psRegion *region, ///< Region to search, or NULL
    96     float spacing, ///< Rough spacing for stamps
    97     int exclusionZone ///< Exclusion zone around each star
     93    const psArray *sources,             ///< Sources for each stamp
     94    const psImage *subMask,             ///< Mask, or NULL
     95    const psRegion *region,             ///< Region to search, or NULL
     96    float spacing,                      ///< Rough spacing for stamps
     97    pmSubtractionMode mode              ///< Mode for subtraction
     98    );
     99
     100/// Set stamps based on values in a file
     101pmSubtractionStampList *pmSubtractionStampsSetFromFile(
     102    const char *filename,               ///< Filename of file containing x,y (or x,y,flux) on each line
     103    const psImage *subMask,             ///< Mask, or NULL
     104    const psRegion *region,             ///< Region to search, or NULL
     105    float spacing,                      ///< Rough spacing for stamps
     106    pmSubtractionMode mode              ///< Mode for subtraction
    98107    );
    99108
     
    107116    );
    108117
    109 /// Generate target for stamps based on list
    110 bool pmSubtractionStampsGenerate(pmSubtractionStampList *stamps, ///< Stamps
    111                                  float fwhm, ///< FWHM for each stamp
    112                                  int footprint, ///< Stamp footprint size
    113                                  int size ///< Kernel half-size
    114     );
    115 
    116118#endif
Note: See TracChangeset for help on using the changeset viewer.