Changeset 28013 for trunk/psphot/src/psphotStackMatchPSFsUtils.c
- Timestamp:
- May 18, 2010, 4:11:53 PM (16 years ago)
- Location:
- trunk/psphot
- Files:
-
- 2 edited
-
. (modified) (1 prop)
-
src/psphotStackMatchPSFsUtils.c (modified) (14 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psphot
-
Property svn:mergeinfo
set to
/branches/eam_branches/psphot.20100506 merged eligible
-
Property svn:mergeinfo
set to
-
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 9 3 10 4 // XXX better name … … 18 12 psFree(resolved); 19 13 if (!fits) { 20 psError(P PSTACK_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); 21 15 return false; 22 16 } 23 17 psImage *image = psFitsReadImage(fits, psRegionSet(0,0,0,0), 0); // Image of interest 24 18 if (!image) { 25 psError(P PSTACK_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); 26 20 psFitsClose(fits); 27 21 return false; … … 51 45 } 52 46 47 # define SN_MIN 50.0 53 48 psArray *stackSourcesFilter(psArray *sources, // Source list to filter 54 int exclusion // Exclusion zone, pixels49 int exclusion // Exclusion zone, pixels 55 50 ) 56 51 { … … 68 63 continue; 69 64 } 65 if (!source->peak) continue; 66 if (source->peak->SN < SN_MIN) continue; 70 67 coordsFromSource(&x->data.F32[numGood], &y->data.F32[numGood], source); 71 68 numGood++; … … 83 80 continue; 84 81 } 82 if (!source->peak) continue; 83 if (source->peak->SN < SN_MIN) continue; 85 84 float xSource, ySource; // Coordinates of source 86 85 coordsFromSource(&xSource, &ySource, source); … … 90 89 91 90 long numWithin = psTreeWithin(tree, coords, exclusion); // Number within exclusion zone 92 psTrace("p pStack", 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", 93 92 coords->data.F64[0], coords->data.F64[1], numWithin); 94 93 if (numWithin == 1) { … … 104 103 psFree(y); 105 104 106 psLogMsg("p pStack", 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); 107 106 108 107 return filtered; … … 111 110 // Add background into the fake image 112 111 // Based on ppSubBackground() 113 staticpsImage *stackBackgroundModel(pmReadout *ro, // Readout for which to generate background model112 psImage *stackBackgroundModel(pmReadout *ro, // Readout for which to generate background model 114 113 const pmConfig *config // Configuration 115 114 ) … … 121 120 int numCols = image->numCols, numRows = image->numRows; // Size of image 122 121 123 psMetadata *ppStackRecipe = psMetadataLookupPtr(NULL, config->recipes, PPSTACK_RECIPE);122 psMetadata *ppStackRecipe = psMetadataLookupPtr(NULL, config->recipes, "PPSTACK"); 124 123 psAssert(ppStackRecipe, "Need PPSTACK recipe"); 125 psMetadata *psphotRecipe = psMetadataLookupPtr(NULL, config->recipes, PSPHOT_RECIPE);124 psMetadata *psphotRecipe = psMetadataLookupPtr(NULL, config->recipes, "PSPHOT"); 126 125 psAssert(psphotRecipe, "Need PSPHOT recipe"); 127 126 … … 138 137 psImage *unbinned = psImageAlloc(numCols, numRows, PS_TYPE_F32); // Unbinned background model 139 138 if (!psImageUnbin(unbinned, binned, binning)) { 140 psError(P PSTACK_ERR_DATA, false, "Unable to unbin background model");139 psError(PSPHOT_ERR_DATA, false, "Unable to unbin background model"); 141 140 psFree(binned); 142 141 psFree(unbinned); … … 153 152 ) 154 153 { 155 #if 1156 154 bool mdok; // Status of metadata lookups 157 155 158 psMetadata *recipe = psMetadataLookupPtr(NULL, config->recipes, PPSTACK_RECIPE); // Recipe for ppStack156 psMetadata *recipe = psMetadataLookupPtr(NULL, config->recipes, "PPSTACK"); // Recipe for ppStack 159 157 psAssert(recipe, "Need PPSTACK recipe"); 160 158 … … 163 161 int num = psMetadataLookupS32(&mdok, recipe, "RENORM.NUM"); 164 162 if (!mdok) { 165 psError(P PSTACK_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"); 166 164 return false; 167 165 } 168 166 float minValid = psMetadataLookupF32(&mdok, recipe, "RENORM.MIN"); 169 167 if (!mdok) { 170 psError(P PSTACK_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"); 171 169 return false; 172 170 } 173 171 float maxValid = psMetadataLookupF32(&mdok, recipe, "RENORM.MAX"); 174 172 if (!mdok) { 175 psError(P PSTACK_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"); 176 174 return false; 177 175 } … … 181 179 psImageCovarianceTransfer(readout->variance, readout->covariance); 182 180 return pmReadoutVarianceRenormalise(readout, maskBad, num, minValid, maxValid); 183 #else184 return true;185 #endif186 181 } 187 182 … … 190 185 // It implicitly assumes the output root name is the same between invocations. 191 186 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) 188 bool 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 238 bool 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 251 bool 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 266 bool 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 392 escape: 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 405 bool saveMatchData (pmReadout *readout, psphotStackOptions *options, int index) { 406 407 psArray *regions = options->regions->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match regions 287 408 { 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(®ex, "^%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(®ex, "^%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(®ex, "^%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 460 bool 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(®ex, "^%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) 487 bool 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."); 367 530 psFree(rng); 368 531 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(®ex, "^%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; 490 533 } 491 psArray *kernels = options->kernels->data[index] = psArrayAllocEmpty(ARRAY_BUFFER); // Match kernels 492 { 493 psString regex = NULL; // Regular expression 494 psStringAppend(®ex, "^%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(®ex, "^%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(®ex, "^%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 549 pmReadout *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 585 psVector *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.
