IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

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).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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                    }
Note: See TracChangeset for help on using the changeset viewer.