IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
May 6, 2010, 8:02:38 PM (16 years ago)
Author:
eugene
Message:

working on psphotStack

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/eam_branches/psphot.20100506/src/psphotStackMatchPSFsUtils.c

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