IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
May 18, 2010, 4:11:53 PM (16 years ago)
Author:
eugene
Message:

merge changes from branches/eam_branches/psphot,psModules.20100506 (finish basic psphotStack)

Location:
trunk/psphot
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/psphot

  • trunk/psphot/src/psphotStackMatchPSFsUtils.c

    r27850 r28013  
    1 /***** defines *****/
    2 
    3 #define ARRAY_BUFFER 16                 // Number to add to array at a time
    4 #define MAG_IGNORE 50                   // Ignore magnitudes fainter than this --- they're not real!
    5 #define FAKE_SIZE 1                     // Size of fake convolution kernel
    6 #define SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_EXT_LIMIT) // Mask to apply to input sources
    7 #define NOISE_FRACTION 0.01             // Set minimum flux to this fraction of noise
    8 #define COVAR_FRAC 0.01                 // Truncation fraction for covariance matrix
     1# include "psphotInternal.h"
     2# define ARRAY_BUFFER 16                 // Number to add to array at a time
    93
    104// XXX better name
     
    1812    psFree(resolved);
    1913    if (!fits) {
    20         psError(PPSTACK_ERR_IO, false, "Unable to open previously produced image: %s", name);
     14        psError(PSPHOT_ERR_IO, false, "Unable to open previously produced image: %s", name);
    2115        return false;
    2216    }
    2317    psImage *image = psFitsReadImage(fits, psRegionSet(0,0,0,0), 0); // Image of interest
    2418    if (!image) {
    25         psError(PPSTACK_ERR_IO, false, "Unable to read previously produced image: %s", name);
     19        psError(PSPHOT_ERR_IO, false, "Unable to read previously produced image: %s", name);
    2620        psFitsClose(fits);
    2721        return false;
     
    5145}
    5246
     47# define SN_MIN 50.0
    5348psArray *stackSourcesFilter(psArray *sources, // Source list to filter
    54                                    int exclusion // Exclusion zone, pixels
     49                            int exclusion // Exclusion zone, pixels
    5550    )
    5651{
     
    6863            continue;
    6964        }
     65        if (!source->peak) continue;
     66        if (source->peak->SN < SN_MIN) continue;
    7067        coordsFromSource(&x->data.F32[numGood], &y->data.F32[numGood], source);
    7168        numGood++;
     
    8380            continue;
    8481        }
     82        if (!source->peak) continue;
     83        if (source->peak->SN < SN_MIN) continue;
    8584        float xSource, ySource;         // Coordinates of source
    8685        coordsFromSource(&xSource, &ySource, source);
     
    9089
    9190        long numWithin = psTreeWithin(tree, coords, exclusion); // Number within exclusion zone
    92         psTrace("ppStack", 9, "Source at %.0lf,%.0lf has %ld sources in exclusion zone",
     91        psTrace("psphotStack", 9, "Source at %.0lf,%.0lf has %ld sources in exclusion zone",
    9392                coords->data.F64[0], coords->data.F64[1], numWithin);
    9493        if (numWithin == 1) {
     
    104103    psFree(y);
    105104
    106     psLogMsg("ppStack", PS_LOG_INFO, "Filtered out %d of %d sources", numFiltered, numGood);
     105    psLogMsg("psphotStack", PS_LOG_INFO, "Filtered out %d of %d sources", numFiltered, numGood);
    107106
    108107    return filtered;
     
    111110// Add background into the fake image
    112111// Based on ppSubBackground()
    113 static psImage *stackBackgroundModel(pmReadout *ro, // Readout for which to generate background model
     112psImage *stackBackgroundModel(pmReadout *ro, // Readout for which to generate background model
    114113                                     const pmConfig *config // Configuration
    115114    )
     
    121120    int numCols = image->numCols, numRows = image->numRows; // Size of image
    122121
    123     psMetadata *ppStackRecipe = psMetadataLookupPtr(NULL, config->recipes, PPSTACK_RECIPE);
     122    psMetadata *ppStackRecipe = psMetadataLookupPtr(NULL, config->recipes, "PPSTACK");
    124123    psAssert(ppStackRecipe, "Need PPSTACK recipe");
    125     psMetadata *psphotRecipe = psMetadataLookupPtr(NULL, config->recipes, PSPHOT_RECIPE);
     124    psMetadata *psphotRecipe = psMetadataLookupPtr(NULL, config->recipes, "PSPHOT");
    126125    psAssert(psphotRecipe, "Need PSPHOT recipe");
    127126
     
    138137    psImage *unbinned = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Unbinned background model
    139138    if (!psImageUnbin(unbinned, binned, binning)) {
    140         psError(PPSTACK_ERR_DATA, false, "Unable to unbin background model");
     139        psError(PSPHOT_ERR_DATA, false, "Unable to unbin background model");
    141140        psFree(binned);
    142141        psFree(unbinned);
     
    153152    )
    154153{
    155 #if 1
    156154    bool mdok; // Status of metadata lookups
    157155
    158     psMetadata *recipe = psMetadataLookupPtr(NULL, config->recipes, PPSTACK_RECIPE); // Recipe for ppStack
     156    psMetadata *recipe = psMetadataLookupPtr(NULL, config->recipes, "PPSTACK"); // Recipe for ppStack
    159157    psAssert(recipe, "Need PPSTACK recipe");
    160158
     
    163161    int num = psMetadataLookupS32(&mdok, recipe, "RENORM.NUM");
    164162    if (!mdok) {
    165         psError(PPSTACK_ERR_CONFIG, true, "RENORM.NUM is not set in the recipe");
     163        psError(PSPHOT_ERR_CONFIG, true, "RENORM.NUM is not set in the recipe");
    166164        return false;
    167165    }
    168166    float minValid = psMetadataLookupF32(&mdok, recipe, "RENORM.MIN");
    169167    if (!mdok) {
    170         psError(PPSTACK_ERR_CONFIG, true, "RENORM.MIN is not set in the recipe");
     168        psError(PSPHOT_ERR_CONFIG, true, "RENORM.MIN is not set in the recipe");
    171169        return false;
    172170    }
    173171    float maxValid = psMetadataLookupF32(&mdok, recipe, "RENORM.MAX");
    174172    if (!mdok) {
    175         psError(PPSTACK_ERR_CONFIG, true, "RENORM.MAX is not set in the recipe");
     173        psError(PSPHOT_ERR_CONFIG, true, "RENORM.MAX is not set in the recipe");
    176174        return false;
    177175    }
     
    181179    psImageCovarianceTransfer(readout->variance, readout->covariance);
    182180    return pmReadoutVarianceRenormalise(readout, maskBad, num, minValid, maxValid);
    183 #else
    184     return true;
    185 #endif
    186181}
    187182
     
    190185// It implicitly assumes the output root name is the same between invocations.
    191186
    192 bool loadKernel () {
    193             pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.CONV.KERNEL", index);
    194             psAssert(file, "Require file");
    195 
    196             pmFPAview *view = pmFPAviewAlloc(0); // View to readout of interest
    197             view->chip = view->cell = view->readout = 0;
    198             psString filename = pmFPAfileNameFromRule(file->filerule, file, view); // Filename of interest
    199 
    200             // Read convolution kernel
    201             psString resolved = pmConfigConvertFilename(filename, config, false, false); // Resolved filename
    202             psFree(filename);
    203             psFits *fits = psFitsOpen(resolved, "r"); // FITS file for subtraction kernel
    204             psFree(resolved);
    205             if (!fits || !pmReadoutReadSubtractionKernels(conv, fits)) {
    206                 psError(PPSTACK_ERR_IO, false, "Unable to read previously produced kernel");
    207                 psFitsClose(fits);
    208                 return false;
    209             }
    210             psFitsClose(fits);
    211 
    212             if (!readImage(&readout->image, options->convImages->data[index], config) ||
    213                 !readImage(&readout->mask, options->convMasks->data[index], config) ||
    214                 !readImage(&readout->variance, options->convVariances->data[index], config)) {
    215                 psError(PPSTACK_ERR_IO, false, "Unable to read previously produced image.");
    216                 return false;
    217             }
    218 
    219             psRegion *region = psMetadataLookupPtr(NULL, conv->analysis,
    220                                                    PM_SUBTRACTION_ANALYSIS_REGION); // Convolution region
    221             pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, conv->analysis,
    222                                                                 PM_SUBTRACTION_ANALYSIS_KERNEL);
    223 
    224             pmSubtractionAnalysis(conv->analysis, NULL, kernels, region,
    225                                   readout->image->numCols, readout->image->numRows);
    226 
    227             psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0, false); // Convolution kernel
    228             bool oldThreads = psImageCovarianceSetThreads(true);              // Old thread setting
    229             psKernel *covar = psImageCovarianceCalculate(kernel, readout->covariance); // Covariance matrix
    230             psImageCovarianceSetThreads(oldThreads);
    231             psFree(readout->covariance);
    232             readout->covariance = covar;
    233             psFree(kernel);
    234 }
    235 
    236 bool dumpImage() {
    237     // XXX should be optional
    238             {
    239                 pmHDU *hdu = pmHDUFromCell(readout->parent);
    240                 psString name = NULL;
    241                 psStringAppend(&name, "fake_%03d.fits", index);
    242                 pmStackVisualPlotTestImage(fake->image, name);
    243                 psFits *fits = psFitsOpen(name, "w");
    244                 psFree(name);
    245                 psFitsWriteImage(fits, hdu->header, fake->image, 0, NULL);
    246                 psFitsClose(fits);
    247             }
    248             {
    249                 pmHDU *hdu = pmHDUFromCell(readout->parent);
    250                 psString name = NULL;
    251                 psStringAppend(&name, "real_%03d.fits", index);
    252                 pmStackVisualPlotTestImage(readout->image, name);
    253                 psFits *fits = psFitsOpen(name, "w");
    254                 psFree(name);
    255                 psFitsWriteImage(fits, hdu->header, readout->image, 0, NULL);
    256                 psFitsClose(fits);
    257             }
    258 }
    259 
    260 bool dumpImage2() {
    261     // XXX should be optional
    262 
    263             {
    264                 pmHDU *hdu = pmHDUFromCell(readout->parent);
    265                 psString name = NULL;
    266                 psStringAppend(&name, "conv_%03d.fits", index);
    267                 pmStackVisualPlotTestImage(conv->image, name);
    268                 psFits *fits = psFitsOpen(name, "w");
    269                 psFree(name);
    270                 psFitsWriteImage(fits, hdu->header, conv->image, 0, NULL);
    271                 psFitsClose(fits);
    272             }
    273             {
    274                 pmHDU *hdu = pmHDUFromCell(readout->parent);
    275                 psString name = NULL;
    276                 psStringAppend(&name, "diff_%03d.fits", index);
    277                 pmStackVisualPlotTestImage(fake->image, name);
    278                 psFits *fits = psFitsOpen(name, "w");
    279                 psFree(name);
    280                 psBinaryOp(fake->image, conv->image, "-", fake->image);
    281                 psFitsWriteImage(fits, hdu->header, fake->image, 0, NULL);
    282                 psFitsClose(fits);
    283             }
    284 }
    285 
    286 bool dumpImage3()
     187# if (0)
     188bool loadKernel (pmConfig *config, pmReadout *readoutCnv, psphotStackOptions *options, int index) {
     189
     190    // Read the convolution kernel from the saved file
     191    pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.CONV.KERNEL", index);
     192    psAssert(file, "Require file");
     193
     194    pmFPAview *view = pmFPAviewAlloc(0); // View to readout of interest
     195    view->chip = view->cell = view->readout = 0;
     196    psString filename = pmFPAfileNameFromRule(file->filerule, file, view); // Filename of interest
     197
     198    // Read convolution kernel data
     199    psString resolved = pmConfigConvertFilename(filename, config, false, false); // Resolved filename
     200    psFree(filename);
     201    psFits *fits = psFitsOpen(resolved, "r"); // FITS file for subtraction kernel
     202    psFree(resolved);
     203    if (!fits || !pmReadoutReadSubtractionKernels(readoutCnv, fits)) {
     204        psError(PSPHOT_ERR_IO, false, "Unable to read previously produced kernel");
     205        psFitsClose(fits);
     206        return false;
     207    }
     208    psFitsClose(fits);
     209
     210    // read the convolved pixels (image, mask, variance) -- names are pre-defined
     211    if (!readImage(&readoutCnv->image,    options->convImages->data[index],    config) ||
     212        !readImage(&readoutCnv->mask,     options->convMasks->data[index],     config) ||
     213        !readImage(&readoutCnv->variance, options->convVariances->data[index], config)) {
     214        psError(PSPHOT_ERR_IO, false, "Unable to read previously produced image.");
     215        return false;
     216    }
     217
     218    // XXX ??? not sure what is happening here -- consult Paul
     219    psRegion *region = psMetadataLookupPtr(NULL, readoutCnv->analysis, PM_SUBTRACTION_ANALYSIS_REGION); // Convolution region
     220    pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, readoutCnv->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL);
     221
     222    pmSubtractionAnalysis(readoutCnv->analysis, NULL, kernels, region, readoutCnv->image->numCols, readoutCnv->image->numRows);
     223
     224    psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0, false); // Convolution kernel
     225
     226    // update the covariance matrix
     227    // XXX why is this needed if we have correctly read the saved data?
     228    bool oldThreads = psImageCovarianceSetThreads(true);              // Old thread setting
     229    psKernel *covar = psImageCovarianceCalculate(kernel, readoutCnv->covariance); // Covariance matrix
     230    psImageCovarianceSetThreads(oldThreads);
     231    psFree(readoutCnv->covariance);
     232    readoutCnv->covariance = covar;
     233    psFree(kernel);
     234    return true;
     235}
     236# endif
     237
     238bool dumpImage(pmReadout *readoutOut, pmReadout *readoutRef, int index, char *rootname) {
     239
     240    pmHDU *hdu = pmHDUFromCell(readoutRef->parent);
     241    psString name = NULL;
     242    psStringAppend(&name, "%s_%03d.fits", rootname, index);
     243    pmStackVisualPlotTestImage(readoutOut->image, name);
     244    psFits *fits = psFitsOpen(name, "w");
     245    psFree(name);
     246    psFitsWriteImage(fits, hdu->header, readoutOut->image, 0, NULL);
     247    psFitsClose(fits);
     248    return true;
     249}
     250
     251bool dumpImageDiff(pmReadout *readoutConv, pmReadout *readoutFake, pmReadout *readoutRef, int index, char *rootname) {
     252
     253    pmHDU *hdu = pmHDUFromCell(readoutRef->parent);
     254    psString name = NULL;
     255    psStringAppend(&name, "%s_%03d.fits", rootname, index);
     256    pmStackVisualPlotTestImage(readoutFake->image, name);
     257    psFits *fits = psFitsOpen(name, "w");
     258    psFree(name);
     259    psBinaryOp(readoutFake->image, readoutConv->image, "-", readoutFake->image);
     260    psFitsWriteImage(fits, hdu->header, readoutFake->image, 0, NULL);
     261    psFitsClose(fits);
     262    return true;
     263}
     264
     265// perform the bulk of the PSF-matching
     266bool matchKernel(pmConfig *config, pmReadout *readoutOut, pmReadout *readoutSrc, psphotStackOptions *options, int index) {
     267
     268    bool mdok;
     269
     270    psAssert(options->psf, "Require target PSF");
     271    psAssert(options->sourceLists && options->sourceLists->data[index], "Require source list");
     272
     273    psMetadata *stackRecipe = psMetadataLookupMetadata(NULL, config->recipes, "PPSTACK"); // ppStack recipe
     274    psAssert(stackRecipe, "We've thrown an error on this before.");
     275
     276    // Look up appropriate values from the ppSub recipe
     277    psMetadata *subRecipe = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
     278    psAssert(subRecipe, "recipe missing");
     279
     280    psString maskValStr = psMetadataLookupStr(NULL, subRecipe, "MASK.VAL"); // Name of bits to mask going in
     281    psString maskPoorStr = psMetadataLookupStr(NULL, stackRecipe, "MASK.POOR"); // Name of bits to mask for poor
     282    psString maskBadStr = psMetadataLookupStr(NULL, stackRecipe, "MASK.BAD"); // Name of bits to mask for bad
     283
     284    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
     285    psImageMaskType maskPoor = pmConfigMaskGet(maskPoorStr, config); // Bits to mask for poor pixels
     286    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
     287
     288    float penalty = psMetadataLookupF32(NULL, subRecipe, "PENALTY"); // Penalty for wideness
     289    int threads = psMetadataLookupS32(NULL, config->arguments, "-threads"); // Number of threads
     290
     291    int order = psMetadataLookupS32(NULL, subRecipe, "SPATIAL.ORDER"); // Spatial polynomial order
     292    float regionSize = psMetadataLookupF32(NULL, subRecipe, "REGION.SIZE"); // Size of iso-kernel regs
     293    float spacing = psMetadataLookupF32(NULL, subRecipe, "STAMP.SPACING"); // Typical stamp spacing
     294
     295    int footprint = psMetadataLookupS32(NULL, subRecipe, "STAMP.FOOTPRINT"); // Stamp half-size
     296    int size = psMetadataLookupS32(NULL, subRecipe, "KERNEL.SIZE"); // Kernel half-size
     297
     298    float threshold = psMetadataLookupF32(NULL, subRecipe, "STAMP.THRESHOLD"); // Threshold for stmps
     299    int stride = psMetadataLookupS32(NULL, subRecipe, "STRIDE"); // Size of convolution patches
     300    int iter = psMetadataLookupS32(NULL, subRecipe, "ITER"); // Rejection iterations
     301    float rej = psMetadataLookupF32(NULL, subRecipe, "REJ"); // Rejection threshold
     302    float kernelError = psMetadataLookupF32(NULL, subRecipe, "KERNEL.ERR"); // Relative systematic error in kernel
     303    float normFrac = psMetadataLookupF32(NULL, subRecipe, "NORM.FRAC"); // Fraction of window for normalisn windw
     304    float sysError = psMetadataLookupF32(NULL, subRecipe, "SYS.ERR"); // Relative systematic error in images
     305    float skyErr = psMetadataLookupF32(NULL, subRecipe, "SKY.ERR"); // Additional error in sky
     306    float covarFrac = psMetadataLookupF32(NULL, subRecipe, "COVAR.FRAC"); // Fraction for covariance calculation
     307
     308    const char *typeStr = psMetadataLookupStr(NULL, subRecipe, "KERNEL.TYPE"); // Kernel type
     309    pmSubtractionKernelsType type = pmSubtractionKernelsTypeFromString(typeStr); // Kernel type
     310    psVector *widths = psMetadataLookupPtr(NULL, subRecipe, "ISIS.WIDTHS"); // ISIS Gaussian widths
     311    psVector *orders = psMetadataLookupPtr(NULL, subRecipe, "ISIS.ORDERS"); // ISIS Polynomial orders
     312    int inner = psMetadataLookupS32(NULL, subRecipe, "INNER"); // Inner radius
     313    int ringsOrder = psMetadataLookupS32(NULL, subRecipe, "RINGS.ORDER"); // RINGS polynomial order
     314    int binning = psMetadataLookupS32(NULL, subRecipe, "SPAM.BINNING"); // Binning for SPAM kernel
     315    float badFrac = psMetadataLookupF32(NULL, subRecipe, "BADFRAC"); // Maximum bad fraction
     316    float optThresh = psMetadataLookupF32(&mdok, subRecipe, "OPTIMUM.TOL"); // Tolerance for search
     317    int optOrder = psMetadataLookupS32(&mdok, subRecipe, "OPTIMUM.ORDER"); // Order for search
     318    float poorFrac = psMetadataLookupF32(&mdok, subRecipe, "POOR.FRACTION"); // Fraction for "poor"
     319
     320    bool scale = psMetadataLookupBool(NULL, subRecipe, "SCALE");        // Scale kernel parameters?
     321    float scaleRef = psMetadataLookupF32(NULL, subRecipe, "SCALE.REF"); // Reference for scaling
     322    float scaleMin = psMetadataLookupF32(NULL, subRecipe, "SCALE.MIN"); // Minimum for scaling
     323    float scaleMax = psMetadataLookupF32(NULL, subRecipe, "SCALE.MAX"); // Maximum for scaling
     324    if (!isfinite(scaleRef) || !isfinite(scaleMin) || !isfinite(scaleMax)) {
     325        psError(PSPHOT_ERR_CONFIG, false,
     326                "Scale parameters (SCALE.REF=%f, SCALE.MIN=%f, SCALE.MAX=%f) not set in PPSUB recipe.",
     327                scaleRef, scaleMin, scaleMax);
     328        return false;
     329    }
     330
     331    // These values are specified specifically for stacking
     332    const char *stampsName = psMetadataLookupStr(&mdok, config->arguments, "STAMPS");// Stamps filename
     333
     334    psVector *widthsCopy = NULL;
     335    psVector *optWidths = NULL;
     336    pmReadout *fake = NULL;
     337    psArray *stampSources = NULL;
     338
     339    bool optimum = false;
     340    optWidths = SetOptWidths(&optimum, subRecipe); // Vector with FWHMs for optimum search
     341
     342    // For the sake of stamps, remove nearby sources
     343    stampSources = stackSourcesFilter(options->sourceLists->data[index], footprint); // Filtered list of sources
     344
     345    fake = makeFakeReadout(config, readoutSrc, stampSources, options->psf, maskVal | maskBad, footprint + size);
     346    if (!fake) goto escape;
     347
     348    dumpImage(fake, readoutSrc, index, "fake");
     349    dumpImage(readoutSrc,  readoutSrc, index, "real");
     350
     351    if (threads) pmSubtractionThreadsInit();
     352
     353    // Do the image matching
     354    pmSubtractionKernels *kernel = psMetadataLookupPtr(&mdok, readoutSrc->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL); // Conv kernel
     355    if (kernel) {
     356        if (!pmSubtractionMatchPrecalc(NULL, readoutOut, fake, readoutSrc, readoutSrc->analysis, stride, kernelError, covarFrac, maskVal, maskBad, maskPoor, poorFrac, badFrac)) {
     357            psError(psErrorCodeLast(), false, "Unable to convolve images.");
     358            goto escape;
     359        }
     360    } else {
     361        // Scale the input parameters
     362        widthsCopy = psVectorCopy(NULL, widths, PS_TYPE_F32); // Copy of kernel widths
     363        if (scale && !pmSubtractionParamsScale(&size, &footprint, widthsCopy, options->inputSeeing->data.F32[index], options->targetSeeing, scaleRef, scaleMin, scaleMax)) {
     364            psError(psErrorCodeLast(), false, "Unable to scale kernel parameters");
     365            goto escape;
     366        }
     367
     368        if (!pmSubtractionMatch(NULL, readoutOut, fake, readoutSrc, footprint, stride, regionSize, spacing, threshold, stampSources, stampsName, type, size, order, widthsCopy, orders, inner, ringsOrder, binning, penalty, optimum, optWidths, optOrder, optThresh, iter, rej, normFrac, sysError, skyErr, kernelError, covarFrac, maskVal, maskBad, maskPoor, poorFrac, badFrac, PM_SUBTRACTION_MODE_2)) {
     369            psError(psErrorCodeLast(), false, "Unable to match images.");
     370            goto escape;
     371        }
     372    }
     373
     374    // Reject image completely if the maximum deconvolution fraction exceeds the limit
     375    float deconvLimit = psMetadataLookupF32(NULL, stackRecipe, "DECONV.LIMIT"); // Limit on deconvolution fraction
     376    float deconv = psMetadataLookupF32(NULL, readoutOut->analysis, PM_SUBTRACTION_ANALYSIS_DECONV_MAX); // Max deconvolution fraction
     377    if (deconv > deconvLimit) {
     378        psWarning("Maximum deconvolution fraction (%f) exceeds limit (%f) --- rejecting image %d\n", deconv, deconvLimit, index);
     379        goto escape;
     380    }
     381
     382    dumpImage(readoutOut, readoutSrc, index, "conv");
     383    dumpImageDiff(readoutOut, fake, readoutSrc, index, "diff");
     384
     385    psFree(fake);
     386    psFree(optWidths);
     387    psFree(stampSources);
     388    psFree(widthsCopy);
     389    pmSubtractionThreadsFinalize();
     390    return true;
     391
     392escape:
     393    psFree(fake);
     394    psFree(optWidths);
     395    psFree(stampSources);
     396    psFree(widthsCopy);
     397    pmSubtractionThreadsFinalize();
     398    return false;
     399}
     400
     401// Extract the regions and solutions used in the image matching
     402// This stops them from being freed when we iterate back up the FPA
     403// Record the chi-square value
     404// XXX this function may not be needed for psphotStack
     405bool saveMatchData (pmReadout *readout, psphotStackOptions *options, int index) {
     406
     407    psArray *regions = options->regions->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match regions
    287408    {
    288         pmHDU *hdu = pmHDUFromCell(readout->parent);
    289         psString name = NULL;
    290         psStringAppend(&name, "convolved_%03d.fits", index);
    291         pmStackVisualPlotTestImage(readout->image, name);
    292         psFits *fits = psFitsOpen(name, "w");
    293         psFree(name);
    294         psFitsWriteImage(fits, hdu->header, readout->image, 0, NULL);
    295         psFitsClose(fits);
    296     }
    297 
    298 bool matchKernel() {
    299             // Normal operations here
    300             psAssert(options->psf, "Require target PSF");
    301             psAssert(options->sourceLists && options->sourceLists->data[index], "Require source list");
    302 
    303             int order = psMetadataLookupS32(NULL, subRecipe, "SPATIAL.ORDER"); // Spatial polynomial order
    304             float regionSize = psMetadataLookupF32(NULL, subRecipe, "REGION.SIZE"); // Size of iso-kernel regs
    305             float spacing = psMetadataLookupF32(NULL, subRecipe, "STAMP.SPACING"); // Typical stamp spacing
    306             int footprint = psMetadataLookupS32(NULL, subRecipe, "STAMP.FOOTPRINT"); // Stamp half-size
    307             float threshold = psMetadataLookupF32(NULL, subRecipe, "STAMP.THRESHOLD"); // Threshold for stmps
    308             int stride = psMetadataLookupS32(NULL, subRecipe, "STRIDE"); // Size of convolution patches
    309             int iter = psMetadataLookupS32(NULL, subRecipe, "ITER"); // Rejection iterations
    310             float rej = psMetadataLookupF32(NULL, subRecipe, "REJ"); // Rejection threshold
    311             float kernelError = psMetadataLookupF32(NULL, subRecipe, "KERNEL.ERR"); // Relative systematic error in kernel
    312             float normFrac = psMetadataLookupF32(NULL, subRecipe, "NORM.FRAC"); // Fraction of window for normalisn windw
    313             float sysError = psMetadataLookupF32(NULL, subRecipe, "SYS.ERR"); // Relative systematic error in images
    314             float skyErr = psMetadataLookupF32(NULL, subRecipe, "SKY.ERR"); // Additional error in sky
    315             float covarFrac = psMetadataLookupF32(NULL, subRecipe, "COVAR.FRAC"); // Fraction for covariance calculation
    316 
    317             const char *typeStr = psMetadataLookupStr(NULL, subRecipe, "KERNEL.TYPE"); // Kernel type
    318             pmSubtractionKernelsType type = pmSubtractionKernelsTypeFromString(typeStr); // Kernel type
    319             psVector *widths = psMetadataLookupPtr(NULL, subRecipe, "ISIS.WIDTHS"); // ISIS Gaussian widths
    320             psVector *orders = psMetadataLookupPtr(NULL, subRecipe, "ISIS.ORDERS"); // ISIS Polynomial orders
    321             int inner = psMetadataLookupS32(NULL, subRecipe, "INNER"); // Inner radius
    322             int ringsOrder = psMetadataLookupS32(NULL, subRecipe, "RINGS.ORDER"); // RINGS polynomial order
    323             int binning = psMetadataLookupS32(NULL, subRecipe, "SPAM.BINNING"); // Binning for SPAM kernel
    324             float badFrac = psMetadataLookupF32(NULL, subRecipe, "BADFRAC"); // Maximum bad fraction
    325             bool optimum = psMetadataLookupBool(&mdok, subRecipe, "OPTIMUM"); // Derive optimum parameters?
    326             float optMin = psMetadataLookupF32(&mdok, subRecipe, "OPTIMUM.MIN"); // Minimum width for search
    327             float optMax = psMetadataLookupF32(&mdok, subRecipe, "OPTIMUM.MAX"); // Maximum width for search
    328             float optStep = psMetadataLookupF32(&mdok, subRecipe, "OPTIMUM.STEP"); // Step for search
    329             float optThresh = psMetadataLookupF32(&mdok, subRecipe, "OPTIMUM.TOL"); // Tolerance for search
    330             int optOrder = psMetadataLookupS32(&mdok, subRecipe, "OPTIMUM.ORDER"); // Order for search
    331             float poorFrac = psMetadataLookupF32(&mdok, subRecipe, "POOR.FRACTION"); // Fraction for "poor"
    332 
    333             bool scale = psMetadataLookupBool(NULL, subRecipe, "SCALE");        // Scale kernel parameters?
    334             float scaleRef = psMetadataLookupF32(NULL, subRecipe, "SCALE.REF"); // Reference for scaling
    335             float scaleMin = psMetadataLookupF32(NULL, subRecipe, "SCALE.MIN"); // Minimum for scaling
    336             float scaleMax = psMetadataLookupF32(NULL, subRecipe, "SCALE.MAX"); // Maximum for scaling
    337             if (!isfinite(scaleRef) || !isfinite(scaleMin) || !isfinite(scaleMax)) {
    338                 psError(PPSTACK_ERR_CONFIG, false,
    339                         "Scale parameters (SCALE.REF=%f, SCALE.MIN=%f, SCALE.MAX=%f) not set in PPSUB recipe.",
    340                         scaleRef, scaleMin, scaleMax);
    341                 return false;
    342             }
    343 
    344 
    345             // These values are specified specifically for stacking
    346             const char *stampsName = psMetadataLookupStr(NULL, config->arguments, "STAMPS");// Stamps filename
    347 
    348             psVector *optWidths = NULL;         // Vector with FWHMs for optimum search
    349             if (optimum) {
    350                 optWidths = psVectorCreate(optWidths, optMin, optMax, optStep, PS_TYPE_F32);
    351             }
    352 
    353             pmReadout *fake = pmReadoutAlloc(NULL); // Fake readout with target PSF
    354 
    355             psStats *bg = psStatsAlloc(PS_STAT_ROBUST_STDEV); // Statistics for background
    356             psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
    357             if (!psImageBackground(bg, NULL, readout->image, readout->mask, maskVal | maskBad, rng)) {
    358                 psError(PPSTACK_ERR_DATA, false, "Can't measure background for image.");
    359                 psFree(fake);
    360                 psFree(optWidths);
    361                 psFree(conv);
    362                 psFree(bg);
    363                 psFree(rng);
    364                 return false;
    365             }
    366             float minFlux = NOISE_FRACTION * bg->robustStdev; // Minimum flux level for fake image
     409        psString regex = NULL;          // Regular expression
     410        psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_REGION);
     411        psMetadataIterator *iter = psMetadataIteratorAlloc(readout->analysis, PS_LIST_HEAD, regex);
     412        psFree(regex);
     413        psMetadataItem *item = NULL;// Item from iteration
     414        while ((item = psMetadataGetAndIncrement(iter))) {
     415            assert(item->type == PS_DATA_REGION);
     416            regions = psArrayAdd(regions, ARRAY_BUFFER, item->data.V);
     417        }
     418        psFree(iter);
     419    }
     420
     421    psArray *kernels = options->kernels->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match kernels
     422    {
     423        psString regex = NULL;          // Regular expression
     424        psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
     425        psMetadataIterator *iter = psMetadataIteratorAlloc(readout->analysis, PS_LIST_HEAD, regex);
     426        psFree(regex);
     427        psMetadataItem *item = NULL;// Item from iteration
     428        while ((item = psMetadataGetAndIncrement(iter))) {
     429            assert(item->type == PS_DATA_UNKNOWN);
     430            pmSubtractionKernels *kernel = item->data.V; // Kernel used in subtraction
     431            kernels = psArrayAdd(kernels, ARRAY_BUFFER, kernel);
     432        }
     433        psFree(iter);
     434    }
     435    psAssert((regions)->n == (kernels)->n, "Number of match regions and kernels should match");
     436
     437    // Record chi^2
     438    {
     439        double sum = 0.0;           // Sum of chi^2
     440        int num = 0;                // Number of measurements of chi^2
     441        psString regex = NULL;      // Regular expression
     442        psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
     443        psMetadataIterator *iter = psMetadataIteratorAlloc(readout->analysis, PS_LIST_HEAD, regex);
     444        psFree(regex);
     445        psMetadataItem *item = NULL;// Item from iteration
     446        while ((item = psMetadataGetAndIncrement(iter))) {
     447            assert(item->type == PS_DATA_UNKNOWN);
     448            pmSubtractionKernels *kernels = item->data.V; // Convolution kernels
     449            sum += kernels->mean;
     450            num++;
     451        }
     452        psFree(iter);
     453        options->matchChi2->data.F32[index] = sum / (psImageCovarianceFactor(readout->covariance) * num);
     454    }
     455
     456    return true;
     457}
     458
     459// Kernel normalisation for convolved readout
     460bool renormKernel(pmReadout *readout, psphotStackOptions *options, int index) {
     461
     462    double sum = 0.0;           // Sum of chi^2
     463    int num = 0;                // Number of measurements of chi^2
     464    psString regex = NULL;      // Regular expression
     465    psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_NORM);
     466    psMetadataIterator *iter = psMetadataIteratorAlloc(readout->analysis, PS_LIST_HEAD, regex);
     467    psFree(regex);
     468    psMetadataItem *item = NULL;// Item from iteration
     469    while ((item = psMetadataGetAndIncrement(iter))) {
     470        assert(item->type == PS_TYPE_F32);
     471        float norm = item->data.F32; // Normalisation
     472        sum += norm;
     473        num++;
     474    }
     475    psFree(iter);
     476    float conv = sum/num;       // Mean normalisation from convolution
     477    float stars = powf(10.0, -0.4 * options->norm->data.F32[index]); // Normalisation from stars
     478    float renorm =  stars / conv; // Renormalisation to apply
     479    psLogMsg("psphotStack", PS_LOG_INFO, "Renormalising image %d by %f (kernel: %f, stars: %f)\n", index, renorm, conv, stars);
     480
     481    psBinaryOp(readout->image, readout->image, "*", psScalarAlloc(renorm, PS_TYPE_F32));
     482    psBinaryOp(readout->variance, readout->variance, "*", psScalarAlloc(PS_SQR(renorm), PS_TYPE_F32));
     483    return true;
     484}
     485
     486// adjust scaling for readout (remove background, ..., determine weighting)
     487bool rescaleData(pmReadout *readout, pmConfig *config, psphotStackOptions *options, int index) {
     488
     489    psMetadata *stackRecipe = psMetadataLookupMetadata(NULL, config->recipes, "PPSTACK"); // ppStack recipe
     490    psAssert(stackRecipe, "We've thrown an error on this before.");
     491
     492    // Look up appropriate values from the ppSub recipe
     493    psMetadata *subRecipe = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
     494    psAssert(subRecipe, "recipe missing");
     495
     496    psString maskValStr = psMetadataLookupStr(NULL, subRecipe, "MASK.VAL"); // Name of bits to mask going in
     497    psString maskBadStr = psMetadataLookupStr(NULL, stackRecipe, "MASK.BAD"); // Name of bits to mask for bad
     498
     499    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
     500    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
     501
     502    // Ensure the background value is zero
     503    psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for background
     504    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
     505
     506    // XXX why is this in config->arguments and not recipe?
     507    if (!psMetadataLookupBool(NULL, config->arguments, "PPSTACK.SKIP.BG.SUB")) {
     508        if (!psImageBackground(bg, NULL, readout->image, readout->mask, maskVal | maskBad, rng)) {
     509            psAbort("Can't measure background for image.");
     510            // XXX we used to clear error: why is this acceptable? psErrorClear();
     511        }
     512
     513        float value = psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN);
     514        float stdev = psStatsGetValue(bg, PS_STAT_ROBUST_STDEV);
     515
     516        psLogMsg("psphotStack", PS_LOG_INFO, "Correcting convolved image background by %lf (+/- %lf)", value, stdev);
     517        psBinaryOp(readout->image, readout->image, "-", psScalarAlloc(value, PS_TYPE_F32));
     518    }
     519
     520    if (!stackRenormaliseReadout(config, readout)) {
     521        psFree(rng);
     522        psFree(bg);
     523        return false;
     524    }
     525
     526    // Measure the variance level for the weighting
     527    if (psMetadataLookupBool(NULL, stackRecipe, "WEIGHTS")) {
     528        if (!psImageBackground(bg, NULL, readout->variance, readout->mask, maskVal | maskBad, rng)) {
     529            psError(PSPHOT_ERR_DATA, false, "Can't measure mean variance for image.");
    367530            psFree(rng);
    368531            psFree(bg);
    369 
    370             // For the sake of stamps, remove nearby sources
    371             psArray *stampSources = stackSourcesFilter(options->sourceLists->data[index],
    372                                                        footprint); // Filtered list of sources
    373 
    374             bool oldThreads = pmReadoutFakeThreads(true); // Old threading state
    375             if (!pmReadoutFakeFromSources(fake, readout->image->numCols, readout->image->numRows,
    376                                           stampSources, SOURCE_MASK, NULL, NULL, options->psf,
    377                                           minFlux, footprint + size, false, true)) {
    378                 psError(PPSTACK_ERR_DATA, false, "Unable to generate fake image with target PSF.");
    379                 psFree(fake);
    380                 psFree(optWidths);
    381                 psFree(conv);
    382                 return false;
    383             }
    384             pmReadoutFakeThreads(oldThreads);
    385 
    386             fake->mask = psImageCopy(NULL, readout->mask, PS_TYPE_IMAGE_MASK);
    387 
    388             // Add the background into the target image
    389             psImage *bgImage = stackBackgroundModel(readout, config); // Image of background
    390             psBinaryOp(fake->image, fake->image, "+", bgImage);
    391             psFree(bgImage);
    392 
    393             dumpImage();
    394 
    395             if (threads > 0) {
    396                 pmSubtractionThreadsInit();
    397             }
    398 
    399             // Do the image matching
    400             pmSubtractionKernels *kernel = psMetadataLookupPtr(&mdok, readout->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL); // Conv kernel
    401             if (kernel) {
    402                 if (!pmSubtractionMatchPrecalc(NULL, conv, fake, readout, readout->analysis,
    403                                                stride, kernelError, covarFrac, maskVal, maskBad, maskPoor,
    404                                                poorFrac, badFrac)) {
    405                     psError(psErrorCodeLast(), false, "Unable to convolve images.");
    406                     psFree(fake);
    407                     psFree(optWidths);
    408                     psFree(stampSources);
    409                     psFree(conv);
    410                     if (threads > 0) {
    411                         pmSubtractionThreadsFinalize();
    412                     }
    413                     return false;
    414                 }
    415             } else {
    416                 // Scale the input parameters
    417                 psVector *widthsCopy = psVectorCopy(NULL, widths, PS_TYPE_F32); // Copy of kernel widths
    418                 if (scale && !pmSubtractionParamsScale(&size, &footprint, widthsCopy,
    419                                                        options->inputSeeing->data.F32[index],
    420                                                        options->targetSeeing, scaleRef, scaleMin, scaleMax)) {
    421                     psError(psErrorCodeLast(), false, "Unable to scale kernel parameters");
    422                     psFree(fake);
    423                     psFree(optWidths);
    424                     psFree(stampSources);
    425                     psFree(conv);
    426                     psFree(widthsCopy);
    427                     if (threads > 0) {
    428                         pmSubtractionThreadsFinalize();
    429                     }
    430                     return false;
    431                 }
    432 
    433                 if (!pmSubtractionMatch(NULL, conv, fake, readout, footprint, stride, regionSize, spacing,
    434                                         threshold, stampSources, stampsName, type, size, order, widthsCopy,
    435                                         orders, inner, ringsOrder, binning, penalty,
    436                                         optimum, optWidths, optOrder, optThresh, iter, rej, normFrac,
    437                                         sysError, skyErr, kernelError, covarFrac, maskVal, maskBad, maskPoor,
    438                                         poorFrac, badFrac, PM_SUBTRACTION_MODE_2)) {
    439                     psError(psErrorCodeLast(), false, "Unable to match images.");
    440                     psFree(fake);
    441                     psFree(optWidths);
    442                     psFree(stampSources);
    443                     psFree(conv);
    444                     psFree(widthsCopy);
    445                     if (threads > 0) {
    446                         pmSubtractionThreadsFinalize();
    447                     }
    448                     return false;
    449                 }
    450                 psFree(widthsCopy);
    451             }
    452 
    453             dumpImage2();
    454 
    455             psFree(fake);
    456             psFree(optWidths);
    457             psFree(stampSources);
    458 
    459             if (threads > 0) {
    460                 pmSubtractionThreadsFinalize();
    461             }
    462 
    463             // Replace original images with convolved
    464             psFree(readout->image);
    465             psFree(readout->mask);
    466             psFree(readout->variance);
    467             psFree(readout->covariance);
    468             readout->image  = psMemIncrRefCounter(conv->image);
    469             readout->mask   = psMemIncrRefCounter(conv->mask);
    470             readout->variance = psMemIncrRefCounter(conv->variance);
    471             readout->covariance = psImageCovarianceTruncate(conv->covariance, COVAR_FRAC);
    472 
    473 }
    474 
    475 bool saveMatchData () {
    476        // Extract the regions and solutions used in the image matching
    477         // This stops them from being freed when we iterate back up the FPA
    478         psArray *regions = options->regions->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match regions
    479         {
    480             psString regex = NULL;          // Regular expression
    481             psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_REGION);
    482             psMetadataIterator *iter = psMetadataIteratorAlloc(conv->analysis, PS_LIST_HEAD, regex);
    483             psFree(regex);
    484             psMetadataItem *item = NULL;// Item from iteration
    485             while ((item = psMetadataGetAndIncrement(iter))) {
    486                 assert(item->type == PS_DATA_REGION);
    487                 regions = psArrayAdd(regions, ARRAY_BUFFER, item->data.V);
    488             }
    489             psFree(iter);
     532            return false;
    490533        }
    491         psArray *kernels = options->kernels->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match kernels
    492         {
    493             psString regex = NULL;          // Regular expression
    494             psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
    495             psMetadataIterator *iter = psMetadataIteratorAlloc(conv->analysis, PS_LIST_HEAD, regex);
    496             psFree(regex);
    497             psMetadataItem *item = NULL;// Item from iteration
    498             while ((item = psMetadataGetAndIncrement(iter))) {
    499                 assert(item->type == PS_DATA_UNKNOWN);
    500                 pmSubtractionKernels *kernel = item->data.V; // Kernel used in subtraction
    501                 kernels = psArrayAdd(kernels, ARRAY_BUFFER, kernel);
    502             }
    503             psFree(iter);
    504         }
    505         psAssert((regions)->n == (kernels)->n, "Number of match regions and kernels should match");
    506 }
    507 
    508 bool saveChiSquare() {
    509         // Record chi^2
    510         {
    511             double sum = 0.0;           // Sum of chi^2
    512             int num = 0;                // Number of measurements of chi^2
    513             psString regex = NULL;      // Regular expression
    514             psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_KERNEL);
    515             psMetadataIterator *iter = psMetadataIteratorAlloc(conv->analysis, PS_LIST_HEAD, regex);
    516             psFree(regex);
    517             psMetadataItem *item = NULL;// Item from iteration
    518             while ((item = psMetadataGetAndIncrement(iter))) {
    519                 assert(item->type == PS_DATA_UNKNOWN);
    520                 pmSubtractionKernels *kernels = item->data.V; // Convolution kernels
    521                 sum += kernels->mean;
    522                 num++;
    523             }
    524             psFree(iter);
    525             options->matchChi2->data.F32[index] = sum / (psImageCovarianceFactor(readout->covariance) * num);
    526         }
    527 
    528 }
    529 
    530 bool renormKernel() {
    531         // Kernel normalisation
    532         {
    533             double sum = 0.0;           // Sum of chi^2
    534             int num = 0;                // Number of measurements of chi^2
    535             psString regex = NULL;      // Regular expression
    536             psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_NORM);
    537             psMetadataIterator *iter = psMetadataIteratorAlloc(conv->analysis, PS_LIST_HEAD, regex);
    538             psFree(regex);
    539             psMetadataItem *item = NULL;// Item from iteration
    540             while ((item = psMetadataGetAndIncrement(iter))) {
    541                 assert(item->type == PS_TYPE_F32);
    542                 float norm = item->data.F32; // Normalisation
    543                 sum += norm;
    544                 num++;
    545             }
    546             psFree(iter);
    547             float conv = sum/num;       // Mean normalisation from convolution
    548             float stars = powf(10.0, -0.4 * options->norm->data.F32[index]); // Normalisation from stars
    549             float renorm =  stars / conv; // Renormalisation to apply
    550             psLogMsg("ppStack", PS_LOG_INFO, "Renormalising image %d by %f (kernel: %f, stars: %f)\n",
    551                      index, renorm, conv, stars);
    552             psBinaryOp(readout->image, readout->image, "*", psScalarAlloc(renorm, PS_TYPE_F32));
    553             psBinaryOp(readout->variance, readout->variance, "*", psScalarAlloc(PS_SQR(renorm), PS_TYPE_F32));
    554         }
    555 
    556 }
     534        options->weightings->data.F32[index] = 1.0 / (psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN) * psImageCovarianceFactor(readout->covariance));
     535    } else {
     536        options->weightings->data.F32[index] = 1.0;
     537    }
     538    psLogMsg("psphotStack", PS_LOG_INFO, "Weighting for image %d is %f\n", index, options->weightings->data.F32[index]);
     539
     540    psFree(rng);
     541    psFree(bg);
     542    return true;
     543}
     544
     545# define NOISE_FRACTION 0.01             // Set minimum flux to this fraction of noise
     546# define SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_EXT_LIMIT) // Mask to apply to input sources
     547
     548// generate a fake readout against which to PSF match
     549pmReadout *makeFakeReadout(pmConfig *config, pmReadout *readoutSrc, psArray *sources, pmPSF *psf, psImageMaskType maskVal, int fullSize) {
     550
     551    pmReadout *fake = pmReadoutAlloc(NULL); // Fake readout with target PSF
     552
     553    psStats *bg = psStatsAlloc(PS_STAT_ROBUST_STDEV); // Statistics for background
     554    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
     555    if (!psImageBackground(bg, NULL, readoutSrc->image, readoutSrc->mask, maskVal, rng)) {
     556        psError(PSPHOT_ERR_DATA, false, "Can't measure background for image.");
     557        psFree(fake);
     558        psFree(bg);
     559        psFree(rng);
     560        return NULL;
     561    }
     562    float minFlux = NOISE_FRACTION * bg->robustStdev; // Minimum flux level for fake image
     563    psFree(rng);
     564    psFree(bg);
     565
     566    bool oldThreads = pmReadoutFakeThreads(true); // Old threading state
     567    if (!pmReadoutFakeFromSources(fake, readoutSrc->image->numCols, readoutSrc->image->numRows, sources, SOURCE_MASK, NULL, NULL, psf, minFlux, fullSize, false, true)) {
     568        psError(PSPHOT_ERR_DATA, false, "Unable to generate fake image with target PSF.");
     569        psFree(fake);
     570        return NULL;
     571    }
     572    pmReadoutFakeThreads(oldThreads);
     573
     574    fake->mask = psImageCopy(NULL, readoutSrc->mask, PS_TYPE_IMAGE_MASK);
     575
     576    // Add the background into the target image
     577    psImage *bgImage = stackBackgroundModel(readoutSrc, config); // Image of background
     578    psBinaryOp(fake->image, fake->image, "+", bgImage);
     579    psFree(bgImage);
     580
     581    return fake;
     582}
     583
     584// set the widths
     585psVector *SetOptWidths (bool *optimum, psMetadata *recipe) {
     586
     587    bool status;
     588
     589    *optimum = psMetadataLookupBool(&status, recipe, "OPTIMUM"); // Derive optimum parameters?
     590    psAssert (status, "missing recipe value %s", "OPTIMUM");
     591
     592    psVector *optWidths = NULL;         // Vector with FWHMs for optimum search
     593
     594    if (*optimum) {
     595        float optMin = psMetadataLookupF32(&status,  recipe, "OPTIMUM.MIN"); // Minimum width for search
     596        psAssert (status, "missing recipe value %s", "OPTIMUM.MIN");
     597       
     598        float optMax = psMetadataLookupF32(&status,  recipe, "OPTIMUM.MAX"); // Maximum width for search
     599        psAssert (status, "missing recipe value %s", "OPTIMUM.MAX");
     600       
     601        float optStep = psMetadataLookupF32(&status, recipe, "OPTIMUM.STEP"); // Step for search
     602        psAssert (status, "missing recipe value %s", "OPTIMUM.STEP");
     603
     604        optWidths = psVectorCreate(optWidths, optMin, optMax, optStep, PS_TYPE_F32);
     605    }
     606
     607    return optWidths;
     608}
Note: See TracChangeset for help on using the changeset viewer.