IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Aug 29, 2007, 5:50:28 PM (19 years ago)
Author:
Paul Price
Message:

Extensive changes to image subtraction codes. Discovered that I
wasn't (again) implementing the algorithm correctly --- I was
accumulating

sum_x,y C_i(x,y) sum_x,y C_j(x,y)

instead of

sum_x,y C_i(x,y) C_j(x,y)

i.e., I was doing the sums separately. I'm amazed that it was
actually getting fairly decent results before, but now it's doing it
properly. As part of this fix, added in, for each stamp, images
corresponding to the convolution of the reference with each of the
kernel components. This allows fast rejection (instead of generating
the kernel and convolving, can simply sum the individual convolutions
with the correct scaling).
Added use of FFT to do convolutions. This involved changing around
the convolvePixel function (now convolveRef, since it generates the
entire image for the convolution of the reference stamp with a kernel
component, rather than a single pixel as previously). Tested with
POIS and ISIS kernels, but no others yet. FFT is nice and fast for
the large (size = 10) kernels I've been using, so I also moved it in
to the convolveRef function for appropriate kernel types.
As part of the FFT move, discovered that my convolutions were around
the wrong way --- supposed to be convolved = image[y][x] * conv[y - v][x - u]
instead of conv[y + v][x + u]. However, this problem didn't manifest
itself previously because everything was consistently around the wrong
way. The FFT does it the Correct Way, so it revealed the problem. This
is now fixed throughout the image subtraction code.

File:
1 edited

Legend:

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

    r14648 r14701  
    44 *  @author GLG, MHPCC
    55 *
    6  *  @version $Revision: 1.48 $ $Name: not supported by cvs2svn $
    7  *  @date $Date: 2007-08-23 23:43:12 $
     6 *  @version $Revision: 1.49 $ $Name: not supported by cvs2svn $
     7 *  @date $Date: 2007-08-30 03:50:28 $
    88 *
    99 *  Copyright 2004-2007 Institute for Astronomy, University of Hawaii
     
    1717#include <stdio.h>
    1818#include <math.h>
     19#include <string.h>
    1920#include <pslib.h>
    2021
     
    2425
    2526#include "pmSubtraction.h"
     27
     28//#define TESTING
    2629
    2730#define PIXEL_LIST_BUFFER 100           // Number of entries to add to pixel list at a time
     
    216219}
    217220
    218 // Generate the convolved pixel value
    219 static inline double convolvePixel(const pmSubtractionKernels *kernels, // Kernel basis functions
    220                                    int index, // Kernel basis function index
    221                                    int x, int y, // Pixel around which to convolve
    222                                    const psKernel *image // Image to convolve (a kernel for convenience)
    223                                    )
     221// Subtract the (0,0) element to preserve photometric scaling
     222static void convolveSub(psKernel *convolved, // Convolved image
     223                        const psKernel *image, // Image being convolved
     224                        int footprint  // Size of region of interest
     225                        )
     226{
     227    // Can't use psBinaryOp because the images are of different size
     228    for (int y = -footprint; y <= footprint; y++) {
     229        for (int x = -footprint; x <= footprint; x++) {
     230            convolved->kernel[y][x] -= image->kernel[y][x];
     231        }
     232    }
     233    return;
     234}
     235
     236// Generate the convolution given some offset
     237static psKernel *convolveOffset(const psKernel *image, // Image to convolve (a kernel for convenience)
     238                                int u, int v, // Offset to apply
     239                                int footprint // Size of region of interest
     240                                )
     241{
     242    psKernel *convolved = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Convolved image
     243    int numBytes = (2 * footprint + 1) * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes to copy
     244    for (int y = -footprint; y <= footprint; y++) {
     245        // Convolution with a delta function is just the value specified by the offset
     246        memcpy(&convolved->kernel[y][-footprint], &image->kernel[y - v][-footprint - u], numBytes);
     247    }
     248    return convolved;
     249}
     250
     251// Generate the convolution given a precalculated kernel
     252static psKernel *convolvePrecalc(const psKernel *image, // Image to convolve (a kernel for convenience)
     253                                 const psKernel *kernel, // Kernel by which to convolve
     254                                 int footprint // Size of region of interest
     255                                 )
     256{
     257    psImage *conv = psImageConvolveFFT(image->image, kernel, 0.0); // Convolved image
     258    int x0 = - image->xMin, y0 = - image->yMin; // Position of centre of convolved image
     259    psKernel *convolved = psKernelAllocFromImage(conv, x0, y0); // Kernel version
     260    psFree(conv);
     261    return convolved;
     262}
     263
     264// Generate the convolved reference image
     265static psKernel *convolveRef(const pmSubtractionKernels *kernels, // Kernel basis functions
     266                             int index, // Kernel basis function index
     267                             const psKernel *image, // Image to convolve (a kernel for convenience)
     268                             int footprint // Size of region of interest
     269                             )
    224270{
    225271    switch (kernels->type) {
    226272      case PM_SUBTRACTION_KERNEL_POIS: {
    227           // Convolution with a delta function is just the value specified by the offset
    228273          int u = kernels->u->data.S32[index]; // Offset in x
    229274          int v = kernels->v->data.S32[index]; // Offset in y
    230           float value = image->kernel[y + v][x + u]; // Value of convolution
     275          psKernel *convolved = convolveOffset(image, u, v, footprint); // Convolved image
    231276          if (kernels->spatialOrder > 0 && index != kernels->subIndex) {
    232               // The (0,0) element is subtracted from most kernels to preserve photometric scaling
    233               value -= image->kernel[y][x];
     277              convolveSub(convolved, image, footprint);
    234278          }
    235           return value;
     279          return convolved;
    236280      }
    237281        // Method for SPAM and FRIES is the same
    238282      case PM_SUBTRACTION_KERNEL_SPAM:
    239283      case PM_SUBTRACTION_KERNEL_FRIES: {
     284          psKernel *convolved = psKernelAlloc(-footprint, footprint,
     285                                              -footprint, footprint); // Convolved image
    240286          int uStart = kernels->u->data.S32[index];
    241287          int uStop = kernels->uStop->data.S32[index];
    242288          int vStart = kernels->v->data.S32[index];
    243289          int vStop = kernels->vStop->data.S32[index];
    244           double sum = 0.0;
    245           for (int v = vStart; v <= vStop; v++) {
    246               for (int u = uStart; u <= uStop; u++) {
    247                   sum += image->kernel[y + v][x + u];
     290          float norm = 1.0 / (uStop - uStart + 1) * (vStop - vStart + 1); // Normalisation
     291          for (int y = -footprint; y <= footprint; y++) {
     292              for (int x = -footprint; x <= footprint; x++) {
     293                  double sum = 0.0;
     294                  for (int v = vStart; v <= vStop; v++) {
     295                      for (int u = uStart; u <= uStop; u++) {
     296                          sum += image->kernel[y - v][x - u];
     297                      }
     298                  }
     299                  convolved->kernel[y][x] = norm * sum;
    248300              }
    249301          }
    250           sum /= (uStop - uStart + 1) * (vStop - vStart + 1); // Normalising sum of kernel component to unity
    251302          if (kernels->spatialOrder > 0 && index != kernels->subIndex) {
    252               // The (0,0) element is subtracted from most kernels to preserve photometric scaling
    253               sum -= image->kernel[y][x];
     303              convolveSub(convolved, image, footprint);
    254304          }
    255           return sum;
     305          return convolved;
    256306      }
    257307      case PM_SUBTRACTION_KERNEL_GUNK: {
    258308          if (index < kernels->inner) {
    259               // Using pre-calculated function
    260               psKernel *kernel = kernels->preCalc->data[index]; // The convolution kernel
    261               int size = kernels->size;     // Kernel half-size
    262               double sum = 0.0;             // Accumulated sum from convolution
    263               for (int v = -size; v <= size; v++) {
    264                   for (int u = -size; u <= size; u++) {
    265                       sum += kernel->kernel[v][u] * image->kernel[y + v][x + u];
    266                   }
    267               }
    268               return sum;
     309              // Photometric scaling is already built in to the precalculated kernel
     310              return convolvePrecalc(image, kernels->preCalc->data[index], footprint);
    269311          }
    270312          // Using delta function
    271313          int u = kernels->u->data.S32[index]; // Offset in x
    272314          int v = kernels->v->data.S32[index]; // Offset in y
    273           float value = image->kernel[y + v][x + u]; // Value of convolution
    274           // The (0,0) delta function is subtracted from most kernels to preserve photometric scaling
     315          psKernel *convolved = convolveOffset(image, u, v, footprint); // Convolved image
    275316          if (kernels->spatialOrder > 0 && index != kernels->subIndex) {
    276               value -= image->kernel[y][x];
     317              convolveSub(convolved, image, footprint);
    277318          }
    278           return value;
     319          return convolved;
    279320      }
    280321      case PM_SUBTRACTION_KERNEL_ISIS: {
    281           psKernel *kernel = kernels->preCalc->data[index]; // The convolution kernel
    282           int size = kernels->size;     // Kernel half-size
    283           double sum = 0.0;             // Accumulated sum from convolution
    284           for (int v = -size; v <= size; v++) {
    285               for (int u = -size; u <= size; u++) {
    286                   sum += kernel->kernel[v][u] * image->kernel[y + v][x + u];
    287                   // Photometric scaling is already built in to the precalculated kernel
    288               }
    289           }
    290           return sum;
     322          // Photometric scaling is already built in to the precalculated kernel
     323          return convolvePrecalc(image, kernels->preCalc->data[index], footprint);
    291324      }
    292325      case PM_SUBTRACTION_KERNEL_RINGS: {
     326          psKernel *convolved = psKernelAlloc(-footprint, footprint,
     327                                              -footprint, footprint); // Convolved image
    293328          if (index == kernels->subIndex) {
    294               return image->kernel[y][x];
     329              // Simply copying over the image
     330              return convolveOffset(image, 0, 0, footprint);
    295331          }
     332
    296333          psArray *preCalc = kernels->preCalc->data[index]; // Precalculated data
    297334          psVector *uCoords = preCalc->data[0]; // u coordinates
     
    299336          psVector *poly = preCalc->data[2]; // Polynomial values
    300337          int num = uCoords->n;         // Number of pixels
    301           double sum = 0.0;             // Accumulated sum from convolution
    302           for (int j = 0; j < num; j++) {
    303               int u = uCoords->data.S32[j], v = vCoords->data.S32[j]; // Kernel coordinates
    304               sum += image->kernel[y + v][x + u] * poly->data.F32[j];
     338          for (int y = -footprint; y <= footprint; y++) {
     339              for (int x = -footprint; x <= footprint; x++) {
     340                  double sum = 0.0;             // Accumulated sum from convolution
     341                  for (int j = 0; j < num; j++) {
     342                      int u = uCoords->data.S32[j], v = vCoords->data.S32[j]; // Kernel coordinates
     343                      sum += image->kernel[y - v][x - u] * poly->data.F32[j];
     344                  }
     345                  convolved->kernel[y][x] = sum;
     346                  // Photometric scaling is built into the kernel --- no subtraction!
     347              }
    305348          }
    306           // Photometric scaling is built into the kernel --- no subtraction!
    307           return sum;
     349          return convolved;
    308350      }
    309351      default:
    310352        psAbort("Should never get here.");
    311353    }
    312     return NAN;
     354    return NULL;
    313355}
    314356
     
    460502    int numParams = numKernels * numSpatial + numBackground;
    461503    int bgIndex = numParams - numBackground; // Index in matrix for the background
    462     psVector *convolutions = psVectorAlloc(numKernels * numSpatial, PS_TYPE_F64); // Convolutions
    463504
    464505    // We iterate over each stamp, allocate the matrix and vectors if
     
    466507    for (int i = 0; i < stamps->n; i++) {
    467508        pmSubtractionStamp *stamp = stamps->data[i]; // Stamp of interest
    468         if (stamp->status == PM_SUBTRACTION_STAMP_CALCULATE) {
    469             psImage *stampMatrix = stamp->matrix; // Least-squares matrix for this stamp
    470             psVector *stampVector = stamp->vector; // Least-squares vector for this stamp
    471 
    472             if (!stampMatrix) {
    473                 stamp->matrix = stampMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
    474             }
    475             assert(stampMatrix->type.type == PS_TYPE_F64);
    476             assert(stampMatrix->numCols == numParams && stampMatrix->numRows == numParams);
    477             psImageInit(stampMatrix, 0.0);
    478 
    479             if (!stampVector) {
    480                 stamp->vector = stampVector = psVectorAlloc(numParams, PS_TYPE_F64);
    481             }
    482             assert(stampVector->type.type == PS_TYPE_F64);
    483             assert(stampVector->n == numParams);
    484             psVectorInit(stampVector, 0.0);
    485 
    486             // Spatial polynomial terms
    487             psImage *polyValues = spatialPolyValues(spatialOrder, stamp->xNorm, stamp->yNorm);
    488 
    489             psKernel *reference = stamp->reference; // Reference postage stamp
    490             psKernel *input = stamp->input; // Input postage stamp
    491             psKernel *weight = stamp->weight; // Weight map postage stamp
    492 
    493             for (int y = - footprint; y <= footprint; y++) {
    494                 for (int x = - footprint; x <= footprint; x++) {
    495                     float invNoise2 = 1.0 / weight->kernel[y][x]; // Inverse square noise
    496 
    497                     // Generate the convolutions
    498                     for (int j = 0; j < numKernels; j++) {
    499                         double value = convolvePixel(kernels, j, x, y, reference); // Value from convolution
    500                         // Generate the pseudo-convolutions from the spatial polynomial terms
    501                         for (int yOrder = 0, index = j; yOrder <= spatialOrder; yOrder++) {
    502                             for (int xOrder = 0; xOrder <= spatialOrder - yOrder;
    503                                  xOrder++, index += numKernels) {
    504                                 convolutions->data.F64[index] = value * polyValues->data.F64[yOrder][xOrder];
     509        if (stamp->status != PM_SUBTRACTION_STAMP_CALCULATE) {
     510            continue;
     511        }
     512
     513        psImage *stampMatrix = stamp->matrix; // Least-squares matrix for this stamp
     514        psVector *stampVector = stamp->vector; // Least-squares vector for this stamp
     515
     516        if (!stampMatrix) {
     517            stamp->matrix = stampMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
     518        }
     519        assert(stampMatrix->type.type == PS_TYPE_F64);
     520        assert(stampMatrix->numCols == numParams && stampMatrix->numRows == numParams);
     521        psImageInit(stampMatrix, 0.0);
     522
     523        if (!stampVector) {
     524            stamp->vector = stampVector = psVectorAlloc(numParams, PS_TYPE_F64);
     525        }
     526        assert(stampVector->type.type == PS_TYPE_F64);
     527        assert(stampVector->n == numParams);
     528        psVectorInit(stampVector, 0.0);
     529
     530        psKernel *reference = stamp->reference; // Reference postage stamp
     531        psKernel *input = stamp->input; // Input postage stamp
     532        psKernel *weight = stamp->weight; // Weight map postage stamp
     533
     534        // Generate convolutions of the reference
     535        psArray *convolutions = stamp->convolutions; // Convolutions of the reference for each kernel
     536        if (!convolutions) {
     537            stamp->convolutions = convolutions = psArrayAlloc(numKernels);
     538        }
     539        for (int j = 0; j < numKernels; j++) {
     540            if (convolutions->data[j]) {
     541                psFree(convolutions->data[j]);
     542            }
     543            convolutions->data[j] = convolveRef(kernels, j, reference, footprint);
     544#ifdef TESTING
     545            {
     546                psKernel *conv = convolutions->data[j]; // Convolution of interest
     547                psString filename = NULL;
     548                psStringAppend(&filename, "conv_%03d_%03d.fits", i, j);
     549                psFits *fits = psFitsOpen(filename, "w");
     550                psFree(filename);
     551                psFitsWriteImage(fits, NULL, conv->image, 0, NULL);
     552                psFitsClose(fits);
     553            }
     554#endif
     555       }
     556
     557        psImage *polyValues = spatialPolyValues(spatialOrder, stamp->xNorm, stamp->yNorm); // Polynomial terms
     558
     559        // Generate least-squares vector and matrix
     560        for (int j = 0; j < numKernels; j++) {
     561            psKernel *jConv = convolutions->data[j]; // Convolution for j-th element
     562
     563            // Generate upper diagonals of matrix
     564            for (int k = 0; k < numKernels; k++) {
     565                psKernel *kConv = convolutions->data[k]; // Convolution for k-th element
     566                double sumCC = 0.0; // Sum of the convolution products
     567                for (int y = - footprint; y <= footprint; y++) {
     568                    for (int x = - footprint; x <= footprint; x++) {
     569                        sumCC += jConv->kernel[y][x] * kConv->kernel[y][x] / weight->kernel[y][x];
     570                    }
     571                }
     572                // Generate the pseudo-convolutions from the spatial polynomial terms
     573                for (int jyOrder = 0, jIndex = j; jyOrder <= spatialOrder; jyOrder++) {
     574                    for (int jxOrder = 0; jxOrder <= spatialOrder - jyOrder;
     575                         jxOrder++, jIndex += numKernels) {
     576                        for (int kyOrder = 0, kIndex = k; kyOrder <= spatialOrder; kyOrder++) {
     577                            for (int kxOrder = 0; kxOrder <= spatialOrder - kyOrder;
     578                                 kxOrder++, kIndex += numKernels) {
     579                                stampMatrix->data.F64[jIndex][kIndex] = sumCC *
     580                                    polyValues->data.F64[jyOrder][jxOrder] *
     581                                    polyValues->data.F64[kyOrder][kxOrder];
    505582                            }
    506583                        }
    507584                    }
    508 
    509                     // Generate the least-squares matrix and vector
    510                     // Upper diagonal only
    511                     for (int i = 0; i < bgIndex; i++) {
    512                         for (int j = i; j < bgIndex; j++) {
    513                             stampMatrix->data.F64[i][j] += convolutions->data.F64[i] *
    514                                 convolutions->data.F64[j] * invNoise2;
     585                }
     586            }
     587
     588            // Vector and background term for matrix
     589            double sumC = 0.0;      // Sum of the convolution
     590            double sumIC = 0.0;     // Sum of the convolution/input product
     591            for (int y = - footprint; y <= footprint; y++) {
     592                for (int x = - footprint; x <= footprint; x++) {
     593                    double convProduct = jConv->kernel[y][x] / weight->kernel[y][x]; // Convolution / noise^2
     594                    sumC += convProduct;
     595                    sumIC += input->kernel[y][x] * convProduct;
     596                }
     597            }
     598            for (int jyOrder = 0, jIndex = j; jyOrder <= spatialOrder; jyOrder++) {
     599                for (int jxOrder = 0; jxOrder <= spatialOrder - jyOrder;
     600                     jxOrder++, jIndex += numKernels) {
     601                    stampVector->data.F64[jIndex] = sumIC * polyValues->data.F64[jyOrder][jxOrder];
     602                    stampMatrix->data.F64[jIndex][bgIndex] = sumC * polyValues->data.F64[jyOrder][jxOrder];
     603                    stampMatrix->data.F64[bgIndex][jIndex] = sumC * polyValues->data.F64[jyOrder][jxOrder];
     604                }
     605            }
     606
     607        }
     608        psFree(polyValues);
     609
     610        // Background only terms
     611        double sum1 = 0.0;              // Sum of the weighting
     612        double sumI = 0.0;              // Sum of the input
     613        for (int y = - footprint; y <= footprint; y++) {
     614            for (int x = - footprint; x <= footprint; x++) {
     615                double invNoise2 = 1.0 / weight->kernel[y][x]; // Inverse noise, squared
     616                sum1 += invNoise2;
     617                sumI += input->kernel[y][x] * invNoise2;
     618            }
     619        }
     620        stampMatrix->data.F64[bgIndex][bgIndex] = sum1;
     621        stampVector->data.F64[bgIndex] = sumI;
     622
     623        // Fill in lower diagonals of symmetric matrix, while checking for bad values
     624        // Note, there are two symmetries going on here --- the matrix is C_i C_j P_i P_j
     625        bool bad = false;           // Are there bad values?
     626#if 0
     627        for (int j = 0; j < numKernels; j++) {
     628            for (int jSpatial = 0, jIndex = j; jSpatial < numSpatial; jSpatial++, jIndex += numKernels) {
     629                for (int k = 0; k < j; k++) {
     630                    for (int kSpatial = 0, kIndex = k; kSpatial < numSpatial;
     631                         kSpatial++, kIndex += numKernels) {
     632                        double value = stampMatrix->data.F64[kIndex][jIndex]; // Value of matrix
     633                        stampMatrix->data.F64[jIndex][kIndex] = value;
     634                        if (!isfinite(value)) {
     635                            bad = true;
    515636                        }
    516                         stampVector->data.F64[i] += input->kernel[y][x] * convolutions->data.F64[i] *
    517                             invNoise2;
    518 
    519                         // Background term
    520                         stampMatrix->data.F64[i][bgIndex] += convolutions->data.F64[i] * invNoise2;
    521637                    }
    522                     // Background only terms
    523                     stampMatrix->data.F64[bgIndex][bgIndex] += invNoise2;
    524                     stampVector->data.F64[bgIndex] += input->kernel[y][x] * invNoise2;
    525                 }
    526             }
    527             psFree(polyValues);
    528 
    529             // Fill in lower diagonal of symmetric matrix, while checking for bad values
    530             bool bad = false;           // Are there bad values?
    531             for (int i = 0; i < bgIndex; i++) {
    532                 for (int j = 0; j < i; j++) {
    533                     stampMatrix->data.F64[i][j] = stampMatrix->data.F64[j][i];
    534                     if (!isfinite(stampMatrix->data.F64[j][i])) {
    535                         bad = true;
    536                     }
    537                 }
    538                 stampMatrix->data.F64[bgIndex][i] = stampMatrix->data.F64[i][bgIndex];
    539                 if (!isfinite(stampMatrix->data.F64[i][bgIndex]) ||
    540                     !isfinite(stampMatrix->data.F64[i][i]) ||
    541                     !isfinite(stampVector->data.F64[i])) {
     638                }
     639
     640                stampMatrix->data.F64[bgIndex][jIndex] = stampMatrix->data.F64[jIndex][bgIndex];
     641                if (!isfinite(stampMatrix->data.F64[jIndex][bgIndex]) ||
     642                    !isfinite(stampMatrix->data.F64[jIndex][jIndex]) ||
     643                    !isfinite(stampVector->data.F64[jIndex])) {
    542644                    bad = true;
    543645                }
    544646            }
    545             if (!isfinite(stampVector->data.F64[bgIndex])) {
    546                 bad = true;
    547             }
    548 
    549             if (bad) {
    550                 stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
    551                 psTrace("psModules.imcombine", 3, "Rejecting stamp %d (%d,%d) because of bad equation\n",
    552                         i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
    553             } else {
    554                 stamp->status = PM_SUBTRACTION_STAMP_USED;
    555             }
    556 
    557             if (psTraceGetLevel("psModules.imcombine.equation") >= 10) {
    558                 psString matrixName = NULL;
    559                 psStringAppend(&matrixName, "matrix%d.fits", i);
    560                 psFits *matrixFile = psFitsOpen(matrixName, "w");
    561                 psFree(matrixName);
    562                 psFitsWriteImage(matrixFile, NULL, stampMatrix, 0, NULL);
    563                 psFitsClose(matrixFile);
    564             }
    565 
    566         }
    567     }
    568 
    569     psFree(convolutions);
     647        }
     648#endif
     649        if (!isfinite(stampVector->data.F64[bgIndex])) {
     650            bad = true;
     651        }
     652
     653        if (bad) {
     654            stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
     655            psTrace("psModules.imcombine", 3, "Rejecting stamp %d (%d,%d) because of bad equation\n",
     656                    i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
     657        } else {
     658            stamp->status = PM_SUBTRACTION_STAMP_USED;
     659        }
     660
     661        if (psTraceGetLevel("psModules.imcombine.equation") >= 10) {
     662            psString matrixName = NULL;
     663            psStringAppend(&matrixName, "matrix%d.fits", i);
     664            psFits *matrixFile = psFitsOpen(matrixName, "w");
     665            psFree(matrixName);
     666            psFitsWriteImage(matrixFile, NULL, stampMatrix, 0, NULL);
     667            psFitsClose(matrixFile);
     668        }
     669    }
    570670
    571671    return true;
     
    665765    double totalSquareDev = 0.0;        // Total square deviation from zero
    666766    int numStamps = 0;                  // Number of used stamps
     767    int numKernels = kernels->num;      // Number of kernels
     768    int spatialOrder = kernels->spatialOrder; // Order of kernel spatial variations
     769    double devNorm = 1.0 / PS_SQR(2 * footprint + 1); // Normalisation for deviations
    667770    {
    668         psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // Statistics
    669         psKernel *convolution = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Convolution
    670         psKernel *kernelImage = NULL;       // The kernel, with which to convolve the stamps
    671771        float background = solution->data.F64[solution->n-1]; // The difference in background
    672         int size = kernels->size;       // Half-size of the kernel
    673772
    674773        for (int i = 0; i < stamps->n; i++) {
     
    681780                                                    stamp->yNorm); // Polynomial terms
    682781
    683             psKernel *reference = stamp->reference; // Reference postage stamp
    684 
    685             kernelImage = solvedKernel(kernelImage, solution, kernels, polyValues, false);
     782            psKernel *input = stamp->input; // Input postage stamp
     783            psKernel *weight = stamp->weight; // Weight postage stamp
     784            psArray *convolutions = stamp->convolutions; // Convolutions of reference image for each kernel
     785#ifdef TESTING
     786            psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint);
     787#endif
     788            float deviation = 0.0;      // Deviation for this stamp
    686789            for (int y = - footprint; y <= footprint; y++) {
    687790                for (int x = - footprint; x <= footprint; x++) {
    688                     convolution->kernel[y][x] = background;
    689                     for (int v = -size; v <= size; v++) {
    690                         for (int u = -size; u <= size; u++) {
    691                             convolution->kernel[y][x] += kernelImage->kernel[v][u] *
    692                                 reference->kernel[y + v][x + u];
     791                    float conv = background; // The value of the convolution
     792                    for (int j = 0; j < numKernels; j++) {
     793                        psKernel *convolution = convolutions->data[j]; // Convolution of reference
     794                        double polynomial = 0.0; // Value of the polynomial
     795                        for (int yOrder = 0, index = j; yOrder <= spatialOrder; yOrder++) {
     796                            for (int xOrder = 0; xOrder <= spatialOrder - yOrder;
     797                                 xOrder++, index += numKernels) {
     798                                polynomial += solution->data.F64[index] *
     799                                    polyValues->data.F64[yOrder][xOrder];
     800                            }
    693801                        }
     802                        conv += convolution->kernel[y][x] * polynomial;
    694803                    }
     804                    float diff = input->kernel[y][x] - conv;
     805                    deviation += PS_SQR(diff) / weight->kernel[y][x];
     806#ifdef TESTING
     807                    residual->kernel[y][x] = diff;
     808#endif
    695809                }
    696810            }
    697811            psFree(polyValues);
    698812
    699             psImage *convolvedStamp = convolution->image; // Image of the convolution
    700             psImage *input = stamp->input->image; // Input image postage stamp
    701             psImage *weight = stamp->weight->image; // Weight image postage stamp
    702 
    703             // Region of interest
    704             psRegion inRegion = psRegionSet(input->col0 + size, input->col0 + size + 2 * footprint + 1,
    705                                           input->row0 + size, input->row0 + size + 2 * footprint + 1);
    706             psRegion wtRegion = (input == weight ? inRegion :
    707                                  psRegionSet(weight->col0 + size, weight->col0 + size + 2 * footprint + 1,
    708                                              weight->row0 + size, weight->row0 + size + 2 * footprint + 1));
    709 
    710             psImage *inStamp = psImageSubset(stamp->input->image, inRegion); // Image of stamp
    711             psImage *wtStamp = psImageSubset(stamp->weight->image, wtRegion); // Image of stamp
    712             assert(convolvedStamp->numCols == inStamp->numCols &&
    713                    convolvedStamp->numRows == inStamp->numRows);
    714             assert(convolvedStamp->numCols == wtStamp->numCols &&
    715                    convolvedStamp->numRows == wtStamp->numRows);
    716             (void)psBinaryOp(convolvedStamp, inStamp, "-", convolvedStamp);
    717             (void)psBinaryOp(convolvedStamp, convolvedStamp, "*", convolvedStamp);
    718             (void)psBinaryOp(convolvedStamp, convolvedStamp, "/", wtStamp);
    719             psFree(inStamp);
    720             psFree(wtStamp);
    721             if (!psImageStats(stats, convolvedStamp, NULL, 0)) {
    722                 psError(PS_ERR_UNKNOWN, false,
    723                         "Unable to calculate statistics on normalised residual image of stamp.");
    724                 psFree(deviations);
    725                 psFree(convolution);
    726                 psFree(stats);
    727                 return -1;
    728             }
    729             deviations->data.F32[i] = sqrt(stats->sampleMean / 2.0);
    730             psTrace("psModules.imcombine", 1, "Deviation for stamp %d (%d,%d): %f\n",
    731                     i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5), deviations->data.F32[i]);
     813            deviations->data.F32[i] = devNorm * deviation;
     814            psTrace("psModules.imcombine", 5, "Deviation for stamp %d (%d,%d): %f\n",
     815                    i, (int)stamp->x, (int)stamp->y, deviations->data.F32[i]);
    732816            totalSquareDev += PS_SQR(deviations->data.F32[i]);
    733817            numStamps++;
    734         }
    735 
    736         psFree(kernelImage);
    737         psFree(convolution);
    738         psFree(stats);
     818
     819#ifdef TESTING
     820            {
     821                psString filename = NULL;
     822                psStringAppend(&filename, "resid_%03d.fits", i);
     823                psFits *fits = psFitsOpen(filename, "w");
     824                psFree(filename);
     825                psFitsWriteImage(fits, NULL, residual->image, 0, NULL);
     826                psFitsClose(fits);
     827            }
     828            psFree(residual);
     829#endif
     830
     831        }
    739832    }
    740833
     
    823916}
    824917
     918// Convolve an image using FFT
     919static void convolveFFT(psImage *target,// Place the result in here
     920                        const psImage *image, // Image to convolve
     921                        const psKernel *kernel, // Kernel by which to convolve
     922                        psRegion region,// Region of interest
     923                        float background, // Background to add
     924                        int size        // Size of (square) kernel
     925                        )
     926{
     927    psRegion border = psRegionSet(region.x0 - size, region.x1 + size,
     928                                  region.y0 - size, region.y1 + size); // Add a border
     929
     930    // Casting away const so psImageSubset can add the child
     931    psImage *subImage = psImageSubset((psImage*)image, border); // Subimage to convolve
     932    psImage *convolved = psImageConvolveFFT(subImage, kernel, 0.0); // Convolution
     933    psFree(subImage);
     934
     935    // Now, we have to chop off the borders, and stick it in where it belongs
     936    psImage *subConv = psImageSubset(convolved, psRegionSet(size, -size,
     937                                                            size, -size)); // Cut off the edges
     938    psImage *subTarget = psImageSubset(target, region); // Target for this subregion
     939    if (background != 0.0) {
     940        psBinaryOp(subTarget, subConv, "+", psScalarAlloc(background, PS_TYPE_F32));
     941    } else {
     942        int numBytes = subTarget->numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes to copy
     943        for (int y = 0; y < subTarget->numRows; y++) {
     944            memcpy(subTarget->data.F32[y], subConv->data.F32[y], numBytes);
     945        }
     946    }
     947    psFree(subConv);
     948    psFree(convolved);
     949    psFree(subTarget);
     950    return;
     951}
     952
     953// Convolve an image directly
     954static void convolveDirect(psImage *target, // Put the result here
     955                           const psImage *image, // Image to convolve
     956                           const psKernel *kernel, // Kernel by which to convolve
     957                           psRegion region,// Region of interest
     958                           float background, // Background to add
     959                           int size        // Size of (square) kernel
     960                           )
     961{
     962    for (int y = region.y0; y < region.y1; y++) {
     963        for (int x = region.x0; x < region.x1; x++) {
     964            target->data.F32[y][x] = background;
     965            for (int v = -size; v <= size; v++) {
     966                for (int u = -size; u <= size; u++) {
     967                    target->data.F32[y][x] += kernel->kernel[v][u] *
     968                        image->data.F32[y - v][x - u];
     969                }
     970            }
     971        }
     972    }
     973    return;
     974}
    825975
    826976bool pmSubtractionConvolve(psImage **outImage, psImage **outWeight, psImage **outMask,
    827977                           const psImage *inImage, const psImage *inWeight, const psImage *subMask,
    828978                           psMaskType blank, const psRegion *region, const psVector *solution,
    829                            const pmSubtractionKernels *kernels)
     979                           const pmSubtractionKernels *kernels, bool useFFT)
    830980{
    831981    PS_ASSERT_IMAGE_NON_NULL(inImage, false);
     
    9271077
    9281078    for (int j = yMin; j < yMax; j += fullSize) {
     1079        int ySubMax = PS_MIN(j + fullSize, yMax); // Range for subregion of interest
    9291080        for (int i = xMin; i < xMax; i += fullSize) {
     1081            int xSubMax = PS_MIN(i + fullSize, xMax); // Range for subregion of interest
    9301082
    9311083            // Only generate polynomial values every kernel footprint, since we have already assumed
     
    9351087                                           2.0 * (float)(i + size + 1 - numCols/2.0) / (float)numCols,
    9361088                                           2.0 * (float)(j + size + 1 - numRows/2.0) / (float)numRows);
     1089
    9371090            kernelImage = solvedKernel(kernelImage, solution, kernels, polyValues, false);
    9381091            if (inWeight) {
     
    9401093            }
    9411094
    942             for (int y = j; y < PS_MIN(j + fullSize, yMax); y++) {
    943                 for (int x = i; x < PS_MIN(i + fullSize, xMax); x++) {
    944                     // Check and propagate the kernel footprint, if required
     1095
     1096            if (useFFT) {
     1097                // Use Fast Fourier Transform to do the convolution
     1098                // This provides a big speed-up for large kernels
     1099                convolveFFT(convImage, inImage, kernelImage, psRegionSet(i, xSubMax, j, ySubMax),
     1100                            background, size);
     1101                if (inWeight) {
     1102                    convolveFFT(convWeight, inWeight, kernelWeight, psRegionSet(i, xSubMax, j, ySubMax),
     1103                                0.0, size);
     1104                }
     1105            } else {
     1106                convolveDirect(convImage, inImage, kernelImage, psRegionSet(i, xSubMax, j, ySubMax),
     1107                               background, size);
     1108                if (inWeight) {
     1109                    convolveDirect(convWeight, inWeight, kernelWeight, psRegionSet(i, xSubMax, j, ySubMax),
     1110                                   0.0, size);
     1111                }
     1112            }
     1113
     1114            // Propagate the mask
     1115            for (int y = j; y < ySubMax; y++) {
     1116                for (int x = i; x < xSubMax; x++) {
    9451117                    if (subMask && (subMask->data.PS_TYPE_MASK_DATA[y][x] &
    9461118                                    (PM_SUBTRACTION_MASK_INPUT | PM_SUBTRACTION_MASK_CONVOLVE))) {
     
    9501122                            convWeight->data.F32[y][x] = NAN;
    9511123                        }
    952                     } else {
    953                         // Convolve the image
    954                         convImage->data.F32[y][x] = background;
    955                         for (int v = -size; v <= size; v++) {
    956                             for (int u = -size; u <= size; u++) {
    957                                 convImage->data.F32[y][x] += kernelImage->kernel[v][u] *
    958                                     inImage->data.F32[y + v][x + u];
    959                             }
    960                         }
    961 
    962                         // Convolve the weight (variance) map
    963                         if (inWeight) {
    964                             for (int v = -size; v <= size; v++) {
    965                                 for (int u = -size; u <= size; u++) {
    966                                     convWeight->data.F32[y][x] += kernelWeight->kernel[v][u] *
    967                                         inWeight->data.F32[y + v][x + u];
    968                                 }
    969                             }
    970                         }
    9711124                    }
    9721125                }
    9731126            }
     1127
    9741128        }
    9751129    }
Note: See TracChangeset for help on using the changeset viewer.