IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
May 3, 2010, 8:50:52 AM (16 years ago)
Author:
eugene
Message:

updates from trunk

Location:
branches/simtest_nebulous_branches
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • branches/simtest_nebulous_branches

  • branches/simtest_nebulous_branches/psModules

  • branches/simtest_nebulous_branches/psModules/src/imcombine/pmSubtraction.c

    r24298 r27840  
    1717#include <pslib.h>
    1818
     19#include "pmErrorCodes.h"
    1920#include "pmHDU.h"                      // Required for pmFPA.h
    2021#include "pmFPA.h"
    2122#include "pmSubtractionStamps.h"
    2223#include "pmSubtractionEquation.h"
     24#include "pmSubtractionVisual.h"
    2325#include "pmSubtractionThreads.h"
    2426
     
    2931#define PIXEL_LIST_BUFFER 100           // Number of entries to add to pixel list at a time
    3032#define MIN_SAMPLE_STATS    7           // Minimum number to use sample statistics; otherwise use quartiles
     33#define USE_KERNEL_ERR                  // Use kernel error image?
    3134
    3235//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    3639// Generate the kernel to apply to the variance from the normal kernel
    3740static psKernel *varianceKernel(psKernel *out, // Output kernel
    38                                 psKernel *normalKernel // Normal kernel
     41                                const psKernel *normalKernel // Normal kernel
    3942                                )
    4043{
     
    4851
    4952    // Take the square of the normal kernel
    50     double sumNormal = 0.0, sumVariance = 0.0; // Sum of the normal and variance kernels
     53    double sumVariance = 0.0; // Sum of the variance kernels
    5154    for (int v = yMin; v <= yMax; v++) {
    5255        for (int u = xMin; u <= xMax; u++) {
    53             float value = normalKernel->kernel[v][u]; // Value of interest
    54             float value2 = PS_SQR(value); // Value squared
    55             sumNormal += value;
    56             sumVariance += value2;
    57             out->kernel[v][u] = value2;
     56            sumVariance += out->kernel[v][u] = PS_SQR(normalKernel->kernel[v][u]);
    5857        }
    5958    }
     
    6564    return out;
    6665}
     66
     67// Contribute to an image of the solved kernel component using the preCalculated image
     68static void solvedKernelPreCalc(psKernel *kernel, // Kernel, updated
     69                                const pmSubtractionKernels *kernels, // Kernel basis functions
     70                                float value,                         // Normalisation value for basis function
     71                                int index                  // Index of basis function of interest
     72    )
     73{
     74    int size = kernels->size;           // Kernel half-size
     75    pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[index]; // Precalculated values
     76#if 0
     77    // Iterating over the kernel
     78    for (int y = 0, v = -size; v <= size; y++, v++) {
     79        float yValue = value * preCalc->yKernel->data.F32[y];
     80        for (int x = 0, u = -size; u <= size; x++, u++) {
     81            kernel->kernel[v][u] +=  yValue * preCalc->xKernel->data.F32[x];
     82        }
     83    }
     84    // Photometric scaling for even kernels only
     85    if (kernels->u->data.S32[i] % 2 == 0 && kernels->v->data.S32[i] % 2 == 0) {
     86        kernel->kernel[0][0] -= value;
     87    }
     88#else
     89    for (int v = -size; v <= size; v++) {
     90        for (int u = -size; u <= size; u++) {
     91            kernel->kernel[v][u] +=  value * preCalc->kernel->kernel[v][u];
     92        }
     93    }
     94#endif
     95
     96    return;
     97}
     98
    6799
    68100// Generate an image of the solved kernel
     
    70102                              const pmSubtractionKernels *kernels, // Kernel basis functions
    71103                              const psImage *polyValues, // Spatial polynomial values
     104                              bool normalise,            // Add normalisation?
    72105                              bool wantDual // Want the dual (second) kernel?
    73106                              )
     
    85118    for (int i = 0; i < numKernels; i++) {
    86119        double value = p_pmSubtractionSolutionCoeff(kernels, polyValues, i, wantDual); // Polynomial value
     120        if (wantDual) {
     121            // The model is built with the dual convolution terms added, so to produce zero residual the
     122            // equation results in negative coefficients which we must undo.
     123            value *= -1.0;
     124        }
    87125
    88126        switch (kernels->type) {
     
    115153          case PM_SUBTRACTION_KERNEL_GUNK: {
    116154              if (i < kernels->inner) {
    117                   // Using pre-calculated function
    118                   psKernel *preCalc = kernels->preCalc->data[i]; // Precalculated values
    119                   // Iterating over the kernel
    120                   for (int v = -size; v <= size; v++) {
    121                       for (int u = -size; u <= size; u++) {
    122                           kernel->kernel[v][u] += preCalc->kernel[v][u] * value;
    123                           // Photometric scaling is built into the preCalc kernel --- no subtraction!
    124                       }
    125                   }
     155                  solvedKernelPreCalc(kernel, kernels, value, i);
    126156              } else {
    127157                  // Using delta function
     
    133163              break;
    134164          }
    135           case PM_SUBTRACTION_KERNEL_ISIS: {
    136               psArray *preCalc = kernels->preCalc->data[i]; // Precalculated values
    137               psVector *xKernel = preCalc->data[0]; // Kernel in x
    138               psVector *yKernel = preCalc->data[1]; // Kernel in y
    139               // Iterating over the kernel
    140               for (int y = 0, v = -size; v <= size; y++, v++) {
    141                   for (int x = 0, u = -size; u <= size; x++, u++) {
    142                       kernel->kernel[v][u] +=  value * xKernel->data.F32[x] * yKernel->data.F32[y];
    143                   }
    144               }
    145               // Photometric scaling for even kernels only
    146               if (kernels->u->data.S32[i] % 2 == 0 && kernels->v->data.S32[i] % 2 == 0) {
    147                   kernel->kernel[0][0] -= value;
    148               }
     165          case PM_SUBTRACTION_KERNEL_ISIS:
     166          case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     167          case PM_SUBTRACTION_KERNEL_HERM:
     168          case PM_SUBTRACTION_KERNEL_DECONV_HERM: {
     169              solvedKernelPreCalc(kernel, kernels, value, i);
    149170              break;
    150171          }
    151172          case PM_SUBTRACTION_KERNEL_RINGS: {
    152               psArray *preCalc = kernels->preCalc->data[i]; // Precalculated data
    153               psVector *uCoords = preCalc->data[0]; // u coordinates
    154               psVector *vCoords = preCalc->data[1]; // v coordinates
    155               psVector *poly = preCalc->data[2]; // Polynomial values
    156               int num = uCoords->n;     // Number of pixels
     173              pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[i]; // Precalculated kernels
     174              int num = preCalc->uCoords->n;     // Number of pixels
    157175
    158176              for (int j = 0; j < num; j++) {
    159                   int u = uCoords->data.S32[j], v = vCoords->data.S32[j]; // Kernel coordinates
    160                   kernel->kernel[v][u] += poly->data.F32[j] * value;
     177                  int u = preCalc->uCoords->data.S32[j];
     178                  int v = preCalc->vCoords->data.S32[j]; // Kernel coordinates
     179                  kernel->kernel[v][u] += preCalc->poly->data.F32[j] * value;
    161180              }
    162181              // Photometric scaling is built into the kernel --- no subtraction!
     
    168187    }
    169188
    170     // Put in the normalisation component
    171     kernel->kernel[0][0] += (wantDual ? 1.0 : p_pmSubtractionSolutionNorm(kernels));
     189    if (normalise) {
     190        // Put in the normalisation component
     191        kernel->kernel[0][0] += (wantDual ? 1.0 : p_pmSubtractionSolutionNorm(kernels));
     192    }
    172193
    173194    return kernel;
     
    250271static void convolveVarianceFFT(psImage *target,// Place the result in here
    251272                              psImage *variance, // Variance map to convolve
    252                               psImage *sys, // Systematic error image
     273                              psImage *kernelErr, // Kernel error image
    253274                              psImage *mask, // Mask image
    254275                              psImageMaskType maskVal, // Value to mask
     
    262283
    263284    psImage *subVariance = variance ? psImageSubset(variance, border) : NULL; // Variance map
    264     psImage *subSys = sys ? psImageSubset(sys, border) : NULL; // Systematic error image
     285    psImage *subKE = kernelErr ? psImageSubset(kernelErr, border) : NULL; // Kernel error image
    265286    psImage *subMask = mask ? psImageSubset(mask, border) : NULL; // Mask
    266287
    267288    // XXX Can trim this a little by combining the convolution: only have to take the FFT of the kernel once
    268289    psImage *convVariance = psImageConvolveFFT(NULL, subVariance, subMask, maskVal, kernel); // Convolved variance
    269     psImage *convSys = subSys ? psImageConvolveFFT(NULL, subSys, subMask, maskVal, kernel) : NULL; // Conv sys
     290    psImage *convKE = subKE ? psImageConvolveFFT(NULL, subKE, subMask, maskVal, kernel) : NULL; // Conv KE
    270291
    271292    psFree(subVariance);
    272     psFree(subSys);
     293    psFree(subKE);
    273294    psFree(subMask);
    274295
    275296    // Now, we have to stick it in where it belongs
    276297    int xMin = region.x0, xMax = region.x1, yMin = region.y0, yMax = region.y1; // Bounds of region
    277     if (convSys) {
     298    if (convKE) {
    278299        for (int yTarget = yMin, ySource = size; yTarget < yMax; yTarget++, ySource++) {
    279300            for (int xTarget = xMin, xSource = size; xTarget < xMax; xTarget++, xSource++) {
    280301                target->data.F32[yTarget][xTarget] = convVariance->data.F32[ySource][xSource] +
    281                     convSys->data.F32[ySource][xSource];
     302                    convKE->data.F32[ySource][xSource];
    282303            }
    283304        }
     
    290311
    291312    psFree(convVariance);
    292     psFree(convSys);
     313    psFree(convKE);
    293314
    294315    return;
     
    326347                                  psImage *image, // Image to convolve
    327348                                  psImage *variance, // Variance map to convolve, or NULL
    328                                   psImage *sys, // Systematic error image, or NULL
     349                                  psImage *kernelErr, // Kernel error image, or NULL
    329350                                  psImage *subMask, // Subtraction mask
    330351                                  const pmSubtractionKernels *kernels, // Kernels
     
    339360    )
    340361{
    341     *kernelImage = solvedKernel(*kernelImage, kernels, polyValues, wantDual);
     362    *kernelImage = solvedKernel(*kernelImage, kernels, polyValues, true, wantDual);
    342363    if (variance || subMask) {
    343364        *kernelVariance = varianceKernel(*kernelVariance, *kernelImage);
     
    363384        convolveFFT(convImage, image, subMask, subBad, *kernelImage, region, background, kernels->size);
    364385        if (variance) {
    365             convolveVarianceFFT(convVariance, variance, sys, subMask, subBad, *kernelVariance, region, kernels->size);
     386            convolveVarianceFFT(convVariance, variance, kernelErr, subMask, subBad, *kernelVariance,
     387                                region, kernels->size);
    366388        }
    367389    } else {
     
    373395    }
    374396
    375     // Convolve the mask for bad pixels
     397    // Convolve the mask for bad/poor pixels
    376398    if (subMask && convMask) {
    377399        int box = p_pmSubtractionBadRadius(*kernelImage, kernels, polyValues,
    378400                                           wantDual, poorFrac); // Size of bad box
     401        psAssert(box >= 0, "Bad radius must be >= 0");
     402
     403        int colMin = region.x0, colMax = region.x1, rowMin = region.y0, rowMax = region.y1; // Bounds
     404        psImage *convolved = NULL; // Convolved subtraction mask
    379405        if (box > 0) {
    380             int colMin = region.x0, colMax = region.x1, rowMin = region.y0, rowMax = region.y1; // Bounds
    381             psRegion region = psRegionSet(colMin - box, colMax + box,
    382                                           rowMin - box, rowMax + box); // Region to convolve
    383 
    384             psImage *image = subMask ? psImageSubset(subMask, region) : NULL; // Mask to convolve
    385 
    386             psImage *convolved = psImageConvolveMask(NULL, image, subBad, subConvBad,
    387                                                      -box, box, -box, box); // Convolved subtraction mask
    388 
     406            psRegion maskRegion = psRegionSet(colMin - box, colMax + box,
     407                                              rowMin - box, rowMax + box); // Region to convolve
     408            psImage *image = subMask ? psImageSubset(subMask, maskRegion) : NULL; // Mask to convolve
     409            convolved = psImageConvolveMask(NULL, image, subBad, subConvBad, -box, box, -box, box);
    389410            psFree(image);
    390 
    391             psAssert(convolved->numCols - 2 * box == colMax - colMin, "Bad number of columns");
    392             psAssert(convolved->numRows - 2 * box == rowMax - rowMin, "Bad number of rows");
    393 
    394             for (int yTarget = rowMin, ySource = box; yTarget < rowMax; yTarget++, ySource++) {
    395                 // Dereference images
    396                 psImageMaskType *target = &convMask->data.PS_TYPE_IMAGE_MASK_DATA[yTarget][colMin]; // Target values
    397                 psImageMaskType *source = &convolved->data.PS_TYPE_IMAGE_MASK_DATA[ySource][box]; // Source values
    398                 for (int xTarget = colMin; xTarget < colMax; xTarget++, target++, source++) {
    399                     if (*source & subConvBad) {
    400                         *target |= maskBad;
    401                     } else if (*source & subConvPoor) {
    402                         *target |= maskPoor;
    403                     }
     411        } else {
     412            convolved = psImageSubset(subMask, region);
     413        }
     414
     415        psAssert(convolved->numCols - 2 * box == colMax - colMin, "Bad number of columns");
     416        psAssert(convolved->numRows - 2 * box == rowMax - rowMin, "Bad number of rows");
     417
     418        for (int yTarget = rowMin, ySource = box; yTarget < rowMax; yTarget++, ySource++) {
     419            // Dereference images
     420            psImageMaskType *target = &convMask->data.PS_TYPE_IMAGE_MASK_DATA[yTarget][colMin]; // Target values
     421            psImageMaskType *source = &convolved->data.PS_TYPE_IMAGE_MASK_DATA[ySource][box]; // Source values
     422            for (int xTarget = colMin; xTarget < colMax; xTarget++, target++, source++) {
     423                if (*source & subConvBad) {
     424                    *target |= maskBad;
     425                } else if (*source & subConvPoor) {
     426                    *target &= ~maskBad;
     427                    *target |= maskPoor;
     428                } else {
     429                    *target &= ~maskBad & ~maskPoor;
    404430                }
    405431            }
    406 
    407             // No need to lock: we own this
    408             psFree(convolved);
    409         }
     432        }
     433
     434        psFree(convolved);
    410435    }
    411436
     
    413438}
    414439
    415 // Generate an image that can be used to track systematic errors
    416 static psImage *subtractionSysErrImage(const psImage *image, // Image from which to make sys err image
    417                                        float sysError // Relative systematic error
    418                                        )
    419 {
    420     if (!isfinite(sysError) || sysError == 0.0) {
     440#ifdef USE_KERNEL_ERR
     441// Generate an image that can be used to track systematic errors in the kernel
     442static psImage *subtractionKernelErrImage(const psImage *image, // Image from which to make kernel error image
     443                                          float kernelError // Relative systematic error in kernel
     444    )
     445{
     446    if (!isfinite(kernelError) || kernelError == 0.0) {
    421447        return NULL;
    422448    }
    423449
    424450    int numCols = image->numCols, numRows = image->numRows; // Size of image
    425     psImage *sys = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Systematic error image
    426 
    427     float sysError2 = PS_SQR(sysError); // Square of the systematic error
     451    psImage *kernelErr = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Kernel error image
     452
     453    float kernelError2 = PS_SQR(kernelError); // Square of the kernel error
    428454    for (int y = 0; y < numRows; y++) {
    429455        for (int x = 0; x < numCols; x++) {
    430             sys->data.F32[y][x] = PS_SQR(image->data.F32[y][x]) * sysError2;
    431         }
    432     }
    433 
    434     return sys;
     456            kernelErr->data.F32[y][x] = PS_SQR(image->data.F32[y][x]) * kernelError2;
     457        }
     458    }
     459
     460    return kernelErr;
     461}
     462#endif
     463
     464// Convolve a stamp using a pre-calculated kernel basis function
     465static psKernel *convolveStampPreCalc(const psKernel *image, // Image to convolve
     466                                      const pmSubtractionKernels *kernels, // Kernel basis functions
     467                                      int index,                            // Index of basis function of interest
     468                                      int footprint                         // Half-size of stamp
     469    )
     470{
     471    pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[index]; // Precalculated data
     472#if 0
     473    // Convolving using separable convolution
     474    int size = kernels->size;     // Size of kernel
     475
     476    // Convolve in x
     477    // Need to convolve a bit more than the footprint, for the y convolution
     478    int yMin = -size - footprint, yMax = size + footprint; // Range for y
     479    psKernel *temp = psKernelAlloc(yMin, yMax,
     480                                   -footprint, footprint); // Temporary convolution; NOTE: wrong way!
     481    for (int y = yMin; y <= yMax; y++) {
     482        for (int x = -footprint; x <= footprint; x++) {
     483            float value = 0.0;    // Value of convolved pixel
     484            int uMin = x - size, uMax = x + size; // Range for u
     485            psF32 *xKernelData = &preCalc->xKernel->data.F32[xKernel->n - 1]; // Kernel values
     486            psF32 *imageData = &image->kernel[y][uMin]; // Image values
     487            for (int u = uMin; u <= uMax; u++, xKernelData--, imageData++) {
     488                value += *xKernelData * *imageData;
     489            }
     490            temp->kernel[x][y] = value; // NOTE: putting in wrong way!
     491        }
     492    }
     493
     494    // Convolve in y
     495    psKernel *convolved = psKernelAlloc(-footprint, footprint, -footprint, footprint);// Convolved image
     496    for (int x = -footprint; x <= footprint; x++) {
     497        for (int y = -footprint; y <= footprint; y++) {
     498            float value = 0.0;    // Value of convolved pixel
     499            int vMin = y - size, vMax = y + size; // Range for v
     500            psF32 *yKernelData = &preCalc->yKernel->data.F32[yKernel->n - 1]; // Kernel values
     501            psF32 *imageData = &temp->kernel[x][vMin]; // Image values; NOTE: wrong way!
     502            for (int v = vMin; v <= vMax; v++, yKernelData--, imageData++) {
     503                value += *yKernelData * *imageData;
     504            }
     505            convolved->kernel[y][x] = value;
     506        }
     507    }
     508    psFree(temp);
     509
     510    // Photometric scaling for even kernels only
     511    if (kernels->u->data.S32[index] % 2 == 0 && kernels->v->data.S32[index] % 2 == 0) {
     512        convolveSub(convolved, image, footprint);
     513    }
     514    return convolved;
     515#else
     516    // Convolving using precalculated kernel
     517    return p_pmSubtractionConvolveStampPrecalc(image, preCalc->kernel);
     518#endif
    435519}
    436520
     
    447531    int x0 = - image->xMin, y0 = - image->yMin; // Position of centre of convolved image
    448532    psKernel *convolved = psKernelAllocFromImage(conv, x0, y0); // Kernel version
     533
     534    // pmSubtractionVisualShowSubtraction(image->image, kernel->image, conv);
     535
    449536    psFree(conv);
    450537    return convolved;
     
    456543    psKernel *kernel;                   // Kernel to use
    457544    if (!preKernel) {
    458         kernel = solvedKernel(NULL, kernels, polyValues, wantDual);
     545        kernel = solvedKernel(NULL, kernels, polyValues, true, wantDual);
    459546    } else {
    460547        kernel = psMemIncrRefCounter(preKernel);
     
    491578}
    492579
     580void p_pmSubtractionPolynomialNormCoords(float *xOut, float *yOut, float xIn, float yIn,
     581                                         int xMin, int xMax, int yMin, int yMax)
     582{
     583    float xNormSize = xMax - xMin, yNormSize = yMax - yMin; // Size to use for normalisation
     584    *xOut = 2.0 * (float)(xIn - xMin - xNormSize/2.0) / xNormSize;
     585    *yOut = 2.0 * (float)(yIn - yMin - yNormSize/2.0) / yNormSize;
     586    return;
     587}
     588
    493589psImage *p_pmSubtractionPolynomialFromCoords(psImage *output, const pmSubtractionKernels *kernels,
    494                                              int numCols, int numRows, int x, int y)
     590                                             int x, int y)
    495591{
    496592    assert(kernels);
    497     assert(numCols > 0 && numRows > 0);
    498 
    499     // Size to use when calculating normalised coordinates (different from actual size when convolving
    500     // subimage)
    501     int xNormSize = (kernels->numCols > 0 ? kernels->numCols : numCols);
    502     int yNormSize = (kernels->numRows > 0 ? kernels->numRows : numRows);
    503 
    504     // Normalised coordinates
    505     float yNorm = 2.0 * (float)(y - yNormSize/2.0) / (float)yNormSize;
    506     float xNorm = 2.0 * (float)(x - xNormSize/2.0) / (float)xNormSize;
    507 
     593
     594    float xNorm, yNorm;                 // Normalised coordinates
     595    p_pmSubtractionPolynomialNormCoords(&xNorm, &yNorm, x, y,
     596                                        kernels->xMin, kernels->xMax, kernels->yMin, kernels->yMax);
    508597    return p_pmSubtractionPolynomial(output, kernels->spatialOrder, xNorm, yNorm);
    509598}
     
    586675          if (index < kernels->inner) {
    587676              // Photometric scaling is already built in to the precalculated kernel
    588               return p_pmSubtractionConvolveStampPrecalc(image, kernels->preCalc->data[index]);
     677              return convolveStampPreCalc(image, kernels, index, footprint);
    589678          }
    590679          // Using delta function
     
    595684          return convolved;
    596685      }
    597       case PM_SUBTRACTION_KERNEL_ISIS: {
    598           psArray *preCalc = kernels->preCalc->data[index]; // Precalculated values
    599           psVector *xKernel = preCalc->data[0]; // Kernel in x
    600           psVector *yKernel = preCalc->data[1]; // Kernel in y
    601           int size = kernels->size;     // Size of kernel
    602 
    603           // Convolve in x
    604           // Need to convolve a bit more than the footprint, for the y convolution
    605           int yMin = -size - footprint, yMax = size + footprint; // Range for y
    606           psKernel *temp = psKernelAlloc(yMin, yMax,
    607                                          -footprint, footprint); // Temporary convolution; NOTE: wrong way!
    608           for (int y = yMin; y <= yMax; y++) {
    609               for (int x = -footprint; x <= footprint; x++) {
    610                   float value = 0.0;    // Value of convolved pixel
    611                   int uMin = x - size, uMax = x + size; // Range for u
    612                   psF32 *xKernelData = &xKernel->data.F32[xKernel->n - 1]; // Kernel values
    613                   psF32 *imageData = &image->kernel[y][uMin]; // Image values
    614                   for (int u = uMin; u <= uMax; u++, xKernelData--, imageData++) {
    615                       value += *xKernelData * *imageData;
    616                   }
    617                   temp->kernel[x][y] = value; // NOTE: putting in wrong way!
    618               }
    619           }
    620 
    621           // Convolve in y
    622           psKernel *convolved = psKernelAlloc(-footprint, footprint, -footprint, footprint);// Convolved image
    623           for (int x = -footprint; x <= footprint; x++) {
    624               for (int y = -footprint; y <= footprint; y++) {
    625                   float value = 0.0;    // Value of convolved pixel
    626                   int vMin = y - size, vMax = y + size; // Range for v
    627                   psF32 *yKernelData = &yKernel->data.F32[yKernel->n - 1]; // Kernel values
    628                   psF32 *imageData = &temp->kernel[x][vMin]; // Image values; NOTE: wrong way!
    629                   for (int v = vMin; v <= vMax; v++, yKernelData--, imageData++) {
    630                       value += *yKernelData * *imageData;
    631                   }
    632                   convolved->kernel[y][x] = value;
    633               }
    634           }
    635           psFree(temp);
    636 
    637           // Photometric scaling for even kernels only
    638           if (kernels->u->data.S32[index] % 2 == 0 && kernels->v->data.S32[index] % 2 == 0) {
    639               convolveSub(convolved, image, footprint);
    640           }
    641           return convolved;
    642       }
     686      case PM_SUBTRACTION_KERNEL_ISIS:
     687      case PM_SUBTRACTION_KERNEL_ISIS_RADIAL:
     688      case PM_SUBTRACTION_KERNEL_HERM:
     689      case PM_SUBTRACTION_KERNEL_DECONV_HERM: {
     690            return convolveStampPreCalc(image, kernels, index, footprint);
     691        }
    643692      case PM_SUBTRACTION_KERNEL_RINGS: {
    644           psKernel *convolved = psKernelAlloc(-footprint, footprint,
    645                                               -footprint, footprint); // Convolved image
    646           psArray *preCalc = kernels->preCalc->data[index]; // Precalculated data
    647           psVector *uCoords = preCalc->data[0]; // u coordinates
    648           psVector *vCoords = preCalc->data[1]; // v coordinates
    649           psVector *poly = preCalc->data[2]; // Polynomial values
    650           int num = uCoords->n;         // Number of pixels
    651           psS32 *uData = uCoords->data.S32, *vData = vCoords->data.S32; // Dereference u,v coordinates
    652           psF32 *polyData = poly->data.F32; // Dereference polynomial values
     693          psKernel *convolved = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Convolved image
     694          pmSubtractionKernelPreCalc *preCalc = kernels->preCalc->data[index]; // Precalculated data
     695
     696          int num = preCalc->uCoords->n;         // Number of pixels
     697          psS32 *uData = preCalc->uCoords->data.S32; // Dereference v coordinate
     698          psS32 *vData = preCalc->vCoords->data.S32; // Dereference u coordinate
     699          psF32 *polyData = preCalc->poly->data.F32; // Dereference polynomial values
    653700          psF32 **imageData = image->kernel;  // Dereference image
    654701          psF32 **convData = convolved->kernel; // Dereference convolved image
     
    706753
    707754    if (stamp->status != PM_SUBTRACTION_STAMP_CALCULATE) {
    708         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Stamp not marked for calculation.");
     755        psError(PM_ERR_PROG, true, "Stamp not marked for calculation.");
    709756        return false;
    710757    }
     
    730777
    731778
    732 
    733 
    734779int pmSubtractionRejectStamps(pmSubtractionKernels *kernels, pmSubtractionStampList *stamps,
    735                               const psVector *deviations, psImage *subMask, float sigmaRej, int footprint)
     780                              const psVector *deviations, psImage *subMask, float sigmaRej)
    736781{
    737782    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
     
    764809
    765810    if (numStamps == 0) {
    766         psError(PS_ERR_UNKNOWN, true, "No good stamps found.");
     811        psError(PM_ERR_STAMPS, true, "No good stamps found.");
    767812        psFree(mask);
    768813        return -1;
     
    772817                                  PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_QUARTILE); // Statistics for deviatns
    773818    if (!psVectorStats(stats, deviations, NULL, mask, 0xff)) {
    774         psError(PS_ERR_UNKNOWN, false, "Unable to measure statistics for deviations.");
     819        psError(PM_ERR_DATA, false, "Unable to measure statistics for deviations.");
    775820        psFree(stats);
    776821        psFree(mask);
     
    821866    ds9num++;
    822867
     868    int footprint = stamps->footprint;  // Half-size of stamp region of interest
    823869    int numRejected = 0;                // Number of stamps rejected
    824870    int numGood = 0;                    // Number of good stamps
    825871    double newMean = 0.0;               // New mean
     872    psString log = NULL;                // Log message
     873    psStringAppend(&log, "Rejecting stamps, mean = %f, threshold = %f\n", mean, limit);
    826874    for (int i = 0; i < stamps->num; i++) {
    827875        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     
    836884                // Mask out the stamp in the image so you it's not found again
    837885                psTrace("psModules.imcombine", 3, "Rejecting stamp %d (%d,%d)\n", i,
    838                         (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
     886                        (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
     887                psStringAppend(&log, "Stamp %d (%d,%d): %f\n", i,
     888                               (int)(stamp->x - 0.5), (int)(stamp->y - 0.5),
     889                               fabsf(deviations->data.F32[i] - mean));
    839890                numRejected++;
    840891                for (int y = stamp->y - footprint; y <= stamp->y + footprint; y++) {
     
    857908                psFree(stamp->image1);
    858909                psFree(stamp->image2);
    859                 psFree(stamp->variance);
    860                 stamp->image1 = stamp->image2 = stamp->variance = NULL;
    861                 psFree(stamp->matrix1);
    862                 psFree(stamp->matrix2);
    863                 psFree(stamp->matrixX);
    864                 stamp->matrix1 = stamp->matrix2 = stamp->matrixX = NULL;
    865                 psFree(stamp->vector1);
    866                 psFree(stamp->vector2);
    867                 stamp->vector1 = stamp->vector2 = NULL;
     910                psFree(stamp->weight);
     911                stamp->image1 = stamp->image2 = stamp->weight = NULL;
     912                psFree(stamp->matrix);
     913                stamp->matrix = NULL;
     914                psFree(stamp->vector);
     915                stamp->vector = NULL;
    868916            } else {
    869917                numGood++;
     
    874922    }
    875923    newMean /= numGood;
     924
     925    if (numRejected == 0) {
     926        psStringAppend(&log, "<none>\n");
     927    }
     928    psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "%s", log);
     929    psFree(log);
    876930
    877931    if (ds9) {
     
    900954
    901955    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, x, y); // Solved polynomial
    902     psKernel *kernel = solvedKernel(NULL, kernels, polyValues, wantDual); // The appropriate kernel
     956    psKernel *kernel = solvedKernel(NULL, kernels, polyValues, true, wantDual); // The appropriate kernel
    903957    psFree(polyValues);
    904958
     
    931985    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, x, y);
    932986
    933     psKernel *kernel = solvedKernel(NULL, kernels, polyValues, wantDual); // The appropriate kernel
     987    psKernel *kernel = solvedKernel(NULL, kernels, polyValues, true, wantDual); // The appropriate kernel
    934988    psFree(polyValues);
    935989
     
    9481002}
    9491003
    950 #if 0
     1004#if 1
    9511005psArray *pmSubtractionKernelSolutions(const pmSubtractionKernels *kernels, float x, float y, bool wantDual)
    9521006{
     
    9561010    PS_ASSERT_FLOAT_WITHIN_RANGE(y, -1.0, 1.0, NULL);
    9571011
    958     psArray *images = psArrayAlloc(solution->n - 1); // Images of each kernel to return
    959     psVector *fakeSolution = psVectorAlloc(solution->n, PS_TYPE_F64); // Fake solution vector
    960     psVectorInit(fakeSolution, 0.0);
    961 
    962     for (int i = 0; i < solution->n - 1; i++) {
    963         fakeSolution->data.F64[i] = solution->data.F64[i];
    964         images->data[i] = pmSubtractionKernelImage(kernels, x, y, wantDual);
    965         fakeSolution->data.F64[i] = 0.0;
    966     }
    967 
    968     psFree(fakeSolution);
     1012    psVector *solution = wantDual ? kernels->solution2 : kernels->solution1; // Solution of interest
     1013    psVector *backup = psVectorCopy(NULL, solution, PS_TYPE_F64);  // Backup version
     1014
     1015    int num = kernels->num;             // Number of kernel basis functions
     1016
     1017    psImage *polyValues = p_pmSubtractionPolynomial(NULL, kernels->spatialOrder, x, y); // Solved polynomial
     1018    psArray *images = psArrayAlloc(num + 1); // Images of each kernel to return
     1019
     1020    // The whole kernel
     1021    {
     1022        psKernel *kernel = solvedKernel(NULL, kernels, polyValues, true, wantDual); // The appropriate kernel
     1023        images->data[0] = psMemIncrRefCounter(kernel->image);
     1024        psFree(kernel);
     1025    }
     1026
     1027    // The parts
     1028    psVectorInit(solution, 0.0);
     1029    for (int i = 0; i < num; i++) {
     1030        solution->data.F64[i] = backup->data.F64[i];
     1031        psKernel *kernel = solvedKernel(NULL, kernels, polyValues, false, wantDual); // The appropriate kernel
     1032#if 0
     1033        int size = kernels->size;
     1034        double sum = 0.0;
     1035        for (int v = -size; v <= size; v++) {
     1036            for (int u = -size; u <= size; u++) {
     1037                sum += kernel->kernel[v][u];
     1038            }
     1039        }
     1040        fprintf(stderr, "Kernel %d: %lf\n", i, sum);
     1041#endif
     1042        images->data[i + 1] = psMemIncrRefCounter(kernel->image);
     1043        psFree(kernel);
     1044        solution->data.F64[i] = 0.0;
     1045    }
     1046    psFree(polyValues);
     1047    psVectorCopy(solution, backup, PS_TYPE_F64);
     1048    psFree(backup);
    9691049
    9701050    return images;
     
    9791059                                     psImage *convMask, // Output convolved mask
    9801060                                     const pmReadout *ro1, const pmReadout *ro2, // Input readouts
    981                                      psImage *sys1, psImage *sys2, // Systematic error images
     1061                                     psImage *kernelErr1, psImage *kernelErr2, // Kernel error images
    9821062                                     psImage *subMask, // Input subtraction mask
    9831063                                     psImageMaskType maskBad, // Mask value to give bad pixels
     
    9981078    // Only generate polynomial values every kernel footprint, since we have already assumed
    9991079    // (with the stamps) that it does not vary rapidly on this scale.
    1000     psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, numCols, numRows,
    1001                                                               xMin + x0 + size + 1,
    1002                                                               yMin + y0 + size + 1);
     1080    psImage *polyValues = p_pmSubtractionPolynomialFromCoords(NULL, kernels, xMin + x0 + size + 1,
     1081                                                              yMin + y0 + size + 1);        // Polynomial
    10031082    float background = doBG ? p_pmSubtractionSolutionBackground(kernels, polyValues) : 0.0; // Background term
    10041083
    10051084    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1006         convolveRegion(out1->image, out1->variance, convMask, &kernelImage, &kernelVariance,
    1007                        ro1->image, ro1->variance, sys1, subMask, kernels, polyValues, background, *region,
    1008                        maskBad, maskPoor, poorFrac, useFFT, false);
     1085        convolveRegion(out1->image, out1->variance, out1->mask, &kernelImage, &kernelVariance,
     1086                       ro1->image, ro1->variance, kernelErr1, subMask, kernels, polyValues, background,
     1087                       *region, maskBad, maskPoor, poorFrac, useFFT, false);
    10091088    }
    10101089    if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1011         convolveRegion(out2->image, out2->variance, convMask, &kernelImage, &kernelVariance,
    1012                        ro2->image, ro2->variance, sys2, subMask, kernels, polyValues, background, *region,
    1013                        maskBad, maskPoor, poorFrac, useFFT, kernels->mode == PM_SUBTRACTION_MODE_DUAL);
     1090        convolveRegion(out2->image, out2->variance, out2->mask, &kernelImage, &kernelVariance,
     1091                       ro2->image, ro2->variance, kernelErr2, subMask, kernels, polyValues, background,
     1092                       *region, maskBad, maskPoor, poorFrac, useFFT,
     1093                       kernels->mode == PM_SUBTRACTION_MODE_DUAL);
    10141094    }
    10151095
     
    10191099
    10201100    if ((kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) && ro1->mask) {
    1021         psImageMaskType **target = convMask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
     1101        psImageMaskType **target = out1->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
    10221102        psImageMaskType **source = ro1->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Source mask
    10231103
     
    10291109    }
    10301110    if ((kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) && ro2->mask) {
    1031         psImageMaskType **target = convMask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
     1111        psImageMaskType **target = out2->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Target mask
    10321112        psImageMaskType **source = ro2->mask->data.PS_TYPE_IMAGE_MASK_DATA; // Source mask
    10331113
     
    10561136    const pmReadout *ro1 = args->data[7]; // Input readout 1
    10571137    const pmReadout *ro2 = args->data[8]; // Input readout 2
    1058     psImage *sys1 = args->data[9]; // Systematic error image 1
    1059     psImage *sys2 = args->data[10]; // Systematic error image 2
     1138    psImage *kernelErr1 = args->data[9]; // Kernel error image 1
     1139    psImage *kernelErr2 = args->data[10]; // Kernel error image 2
    10601140    psImage *subMask = args->data[11]; // Subtraction mask
    10611141    psImageMaskType maskBad = PS_SCALAR_VALUE(args->data[12], PS_TYPE_IMAGE_MASK_DATA); // Output mask value for bad pixels
     
    10671147    bool useFFT = PS_SCALAR_VALUE(args->data[18], U8); // Use FFT for convolution?
    10681148
    1069     return subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2, sys1, sys2,
    1070                                     subMask, maskBad, maskPoor, poorFrac, region, kernels, doBG, useFFT);
     1149    return subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2, kernelErr1,
     1150                                    kernelErr2, subMask, maskBad, maskPoor, poorFrac, region, kernels,
     1151                                    doBG, useFFT);
    10711152}
    10721153
    10731154bool pmSubtractionConvolve(pmReadout *out1, pmReadout *out2, const pmReadout *ro1, const pmReadout *ro2,
    10741155                           psImage *subMask, int stride, psImageMaskType maskBad, psImageMaskType maskPoor,
    1075                            float poorFrac, float sysError, const psRegion *region,
     1156                           float poorFrac, float kernelError, float covarFrac, const psRegion *region,
    10761157                           const pmSubtractionKernels *kernels, bool doBG, bool useFFT)
    10771158{
     
    10821163        PM_ASSERT_READOUT_NON_NULL(ro1, false);
    10831164        PM_ASSERT_READOUT_IMAGE(ro1, false);
     1165        PM_ASSERT_READOUT_IMAGE(out1, false);
    10841166        numCols = ro1->image->numCols;
    10851167        numRows = ro1->image->numRows;
     
    10911173        PM_ASSERT_READOUT_NON_NULL(ro2, false);
    10921174        PM_ASSERT_READOUT_IMAGE(ro2, false);
     1175        PM_ASSERT_READOUT_IMAGE(out2, false);
    10931176        if (numCols == 0 && numRows == 0) {
    10941177            numCols = ro2->image->numCols;
     
    11111194    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(poorFrac, 0.0, false);
    11121195    PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(poorFrac, 1.0, false);
    1113     PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(sysError, 0.0, false);
    1114     PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(sysError, 1.0, false);
     1196    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(kernelError, 0.0, false);
     1197    PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(kernelError, 1.0, false);
     1198    PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(covarFrac, 0.0, false);
     1199    PS_ASSERT_FLOAT_LESS_THAN(covarFrac, 1.0, false);
    11151200    if (region && psRegionIsNaN(*region)) {
    11161201        psString string = psRegionToString(*region);
    1117         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Input region (%s) contains NAN values", string);
     1202        psError(PM_ERR_PROG, true, "Input region (%s) contains NAN values", string);
    11181203        psFree(string);
    11191204        return false;
     
    11241209    bool threaded = pmSubtractionThreaded(); // Running threaded?
    11251210
    1126     // Outputs
    1127     if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1128         if (!out1->image) {
    1129             out1->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1130             // XXX if (threaded) {
    1131             // XXX     psMutexInit(out1->image);
    1132             // XXX }
    1133         }
    1134         if (ro1->variance) {
    1135             if (!out1->variance) {
    1136                 out1->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1137                 // XXX if (threaded) {
    1138                 // XXX     psMutexInit(out1->variance);
    1139                 // XXX }
    1140             }
    1141             psImageInit(out1->variance, 0.0);
    1142         }
    1143     }
    1144     if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1145         if (!out2->image) {
    1146             out2->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1147             // XXX if (threaded) {
    1148             // XXX     psMutexInit(out2->image);
    1149             // XXX }
    1150         }
    1151         if (ro2->variance) {
    1152             if (!out2->variance) {
    1153                 out2->variance = psImageAlloc(numCols, numRows, PS_TYPE_F32);
    1154                 // XXX if (threaded) {
    1155                 // XXX     psMutexInit(out2->variance);
    1156                 // XXX }
    1157             }
    1158             psImageInit(out2->variance, 0.0);
    1159         }
    1160     }
    11611211    psImage *convMask = NULL;           // Convolved mask image (common to inputs 1 and 2)
    11621212    if (subMask) {
    1163         // XXX if (threaded) {
    1164         // XXX     psMutexInit(subMask);
    1165         // XXX }
    11661213        if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1167             if (!out1->mask) {
    1168                 out1->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    1169             }
    11701214            convMask = out1->mask;
    11711215        }
    11721216        if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1173             if (convMask) {
    1174                 if (out2->mask) {
    1175                     psFree(out2->mask);
    1176                 }
    1177                 out2->mask = psMemIncrRefCounter(convMask);
    1178             } else {
    1179                 if (!out2->mask) {
    1180                     out2->mask = psImageAlloc(numCols, numRows, PS_TYPE_IMAGE_MASK);
    1181                 }
     1217            if (!convMask) {
    11821218                convMask = out2->mask;
    11831219            }
    11841220        }
    1185         psImageInit(convMask, 0);
    1186     }
    1187 
    1188     psImage *sys1 = NULL, *sys2 = NULL; // Systematic error images
     1221    }
     1222
     1223    psImage *kernelErr1 = NULL, *kernelErr2 = NULL; // Kernel error images
     1224#ifdef USE_KERNEL_ERR
    11891225    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1190         sys1 = subtractionSysErrImage(ro1->image, sysError);
    1191         // XXX if (threaded && sys1) {
    1192         // XXX     psMutexInit(sys1);
    1193         // XXX }
     1226        kernelErr1 = subtractionKernelErrImage(ro1->image, kernelError);
    11941227    }
    11951228    if (kernels->mode == PM_SUBTRACTION_MODE_2 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    1196         sys2 = subtractionSysErrImage(ro2->image, sysError);
    1197         // XXX if (threaded && sys2) {
    1198         // XXX     psMutexInit(sys2);
    1199         // XXX }
    1200     }
     1229        kernelErr2 = subtractionKernelErrImage(ro2->image, kernelError);
     1230    }
     1231#endif
    12011232
    12021233    int size = kernels->size;           // Half-size of kernel
    12031234
    12041235    // Get region for convolution: [xMin:xMax,yMin:yMax]
    1205     int xMin = size, xMax = numCols - size;
    1206     int yMin = size, yMax = numRows - size;
     1236    int xMin = kernels->xMin + size, xMax = kernels->xMax - size;
     1237    int yMin = kernels->yMin + size, yMax = kernels->yMax - size;
    12071238    if (region) {
    12081239        xMin = PS_MAX(region->x0, xMin);
     
    12471278                psArrayAdd(args, 1, (pmReadout*)ro1); // Casting away const
    12481279                psArrayAdd(args, 1, (pmReadout*)ro2); // Casting away const
    1249                 // Since adding to the array can impact the reference count, we need to lock
    1250                 // XXX if (sys1) {
    1251                 // XXX     psMutexLock(sys1);
    1252                 // XXX }
    1253                 psArrayAdd(args, 1, sys1);
    1254                 // XXX if (sys1) {
    1255                 // XXX     psMutexUnlock(sys1);
    1256                 // XXX }
    1257                 // XXX if (sys2) {
    1258                 // XXX     psMutexLock(sys2);
    1259                 // XXX }
    1260                 psArrayAdd(args, 1, sys2);
    1261                 // XXX if (sys2) {
    1262                 // XXX     psMutexUnlock(sys2);
    1263                 // XXX }
    1264                 // XXX if (subMask) {
    1265                 // XXX     psMutexLock(subMask);
    1266                 // XXX }
     1280                psArrayAdd(args, 1, kernelErr1);
     1281                psArrayAdd(args, 1, kernelErr2);
    12671282                psArrayAdd(args, 1, subMask);
    1268                 // XXX if (subMask) {
    1269                 // XXX     psMutexUnlock(subMask);
    1270                 // XXX }
    12711283                PS_ARRAY_ADD_SCALAR(args, maskBad, PS_TYPE_IMAGE_MASK);
    12721284                PS_ARRAY_ADD_SCALAR(args, maskPoor, PS_TYPE_IMAGE_MASK);
     
    12831295                psFree(job);
    12841296            } else {
    1285                 subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2, sys1, sys2,
    1286                                          subMask, maskBad, maskPoor, poorFrac, subRegion, kernels, doBG,
    1287                                          useFFT);
     1297                subtractionConvolvePatch(numCols, numRows, x0, y0, out1, out2, convMask, ro1, ro2,
     1298                                         kernelErr1, kernelErr2, subMask, maskBad, maskPoor, poorFrac,
     1299                                         subRegion, kernels, doBG, useFFT);
    12881300            }
    12891301            psFree(subRegion);
     
    12921304
    12931305    if (!psThreadPoolWait(false)) {
    1294         psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
     1306        psError(psErrorCodeLast(), false, "Error waiting for threads.");
    12951307        return false;
    12961308    }
     
    13071319            psFree(job);
    13081320        }
    1309 
    1310         // XXX if (subMask) {
    1311         // XXX     psMutexDestroy(subMask);
    1312         // XXX }
    1313         // XXX if (sys1) {
    1314         // XXX     psMutexDestroy(sys1);
    1315         // XXX }
    1316         // XXX if (sys2) {
    1317         // XXX     psMutexDestroy(sys2);
    1318         // XXX }
    13191321    }
    13201322    psImageConvolveSetThreads(oldThreads);
    13211323
    1322     psFree(sys1);
    1323     psFree(sys2);
     1324    psFree(kernelErr1);
     1325    psFree(kernelErr2);
    13241326
    13251327    // Calculate covariances
    1326     // This can take a while, so we only do it for a single instance
    1327     // XXX psImageCovarianceCalculate could be multithreaded
     1328    // This can be fairly involved, so we only do it for a single instance
     1329    // Enable threads for covariance calculation, since we're not threading on top of it.
     1330    oldThreads = psImageCovarianceSetThreads(true);
    13281331    if (kernels->mode == PM_SUBTRACTION_MODE_1 || kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    13291332        psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0, false); // Convolution kernel
     1333        psKernelTruncate(kernel, covarFrac);
    13301334        out1->covariance = psImageCovarianceCalculate(kernel, ro1->covariance);
    13311335        psFree(kernel);
     
    13341338        psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0,
    13351339                                               kernels->mode == PM_SUBTRACTION_MODE_DUAL); // Conv. kernel
     1340        psKernelTruncate(kernel, covarFrac);
    13361341        out2->covariance = psImageCovarianceCalculate(kernel, ro2->covariance);
    13371342        psFree(kernel);
    13381343    }
     1344    psImageCovarianceSetThreads(oldThreads);
    13391345
    13401346    // Copy anything that wasn't convolved
     
    13761382    }
    13771383
    1378 
    13791384    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Convolve image: %f sec",
    13801385             psTimerClear("pmSubtractionConvolve"));
Note: See TracChangeset for help on using the changeset viewer.